]> Shamusworld >> Repos - thunder/blob - src/screen.cpp
Some cleanup of the screen tile rendering. More to come.
[thunder] / src / screen.cpp
1 //
2 // Screen Handler
3 //
4 // This sets screen to VESA 320x240 LFB so we can use the WHOLE laptop screen
5 // Now with VESA2 support!
6 // Also, support routines for video hardware emulation are included
7 //
8 // by James L. Hammons
9 //
10 // (C) 2003 Underground Software
11 //
12 // JLH = James L. Hammons <jlhamm@acm.org>
13 //
14 // Who  When        What
15 // ---  ----------  ------------------------------------------------------------
16 // JLH  03/12/2003  Ported this steaming pile of crap from VESA to SDL
17 //
18
19 #include "screen.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string>                     // For memset()
24 #include "gui.h"
25
26 // Private function prototypes
27
28 void DrawSprites(uint8 priority);
29 int FindPCXName(void);
30 static void DrawChar(uint8 * chr, uint8 * ram, uint8 sx, uint8 sy, uint16 scp, uint32 baseAddr, uint32 scrollOffset, bool transparent = true);
31
32 // Private global variables
33
34 uint8 my_scr[0x14000];                                                                  // Screen buffer...
35 uint8 palette[768];                                                                             // Screen palette
36 uint8 ccolor[256][8];                                                                   // Character colors
37 uint8 scolor[128][16];                                                                  // Sprite colors
38 bool charbase;                                                                                  // Character base pointer...
39 uint8 hScrollOffset;                                                                            // Horizontal scroll offset
40 uint8 vScrollOffset;                                                                            // Vertical scroll offset
41 uint8 spr_color_index;                                                                  // Sprite color index
42 uint32 offsets[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };                 // Scroll offsets...
43 uint32 voffsets[8] = { 0, 320, 640, 960, 1280, 1600, 1920, 2240 };
44
45 extern bool show_text;                                                                  // Whether or not to show text
46 extern bool show_scr;                                                                   // Whether or not to show screen
47
48 //
49 // Render the NAMCO screen
50 //
51 void BlitChar(SDL_Surface * scr, uint8 * chr, uint8 * ram)
52 {
53 // Screen structure:
54 // Screen is 288 x 224 pixels, with character and independent sprites. Tiles are 36 x 28.
55 // There are four tile planes, three of them affected by the h/vscroll values, the last
56 // one is a static display (also has highest priority). Screens are 128 bytes wide by
57 // 32 bytes high.
58
59         if (show_scr)
60         {
61                 int sx, sy;
62                 uint32 sc_base = ((ram[0x9000] << 8) | ram[0x9001]) + 4;        // Adjust hscroll val
63                 hScrollOffset = sc_base & 0x07;                                 // Horiz. fine scroll offset
64                 sc_base = (sc_base & 0xFFF8) >> 2;                              // Skip odds..
65                 uint8 vsc_base = ((ram[0x9002] + 1) & 0xF8) >> 3;// Vertical scroll addr adjust
66                 vScrollOffset = ((ram[0x9002] + 1) & 0x07);             // Vertical fine scroll amount
67                 uint32 scp1 = 0x0180 | ((sc_base + 0x04) & 0x7F);       /*0x0188;*/
68                 uint32 scp2 = 0x1180 | ((sc_base + 0x04) & 0x7F);       /*0x1188;*/
69                 uint32 scp3 = 0x2180 | ((sc_base + 0x04) & 0x7F);       /*0x2188;*/
70                 uint32 scp = 0x3208;
71                 scp1 += vsc_base * 0x80;
72                 scp1 &= 0x0FFF;                                                                 // Set vertical scroll addr
73                 scp2 += vsc_base * 0x80;
74                 scp2 = 0x1000 | (scp2 & 0x0FFF);                                // Set vertical scroll addr
75                 scp3 += vsc_base * 0x80;
76                 scp3 = 0x2000 | (scp3 & 0x0FFF);                                // Set vertical scroll addr
77
78                 for(sy=0; sy<29; sy++)
79                 {
80                         for(sx=0; sx<37; sx++)
81                                 DrawChar(chr, ram, sx, sy, scp1, (charbase ? 0x20000 : 0x00000), 0, false);
82
83                         scp1 += 0x80;
84                         scp1 = 0x0000 | (scp1 & 0x0FFF);
85                 }
86
87                 DrawSprites(0x40);                                                              // Draw sprites at lowest layer...
88
89                 for(sy=0; sy<29; sy++)
90                 {
91                         for(sx=0; sx<37; sx++)
92                                 DrawChar(chr, ram, sx, sy, scp2, (charbase ? 0x30000 : 0x10000), 0);
93
94                         scp2 += 0x80;
95                         scp2 = 0x1000 | (scp2 & 0x0FFF);
96                 }
97
98                 DrawSprites(0x80);                                                              // Draw sprites under layer #2...
99
100                 for(sy=0; sy<29; sy++)
101                 {
102                         for(sx=0; sx<37; sx++)
103                                 DrawChar(chr, ram, sx, sy, scp3, 0x40000, 0);
104
105                         scp3 += 0x80;
106                         scp3 = 0x2000 | (scp3 & 0x0FFF);
107                 }
108
109                 DrawSprites(0xC0);                                                              // Draw highest priority sprites...
110
111                 for(sy=0; sy<28; sy++)
112                 {
113                         for(sx=0; sx<36; sx++)
114                                 DrawChar(chr, ram, sx, sy, scp, 0x50000, hScrollOffset + voffsets[vScrollOffset]);
115
116                         scp += 0x80;
117                 }
118         }
119
120         if (show_text)
121                 DrawText();                                                                             // Draw a msg if needed...
122
123         if (ShowGUI())
124                 DrawGUI();                                                                              // Show GUI if active...
125
126         if (SDL_LockSurface(scr) < 0)
127         {
128 //              fprintf(stderr, "Couldn't lock the display surface: %s\n", SDL_GetError());
129 //              exit(2);
130         }
131
132         // Rolling Thunder screen size is 288 x 224. Virtual is this, real may not be...
133
134         // Doubled pixel blit (should be faster now!)
135 //      uint8 * pMem = (uint8 *)scr->pixels + ((scr->pitch * 8 + 16) * 2);
136         uint8 * pMem = (uint8 *)scr->pixels;
137         uint32 src = (uint32)(offsets[hScrollOffset] + voffsets[vScrollOffset]),
138                 dst1 = 0, dst2 = scr->pitch;
139 //      uint32 srcAdd = 320 - 288, dstAdd = (scr->pitch * 2) - (288 * 2);
140         uint32 srcAdd = 320 - 288, dstAdd = (scr->pitch * 2) - (288 * 2);
141
142         for(int i=0; i<224; i++)
143         {
144                 for (int j=0; j<288; j++)
145                 {
146                         pMem[dst1] = pMem[dst1 + 1] = pMem[dst2] = pMem[dst2 + 1] = my_scr[src++];
147                         dst1 += 2;
148                         dst2 += 2;
149                 }
150
151                 src += srcAdd;
152                 dst1 += dstAdd;
153                 dst2 += dstAdd;
154         }
155
156         SDL_UnlockSurface(scr);
157         SDL_UpdateRect(scr, 0, 0, 0, 0);
158 }
159
160 //
161 // Draw character on screen
162 //
163 static inline void DrawChar(uint8 * chr, uint8 * ram, uint8 sx, uint8 sy, uint16 scp, uint32 baseAddr, uint32 scrollOffset, bool transparent/*= true*/)
164 {
165         uint8 scp_lo = (scp + (sx << 1)) & 0x7F;// Let LO byte wrap only...
166         uint16 sp2 = (scp & 0xFF80) | scp_lo;
167         uint8 tile  = ram[sp2++];
168         uint8 index = ram[sp2] & 0x03;
169         uint8 color = ram[sp2];
170         uint32 chind = baseAddr + (((index << 8) + tile) * 64);
171         uint32 sc_addr = (sx * 8) + (sy * 2560) + scrollOffset; // Start addr in my_scr[]
172
173         for(int y=0; y<8; y++)
174         {
175                 for(int x=0; x<8; x++)
176                 {
177                         if (transparent)
178                         {
179                                 if (chr[chind] != 7)
180                                         my_scr[sc_addr] = ccolor[color][chr[chind]];
181                         }
182                         else
183                                 my_scr[sc_addr] = ccolor[color][chr[chind]];
184
185                         sc_addr++;
186                         chind++;
187                 }
188
189                 sc_addr += 312;                                         // Do next line of char...
190         }
191 }
192
193 //
194 // Draw sprites at priority level (new code)
195 //
196 void DrawSprites(uint8 priority)
197 {
198         extern uint8 gram1[];                                                           // Game RAM space
199
200         for(uint16 i=0x5800; i<0x6000; i+=0x10)
201         {
202                 if ((gram1[i + 8] & 0xC0) == priority)                  // Check for correct layer...
203                 {
204                         spr_color_index = gram1[i + 6] >> 1;            // Set color...
205                         uint16 x = ((gram1[i + 6] & 0x01) << 8) | gram1[i + 7];
206
207                         if (x > 512 - 32)
208                                 x -= 512;                                                               // Handle neg x values
209
210                         uint16 y = 192 - gram1[i + 9];
211                         uint16 hdr = (gram1[i + 4] & 0x90) << 8 | (gram1[i + 8] & 0x14);
212                         uint8 flip = gram1[i + 4] & 0x20;                       // Horizontal flip
213                         uint32 spr_num = ((gram1[i + 4] & 0x07) << 9) | ((gram1[i + 5] & 0x7F) << 2);
214
215                         Sprite(spr_num, x, y, flip, hdr);                       // Draw sprite...
216                 }
217         }
218 }
219
220 //
221 // Sprite handler
222 //
223 void Sprite(uint32 sprnum, uint16 x, uint16 y, uint8 flip, uint16 spr_id)
224 {
225         extern uint8 spr_rom[];
226         // To show or not to show a 16x16 block in the 4x4 grid...
227         bool horiz_bl = false, vert_bl = false;
228         uint32 sc_addr;
229
230         x += hScrollOffset;                                                                     // Adjust x-coord
231         y += vScrollOffset;                                                                     // Adjust y-coord
232
233 //      8421 8421 8421 8421
234 //      1xxx xxxx xxxx xxxx   horiz_bl?
235 //      xxxx xxxx xxxx x1xx   vert_bl?
236 //      xxxx xxxx xxxx x0xx   y + 16?
237 //      xxxB xxxx xxxA xxxx   spr# (AB) to start
238
239 #if 1
240         if (spr_id & 0x8000)
241                 horiz_bl = true;
242
243         if (spr_id & 0x0004)
244                 vert_bl = true;
245         else
246                 y += 16;
247
248         sprnum += ((spr_id & 0x1000) >> 12) + ((spr_id & 0x0010) >> 3);
249 #else
250         if (spr_id == 0x8004)
251         {
252                 horiz_bl = true;  vert_bl = true;
253         }
254         if (spr_id == 0x8000)
255         {
256                 horiz_bl = true;  y += 16;
257         }
258         if (spr_id == 0x8010)
259         {
260                 horiz_bl = true;  y += 16;  sprnum += 2;
261         }
262         if (spr_id == 0x0004)
263         {
264                 vert_bl = true;
265         }
266         if (spr_id == 0x1004)
267         {
268                 vert_bl = true;  sprnum++;
269         }
270         if (spr_id == 0x0000)
271         {
272                 y += 16;
273         }
274         if (spr_id == 0x1000)
275         {
276                 y += 16;  sprnum++;
277         }
278         if (spr_id == 0x0010)
279         {
280                 y += 16;  sprnum += 2;
281         }
282         if (spr_id == 0x1010)
283         {
284                 y += 16;  sprnum += 3;
285         }
286 #endif
287
288         sprnum <<= 7;                                                                   // 128 bytes per sprite
289
290         if (!flip)
291         {
292                 for(uint16 sy=0; sy<16; sy++)
293                 {
294                         for(uint16 sx=0; sx<16; sx+=2)
295                         {
296                                 uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
297                                 uint16 spy = y + sy, spx = x + sx;      // Need to optimize this clipping!
298
299                                 if (spy > 223 || spx > 299)
300                                         sc_addr = 0x13FFE;
301                                 else
302                                         sc_addr = spx + spy * 320;
303
304                                 if (b1 != 15)
305                                         my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
306
307                                 sc_addr++;
308
309                                 if (b2 != 15)
310                                         my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
311                         }
312                 }
313
314                 if (horiz_bl)
315                 {
316                         for(uint16 sy=0; sy<16; sy++)
317                         {
318                                 for(uint16 sx=16; sx<32; sx+=2)
319                                 {
320                                         uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
321                                         uint16 spy = y + sy, spx = x + sx;
322
323                                         if (spy > 223 || spx > 299)
324                                                 sc_addr = 0x13FFE;
325                                         else
326                                                 sc_addr = spx + spy * 320;
327
328                                         if (b1 != 15)
329                                                 my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
330
331                                         sc_addr++;
332
333                                         if (b2 != 15)
334                                                 my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
335                                 }
336                         }
337                 }
338                 else
339                         sprnum += 128;                                                          // Advance to next...
340
341                 if (vert_bl)
342                 {
343                         y += 16;                                                                        // Do next row...
344
345                         for(uint16 sy=0; sy<16; sy++)
346                         {
347                                 for(uint16 sx=0; sx<16; sx+=2)
348                                 {
349                                         uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
350                                         uint16 spy = y + sy, spx = x + sx;
351
352                                         if (spy > 223 || spx > 299)
353                                                 sc_addr = 0x13FFE;
354                                         else
355                                                 sc_addr = spx + spy * 320;
356
357                                         if (b1 != 15)
358                                                 my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
359
360                                         sc_addr++;
361
362                                         if (b2 != 15)
363                                                 my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
364                                 }
365                         }
366
367                         if (horiz_bl)
368                         {
369                                 for(uint16 sy=0; sy<16; sy++)
370                                 {
371                                         for(uint16 sx=16; sx<32; sx+=2)
372                                         {
373                                                 uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
374                                                 uint16 spy = y + sy, spx = x + sx;
375
376                                                 if (spy > 223 || spx > 299)
377                                                         sc_addr = 0x13FFE;
378                                                 else
379                                                         sc_addr = spx + spy * 320;
380
381                                                 if (b1 != 15)
382                                                         my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
383
384                                                 sc_addr++;
385
386                                                 if (b2 != 15)
387                                                         my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
388                                         }
389                                 }
390                         }
391                 }
392         }
393         else    // Flip
394         {
395     if (horiz_bl)
396     {
397       for(uint16 sy=0; sy<16; sy++)
398       {
399         for(uint16 sx=30; sx!=14; sx-=2)
400         {
401           uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
402         uint16 spy = y+sy, spx = x+sx;
403         if ((spy>223) || (spx>299))
404           sc_addr = 0x13FFE;
405         else
406           sc_addr = spx+spy*320;
407           if (b2 != 15)
408             my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
409           sc_addr++;
410           if (b1 != 15)
411             my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
412         }
413       }
414     }
415     for(uint16 sy=0; sy<16; sy++)
416     {
417       for(uint16 sx=14; sx!=0xFFFE; sx-=2)
418       {
419         uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
420         uint16 spy = y+sy, spx = x+sx;
421         if ((spy>223) || (spx>299))
422           sc_addr = 0x13FFE;
423         else
424           sc_addr = spx+spy*320;
425         if (b2 != 15)
426           my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
427         sc_addr++;
428         if (b1 != 15)
429           my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
430       }
431     }
432     if (!horiz_bl)  sprnum += 128;  // If single, skip sprite...
433     if (vert_bl)
434     {
435       y += 16;      // Adjust Y coord...
436       if (horiz_bl)
437       {
438         for(uint16 sy=0; sy<16; sy++)
439         {
440           for(uint16 sx=30; sx!=14; sx-=2)
441           {
442             uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
443         uint16 spy = y+sy, spx = x+sx;
444         if ((spy>223) || (spx>299))
445           sc_addr = 0x13FFE;
446         else
447           sc_addr = spx+spy*320;
448             if (b2 != 15)
449               my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
450             sc_addr++;
451             if (b1 != 15)
452                my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
453           }
454         }
455       }
456       for(uint16 sy=0; sy<16; sy++)
457       {
458         for(uint16 sx=14; sx!=0xFFFE; sx-=2)
459         {
460           uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
461         uint16 spy = y+sy, spx = x+sx;
462         if ((spy>223) || (spx>299))
463           sc_addr = 0x13FFE;
464         else
465           sc_addr = spx+spy*320;
466           if (b2 != 15)
467             my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
468           sc_addr++;
469           if (b1 != 15)
470             my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
471         }
472       }
473     }
474   }
475 }
476
477 int FindPCXName()
478 {
479         static int pcxNum = -1;                                                         // This needs to go elsewhere... (or does it?)
480         char filename[30];
481         FILE * fr;
482
483         pcxNum++;
484
485         while (pcxNum < 10000)
486         {
487                 sprintf(filename, "thnd%04i.pcx", pcxNum);
488
489                 if ((fr = fopen(filename, "r")) == NULL)
490                         return pcxNum;                                                          // file does not exist - we can create it
491
492                 pcxNum++;
493         }
494
495         return -1;
496 }
497
498 void SnapPCX(SDL_Surface * scr)
499 {
500         char filename[30];
501         int i, line;
502         FILE * fw;
503         int XMax = 319;       // Need to adjust this to handle 288 bytes per line
504         int YMax = 223;
505         int bytesPerLine = 320;
506
507         if ((i = FindPCXName()) < 0)
508                 return;               // failed...
509
510         sprintf(filename, "thnd%04i.pcx", i);
511
512         if ((fw = fopen(filename, "wb")) == NULL)
513                 return;  // failed...
514
515         // Write the header
516
517         fputc(0xA, fw); // pcx signature
518         fputc(0x5, fw); // version 5
519         fputc(0x1, fw); // RLE encoding
520         fputc(0x8, fw); // bits per pixel
521         fputc(0, fw);  fputc(0, fw);  fputc(0, fw);  fputc(0, fw); // XMin=0,YMin=0
522         fputc(XMax&0xFF, fw);  fputc(XMax>>8, fw);
523         fputc(YMax&0xFF, fw);  fputc(YMax>>8, fw);
524         fputc(0, fw);  fputc(0, fw);  fputc(0, fw);  fputc(0,fw); // unknown DPI
525         for (i=0; i<48; i++)  fputc(0, fw);                 // EGA color palette
526         fputc(0, fw); // reserved
527         fputc(1, fw); // number of bit planes
528         fputc(bytesPerLine&0xFF, fw);  fputc(bytesPerLine>>8, fw);
529         fputc(1, fw);  fputc(0, fw); // palette info - unused
530         fputc((XMax+1)&0xFF, fw);  fputc((XMax+1)>>8, fw);
531         fputc((YMax+1)&0xFF, fw);  fputc((YMax+1)>>8, fw); // screen resolution
532         for (i=0; i<54; i++)  fputc(0, fw); // unused
533
534         // Instead of using the screen, we should use our internal buffer...
535         SDL_LockSurface(scr);
536
537         uint32 mem = scr->pitch * 8; // Skip first line... WAS:320*8;
538         for (line=0; line<=YMax; line++)
539         {
540                 int count;
541                 int last;
542                 int xpos;
543                 uint8 * pMem = (uint8 *)scr->pixels;
544
545                 xpos = 0;
546                 while (xpos < bytesPerLine)
547                 {
548                         last = pMem[mem++];
549                         xpos++;
550                         count = 1;
551                         while (pMem[mem] == last && xpos < bytesPerLine && count < 63)
552                         {
553                                 mem++;  count++;  xpos++;
554                         }
555
556                         if (count > 1 || (last&0xC0) == 0xC0)
557                         {
558                                 fputc(0xC0 | (count & 0x3F), fw);  fputc(last & 0xFF, fw);
559                         }
560                         else fputc(last & 0xFF, fw);
561                 }
562                 mem += (scr->pitch - 320);  // Handle non-standard line lengths...
563         }
564
565         SDL_UnlockSurface(scr);
566
567         // Write the palette
568
569         fputc(0x0C, fw);
570         for (i=0; i<768; i++)
571                 fputc(palette[i], fw);
572
573         fclose(fw);    // success!
574 }