]> 4ch.mooo.com Git - 16.git/blob - src/lib/16_rf.c
46400385631dd51e43858d4839d72842b245e047
[16.git] / src / lib / 16_rf.c
1 /* Keen Dreams Source Code\r
2  * Copyright (C) 2014 Javier M. Chavez\r
3  *\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
8  *\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
13  *\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
17  */\r
18 \r
19 // 16_RF.C\r
20 \r
21 /*\r
22 =============================================================================\r
23 \r
24 notes\r
25 -----\r
26 \r
27 scrolling more than one tile / refresh forces a total redraw\r
28 \r
29 two overlapping sprites of equal priority can change drawing order when\r
30 updated\r
31 \r
32 =============================================================================\r
33 */\r
34 \r
35 #include "src/lib/16_rf.h"\r
36 #pragma hdrstop\r
37 \r
38 /*\r
39 =============================================================================\r
40 \r
41                                                  LOCAL CONSTANTS\r
42 \r
43 =============================================================================\r
44 */\r
45 \r
46 #define SCREENTILESWIDE 20\r
47 #define SCREENTILESHIGH 13\r
48 \r
49 #define SCREENSPACE             (SCREENWIDTH*240)\r
50 #define FREEEGAMEM              (0x10000l-3l*SCREENSPACE)\r
51 \r
52 //\r
53 // the update array must have enough space for two screens that can float\r
54 // up two two tiles each way\r
55 //\r
56 // (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared\r
57 // by word width instructions\r
58 \r
59 #define UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
60 #define UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
61 #define UPDATESIZE                      (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
62 \r
63 #define G_EGASX_SHIFT   7       // global >> ?? = screen x\r
64 #define G_CGASX_SHIFT   6       // global >> ?? = screen x\r
65 #define G_SY_SHIFT              4       // global >> ?? = screen y\r
66 \r
67 unsigned        SX_T_SHIFT;             // screen x >> ?? = tile EGA = 1, CGA = 2;\r
68 #define SY_T_SHIFT              4       // screen y >> ?? = tile\r
69 \r
70 \r
71 #define EGAPORTSCREENWIDE       42\r
72 #define CGAPORTSCREENWIDE       84\r
73 #define PORTSCREENHIGH  224\r
74 \r
75 #define UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
76 #define UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
77 #define UPDATESIZE                      (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
78 \r
79 #define MAXSCROLLEDGES  6\r
80 \r
81 /*\r
82 =============================================================================\r
83 \r
84                                                    LOCAL TYPES\r
85 \r
86 =============================================================================\r
87 */\r
88 \r
89 typedef struct spriteliststruct\r
90 {\r
91         int                     screenx,screeny;\r
92         int                     width,height;\r
93 \r
94         unsigned        grseg,sourceofs,planesize;\r
95         drawtype        draw;\r
96         unsigned        tilex,tiley,tilewide,tilehigh;\r
97         int                     priority,updatecount;\r
98         struct spriteliststruct **prevptr,*nextsprite;\r
99 } spritelisttype;\r
100 \r
101 \r
102 typedef struct\r
103 {\r
104         int                     screenx,screeny;\r
105         int                     width,height;\r
106 } eraseblocktype;\r
107 \r
108 \r
109 typedef struct\r
110 {\r
111         unsigned        current;                // foreground tiles have high bit set\r
112         int                     count;\r
113 } tiletype;\r
114 \r
115 \r
116 typedef struct animtilestruct\r
117 {\r
118         unsigned        x,y,tile;\r
119         tiletype        *chain;\r
120         unsigned        far *mapplane;\r
121         struct animtilestruct **prevptr,*nexttile;\r
122 } animtiletype;\r
123 \r
124 /*\r
125 =============================================================================\r
126 \r
127                                                  GLOBAL VARIABLES\r
128 \r
129 =============================================================================\r
130 */\r
131 \r
132 unsigned        tics;\r
133 long            lasttimecount;\r
134 \r
135 boolean         compatability;                  // crippled refresh for wierdo SVGAs\r
136 \r
137 unsigned        mapwidth,mapheight,mapbyteswide,mapwordswide\r
138                         ,mapbytesextra,mapwordsextra;\r
139 unsigned        mapbwidthtable[MAXMAPHEIGHT];\r
140 \r
141 //\r
142 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow\r
143 // for fractional movement and acceleration.\r
144 //\r
145 // Tiles  : Tile offsets from the upper left corner of the current map.\r
146 //\r
147 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.\r
148 // originxscreen is the same spot as originxtile, just with extra precision\r
149 // so graphics don't need to be done in tile boundaries.\r
150 //\r
151 \r
152 unsigned        originxglobal,originyglobal;\r
153 unsigned        originxtile,originytile;\r
154 unsigned        originxscreen,originyscreen;\r
155 unsigned        originmap;\r
156 unsigned        originxmin,originxmax,originymin,originymax;\r
157 unsigned        originxtile,originytile;\r
158 \r
159 unsigned        masterofs;\r
160 \r
161 //\r
162 // Table of the offsets from bufferofs of each tile spot in the\r
163 // view port.  The extra wide tile should never be drawn, but the space\r
164 // is needed to account for the extra 0 in the update arrays.  Built by\r
165 // RF_Startup\r
166 //\r
167 \r
168 unsigned        blockstarts[UPDATEWIDE*UPDATEHIGH];\r
169 unsigned        updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
170 \r
171 unsigned        uwidthtable[PORTTILESHIGH];             // lookup instead of multiply\r
172 \r
173 byte            update[2][UPDATESIZE];\r
174 byte            *updateptr,*baseupdateptr,                                              // current start of update window\r
175                         *updatestart[2],\r
176                         *baseupdatestart[2];\r
177 \r
178 //from others\r
179 cardtype        videocard;              // set by VW_Startup\r
180 grtype          grmode;                 // CGAgr, EGAgr, VGAgr\r
181 \r
182 unsigned        bufferofs;              // hidden area to draw to before displaying\r
183 unsigned        displayofs;             // origin of the visable screen\r
184 //\r
185 \r
186 /*\r
187 =============================================================================\r
188 \r
189                                                  LOCAL VARIABLES\r
190 \r
191 =============================================================================\r
192 */\r
193 #ifdef PROFILE\r
194 static          char    scratch[20],str[20];\r
195 #endif\r
196 \r
197 tiletype        allanims[MAXANIMTYPES];\r
198 unsigned        numanimchains;\r
199 \r
200 void            (*refreshvector) (void);\r
201 \r
202 unsigned        screenstart[3] =\r
203         {0,SCREENSPACE,SCREENSPACE*2};\r
204 \r
205 unsigned        xpanmask;                       // prevent panning to odd pixels\r
206 \r
207 unsigned        screenpage;                     // screen currently being displayed\r
208 unsigned        otherpage;\r
209 \r
210 #if GRMODE == EGAGR\r
211 unsigned        tilecache[NUMTILE16];\r
212 #endif\r
213 \r
214 spritelisttype  spritearray[MAXSPRITES],*prioritystart[PRIORITIES],\r
215                                 *spritefreeptr;\r
216 \r
217 animtiletype    animarray[MAXANIMTILES],*animhead,*animfreeptr;\r
218 \r
219 int                             animfreespot;\r
220 \r
221 eraseblocktype  eraselist[2][MAXSPRITES],*eraselistptr[2];\r
222 \r
223 /*\r
224 =============================================================================\r
225 \r
226                                                  LOCAL PROTOTYPES\r
227 \r
228 =============================================================================\r
229 */\r
230 \r
231 void RFL_NewTile (unsigned updateoffset);\r
232 void RFL_MaskForegroundTiles (void);\r
233 void RFL_UpdateTiles (void);\r
234 \r
235 void RFL_BoundScroll (int x, int y);//++++??\r
236 void RFL_CalcOriginStuff (long x, long y);\r
237 void RFL_ClearScrollBlocks (void);//++++??\r
238 void RFL_InitSpriteList (void);\r
239 void RFL_InitAnimList (void);\r
240 void RFL_CheckForAnimTile (unsigned x, unsigned y);\r
241 void RFL_AnimateTiles (void);\r
242 void RFL_RemoveAnimsOnX (unsigned x);\r
243 void RFL_RemoveAnimsOnY (unsigned y);\r
244 void RFL_EraseBlocks (void);\r
245 void RFL_UpdateSprites (void);\r
246 \r
247 \r
248 /*\r
249 =============================================================================\r
250 \r
251                                          GRMODE INDEPENDANT ROUTINES\r
252 \r
253 =============================================================================\r
254 */\r
255 \r
256 \r
257 /*\r
258 =====================\r
259 =\r
260 = RF_Startup\r
261 =\r
262 =====================\r
263 */\r
264 \r
265 static  char *ParmStrings[] = {"comp",""};\r
266 \r
267 void RF_Startup (void)\r
268 {\r
269         int i,x,y;\r
270         unsigned        *blockstart;\r
271 \r
272         if (grmode == EGAGR)\r
273                 for (i = 1;i < _argc;i++)\r
274                         if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
275                         {\r
276                                 compatability = true;\r
277                                 break;\r
278                         }\r
279 \r
280         for (i=0;i<PORTTILESHIGH;i++)\r
281                 uwidthtable[i] = UPDATEWIDE*i;\r
282 \r
283         originxmin = originymin = MAPBORDER*TILEGLOBAL;\r
284 \r
285         eraselistptr[0] = &eraselist[0][0];\r
286         eraselistptr[1] = &eraselist[1][0];\r
287 \r
288 \r
289 \r
290         if (grmode == EGAGR)\r
291         {\r
292                 SX_T_SHIFT = 1;\r
293 \r
294                 baseupdatestart[0] = &update[0][UPDATESPARESIZE];\r
295                 baseupdatestart[1] = &update[1][UPDATESPARESIZE];\r
296 \r
297                 screenpage = 0;\r
298                 otherpage = 1;\r
299                 displayofs = screenstart[screenpage];\r
300                 bufferofs = screenstart[otherpage];\r
301                 masterofs = screenstart[2];\r
302 \r
303                 updateptr = baseupdatestart[otherpage];\r
304 \r
305                 blockstart = &blockstarts[0];\r
306                 for (y=0;y<UPDATEHIGH;y++)\r
307                         for (x=0;x<UPDATEWIDE;x++)\r
308                                 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
309 \r
310                 xpanmask = 6;   // dont pan to odd pixels\r
311         }\r
312 \r
313         else if (grmode == CGAGR)\r
314         {\r
315                 SX_T_SHIFT = 2;\r
316 \r
317                 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];\r
318 \r
319                 bufferofs = 0;\r
320                 masterofs = 0x8000;\r
321 \r
322                 blockstart = &blockstarts[0];\r
323                 for (y=0;y<UPDATEHIGH;y++)\r
324                         for (x=0;x<UPDATEWIDE;x++)\r
325                                 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
326         }\r
327 }\r
328 \r
329 \r
330 \r
331 \r
332 /*\r
333 =====================\r
334 =\r
335 = RF_Shutdown\r
336 =\r
337 =====================\r
338 */\r
339 \r
340 void RF_Shutdown (void)\r
341 {\r
342 \r
343 }\r
344 \r
345 //===========================================================================\r
346 \r
347 \r
348 /*\r
349 =====================\r
350 =\r
351 = RF_FixOfs\r
352 =\r
353 = Sets bufferofs,displayofs, and masterofs to regular values, for the\r
354 = occasions when you have moved them around manually\r
355 =\r
356 =====================\r
357 */\r
358 \r
359 void RF_FixOfs (void)\r
360 {\r
361         if (grmode == EGAGR)\r
362         {\r
363                 screenpage = 0;\r
364                 otherpage = 1;\r
365                 panx = pany = pansx = pansy = panadjust = 0;\r
366                 displayofs = screenstart[screenpage];\r
367                 bufferofs = screenstart[otherpage];\r
368                 masterofs = screenstart[2];\r
369                 VW_SetScreen (displayofs,0);\r
370         }\r
371         else\r
372         {\r
373                 bufferofs = 0;\r
374                 masterofs = 0x8000;\r
375         }\r
376 }\r
377 \r
378 \r
379 //===========================================================================\r
380 \r
381 /*\r
382 =====================\r
383 =\r
384 = RF_NewMap\r
385 =\r
386 = Makes some convienient calculations based on maphead->\r
387 =\r
388 =====================\r
389 */\r
390 /*++++\r
391 void RF_NewMap (void)\r
392 {\r
393         int i,x,y;\r
394         unsigned spot,*table;\r
395 \r
396         mapwidth = mapheaderseg[mapon]->width;\r
397         mapbyteswide = 2*mapwidth;\r
398         mapheight = mapheaderseg[mapon]->height;\r
399         mapwordsextra = mapwidth-PORTTILESWIDE;\r
400         mapbytesextra = 2*mapwordsextra;\r
401 \r
402 //\r
403 // make a lookup table for the maps left edge\r
404 //\r
405         if (mapheight > MAXMAPHEIGHT)\r
406         Quit ("RF_NewMap: Map too tall!");\r
407         spot = 0;\r
408         for (i=0;i<mapheight;i++)\r
409         {\r
410           mapbwidthtable[i] = spot;\r
411           spot += mapbyteswide;\r
412         }\r
413 \r
414 //\r
415 // fill in updatemapofs with the new width info\r
416 //\r
417         table = &updatemapofs[0];\r
418         for (y=0;y<PORTTILESHIGH;y++)\r
419                 for (x=0;x<UPDATEWIDE;x++)\r
420                         *table++ = mapbwidthtable[y]+x*2;\r
421 \r
422 //\r
423 // the y max value clips off the bottom half of a tile so a map that is\r
424 // 13 + MAPBORDER*2 tile high will not scroll at all vertically\r
425 //\r
426         originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;\r
427         originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;\r
428         if (originxmax<originxmin)              // for very small maps\r
429                 originxmax=originxmin;\r
430         if (originymax<originymin)\r
431                 originymax=originymin;\r
432 \r
433 //\r
434 // clear out the lists\r
435 //\r
436         RFL_InitSpriteList ();\r
437         RFL_InitAnimList ();\r
438         RFL_ClearScrollBlocks ();\r
439         RF_SetScrollBlock (0,MAPBORDER-1,true);\r
440         RF_SetScrollBlock (0,mapheight-MAPBORDER,true);\r
441         RF_SetScrollBlock (MAPBORDER-1,0,false);\r
442         RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);\r
443 \r
444 \r
445         lasttimecount = TimeCount;              // setup for adaptive timing\r
446         tics = 1;\r
447 }\r
448 */\r
449 //===========================================================================\r
450 \r
451 /*\r
452 ==========================\r
453 =\r
454 = RF_MarkTileGraphics\r
455 =\r
456 = Goes through mapplane[0/1] and marks all background/foreground tiles\r
457 = needed, then follows all animation sequences to make sure animated\r
458 = tiles get all the stages.  Every unique animating tile is given an\r
459 = entry in allanims[], so every instance of that tile will animate at the\r
460 = same rate.  The info plane for each animating tile will hold a pointer\r
461 = into allanims[], therefore you can't have both an animating foreground\r
462 = and background tile in the same spot!\r
463 =\r
464 ==========================\r
465 */\r
466 /*++++\r
467 void RF_MarkTileGraphics (void)\r
468 {\r
469         unsigned        size;\r
470         int                     tile,next,anims;\r
471         unsigned        far     *start,far *end,far *info;\r
472         unsigned        i,tilehigh;\r
473 \r
474         memset (allanims,0,sizeof(allanims));\r
475         numanimchains = 0;\r
476 \r
477         size = mapwidth*mapheight;\r
478 \r
479 //\r
480 // background plane\r
481 //\r
482         start = mapsegs[0];\r
483         info = mapsegs[2];\r
484         end = start+size;\r
485         do\r
486         {\r
487                 tile = *start++;\r
488                 if (tile>=0)                    // <0 is a tile that is never drawn\r
489                 {\r
490                         CA_MarkGrChunk(STARTTILE16+tile);\r
491                         if (tinf[ANIM+tile])\r
492                         {\r
493                                 // this tile will animated\r
494 \r
495                                 for (i=0;i<numanimchains;i++)\r
496                                         if (allanims[i].current == tile)\r
497                                         {\r
498                                                 *info = (unsigned)&allanims[i];\r
499                                                 goto nextback;\r
500                                         }\r
501 \r
502                                 // new chain of animating tiles\r
503 \r
504                                 if (i>=MAXANIMTYPES)\r
505                                         Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
506                                 allanims[i].current = tile;\r
507                                 allanims[i].count = tinf[SPEED+tile];\r
508 \r
509                                 *info = (unsigned)&allanims[i];\r
510                                 numanimchains++;\r
511 \r
512                                 anims = 0;\r
513                                 next = tile+(signed char)(tinf[ANIM+tile]);\r
514                                 while (next != tile)\r
515                                 {\r
516                                         CA_MarkGrChunk(STARTTILE16+next);\r
517                                         next += (signed char)(tinf[ANIM+next]);\r
518                                         if (++anims > 20)\r
519                                                 Quit ("MarkTileGraphics: Unending animation!");\r
520                                 }\r
521 \r
522                         }\r
523                 }\r
524 nextback:\r
525                 info++;\r
526         } while (start<end);\r
527 \r
528 //\r
529 // foreground plane\r
530 //\r
531         start = mapsegs[1];\r
532         info = mapsegs[2];\r
533         end = start+size;\r
534         do\r
535         {\r
536                 tile = *start++;\r
537                 if (tile>=0)                    // <0 is a tile that is never drawn\r
538                 {\r
539                         CA_MarkGrChunk(STARTTILE16M+tile);\r
540                         if (tinf[MANIM+tile])\r
541                         {\r
542                                 // this tile will animated\r
543 \r
544                                 tilehigh = tile | 0x8000;       // foreground tiles have high bit\r
545                                 for (i=0;i<numanimchains;i++)\r
546                                         if (allanims[i].current == tilehigh)\r
547                                         {\r
548                                                 *info = (unsigned)&allanims[i];\r
549                                                 goto nextfront;\r
550                                         }\r
551 \r
552                                 // new chain of animating tiles\r
553 \r
554                                 if (i>=MAXANIMTYPES)\r
555                                         Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
556                                 allanims[i].current = tilehigh;\r
557                                 allanims[i].count = tinf[MSPEED+tile];\r
558 \r
559                                 *info = (unsigned)&allanims[i];\r
560                                 numanimchains++;\r
561 \r
562                                 anims = 0;\r
563                                 next = tile+(signed char)(tinf[MANIM+tile]);\r
564                                 while (next != tile)\r
565                                 {\r
566                                         CA_MarkGrChunk(STARTTILE16M+next);\r
567                                         next += (signed char)(tinf[MANIM+next]);\r
568                                         if (++anims > 20)\r
569                                                 Quit ("MarkTileGraphics: Unending animation!");\r
570                                 }\r
571 \r
572                         }\r
573                 }\r
574 nextfront:\r
575                 info++;\r
576         } while (start<end);\r
577 }\r
578 */\r
579 \r
580 //===========================================================================\r
581 \r
582 \r
583 /*\r
584 =========================\r
585 =\r
586 = RFL_InitAnimList\r
587 =\r
588 = Call to clear out the entire animating tile list and return all of them to\r
589 = the free list.\r
590 =\r
591 =========================\r
592 */\r
593 \r
594 void RFL_InitAnimList (void)\r
595 {\r
596         int     i;\r
597 \r
598         animfreeptr = &animarray[0];\r
599 \r
600         for (i=0;i<MAXANIMTILES-1;i++)\r
601                 animarray[i].nexttile = &animarray[i+1];\r
602 \r
603         animarray[i].nexttile = NULL;\r
604 \r
605         animhead = NULL;                        // nothing in list\r
606 }\r
607 \r
608 \r
609 /*\r
610 ====================\r
611 =\r
612 = RFL_CheckForAnimTile\r
613 =\r
614 ====================\r
615 */\r
616 /*++++\r
617 void RFL_CheckForAnimTile (unsigned x, unsigned y)\r
618 {\r
619         unsigned        tile,offset,speed,lasttime,thistime,timemissed;\r
620         unsigned        far *map;\r
621         animtiletype    *anim,*next;\r
622 \r
623 // the info plane of each animating tile has a near pointer into allanims[]\r
624 // which gives the current state of all concurrently animating tiles\r
625 \r
626         offset = mapbwidthtable[y]/2+x;\r
627 \r
628 //\r
629 // background\r
630 //\r
631         map = mapsegs[0]+offset;\r
632         tile = *map;\r
633         if (tinf[ANIM+tile] && tinf[SPEED+tile])\r
634         {\r
635                 if (!animfreeptr)\r
636                         Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
637                 anim = animfreeptr;\r
638                 animfreeptr = animfreeptr->nexttile;\r
639                 next = animhead;                                // stick it at the start of the list\r
640                 animhead = anim;\r
641                 if (next)\r
642                         next->prevptr = &anim->nexttile;\r
643                 anim->nexttile = next;\r
644                 anim->prevptr = &animhead;\r
645 \r
646                 anim->x = x;\r
647                 anim->y = y;\r
648                 anim->tile = tile;\r
649                 anim->mapplane = map;\r
650                 anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
651         }\r
652 \r
653 //\r
654 // foreground\r
655 //\r
656         map = mapsegs[1]+offset;\r
657         tile = *map;\r
658         if (tinf[MANIM+tile] && tinf[MSPEED+tile])\r
659         {\r
660                 if (!animfreeptr)\r
661                         Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
662                 anim = animfreeptr;\r
663                 animfreeptr = animfreeptr->nexttile;\r
664                 next = animhead;                                // stick it at the start of the list\r
665                 animhead = anim;\r
666                 if (next)\r
667                         next->prevptr = &anim->nexttile;\r
668                 anim->nexttile = next;\r
669                 anim->prevptr = &animhead;\r
670 \r
671                 anim->x = x;\r
672                 anim->y = y;\r
673                 anim->tile = tile;\r
674                 anim->mapplane = map;\r
675                 anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
676         }\r
677 \r
678 }\r
679 */\r
680 \r
681 /*\r
682 ====================\r
683 =\r
684 = RFL_RemoveAnimsOnX\r
685 =\r
686 ====================\r
687 */\r
688 \r
689 void RFL_RemoveAnimsOnX (unsigned x)\r
690 {\r
691         animtiletype *current,*next;\r
692 \r
693         current = animhead;\r
694         while (current)\r
695         {\r
696                 if (current->x == x)\r
697                 {\r
698                         *(void **)current->prevptr = current->nexttile;\r
699                         if (current->nexttile)\r
700                                 current->nexttile->prevptr = current->prevptr;\r
701                         next = current->nexttile;\r
702                         current->nexttile = animfreeptr;\r
703                         animfreeptr = current;\r
704                         current = next;\r
705                 }\r
706                 else\r
707                         current = current->nexttile;\r
708         }\r
709 }\r
710 \r
711 \r
712 /*\r
713 ====================\r
714 =\r
715 = RFL_RemoveAnimsOnY\r
716 =\r
717 ====================\r
718 */\r
719 \r
720 void RFL_RemoveAnimsOnY (unsigned y)\r
721 {\r
722         animtiletype *current,*next;\r
723 \r
724         current = animhead;\r
725         while (current)\r
726         {\r
727                 if (current->y == y)\r
728                 {\r
729                         *(void **)current->prevptr = current->nexttile;\r
730                         if (current->nexttile)\r
731                                 current->nexttile->prevptr = current->prevptr;\r
732                         next = current->nexttile;\r
733                         current->nexttile = animfreeptr;\r
734                         animfreeptr = current;\r
735                         current = next;\r
736                 }\r
737                 else\r
738                         current = current->nexttile;\r
739         }\r
740 }\r
741 \r
742 \r
743 /*\r
744 ====================\r
745 =\r
746 = RFL_RemoveAnimsInBlock\r
747 =\r
748 ====================\r
749 */\r
750 \r
751 void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)\r
752 {\r
753         animtiletype *current,*next;\r
754 \r
755         current = animhead;\r
756         while (current)\r
757         {\r
758                 if (current->x - x < width && current->y - y < height)\r
759                 {\r
760                         *(void **)current->prevptr = current->nexttile;\r
761                         if (current->nexttile)\r
762                                 current->nexttile->prevptr = current->prevptr;\r
763                         next = current->nexttile;\r
764                         current->nexttile = animfreeptr;\r
765                         animfreeptr = current;\r
766                         current = next;\r
767                 }\r
768                 else\r
769                         current = current->nexttile;\r
770         }\r
771 }\r
772 \r
773 \r
774 /*\r
775 ====================\r
776 =\r
777 = RFL_AnimateTiles\r
778 =\r
779 ====================\r
780 */\r
781 /*++++\r
782 void RFL_AnimateTiles (void)\r
783 {\r
784         animtiletype *current;\r
785         unsigned        updateofs,tile,x,y;\r
786         tiletype        *anim;\r
787 \r
788 //\r
789 // animate the lists of tiles\r
790 //\r
791         anim = &allanims[0];\r
792         while (anim->current)\r
793         {\r
794                 anim->count-=tics;\r
795                 while ( anim->count < 1)\r
796                 {\r
797                         if (anim->current & 0x8000)\r
798                         {\r
799                                 tile = anim->current & 0x7fff;\r
800                                 tile += (signed char)tinf[MANIM+tile];\r
801                                 anim->count += tinf[MSPEED+tile];\r
802                                 tile |= 0x8000;\r
803                         }\r
804                         else\r
805                         {\r
806                                 tile = anim->current;\r
807                                 tile += (signed char)tinf[ANIM+tile];\r
808                                 anim->count += tinf[SPEED+tile];\r
809                         }\r
810                         anim->current = tile;\r
811                 }\r
812                 anim++;\r
813         }\r
814 \r
815 \r
816 //\r
817 // traverse the list of animating tiles\r
818 //\r
819         current = animhead;\r
820         while (current)\r
821         {\r
822                 tile =current->chain->current;\r
823                 if ( tile != current->tile)\r
824                 {\r
825                 // tile has animated\r
826                 //\r
827                 // remove tile from master screen cache,\r
828                 // change a tile to its next state, set the structure up for\r
829                 // next animation, and post an update region to both update pages\r
830                 //\r
831                         current->tile = tile;\r
832 \r
833                         *(current->mapplane) = tile & 0x7fff;           // change in map\r
834 \r
835 #if GRMODE == EGAGR\r
836                         if (tile<0x8000)                // background\r
837                                 tilecache[tile] = 0;\r
838 #endif\r
839 \r
840                         x = current->x-originxtile;\r
841                         y = current->y-originytile;\r
842 \r
843                         if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)\r
844                                 Quit ("RFL_AnimateTiles: Out of bounds!");\r
845 \r
846                         updateofs = uwidthtable[y] + x;\r
847                         RFL_NewTile(updateofs);                         // puts "1"s in both pages\r
848                 }\r
849                 current = current->nexttile;\r
850         }\r
851 }\r
852 */\r
853 \r
854 //===========================================================================\r
855 \r
856 /*\r
857 =========================\r
858 =\r
859 = RFL_InitSpriteList\r
860 =\r
861 = Call to clear out the entire sprite list and return all of them to\r
862 = the free list.\r
863 =\r
864 =========================\r
865 */\r
866 \r
867 void RFL_InitSpriteList (void)\r
868 {\r
869         int     i;\r
870 \r
871         spritefreeptr = &spritearray[0];\r
872         for (i=0;i<MAXSPRITES-1;i++)\r
873                 spritearray[i].nextsprite = &spritearray[i+1];\r
874 \r
875         spritearray[i].nextsprite = NULL;\r
876 \r
877 // NULL in all priority levels\r
878 \r
879         memset (prioritystart,0,sizeof(prioritystart));\r
880 }\r
881 \r
882 //===========================================================================\r
883 \r
884 /*\r
885 =================\r
886 =\r
887 = RFL_CalcOriginStuff\r
888 =\r
889 = Calculate all the global variables for a new position\r
890 = Long parms so position can be clipped to a maximum near 64k\r
891 =\r
892 =================\r
893 */\r
894 \r
895 void RFL_CalcOriginStuff (long x, long y)\r
896 {\r
897         if (x<originxmin)\r
898           x=originxmin;\r
899         else if (x>originxmax)\r
900           x=originxmax;\r
901 \r
902         if (y<originymin)\r
903           y=originymin;\r
904         else if (y>originymax)\r
905           y=originymax;\r
906 \r
907         originxglobal = x;\r
908         originyglobal = y;\r
909         originxtile = originxglobal>>G_T_SHIFT;\r
910         originytile = originyglobal>>G_T_SHIFT;\r
911         originxscreen = originxtile<<SX_T_SHIFT;\r
912         originyscreen = originytile<<SY_T_SHIFT;\r
913         originmap = mapbwidthtable[originytile] + originxtile*2;\r
914 \r
915 #if GRMODE == EGAGR\r
916         panx = (originxglobal>>G_P_SHIFT) & 15;\r
917         pansx = panx & 8;\r
918         pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
919         panadjust = panx/8 + ylookup[pany];\r
920 #endif\r
921 \r
922 #if GRMODE == CGAGR\r
923         panx = (originxglobal>>G_P_SHIFT) & 15;\r
924         pansx = panx & 12;\r
925         pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
926         panadjust = pansx/4 + ylookup[pansy];\r
927 #endif\r
928 \r
929 }\r
930 \r
931 \r
932 /*\r
933 =================\r
934 =\r
935 = RFL_ClearScrollBlocks\r
936 =\r
937 =================\r
938 */\r
939 \r
940 void RFL_ClearScrollBlocks (void)\r
941 {\r
942         hscrollblocks = vscrollblocks = 0;\r
943 }\r
944 \r
945 \r
946 /*\r
947 =================\r
948 =\r
949 = RF_SetScrollBlock\r
950 =\r
951 = Sets a horizontal or vertical scroll block\r
952 = a horizontal block is ----, meaning it blocks up/down movement\r
953 =\r
954 =================\r
955 */\r
956 \r
957 void RF_SetScrollBlock (int x, int y, boolean horizontal)\r
958 {\r
959         if (horizontal)\r
960         {\r
961                 hscrolledge[hscrollblocks] = y;\r
962                 if (hscrollblocks++ == MAXSCROLLEDGES)\r
963                         Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");\r
964         }\r
965         else\r
966         {\r
967                 vscrolledge[vscrollblocks] = x;\r
968                 if (vscrollblocks++ == MAXSCROLLEDGES)\r
969                         Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");\r
970         }\r
971 }\r
972 \r
973 \r
974 /*\r
975 =================\r
976 =\r
977 = RFL_BoundScroll\r
978 =\r
979 = Bound a given x/y movement to scroll blocks\r
980 =\r
981 =================\r
982 */\r
983 \r
984 void RFL_BoundScroll (int x, int y)\r
985 {\r
986         int     check,newxtile,newytile;\r
987 \r
988         originxglobal += x;\r
989         originyglobal += y;\r
990 \r
991         newxtile= originxglobal >> G_T_SHIFT;\r
992         newytile = originyglobal >> G_T_SHIFT;\r
993 \r
994         if (x>0)\r
995         {\r
996                 newxtile+=SCREENTILESWIDE;\r
997                 for (check=0;check<vscrollblocks;check++)\r
998                         if (vscrolledge[check] == newxtile)\r
999                         {\r
1000                                 originxglobal = originxglobal&0xff00;\r
1001                                 break;\r
1002                         }\r
1003         }\r
1004         else if (x<0)\r
1005         {\r
1006                 for (check=0;check<vscrollblocks;check++)\r
1007                         if (vscrolledge[check] == newxtile)\r
1008                         {\r
1009                                 originxglobal = (originxglobal&0xff00)+0x100;\r
1010                                 break;\r
1011                         }\r
1012         }\r
1013 \r
1014 \r
1015         if (y>0)\r
1016         {\r
1017                 newytile+=SCREENTILESHIGH;\r
1018                 for (check=0;check<hscrollblocks;check++)\r
1019                         if (hscrolledge[check] == newytile)\r
1020                         {\r
1021                                 originyglobal = originyglobal&0xff00;\r
1022                                 break;\r
1023                         }\r
1024         }\r
1025         else if (y<0)\r
1026         {\r
1027                 for (check=0;check<hscrollblocks;check++)\r
1028                         if (hscrolledge[check] == newytile)\r
1029                         {\r
1030                                 originyglobal = (originyglobal&0xff00)+0x100;\r
1031                                 break;\r
1032                         }\r
1033         }\r
1034 \r
1035 \r
1036         RFL_CalcOriginStuff (originxglobal, originyglobal);\r
1037 }\r
1038 \r
1039 \r
1040 //===========================================================================\r
1041 \r
1042 /*\r
1043 =====================\r
1044 =\r
1045 = RF_SetRefreshHook\r
1046 =\r
1047 =====================\r
1048 */\r
1049 \r
1050 void RF_SetRefreshHook (void (*func) (void) )\r
1051 {\r
1052         refreshvector = func;\r
1053 }\r
1054 \r
1055 \r
1056 //===========================================================================\r
1057 \r
1058 /*\r
1059 =================\r
1060 =\r
1061 = RFL_NewRow\r
1062 =\r
1063 = Bring a new row of tiles onto the port, spawning animating tiles\r
1064 =\r
1065 =================\r
1066 */\r
1067 \r
1068 void    RFL_NewRow (int dir)\r
1069 {\r
1070         unsigned count,updatespot,updatestep;\r
1071         int             x,y,xstep,ystep;\r
1072 \r
1073         switch (dir)\r
1074         {\r
1075         case 0:         // top row\r
1076                 updatespot = 0;\r
1077                 updatestep = 1;\r
1078                 x = originxtile;\r
1079                 y = originytile;\r
1080                 xstep = 1;\r
1081                 ystep = 0;\r
1082                 count = PORTTILESWIDE;\r
1083                 break;\r
1084 \r
1085         case 1:         // right row\r
1086                 updatespot = PORTTILESWIDE-1;\r
1087                 updatestep = UPDATEWIDE;\r
1088                 x = originxtile + PORTTILESWIDE-1;\r
1089                 y = originytile;\r
1090                 xstep = 0;\r
1091                 ystep = 1;\r
1092                 count = PORTTILESHIGH;\r
1093                 break;\r
1094 \r
1095         case 2:         // bottom row\r
1096                 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
1097                 updatestep = 1;\r
1098                 x = originxtile;\r
1099                 y = originytile + PORTTILESHIGH-1;\r
1100                 xstep = 1;\r
1101                 ystep = 0;\r
1102                 count = PORTTILESWIDE;\r
1103                 break;\r
1104 \r
1105         case 3:         // left row\r
1106                 updatespot = 0;\r
1107                 updatestep = UPDATEWIDE;\r
1108                 x = originxtile;\r
1109                 y = originytile;\r
1110                 xstep = 0;\r
1111                 ystep = 1;\r
1112                 count = PORTTILESHIGH;\r
1113                 break;\r
1114         default:\r
1115                 Quit ("RFL_NewRow: Bad dir!");\r
1116         }\r
1117 \r
1118         while (count--)\r
1119         {\r
1120                 RFL_NewTile(updatespot);\r
1121                 RFL_CheckForAnimTile (x,y);\r
1122                 updatespot+=updatestep;\r
1123                 x+=xstep;\r
1124                 y+=ystep;\r
1125         }\r
1126 }\r
1127 \r
1128 //===========================================================================\r
1129 \r
1130 /*\r
1131 =====================\r
1132 =\r
1133 = RF_ForceRefresh\r
1134 =\r
1135 =====================\r
1136 */\r
1137 \r
1138 void RF_ForceRefresh (void)\r
1139 {\r
1140         RF_NewPosition (originxglobal,originyglobal);\r
1141         RF_Refresh ();\r
1142         RF_Refresh ();\r
1143 }\r
1144 \r
1145 //===========================================================================\r
1146 \r
1147 /*\r
1148 =====================\r
1149 =\r
1150 = RF_MapToMap\r
1151 =\r
1152 = Copies a block of tiles (all three planes) from one point\r
1153 = in the map to another, accounting for animating tiles\r
1154 =\r
1155 =====================\r
1156 */\r
1157 \r
1158 void RF_MapToMap (unsigned srcx, unsigned srcy,\r
1159                                   unsigned destx, unsigned desty,\r
1160                                   unsigned width, unsigned height)\r
1161 {\r
1162         int                     x,y;\r
1163         unsigned        source,destofs,xspot,yspot;\r
1164         unsigned        linedelta,p0,p1,p2,updatespot;\r
1165         unsigned        far *source0, far *source1, far *source2;\r
1166         unsigned        far *dest0, far *dest1, far *dest2;\r
1167         boolean         changed;\r
1168 \r
1169         RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
1170 \r
1171         source = mapbwidthtable[srcy]/2 + srcx;\r
1172 \r
1173         source0 = mapsegs[0]+source;\r
1174         source1 = mapsegs[1]+source;\r
1175         source2 = mapsegs[2]+source;\r
1176 \r
1177         destofs = mapbwidthtable[desty]/2 + destx;\r
1178         destofs -= source;\r
1179 \r
1180         linedelta = mapwidth - width;\r
1181 \r
1182         for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)\r
1183                 for (x=0;x<width;x++,source0++,source1++,source2++)\r
1184                 {\r
1185                         p0 = *source0;\r
1186                         p1 = *source1;\r
1187                         p2 = *source2;\r
1188 \r
1189                         dest0 = source0 + destofs;\r
1190                         dest1 = source1 + destofs;\r
1191                         dest2 = source2 + destofs;\r
1192 \r
1193 //\r
1194 // only make a new tile if it is different\r
1195 //\r
1196                         if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)\r
1197                         {\r
1198                                 *dest0 = p0;\r
1199                                 *dest1 = p1;\r
1200                                 *dest2 = p2;\r
1201                                 changed = true;\r
1202                         }\r
1203                         else\r
1204                                 changed = false;\r
1205 \r
1206 //\r
1207 // if tile is on the view port\r
1208 //\r
1209                         xspot = destx+x-originxtile;\r
1210                         yspot = desty+y-originytile;\r
1211                         if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
1212                         {\r
1213                                 if (changed)\r
1214                                 {\r
1215                                         updatespot = uwidthtable[yspot]+xspot;\r
1216                                         RFL_NewTile(updatespot);\r
1217                                 }\r
1218                                 RFL_CheckForAnimTile (destx+x,desty+y);\r
1219                         }\r
1220                 }\r
1221 }\r
1222 \r
1223 //===========================================================================\r
1224 \r
1225 \r
1226 /*\r
1227 =====================\r
1228 =\r
1229 = RF_MemToMap\r
1230 =\r
1231 = Copies a string of tiles from main memory to the map,\r
1232 = accounting for animating tiles\r
1233 =\r
1234 =====================\r
1235 */\r
1236 \r
1237 void RF_MemToMap (unsigned far *source, unsigned plane,\r
1238                                   unsigned destx, unsigned desty,\r
1239                                   unsigned width, unsigned height)\r
1240 {\r
1241         int                     x,y;\r
1242         unsigned        xspot,yspot;\r
1243         unsigned        linedelta,updatespot;\r
1244         unsigned        far *dest,old,new;\r
1245         boolean         changed;\r
1246 \r
1247         RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
1248 \r
1249         dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;\r
1250 \r
1251         linedelta = mapwidth - width;\r
1252 \r
1253         for (y=0;y<height;y++,dest+=linedelta)\r
1254                 for (x=0;x<width;x++)\r
1255                 {\r
1256                         old = *dest;\r
1257                         new = *source++;\r
1258                         if (old != new)\r
1259                         {\r
1260                                 *dest = new;\r
1261                                 changed = true;\r
1262                         }\r
1263                         else\r
1264                                 changed = false;\r
1265 \r
1266                         dest++;\r
1267                         xspot = destx+x-originxtile;\r
1268                         yspot = desty+y-originytile;\r
1269                         if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
1270                         {\r
1271                                 if (changed)\r
1272                                 {\r
1273                                         updatespot = uwidthtable[yspot]+xspot;\r
1274                                         RFL_NewTile(updatespot);\r
1275                                 }\r
1276                                 RFL_CheckForAnimTile (destx+x,desty+y);\r
1277                         }\r
1278                 }\r
1279 }\r
1280 \r
1281 //===========================================================================\r
1282 \r
1283 \r
1284 /*\r
1285 =====================\r
1286 =\r
1287 = RFL_BoundNewOrigin\r
1288 =\r
1289 = Copies a string of tiles from main memory to the map,\r
1290 = accounting for animating tiles\r
1291 =\r
1292 =====================\r
1293 */\r
1294 \r
1295 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)\r
1296 {\r
1297         int     check,edge;\r
1298 \r
1299 //\r
1300 // calculate new origin related globals\r
1301 //\r
1302         if (orgx<originxmin)\r
1303           orgx=originxmin;\r
1304         else if (orgx>originxmax)\r
1305           orgx=originxmax;\r
1306 \r
1307         if (orgy<originymin)\r
1308           orgy=originymin;\r
1309         else if (orgy>originymax)\r
1310           orgy=originymax;\r
1311 \r
1312         originxtile = orgx>>G_T_SHIFT;\r
1313         originytile = orgy>>G_T_SHIFT;\r
1314 \r
1315         for (check=0;check<vscrollblocks;check++)\r
1316         {\r
1317                 edge = vscrolledge[check];\r
1318                 if (edge>=originxtile && edge <=originxtile+10)\r
1319                 {\r
1320                         orgx = (edge+1)*TILEGLOBAL;\r
1321                         break;\r
1322                 }\r
1323                 if (edge>=originxtile+11 && edge <=originxtile+20)\r
1324                 {\r
1325                         orgx = (edge-20)*TILEGLOBAL;\r
1326                         break;\r
1327                 }\r
1328         }\r
1329 \r
1330         for (check=0;check<hscrollblocks;check++)\r
1331         {\r
1332                 edge = hscrolledge[check];\r
1333                 if (edge>=originytile && edge <=originytile+6)\r
1334                 {\r
1335                         orgy = (edge+1)*TILEGLOBAL;\r
1336                         break;\r
1337                 }\r
1338                 if (edge>=originytile+7 && edge <=originytile+13)\r
1339                 {\r
1340                         orgy = (edge-13)*TILEGLOBAL;\r
1341                         break;\r
1342                 }\r
1343         }\r
1344 \r
1345 \r
1346         RFL_CalcOriginStuff (orgx,orgy);\r
1347 }\r
1348 \r
1349 \r
1350 //===========================================================================\r
1351 \r
1352 /*\r
1353 =====================\r
1354 =\r
1355 = RF_ClearBlock\r
1356 =\r
1357 = Posts erase blocks to clear a certain area of the screen to the master\r
1358 = screen, to erase text or something draw directly to the screen\r
1359 =\r
1360 = Parameters in pixels, but erasure is byte bounded\r
1361 =\r
1362 =====================\r
1363 */\r
1364 \r
1365 void RF_ClearBlock (int x, int y, int width, int height)\r
1366 {\r
1367         eraseblocktype block;\r
1368 \r
1369 #if GRMODE == EGAGR\r
1370         block.screenx = x/8+originxscreen;\r
1371         block.screeny = y+originyscreen;\r
1372         block.width = (width+(x&7)+7)/8;\r
1373         block.height = height;\r
1374         memcpy (eraselistptr[0]++,&block,sizeof(block));\r
1375         memcpy (eraselistptr[1]++,&block,sizeof(block));\r
1376 #endif\r
1377 \r
1378 #if GRMODE == CGAGR\r
1379         block.screenx = x/4+originxscreen;\r
1380         block.screeny = y+originyscreen;\r
1381         block.width = (width+(x&3)+3)/4;\r
1382         block.height = height;\r
1383         memcpy (eraselistptr[0]++,&block,sizeof(block));\r
1384 #endif\r
1385 \r
1386 }\r
1387 \r
1388 //===========================================================================\r
1389 \r
1390 /*\r
1391 =====================\r
1392 =\r
1393 = RF_RedrawBlock\r
1394 =\r
1395 = Causes a number of tiles to be redrawn to the master screen and updated\r
1396 =\r
1397 = Parameters in pixels, but erasure is tile bounded\r
1398 =\r
1399 =====================\r
1400 */\r
1401 \r
1402 void RF_RedrawBlock (int x, int y, int width, int height)\r
1403 {\r
1404         int     xx,yy,xl,xh,yl,yh;\r
1405 \r
1406         xl=(x+panx)/16;\r
1407         xh=(x+panx+width+15)/16;\r
1408         yl=(y+pany)/16;\r
1409         yh=(y+pany+height+15)/16;\r
1410         for (yy=yl;yy<=yh;yy++)\r
1411                 for (xx=xl;xx<=xh;xx++)\r
1412                         RFL_NewTile (yy*UPDATEWIDE+xx);\r
1413 }\r
1414 \r
1415 \r
1416 //===========================================================================\r
1417 \r
1418 /*\r
1419 =====================\r
1420 =\r
1421 = RF_CalcTics\r
1422 =\r
1423 =====================\r
1424 */\r
1425 \r
1426 void RF_CalcTics (void)\r
1427 {\r
1428         long    newtime,oldtimecount;\r
1429 \r
1430 //\r
1431 // calculate tics since last refresh for adaptive timing\r
1432 //\r
1433         if (lasttimecount > TimeCount)\r
1434                 TimeCount = lasttimecount;              // if the game was paused a LONG time\r
1435 \r
1436         if (DemoMode)                                   // demo recording and playback needs\r
1437         {                                                               // to be constant\r
1438 //\r
1439 // take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
1440 //\r
1441                 oldtimecount = lasttimecount;\r
1442                 while (TimeCount<oldtimecount+DEMOTICS*2)\r
1443                 ;\r
1444                 lasttimecount = oldtimecount + DEMOTICS;\r
1445                 TimeCount = lasttimecount + DEMOTICS;\r
1446                 tics = DEMOTICS;\r
1447         }\r
1448         else\r
1449         {\r
1450 //\r
1451 // non demo, so report actual time\r
1452 //\r
1453                 do\r
1454                 {\r
1455                         newtime = TimeCount;\r
1456                         tics = newtime-lasttimecount;\r
1457                 } while (tics<MINTICS);\r
1458                 lasttimecount = newtime;\r
1459 \r
1460 #ifdef PROFILE\r
1461                         strcpy (scratch,"\tTics:");\r
1462                         itoa (tics,str,10);\r
1463                         strcat (scratch,str);\r
1464                         strcat (scratch,"\n");\r
1465                         write (profilehandle,scratch,strlen(scratch));\r
1466 #endif\r
1467 \r
1468                 if (tics>MAXTICS)\r
1469                 {\r
1470                         TimeCount -= (tics-MAXTICS);\r
1471                         tics = MAXTICS;\r
1472                 }\r
1473         }\r
1474 }\r
1475 \r
1476 /*\r
1477 =============================================================================\r
1478 \r
1479                                         EGA specific routines\r
1480 \r
1481 =============================================================================\r
1482 */\r
1483 \r
1484 #if GRMODE == EGAGR\r
1485 \r
1486 /*\r
1487 =====================\r
1488 =\r
1489 = RF_FindFreeBuffer\r
1490 =\r
1491 = Finds the start of unused, non visable buffer space\r
1492 =\r
1493 =====================\r
1494 */\r
1495 \r
1496 unsigned RF_FindFreeBuffer (void)\r
1497 {\r
1498         unsigned        spot,i,j;\r
1499         boolean         ok;\r
1500 \r
1501         for (i=0;i<3;i++)\r
1502         {\r
1503                 spot = screenstart[i]+SCREENSPACE;\r
1504                 ok = true;\r
1505                 for (j=0;j<3;j++)\r
1506                         if (spot == screenstart[j])\r
1507                         {\r
1508                                 ok = false;\r
1509                                 break;\r
1510                         }\r
1511                 if (ok)\r
1512                         return spot;\r
1513         }\r
1514 \r
1515         return 0;       // never get here...\r
1516 }\r
1517 \r
1518 //===========================================================================\r
1519 \r
1520 /*\r
1521 =====================\r
1522 =\r
1523 = RF_NewPosition EGA\r
1524 =\r
1525 =====================\r
1526 */\r
1527 \r
1528 void RF_NewPosition (unsigned x, unsigned y)\r
1529 {\r
1530         int mx,my;\r
1531         byte    *page0ptr,*page1ptr;\r
1532         unsigned        updatenum;\r
1533 \r
1534         RFL_BoundNewOrigin (x,y);\r
1535 /*??\r
1536 // calculate new origin related globals\r
1537 //\r
1538         RFL_CalcOriginStuff (x,y);*/\r
1539 \r
1540 //\r
1541 // clear out all animating tiles\r
1542 //\r
1543         RFL_InitAnimList ();\r
1544 \r
1545 //\r
1546 // set up the new update arrays at base position\r
1547 //\r
1548 //??    memset (tilecache,0,sizeof(tilecache));         // old cache is invalid\r
1549 \r
1550         updatestart[0] = baseupdatestart[0];\r
1551         updatestart[1] = baseupdatestart[1];\r
1552         updateptr = updatestart[otherpage];\r
1553 \r
1554         page0ptr = updatestart[0]+PORTTILESWIDE;        // used to stick "0"s after rows\r
1555         page1ptr = updatestart[1]+PORTTILESWIDE;\r
1556 \r
1557         updatenum = 0;                          // start at first visable tile\r
1558 \r
1559         for (my=0;my<PORTTILESHIGH;my++)\r
1560         {\r
1561                 for (mx=0;mx<PORTTILESWIDE;mx++)\r
1562                 {\r
1563                         RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
1564                         RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
1565                         updatenum++;\r
1566                 }\r
1567                 updatenum++;\r
1568                 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles\r
1569                 page0ptr+=(PORTTILESWIDE+1);\r
1570                 page1ptr+=(PORTTILESWIDE+1);\r
1571         }\r
1572         *(word *)(page0ptr-PORTTILESWIDE)\r
1573                 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;\r
1574 }\r
1575 \r
1576 //===========================================================================\r
1577 \r
1578 /*\r
1579 =================\r
1580 =\r
1581 = RFL_OldRow EGA\r
1582 =\r
1583 = Uncache the trailing row of tiles\r
1584 =\r
1585 =================\r
1586 */\r
1587 \r
1588 void    RFL_OldRow (unsigned updatespot,unsigned count,unsigned step)\r
1589 {\r
1590 \r
1591 asm     mov     si,[updatespot]                 // pointer inside each map plane\r
1592 asm     mov     cx,[count]                              // number of tiles to clear\r
1593 asm     mov     dx,[step]                               // move to next tile\r
1594 asm     mov     es,[WORD PTR mapsegs]                   // background plane\r
1595 asm     mov     ds,[WORD PTR mapsegs+2]                 // foreground plane\r
1596 \r
1597 clearcache:\r
1598 asm     mov     bx,[si]\r
1599 asm     or      bx,bx\r
1600 asm     jnz     blockok                                 // if a foreground tile, block wasn't cached\r
1601 asm     mov     bx,[es:si]\r
1602 asm     shl     bx,1\r
1603 asm     mov     [WORD PTR ss:tilecache+bx],0  //tile is no longer in master screen cache\r
1604 blockok:\r
1605 asm     add     si,dx\r
1606 asm     loop    clearcache\r
1607 \r
1608 asm     mov     ax,ss\r
1609 asm     mov     ds,ax\r
1610 \r
1611 }\r
1612 \r
1613 \r
1614 /*\r
1615 =====================\r
1616 =\r
1617 = RF_Scroll  EGA\r
1618 =\r
1619 = Move the origin x/y global coordinates, readjust the screen panning, and\r
1620 = scroll if needed.  If the scroll distance is greater than one tile, the\r
1621 = entire screen will be redrawn (this could be generalized, but scrolling\r
1622 = more than one tile per refresh is a bad idea!).\r
1623 =\r
1624 =====================\r
1625 */\r
1626 \r
1627 void RF_Scroll (int x, int y)\r
1628 {\r
1629         long            neworgx,neworgy;\r
1630         int                     i,deltax,deltay,absdx,absdy;\r
1631         int                     oldxt,oldyt,move,yy;\r
1632         unsigned        updatespot;\r
1633         byte            *update0,*update1;\r
1634         unsigned        oldpanx,oldpanadjust,oldoriginmap,oldscreen,newscreen,screencopy;\r
1635         int                     screenmove;\r
1636 \r
1637         oldxt = originxtile;\r
1638         oldyt = originytile;\r
1639         oldoriginmap = originmap;\r
1640         oldpanadjust = panadjust;\r
1641         oldpanx = panx;\r
1642 \r
1643         RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);\r
1644 \r
1645         deltax = originxtile - oldxt;\r
1646         absdx = abs(deltax);\r
1647         deltay = originytile - oldyt;\r
1648         absdy = abs(deltay);\r
1649 \r
1650         if (absdx>1 || absdy>1)\r
1651         {\r
1652         //\r
1653         // scrolled more than one tile, so start from scratch\r
1654         //\r
1655                 RF_NewPosition(originxglobal,originyglobal);\r
1656                 return;\r
1657         }\r
1658 \r
1659         if (!absdx && !absdy)\r
1660                 return;                                 // the screen has not scrolled an entire tile\r
1661 \r
1662 \r
1663 //\r
1664 // adjust screens and handle SVGA crippled compatability mode\r
1665 //\r
1666         screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
1667         for (i=0;i<3;i++)\r
1668         {\r
1669                 screenstart[i]+= screenmove;\r
1670                 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )\r
1671                 {\r
1672                         //\r
1673                         // move the screen to the opposite end of the buffer\r
1674                         //\r
1675                         screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;\r
1676                         oldscreen = screenstart[i] - screenmove;\r
1677                         newscreen = oldscreen + screencopy;\r
1678                         screenstart[i] = newscreen + screenmove;\r
1679                         VW_ScreenToScreen (oldscreen,newscreen,\r
1680                                 PORTTILESWIDE*2,PORTTILESHIGH*16);\r
1681 \r
1682                         if (i==screenpage)\r
1683                                 VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);\r
1684                 }\r
1685         }\r
1686         bufferofs = screenstart[otherpage];\r
1687         displayofs = screenstart[screenpage];\r
1688         masterofs = screenstart[2];\r
1689 \r
1690 \r
1691 //\r
1692 // float the update regions\r
1693 //\r
1694         move = deltax;\r
1695         if (deltay==1)\r
1696           move += UPDATEWIDE;\r
1697         else if (deltay==-1)\r
1698           move -= UPDATEWIDE;\r
1699 \r
1700         updatestart[0]+=move;\r
1701         updatestart[1]+=move;\r
1702 \r
1703 //\r
1704 // draw the new tiles just scrolled on to the master screen, and\r
1705 // mark them as needing to be copied to each screen next refreshes\r
1706 // Make sure a zero is at the end of each row in update\r
1707 //\r
1708 \r
1709         if (deltax)\r
1710         {\r
1711                 if (deltax==1)\r
1712                 {\r
1713                         RFL_NewRow (1);                 // new right row\r
1714                         RFL_OldRow (oldoriginmap,PORTTILESHIGH,mapbyteswide);\r
1715                         RFL_RemoveAnimsOnX (originxtile-1);\r
1716                 }\r
1717                 else\r
1718                 {\r
1719                         RFL_NewRow (3);                 // new left row\r
1720                         RFL_OldRow (oldoriginmap+(PORTTILESWIDE-1)*2,PORTTILESHIGH\r
1721                         ,mapbyteswide);\r
1722                         RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
1723                 }\r
1724 \r
1725                 update0 = updatestart[0]+PORTTILESWIDE;\r
1726                 update1 = updatestart[1]+PORTTILESWIDE;\r
1727                 for     (yy=0;yy<PORTTILESHIGH;yy++)\r
1728                 {\r
1729                         *update0 = *update1 = 0;        // drop a 0 at end of each row\r
1730                         update0+=UPDATEWIDE;\r
1731                         update1+=UPDATEWIDE;\r
1732                 }\r
1733         }\r
1734 \r
1735 //----------------\r
1736 \r
1737         if (deltay)\r
1738         {\r
1739                 if (deltay==1)\r
1740                 {\r
1741                         RFL_NewRow (2);                 // new bottom row\r
1742                         RFL_OldRow (oldoriginmap,PORTTILESWIDE,2);\r
1743                         updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
1744                         RFL_RemoveAnimsOnY (originytile-1);\r
1745                 }\r
1746                 else\r
1747                 {\r
1748                         RFL_NewRow (0);                 // new top row\r
1749                         RFL_OldRow (oldoriginmap+mapbwidthtable[PORTTILESHIGH-1]\r
1750                         ,PORTTILESWIDE,2);\r
1751                         updatespot = 0;\r
1752                         RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
1753                 }\r
1754 \r
1755                 *(updatestart[0]+updatespot+PORTTILESWIDE) =\r
1756                         *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;\r
1757         }\r
1758 \r
1759 //----------------\r
1760 \r
1761         //\r
1762         // place a new terminator\r
1763         //\r
1764         update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;\r
1765         update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;\r
1766         *update0++ = *update1++ = 0;\r
1767         *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;\r
1768 }\r
1769 \r
1770 //===========================================================================\r
1771 \r
1772 /*\r
1773 =====================\r
1774 =\r
1775 = RF_PlaceSprite   EGA\r
1776 =\r
1777 =====================\r
1778 */\r
1779 \r
1780 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
1781         unsigned spritenumber, drawtype draw, int priority)\r
1782 {\r
1783         spritelisttype  register *sprite,*next;\r
1784         spritetabletype far *spr;\r
1785         spritetype _seg *block;\r
1786         unsigned        shift,pixx;\r
1787         char            str[80],str2[10];\r
1788 \r
1789         if (!spritenumber || spritenumber == (unsigned)-1)\r
1790         {\r
1791                 RF_RemoveSprite (user);\r
1792                 return;\r
1793         }\r
1794 \r
1795         sprite = (spritelisttype *)*user;\r
1796 \r
1797         if      (sprite)\r
1798         {\r
1799         // sprite allready exists in the list, so we can use it's block\r
1800 \r
1801         //\r
1802         // post an erase block to both pages by copying screenx,screeny,width,height\r
1803         // both pages may not need to be erased if the sprite just changed last frame\r
1804         //\r
1805                 if (sprite->updatecount<2)\r
1806                 {\r
1807                         if (!sprite->updatecount)\r
1808                                 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
1809                         memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
1810                 }\r
1811 \r
1812                 if (priority != sprite->priority)\r
1813                 {\r
1814                 // sprite mvoed to another priority, so unlink the old one and\r
1815                 // relink it in the new priority\r
1816 \r
1817                         next = sprite->nextsprite;                      // cut old links\r
1818                         if (next)\r
1819                                 next->prevptr = sprite->prevptr;\r
1820                         *sprite->prevptr = next;\r
1821                         goto linknewspot;\r
1822                 }\r
1823         }\r
1824         else\r
1825         {\r
1826         // this is a brand new sprite, so allocate a block from the array\r
1827 \r
1828                 if (!spritefreeptr)\r
1829                         Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
1830 \r
1831                 sprite = spritefreeptr;\r
1832                 spritefreeptr = spritefreeptr->nextsprite;\r
1833 \r
1834 linknewspot:\r
1835                 next = prioritystart[priority];         // stick it in new spot\r
1836                 if (next)\r
1837                         next->prevptr = &sprite->nextsprite;\r
1838                 sprite->nextsprite = next;\r
1839                 prioritystart[priority] = sprite;\r
1840                 sprite->prevptr = &prioritystart[priority];\r
1841         }\r
1842 \r
1843 //\r
1844 // write the new info to the sprite\r
1845 //\r
1846         spr = &spritetable[spritenumber-STARTSPRITES];\r
1847         block = (spritetype _seg *)grsegs[spritenumber];\r
1848 \r
1849         if (!block)\r
1850         {\r
1851                 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");\r
1852                 itoa (spritenumber,str2,10);\r
1853                 strcat (str,str2);\r
1854                 Quit (str);\r
1855         }\r
1856 \r
1857         globaly+=spr->orgy;\r
1858         globalx+=spr->orgx;\r
1859 \r
1860         pixx = globalx >> G_SY_SHIFT;\r
1861         shift = (pixx&7)/2;\r
1862 \r
1863         sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);\r
1864         sprite->screeny = globaly >> G_SY_SHIFT;\r
1865         sprite->width = block->width[shift];\r
1866         sprite->height = spr->height;\r
1867         sprite->grseg = spritenumber;\r
1868         sprite->sourceofs = block->sourceoffset[shift];\r
1869         sprite->planesize = block->planesize[shift];\r
1870         sprite->draw = draw;\r
1871         sprite->priority = priority;\r
1872         sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
1873         sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
1874         sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
1875                 - sprite->tilex + 1;\r
1876         sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
1877                 - sprite->tiley + 1;\r
1878 \r
1879         sprite->updatecount = 2;                // draw on next two refreshes\r
1880 \r
1881 // save the sprite pointer off in the user's pointer so it can be moved\r
1882 // again later\r
1883 \r
1884         *user = sprite;\r
1885 }\r
1886 \r
1887 //===========================================================================\r
1888 \r
1889 /*\r
1890 =====================\r
1891 =\r
1892 = RF_RemoveSprite  EGA\r
1893 =\r
1894 =====================\r
1895 */\r
1896 \r
1897 void RF_RemoveSprite (void **user)\r
1898 {\r
1899         spritelisttype  *sprite,*next;\r
1900 \r
1901         sprite = (spritelisttype *)*user;\r
1902         if (!sprite)\r
1903                 return;\r
1904 \r
1905 //\r
1906 // post an erase block to both pages by copying screenx,screeny,width,height\r
1907 // both pages may not need to be erased if the sprite just changed last frame\r
1908 //\r
1909         if (sprite->updatecount<2)\r
1910         {\r
1911                 if (!sprite->updatecount)\r
1912                         memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
1913                 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
1914         }\r
1915 \r
1916 //\r
1917 // unlink the sprite node\r
1918 //\r
1919         next = sprite->nextsprite;\r
1920         if (next)                                               // if (!next), sprite is last in chain\r
1921                 next->prevptr = sprite->prevptr;\r
1922         *sprite->prevptr = next;\r
1923 \r
1924 //\r
1925 // add it back to the free list\r
1926 //\r
1927         sprite->nextsprite = spritefreeptr;\r
1928         spritefreeptr = sprite;\r
1929 \r
1930 //\r
1931 // null the users pointer, so next time that actor gets placed, it will\r
1932 // allocate a new block\r
1933 //\r
1934 \r
1935         *user = 0;\r
1936 }\r
1937 \r
1938 \r
1939 //===========================================================================\r
1940 \r
1941 \r
1942 /*\r
1943 ====================\r
1944 =\r
1945 = RFL_EraseBlocks  EGA\r
1946 =\r
1947 = Write mode 1 should be set\r
1948 =\r
1949 ====================\r
1950 */\r
1951 \r
1952 void RFL_EraseBlocks (void)\r
1953 {\r
1954         eraseblocktype  *block,*done;\r
1955         int                     screenxh,screenyh;\r
1956         unsigned        pos,xtl,ytl,xth,yth,x,y;\r
1957         byte            *updatespot;\r
1958         unsigned        updatedelta;\r
1959         unsigned        erasecount;\r
1960 \r
1961 #ifdef PROFILE\r
1962         erasecount = 0;\r
1963 #endif\r
1964 \r
1965         block = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
1966 \r
1967         done = eraselistptr[otherpage];\r
1968 \r
1969         while (block != done)\r
1970         {\r
1971 \r
1972         //\r
1973         // clip the block to the current screen view\r
1974         //\r
1975                 block->screenx -= originxscreen;\r
1976                 block->screeny -= originyscreen;\r
1977 \r
1978                 if (block->screenx < 0)\r
1979                 {\r
1980                         block->width += block->screenx;\r
1981                         if (block->width<1)\r
1982                                 goto next;\r
1983                         block->screenx = 0;\r
1984                 }\r
1985 \r
1986                 if (block->screeny < 0)\r
1987                 {\r
1988                         block->height += block->screeny;\r
1989                         if (block->height<1)\r
1990                                 goto next;\r
1991                         block->screeny = 0;\r
1992                 }\r
1993 \r
1994                 screenxh = block->screenx + block->width;\r
1995                 screenyh = block->screeny + block->height;\r
1996 \r
1997                 if (screenxh > EGAPORTSCREENWIDE)\r
1998                 {\r
1999                         block->width = EGAPORTSCREENWIDE-block->screenx;\r
2000                         screenxh = block->screenx + block->width;\r
2001                 }\r
2002 \r
2003                 if (screenyh > PORTSCREENHIGH)\r
2004                 {\r
2005                         block->height = PORTSCREENHIGH-block->screeny;\r
2006                         screenyh = block->screeny + block->height;\r
2007                 }\r
2008 \r
2009                 if (block->width<1 || block->height<1)\r
2010                         goto next;\r
2011 \r
2012         //\r
2013         // erase the block by copying from the master screen\r
2014         //\r
2015                 pos = ylookup[block->screeny]+block->screenx;\r
2016                 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
2017                         block->width,block->height);\r
2018 \r
2019         //\r
2020         // put 2s in update where the block was, to force sprites to update\r
2021         //\r
2022                 xtl = block->screenx >> SX_T_SHIFT;\r
2023                 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
2024                 ytl = block->screeny >> SY_T_SHIFT;\r
2025                 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
2026 \r
2027                 updatespot = updateptr + uwidthtable[ytl] + xtl;\r
2028                 updatedelta = UPDATEWIDE - (xth-xtl+1);\r
2029 \r
2030                 for (y=ytl;y<=yth;y++)\r
2031                 {\r
2032                         for (x=xtl;x<=xth;x++)\r
2033                                 *updatespot++ = 2;\r
2034                         updatespot += updatedelta;              // down to next line\r
2035                 }\r
2036 #ifdef PROFILE\r
2037                 erasecount++;\r
2038 #endif\r
2039 \r
2040 next:\r
2041                 block++;\r
2042         }\r
2043         eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
2044 #ifdef PROFILE\r
2045         strcpy (scratch,"\tErase:");\r
2046         itoa (erasecount,str,10);\r
2047         strcat (scratch,str);\r
2048         write (profilehandle,scratch,strlen(scratch));\r
2049 #endif\r
2050 \r
2051 }\r
2052 \r
2053 \r
2054 /*\r
2055 ====================\r
2056 =\r
2057 = RFL_UpdateSprites EGA\r
2058 =\r
2059 = NOTE: Implement vertical clipping!\r
2060 =\r
2061 ====================\r
2062 */\r
2063 \r
2064 void RFL_UpdateSprites (void)\r
2065 {\r
2066         spritelisttype  *sprite;\r
2067         int     portx,porty,x,y,xtl,xth,ytl,yth;\r
2068         int     priority;\r
2069         unsigned dest;\r
2070         byte            *updatespot,*baseupdatespot;\r
2071         unsigned        updatedelta;\r
2072         unsigned        updatecount;\r
2073         unsigned        height,sourceofs;\r
2074 \r
2075 #ifdef PROFILE\r
2076         updatecount = 0;\r
2077 #endif\r
2078 \r
2079         for (priority=0;priority<PRIORITIES;priority++)\r
2080         {\r
2081                 if (priority==MASKEDTILEPRIORITY)\r
2082                         RFL_MaskForegroundTiles ();\r
2083 \r
2084                 for (sprite = prioritystart[priority]; sprite ;\r
2085                         sprite = (spritelisttype *)sprite->nextsprite)\r
2086                 {\r
2087                 //\r
2088                 // see if the sprite has any visable area in the port\r
2089                 //\r
2090 \r
2091                         portx = sprite->screenx - originxscreen;\r
2092                         porty = sprite->screeny - originyscreen;\r
2093                         xtl = portx >> SX_T_SHIFT;\r
2094                         xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
2095                         ytl = porty >> SY_T_SHIFT;\r
2096                         yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
2097 \r
2098                         if (xtl<0)\r
2099                           xtl = 0;\r
2100                         if (xth>=PORTTILESWIDE)\r
2101                           xth = PORTTILESWIDE-1;\r
2102                         if (ytl<0)\r
2103                           ytl = 0;\r
2104                         if (yth>=PORTTILESHIGH)\r
2105                           yth = PORTTILESHIGH-1;\r
2106 \r
2107                         if (xtl>xth || ytl>yth)\r
2108                                 continue;\r
2109 \r
2110                 //\r
2111                 // see if it's visable area covers any non 0 update tiles\r
2112                 //\r
2113                         updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
2114                         updatedelta = UPDATEWIDE - (xth-xtl+1);\r
2115 \r
2116                         if (sprite->updatecount)\r
2117                         {\r
2118                                 sprite->updatecount--;                  // the sprite was just placed,\r
2119                                 goto redraw;                                    // so draw it for sure\r
2120                         }\r
2121 \r
2122                         for (y=ytl;y<=yth;y++)\r
2123                         {\r
2124                                 for (x=xtl;x<=xth;x++)\r
2125                                         if (*updatespot++)\r
2126                                                 goto redraw;\r
2127                                 updatespot += updatedelta;              // down to next line\r
2128                         }\r
2129                         continue;                                                       // no need to update\r
2130 \r
2131 redraw:\r
2132                 //\r
2133                 // set the tiles it covers to 3, because those tiles are being\r
2134                 // updated\r
2135                 //\r
2136                         updatespot = baseupdatespot;\r
2137                         for (y=ytl;y<=yth;y++)\r
2138                         {\r
2139                                 for (x=xtl;x<=xth;x++)\r
2140                                         *updatespot++ = 3;\r
2141                                 updatespot += updatedelta;              // down to next line\r
2142                         }\r
2143                 //\r
2144                 // draw it!\r
2145                 //\r
2146                         height = sprite->height;\r
2147                         sourceofs = sprite->sourceofs;\r
2148                         if (porty<0)\r
2149                         {\r
2150                                 height += porty;                                        // clip top off\r
2151                                 sourceofs -= porty*sprite->width;\r
2152                                 porty = 0;\r
2153                         }\r
2154                         else if (porty+height>PORTSCREENHIGH)\r
2155                         {\r
2156                                 height = PORTSCREENHIGH - porty;    // clip bottom off\r
2157                         }\r
2158 \r
2159                         dest = bufferofs + ylookup[porty] + portx;\r
2160 \r
2161                         switch (sprite->draw)\r
2162                         {\r
2163                         case spritedraw:\r
2164                                 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
2165                                         dest,sprite->width,height,sprite->planesize);\r
2166                                 break;\r
2167 \r
2168                         case maskdraw:\r
2169                                 break;\r
2170 \r
2171                         }\r
2172 #ifdef PROFILE\r
2173                         updatecount++;\r
2174 #endif\r
2175 \r
2176 \r
2177                 }\r
2178         }\r
2179 #ifdef PROFILE\r
2180         strcpy (scratch,"\tSprites:");\r
2181         itoa (updatecount,str,10);\r
2182         strcat (scratch,str);\r
2183         write (profilehandle,scratch,strlen(scratch));\r
2184 #endif\r
2185 \r
2186 }\r
2187 \r
2188 \r
2189 /*\r
2190 =====================\r
2191 =\r
2192 = RF_Refresh   EGA\r
2193 =\r
2194 = All routines will draw at the port at bufferofs, possibly copying from\r
2195 = the port at masterofs.  The EGA version then page flips, while the\r
2196 = CGA version updates the screen from the buffer port.\r
2197 =\r
2198 = Screenpage is the currently displayed page, not the one being drawn\r
2199 = Otherpage is the page to be worked with now\r
2200 =\r
2201 =====================\r
2202 */\r
2203 \r
2204 void RF_Refresh (void)\r
2205 {\r
2206         byte    *newupdate;\r
2207 \r
2208         updateptr = updatestart[otherpage];\r
2209 \r
2210         RFL_AnimateTiles ();            // DEBUG\r
2211 \r
2212 //\r
2213 // update newly scrolled on tiles and animated tiles from the master screen\r
2214 //\r
2215         EGAWRITEMODE(1);\r
2216         EGAMAPMASK(15);\r
2217         RFL_UpdateTiles ();\r
2218         RFL_EraseBlocks ();\r
2219 \r
2220 //\r
2221 // Update is all 0 except where sprites have changed or new area has\r
2222 // been scrolled on.  Go through all sprites and update the ones that cover\r
2223 // a non 0 update tile\r
2224 //\r
2225         EGAWRITEMODE(0);\r
2226         RFL_UpdateSprites ();\r
2227 \r
2228 //\r
2229 // if the main program has a refresh hook set, call their function before\r
2230 // displaying the new page\r
2231 //\r
2232         if (refreshvector)\r
2233                 refreshvector();\r
2234 \r
2235 //\r
2236 // display the changed screen\r
2237 //\r
2238         VW_SetScreen(bufferofs+panadjust,panx & xpanmask);\r
2239 \r
2240 //\r
2241 // prepare for next refresh\r
2242 //\r
2243 // Set the update array to the middle position and clear it out to all "0"s\r
2244 // with an UPDATETERMINATE at the end\r
2245 //\r
2246         updatestart[otherpage] = newupdate = baseupdatestart[otherpage];\r
2247 asm     mov     ax,ds\r
2248 asm     mov     es,ax\r
2249 asm     xor     ax,ax\r
2250 asm     mov     cx,(UPDATESCREENSIZE-2)/2\r
2251 asm     mov     di,[newupdate]\r
2252 asm     rep     stosw\r
2253 asm     mov     [WORD PTR es:di],UPDATETERMINATE\r
2254 \r
2255         screenpage ^= 1;\r
2256         otherpage ^= 1;\r
2257         bufferofs = screenstart[otherpage];\r
2258         displayofs = screenstart[screenpage];\r
2259 \r
2260 //\r
2261 // calculate tics since last refresh for adaptive timing\r
2262 //\r
2263         RF_CalcTics ();\r
2264 }\r
2265 \r
2266 #endif          // GRMODE == EGAGR\r
2267 \r
2268 /*\r
2269 =============================================================================\r
2270 \r
2271                                         CGA specific routines\r
2272 \r
2273 =============================================================================\r
2274 */\r
2275 \r
2276 #if GRMODE == CGAGR\r
2277 \r
2278 \r
2279 /*\r
2280 =====================\r
2281 =\r
2282 = RF_NewPosition   CGA\r
2283 =\r
2284 =====================\r
2285 */\r
2286 \r
2287 void RF_NewPosition (unsigned x, unsigned y)\r
2288 {\r
2289         int mx,my;\r
2290         byte    *spotptr;\r
2291         unsigned        updatenum;\r
2292 \r
2293         RFL_BoundNewOrigin (x,y);\r
2294 /*??\r
2295 // calculate new origin related globals\r
2296 //\r
2297         RFL_CalcOriginStuff (x,y);*/\r
2298 \r
2299 //\r
2300 // clear out all animating tiles\r
2301 //\r
2302         RFL_InitAnimList ();\r
2303 \r
2304 //\r
2305 // set up the new update arrays at base position\r
2306 //\r
2307         updateptr = baseupdateptr;\r
2308 \r
2309         spotptr = updateptr + PORTTILESWIDE;    // used to stick "0"s after rows\r
2310 \r
2311         updatenum = 0;                          // start at first visable tile\r
2312 \r
2313         for (my=0;my<PORTTILESHIGH;my++)\r
2314         {\r
2315                 for (mx=0;mx<PORTTILESWIDE;mx++)\r
2316                 {\r
2317                         RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
2318                         RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
2319                         updatenum++;\r
2320                 }\r
2321                 updatenum++;\r
2322                 *spotptr = 0; // set a 0 at end of a line of tiles\r
2323                 spotptr +=(PORTTILESWIDE+1);\r
2324         }\r
2325         *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;\r
2326 }\r
2327 \r
2328 \r
2329 /*\r
2330 =====================\r
2331 =\r
2332 = RF_Scroll       CGA\r
2333 =\r
2334 = Move the origin x/y global coordinates, readjust the screen panning, and\r
2335 = scroll if needed.  If the scroll distance is greater than one tile, the\r
2336 = entire screen will be redrawn (this could be generalized, but scrolling\r
2337 = more than one tile per refresh is a bad idea!).\r
2338 =\r
2339 =====================\r
2340 */\r
2341 \r
2342 void RF_Scroll (int x, int y)\r
2343 {\r
2344         long            neworgx,neworgy;\r
2345         int                     i,deltax,deltay,absdx,absdy;\r
2346         int                     oldxt,oldyt,move,yy;\r
2347         unsigned        updatespot;\r
2348         byte            *spotptr;\r
2349         unsigned        oldoriginmap,oldscreen,newscreen,screencopy;\r
2350         int                     screenmove;\r
2351 \r
2352         oldxt = originxtile;\r
2353         oldyt = originytile;\r
2354 \r
2355         RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);\r
2356 \r
2357         deltax = originxtile - oldxt;\r
2358         absdx = abs(deltax);\r
2359         deltay = originytile - oldyt;\r
2360         absdy = abs(deltay);\r
2361 \r
2362         if (absdx>1 || absdy>1)\r
2363         {\r
2364         //\r
2365         // scrolled more than one tile, so start from scratch\r
2366         //\r
2367                 RF_NewPosition(originxglobal,originyglobal);\r
2368                 return;\r
2369         }\r
2370 \r
2371         if (!absdx && !absdy)\r
2372                 return;                                 // the screen has not scrolled an entire tile\r
2373 \r
2374 \r
2375 //\r
2376 // float screens\r
2377 //\r
2378         screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
2379         bufferofs += screenmove;\r
2380         masterofs += screenmove;\r
2381 \r
2382 \r
2383 //\r
2384 // float the update regions\r
2385 //\r
2386         move = deltax;\r
2387         if (deltay==1)\r
2388           move += UPDATEWIDE;\r
2389         else if (deltay==-1)\r
2390           move -= UPDATEWIDE;\r
2391 \r
2392         updateptr+=move;\r
2393 \r
2394 //\r
2395 // draw the new tiles just scrolled on to the master screen, and\r
2396 // mark them as needing to be copied to each screen next refreshes\r
2397 // Make sure a zero is at the end of each row in update\r
2398 //\r
2399 \r
2400         if (deltax)\r
2401         {\r
2402                 if (deltax==1)\r
2403                 {\r
2404                         RFL_NewRow (1);                 // new right row\r
2405                         RFL_RemoveAnimsOnX (originxtile-1);\r
2406                 }\r
2407                 else\r
2408                 {\r
2409                         RFL_NewRow (3);                 // new left row\r
2410                         RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
2411                 }\r
2412 \r
2413                 spotptr = updateptr+PORTTILESWIDE;\r
2414                 for     (yy=0;yy<PORTTILESHIGH;yy++)\r
2415                 {\r
2416                         *spotptr = 0;           // drop a 0 at end of each row\r
2417                         spotptr+=UPDATEWIDE;\r
2418                 }\r
2419         }\r
2420 \r
2421 //----------------\r
2422 \r
2423         if (deltay)\r
2424         {\r
2425                 if (deltay==1)\r
2426                 {\r
2427                         RFL_NewRow (2);                 // new bottom row\r
2428                         *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;\r
2429                         RFL_RemoveAnimsOnY (originytile-1);\r
2430                 }\r
2431                 else\r
2432                 {\r
2433                         RFL_NewRow (0);                 // new top row\r
2434                         *(updateptr+PORTTILESWIDE) = 0;\r
2435                         RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
2436                 }\r
2437         }\r
2438 \r
2439 //----------------\r
2440 \r
2441         //\r
2442         // place a new terminator\r
2443         //\r
2444         spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;\r
2445         *spotptr++ = 0;\r
2446         *(unsigned *)spotptr = UPDATETERMINATE;\r
2447 }\r
2448 \r
2449 /*\r
2450 =====================\r
2451 =\r
2452 = RF_PlaceSprite  CGA\r
2453 =\r
2454 =====================\r
2455 */\r
2456 \r
2457 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
2458         unsigned spritenumber, drawtype draw, int priority)\r
2459 {\r
2460         spritelisttype  register *sprite,*next;\r
2461         spritetabletype far *spr;\r
2462         spritetype _seg *block;\r
2463         unsigned        shift,pixx;\r
2464         char            str[80],str2[10];\r
2465 \r
2466         if (!spritenumber || spritenumber == (unsigned)-1)\r
2467         {\r
2468                 RF_RemoveSprite (user);\r
2469                 return;\r
2470         }\r
2471 \r
2472         sprite = (spritelisttype *)*user;\r
2473 \r
2474         if      (sprite)\r
2475         {\r
2476         // sprite allready exists in the list, so we can use it's block\r
2477 \r
2478         //\r
2479         // post an erase block to erase the old position by copying\r
2480         // screenx,screeny,width,height\r
2481         //\r
2482                 if (!sprite->updatecount)               // may not have been drawn at all yet\r
2483                         memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
2484 \r
2485                 if (priority != sprite->priority)\r
2486                 {\r
2487                 // sprite moved to another priority, so unlink the old one and\r
2488                 // relink it in the new priority\r
2489 \r
2490                         next = sprite->nextsprite;                      // cut old links\r
2491                         if (next)\r
2492                                 next->prevptr = sprite->prevptr;\r
2493                         *sprite->prevptr = next;\r
2494                         goto linknewspot;\r
2495                 }\r
2496         }\r
2497         else\r
2498         {\r
2499         // this is a brand new sprite, so allocate a block from the array\r
2500 \r
2501                 if (!spritefreeptr)\r
2502                         Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
2503 \r
2504                 sprite = spritefreeptr;\r
2505                 spritefreeptr = spritefreeptr->nextsprite;\r
2506 \r
2507 linknewspot:\r
2508                 next = prioritystart[priority];         // stick it in new spot\r
2509                 if (next)\r
2510                         next->prevptr = &sprite->nextsprite;\r
2511                 sprite->nextsprite = next;\r
2512                 prioritystart[priority] = sprite;\r
2513                 sprite->prevptr = &prioritystart[priority];\r
2514         }\r
2515 \r
2516 //\r
2517 // write the new info to the sprite\r
2518 //\r
2519         spr = &spritetable[spritenumber-STARTSPRITES];\r
2520         block = (spritetype _seg *)grsegs[spritenumber];\r
2521 \r
2522         if (!block)\r
2523         {\r
2524                 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");\r
2525                 itoa (spritenumber,str2,10);\r
2526                 strcat (str,str2);\r
2527                 Quit (str);\r
2528         }\r
2529 \r
2530 \r
2531         globaly+=spr->orgy;\r
2532         globalx+=spr->orgx;\r
2533 \r
2534         sprite->screenx = globalx >> G_CGASX_SHIFT;\r
2535         sprite->screeny = globaly >> G_SY_SHIFT;\r
2536         sprite->width = block->width[0];\r
2537         sprite->height = spr->height;\r
2538         sprite->grseg = spritenumber;\r
2539         sprite->sourceofs = block->sourceoffset[0];\r
2540         sprite->planesize = block->planesize[0];\r
2541         sprite->draw = draw;\r
2542         sprite->priority = priority;\r
2543         sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
2544         sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
2545         sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
2546                 - sprite->tilex + 1;\r
2547         sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
2548                 - sprite->tiley + 1;\r
2549 \r
2550         sprite->updatecount = 1;                // draw on next refresh\r
2551 \r
2552 // save the sprite pointer off in the user's pointer so it can be moved\r
2553 // again later\r
2554 \r
2555         *user = sprite;\r
2556 }\r
2557 \r
2558 //===========================================================================\r
2559 \r
2560 /*\r
2561 =====================\r
2562 =\r
2563 = RF_RemoveSprite CGA\r
2564 =\r
2565 =====================\r
2566 */\r
2567 \r
2568 void RF_RemoveSprite (void **user)\r
2569 {\r
2570         spritelisttype  *sprite,*next;\r
2571 \r
2572         sprite = (spritelisttype *)*user;\r
2573         if (!sprite)\r
2574                 return;\r
2575 \r
2576 //\r
2577 // post an erase block to erase the old position by copying\r
2578 // screenx,screeny,width,height\r
2579 //\r
2580         if (!sprite->updatecount)\r
2581         {\r
2582                 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
2583         }\r
2584 \r
2585 //\r
2586 // unlink the sprite node\r
2587 //\r
2588         next = sprite->nextsprite;\r
2589         if (next)                                               // if (!next), sprite is last in chain\r
2590                 next->prevptr = sprite->prevptr;\r
2591         *sprite->prevptr = next;\r
2592 \r
2593 //\r
2594 // add it back to the free list\r
2595 //\r
2596         sprite->nextsprite = spritefreeptr;\r
2597         spritefreeptr = sprite;\r
2598 \r
2599 //\r
2600 // null the users pointer, so next time that actor gets placed, it will\r
2601 // allocate a new block\r
2602 //\r
2603 \r
2604         *user = 0;\r
2605 }\r
2606 \r
2607 \r
2608 /*\r
2609 ====================\r
2610 =\r
2611 = RFL_EraseBlocks CGA\r
2612 =\r
2613 = Write mode 1 should be set\r
2614 =\r
2615 ====================\r
2616 */\r
2617 \r
2618 void RFL_EraseBlocks (void)\r
2619 {\r
2620         eraseblocktype  *block,*done;\r
2621         int                     screenxh,screenyh;\r
2622         unsigned        pos,xtl,ytl,xth,yth,x,y;\r
2623         byte            *updatespot;\r
2624         unsigned        updatedelta;\r
2625 \r
2626         block = &eraselist[0][0];\r
2627 \r
2628         done = eraselistptr[0];\r
2629 \r
2630         while (block != done)\r
2631         {\r
2632 \r
2633         //\r
2634         // clip the block to the current screen view\r
2635         //\r
2636                 block->screenx -= originxscreen;\r
2637                 block->screeny -= originyscreen;\r
2638 \r
2639                 if (block->screenx < 0)\r
2640                 {\r
2641                         block->width += block->screenx;\r
2642                         if (block->width<1)\r
2643                                 goto next;\r
2644                         block->screenx = 0;\r
2645                 }\r
2646 \r
2647                 if (block->screeny < 0)\r
2648                 {\r
2649                         block->height += block->screeny;\r
2650                         if (block->height<1)\r
2651                                 goto next;\r
2652                         block->screeny = 0;\r
2653                 }\r
2654 \r
2655                 screenxh = block->screenx + block->width;\r
2656                 screenyh = block->screeny + block->height;\r
2657 \r
2658                 if (screenxh > CGAPORTSCREENWIDE)\r
2659                 {\r
2660                         block->width = CGAPORTSCREENWIDE-block->screenx;\r
2661                         screenxh = block->screenx + block->width;\r
2662                 }\r
2663 \r
2664                 if (screenyh > PORTSCREENHIGH)\r
2665                 {\r
2666                         block->height = PORTSCREENHIGH-block->screeny;\r
2667                         screenyh = block->screeny + block->height;\r
2668                 }\r
2669 \r
2670                 if (block->width<1 || block->height<1)\r
2671                         goto next;\r
2672 \r
2673         //\r
2674         // erase the block by copying from the master screen\r
2675         //\r
2676                 pos = ylookup[block->screeny]+block->screenx;\r
2677                 block->width = (block->width + (pos&1) + 1)& ~1;\r
2678                 pos &= ~1;                              // make sure a word copy gets used\r
2679                 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
2680                         block->width,block->height);\r
2681 \r
2682         //\r
2683         // put 2s in update where the block was, to force sprites to update\r
2684         //\r
2685                 xtl = block->screenx >> SX_T_SHIFT;\r
2686                 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
2687                 ytl = block->screeny >> SY_T_SHIFT;\r
2688                 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
2689 \r
2690                 updatespot = updateptr + uwidthtable[ytl] + xtl;\r
2691                 updatedelta = UPDATEWIDE - (xth-xtl+1);\r
2692 \r
2693                 for (y=ytl;y<=yth;y++)\r
2694                 {\r
2695                         for (x=xtl;x<=xth;x++)\r
2696                                 *updatespot++ = 2;\r
2697                         updatespot += updatedelta;              // down to next line\r
2698                 }\r
2699 \r
2700 next:\r
2701                 block++;\r
2702         }\r
2703         eraselistptr[0] = &eraselist[0][0];\r
2704 }\r
2705 \r
2706 \r
2707 /*\r
2708 ====================\r
2709 =\r
2710 = RFL_UpdateSprites      CGA\r
2711 =\r
2712 = NOTE: Implement vertical clipping!\r
2713 =\r
2714 ====================\r
2715 */\r
2716 \r
2717 void RFL_UpdateSprites (void)\r
2718 {\r
2719         spritelisttype  *sprite;\r
2720         int     portx,porty,x,y,xtl,xth,ytl,yth;\r
2721         int     priority;\r
2722         unsigned dest;\r
2723         byte            *updatespot,*baseupdatespot;\r
2724         unsigned        updatedelta;\r
2725 \r
2726         unsigned        updatecount;\r
2727         unsigned        height,sourceofs;\r
2728 \r
2729 #ifdef PROFILE\r
2730         updatecount = 0;\r
2731 #endif\r
2732 \r
2733 \r
2734         for (priority=0;priority<PRIORITIES;priority++)\r
2735         {\r
2736                 if (priority==MASKEDTILEPRIORITY)\r
2737                         RFL_MaskForegroundTiles ();\r
2738 \r
2739                 for (sprite = prioritystart[priority]; sprite ;\r
2740                         sprite = (spritelisttype *)sprite->nextsprite)\r
2741                 {\r
2742                 //\r
2743                 // see if the sprite has any visable area in the port\r
2744                 //\r
2745 \r
2746                         portx = sprite->screenx - originxscreen;\r
2747                         porty = sprite->screeny - originyscreen;\r
2748                         xtl = portx >> SX_T_SHIFT;\r
2749                         xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
2750                         ytl = porty >> SY_T_SHIFT;\r
2751                         yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
2752 \r
2753                         if (xtl<0)\r
2754                           xtl = 0;\r
2755                         if (xth>=PORTTILESWIDE)\r
2756                           xth = PORTTILESWIDE-1;\r
2757                         if (ytl<0)\r
2758                           ytl = 0;\r
2759                         if (yth>=PORTTILESHIGH)\r
2760                           yth = PORTTILESHIGH-1;\r
2761 \r
2762                         if (xtl>xth || ytl>yth)\r
2763                                 continue;\r
2764 \r
2765                 //\r
2766                 // see if it's visable area covers any non 0 update tiles\r
2767                 //\r
2768                         updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
2769                         updatedelta = UPDATEWIDE - (xth-xtl+1);\r
2770 \r
2771                         if (sprite->updatecount)\r
2772                         {\r
2773                                 sprite->updatecount--;                  // the sprite was just placed,\r
2774                                 goto redraw;                                    // so draw it for sure\r
2775                         }\r
2776 \r
2777                         for (y=ytl;y<=yth;y++)\r
2778                         {\r
2779                                 for (x=xtl;x<=xth;x++)\r
2780                                         if (*updatespot++)\r
2781                                                 goto redraw;\r
2782                                 updatespot += updatedelta;              // down to next line\r
2783                         }\r
2784                         continue;                                                       // no need to update\r
2785 \r
2786 redraw:\r
2787                 //\r
2788                 // set the tiles it covers to 3, because those tiles are being\r
2789                 // updated\r
2790                 //\r
2791                         updatespot = baseupdatespot;\r
2792                         for (y=ytl;y<=yth;y++)\r
2793                         {\r
2794                                 for (x=xtl;x<=xth;x++)\r
2795                                         *updatespot++ = 3;\r
2796                                 updatespot += updatedelta;              // down to next line\r
2797                         }\r
2798                 //\r
2799                 // draw it!\r
2800                 //\r
2801                         height = sprite->height;\r
2802                         sourceofs = sprite->sourceofs;\r
2803                         if (porty<0)\r
2804                         {\r
2805                                 height += porty;                                        // clip top off\r
2806                                 sourceofs -= porty*sprite->width;\r
2807                                 porty = 0;\r
2808                         }\r
2809                         else if (porty+height>PORTSCREENHIGH)\r
2810                         {\r
2811                                 height = PORTSCREENHIGH - porty;    // clip bottom off\r
2812                         }\r
2813 \r
2814                         dest = bufferofs + ylookup[porty] + portx;\r
2815 \r
2816                         switch (sprite->draw)\r
2817                         {\r
2818                         case spritedraw:\r
2819                                 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
2820                                         dest,sprite->width,height,sprite->planesize);\r
2821                                 break;\r
2822 \r
2823                         case maskdraw:\r
2824                                 break;\r
2825 \r
2826                         }\r
2827 #ifdef PROFILE\r
2828                         updatecount++;\r
2829 #endif\r
2830 \r
2831 \r
2832                 }\r
2833         }\r
2834 }\r
2835 \r
2836 \r
2837 /*\r
2838 =====================\r
2839 =\r
2840 = RF_Refresh        CGA\r
2841 =\r
2842 = All routines will draw at the port at bufferofs, possibly copying from\r
2843 = the port at masterofs.  The EGA version then page flips, while the\r
2844 = CGA version updates the screen from the buffer port.\r
2845 =\r
2846 = Screenpage is the currently displayed page, not the one being drawn\r
2847 = Otherpage is the page to be worked with now\r
2848 =\r
2849 =====================\r
2850 */\r
2851 \r
2852 void RF_Refresh (void)\r
2853 {\r
2854         long newtime;\r
2855 \r
2856         RFL_AnimateTiles ();\r
2857 \r
2858 //\r
2859 // update newly scrolled on tiles and animated tiles from the master screen\r
2860 //\r
2861         RFL_UpdateTiles ();\r
2862         RFL_EraseBlocks ();\r
2863 \r
2864 //\r
2865 // Update is all 0 except where sprites have changed or new area has\r
2866 // been scrolled on.  Go through all sprites and update the ones that cover\r
2867 // a non 0 update tile\r
2868 //\r
2869         RFL_UpdateSprites ();\r
2870 \r
2871 //\r
2872 // if the main program has a refresh hook set, call their function before\r
2873 // displaying the new page\r
2874 //\r
2875         if (refreshvector)\r
2876                 refreshvector();\r
2877 \r
2878 //\r
2879 // update everything to the screen\r
2880 //\r
2881         VW_CGAFullUpdate ();\r
2882 \r
2883 //\r
2884 // calculate tics since last refresh for adaptive timing\r
2885 //\r
2886         RF_CalcTics ();\r
2887 }\r
2888 \r
2889 #endif          // GRMODE == CGAGR\r
2890 //===============================\r
2891 /*\r
2892 ; Keen Dreams Source Code\r
2893 ; Copyright (C) 2014 Javier M. Chavez\r
2894 ;\r
2895 ; This program is free software; you can redistribute it and/or modify\r
2896 ; it under the terms of the GNU General Public License as published by\r
2897 ; the Free Software Foundation; either version 2 of the License, or\r
2898 ; (at your option) any later version.\r
2899 ;\r
2900 ; This program is distributed in the hope that it will be useful,\r
2901 ; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
2902 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
2903 ; GNU General Public License for more details.\r
2904 ;\r
2905 ; You should have received a copy of the GNU General Public License along\r
2906 ; with this program; if not, write to the Free Software Foundation, Inc.,\r
2907 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
2908 \r
2909 ; ID_RF_A.ASM\r
2910 \r
2911 IDEAL\r
2912 MODEL   MEDIUM,C\r
2913 \r
2914 INCLUDE "ID_ASM.EQU"\r
2915 \r
2916 CACHETILES      = 1             ;enable master screen tile caching\r
2917 \r
2918 ;============================================================================\r
2919 \r
2920 TILESWIDE       =       21\r
2921 TILESHIGH       =       14\r
2922 \r
2923 UPDATESIZE      =       (TILESWIDE+1)*TILESHIGH+1\r
2924 \r
2925 DATASEG\r
2926 \r
2927 EXTRN   screenseg:WORD\r
2928 EXTRN   updateptr:WORD\r
2929 EXTRN   updatestart:WORD\r
2930 EXTRN   masterofs:WORD          ;start of master tile port\r
2931 EXTRN   bufferofs:WORD          ;start of current buffer port\r
2932 EXTRN   screenstart:WORD        ;starts of three screens (0/1/master) in EGA mem\r
2933 EXTRN   grsegs:WORD\r
2934 EXTRN   mapsegs:WORD\r
2935 EXTRN   originmap:WORD\r
2936 EXTRN   updatemapofs:WORD\r
2937 EXTRN   tilecache:WORD\r
2938 EXTRN   tinf:WORD                       ;seg pointer to map header and tile info\r
2939 EXTRN   blockstarts:WORD        ;offsets from bufferofs for each update block\r
2940 \r
2941 planemask       db      ?\r
2942 planenum        db      ?\r
2943 \r
2944 CODESEG\r
2945 \r
2946 screenstartcs   dw      ?               ;in code segment for accesability\r
2947 \r
2948 \r
2949 \r
2950 \r
2951 IFE GRMODE-CGAGR\r
2952 ;============================================================================\r
2953 ;\r
2954 ; CGA refresh routines\r
2955 ;\r
2956 ;============================================================================\r
2957 \r
2958 TILEWIDTH       =       4\r
2959 \r
2960 ;=================\r
2961 ;\r
2962 ; RFL_NewTile\r
2963 ;\r
2964 ; Draws a composit two plane tile to the master screen and sets the update\r
2965 ; spot to 1 in both update pages, forcing the tile to be copied to the\r
2966 ; view pages the next two refreshes\r
2967 ;\r
2968 ; Called to draw newlly scrolled on strips and animating tiles\r
2969 ;\r
2970 ;=================\r
2971 \r
2972 PROC    RFL_NewTile     updateoffset:WORD\r
2973 PUBLIC  RFL_NewTile\r
2974 USES    SI,DI\r
2975 \r
2976 ;\r
2977 ; mark both update lists at this spot\r
2978 ;\r
2979         mov     di,[updateoffset]\r
2980 \r
2981         mov     bx,[updateptr]                  ;start of update matrix\r
2982         mov     [BYTE bx+di],1\r
2983 \r
2984         mov     dx,SCREENWIDTH-TILEWIDTH                ;add to get to start of next line\r
2985 \r
2986 ;\r
2987 ; set di to the location in screenseg to draw the tile\r
2988 ;\r
2989         shl     di,1\r
2990         mov     si,[updatemapofs+di]    ;offset in map from origin\r
2991         add     si,[originmap]\r
2992         mov     di,[blockstarts+di]             ;screen location for tile\r
2993         add     di,[masterofs]\r
2994 \r
2995 ;\r
2996 ; set BX to the foreground tile number and SI to the background number\r
2997 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
2998 ; as one of the planes totally eclipses the other\r
2999 ;\r
3000         mov     es,[mapsegs+2]                  ;foreground plane\r
3001         mov     bx,[es:si]\r
3002         mov     es,[mapsegs]                    ;background plane\r
3003         mov     si,[es:si]\r
3004 \r
3005         mov     es,[screenseg]\r
3006 \r
3007         or      bx,bx\r
3008         jz      @@singletile\r
3009         jmp     @@maskeddraw                    ;draw both together\r
3010 \r
3011 ;=============\r
3012 ;\r
3013 ; Draw single background tile from main memory\r
3014 ;\r
3015 ;=============\r
3016 \r
3017 @@singletile:\r
3018         shl     si,1\r
3019         mov     ds,[grsegs+STARTTILE16*2+si]\r
3020 \r
3021         xor     si,si                                   ;block is segment aligned\r
3022 \r
3023 REPT    15\r
3024         movsw\r
3025         movsw\r
3026         add     di,dx\r
3027 ENDM\r
3028         movsw\r
3029         movsw\r
3030 \r
3031         mov     ax,ss\r
3032         mov     ds,ax                                   ;restore turbo's data segment\r
3033         ret\r
3034 \r
3035 \r
3036 ;=========\r
3037 ;\r
3038 ; Draw a masked tile combo\r
3039 ; Interupts are disabled and the stack segment is reassigned\r
3040 ;\r
3041 ;=========\r
3042 @@maskeddraw:\r
3043         cli                                                     ; don't allow ints when SS is set\r
3044         shl     bx,1\r
3045         mov     ss,[grsegs+STARTTILE16M*2+bx]\r
3046         shl     si,1\r
3047         mov     ds,[grsegs+STARTTILE16*2+si]\r
3048 \r
3049         xor     si,si                                   ;first word of tile data\r
3050 \r
3051 REPT    16\r
3052         mov     ax,[si]                                 ;background tile\r
3053         and     ax,[ss:si]                              ;mask\r
3054         or      ax,[ss:si+64]                   ;masked data\r
3055         stosw\r
3056         mov     ax,[si+2]                               ;background tile\r
3057         and     ax,[ss:si+2]                    ;mask\r
3058         or      ax,[ss:si+66]                   ;masked data\r
3059         stosw\r
3060         add     si,4\r
3061         add     di,dx\r
3062 ENDM\r
3063 \r
3064         mov     ax,@DATA\r
3065         mov     ss,ax\r
3066         sti\r
3067         mov     ds,ax\r
3068         ret\r
3069 ENDP\r
3070 \r
3071 ENDIF\r
3072 \r
3073 \r
3074 \r
3075 IFE GRMODE-EGAGR\r
3076 ;===========================================================================\r
3077 ;\r
3078 ; EGA refresh routines\r
3079 ;\r
3080 ;===========================================================================\r
3081 \r
3082 TILEWIDTH       =       2\r
3083 \r
3084 ;=================\r
3085 ;\r
3086 ; RFL_NewTile\r
3087 ;\r
3088 ; Draws a composit two plane tile to the master screen and sets the update\r
3089 ; spot to 1 in both update pages, forcing the tile to be copied to the\r
3090 ; view pages the next two refreshes\r
3091 ;\r
3092 ; Called to draw newlly scrolled on strips and animating tiles\r
3093 ;\r
3094 ; Assumes write mode 0\r
3095 ;\r
3096 ;=================\r
3097 \r
3098 PROC    RFL_NewTile     updateoffset:WORD\r
3099 PUBLIC  RFL_NewTile\r
3100 USES    SI,DI\r
3101 \r
3102 ;\r
3103 ; mark both update lists at this spot\r
3104 ;\r
3105         mov     di,[updateoffset]\r
3106 \r
3107         mov     bx,[updatestart]                ;page 0 pointer\r
3108         mov     [BYTE bx+di],1\r
3109         mov     bx,[updatestart+2]              ;page 1 pointer\r
3110         mov     [BYTE bx+di],1\r
3111 \r
3112 ;\r
3113 ; set screenstartcs to the location in screenseg to draw the tile\r
3114 ;\r
3115         shl     di,1\r
3116         mov     si,[updatemapofs+di]    ;offset in map from origin\r
3117         add     si,[originmap]\r
3118         mov     di,[blockstarts+di]             ;screen location for tile\r
3119         add     di,[masterofs]\r
3120         mov     [cs:screenstartcs],di\r
3121 \r
3122 ;\r
3123 ; set BX to the foreground tile number and SI to the background number\r
3124 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
3125 ; as one of the planes totally eclipses the other\r
3126 ;\r
3127         mov     es,[mapsegs+2]                  ;foreground plane\r
3128         mov     bx,[es:si]\r
3129         mov     es,[mapsegs]                    ;background plane\r
3130         mov     si,[es:si]\r
3131 \r
3132         mov     es,[screenseg]\r
3133         mov     dx,SC_INDEX                             ;for stepping through map mask planes\r
3134 \r
3135         or      bx,bx\r
3136         jz      @@singletile\r
3137         jmp     @@maskeddraw                    ;draw both together\r
3138 \r
3139 ;=========\r
3140 ;\r
3141 ; No foreground tile, so draw a single background tile.\r
3142 ; Use the master screen cache if possible\r
3143 ;\r
3144 ;=========\r
3145 @@singletile:\r
3146 \r
3147         mov     bx,SCREENWIDTH-2                ;add to get to start of next line\r
3148         shl     si,1\r
3149 \r
3150 IFE CACHETILES\r
3151         jmp     @@singlemain\r
3152 ENDIF\r
3153 \r
3154         mov     ax,[tilecache+si]\r
3155         or      ax,ax\r
3156         jz      @@singlemain\r
3157 ;=============\r
3158 ;\r
3159 ; Draw single tile from cache\r
3160 ;\r
3161 ;=============\r
3162 \r
3163         mov     si,ax\r
3164 \r
3165         mov     ax,SC_MAPMASK + 15*256  ;all planes\r
3166         WORDOUT\r
3167 \r
3168         mov     dx,GC_INDEX\r
3169         mov     ax,GC_MODE + 1*256              ;write mode 1\r
3170         WORDOUT\r
3171 \r
3172         mov     di,[cs:screenstartcs]\r
3173         mov     ds,[screenseg]\r
3174 \r
3175 REPT    15\r
3176         movsb\r
3177         movsb\r
3178         add     si,bx\r
3179         add     di,bx\r
3180 ENDM\r
3181         movsb\r
3182         movsb\r
3183 \r
3184         xor     ah,ah                                   ;write mode 0\r
3185         WORDOUT\r
3186 \r
3187         mov     ax,ss\r
3188         mov     ds,ax                                   ;restore turbo's data segment\r
3189         ret\r
3190 \r
3191 ;=============\r
3192 ;\r
3193 ; Draw single tile from main memory\r
3194 ;\r
3195 ;=============\r
3196 \r
3197 @@singlemain:\r
3198         mov     ax,[cs:screenstartcs]\r
3199         mov     [tilecache+si],ax               ;next time it can be drawn from here with latch\r
3200         mov     ds,[grsegs+STARTTILE16*2+si]\r
3201 \r
3202         xor     si,si                                   ;block is segment aligned\r
3203 \r
3204         mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
3205 \r
3206         mov     cx,4                                    ;draw four planes\r
3207 @@planeloop:\r
3208         mov     dx,SC_INDEX\r
3209         WORDOUT\r
3210 \r
3211         mov     di,[cs:screenstartcs]   ;start at same place in all planes\r
3212 \r
3213 REPT    15\r
3214         movsw\r
3215         add     di,bx\r
3216 ENDM\r
3217         movsw\r
3218 \r
3219         shl     ah,1                                    ;shift plane mask over for next plane\r
3220         loop    @@planeloop\r
3221 \r
3222         mov     ax,ss\r
3223         mov     ds,ax                                   ;restore turbo's data segment\r
3224         ret\r
3225 \r
3226 \r
3227 ;=========\r
3228 ;\r
3229 ; Draw a masked tile combo\r
3230 ; Interupts are disabled and the stack segment is reassigned\r
3231 ;\r
3232 ;=========\r
3233 @@maskeddraw:\r
3234         cli                                                     ; don't allow ints when SS is set\r
3235         shl     bx,1\r
3236         mov     ss,[grsegs+STARTTILE16M*2+bx]\r
3237         shl     si,1\r
3238         mov     ds,[grsegs+STARTTILE16*2+si]\r
3239 \r
3240         xor     si,si                                   ;first word of tile data\r
3241 \r
3242         mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
3243 \r
3244         mov     di,[cs:screenstartcs]\r
3245 @@planeloopm:\r
3246         WORDOUT\r
3247 tileofs         =       0\r
3248 lineoffset      =       0\r
3249 REPT    16\r
3250         mov     bx,[si+tileofs]                 ;background tile\r
3251         and     bx,[ss:tileofs]                 ;mask\r
3252         or      bx,[ss:si+tileofs+32]   ;masked data\r
3253         mov     [es:di+lineoffset],bx\r
3254 tileofs         =       tileofs + 2\r
3255 lineoffset      =       lineoffset + SCREENWIDTH\r
3256 ENDM\r
3257         add     si,32\r
3258         shl     ah,1                                    ;shift plane mask over for next plane\r
3259         cmp     ah,10000b\r
3260         je      @@done                                  ;drawn all four planes\r
3261         jmp     @@planeloopm\r
3262 \r
3263 @@done:\r
3264         mov     ax,@DATA\r
3265         mov     ss,ax\r
3266         sti\r
3267         mov     ds,ax\r
3268         ret\r
3269 ENDP\r
3270 \r
3271 ENDIF\r
3272 \r
3273 IFE GRMODE-VGAGR\r
3274 ;============================================================================\r
3275 ;\r
3276 ; VGA refresh routines\r
3277 ;\r
3278 ;============================================================================\r
3279 \r
3280 \r
3281 ENDIF\r
3282 \r
3283 \r
3284 ;============================================================================\r
3285 ;\r
3286 ; reasonably common refresh routines\r
3287 ;\r
3288 ;============================================================================\r
3289 \r
3290 \r
3291 ;=================\r
3292 ;\r
3293 ; RFL_UpdateTiles\r
3294 ;\r
3295 ; Scans through the update matrix pointed to by updateptr, looking for 1s.\r
3296 ; A 1 represents a tile that needs to be copied from the master screen to the\r
3297 ; current screen (a new row or an animated tiled).  If more than one adjacent\r
3298 ; tile in a horizontal row needs to be copied, they will be copied as a group.\r
3299 ;\r
3300 ; Assumes write mode 1\r
3301 ;\r
3302 ;=================\r
3303 \r
3304 \r
3305 ; AX    0/1 for scasb, temp for segment register transfers\r
3306 ; BX    width for block copies\r
3307 ; CX    REP counter\r
3308 ; DX    line width deltas\r
3309 ; SI    source for copies\r
3310 ; DI    scas dest / movsb dest\r
3311 ; BP    pointer to UPDATETERMINATE\r
3312 ;\r
3313 ; DS\r
3314 ; ES\r
3315 ; SS\r
3316 \r
3317 PROC    RFL_UpdateTiles\r
3318 PUBLIC  RFL_UpdateTiles\r
3319 USES    SI,DI,BP\r
3320 \r
3321         jmp     SHORT @@realstart\r
3322 @@done:\r
3323 ;\r
3324 ; all tiles have been scanned\r
3325 ;\r
3326         ret\r
3327 \r
3328 @@realstart:\r
3329         mov     di,[updateptr]\r
3330         mov     bp,(TILESWIDE+1)*TILESHIGH+1\r
3331         add     bp,di                                   ; when di = bx, all tiles have been scanned\r
3332         push    di\r
3333         mov     cx,-1                                   ; definately scan the entire thing\r
3334 \r
3335 ;\r
3336 ; scan for a 1 in the update list, meaning a tile needs to be copied\r
3337 ; from the master screen to the current screen\r
3338 ;\r
3339 @@findtile:\r
3340         pop     di                                              ; place to continue scaning from\r
3341         mov     ax,ss\r
3342         mov     es,ax                                   ; search in the data segment\r
3343         mov     ds,ax\r
3344         mov al,1\r
3345         repne   scasb\r
3346         cmp     di,bp\r
3347         je      @@done\r
3348 \r
3349         cmp     [BYTE di],al\r
3350         jne     @@singletile\r
3351         jmp     @@tileblock\r
3352 \r
3353 ;============\r
3354 ;\r
3355 ; copy a single tile\r
3356 ;\r
3357 ;============\r
3358 EVEN\r
3359 @@singletile:\r
3360         inc     di                                              ; we know the next tile is nothing\r
3361         push    di                                      ; save off the spot being scanned\r
3362         sub     di,[updateptr]\r
3363         shl     di,1\r
3364         mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
3365         mov     si,di\r
3366         add     di,[bufferofs]                  ; dest in current screen\r
3367         add     si,[masterofs]                  ; source in master screen\r
3368 \r
3369         mov     dx,SCREENWIDTH-TILEWIDTH\r
3370         mov     ax,[screenseg]\r
3371         mov     ds,ax\r
3372         mov     es,ax\r
3373 \r
3374 ;--------------------------\r
3375 \r
3376 IFE GRMODE-CGAGR\r
3377 \r
3378 REPT    15\r
3379         movsw\r
3380         movsw\r
3381         add     si,dx\r
3382         add     di,dx\r
3383 ENDM\r
3384         movsw\r
3385         movsw\r
3386 \r
3387 ENDIF\r
3388 \r
3389 ;--------------------------\r
3390 \r
3391 IFE GRMODE-EGAGR\r
3392 \r
3393 REPT    15\r
3394         movsb\r
3395         movsb\r
3396         add     si,dx\r
3397         add     di,dx\r
3398 ENDM\r
3399         movsb\r
3400         movsb\r
3401 \r
3402 ENDIF\r
3403 \r
3404 ;--------------------------\r
3405 \r
3406         jmp     @@findtile\r
3407 \r
3408 ;============\r
3409 ;\r
3410 ; more than one tile in a row needs to be updated, so do it as a group\r
3411 ;\r
3412 ;============\r
3413 EVEN\r
3414 @@tileblock:\r
3415         mov     dx,di                                   ; hold starting position + 1 in dx\r
3416         inc     di                                              ; we know the next tile also gets updated\r
3417         repe    scasb                           ; see how many more in a row\r
3418         push    di                                      ; save off the spot being scanned\r
3419 \r
3420         mov     bx,di\r
3421         sub     bx,dx                                   ; number of tiles in a row\r
3422         shl     bx,1                                    ; number of bytes / row\r
3423 \r
3424         mov     di,dx                                   ; lookup position of start tile\r
3425         sub     di,[updateptr]\r
3426         shl     di,1\r
3427         mov     di,[blockstarts-2+di]   ; start of tile location\r
3428         mov     si,di\r
3429         add     di,[bufferofs]                  ; dest in current screen\r
3430         add     si,[masterofs]                  ; source in master screen\r
3431 \r
3432         mov     dx,SCREENWIDTH\r
3433         sub     dx,bx                                   ; offset to next line on screen\r
3434 IFE GRMODE-CGAGR\r
3435         sub     dx,bx                                   ; bx is words wide in CGA tiles\r
3436 ENDIF\r
3437 \r
3438         mov     ax,[screenseg]\r
3439         mov     ds,ax\r
3440         mov     es,ax\r
3441 \r
3442 REPT    15\r
3443         mov     cx,bx\r
3444 IFE GRMODE-CGAGR\r
3445         rep     movsw\r
3446 ENDIF\r
3447 IFE GRMODE-EGAGR\r
3448         rep     movsb\r
3449 ENDIF\r
3450         add     si,dx\r
3451         add     di,dx\r
3452 ENDM\r
3453         mov     cx,bx\r
3454 IFE GRMODE-CGAGR\r
3455         rep     movsw\r
3456 ENDIF\r
3457 IFE GRMODE-EGAGR\r
3458         rep     movsb\r
3459 ENDIF\r
3460 \r
3461         dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
3462         jmp     @@findtile\r
3463 \r
3464 ENDP\r
3465 \r
3466 \r
3467 ;============================================================================\r
3468 \r
3469 \r
3470 ;=================\r
3471 ;\r
3472 ; RFL_MaskForegroundTiles\r
3473 ;\r
3474 ; Scan through update looking for 3's.  If the foreground tile there is a\r
3475 ; masked foreground tile, draw it to the screen\r
3476 ;\r
3477 ;=================\r
3478 \r
3479 PROC    RFL_MaskForegroundTiles\r
3480 PUBLIC  RFL_MaskForegroundTiles\r
3481 USES    SI,DI,BP\r
3482         jmp     SHORT @@realstart\r
3483 @@done:\r
3484 ;\r
3485 ; all tiles have been scanned\r
3486 ;\r
3487         ret\r
3488 \r
3489 @@realstart:\r
3490         mov     di,[updateptr]\r
3491         mov     bp,(TILESWIDE+1)*TILESHIGH+2\r
3492         add     bp,di                                   ; when di = bx, all tiles have been scanned\r
3493         push    di\r
3494         mov     cx,-1                                   ; definately scan the entire thing\r
3495 ;\r
3496 ; scan for a 3 in the update list\r
3497 ;\r
3498 @@findtile:\r
3499         mov     ax,ss\r
3500         mov     es,ax                                   ; scan in the data segment\r
3501         mov     al,3\r
3502         pop     di                                              ; place to continue scaning from\r
3503         repne   scasb\r
3504         cmp     di,bp\r
3505         je      @@done\r
3506 \r
3507 ;============\r
3508 ;\r
3509 ; found a tile, see if it needs to be masked on\r
3510 ;\r
3511 ;============\r
3512 \r
3513         push    di\r
3514 \r
3515         sub     di,[updateptr]\r
3516         shl     di,1\r
3517         mov     si,[updatemapofs-2+di]  ; offset from originmap\r
3518         add     si,[originmap]\r
3519 \r
3520         mov     es,[mapsegs+2]                  ; foreground map plane segment\r
3521         mov     si,[es:si]                              ; foreground tile number\r
3522 \r
3523         or      si,si\r
3524         jz      @@findtile                              ; 0 = no foreground tile\r
3525 \r
3526         mov     bx,si\r
3527         add     bx,INTILE                               ;INTILE tile info table\r
3528         mov     es,[tinf]\r
3529         test    [BYTE PTR es:bx],80h            ;high bit = masked tile\r
3530         jz      @@findtile\r
3531 \r
3532 ;-------------------\r
3533 \r
3534 IFE GRMODE-CGAGR\r
3535 ;=================\r
3536 ;\r
3537 ; mask the tile CGA\r
3538 ;\r
3539 ;=================\r
3540 \r
3541         mov     di,[blockstarts-2+di]\r
3542         add     di,[bufferofs]\r
3543         mov     es,[screenseg]\r
3544         shl     si,1\r
3545         mov     ds,[grsegs+STARTTILE16M*2+si]\r
3546 \r
3547         mov     bx,64                                   ;data starts 64 bytes after mask\r
3548 \r
3549         xor     si,si\r
3550 \r
3551 lineoffset      =       0\r
3552 REPT    16\r
3553         mov     ax,[es:di+lineoffset]   ;background\r
3554         and     ax,[si]                                 ;mask\r
3555         or      ax,[si+bx]                              ;masked data\r
3556         mov     [es:di+lineoffset],ax   ;background\r
3557         inc     si\r
3558         inc     si\r
3559         mov     ax,[es:di+lineoffset+2] ;background\r
3560         and     ax,[si]                                 ;mask\r
3561         or      ax,[si+bx]                              ;masked data\r
3562         mov     [es:di+lineoffset+2],ax ;background\r
3563         inc     si\r
3564         inc     si\r
3565 lineoffset      =       lineoffset + SCREENWIDTH\r
3566 ENDM\r
3567 ENDIF\r
3568 \r
3569 ;-------------------\r
3570 \r
3571 IFE GRMODE-EGAGR\r
3572 ;=================\r
3573 ;\r
3574 ; mask the tile\r
3575 ;\r
3576 ;=================\r
3577 \r
3578         mov     [BYTE planemask],1\r
3579         mov     [BYTE planenum],0\r
3580 \r
3581         mov     di,[blockstarts-2+di]\r
3582         add     di,[bufferofs]\r
3583         mov     [cs:screenstartcs],di\r
3584         mov     es,[screenseg]\r
3585         shl     si,1\r
3586         mov     ds,[grsegs+STARTTILE16M*2+si]\r
3587 \r
3588         mov     bx,32                                   ;data starts 32 bytes after mask\r
3589 \r
3590 @@planeloopm:\r
3591         mov     dx,SC_INDEX\r
3592         mov     al,SC_MAPMASK\r
3593         mov     ah,[ss:planemask]\r
3594         WORDOUT\r
3595         mov     dx,GC_INDEX\r
3596         mov     al,GC_READMAP\r
3597         mov     ah,[ss:planenum]\r
3598         WORDOUT\r
3599 \r
3600         xor     si,si\r
3601         mov     di,[cs:screenstartcs]\r
3602 lineoffset      =       0\r
3603 REPT    16\r
3604         mov     cx,[es:di+lineoffset]   ;background\r
3605         and     cx,[si]                                 ;mask\r
3606         or      cx,[si+bx]                              ;masked data\r
3607         inc     si\r
3608         inc     si\r
3609         mov     [es:di+lineoffset],cx\r
3610 lineoffset      =       lineoffset + SCREENWIDTH\r
3611 ENDM\r
3612         add     bx,32                                   ;the mask is now further away\r
3613         inc     [ss:planenum]\r
3614         shl     [ss:planemask],1                ;shift plane mask over for next plane\r
3615         cmp     [ss:planemask],10000b   ;done all four planes?\r
3616         je      @@drawn                                 ;drawn all four planes\r
3617         jmp     @@planeloopm\r
3618 \r
3619 @@drawn:\r
3620 ENDIF\r
3621 \r
3622 ;-------------------\r
3623 \r
3624         mov     ax,ss\r
3625         mov     ds,ax\r
3626         mov     cx,-1                                   ;definately scan the entire thing\r
3627 \r
3628         jmp     @@findtile\r
3629 \r
3630 ENDP\r
3631 \r
3632 \r
3633 END\r
3634 \r
3635 */\r