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
38 struct glob_game_vars *gvar;
\r
39 static word far* clockw= (word far*) 0x046C; /* 18.2hz clock */
\r
42 =============================================================================
\r
46 =============================================================================
\r
49 #define SCREENTILESWIDE 20
\r
50 #define SCREENTILESHIGH 13
\r
52 #define SCREENSPACE (SCREENWIDTH*240)
\r
53 #define FREEEGAMEM (0x10000l-3l*SCREENSPACE)
\r
56 // the update array must have enough space for two screens that can float
\r
57 // up two two tiles each way
\r
59 // (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared
\r
60 // by word width instructions
\r
62 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
63 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
64 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
66 #define G_EGASX_SHIFT 7 // global >> ?? = screen x
\r
67 #define G_CGASX_SHIFT 6 // global >> ?? = screen x
\r
68 #define G_SY_SHIFT 4 // global >> ?? = screen y
\r
70 unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2;
\r
71 #define SY_T_SHIFT 4 // screen y >> ?? = tile
\r
74 #define EGAPORTSCREENWIDE 42
\r
75 #define CGAPORTSCREENWIDE 84
\r
76 #define PORTSCREENHIGH 224
\r
78 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
79 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
80 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
82 #define MAXSCROLLEDGES 6
\r
85 =============================================================================
\r
89 =============================================================================
\r
92 typedef struct spriteliststruct
\r
94 int screenx,screeny;
\r
97 unsigned grseg,sourceofs,planesize;
\r
99 unsigned tilex,tiley,tilewide,tilehigh;
\r
100 int priority,updatecount;
\r
101 struct spriteliststruct **prevptr,*nextsprite;
\r
107 int screenx,screeny;
\r
114 unsigned current; // foreground tiles have high bit set
\r
119 typedef struct animtilestruct
\r
123 unsigned far *mapplane;
\r
124 struct animtilestruct **prevptr,*nexttile;
\r
128 =============================================================================
\r
132 =============================================================================
\r
136 long lasttimecount;
\r
138 boolean compatability; // crippled refresh for wierdo SVGAs
\r
140 unsigned mapwidth,mapheight,mapbyteswide,mapwordswide
\r
141 ,mapbytesextra,mapwordsextra;
\r
142 unsigned mapbwidthtable[MAXMAPHEIGHT];
\r
145 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow
\r
146 // for fractional movement and acceleration.
\r
148 // Tiles : Tile offsets from the upper left corner of the current map.
\r
150 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.
\r
151 // originxscreen is the same spot as originxtile, just with extra precision
\r
152 // so graphics don't need to be done in tile boundaries.
\r
155 unsigned originxglobal,originyglobal;
\r
156 unsigned originxtile,originytile;
\r
157 unsigned originxscreen,originyscreen;
\r
158 unsigned originmap;
\r
159 unsigned originxmin,originxmax,originymin,originymax;
\r
160 unsigned originxtile,originytile;
\r
162 unsigned masterofs;
\r
165 // Table of the offsets from bufferofs of each tile spot in the
\r
166 // view port. The extra wide tile should never be drawn, but the space
\r
167 // is needed to account for the extra 0 in the update arrays. Built by
\r
171 unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
\r
172 unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];
\r
174 unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply
\r
176 byte update[2][UPDATESIZE];
\r
177 byte *updateptr,*baseupdateptr, // current start of update window
\r
179 *baseupdatestart[2];
\r
182 cardtype videocard; // set by VW_Startup
\r
183 grtype grmode; // CGAgr, EGAgr, VGAgr
\r
185 unsigned bufferofs; // hidden area to draw to before displaying
\r
186 unsigned displayofs; // origin of the visable screen
\r
190 =============================================================================
\r
194 =============================================================================
\r
197 static char scratch[20],str[20];
\r
200 tiletype allanims[MAXANIMTYPES];
\r
201 unsigned numanimchains;
\r
203 void (*refreshvector) (void);
\r
205 unsigned screenstart[3] =
\r
206 {0,SCREENSPACE,SCREENSPACE*2};
\r
208 unsigned xpanmask; // prevent panning to odd pixels
\r
210 unsigned screenpage; // screen currently being displayed
\r
211 unsigned otherpage;
\r
213 #if GRMODE == EGAGR
\r
214 unsigned tilecache[NUMTILE16];
\r
217 spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],
\r
220 animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;
\r
224 eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];
\r
227 =============================================================================
\r
231 =============================================================================
\r
234 void RFL_NewTile (unsigned updateoffset);
\r
235 void RFL_MaskForegroundTiles (void);
\r
236 void RFL_UpdateTiles (void);
\r
238 void RFL_BoundScroll (int x, int y);//++++??
\r
239 void RFL_CalcOriginStuff (long x, long y);
\r
240 void RFL_ClearScrollBlocks (void);//++++??
\r
241 void RFL_InitSpriteList (void);
\r
242 void RFL_InitAnimList (void);
\r
243 void RFL_CheckForAnimTile (unsigned x, unsigned y);
\r
244 void RFL_AnimateTiles (void);
\r
245 void RFL_RemoveAnimsOnX (unsigned x);
\r
246 void RFL_RemoveAnimsOnY (unsigned y);
\r
247 void RFL_EraseBlocks (void);
\r
248 void RFL_UpdateSprites (void);
\r
252 =============================================================================
\r
254 GRMODE INDEPENDANT ROUTINES
\r
256 =============================================================================
\r
261 =====================
\r
265 =====================
\r
268 static char *ParmStrings[] = {"comp",""};
\r
270 void RF_Startup (void)
\r
273 unsigned *blockstart;
\r
275 if (grmode == EGAGR)
\r
276 for (i = 1;i < _argc;i++)
\r
277 if (US_CheckParm(_argv[i],ParmStrings) == 0)
\r
279 compatability = true;
\r
283 for (i=0;i<PORTTILESHIGH;i++)
\r
284 uwidthtable[i] = UPDATEWIDE*i;
\r
286 originxmin = originymin = MAPBORDER*TILEGLOBAL;
\r
288 eraselistptr[0] = &eraselist[0][0];
\r
289 eraselistptr[1] = &eraselist[1][0];
\r
293 if (grmode == EGAGR)
\r
297 baseupdatestart[0] = &update[0][UPDATESPARESIZE];
\r
298 baseupdatestart[1] = &update[1][UPDATESPARESIZE];
\r
302 displayofs = screenstart[screenpage];
\r
303 bufferofs = screenstart[otherpage];
\r
304 masterofs = screenstart[2];
\r
306 updateptr = baseupdatestart[otherpage];
\r
308 blockstart = &blockstarts[0];
\r
309 for (y=0;y<UPDATEHIGH;y++)
\r
310 for (x=0;x<UPDATEWIDE;x++)
\r
311 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
313 xpanmask = 6; // dont pan to odd pixels
\r
316 else if (grmode == CGAGR)
\r
320 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
\r
323 masterofs = 0x8000;
\r
325 blockstart = &blockstarts[0];
\r
326 for (y=0;y<UPDATEHIGH;y++)
\r
327 for (x=0;x<UPDATEWIDE;x++)
\r
328 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
336 =====================
\r
340 =====================
\r
343 void RF_Shutdown (void)
\r
348 //===========================================================================
\r
352 =====================
\r
356 = Sets bufferofs,displayofs, and masterofs to regular values, for the
\r
357 = occasions when you have moved them around manually
\r
359 =====================
\r
362 void RF_FixOfs (global_game_variables_t *gvar)
\r
364 // if (grmode == EGAGR)
\r
368 gvar->video.ofs.pan.panx = gvar->video.ofs.pan.pany = gvar->video.ofs.pan.pansx = gvar->video.ofs.pan.pansy = gvar->video.ofs.pan.panadjust = 0;
\r
369 displayofs = screenstart[screenpage];
\r
370 bufferofs = screenstart[otherpage];
\r
371 masterofs = screenstart[2];
\r
372 /*++++ VL_SetScreen (displayofs,0);
\r
377 masterofs = 0x8000;
\r
382 //===========================================================================
\r
385 =====================
\r
389 = Makes some convienient calculations based on maphead->
\r
391 =====================
\r
394 void RF_NewMap (void)
\r
397 unsigned spot,*table;
\r
399 mapwidth = mapheaderseg[mapon]->width;
\r
400 mapbyteswide = 2*mapwidth;
\r
401 mapheight = mapheaderseg[mapon]->height;
\r
402 mapwordsextra = mapwidth-PORTTILESWIDE;
\r
403 mapbytesextra = 2*mapwordsextra;
\r
406 // make a lookup table for the maps left edge
\r
408 if (mapheight > MAXMAPHEIGHT)
\r
409 Quit (gvar, "RF_NewMap: Map too tall!");
\r
411 for (i=0;i<mapheight;i++)
\r
413 mapbwidthtable[i] = spot;
\r
414 spot += mapbyteswide;
\r
418 // fill in updatemapofs with the new width info
\r
420 table = &updatemapofs[0];
\r
421 for (y=0;y<PORTTILESHIGH;y++)
\r
422 for (x=0;x<UPDATEWIDE;x++)
\r
423 *table++ = mapbwidthtable[y]+x*2;
\r
426 // the y max value clips off the bottom half of a tile so a map that is
\r
427 // 13 + MAPBORDER*2 tile high will not scroll at all vertically
\r
429 originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
\r
430 originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
\r
431 if (originxmax<originxmin) // for very small maps
\r
432 originxmax=originxmin;
\r
433 if (originymax<originymin)
\r
434 originymax=originymin;
\r
437 // clear out the lists
\r
439 RFL_InitSpriteList ();
\r
440 RFL_InitAnimList ();
\r
441 RFL_ClearScrollBlocks ();
\r
442 RF_SetScrollBlock (0,MAPBORDER-1,true);
\r
443 RF_SetScrollBlock (0,mapheight-MAPBORDER,true);
\r
444 RF_SetScrollBlock (MAPBORDER-1,0,false);
\r
445 RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);
\r
448 lasttimecount = TimeCount; // setup for adaptive timing
\r
452 //===========================================================================
\r
455 ==========================
\r
457 = RF_MarkTileGraphics
\r
459 = Goes through mapplane[0/1] and marks all background/foreground tiles
\r
460 = needed, then follows all animation sequences to make sure animated
\r
461 = tiles get all the stages. Every unique animating tile is given an
\r
462 = entry in allanims[], so every instance of that tile will animate at the
\r
463 = same rate. The info plane for each animating tile will hold a pointer
\r
464 = into allanims[], therefore you can't have both an animating foreground
\r
465 = and background tile in the same spot!
\r
467 ==========================
\r
470 void RF_MarkTileGraphics (void)
\r
473 int tile,next,anims;
\r
474 unsigned far *start,far *end,far *info;
\r
475 unsigned i,tilehigh;
\r
477 memset (allanims,0,sizeof(allanims));
\r
480 size = mapwidth*mapheight;
\r
483 // background plane
\r
485 start = mapsegs[0];
\r
491 if (tile>=0) // <0 is a tile that is never drawn
\r
493 CA_MarkGrChunk(STARTTILE16+tile);
\r
494 if (tinf[ANIM+tile])
\r
496 // this tile will animated
\r
498 for (i=0;i<numanimchains;i++)
\r
499 if (allanims[i].current == tile)
\r
501 *info = (unsigned)&allanims[i];
\r
505 // new chain of animating tiles
\r
507 if (i>=MAXANIMTYPES)
\r
508 Quit (gvar, "RF_MarkTileGraphics: Too many unique animated tiles!");
\r
509 allanims[i].current = tile;
\r
510 allanims[i].count = tinf[SPEED+tile];
\r
512 *info = (unsigned)&allanims[i];
\r
516 next = tile+(signed char)(tinf[ANIM+tile]);
\r
517 while (next != tile)
\r
519 CA_MarkGrChunk(STARTTILE16+next);
\r
520 next += (signed char)(tinf[ANIM+next]);
\r
522 Quit (gvar, "MarkTileGraphics: Unending animation!");
\r
529 } while (start<end);
\r
532 // foreground plane
\r
534 start = mapsegs[1];
\r
540 if (tile>=0) // <0 is a tile that is never drawn
\r
542 CA_MarkGrChunk(STARTTILE16M+tile);
\r
543 if (tinf[MANIM+tile])
\r
545 // this tile will animated
\r
547 tilehigh = tile | 0x8000; // foreground tiles have high bit
\r
548 for (i=0;i<numanimchains;i++)
\r
549 if (allanims[i].current == tilehigh)
\r
551 *info = (unsigned)&allanims[i];
\r
555 // new chain of animating tiles
\r
557 if (i>=MAXANIMTYPES)
\r
558 Quit (gvar, "RF_MarkTileGraphics: Too many unique animated tiles!");
\r
559 allanims[i].current = tilehigh;
\r
560 allanims[i].count = tinf[MSPEED+tile];
\r
562 *info = (unsigned)&allanims[i];
\r
566 next = tile+(signed char)(tinf[MANIM+tile]);
\r
567 while (next != tile)
\r
569 CA_MarkGrChunk(STARTTILE16M+next);
\r
570 next += (signed char)(tinf[MANIM+next]);
\r
572 Quit (gvar, "MarkTileGraphics: Unending animation!");
\r
579 } while (start<end);
\r
583 //===========================================================================
\r
587 =========================
\r
591 = Call to clear out the entire animating tile list and return all of them to
\r
594 =========================
\r
597 void RFL_InitAnimList (void)
\r
601 animfreeptr = &animarray[0];
\r
603 for (i=0;i<MAXANIMTILES-1;i++)
\r
604 animarray[i].nexttile = &animarray[i+1];
\r
606 animarray[i].nexttile = NULL;
\r
608 animhead = NULL; // nothing in list
\r
613 ====================
\r
615 = RFL_CheckForAnimTile
\r
617 ====================
\r
620 void RFL_CheckForAnimTile (unsigned x, unsigned y)
\r
622 unsigned tile,offset,speed,lasttime,thistime,timemissed;
\r
624 animtiletype *anim,*next;
\r
626 // the info plane of each animating tile has a near pointer into allanims[]
\r
627 // which gives the current state of all concurrently animating tiles
\r
629 offset = mapbwidthtable[y]/2+x;
\r
634 map = mapsegs[0]+offset;
\r
636 if (tinf[ANIM+tile] && tinf[SPEED+tile])
\r
639 Quit (gvar, "RF_CheckForAnimTile: No free spots in tilearray!");
\r
640 anim = animfreeptr;
\r
641 animfreeptr = animfreeptr->nexttile;
\r
642 next = animhead; // stick it at the start of the list
\r
645 next->prevptr = &anim->nexttile;
\r
646 anim->nexttile = next;
\r
647 anim->prevptr = &animhead;
\r
652 anim->mapplane = map;
\r
653 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
659 map = mapsegs[1]+offset;
\r
661 if (tinf[MANIM+tile] && tinf[MSPEED+tile])
\r
664 Quit (gvar, "RF_CheckForAnimTile: No free spots in tilearray!");
\r
665 anim = animfreeptr;
\r
666 animfreeptr = animfreeptr->nexttile;
\r
667 next = animhead; // stick it at the start of the list
\r
670 next->prevptr = &anim->nexttile;
\r
671 anim->nexttile = next;
\r
672 anim->prevptr = &animhead;
\r
677 anim->mapplane = map;
\r
678 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
685 ====================
\r
687 = RFL_RemoveAnimsOnX
\r
689 ====================
\r
692 void RFL_RemoveAnimsOnX (unsigned x)
\r
694 animtiletype *current,*next;
\r
696 current = animhead;
\r
699 if (current->x == x)
\r
701 *(void **)current->prevptr = current->nexttile;
\r
702 if (current->nexttile)
\r
703 current->nexttile->prevptr = current->prevptr;
\r
704 next = current->nexttile;
\r
705 current->nexttile = animfreeptr;
\r
706 animfreeptr = current;
\r
710 current = current->nexttile;
\r
716 ====================
\r
718 = RFL_RemoveAnimsOnY
\r
720 ====================
\r
723 void RFL_RemoveAnimsOnY (unsigned y)
\r
725 animtiletype *current,*next;
\r
727 current = animhead;
\r
730 if (current->y == y)
\r
732 *(void **)current->prevptr = current->nexttile;
\r
733 if (current->nexttile)
\r
734 current->nexttile->prevptr = current->prevptr;
\r
735 next = current->nexttile;
\r
736 current->nexttile = animfreeptr;
\r
737 animfreeptr = current;
\r
741 current = current->nexttile;
\r
747 ====================
\r
749 = RFL_RemoveAnimsInBlock
\r
751 ====================
\r
754 void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)
\r
756 animtiletype *current,*next;
\r
758 current = animhead;
\r
761 if (current->x - x < width && current->y - y < height)
\r
763 *(void **)current->prevptr = current->nexttile;
\r
764 if (current->nexttile)
\r
765 current->nexttile->prevptr = current->prevptr;
\r
766 next = current->nexttile;
\r
767 current->nexttile = animfreeptr;
\r
768 animfreeptr = current;
\r
772 current = current->nexttile;
\r
778 ====================
\r
782 ====================
\r
785 void RFL_AnimateTiles (void)
\r
787 animtiletype *current;
\r
788 unsigned updateofs,tile,x,y;
\r
792 // animate the lists of tiles
\r
794 anim = &allanims[0];
\r
795 while (anim->current)
\r
798 while ( anim->count < 1)
\r
800 if (anim->current & 0x8000)
\r
802 tile = anim->current & 0x7fff;
\r
803 tile += (signed char)tinf[MANIM+tile];
\r
804 anim->count += tinf[MSPEED+tile];
\r
809 tile = anim->current;
\r
810 tile += (signed char)tinf[ANIM+tile];
\r
811 anim->count += tinf[SPEED+tile];
\r
813 anim->current = tile;
\r
820 // traverse the list of animating tiles
\r
822 current = animhead;
\r
825 tile =current->chain->current;
\r
826 if ( tile != current->tile)
\r
828 // tile has animated
\r
830 // remove tile from master screen cache,
\r
831 // change a tile to its next state, set the structure up for
\r
832 // next animation, and post an update region to both update pages
\r
834 current->tile = tile;
\r
836 *(current->mapplane) = tile & 0x7fff; // change in map
\r
838 #if GRMODE == EGAGR
\r
839 if (tile<0x8000) // background
\r
840 tilecache[tile] = 0;
\r
843 x = current->x-originxtile;
\r
844 y = current->y-originytile;
\r
846 if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)
\r
847 Quit (gvar, "RFL_AnimateTiles: Out of bounds!");
\r
849 updateofs = uwidthtable[y] + x;
\r
850 RFL_NewTile(updateofs); // puts "1"s in both pages
\r
852 current = current->nexttile;
\r
857 //===========================================================================
\r
860 =========================
\r
862 = RFL_InitSpriteList
\r
864 = Call to clear out the entire sprite list and return all of them to
\r
867 =========================
\r
870 void RFL_InitSpriteList (void)
\r
874 spritefreeptr = &spritearray[0];
\r
875 for (i=0;i<MAXSPRITES-1;i++)
\r
876 spritearray[i].nextsprite = &spritearray[i+1];
\r
878 spritearray[i].nextsprite = NULL;
\r
880 // NULL in all priority levels
\r
882 memset (prioritystart,0,sizeof(prioritystart));
\r
885 //===========================================================================
\r
890 = RFL_CalcOriginStuff
\r
892 = Calculate all the global variables for a new position
\r
893 = Long parms so position can be clipped to a maximum near 64k
\r
898 void RFL_CalcOriginStuff (long x, long y)
\r
902 else if (x>originxmax)
\r
907 else if (y>originymax)
\r
912 originxtile = originxglobal>>G_T_SHIFT;
\r
913 originytile = originyglobal>>G_T_SHIFT;
\r
914 originxscreen = originxtile<<SX_T_SHIFT;
\r
915 originyscreen = originytile<<SY_T_SHIFT;
\r
916 originmap = mapbwidthtable[originytile] + originxtile*2;
\r
918 //#if GRMODE == EGAGR
\r
919 gvar->video.ofs.pan.panx = (originxglobal>>G_P_SHIFT) & 15;
\r
920 gvar->video.ofs.pan.pansx = gvar->video.ofs.pan.panx & 8;
\r
921 gvar->video.ofs.pan.pany = gvar->video.ofs.pan.pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
922 gvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.panx/8 + gvar->video.ofs.ylookup[gvar->video.ofs.pan.pany];
\r
925 #if GRMODE == CGAGR
\r
926 gvar->video.ofs.pan.panx = (originxglobal>>G_P_SHIFT) & 15;
\r
927 gvar->video.ofs.pan.pansx = gvar->video.ofs.pan.panx & 12;
\r
928 gvar->video.ofs.pan.pany = gvar->video.ofs.pan.pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
929 gvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.pansx/4 + gvar->video.ofs.ylookup[gvar->video.ofs.pan.pansy];
\r
939 = RFL_ClearScrollBlocks
\r
944 void RFL_ClearScrollBlocks (void)
\r
946 gvar->video.ofs.pan.hscrollblocks = gvar->video.ofs.pan.vscrollblocks = 0;
\r
953 = RF_SetScrollBlock
\r
955 = Sets a horizontal or vertical scroll block
\r
956 = a horizontal block is ----, meaning it blocks up/down movement
\r
961 void RF_SetScrollBlock (int x, int y, boolean horizontal)
\r
965 gvar->video.ofs.pan.hscrolledge[gvar->video.ofs.pan.hscrollblocks] = y;
\r
966 if (gvar->video.ofs.pan.hscrollblocks++ == MAXSCROLLEDGES)
\r
967 Quit (gvar, "RF_SetScrollBlock: Too many horizontal scroll blocks");
\r
971 gvar->video.ofs.pan.vscrolledge[gvar->video.ofs.pan.vscrollblocks] = x;
\r
972 if (gvar->video.ofs.pan.vscrollblocks++ == MAXSCROLLEDGES)
\r
973 Quit (gvar, "RF_SetScrollBlock: Too many vertical scroll blocks");
\r
983 = Bound a given x/y movement to scroll blocks
\r
988 void RFL_BoundScroll (int x, int y)
\r
990 int check,newxtile,newytile;
\r
992 originxglobal += x;
\r
993 originyglobal += y;
\r
995 newxtile= originxglobal >> G_T_SHIFT;
\r
996 newytile = originyglobal >> G_T_SHIFT;
\r
1000 newxtile+=SCREENTILESWIDE;
\r
1001 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1002 if (gvar->video.ofs.pan.vscrolledge[check] == newxtile)
\r
1004 originxglobal = originxglobal&0xff00;
\r
1010 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1011 if (gvar->video.ofs.pan.vscrolledge[check] == newxtile)
\r
1013 originxglobal = (originxglobal&0xff00)+0x100;
\r
1021 newytile+=SCREENTILESHIGH;
\r
1022 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1023 if (gvar->video.ofs.pan.hscrolledge[check] == newytile)
\r
1025 originyglobal = originyglobal&0xff00;
\r
1031 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1032 if (gvar->video.ofs.pan.hscrolledge[check] == newytile)
\r
1034 originyglobal = (originyglobal&0xff00)+0x100;
\r
1040 RFL_CalcOriginStuff (originxglobal, originyglobal);
\r
1044 //===========================================================================
\r
1047 =====================
\r
1049 = RF_SetRefreshHook
\r
1051 =====================
\r
1054 void RF_SetRefreshHook (void (*func) (void) )
\r
1056 refreshvector = func;
\r
1060 //===========================================================================
\r
1067 = Bring a new row of tiles onto the port, spawning animating tiles
\r
1072 void RFL_NewRow (int dir)
\r
1074 unsigned count,updatespot,updatestep;
\r
1075 int x,y,xstep,ystep;
\r
1079 case 0: // top row
\r
1086 count = PORTTILESWIDE;
\r
1089 case 1: // right row
\r
1090 updatespot = PORTTILESWIDE-1;
\r
1091 updatestep = UPDATEWIDE;
\r
1092 x = originxtile + PORTTILESWIDE-1;
\r
1096 count = PORTTILESHIGH;
\r
1099 case 2: // bottom row
\r
1100 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1103 y = originytile + PORTTILESHIGH-1;
\r
1106 count = PORTTILESWIDE;
\r
1109 case 3: // left row
\r
1111 updatestep = UPDATEWIDE;
\r
1116 count = PORTTILESHIGH;
\r
1119 Quit (gvar, "RFL_NewRow: Bad dir!");
\r
1124 RFL_NewTile(updatespot);
\r
1125 RFL_CheckForAnimTile (x,y);
\r
1126 updatespot+=updatestep;
\r
1132 //===========================================================================
\r
1135 =====================
\r
1139 =====================
\r
1142 void RF_ForceRefresh (void)
\r
1144 RF_NewPosition (originxglobal,originyglobal);
\r
1149 //===========================================================================
\r
1152 =====================
\r
1156 = Copies a block of tiles (all three planes) from one point
\r
1157 = in the map to another, accounting for animating tiles
\r
1159 =====================
\r
1162 void RF_MapToMap (unsigned srcx, unsigned srcy,
\r
1163 unsigned destx, unsigned desty,
\r
1164 unsigned width, unsigned height)
\r
1167 unsigned source,destofs,xspot,yspot;
\r
1168 unsigned linedelta,p0,p1,p2,updatespot;
\r
1169 unsigned far *source0, far *source1, far *source2;
\r
1170 unsigned far *dest0, far *dest1, far *dest2;
\r
1173 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1175 source = mapbwidthtable[srcy]/2 + srcx;
\r
1177 source0 = mapsegs[0]+source;
\r
1178 source1 = mapsegs[1]+source;
\r
1179 source2 = mapsegs[2]+source;
\r
1181 destofs = mapbwidthtable[desty]/2 + destx;
\r
1182 destofs -= source;
\r
1184 linedelta = mapwidth - width;
\r
1186 for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
\r
1187 for (x=0;x<width;x++,source0++,source1++,source2++)
\r
1193 dest0 = source0 + destofs;
\r
1194 dest1 = source1 + destofs;
\r
1195 dest2 = source2 + destofs;
\r
1198 // only make a new tile if it is different
\r
1200 if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
\r
1211 // if tile is on the view port
\r
1213 xspot = destx+x-originxtile;
\r
1214 yspot = desty+y-originytile;
\r
1215 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1219 updatespot = uwidthtable[yspot]+xspot;
\r
1220 RFL_NewTile(updatespot);
\r
1222 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1227 //===========================================================================
\r
1231 =====================
\r
1235 = Copies a string of tiles from main memory to the map,
\r
1236 = accounting for animating tiles
\r
1238 =====================
\r
1241 void RF_MemToMap (unsigned far *source, unsigned plane,
\r
1242 unsigned destx, unsigned desty,
\r
1243 unsigned width, unsigned height)
\r
1246 unsigned xspot,yspot;
\r
1247 unsigned linedelta,updatespot;
\r
1248 unsigned far *dest,old,new;
\r
1251 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1253 dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
\r
1255 linedelta = mapwidth - width;
\r
1257 for (y=0;y<height;y++,dest+=linedelta)
\r
1258 for (x=0;x<width;x++)
\r
1271 xspot = destx+x-originxtile;
\r
1272 yspot = desty+y-originytile;
\r
1273 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1277 updatespot = uwidthtable[yspot]+xspot;
\r
1278 RFL_NewTile(updatespot);
\r
1280 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1285 //===========================================================================
\r
1289 =====================
\r
1291 = RFL_BoundNewOrigin
\r
1293 = Copies a string of tiles from main memory to the map,
\r
1294 = accounting for animating tiles
\r
1296 =====================
\r
1299 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
\r
1304 // calculate new origin related globals
\r
1306 if (orgx<originxmin)
\r
1308 else if (orgx>originxmax)
\r
1311 if (orgy<originymin)
\r
1313 else if (orgy>originymax)
\r
1316 originxtile = orgx>>G_T_SHIFT;
\r
1317 originytile = orgy>>G_T_SHIFT;
\r
1319 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1321 edge = gvar->video.ofs.pan.vscrolledge[check];
\r
1322 if (edge>=originxtile && edge <=originxtile+10)
\r
1324 orgx = (edge+1)*TILEGLOBAL;
\r
1327 if (edge>=originxtile+11 && edge <=originxtile+20)
\r
1329 orgx = (edge-20)*TILEGLOBAL;
\r
1334 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1336 edge = gvar->video.ofs.pan.hscrolledge[check];
\r
1337 if (edge>=originytile && edge <=originytile+6)
\r
1339 orgy = (edge+1)*TILEGLOBAL;
\r
1342 if (edge>=originytile+7 && edge <=originytile+13)
\r
1344 orgy = (edge-13)*TILEGLOBAL;
\r
1350 RFL_CalcOriginStuff (orgx,orgy);
\r
1354 //===========================================================================
\r
1357 =====================
\r
1361 = Posts erase blocks to clear a certain area of the screen to the master
\r
1362 = screen, to erase text or something draw directly to the screen
\r
1364 = Parameters in pixels, but erasure is byte bounded
\r
1366 =====================
\r
1369 void RF_ClearBlock (int x, int y, int width, int height)
\r
1371 eraseblocktype block;
\r
1373 #if GRMODE == EGAGR
\r
1374 block.screenx = x/8+originxscreen;
\r
1375 block.screeny = y+originyscreen;
\r
1376 block.width = (width+(x&7)+7)/8;
\r
1377 block.height = height;
\r
1378 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1379 memcpy (eraselistptr[1]++,&block,sizeof(block));
\r
1382 #if GRMODE == CGAGR
\r
1383 block.screenx = x/4+originxscreen;
\r
1384 block.screeny = y+originyscreen;
\r
1385 block.width = (width+(x&3)+3)/4;
\r
1386 block.height = height;
\r
1387 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1392 //===========================================================================
\r
1395 =====================
\r
1399 = Causes a number of tiles to be redrawn to the master screen and updated
\r
1401 = Parameters in pixels, but erasure is tile bounded
\r
1403 =====================
\r
1406 void RF_RedrawBlock (int x, int y, int width, int height)
\r
1408 int xx,yy,xl,xh,yl,yh;
\r
1410 xl=(x+gvar->video.ofs.pan.panx)/16;
\r
1411 xh=(x+gvar->video.ofs.pan.panx+width+15)/16;
\r
1412 yl=(y+gvar->video.ofs.pan.pany)/16;
\r
1413 yh=(y+gvar->video.ofs.pan.pany+height+15)/16;
\r
1414 for (yy=yl;yy<=yh;yy++)
\r
1415 for (xx=xl;xx<=xh;xx++)
\r
1416 RFL_NewTile (yy*UPDATEWIDE+xx);
\r
1420 //===========================================================================
\r
1423 =====================
\r
1427 =====================
\r
1430 void RF_CalcTics (void)
\r
1432 long newtime,oldtimecount;
\r
1433 word TimeCount = *clockw;
\r
1436 // calculate tics since last refresh for adaptive timing
\r
1438 if (lasttimecount > TimeCount)
\r
1439 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1441 /*++++ if (DemoMode) // demo recording and playback needs
\r
1442 { // to be constant
\r
1444 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1446 oldtimecount = lasttimecount;
\r
1447 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1449 lasttimecount = oldtimecount + DEMOTICS;
\r
1450 TimeCount = lasttimecount + DEMOTICS;
\r
1456 // non demo, so report actual time
\r
1460 newtime = TimeCount;
\r
1461 tics = newtime-lasttimecount;
\r
1462 } while (tics<MINTICS);
\r
1463 lasttimecount = newtime;
\r
1466 strcpy (scratch,"\tTics:");
\r
1467 itoa (tics,str,10);
\r
1468 strcat (scratch,str);
\r
1469 strcat (scratch,"\n");
\r
1470 write (profilehandle,scratch,strlen(scratch));
\r
1475 TimeCount -= (tics-MAXTICS);
\r
1482 =============================================================================
\r
1484 EGA specific routines
\r
1486 =============================================================================
\r
1489 #if GRMODE == EGAGR
\r
1492 =====================
\r
1494 = RF_FindFreeBuffer
\r
1496 = Finds the start of unused, non visable buffer space
\r
1498 =====================
\r
1501 unsigned RF_FindFreeBuffer (void)
\r
1503 unsigned spot,i,j;
\r
1508 spot = screenstart[i]+SCREENSPACE;
\r
1511 if (spot == screenstart[j])
\r
1520 return 0; // never get here...
\r
1523 //===========================================================================
\r
1526 =====================
\r
1528 = RF_NewPosition EGA
\r
1530 =====================
\r
1533 void RF_NewPosition (unsigned x, unsigned y)
\r
1536 byte *page0ptr,*page1ptr;
\r
1537 unsigned updatenum;
\r
1539 RFL_BoundNewOrigin (x,y);
\r
1541 // calculate new origin related globals
\r
1543 RFL_CalcOriginStuff (x,y);
\r
1546 // clear out all animating tiles
\r
1548 RFL_InitAnimList ();
\r
1551 // set up the new update arrays at base position
\r
1553 memset (tilecache,0,sizeof(tilecache)); // old cache is invalid
\r
1555 updatestart[0] = baseupdatestart[0];
\r
1556 updatestart[1] = baseupdatestart[1];
\r
1557 updateptr = updatestart[otherpage];
\r
1559 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1560 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1562 updatenum = 0; // start at first visable tile
\r
1564 for (my=0;my<PORTTILESHIGH;my++)
\r
1566 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1568 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1569 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1573 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1574 page0ptr+=(PORTTILESWIDE+1);
\r
1575 page1ptr+=(PORTTILESWIDE+1);
\r
1577 *(word *)(page0ptr-PORTTILESWIDE)
\r
1578 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1581 //===========================================================================
\r
1588 = Uncache the trailing row of tiles
\r
1593 void RFL_OldRow (unsigned updatespot,unsigned count,unsigned step)
\r
1596 asm mov si,[updatespot] // pointer inside each map plane
\r
1597 asm mov cx,[count] // number of tiles to clear
\r
1598 asm mov dx,[step] // move to next tile
\r
1599 asm mov es,[WORD PTR mapsegs] // background plane
\r
1600 asm mov ds,[WORD PTR mapsegs+2] // foreground plane
\r
1605 asm jnz blockok // if a foreground tile, block wasn't cached
\r
1606 asm mov bx,[es:si]
\r
1608 asm mov [WORD PTR ss:tilecache+bx],0 //tile is no longer in master screen cache
\r
1611 asm loop clearcache
\r
1620 =====================
\r
1624 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1625 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1626 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1627 = more than one tile per refresh is a bad idea!).
\r
1629 =====================
\r
1632 void RF_Scroll (int x, int y)
\r
1634 long neworgx,neworgy;
\r
1635 int i,deltax,deltay,absdx,absdy;
\r
1636 int oldxt,oldyt,move,yy;
\r
1637 unsigned updatespot;
\r
1638 byte *update0,*update1;
\r
1639 unsigned oldgvar->video.ofs.pan.panx,oldgvar->video.ofs.pan.panadjust,oldoriginmap,oldscreen,newscreen,screencopy;
\r
1642 oldxt = originxtile;
\r
1643 oldyt = originytile;
\r
1644 oldoriginmap = originmap;
\r
1645 oldgvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.panadjust;
\r
1646 oldgvar->video.ofs.pan.panx = gvar->video.ofs.pan.panx;
\r
1648 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
1650 deltax = originxtile - oldxt;
\r
1651 absdx = abs(deltax);
\r
1652 deltay = originytile - oldyt;
\r
1653 absdy = abs(deltay);
\r
1655 if (absdx>1 || absdy>1)
\r
1658 // scrolled more than one tile, so start from scratch
\r
1660 RF_NewPosition(originxglobal,originyglobal);
\r
1664 if (!absdx && !absdy)
\r
1665 return; // the screen has not scrolled an entire tile
\r
1669 // adjust screens and handle SVGA crippled compatability mode
\r
1671 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1674 screenstart[i]+= screenmove;
\r
1675 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1678 // move the screen to the opposite end of the buffer
\r
1680 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1681 oldscreen = screenstart[i] - screenmove;
\r
1682 newscreen = oldscreen + screencopy;
\r
1683 screenstart[i] = newscreen + screenmove;
\r
1684 //++++ VW_ScreenToScreen (oldscreen,newscreen,
\r
1685 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1687 //++++ if (i==screenpage)
\r
1688 //++++ VL_SetScreen(newscreen+oldgvar->video.ofs.pan.panadjust,oldgvar->video.ofs.pan.panx & xpanmask);
\r
1691 bufferofs = screenstart[otherpage];
\r
1692 displayofs = screenstart[screenpage];
\r
1693 masterofs = screenstart[2];
\r
1697 // float the update regions
\r
1701 move += UPDATEWIDE;
\r
1702 else if (deltay==-1)
\r
1703 move -= UPDATEWIDE;
\r
1705 updatestart[0]+=move;
\r
1706 updatestart[1]+=move;
\r
1709 // draw the new tiles just scrolled on to the master screen, and
\r
1710 // mark them as needing to be copied to each screen next refreshes
\r
1711 // Make sure a zero is at the end of each row in update
\r
1718 RFL_NewRow (1); // new right row
\r
1719 RFL_OldRow (oldoriginmap,PORTTILESHIGH,mapbyteswide);
\r
1720 RFL_RemoveAnimsOnX (originxtile-1);
\r
1724 RFL_NewRow (3); // new left row
\r
1725 RFL_OldRow (oldoriginmap+(PORTTILESWIDE-1)*2,PORTTILESHIGH
\r
1727 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1730 update0 = updatestart[0]+PORTTILESWIDE;
\r
1731 update1 = updatestart[1]+PORTTILESWIDE;
\r
1732 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1734 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1735 update0+=UPDATEWIDE;
\r
1736 update1+=UPDATEWIDE;
\r
1740 //----------------
\r
1746 RFL_NewRow (2); // new bottom row
\r
1747 RFL_OldRow (oldoriginmap,PORTTILESWIDE,2);
\r
1748 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1749 RFL_RemoveAnimsOnY (originytile-1);
\r
1753 RFL_NewRow (0); // new top row
\r
1754 RFL_OldRow (oldoriginmap+mapbwidthtable[PORTTILESHIGH-1]
\r
1755 ,PORTTILESWIDE,2);
\r
1757 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1760 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1761 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1764 //----------------
\r
1767 // place a new terminator
\r
1769 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1770 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1771 *update0++ = *update1++ = 0;
\r
1772 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1775 //===========================================================================
\r
1778 =====================
\r
1780 = RF_PlaceSprite EGA
\r
1782 =====================
\r
1785 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1786 unsigned spritenumber, drawtype draw, int priority)
\r
1788 spritelisttype register *sprite,*next;
\r
1789 spritetabletype far *spr;
\r
1790 spritetype _seg *block;
\r
1791 unsigned shift,pixx;
\r
1792 char str[80],str2[10];
\r
1794 if (!spritenumber || spritenumber == (unsigned)-1)
\r
1796 RF_RemoveSprite (user);
\r
1800 sprite = (spritelisttype *)*user;
\r
1804 // sprite allready exists in the list, so we can use it's block
\r
1807 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1808 // both pages may not need to be erased if the sprite just changed last frame
\r
1810 if (sprite->updatecount<2)
\r
1812 if (!sprite->updatecount)
\r
1813 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1814 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1817 if (priority != sprite->priority)
\r
1819 // sprite mvoed to another priority, so unlink the old one and
\r
1820 // relink it in the new priority
\r
1822 next = sprite->nextsprite; // cut old links
\r
1824 next->prevptr = sprite->prevptr;
\r
1825 *sprite->prevptr = next;
\r
1831 // this is a brand new sprite, so allocate a block from the array
\r
1833 if (!spritefreeptr)
\r
1834 Quit (gvar, "RF_PlaceSprite: No free spots in spritearray!");
\r
1836 sprite = spritefreeptr;
\r
1837 spritefreeptr = spritefreeptr->nextsprite;
\r
1840 next = prioritystart[priority]; // stick it in new spot
\r
1842 next->prevptr = &sprite->nextsprite;
\r
1843 sprite->nextsprite = next;
\r
1844 prioritystart[priority] = sprite;
\r
1845 sprite->prevptr = &prioritystart[priority];
\r
1849 // write the new info to the sprite
\r
1851 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1852 block = (spritetype _seg *)grsegs[spritenumber];
\r
1856 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");
\r
1857 itoa (spritenumber,str2,10);
\r
1858 strcat (str,str2);
\r
1862 globaly+=spr->orgy;
\r
1863 globalx+=spr->orgx;
\r
1865 pixx = globalx >> G_SY_SHIFT;
\r
1866 shift = (pixx&7)/2;
\r
1868 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1869 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1870 sprite->width = block->width[shift];
\r
1871 sprite->height = spr->height;
\r
1872 sprite->grseg = spritenumber;
\r
1873 sprite->sourceofs = block->sourceoffset[shift];
\r
1874 sprite->planesize = block->planesize[shift];
\r
1875 sprite->draw = draw;
\r
1876 sprite->priority = priority;
\r
1877 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1878 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1879 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1880 - sprite->tilex + 1;
\r
1881 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1882 - sprite->tiley + 1;
\r
1884 sprite->updatecount = 2; // draw on next two refreshes
\r
1886 // save the sprite pointer off in the user's pointer so it can be moved
\r
1892 //===========================================================================
\r
1895 =====================
\r
1897 = RF_RemoveSprite EGA
\r
1899 =====================
\r
1902 void RF_RemoveSprite (void **user)
\r
1904 spritelisttype *sprite,*next;
\r
1906 sprite = (spritelisttype *)*user;
\r
1911 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1912 // both pages may not need to be erased if the sprite just changed last frame
\r
1914 if (sprite->updatecount<2)
\r
1916 if (!sprite->updatecount)
\r
1917 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1918 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1922 // unlink the sprite node
\r
1924 next = sprite->nextsprite;
\r
1925 if (next) // if (!next), sprite is last in chain
\r
1926 next->prevptr = sprite->prevptr;
\r
1927 *sprite->prevptr = next;
\r
1930 // add it back to the free list
\r
1932 sprite->nextsprite = spritefreeptr;
\r
1933 spritefreeptr = sprite;
\r
1936 // null the users pointer, so next time that actor gets placed, it will
\r
1937 // allocate a new block
\r
1944 //===========================================================================
\r
1948 ====================
\r
1950 = RFL_EraseBlocks EGA
\r
1952 = Write mode 1 should be set
\r
1954 ====================
\r
1957 void RFL_EraseBlocks (void)
\r
1959 eraseblocktype *block,*done;
\r
1960 int screenxh,screenyh;
\r
1961 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
1963 unsigned updatedelta;
\r
1964 unsigned erasecount;
\r
1970 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1972 done = eraselistptr[otherpage];
\r
1974 while (block != done)
\r
1978 // clip the block to the current screen view
\r
1980 block->screenx -= originxscreen;
\r
1981 block->screeny -= originyscreen;
\r
1983 if (block->screenx < 0)
\r
1985 block->width += block->screenx;
\r
1986 if (block->width<1)
\r
1988 block->screenx = 0;
\r
1991 if (block->screeny < 0)
\r
1993 block->height += block->screeny;
\r
1994 if (block->height<1)
\r
1996 block->screeny = 0;
\r
1999 screenxh = block->screenx + block->width;
\r
2000 screenyh = block->screeny + block->height;
\r
2002 if (screenxh > EGAPORTSCREENWIDE)
\r
2004 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
2005 screenxh = block->screenx + block->width;
\r
2008 if (screenyh > PORTSCREENHIGH)
\r
2010 block->height = PORTSCREENHIGH-block->screeny;
\r
2011 screenyh = block->screeny + block->height;
\r
2014 if (block->width<1 || block->height<1)
\r
2018 // erase the block by copying from the master screen
\r
2020 pos = gvar->video.ofs.ylookup[block->screeny]+block->screenx;
\r
2021 //++++ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2022 block->width,block->height);
\r
2025 // put 2s in update where the block was, to force sprites to update
\r
2027 xtl = block->screenx >> SX_T_SHIFT;
\r
2028 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2029 ytl = block->screeny >> SY_T_SHIFT;
\r
2030 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2032 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2033 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2035 for (y=ytl;y<=yth;y++)
\r
2037 for (x=xtl;x<=xth;x++)
\r
2038 *updatespot++ = 2;
\r
2039 updatespot += updatedelta; // down to next line
\r
2048 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2050 strcpy (scratch,"\tErase:");
\r
2051 itoa (erasecount,str,10);
\r
2052 strcat (scratch,str);
\r
2053 write (profilehandle,scratch,strlen(scratch));
\r
2060 ====================
\r
2062 = RFL_UpdateSprites EGA
\r
2064 = NOTE: Implement vertical clipping!
\r
2066 ====================
\r
2069 void RFL_UpdateSprites (void)
\r
2071 spritelisttype *sprite;
\r
2072 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2075 byte *updatespot,*baseupdatespot;
\r
2076 unsigned updatedelta;
\r
2077 unsigned updatecount;
\r
2078 unsigned height,sourceofs;
\r
2084 for (priority=0;priority<PRIORITIES;priority++)
\r
2086 if (priority==MASKEDTILEPRIORITY)
\r
2087 RFL_MaskForegroundTiles ();
\r
2089 for (sprite = prioritystart[priority]; sprite ;
\r
2090 sprite = (spritelisttype *)sprite->nextsprite)
\r
2093 // see if the sprite has any visable area in the port
\r
2096 portx = sprite->screenx - originxscreen;
\r
2097 porty = sprite->screeny - originyscreen;
\r
2098 xtl = portx >> SX_T_SHIFT;
\r
2099 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2100 ytl = porty >> SY_T_SHIFT;
\r
2101 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2105 if (xth>=PORTTILESWIDE)
\r
2106 xth = PORTTILESWIDE-1;
\r
2109 if (yth>=PORTTILESHIGH)
\r
2110 yth = PORTTILESHIGH-1;
\r
2112 if (xtl>xth || ytl>yth)
\r
2116 // see if it's visable area covers any non 0 update tiles
\r
2118 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2119 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2121 if (sprite->updatecount)
\r
2123 sprite->updatecount--; // the sprite was just placed,
\r
2124 goto redraw; // so draw it for sure
\r
2127 for (y=ytl;y<=yth;y++)
\r
2129 for (x=xtl;x<=xth;x++)
\r
2130 if (*updatespot++)
\r
2132 updatespot += updatedelta; // down to next line
\r
2134 continue; // no need to update
\r
2138 // set the tiles it covers to 3, because those tiles are being
\r
2141 updatespot = baseupdatespot;
\r
2142 for (y=ytl;y<=yth;y++)
\r
2144 for (x=xtl;x<=xth;x++)
\r
2145 *updatespot++ = 3;
\r
2146 updatespot += updatedelta; // down to next line
\r
2151 height = sprite->height;
\r
2152 sourceofs = sprite->sourceofs;
\r
2155 height += porty; // clip top off
\r
2156 sourceofs -= porty*sprite->width;
\r
2159 else if (porty+height>PORTSCREENHIGH)
\r
2161 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2164 dest = bufferofs + gvar->video.ofs.ylookup[porty] + portx;
\r
2166 switch (sprite->draw)
\r
2169 //++++ VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2170 dest,sprite->width,height,sprite->planesize);
\r
2185 strcpy (scratch,"\tSprites:");
\r
2186 itoa (updatecount,str,10);
\r
2187 strcat (scratch,str);
\r
2188 write (profilehandle,scratch,strlen(scratch));
\r
2195 =====================
\r
2199 = All routines will draw at the port at bufferofs, possibly copying from
\r
2200 = the port at masterofs. The EGA version then page flips, while the
\r
2201 = CGA version updates the screen from the buffer port.
\r
2203 = Screenpage is the currently displayed page, not the one being drawn
\r
2204 = Otherpage is the page to be worked with now
\r
2206 =====================
\r
2209 void RF_Refresh (void)
\r
2213 updateptr = updatestart[otherpage];
\r
2215 RFL_AnimateTiles (); // DEBUG
\r
2218 // update newly scrolled on tiles and animated tiles from the master screen
\r
2222 RFL_UpdateTiles ();
\r
2223 RFL_EraseBlocks ();
\r
2226 // Update is all 0 except where sprites have changed or new area has
\r
2227 // been scrolled on. Go through all sprites and update the ones that cover
\r
2228 // a non 0 update tile
\r
2231 RFL_UpdateSprites ();
\r
2234 // if the main program has a refresh hook set, call their function before
\r
2235 // displaying the new page
\r
2237 if (refreshvector)
\r
2241 // display the changed screen
\r
2243 VL_SetScreen(bufferofs+gvar->video.ofs.pan.panadjust,gvar->video.ofs.pan.panx & xpanmask);
\r
2246 // prepare for next refresh
\r
2248 // Set the update array to the middle position and clear it out to all "0"s
\r
2249 // with an UPDATETERMINATE at the end
\r
2251 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
2255 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
2256 asm mov di,[newupdate]
\r
2258 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
2262 bufferofs = screenstart[otherpage];
\r
2263 displayofs = screenstart[screenpage];
\r
2266 // calculate tics since last refresh for adaptive timing
\r
2271 #endif // GRMODE == EGAGR
\r
2274 =============================================================================
\r
2276 CGA specific routines
\r
2278 =============================================================================
\r
2281 #if GRMODE == CGAGR
\r
2285 =====================
\r
2287 = RF_NewPosition CGA
\r
2289 =====================
\r
2292 void RF_NewPosition (unsigned x, unsigned y)
\r
2296 unsigned updatenum;
\r
2298 RFL_BoundNewOrigin (x,y);
\r
2300 // calculate new origin related globals
\r
2302 RFL_CalcOriginStuff (x,y);
\r
2305 // clear out all animating tiles
\r
2307 RFL_InitAnimList ();
\r
2310 // set up the new update arrays at base position
\r
2312 updateptr = baseupdateptr;
\r
2314 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
2316 updatenum = 0; // start at first visable tile
\r
2318 for (my=0;my<PORTTILESHIGH;my++)
\r
2320 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
2322 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
2323 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
2327 *spotptr = 0; // set a 0 at end of a line of tiles
\r
2328 spotptr +=(PORTTILESWIDE+1);
\r
2330 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
2335 =====================
\r
2339 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
2340 = scroll if needed. If the scroll distance is greater than one tile, the
\r
2341 = entire screen will be redrawn (this could be generalized, but scrolling
\r
2342 = more than one tile per refresh is a bad idea!).
\r
2344 =====================
\r
2347 void RF_Scroll (int x, int y)
\r
2349 long neworgx,neworgy;
\r
2350 int i,deltax,deltay,absdx,absdy;
\r
2351 int oldxt,oldyt,move,yy;
\r
2352 unsigned updatespot;
\r
2354 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
2357 oldxt = originxtile;
\r
2358 oldyt = originytile;
\r
2360 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
2362 deltax = originxtile - oldxt;
\r
2363 absdx = abs(deltax);
\r
2364 deltay = originytile - oldyt;
\r
2365 absdy = abs(deltay);
\r
2367 if (absdx>1 || absdy>1)
\r
2370 // scrolled more than one tile, so start from scratch
\r
2372 RF_NewPosition(originxglobal,originyglobal);
\r
2376 if (!absdx && !absdy)
\r
2377 return; // the screen has not scrolled an entire tile
\r
2383 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
2384 bufferofs += screenmove;
\r
2385 masterofs += screenmove;
\r
2389 // float the update regions
\r
2393 move += UPDATEWIDE;
\r
2394 else if (deltay==-1)
\r
2395 move -= UPDATEWIDE;
\r
2400 // draw the new tiles just scrolled on to the master screen, and
\r
2401 // mark them as needing to be copied to each screen next refreshes
\r
2402 // Make sure a zero is at the end of each row in update
\r
2409 RFL_NewRow (1); // new right row
\r
2410 RFL_RemoveAnimsOnX (originxtile-1);
\r
2414 RFL_NewRow (3); // new left row
\r
2415 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
2418 spotptr = updateptr+PORTTILESWIDE;
\r
2419 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
2421 *spotptr = 0; // drop a 0 at end of each row
\r
2422 spotptr+=UPDATEWIDE;
\r
2426 //----------------
\r
2432 RFL_NewRow (2); // new bottom row
\r
2433 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
2434 RFL_RemoveAnimsOnY (originytile-1);
\r
2438 RFL_NewRow (0); // new top row
\r
2439 *(updateptr+PORTTILESWIDE) = 0;
\r
2440 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
2444 //----------------
\r
2447 // place a new terminator
\r
2449 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
2451 *(unsigned *)spotptr = UPDATETERMINATE;
\r
2455 =====================
\r
2457 = RF_PlaceSprite CGA
\r
2459 =====================
\r
2462 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
2463 unsigned spritenumber, drawtype draw, int priority)
\r
2465 spritelisttype register *sprite,*next;
\r
2466 spritetabletype far *spr;
\r
2467 spritetype _seg *block;
\r
2468 unsigned shift,pixx;
\r
2469 char str[80],str2[10];
\r
2471 if (!spritenumber || spritenumber == (unsigned)-1)
\r
2473 RF_RemoveSprite (user);
\r
2477 sprite = (spritelisttype *)*user;
\r
2481 // sprite allready exists in the list, so we can use it's block
\r
2484 // post an erase block to erase the old position by copying
\r
2485 // screenx,screeny,width,height
\r
2487 if (!sprite->updatecount) // may not have been drawn at all yet
\r
2488 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2490 if (priority != sprite->priority)
\r
2492 // sprite moved to another priority, so unlink the old one and
\r
2493 // relink it in the new priority
\r
2495 next = sprite->nextsprite; // cut old links
\r
2497 next->prevptr = sprite->prevptr;
\r
2498 *sprite->prevptr = next;
\r
2504 // this is a brand new sprite, so allocate a block from the array
\r
2506 if (!spritefreeptr)
\r
2507 Quit (gvar, "RF_PlaceSprite: No free spots in spritearray!");
\r
2509 sprite = spritefreeptr;
\r
2510 spritefreeptr = spritefreeptr->nextsprite;
\r
2513 next = prioritystart[priority]; // stick it in new spot
\r
2515 next->prevptr = &sprite->nextsprite;
\r
2516 sprite->nextsprite = next;
\r
2517 prioritystart[priority] = sprite;
\r
2518 sprite->prevptr = &prioritystart[priority];
\r
2522 // write the new info to the sprite
\r
2524 spr = &spritetable[spritenumber-STARTSPRITES];
\r
2525 block = (spritetype _seg *)grsegs[spritenumber];
\r
2529 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");
\r
2530 itoa (spritenumber,str2,10);
\r
2531 strcat (str,str2);
\r
2536 globaly+=spr->orgy;
\r
2537 globalx+=spr->orgx;
\r
2539 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
2540 sprite->screeny = globaly >> G_SY_SHIFT;
\r
2541 sprite->width = block->width[0];
\r
2542 sprite->height = spr->height;
\r
2543 sprite->grseg = spritenumber;
\r
2544 sprite->sourceofs = block->sourceoffset[0];
\r
2545 sprite->planesize = block->planesize[0];
\r
2546 sprite->draw = draw;
\r
2547 sprite->priority = priority;
\r
2548 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
2549 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
2550 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
2551 - sprite->tilex + 1;
\r
2552 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2553 - sprite->tiley + 1;
\r
2555 sprite->updatecount = 1; // draw on next refresh
\r
2557 // save the sprite pointer off in the user's pointer so it can be moved
\r
2563 //===========================================================================
\r
2566 =====================
\r
2568 = RF_RemoveSprite CGA
\r
2570 =====================
\r
2573 void RF_RemoveSprite (void **user)
\r
2575 spritelisttype *sprite,*next;
\r
2577 sprite = (spritelisttype *)*user;
\r
2582 // post an erase block to erase the old position by copying
\r
2583 // screenx,screeny,width,height
\r
2585 if (!sprite->updatecount)
\r
2587 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2591 // unlink the sprite node
\r
2593 next = sprite->nextsprite;
\r
2594 if (next) // if (!next), sprite is last in chain
\r
2595 next->prevptr = sprite->prevptr;
\r
2596 *sprite->prevptr = next;
\r
2599 // add it back to the free list
\r
2601 sprite->nextsprite = spritefreeptr;
\r
2602 spritefreeptr = sprite;
\r
2605 // null the users pointer, so next time that actor gets placed, it will
\r
2606 // allocate a new block
\r
2614 ====================
\r
2616 = RFL_EraseBlocks CGA
\r
2618 = Write mode 1 should be set
\r
2620 ====================
\r
2623 void RFL_EraseBlocks (void)
\r
2625 eraseblocktype *block,*done;
\r
2626 int screenxh,screenyh;
\r
2627 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2629 unsigned updatedelta;
\r
2631 block = &eraselist[0][0];
\r
2633 done = eraselistptr[0];
\r
2635 while (block != done)
\r
2639 // clip the block to the current screen view
\r
2641 block->screenx -= originxscreen;
\r
2642 block->screeny -= originyscreen;
\r
2644 if (block->screenx < 0)
\r
2646 block->width += block->screenx;
\r
2647 if (block->width<1)
\r
2649 block->screenx = 0;
\r
2652 if (block->screeny < 0)
\r
2654 block->height += block->screeny;
\r
2655 if (block->height<1)
\r
2657 block->screeny = 0;
\r
2660 screenxh = block->screenx + block->width;
\r
2661 screenyh = block->screeny + block->height;
\r
2663 if (screenxh > CGAPORTSCREENWIDE)
\r
2665 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2666 screenxh = block->screenx + block->width;
\r
2669 if (screenyh > PORTSCREENHIGH)
\r
2671 block->height = PORTSCREENHIGH-block->screeny;
\r
2672 screenyh = block->screeny + block->height;
\r
2675 if (block->width<1 || block->height<1)
\r
2679 // erase the block by copying from the master screen
\r
2681 pos = gvar->video.ofs.ylookup[block->screeny]+block->screenx;
\r
2682 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2683 pos &= ~1; // make sure a word copy gets used
\r
2684 //++++ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2685 block->width,block->height);
\r
2688 // put 2s in update where the block was, to force sprites to update
\r
2690 xtl = block->screenx >> SX_T_SHIFT;
\r
2691 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2692 ytl = block->screeny >> SY_T_SHIFT;
\r
2693 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2695 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2696 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2698 for (y=ytl;y<=yth;y++)
\r
2700 for (x=xtl;x<=xth;x++)
\r
2701 *updatespot++ = 2;
\r
2702 updatespot += updatedelta; // down to next line
\r
2708 eraselistptr[0] = &eraselist[0][0];
\r
2713 ====================
\r
2715 = RFL_UpdateSprites CGA
\r
2717 = NOTE: Implement vertical clipping!
\r
2719 ====================
\r
2722 void RFL_UpdateSprites (void)
\r
2724 spritelisttype *sprite;
\r
2725 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2728 byte *updatespot,*baseupdatespot;
\r
2729 unsigned updatedelta;
\r
2731 unsigned updatecount;
\r
2732 unsigned height,sourceofs;
\r
2739 for (priority=0;priority<PRIORITIES;priority++)
\r
2741 if (priority==MASKEDTILEPRIORITY)
\r
2742 RFL_MaskForegroundTiles ();
\r
2744 for (sprite = prioritystart[priority]; sprite ;
\r
2745 sprite = (spritelisttype *)sprite->nextsprite)
\r
2748 // see if the sprite has any visable area in the port
\r
2751 portx = sprite->screenx - originxscreen;
\r
2752 porty = sprite->screeny - originyscreen;
\r
2753 xtl = portx >> SX_T_SHIFT;
\r
2754 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2755 ytl = porty >> SY_T_SHIFT;
\r
2756 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2760 if (xth>=PORTTILESWIDE)
\r
2761 xth = PORTTILESWIDE-1;
\r
2764 if (yth>=PORTTILESHIGH)
\r
2765 yth = PORTTILESHIGH-1;
\r
2767 if (xtl>xth || ytl>yth)
\r
2771 // see if it's visable area covers any non 0 update tiles
\r
2773 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2774 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2776 if (sprite->updatecount)
\r
2778 sprite->updatecount--; // the sprite was just placed,
\r
2779 goto redraw; // so draw it for sure
\r
2782 for (y=ytl;y<=yth;y++)
\r
2784 for (x=xtl;x<=xth;x++)
\r
2785 if (*updatespot++)
\r
2787 updatespot += updatedelta; // down to next line
\r
2789 continue; // no need to update
\r
2793 // set the tiles it covers to 3, because those tiles are being
\r
2796 updatespot = baseupdatespot;
\r
2797 for (y=ytl;y<=yth;y++)
\r
2799 for (x=xtl;x<=xth;x++)
\r
2800 *updatespot++ = 3;
\r
2801 updatespot += updatedelta; // down to next line
\r
2806 height = sprite->height;
\r
2807 sourceofs = sprite->sourceofs;
\r
2810 height += porty; // clip top off
\r
2811 sourceofs -= porty*sprite->width;
\r
2814 else if (porty+height>PORTSCREENHIGH)
\r
2816 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2819 dest = bufferofs + gvar->video.ofs.ylookup[porty] + portx;
\r
2821 switch (sprite->draw)
\r
2824 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2825 dest,sprite->width,height,sprite->planesize);
\r
2843 =====================
\r
2847 = All routines will draw at the port at bufferofs, possibly copying from
\r
2848 = the port at masterofs. The EGA version then page flips, while the
\r
2849 = CGA version updates the screen from the buffer port.
\r
2851 = Screenpage is the currently displayed page, not the one being drawn
\r
2852 = Otherpage is the page to be worked with now
\r
2854 =====================
\r
2857 void RF_Refresh (void)
\r
2861 RFL_AnimateTiles ();
\r
2864 // update newly scrolled on tiles and animated tiles from the master screen
\r
2866 RFL_UpdateTiles ();
\r
2867 RFL_EraseBlocks ();
\r
2870 // Update is all 0 except where sprites have changed or new area has
\r
2871 // been scrolled on. Go through all sprites and update the ones that cover
\r
2872 // a non 0 update tile
\r
2874 RFL_UpdateSprites ();
\r
2877 // if the main program has a refresh hook set, call their function before
\r
2878 // displaying the new page
\r
2880 if (refreshvector)
\r
2884 // update everything to the screen
\r
2886 VW_CGAFullUpdate ();
\r
2889 // calculate tics since last refresh for adaptive timing
\r
2894 #endif // GRMODE == CGAGR
\r
2895 //===============================
\r
2897 ; Keen Dreams Source Code
\r
2898 ; Copyright (C) 2014 Javier M. Chavez
\r
2900 ; This program is free software; you can redistribute it and/or modify
\r
2901 ; it under the terms of the GNU General Public License as published by
\r
2902 ; the Free Software Foundation; either version 2 of the License, or
\r
2903 ; (at your option) any later version.
\r
2905 ; This program is distributed in the hope that it will be useful,
\r
2906 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
2907 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
2908 ; GNU General Public License for more details.
\r
2910 ; You should have received a copy of the GNU General Public License along
\r
2911 ; with this program; if not, write to the Free Software Foundation, Inc.,
\r
2912 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
2919 INCLUDE "ID_ASM.EQU"
\r
2921 CACHETILES = 1 ;enable master screen tile caching
\r
2923 ;============================================================================
\r
2928 UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1
\r
2932 EXTRN screenseg:WORD
\r
2933 EXTRN updateptr:WORD
\r
2934 EXTRN updatestart:WORD
\r
2935 EXTRN masterofs:WORD ;start of master tile port
\r
2936 EXTRN bufferofs:WORD ;start of current buffer port
\r
2937 EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem
\r
2939 EXTRN mapsegs:WORD
\r
2940 EXTRN originmap:WORD
\r
2941 EXTRN updatemapofs:WORD
\r
2942 EXTRN tilecache:WORD
\r
2943 EXTRN tinf:WORD ;seg pointer to map header and tile info
\r
2944 EXTRN blockstarts:WORD ;offsets from bufferofs for each update block
\r
2951 screenstartcs dw ? ;in code segment for accesability
\r
2957 ;============================================================================
\r
2959 ; CGA refresh routines
\r
2961 ;============================================================================
\r
2965 ;=================
\r
2969 ; Draws a composit two plane tile to the master screen and sets the update
\r
2970 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
2971 ; view pages the next two refreshes
\r
2973 ; Called to draw newlly scrolled on strips and animating tiles
\r
2975 ;=================
\r
2977 PROC RFL_NewTile updateoffset:WORD
\r
2978 PUBLIC RFL_NewTile
\r
2982 ; mark both update lists at this spot
\r
2984 mov di,[updateoffset]
\r
2986 mov bx,[updateptr] ;start of update matrix
\r
2987 mov [BYTE bx+di],1
\r
2989 mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line
\r
2992 ; set di to the location in screenseg to draw the tile
\r
2995 mov si,[updatemapofs+di] ;offset in map from origin
\r
2996 add si,[originmap]
\r
2997 mov di,[blockstarts+di] ;screen location for tile
\r
2998 add di,[masterofs]
\r
3001 ; set BX to the foreground tile number and SI to the background number
\r
3002 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
3003 ; as one of the planes totally eclipses the other
\r
3005 mov es,[mapsegs+2] ;foreground plane
\r
3007 mov es,[mapsegs] ;background plane
\r
3010 mov es,[screenseg]
\r
3014 jmp @@maskeddraw ;draw both together
\r
3018 ; Draw single background tile from main memory
\r
3024 mov ds,[grsegs+STARTTILE16*2+si]
\r
3026 xor si,si ;block is segment aligned
\r
3037 mov ds,ax ;restore turbo's data segment
\r
3043 ; Draw a masked tile combo
\r
3044 ; Interupts are disabled and the stack segment is reassigned
\r
3048 cli ; don't allow ints when SS is set
\r
3050 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3052 mov ds,[grsegs+STARTTILE16*2+si]
\r
3054 xor si,si ;first word of tile data
\r
3057 mov ax,[si] ;background tile
\r
3058 and ax,[ss:si] ;mask
\r
3059 or ax,[ss:si+64] ;masked data
\r
3061 mov ax,[si+2] ;background tile
\r
3062 and ax,[ss:si+2] ;mask
\r
3063 or ax,[ss:si+66] ;masked data
\r
3081 ;===========================================================================
\r
3083 ; EGA refresh routines
\r
3085 ;===========================================================================
\r
3089 ;=================
\r
3093 ; Draws a composit two plane tile to the master screen and sets the update
\r
3094 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
3095 ; view pages the next two refreshes
\r
3097 ; Called to draw newlly scrolled on strips and animating tiles
\r
3099 ; Assumes write mode 0
\r
3101 ;=================
\r
3103 PROC RFL_NewTile updateoffset:WORD
\r
3104 PUBLIC RFL_NewTile
\r
3108 ; mark both update lists at this spot
\r
3110 mov di,[updateoffset]
\r
3112 mov bx,[updatestart] ;page 0 pointer
\r
3113 mov [BYTE bx+di],1
\r
3114 mov bx,[updatestart+2] ;page 1 pointer
\r
3115 mov [BYTE bx+di],1
\r
3118 ; set screenstartcs to the location in screenseg to draw the tile
\r
3121 mov si,[updatemapofs+di] ;offset in map from origin
\r
3122 add si,[originmap]
\r
3123 mov di,[blockstarts+di] ;screen location for tile
\r
3124 add di,[masterofs]
\r
3125 mov [cs:screenstartcs],di
\r
3128 ; set BX to the foreground tile number and SI to the background number
\r
3129 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
3130 ; as one of the planes totally eclipses the other
\r
3132 mov es,[mapsegs+2] ;foreground plane
\r
3134 mov es,[mapsegs] ;background plane
\r
3137 mov es,[screenseg]
\r
3138 mov dx,SC_INDEX ;for stepping through map mask planes
\r
3142 jmp @@maskeddraw ;draw both together
\r
3146 ; No foreground tile, so draw a single background tile.
\r
3147 ; Use the master screen cache if possible
\r
3152 mov bx,SCREENWIDTH-2 ;add to get to start of next line
\r
3159 mov ax,[tilecache+si]
\r
3164 ; Draw single tile from cache
\r
3170 mov ax,SC_MAPMASK + 15*256 ;all planes
\r
3174 mov ax,GC_MODE + 1*256 ;write mode 1
\r
3177 mov di,[cs:screenstartcs]
\r
3178 mov ds,[screenseg]
\r
3189 xor ah,ah ;write mode 0
\r
3193 mov ds,ax ;restore turbo's data segment
\r
3198 ; Draw single tile from main memory
\r
3203 mov ax,[cs:screenstartcs]
\r
3204 mov [tilecache+si],ax ;next time it can be drawn from here with latch
\r
3205 mov ds,[grsegs+STARTTILE16*2+si]
\r
3207 xor si,si ;block is segment aligned
\r
3209 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3211 mov cx,4 ;draw four planes
\r
3216 mov di,[cs:screenstartcs] ;start at same place in all planes
\r
3224 shl ah,1 ;shift plane mask over for next plane
\r
3228 mov ds,ax ;restore turbo's data segment
\r
3234 ; Draw a masked tile combo
\r
3235 ; Interupts are disabled and the stack segment is reassigned
\r
3239 cli ; don't allow ints when SS is set
\r
3241 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3243 mov ds,[grsegs+STARTTILE16*2+si]
\r
3245 xor si,si ;first word of tile data
\r
3247 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3249 mov di,[cs:screenstartcs]
\r
3255 mov bx,[si+tileofs] ;background tile
\r
3256 and bx,[ss:tileofs] ;mask
\r
3257 or bx,[ss:si+tileofs+32] ;masked data
\r
3258 mov [es:di+lineoffset],bx
\r
3259 tileofs = tileofs + 2
\r
3260 lineoffset = lineoffset + SCREENWIDTH
\r
3263 shl ah,1 ;shift plane mask over for next plane
\r
3265 je @@done ;drawn all four planes
\r
3279 ;============================================================================
\r
3281 ; VGA refresh routines
\r
3283 ;============================================================================
\r
3289 ;============================================================================
\r
3291 ; reasonably common refresh routines
\r
3293 ;============================================================================
\r
3296 ;=================
\r
3300 ; Scans through the update matrix pointed to by updateptr, looking for 1s.
\r
3301 ; A 1 represents a tile that needs to be copied from the master screen to the
\r
3302 ; current screen (a new row or an animated tiled). If more than one adjacent
\r
3303 ; tile in a horizontal row needs to be copied, they will be copied as a group.
\r
3305 ; Assumes write mode 1
\r
3307 ;=================
\r
3310 ; AX 0/1 for scasb, temp for segment register transfers
\r
3311 ; BX width for block copies
\r
3313 ; DX line width deltas
\r
3314 ; SI source for copies
\r
3315 ; DI scas dest / movsb dest
\r
3316 ; BP pointer to UPDATETERMINATE
\r
3322 PROC RFL_UpdateTiles
\r
3323 PUBLIC RFL_UpdateTiles
\r
3326 jmp SHORT @@realstart
\r
3329 ; all tiles have been scanned
\r
3334 mov di,[updateptr]
\r
3335 mov bp,(TILESWIDE+1)*TILESHIGH+1
\r
3336 add bp,di ; when di = bx, all tiles have been scanned
\r
3338 mov cx,-1 ; definately scan the entire thing
\r
3341 ; scan for a 1 in the update list, meaning a tile needs to be copied
\r
3342 ; from the master screen to the current screen
\r
3345 pop di ; place to continue scaning from
\r
3347 mov es,ax ; search in the data segment
\r
3360 ; copy a single tile
\r
3365 inc di ; we know the next tile is nothing
\r
3366 push di ; save off the spot being scanned
\r
3367 sub di,[updateptr]
\r
3369 mov di,[blockstarts-4+di] ; start of tile location on screen
\r
3371 add di,[bufferofs] ; dest in current screen
\r
3372 add si,[masterofs] ; source in master screen
\r
3374 mov dx,SCREENWIDTH-TILEWIDTH
\r
3375 mov ax,[screenseg]
\r
3379 ;--------------------------
\r
3394 ;--------------------------
\r
3409 ;--------------------------
\r
3415 ; more than one tile in a row needs to be updated, so do it as a group
\r
3420 mov dx,di ; hold starting position + 1 in dx
\r
3421 inc di ; we know the next tile also gets updated
\r
3422 repe scasb ; see how many more in a row
\r
3423 push di ; save off the spot being scanned
\r
3426 sub bx,dx ; number of tiles in a row
\r
3427 shl bx,1 ; number of bytes / row
\r
3429 mov di,dx ; lookup position of start tile
\r
3430 sub di,[updateptr]
\r
3432 mov di,[blockstarts-2+di] ; start of tile location
\r
3434 add di,[bufferofs] ; dest in current screen
\r
3435 add si,[masterofs] ; source in master screen
\r
3437 mov dx,SCREENWIDTH
\r
3438 sub dx,bx ; offset to next line on screen
\r
3440 sub dx,bx ; bx is words wide in CGA tiles
\r
3443 mov ax,[screenseg]
\r
3466 dec cx ; was 0 from last rep movsb, now $ffff for scasb
\r
3472 ;============================================================================
\r
3475 ;=================
\r
3477 ; RFL_MaskForegroundTiles
\r
3479 ; Scan through update looking for 3's. If the foreground tile there is a
\r
3480 ; masked foreground tile, draw it to the screen
\r
3482 ;=================
\r
3484 PROC RFL_MaskForegroundTiles
\r
3485 PUBLIC RFL_MaskForegroundTiles
\r
3487 jmp SHORT @@realstart
\r
3490 ; all tiles have been scanned
\r
3495 mov di,[updateptr]
\r
3496 mov bp,(TILESWIDE+1)*TILESHIGH+2
\r
3497 add bp,di ; when di = bx, all tiles have been scanned
\r
3499 mov cx,-1 ; definately scan the entire thing
\r
3501 ; scan for a 3 in the update list
\r
3505 mov es,ax ; scan in the data segment
\r
3507 pop di ; place to continue scaning from
\r
3514 ; found a tile, see if it needs to be masked on
\r
3520 sub di,[updateptr]
\r
3522 mov si,[updatemapofs-2+di] ; offset from originmap
\r
3523 add si,[originmap]
\r
3525 mov es,[mapsegs+2] ; foreground map plane segment
\r
3526 mov si,[es:si] ; foreground tile number
\r
3529 jz @@findtile ; 0 = no foreground tile
\r
3532 add bx,INTILE ;INTILE tile info table
\r
3534 test [BYTE PTR es:bx],80h ;high bit = masked tile
\r
3537 ;-------------------
\r
3540 ;=================
\r
3542 ; mask the tile CGA
\r
3544 ;=================
\r
3546 mov di,[blockstarts-2+di]
\r
3547 add di,[bufferofs]
\r
3548 mov es,[screenseg]
\r
3550 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3552 mov bx,64 ;data starts 64 bytes after mask
\r
3558 mov ax,[es:di+lineoffset] ;background
\r
3560 or ax,[si+bx] ;masked data
\r
3561 mov [es:di+lineoffset],ax ;background
\r
3564 mov ax,[es:di+lineoffset+2] ;background
\r
3566 or ax,[si+bx] ;masked data
\r
3567 mov [es:di+lineoffset+2],ax ;background
\r
3570 lineoffset = lineoffset + SCREENWIDTH
\r
3574 ;-------------------
\r
3577 ;=================
\r
3581 ;=================
\r
3583 mov [BYTE planemask],1
\r
3584 mov [BYTE planenum],0
\r
3586 mov di,[blockstarts-2+di]
\r
3587 add di,[bufferofs]
\r
3588 mov [cs:screenstartcs],di
\r
3589 mov es,[screenseg]
\r
3591 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3593 mov bx,32 ;data starts 32 bytes after mask
\r
3598 mov ah,[ss:planemask]
\r
3602 mov ah,[ss:planenum]
\r
3606 mov di,[cs:screenstartcs]
\r
3609 mov cx,[es:di+lineoffset] ;background
\r
3611 or cx,[si+bx] ;masked data
\r
3614 mov [es:di+lineoffset],cx
\r
3615 lineoffset = lineoffset + SCREENWIDTH
\r
3617 add bx,32 ;the mask is now further away
\r
3619 shl [ss:planemask],1 ;shift plane mask over for next plane
\r
3620 cmp [ss:planemask],10000b ;done all four planes?
\r
3621 je @@drawn ;drawn all four planes
\r
3627 ;-------------------
\r
3631 mov cx,-1 ;definately scan the entire thing
\r