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
923 gvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.panx/8 + (gvar->video.ofs.pan.pany*gvar->video.page[0].stridew);
\r
926 #if GRMODE == CGAGR
\r
927 gvar->video.ofs.pan.panx = (originxglobal>>G_P_SHIFT) & 15;
\r
928 gvar->video.ofs.pan.pansx = gvar->video.ofs.pan.panx & 12;
\r
929 gvar->video.ofs.pan.pany = gvar->video.ofs.pan.pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
930 gvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.pansx/4 + gvar->video.ofs.ylookup[gvar->video.ofs.pan.pansy];
\r
940 = RFL_ClearScrollBlocks
\r
945 void RFL_ClearScrollBlocks (void)
\r
947 gvar->video.ofs.pan.hscrollblocks = gvar->video.ofs.pan.vscrollblocks = 0;
\r
954 = RF_SetScrollBlock
\r
956 = Sets a horizontal or vertical scroll block
\r
957 = a horizontal block is ----, meaning it blocks up/down movement
\r
962 void RF_SetScrollBlock (int x, int y, boolean horizontal)
\r
966 gvar->video.ofs.pan.hscrolledge[gvar->video.ofs.pan.hscrollblocks] = y;
\r
967 if (gvar->video.ofs.pan.hscrollblocks++ == MAXSCROLLEDGES)
\r
968 Quit (gvar, "RF_SetScrollBlock: Too many horizontal scroll blocks");
\r
972 gvar->video.ofs.pan.vscrolledge[gvar->video.ofs.pan.vscrollblocks] = x;
\r
973 if (gvar->video.ofs.pan.vscrollblocks++ == MAXSCROLLEDGES)
\r
974 Quit (gvar, "RF_SetScrollBlock: Too many vertical scroll blocks");
\r
984 = Bound a given x/y movement to scroll blocks
\r
989 void RFL_BoundScroll (int x, int y)
\r
991 int check,newxtile,newytile;
\r
993 originxglobal += x;
\r
994 originyglobal += y;
\r
996 newxtile= originxglobal >> G_T_SHIFT;
\r
997 newytile = originyglobal >> G_T_SHIFT;
\r
1001 newxtile+=SCREENTILESWIDE;
\r
1002 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1003 if (gvar->video.ofs.pan.vscrolledge[check] == newxtile)
\r
1005 originxglobal = originxglobal&0xff00;
\r
1011 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1012 if (gvar->video.ofs.pan.vscrolledge[check] == newxtile)
\r
1014 originxglobal = (originxglobal&0xff00)+0x100;
\r
1022 newytile+=SCREENTILESHIGH;
\r
1023 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1024 if (gvar->video.ofs.pan.hscrolledge[check] == newytile)
\r
1026 originyglobal = originyglobal&0xff00;
\r
1032 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1033 if (gvar->video.ofs.pan.hscrolledge[check] == newytile)
\r
1035 originyglobal = (originyglobal&0xff00)+0x100;
\r
1041 RFL_CalcOriginStuff (originxglobal, originyglobal);
\r
1045 //===========================================================================
\r
1048 =====================
\r
1050 = RF_SetRefreshHook
\r
1052 =====================
\r
1055 void RF_SetRefreshHook (void (*func) (void) )
\r
1057 refreshvector = func;
\r
1061 //===========================================================================
\r
1068 = Bring a new row of tiles onto the port, spawning animating tiles
\r
1073 void RFL_NewRow (int dir)
\r
1075 unsigned count,updatespot,updatestep;
\r
1076 int x,y,xstep,ystep;
\r
1080 case 0: // top row
\r
1087 count = PORTTILESWIDE;
\r
1090 case 1: // right row
\r
1091 updatespot = PORTTILESWIDE-1;
\r
1092 updatestep = UPDATEWIDE;
\r
1093 x = originxtile + PORTTILESWIDE-1;
\r
1097 count = PORTTILESHIGH;
\r
1100 case 2: // bottom row
\r
1101 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1104 y = originytile + PORTTILESHIGH-1;
\r
1107 count = PORTTILESWIDE;
\r
1110 case 3: // left row
\r
1112 updatestep = UPDATEWIDE;
\r
1117 count = PORTTILESHIGH;
\r
1120 Quit (gvar, "RFL_NewRow: Bad dir!");
\r
1125 RFL_NewTile(updatespot);
\r
1126 RFL_CheckForAnimTile (x,y);
\r
1127 updatespot+=updatestep;
\r
1133 //===========================================================================
\r
1136 =====================
\r
1140 =====================
\r
1143 void RF_ForceRefresh (void)
\r
1145 RF_NewPosition (originxglobal,originyglobal);
\r
1150 //===========================================================================
\r
1153 =====================
\r
1157 = Copies a block of tiles (all three planes) from one point
\r
1158 = in the map to another, accounting for animating tiles
\r
1160 =====================
\r
1163 void RF_MapToMap (unsigned srcx, unsigned srcy,
\r
1164 unsigned destx, unsigned desty,
\r
1165 unsigned width, unsigned height)
\r
1168 unsigned source,destofs,xspot,yspot;
\r
1169 unsigned linedelta,p0,p1,p2,updatespot;
\r
1170 unsigned far *source0, far *source1, far *source2;
\r
1171 unsigned far *dest0, far *dest1, far *dest2;
\r
1174 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1176 source = mapbwidthtable[srcy]/2 + srcx;
\r
1178 source0 = mapsegs[0]+source;
\r
1179 source1 = mapsegs[1]+source;
\r
1180 source2 = mapsegs[2]+source;
\r
1182 destofs = mapbwidthtable[desty]/2 + destx;
\r
1183 destofs -= source;
\r
1185 linedelta = mapwidth - width;
\r
1187 for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
\r
1188 for (x=0;x<width;x++,source0++,source1++,source2++)
\r
1194 dest0 = source0 + destofs;
\r
1195 dest1 = source1 + destofs;
\r
1196 dest2 = source2 + destofs;
\r
1199 // only make a new tile if it is different
\r
1201 if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
\r
1212 // if tile is on the view port
\r
1214 xspot = destx+x-originxtile;
\r
1215 yspot = desty+y-originytile;
\r
1216 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1220 updatespot = uwidthtable[yspot]+xspot;
\r
1221 RFL_NewTile(updatespot);
\r
1223 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1228 //===========================================================================
\r
1232 =====================
\r
1236 = Copies a string of tiles from main memory to the map,
\r
1237 = accounting for animating tiles
\r
1239 =====================
\r
1242 void RF_MemToMap (unsigned far *source, unsigned plane,
\r
1243 unsigned destx, unsigned desty,
\r
1244 unsigned width, unsigned height)
\r
1247 unsigned xspot,yspot;
\r
1248 unsigned linedelta,updatespot;
\r
1249 unsigned far *dest,old,new;
\r
1252 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1254 dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
\r
1256 linedelta = mapwidth - width;
\r
1258 for (y=0;y<height;y++,dest+=linedelta)
\r
1259 for (x=0;x<width;x++)
\r
1272 xspot = destx+x-originxtile;
\r
1273 yspot = desty+y-originytile;
\r
1274 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1278 updatespot = uwidthtable[yspot]+xspot;
\r
1279 RFL_NewTile(updatespot);
\r
1281 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1286 //===========================================================================
\r
1290 =====================
\r
1292 = RFL_BoundNewOrigin
\r
1294 = Copies a string of tiles from main memory to the map,
\r
1295 = accounting for animating tiles
\r
1297 =====================
\r
1300 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
\r
1305 // calculate new origin related globals
\r
1307 if (orgx<originxmin)
\r
1309 else if (orgx>originxmax)
\r
1312 if (orgy<originymin)
\r
1314 else if (orgy>originymax)
\r
1317 originxtile = orgx>>G_T_SHIFT;
\r
1318 originytile = orgy>>G_T_SHIFT;
\r
1320 for (check=0;check<gvar->video.ofs.pan.vscrollblocks;check++)
\r
1322 edge = gvar->video.ofs.pan.vscrolledge[check];
\r
1323 if (edge>=originxtile && edge <=originxtile+10)
\r
1325 orgx = (edge+1)*TILEGLOBAL;
\r
1328 if (edge>=originxtile+11 && edge <=originxtile+20)
\r
1330 orgx = (edge-20)*TILEGLOBAL;
\r
1335 for (check=0;check<gvar->video.ofs.pan.hscrollblocks;check++)
\r
1337 edge = gvar->video.ofs.pan.hscrolledge[check];
\r
1338 if (edge>=originytile && edge <=originytile+6)
\r
1340 orgy = (edge+1)*TILEGLOBAL;
\r
1343 if (edge>=originytile+7 && edge <=originytile+13)
\r
1345 orgy = (edge-13)*TILEGLOBAL;
\r
1351 RFL_CalcOriginStuff (orgx,orgy);
\r
1355 //===========================================================================
\r
1358 =====================
\r
1362 = Posts erase blocks to clear a certain area of the screen to the master
\r
1363 = screen, to erase text or something draw directly to the screen
\r
1365 = Parameters in pixels, but erasure is byte bounded
\r
1367 =====================
\r
1370 void RF_ClearBlock (int x, int y, int width, int height)
\r
1372 // eraseblocktype block;
\r
1374 #if GRMODE == EGAGR
\r
1375 block.screenx = x/8+originxscreen;
\r
1376 block.screeny = y+originyscreen;
\r
1377 block.width = (width+(x&7)+7)/8;
\r
1378 block.height = height;
\r
1379 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1380 memcpy (eraselistptr[1]++,&block,sizeof(block));
\r
1383 #if GRMODE == CGAGR
\r
1384 block.screenx = x/4+originxscreen;
\r
1385 block.screeny = y+originyscreen;
\r
1386 block.width = (width+(x&3)+3)/4;
\r
1387 block.height = height;
\r
1388 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1393 //===========================================================================
\r
1396 =====================
\r
1400 = Causes a number of tiles to be redrawn to the master screen and updated
\r
1402 = Parameters in pixels, but erasure is tile bounded
\r
1404 =====================
\r
1407 void RF_RedrawBlock (int x, int y, int width, int height)
\r
1409 int xx,yy,xl,xh,yl,yh;
\r
1411 xl=(x+gvar->video.ofs.pan.panx)/16;
\r
1412 xh=(x+gvar->video.ofs.pan.panx+width+15)/16;
\r
1413 yl=(y+gvar->video.ofs.pan.pany)/16;
\r
1414 yh=(y+gvar->video.ofs.pan.pany+height+15)/16;
\r
1415 for (yy=yl;yy<=yh;yy++)
\r
1416 for (xx=xl;xx<=xh;xx++)
\r
1417 RFL_NewTile (yy*UPDATEWIDE+xx);
\r
1421 //===========================================================================
\r
1424 =====================
\r
1428 =====================
\r
1431 void RF_CalcTics (void)
\r
1433 long newtime;//,oldtimecount;
\r
1434 word TimeCount = *clockw;
\r
1437 // calculate tics since last refresh for adaptive timing
\r
1439 if (lasttimecount > TimeCount)
\r
1440 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1442 /*++++ if (DemoMode) // demo recording and playback needs
\r
1443 { // to be constant
\r
1445 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1447 oldtimecount = lasttimecount;
\r
1448 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1450 lasttimecount = oldtimecount + DEMOTICS;
\r
1451 TimeCount = lasttimecount + DEMOTICS;
\r
1457 // non demo, so report actual time
\r
1461 newtime = TimeCount;
\r
1462 tics = newtime-lasttimecount;
\r
1463 } while (tics<MINTICS);
\r
1464 lasttimecount = newtime;
\r
1467 strcpy (scratch,"\tTics:");
\r
1468 itoa (tics,str,10);
\r
1469 strcat (scratch,str);
\r
1470 strcat (scratch,"\n");
\r
1471 write (profilehandle,scratch,strlen(scratch));
\r
1476 TimeCount -= (tics-MAXTICS);
\r
1483 =============================================================================
\r
1485 EGA specific routines
\r
1487 =============================================================================
\r
1490 #if GRMODE == EGAGR
\r
1493 =====================
\r
1495 = RF_FindFreeBuffer
\r
1497 = Finds the start of unused, non visable buffer space
\r
1499 =====================
\r
1502 unsigned RF_FindFreeBuffer (void)
\r
1504 unsigned spot,i,j;
\r
1509 spot = screenstart[i]+SCREENSPACE;
\r
1512 if (spot == screenstart[j])
\r
1521 return 0; // never get here...
\r
1524 //===========================================================================
\r
1527 =====================
\r
1529 = RF_NewPosition EGA
\r
1531 =====================
\r
1534 void RF_NewPosition (unsigned x, unsigned y)
\r
1537 byte *page0ptr,*page1ptr;
\r
1538 unsigned updatenum;
\r
1540 RFL_BoundNewOrigin (x,y);
\r
1542 // calculate new origin related globals
\r
1544 RFL_CalcOriginStuff (x,y);
\r
1547 // clear out all animating tiles
\r
1549 RFL_InitAnimList ();
\r
1552 // set up the new update arrays at base position
\r
1554 memset (tilecache,0,sizeof(tilecache)); // old cache is invalid
\r
1556 updatestart[0] = baseupdatestart[0];
\r
1557 updatestart[1] = baseupdatestart[1];
\r
1558 updateptr = updatestart[otherpage];
\r
1560 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1561 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1563 updatenum = 0; // start at first visable tile
\r
1565 for (my=0;my<PORTTILESHIGH;my++)
\r
1567 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1569 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1570 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1574 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1575 page0ptr+=(PORTTILESWIDE+1);
\r
1576 page1ptr+=(PORTTILESWIDE+1);
\r
1578 *(word *)(page0ptr-PORTTILESWIDE)
\r
1579 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1582 //===========================================================================
\r
1589 = Uncache the trailing row of tiles
\r
1594 void RFL_OldRow (unsigned updatespot,unsigned count,unsigned step)
\r
1597 asm mov si,[updatespot] // pointer inside each map plane
\r
1598 asm mov cx,[count] // number of tiles to clear
\r
1599 asm mov dx,[step] // move to next tile
\r
1600 asm mov es,[WORD PTR mapsegs] // background plane
\r
1601 asm mov ds,[WORD PTR mapsegs+2] // foreground plane
\r
1606 asm jnz blockok // if a foreground tile, block wasn't cached
\r
1607 asm mov bx,[es:si]
\r
1609 asm mov [WORD PTR ss:tilecache+bx],0 //tile is no longer in master screen cache
\r
1612 asm loop clearcache
\r
1621 =====================
\r
1625 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1626 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1627 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1628 = more than one tile per refresh is a bad idea!).
\r
1630 =====================
\r
1633 void RF_Scroll (int x, int y)
\r
1635 long neworgx,neworgy;
\r
1636 int i,deltax,deltay,absdx,absdy;
\r
1637 int oldxt,oldyt,move,yy;
\r
1638 unsigned updatespot;
\r
1639 byte *update0,*update1;
\r
1640 unsigned oldgvar->video.ofs.pan.panx,oldgvar->video.ofs.pan.panadjust,oldoriginmap,oldscreen,newscreen,screencopy;
\r
1643 oldxt = originxtile;
\r
1644 oldyt = originytile;
\r
1645 oldoriginmap = originmap;
\r
1646 oldgvar->video.ofs.pan.panadjust = gvar->video.ofs.pan.panadjust;
\r
1647 oldgvar->video.ofs.pan.panx = gvar->video.ofs.pan.panx;
\r
1649 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
1651 deltax = originxtile - oldxt;
\r
1652 absdx = abs(deltax);
\r
1653 deltay = originytile - oldyt;
\r
1654 absdy = abs(deltay);
\r
1656 if (absdx>1 || absdy>1)
\r
1659 // scrolled more than one tile, so start from scratch
\r
1661 RF_NewPosition(originxglobal,originyglobal);
\r
1665 if (!absdx && !absdy)
\r
1666 return; // the screen has not scrolled an entire tile
\r
1670 // adjust screens and handle SVGA crippled compatability mode
\r
1672 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1675 screenstart[i]+= screenmove;
\r
1676 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1679 // move the screen to the opposite end of the buffer
\r
1681 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1682 oldscreen = screenstart[i] - screenmove;
\r
1683 newscreen = oldscreen + screencopy;
\r
1684 screenstart[i] = newscreen + screenmove;
\r
1685 //++++ VW_ScreenToScreen (oldscreen,newscreen,
\r
1686 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1688 //++++ if (i==screenpage)
\r
1689 //++++ VL_SetScreen(newscreen+oldgvar->video.ofs.pan.panadjust,oldgvar->video.ofs.pan.panx & xpanmask);
\r
1692 bufferofs = screenstart[otherpage];
\r
1693 displayofs = screenstart[screenpage];
\r
1694 masterofs = screenstart[2];
\r
1698 // float the update regions
\r
1702 move += UPDATEWIDE;
\r
1703 else if (deltay==-1)
\r
1704 move -= UPDATEWIDE;
\r
1706 updatestart[0]+=move;
\r
1707 updatestart[1]+=move;
\r
1710 // draw the new tiles just scrolled on to the master screen, and
\r
1711 // mark them as needing to be copied to each screen next refreshes
\r
1712 // Make sure a zero is at the end of each row in update
\r
1719 RFL_NewRow (1); // new right row
\r
1720 RFL_OldRow (oldoriginmap,PORTTILESHIGH,mapbyteswide);
\r
1721 RFL_RemoveAnimsOnX (originxtile-1);
\r
1725 RFL_NewRow (3); // new left row
\r
1726 RFL_OldRow (oldoriginmap+(PORTTILESWIDE-1)*2,PORTTILESHIGH
\r
1728 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1731 update0 = updatestart[0]+PORTTILESWIDE;
\r
1732 update1 = updatestart[1]+PORTTILESWIDE;
\r
1733 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1735 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1736 update0+=UPDATEWIDE;
\r
1737 update1+=UPDATEWIDE;
\r
1741 //----------------
\r
1747 RFL_NewRow (2); // new bottom row
\r
1748 RFL_OldRow (oldoriginmap,PORTTILESWIDE,2);
\r
1749 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1750 RFL_RemoveAnimsOnY (originytile-1);
\r
1754 RFL_NewRow (0); // new top row
\r
1755 RFL_OldRow (oldoriginmap+mapbwidthtable[PORTTILESHIGH-1]
\r
1756 ,PORTTILESWIDE,2);
\r
1758 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1761 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1762 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1765 //----------------
\r
1768 // place a new terminator
\r
1770 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1771 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1772 *update0++ = *update1++ = 0;
\r
1773 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1776 //===========================================================================
\r
1779 =====================
\r
1781 = RF_PlaceSprite EGA
\r
1783 =====================
\r
1786 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1787 unsigned spritenumber, drawtype draw, int priority)
\r
1789 spritelisttype register *sprite,*next;
\r
1790 spritetabletype far *spr;
\r
1791 spritetype _seg *block;
\r
1792 unsigned shift,pixx;
\r
1793 char str[80],str2[10];
\r
1795 if (!spritenumber || spritenumber == (unsigned)-1)
\r
1797 RF_RemoveSprite (user);
\r
1801 sprite = (spritelisttype *)*user;
\r
1805 // sprite allready exists in the list, so we can use it's block
\r
1808 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1809 // both pages may not need to be erased if the sprite just changed last frame
\r
1811 if (sprite->updatecount<2)
\r
1813 if (!sprite->updatecount)
\r
1814 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1815 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1818 if (priority != sprite->priority)
\r
1820 // sprite mvoed to another priority, so unlink the old one and
\r
1821 // relink it in the new priority
\r
1823 next = sprite->nextsprite; // cut old links
\r
1825 next->prevptr = sprite->prevptr;
\r
1826 *sprite->prevptr = next;
\r
1832 // this is a brand new sprite, so allocate a block from the array
\r
1834 if (!spritefreeptr)
\r
1835 Quit (gvar, "RF_PlaceSprite: No free spots in spritearray!");
\r
1837 sprite = spritefreeptr;
\r
1838 spritefreeptr = spritefreeptr->nextsprite;
\r
1841 next = prioritystart[priority]; // stick it in new spot
\r
1843 next->prevptr = &sprite->nextsprite;
\r
1844 sprite->nextsprite = next;
\r
1845 prioritystart[priority] = sprite;
\r
1846 sprite->prevptr = &prioritystart[priority];
\r
1850 // write the new info to the sprite
\r
1852 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1853 block = (spritetype _seg *)grsegs[spritenumber];
\r
1857 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");
\r
1858 itoa (spritenumber,str2,10);
\r
1859 strcat (str,str2);
\r
1863 globaly+=spr->orgy;
\r
1864 globalx+=spr->orgx;
\r
1866 pixx = globalx >> G_SY_SHIFT;
\r
1867 shift = (pixx&7)/2;
\r
1869 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1870 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1871 sprite->width = block->width[shift];
\r
1872 sprite->height = spr->height;
\r
1873 sprite->grseg = spritenumber;
\r
1874 sprite->sourceofs = block->sourceoffset[shift];
\r
1875 sprite->planesize = block->planesize[shift];
\r
1876 sprite->draw = draw;
\r
1877 sprite->priority = priority;
\r
1878 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1879 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1880 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1881 - sprite->tilex + 1;
\r
1882 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1883 - sprite->tiley + 1;
\r
1885 sprite->updatecount = 2; // draw on next two refreshes
\r
1887 // save the sprite pointer off in the user's pointer so it can be moved
\r
1893 //===========================================================================
\r
1896 =====================
\r
1898 = RF_RemoveSprite EGA
\r
1900 =====================
\r
1903 void RF_RemoveSprite (void **user)
\r
1905 spritelisttype *sprite,*next;
\r
1907 sprite = (spritelisttype *)*user;
\r
1912 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1913 // both pages may not need to be erased if the sprite just changed last frame
\r
1915 if (sprite->updatecount<2)
\r
1917 if (!sprite->updatecount)
\r
1918 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1919 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1923 // unlink the sprite node
\r
1925 next = sprite->nextsprite;
\r
1926 if (next) // if (!next), sprite is last in chain
\r
1927 next->prevptr = sprite->prevptr;
\r
1928 *sprite->prevptr = next;
\r
1931 // add it back to the free list
\r
1933 sprite->nextsprite = spritefreeptr;
\r
1934 spritefreeptr = sprite;
\r
1937 // null the users pointer, so next time that actor gets placed, it will
\r
1938 // allocate a new block
\r
1945 //===========================================================================
\r
1949 ====================
\r
1951 = RFL_EraseBlocks EGA
\r
1953 = Write mode 1 should be set
\r
1955 ====================
\r
1958 void RFL_EraseBlocks (void)
\r
1960 eraseblocktype *block,*done;
\r
1961 int screenxh,screenyh;
\r
1962 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
1964 unsigned updatedelta;
\r
1965 unsigned erasecount;
\r
1971 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1973 done = eraselistptr[otherpage];
\r
1975 while (block != done)
\r
1979 // clip the block to the current screen view
\r
1981 block->screenx -= originxscreen;
\r
1982 block->screeny -= originyscreen;
\r
1984 if (block->screenx < 0)
\r
1986 block->width += block->screenx;
\r
1987 if (block->width<1)
\r
1989 block->screenx = 0;
\r
1992 if (block->screeny < 0)
\r
1994 block->height += block->screeny;
\r
1995 if (block->height<1)
\r
1997 block->screeny = 0;
\r
2000 screenxh = block->screenx + block->width;
\r
2001 screenyh = block->screeny + block->height;
\r
2003 if (screenxh > EGAPORTSCREENWIDE)
\r
2005 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
2006 screenxh = block->screenx + block->width;
\r
2009 if (screenyh > PORTSCREENHIGH)
\r
2011 block->height = PORTSCREENHIGH-block->screeny;
\r
2012 screenyh = block->screeny + block->height;
\r
2015 if (block->width<1 || block->height<1)
\r
2019 // erase the block by copying from the master screen
\r
2021 pos = gvar->video.ofs.ylookup[block->screeny]+block->screenx;
\r
2022 //++++ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2023 block->width,block->height);
\r
2026 // put 2s in update where the block was, to force sprites to update
\r
2028 xtl = block->screenx >> SX_T_SHIFT;
\r
2029 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2030 ytl = block->screeny >> SY_T_SHIFT;
\r
2031 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2033 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2034 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2036 for (y=ytl;y<=yth;y++)
\r
2038 for (x=xtl;x<=xth;x++)
\r
2039 *updatespot++ = 2;
\r
2040 updatespot += updatedelta; // down to next line
\r
2049 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2051 strcpy (scratch,"\tErase:");
\r
2052 itoa (erasecount,str,10);
\r
2053 strcat (scratch,str);
\r
2054 write (profilehandle,scratch,strlen(scratch));
\r
2061 ====================
\r
2063 = RFL_UpdateSprites EGA
\r
2065 = NOTE: Implement vertical clipping!
\r
2067 ====================
\r
2070 void RFL_UpdateSprites (void)
\r
2072 spritelisttype *sprite;
\r
2073 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2076 byte *updatespot,*baseupdatespot;
\r
2077 unsigned updatedelta;
\r
2078 unsigned updatecount;
\r
2079 unsigned height,sourceofs;
\r
2085 for (priority=0;priority<PRIORITIES;priority++)
\r
2087 if (priority==MASKEDTILEPRIORITY)
\r
2088 RFL_MaskForegroundTiles ();
\r
2090 for (sprite = prioritystart[priority]; sprite ;
\r
2091 sprite = (spritelisttype *)sprite->nextsprite)
\r
2094 // see if the sprite has any visable area in the port
\r
2097 portx = sprite->screenx - originxscreen;
\r
2098 porty = sprite->screeny - originyscreen;
\r
2099 xtl = portx >> SX_T_SHIFT;
\r
2100 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2101 ytl = porty >> SY_T_SHIFT;
\r
2102 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2106 if (xth>=PORTTILESWIDE)
\r
2107 xth = PORTTILESWIDE-1;
\r
2110 if (yth>=PORTTILESHIGH)
\r
2111 yth = PORTTILESHIGH-1;
\r
2113 if (xtl>xth || ytl>yth)
\r
2117 // see if it's visable area covers any non 0 update tiles
\r
2119 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2120 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2122 if (sprite->updatecount)
\r
2124 sprite->updatecount--; // the sprite was just placed,
\r
2125 goto redraw; // so draw it for sure
\r
2128 for (y=ytl;y<=yth;y++)
\r
2130 for (x=xtl;x<=xth;x++)
\r
2131 if (*updatespot++)
\r
2133 updatespot += updatedelta; // down to next line
\r
2135 continue; // no need to update
\r
2139 // set the tiles it covers to 3, because those tiles are being
\r
2142 updatespot = baseupdatespot;
\r
2143 for (y=ytl;y<=yth;y++)
\r
2145 for (x=xtl;x<=xth;x++)
\r
2146 *updatespot++ = 3;
\r
2147 updatespot += updatedelta; // down to next line
\r
2152 height = sprite->height;
\r
2153 sourceofs = sprite->sourceofs;
\r
2156 height += porty; // clip top off
\r
2157 sourceofs -= porty*sprite->width;
\r
2160 else if (porty+height>PORTSCREENHIGH)
\r
2162 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2165 dest = bufferofs + gvar->video.ofs.ylookup[porty] + portx;
\r
2167 switch (sprite->draw)
\r
2170 //++++ VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2171 dest,sprite->width,height,sprite->planesize);
\r
2186 strcpy (scratch,"\tSprites:");
\r
2187 itoa (updatecount,str,10);
\r
2188 strcat (scratch,str);
\r
2189 write (profilehandle,scratch,strlen(scratch));
\r
2196 =====================
\r
2200 = All routines will draw at the port at bufferofs, possibly copying from
\r
2201 = the port at masterofs. The EGA version then page flips, while the
\r
2202 = CGA version updates the screen from the buffer port.
\r
2204 = Screenpage is the currently displayed page, not the one being drawn
\r
2205 = Otherpage is the page to be worked with now
\r
2207 =====================
\r
2210 void RF_Refresh (void)
\r
2214 updateptr = updatestart[otherpage];
\r
2216 RFL_AnimateTiles (); // DEBUG
\r
2219 // update newly scrolled on tiles and animated tiles from the master screen
\r
2223 RFL_UpdateTiles ();
\r
2224 RFL_EraseBlocks ();
\r
2227 // Update is all 0 except where sprites have changed or new area has
\r
2228 // been scrolled on. Go through all sprites and update the ones that cover
\r
2229 // a non 0 update tile
\r
2232 RFL_UpdateSprites ();
\r
2235 // if the main program has a refresh hook set, call their function before
\r
2236 // displaying the new page
\r
2238 if (refreshvector)
\r
2242 // display the changed screen
\r
2244 VL_SetScreen(bufferofs+gvar->video.ofs.pan.panadjust,gvar->video.ofs.pan.panx & xpanmask);
\r
2247 // prepare for next refresh
\r
2249 // Set the update array to the middle position and clear it out to all "0"s
\r
2250 // with an UPDATETERMINATE at the end
\r
2252 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
2256 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
2257 asm mov di,[newupdate]
\r
2259 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
2263 bufferofs = screenstart[otherpage];
\r
2264 displayofs = screenstart[screenpage];
\r
2267 // calculate tics since last refresh for adaptive timing
\r
2272 #endif // GRMODE == EGAGR
\r
2275 =============================================================================
\r
2277 CGA specific routines
\r
2279 =============================================================================
\r
2282 #if GRMODE == CGAGR
\r
2286 =====================
\r
2288 = RF_NewPosition CGA
\r
2290 =====================
\r
2293 void RF_NewPosition (unsigned x, unsigned y)
\r
2297 unsigned updatenum;
\r
2299 RFL_BoundNewOrigin (x,y);
\r
2301 // calculate new origin related globals
\r
2303 RFL_CalcOriginStuff (x,y);
\r
2306 // clear out all animating tiles
\r
2308 RFL_InitAnimList ();
\r
2311 // set up the new update arrays at base position
\r
2313 updateptr = baseupdateptr;
\r
2315 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
2317 updatenum = 0; // start at first visable tile
\r
2319 for (my=0;my<PORTTILESHIGH;my++)
\r
2321 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
2323 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
2324 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
2328 *spotptr = 0; // set a 0 at end of a line of tiles
\r
2329 spotptr +=(PORTTILESWIDE+1);
\r
2331 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
2336 =====================
\r
2340 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
2341 = scroll if needed. If the scroll distance is greater than one tile, the
\r
2342 = entire screen will be redrawn (this could be generalized, but scrolling
\r
2343 = more than one tile per refresh is a bad idea!).
\r
2345 =====================
\r
2348 void RF_Scroll (int x, int y)
\r
2350 long neworgx,neworgy;
\r
2351 int i,deltax,deltay,absdx,absdy;
\r
2352 int oldxt,oldyt,move,yy;
\r
2353 unsigned updatespot;
\r
2355 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
2358 oldxt = originxtile;
\r
2359 oldyt = originytile;
\r
2361 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
2363 deltax = originxtile - oldxt;
\r
2364 absdx = abs(deltax);
\r
2365 deltay = originytile - oldyt;
\r
2366 absdy = abs(deltay);
\r
2368 if (absdx>1 || absdy>1)
\r
2371 // scrolled more than one tile, so start from scratch
\r
2373 RF_NewPosition(originxglobal,originyglobal);
\r
2377 if (!absdx && !absdy)
\r
2378 return; // the screen has not scrolled an entire tile
\r
2384 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
2385 bufferofs += screenmove;
\r
2386 masterofs += screenmove;
\r
2390 // float the update regions
\r
2394 move += UPDATEWIDE;
\r
2395 else if (deltay==-1)
\r
2396 move -= UPDATEWIDE;
\r
2401 // draw the new tiles just scrolled on to the master screen, and
\r
2402 // mark them as needing to be copied to each screen next refreshes
\r
2403 // Make sure a zero is at the end of each row in update
\r
2410 RFL_NewRow (1); // new right row
\r
2411 RFL_RemoveAnimsOnX (originxtile-1);
\r
2415 RFL_NewRow (3); // new left row
\r
2416 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
2419 spotptr = updateptr+PORTTILESWIDE;
\r
2420 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
2422 *spotptr = 0; // drop a 0 at end of each row
\r
2423 spotptr+=UPDATEWIDE;
\r
2427 //----------------
\r
2433 RFL_NewRow (2); // new bottom row
\r
2434 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
2435 RFL_RemoveAnimsOnY (originytile-1);
\r
2439 RFL_NewRow (0); // new top row
\r
2440 *(updateptr+PORTTILESWIDE) = 0;
\r
2441 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
2445 //----------------
\r
2448 // place a new terminator
\r
2450 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
2452 *(unsigned *)spotptr = UPDATETERMINATE;
\r
2456 =====================
\r
2458 = RF_PlaceSprite CGA
\r
2460 =====================
\r
2463 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
2464 unsigned spritenumber, drawtype draw, int priority)
\r
2466 spritelisttype register *sprite,*next;
\r
2467 spritetabletype far *spr;
\r
2468 spritetype _seg *block;
\r
2469 unsigned shift,pixx;
\r
2470 char str[80],str2[10];
\r
2472 if (!spritenumber || spritenumber == (unsigned)-1)
\r
2474 RF_RemoveSprite (user);
\r
2478 sprite = (spritelisttype *)*user;
\r
2482 // sprite allready exists in the list, so we can use it's block
\r
2485 // post an erase block to erase the old position by copying
\r
2486 // screenx,screeny,width,height
\r
2488 if (!sprite->updatecount) // may not have been drawn at all yet
\r
2489 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2491 if (priority != sprite->priority)
\r
2493 // sprite moved to another priority, so unlink the old one and
\r
2494 // relink it in the new priority
\r
2496 next = sprite->nextsprite; // cut old links
\r
2498 next->prevptr = sprite->prevptr;
\r
2499 *sprite->prevptr = next;
\r
2505 // this is a brand new sprite, so allocate a block from the array
\r
2507 if (!spritefreeptr)
\r
2508 Quit (gvar, "RF_PlaceSprite: No free spots in spritearray!");
\r
2510 sprite = spritefreeptr;
\r
2511 spritefreeptr = spritefreeptr->nextsprite;
\r
2514 next = prioritystart[priority]; // stick it in new spot
\r
2516 next->prevptr = &sprite->nextsprite;
\r
2517 sprite->nextsprite = next;
\r
2518 prioritystart[priority] = sprite;
\r
2519 sprite->prevptr = &prioritystart[priority];
\r
2523 // write the new info to the sprite
\r
2525 spr = &spritetable[spritenumber-STARTSPRITES];
\r
2526 block = (spritetype _seg *)grsegs[spritenumber];
\r
2530 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");
\r
2531 itoa (spritenumber,str2,10);
\r
2532 strcat (str,str2);
\r
2537 globaly+=spr->orgy;
\r
2538 globalx+=spr->orgx;
\r
2540 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
2541 sprite->screeny = globaly >> G_SY_SHIFT;
\r
2542 sprite->width = block->width[0];
\r
2543 sprite->height = spr->height;
\r
2544 sprite->grseg = spritenumber;
\r
2545 sprite->sourceofs = block->sourceoffset[0];
\r
2546 sprite->planesize = block->planesize[0];
\r
2547 sprite->draw = draw;
\r
2548 sprite->priority = priority;
\r
2549 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
2550 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
2551 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
2552 - sprite->tilex + 1;
\r
2553 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2554 - sprite->tiley + 1;
\r
2556 sprite->updatecount = 1; // draw on next refresh
\r
2558 // save the sprite pointer off in the user's pointer so it can be moved
\r
2564 //===========================================================================
\r
2567 =====================
\r
2569 = RF_RemoveSprite CGA
\r
2571 =====================
\r
2574 void RF_RemoveSprite (void **user)
\r
2576 spritelisttype *sprite,*next;
\r
2578 sprite = (spritelisttype *)*user;
\r
2583 // post an erase block to erase the old position by copying
\r
2584 // screenx,screeny,width,height
\r
2586 if (!sprite->updatecount)
\r
2588 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2592 // unlink the sprite node
\r
2594 next = sprite->nextsprite;
\r
2595 if (next) // if (!next), sprite is last in chain
\r
2596 next->prevptr = sprite->prevptr;
\r
2597 *sprite->prevptr = next;
\r
2600 // add it back to the free list
\r
2602 sprite->nextsprite = spritefreeptr;
\r
2603 spritefreeptr = sprite;
\r
2606 // null the users pointer, so next time that actor gets placed, it will
\r
2607 // allocate a new block
\r
2615 ====================
\r
2617 = RFL_EraseBlocks CGA
\r
2619 = Write mode 1 should be set
\r
2621 ====================
\r
2624 void RFL_EraseBlocks (void)
\r
2626 eraseblocktype *block,*done;
\r
2627 int screenxh,screenyh;
\r
2628 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2630 unsigned updatedelta;
\r
2632 block = &eraselist[0][0];
\r
2634 done = eraselistptr[0];
\r
2636 while (block != done)
\r
2640 // clip the block to the current screen view
\r
2642 block->screenx -= originxscreen;
\r
2643 block->screeny -= originyscreen;
\r
2645 if (block->screenx < 0)
\r
2647 block->width += block->screenx;
\r
2648 if (block->width<1)
\r
2650 block->screenx = 0;
\r
2653 if (block->screeny < 0)
\r
2655 block->height += block->screeny;
\r
2656 if (block->height<1)
\r
2658 block->screeny = 0;
\r
2661 screenxh = block->screenx + block->width;
\r
2662 screenyh = block->screeny + block->height;
\r
2664 if (screenxh > CGAPORTSCREENWIDE)
\r
2666 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2667 screenxh = block->screenx + block->width;
\r
2670 if (screenyh > PORTSCREENHIGH)
\r
2672 block->height = PORTSCREENHIGH-block->screeny;
\r
2673 screenyh = block->screeny + block->height;
\r
2676 if (block->width<1 || block->height<1)
\r
2680 // erase the block by copying from the master screen
\r
2682 //---- pos = gvar->video.ofs.ylookup[block->screeny]+block->screenx;
\r
2683 pos = (block->screeny*gvar->video.page[0].stridew)+block->screenx;
\r
2684 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2685 pos &= ~1; // make sure a word copy gets used
\r
2686 //++++ VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2687 block->width,block->height);
\r
2690 // put 2s in update where the block was, to force sprites to update
\r
2692 xtl = block->screenx >> SX_T_SHIFT;
\r
2693 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2694 ytl = block->screeny >> SY_T_SHIFT;
\r
2695 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2697 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2698 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2700 for (y=ytl;y<=yth;y++)
\r
2702 for (x=xtl;x<=xth;x++)
\r
2703 *updatespot++ = 2;
\r
2704 updatespot += updatedelta; // down to next line
\r
2710 eraselistptr[0] = &eraselist[0][0];
\r
2715 ====================
\r
2717 = RFL_UpdateSprites CGA
\r
2719 = NOTE: Implement vertical clipping!
\r
2721 ====================
\r
2724 void RFL_UpdateSprites (void)
\r
2726 spritelisttype *sprite;
\r
2727 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2730 byte *updatespot,*baseupdatespot;
\r
2731 unsigned updatedelta;
\r
2733 unsigned updatecount;
\r
2734 unsigned height,sourceofs;
\r
2741 for (priority=0;priority<PRIORITIES;priority++)
\r
2743 if (priority==MASKEDTILEPRIORITY)
\r
2744 RFL_MaskForegroundTiles ();
\r
2746 for (sprite = prioritystart[priority]; sprite ;
\r
2747 sprite = (spritelisttype *)sprite->nextsprite)
\r
2750 // see if the sprite has any visable area in the port
\r
2753 portx = sprite->screenx - originxscreen;
\r
2754 porty = sprite->screeny - originyscreen;
\r
2755 xtl = portx >> SX_T_SHIFT;
\r
2756 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2757 ytl = porty >> SY_T_SHIFT;
\r
2758 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2762 if (xth>=PORTTILESWIDE)
\r
2763 xth = PORTTILESWIDE-1;
\r
2766 if (yth>=PORTTILESHIGH)
\r
2767 yth = PORTTILESHIGH-1;
\r
2769 if (xtl>xth || ytl>yth)
\r
2773 // see if it's visable area covers any non 0 update tiles
\r
2775 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2776 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2778 if (sprite->updatecount)
\r
2780 sprite->updatecount--; // the sprite was just placed,
\r
2781 goto redraw; // so draw it for sure
\r
2784 for (y=ytl;y<=yth;y++)
\r
2786 for (x=xtl;x<=xth;x++)
\r
2787 if (*updatespot++)
\r
2789 updatespot += updatedelta; // down to next line
\r
2791 continue; // no need to update
\r
2795 // set the tiles it covers to 3, because those tiles are being
\r
2798 updatespot = baseupdatespot;
\r
2799 for (y=ytl;y<=yth;y++)
\r
2801 for (x=xtl;x<=xth;x++)
\r
2802 *updatespot++ = 3;
\r
2803 updatespot += updatedelta; // down to next line
\r
2808 height = sprite->height;
\r
2809 sourceofs = sprite->sourceofs;
\r
2812 height += porty; // clip top off
\r
2813 sourceofs -= porty*sprite->width;
\r
2816 else if (porty+height>PORTSCREENHIGH)
\r
2818 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2821 dest = bufferofs + gvar->video.ofs.ylookup[porty] + portx;
\r
2823 switch (sprite->draw)
\r
2826 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2827 dest,sprite->width,height,sprite->planesize);
\r
2845 =====================
\r
2849 = All routines will draw at the port at bufferofs, possibly copying from
\r
2850 = the port at masterofs. The EGA version then page flips, while the
\r
2851 = CGA version updates the screen from the buffer port.
\r
2853 = Screenpage is the currently displayed page, not the one being drawn
\r
2854 = Otherpage is the page to be worked with now
\r
2856 =====================
\r
2859 void RF_Refresh (void)
\r
2863 RFL_AnimateTiles ();
\r
2866 // update newly scrolled on tiles and animated tiles from the master screen
\r
2868 RFL_UpdateTiles ();
\r
2869 RFL_EraseBlocks ();
\r
2872 // Update is all 0 except where sprites have changed or new area has
\r
2873 // been scrolled on. Go through all sprites and update the ones that cover
\r
2874 // a non 0 update tile
\r
2876 RFL_UpdateSprites ();
\r
2879 // if the main program has a refresh hook set, call their function before
\r
2880 // displaying the new page
\r
2882 if (refreshvector)
\r
2886 // update everything to the screen
\r
2888 VW_CGAFullUpdate ();
\r
2891 // calculate tics since last refresh for adaptive timing
\r
2896 #endif // GRMODE == CGAGR
\r
2897 //===============================
\r
2899 ; Keen Dreams Source Code
\r
2900 ; Copyright (C) 2014 Javier M. Chavez
\r
2902 ; This program is free software; you can redistribute it and/or modify
\r
2903 ; it under the terms of the GNU General Public License as published by
\r
2904 ; the Free Software Foundation; either version 2 of the License, or
\r
2905 ; (at your option) any later version.
\r
2907 ; This program is distributed in the hope that it will be useful,
\r
2908 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
2909 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
2910 ; GNU General Public License for more details.
\r
2912 ; You should have received a copy of the GNU General Public License along
\r
2913 ; with this program; if not, write to the Free Software Foundation, Inc.,
\r
2914 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
2921 INCLUDE "ID_ASM.EQU"
\r
2923 CACHETILES = 1 ;enable master screen tile caching
\r
2925 ;============================================================================
\r
2930 UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1
\r
2934 EXTRN screenseg:WORD
\r
2935 EXTRN updateptr:WORD
\r
2936 EXTRN updatestart:WORD
\r
2937 EXTRN masterofs:WORD ;start of master tile port
\r
2938 EXTRN bufferofs:WORD ;start of current buffer port
\r
2939 EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem
\r
2941 EXTRN mapsegs:WORD
\r
2942 EXTRN originmap:WORD
\r
2943 EXTRN updatemapofs:WORD
\r
2944 EXTRN tilecache:WORD
\r
2945 EXTRN tinf:WORD ;seg pointer to map header and tile info
\r
2946 EXTRN blockstarts:WORD ;offsets from bufferofs for each update block
\r
2953 screenstartcs dw ? ;in code segment for accesability
\r
2959 ;============================================================================
\r
2961 ; CGA refresh routines
\r
2963 ;============================================================================
\r
2967 ;=================
\r
2971 ; Draws a composit two plane tile to the master screen and sets the update
\r
2972 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
2973 ; view pages the next two refreshes
\r
2975 ; Called to draw newlly scrolled on strips and animating tiles
\r
2977 ;=================
\r
2979 PROC RFL_NewTile updateoffset:WORD
\r
2980 PUBLIC RFL_NewTile
\r
2984 ; mark both update lists at this spot
\r
2986 mov di,[updateoffset]
\r
2988 mov bx,[updateptr] ;start of update matrix
\r
2989 mov [BYTE bx+di],1
\r
2991 mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line
\r
2994 ; set di to the location in screenseg to draw the tile
\r
2997 mov si,[updatemapofs+di] ;offset in map from origin
\r
2998 add si,[originmap]
\r
2999 mov di,[blockstarts+di] ;screen location for tile
\r
3000 add di,[masterofs]
\r
3003 ; set BX to the foreground tile number and SI to the background number
\r
3004 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
3005 ; as one of the planes totally eclipses the other
\r
3007 mov es,[mapsegs+2] ;foreground plane
\r
3009 mov es,[mapsegs] ;background plane
\r
3012 mov es,[screenseg]
\r
3016 jmp @@maskeddraw ;draw both together
\r
3020 ; Draw single background tile from main memory
\r
3026 mov ds,[grsegs+STARTTILE16*2+si]
\r
3028 xor si,si ;block is segment aligned
\r
3039 mov ds,ax ;restore turbo's data segment
\r
3045 ; Draw a masked tile combo
\r
3046 ; Interupts are disabled and the stack segment is reassigned
\r
3050 cli ; don't allow ints when SS is set
\r
3052 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3054 mov ds,[grsegs+STARTTILE16*2+si]
\r
3056 xor si,si ;first word of tile data
\r
3059 mov ax,[si] ;background tile
\r
3060 and ax,[ss:si] ;mask
\r
3061 or ax,[ss:si+64] ;masked data
\r
3063 mov ax,[si+2] ;background tile
\r
3064 and ax,[ss:si+2] ;mask
\r
3065 or ax,[ss:si+66] ;masked data
\r
3083 ;===========================================================================
\r
3085 ; EGA refresh routines
\r
3087 ;===========================================================================
\r
3091 ;=================
\r
3095 ; Draws a composit two plane tile to the master screen and sets the update
\r
3096 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
3097 ; view pages the next two refreshes
\r
3099 ; Called to draw newlly scrolled on strips and animating tiles
\r
3101 ; Assumes write mode 0
\r
3103 ;=================
\r
3105 PROC RFL_NewTile updateoffset:WORD
\r
3106 PUBLIC RFL_NewTile
\r
3110 ; mark both update lists at this spot
\r
3112 mov di,[updateoffset]
\r
3114 mov bx,[updatestart] ;page 0 pointer
\r
3115 mov [BYTE bx+di],1
\r
3116 mov bx,[updatestart+2] ;page 1 pointer
\r
3117 mov [BYTE bx+di],1
\r
3120 ; set screenstartcs to the location in screenseg to draw the tile
\r
3123 mov si,[updatemapofs+di] ;offset in map from origin
\r
3124 add si,[originmap]
\r
3125 mov di,[blockstarts+di] ;screen location for tile
\r
3126 add di,[masterofs]
\r
3127 mov [cs:screenstartcs],di
\r
3130 ; set BX to the foreground tile number and SI to the background number
\r
3131 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
3132 ; as one of the planes totally eclipses the other
\r
3134 mov es,[mapsegs+2] ;foreground plane
\r
3136 mov es,[mapsegs] ;background plane
\r
3139 mov es,[screenseg]
\r
3140 mov dx,SC_INDEX ;for stepping through map mask planes
\r
3144 jmp @@maskeddraw ;draw both together
\r
3148 ; No foreground tile, so draw a single background tile.
\r
3149 ; Use the master screen cache if possible
\r
3154 mov bx,SCREENWIDTH-2 ;add to get to start of next line
\r
3161 mov ax,[tilecache+si]
\r
3166 ; Draw single tile from cache
\r
3172 mov ax,SC_MAPMASK + 15*256 ;all planes
\r
3176 mov ax,GC_MODE + 1*256 ;write mode 1
\r
3179 mov di,[cs:screenstartcs]
\r
3180 mov ds,[screenseg]
\r
3191 xor ah,ah ;write mode 0
\r
3195 mov ds,ax ;restore turbo's data segment
\r
3200 ; Draw single tile from main memory
\r
3205 mov ax,[cs:screenstartcs]
\r
3206 mov [tilecache+si],ax ;next time it can be drawn from here with latch
\r
3207 mov ds,[grsegs+STARTTILE16*2+si]
\r
3209 xor si,si ;block is segment aligned
\r
3211 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3213 mov cx,4 ;draw four planes
\r
3218 mov di,[cs:screenstartcs] ;start at same place in all planes
\r
3226 shl ah,1 ;shift plane mask over for next plane
\r
3230 mov ds,ax ;restore turbo's data segment
\r
3236 ; Draw a masked tile combo
\r
3237 ; Interupts are disabled and the stack segment is reassigned
\r
3241 cli ; don't allow ints when SS is set
\r
3243 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3245 mov ds,[grsegs+STARTTILE16*2+si]
\r
3247 xor si,si ;first word of tile data
\r
3249 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3251 mov di,[cs:screenstartcs]
\r
3257 mov bx,[si+tileofs] ;background tile
\r
3258 and bx,[ss:tileofs] ;mask
\r
3259 or bx,[ss:si+tileofs+32] ;masked data
\r
3260 mov [es:di+lineoffset],bx
\r
3261 tileofs = tileofs + 2
\r
3262 lineoffset = lineoffset + SCREENWIDTH
\r
3265 shl ah,1 ;shift plane mask over for next plane
\r
3267 je @@done ;drawn all four planes
\r
3281 ;============================================================================
\r
3283 ; VGA refresh routines
\r
3285 ;============================================================================
\r
3291 ;============================================================================
\r
3293 ; reasonably common refresh routines
\r
3295 ;============================================================================
\r
3298 ;=================
\r
3302 ; Scans through the update matrix pointed to by updateptr, looking for 1s.
\r
3303 ; A 1 represents a tile that needs to be copied from the master screen to the
\r
3304 ; current screen (a new row or an animated tiled). If more than one adjacent
\r
3305 ; tile in a horizontal row needs to be copied, they will be copied as a group.
\r
3307 ; Assumes write mode 1
\r
3309 ;=================
\r
3312 ; AX 0/1 for scasb, temp for segment register transfers
\r
3313 ; BX width for block copies
\r
3315 ; DX line width deltas
\r
3316 ; SI source for copies
\r
3317 ; DI scas dest / movsb dest
\r
3318 ; BP pointer to UPDATETERMINATE
\r
3324 PROC RFL_UpdateTiles
\r
3325 PUBLIC RFL_UpdateTiles
\r
3328 jmp SHORT @@realstart
\r
3331 ; all tiles have been scanned
\r
3336 mov di,[updateptr]
\r
3337 mov bp,(TILESWIDE+1)*TILESHIGH+1
\r
3338 add bp,di ; when di = bx, all tiles have been scanned
\r
3340 mov cx,-1 ; definately scan the entire thing
\r
3343 ; scan for a 1 in the update list, meaning a tile needs to be copied
\r
3344 ; from the master screen to the current screen
\r
3347 pop di ; place to continue scaning from
\r
3349 mov es,ax ; search in the data segment
\r
3362 ; copy a single tile
\r
3367 inc di ; we know the next tile is nothing
\r
3368 push di ; save off the spot being scanned
\r
3369 sub di,[updateptr]
\r
3371 mov di,[blockstarts-4+di] ; start of tile location on screen
\r
3373 add di,[bufferofs] ; dest in current screen
\r
3374 add si,[masterofs] ; source in master screen
\r
3376 mov dx,SCREENWIDTH-TILEWIDTH
\r
3377 mov ax,[screenseg]
\r
3381 ;--------------------------
\r
3396 ;--------------------------
\r
3411 ;--------------------------
\r
3417 ; more than one tile in a row needs to be updated, so do it as a group
\r
3422 mov dx,di ; hold starting position + 1 in dx
\r
3423 inc di ; we know the next tile also gets updated
\r
3424 repe scasb ; see how many more in a row
\r
3425 push di ; save off the spot being scanned
\r
3428 sub bx,dx ; number of tiles in a row
\r
3429 shl bx,1 ; number of bytes / row
\r
3431 mov di,dx ; lookup position of start tile
\r
3432 sub di,[updateptr]
\r
3434 mov di,[blockstarts-2+di] ; start of tile location
\r
3436 add di,[bufferofs] ; dest in current screen
\r
3437 add si,[masterofs] ; source in master screen
\r
3439 mov dx,SCREENWIDTH
\r
3440 sub dx,bx ; offset to next line on screen
\r
3442 sub dx,bx ; bx is words wide in CGA tiles
\r
3445 mov ax,[screenseg]
\r
3468 dec cx ; was 0 from last rep movsb, now $ffff for scasb
\r
3474 ;============================================================================
\r
3477 ;=================
\r
3479 ; RFL_MaskForegroundTiles
\r
3481 ; Scan through update looking for 3's. If the foreground tile there is a
\r
3482 ; masked foreground tile, draw it to the screen
\r
3484 ;=================
\r
3486 PROC RFL_MaskForegroundTiles
\r
3487 PUBLIC RFL_MaskForegroundTiles
\r
3489 jmp SHORT @@realstart
\r
3492 ; all tiles have been scanned
\r
3497 mov di,[updateptr]
\r
3498 mov bp,(TILESWIDE+1)*TILESHIGH+2
\r
3499 add bp,di ; when di = bx, all tiles have been scanned
\r
3501 mov cx,-1 ; definately scan the entire thing
\r
3503 ; scan for a 3 in the update list
\r
3507 mov es,ax ; scan in the data segment
\r
3509 pop di ; place to continue scaning from
\r
3516 ; found a tile, see if it needs to be masked on
\r
3522 sub di,[updateptr]
\r
3524 mov si,[updatemapofs-2+di] ; offset from originmap
\r
3525 add si,[originmap]
\r
3527 mov es,[mapsegs+2] ; foreground map plane segment
\r
3528 mov si,[es:si] ; foreground tile number
\r
3531 jz @@findtile ; 0 = no foreground tile
\r
3534 add bx,INTILE ;INTILE tile info table
\r
3536 test [BYTE PTR es:bx],80h ;high bit = masked tile
\r
3539 ;-------------------
\r
3542 ;=================
\r
3544 ; mask the tile CGA
\r
3546 ;=================
\r
3548 mov di,[blockstarts-2+di]
\r
3549 add di,[bufferofs]
\r
3550 mov es,[screenseg]
\r
3552 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3554 mov bx,64 ;data starts 64 bytes after mask
\r
3560 mov ax,[es:di+lineoffset] ;background
\r
3562 or ax,[si+bx] ;masked data
\r
3563 mov [es:di+lineoffset],ax ;background
\r
3566 mov ax,[es:di+lineoffset+2] ;background
\r
3568 or ax,[si+bx] ;masked data
\r
3569 mov [es:di+lineoffset+2],ax ;background
\r
3572 lineoffset = lineoffset + SCREENWIDTH
\r
3576 ;-------------------
\r
3579 ;=================
\r
3583 ;=================
\r
3585 mov [BYTE planemask],1
\r
3586 mov [BYTE planenum],0
\r
3588 mov di,[blockstarts-2+di]
\r
3589 add di,[bufferofs]
\r
3590 mov [cs:screenstartcs],di
\r
3591 mov es,[screenseg]
\r
3593 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3595 mov bx,32 ;data starts 32 bytes after mask
\r
3600 mov ah,[ss:planemask]
\r
3604 mov ah,[ss:planenum]
\r
3608 mov di,[cs:screenstartcs]
\r
3611 mov cx,[es:di+lineoffset] ;background
\r
3613 or cx,[si+bx] ;masked data
\r
3616 mov [es:di+lineoffset],cx
\r
3617 lineoffset = lineoffset + SCREENWIDTH
\r
3619 add bx,32 ;the mask is now further away
\r
3621 shl [ss:planemask],1 ;shift plane mask over for next plane
\r
3622 cmp [ss:planemask],10000b ;done all four planes?
\r
3623 je @@drawn ;drawn all four planes
\r
3629 ;-------------------
\r
3633 mov cx,-1 ;definately scan the entire thing
\r