]> Shamusworld >> Repos - thunder/blob - src/screen.cpp
fef69a0a7a1653e77e273d60d8534bb8ee3b2f7a
[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 hoffsets[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;
136         uint32 src = (uint32)(hoffsets[hScrollOffset] + voffsets[vScrollOffset]),
137                 dst1 = 0, dst2 = scr->pitch;
138         uint32 srcAdd = 320 - VIRTUAL_SCREEN_WIDTH, dstAdd = (scr->pitch * 2) - (VIRTUAL_SCREEN_WIDTH * 2);
139
140         for(int i=0; i<VIRTUAL_SCREEN_HEIGHT; i++)
141         {
142                 for (int j=0; j<VIRTUAL_SCREEN_WIDTH; j++)
143                 {
144                         pMem[dst1] = pMem[dst1 + 1] = pMem[dst2] = pMem[dst2 + 1] = my_scr[src++];
145                         dst1 += 2;
146                         dst2 += 2;
147                 }
148
149                 src += srcAdd;
150                 dst1 += dstAdd;
151                 dst2 += dstAdd;
152         }
153
154         SDL_UnlockSurface(scr);
155         SDL_UpdateRect(scr, 0, 0, 0, 0);
156 }
157
158 //
159 // Draw character on screen
160 //
161 static inline void DrawChar(uint8 * chr, uint8 * ram, uint8 sx, uint8 sy, uint16 scp, uint32 baseAddr, uint32 scrollOffset, bool transparent/*= true*/)
162 {
163         uint8 scp_lo = (scp + (sx << 1)) & 0x7F;// Let LO byte wrap only...
164         uint16 sp2 = (scp & 0xFF80) | scp_lo;
165         uint8 tile  = ram[sp2++];
166         uint8 index = ram[sp2] & 0x03;
167         uint8 color = ram[sp2];
168         uint32 chind = baseAddr + (((index << 8) + tile) * 64);
169         uint32 sc_addr = (sx * 8) + (sy * 2560) + scrollOffset; // Start addr in my_scr[]
170
171         for(int y=0; y<8; y++)
172         {
173                 for(int x=0; x<8; x++)
174                 {
175                         if (transparent)
176                         {
177                                 if (chr[chind] != 7)
178                                         my_scr[sc_addr] = ccolor[color][chr[chind]];
179                         }
180                         else
181                                 my_scr[sc_addr] = ccolor[color][chr[chind]];
182
183                         sc_addr++;
184                         chind++;
185                 }
186
187                 sc_addr += 312;                                         // Do next line of char...
188         }
189 }
190
191 //
192 // Draw sprites at priority level (new code)
193 //
194 void DrawSprites(uint8 priority)
195 {
196         // Sprite blocks:
197         //
198         // Offset  Note
199         // ------  -----------------------------------------------------------------------------------
200         // 4       h.fb .nnn (f = horz. flip, h = horz. expand, b = sprite offset lo bit, nnn = upper bits of sprite #)
201         // 5       Lower 7 bits of sprite #
202         // 6       Sprite color index (top 7 bits only), bottom bit is bit 8 of X position
203         // 7       Sprite X position (bits 0-7)
204         // 8       Top two bits are sprite priority, bits 4 & 2 are sprite offset hi bit, vert. expand
205         // 9       Sprite Y position (192 - value)
206
207         extern uint8 gram1[];                                                   // Game RAM space
208
209         for(uint16 i=0x5800; i<0x6000; i+=0x10)
210         {
211                 if ((gram1[i + 8] & 0xC0) == priority)          // Check for correct layer...
212                 {
213                         spr_color_index = gram1[i + 6] >> 1;    // Set color...
214                         uint16 x = ((gram1[i + 6] & 0x01) << 8) | gram1[i + 7];
215
216                         if (x > 512 - 32)
217                                 x -= 512;                                                       // Handle neg x values
218
219                         uint16 y = 192 - gram1[i + 9];
220                         uint8 flip = gram1[i + 4] & 0x20;               // Horizontal flip
221                         uint32 spr_num = ((gram1[i + 4] & 0x07) << 9) | ((gram1[i + 5] & 0x7F) << 2);
222                         spr_num += ((gram1[i + 4] & 0x10) >> 4) + ((gram1[i + 8] & 0x10) >> 3);
223
224                         // Draw sprite...
225                         Sprite(spr_num, x, y, flip, (gram1[i + 4] & 0x80 ? true : false), (gram1[i + 8] & 0x04 ? true : false));
226                 }
227         }
228 }
229
230 //
231 // Sprite handler
232 //
233 //void Sprite(uint32 sprnum, uint16 x, uint16 y, uint8 flip, uint16 spr_id)
234 void Sprite(uint32 sprnum, uint16 x, uint16 y, uint8 flip, bool horiz_bl, bool vert_bl)
235 {
236         extern uint8 spr_rom[];
237         uint32 sc_addr;
238
239         x += hScrollOffset;                                                     // Adjust x-coord
240         y += vScrollOffset;                                                     // Adjust y-coord
241
242         sprnum <<= 7;                                                           // 128 bytes per sprite
243
244         if (!vert_bl)
245                 y += 16;
246
247         if (!flip)
248         {
249                 for(uint16 sy=0; sy<16; sy++)
250                 {
251                         for(uint16 sx=0; sx<16; sx+=2)
252                         {
253                                 uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
254                                 uint16 spy = y + sy, spx = x + sx;      // Need to optimize this clipping!
255
256                                 if (spy > 223 || spx > 299)
257                                         sc_addr = 0x13FFE;
258                                 else
259                                         sc_addr = spx + spy * 320;
260
261                                 if (b1 != 15)
262                                         my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
263
264                                 sc_addr++;
265
266                                 if (b2 != 15)
267                                         my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
268                         }
269                 }
270
271                 if (horiz_bl)
272                 {
273                         for(uint16 sy=0; sy<16; sy++)
274                         {
275                                 for(uint16 sx=16; sx<32; sx+=2)
276                                 {
277                                         uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
278                                         uint16 spy = y + sy, spx = x + sx;
279
280                                         if (spy > 223 || spx > 299)
281                                                 sc_addr = 0x13FFE;
282                                         else
283                                                 sc_addr = spx + spy * 320;
284
285                                         if (b1 != 15)
286                                                 my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
287
288                                         sc_addr++;
289
290                                         if (b2 != 15)
291                                                 my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
292                                 }
293                         }
294                 }
295                 else
296                         sprnum += 128;                                                          // Advance to next...
297
298                 if (vert_bl)
299                 {
300                         y += 16;                                                                        // Do next row...
301
302                         for(uint16 sy=0; sy<16; sy++)
303                         {
304                                 for(uint16 sx=0; sx<16; sx+=2)
305                                 {
306                                         uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
307                                         uint16 spy = y + sy, spx = x + sx;
308
309                                         if (spy > 223 || spx > 299)
310                                                 sc_addr = 0x13FFE;
311                                         else
312                                                 sc_addr = spx + spy * 320;
313
314                                         if (b1 != 15)
315                                                 my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
316
317                                         sc_addr++;
318
319                                         if (b2 != 15)
320                                                 my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
321                                 }
322                         }
323
324                         if (horiz_bl)
325                         {
326                                 for(uint16 sy=0; sy<16; sy++)
327                                 {
328                                         for(uint16 sx=16; sx<32; sx+=2)
329                                         {
330                                                 uint8 b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
331                                                 uint16 spy = y + sy, spx = x + sx;
332
333                                                 if (spy > 223 || spx > 299)
334                                                         sc_addr = 0x13FFE;
335                                                 else
336                                                         sc_addr = spx + spy * 320;
337
338                                                 if (b1 != 15)
339                                                         my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
340
341                                                 sc_addr++;
342
343                                                 if (b2 != 15)
344                                                         my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
345                                         }
346                                 }
347                         }
348                 }
349         }
350         else    // Flip
351         {
352     if (horiz_bl)
353     {
354       for(uint16 sy=0; sy<16; sy++)
355       {
356         for(uint16 sx=30; sx!=14; sx-=2)
357         {
358           uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
359         uint16 spy = y+sy, spx = x+sx;
360         if ((spy>223) || (spx>299))
361           sc_addr = 0x13FFE;
362         else
363           sc_addr = spx+spy*320;
364           if (b2 != 15)
365             my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
366           sc_addr++;
367           if (b1 != 15)
368             my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
369         }
370       }
371     }
372     for(uint16 sy=0; sy<16; sy++)
373     {
374       for(uint16 sx=14; sx!=0xFFFE; sx-=2)
375       {
376         uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
377         uint16 spy = y+sy, spx = x+sx;
378         if ((spy>223) || (spx>299))
379           sc_addr = 0x13FFE;
380         else
381           sc_addr = spx+spy*320;
382         if (b2 != 15)
383           my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
384         sc_addr++;
385         if (b1 != 15)
386           my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
387       }
388     }
389     if (!horiz_bl)  sprnum += 128;  // If single, skip sprite...
390     if (vert_bl)
391     {
392       y += 16;      // Adjust Y coord...
393       if (horiz_bl)
394       {
395         for(uint16 sy=0; sy<16; sy++)
396         {
397           for(uint16 sx=30; sx!=14; sx-=2)
398           {
399             uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
400         uint16 spy = y+sy, spx = x+sx;
401         if ((spy>223) || (spx>299))
402           sc_addr = 0x13FFE;
403         else
404           sc_addr = spx+spy*320;
405             if (b2 != 15)
406               my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
407             sc_addr++;
408             if (b1 != 15)
409                my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
410           }
411         }
412       }
413       for(uint16 sy=0; sy<16; sy++)
414       {
415         for(uint16 sx=14; sx!=0xFFFE; sx-=2)
416         {
417           uint8 b1 = spr_rom[sprnum]>>4, b2 = spr_rom[sprnum++]&0x0F;
418         uint16 spy = y+sy, spx = x+sx;
419         if ((spy>223) || (spx>299))
420           sc_addr = 0x13FFE;
421         else
422           sc_addr = spx+spy*320;
423           if (b2 != 15)
424             my_scr[sc_addr] = scolor[spr_color_index][b2]; // Store it
425           sc_addr++;
426           if (b1 != 15)
427             my_scr[sc_addr] = scolor[spr_color_index][b1]; // Store it
428         }
429       }
430     }
431   }
432 }
433
434 int FindPCXName()
435 {
436         static int pcxNum = -1;                                                         // This needs to go elsewhere... (or does it?)
437         char filename[30];
438         FILE * fr;
439
440         pcxNum++;
441
442         while (pcxNum < 10000)
443         {
444                 sprintf(filename, "thnd%04i.pcx", pcxNum);
445
446                 if ((fr = fopen(filename, "r")) == NULL)
447                         return pcxNum;                                                          // file does not exist - we can create it
448
449                 pcxNum++;
450         }
451
452         return -1;
453 }
454
455 void SnapPCX(SDL_Surface * scr)
456 {
457         char filename[30];
458         int i, line;
459         FILE * fw;
460         int XMax = 319;       // Need to adjust this to handle 288 bytes per line
461         int YMax = 223;
462         int bytesPerLine = 320;
463
464         if ((i = FindPCXName()) < 0)
465                 return;               // failed...
466
467         sprintf(filename, "thnd%04i.pcx", i);
468
469         if ((fw = fopen(filename, "wb")) == NULL)
470                 return;  // failed...
471
472         // Write the header
473
474         fputc(0xA, fw); // pcx signature
475         fputc(0x5, fw); // version 5
476         fputc(0x1, fw); // RLE encoding
477         fputc(0x8, fw); // bits per pixel
478         fputc(0, fw);  fputc(0, fw);  fputc(0, fw);  fputc(0, fw); // XMin=0,YMin=0
479         fputc(XMax&0xFF, fw);  fputc(XMax>>8, fw);
480         fputc(YMax&0xFF, fw);  fputc(YMax>>8, fw);
481         fputc(0, fw);  fputc(0, fw);  fputc(0, fw);  fputc(0,fw); // unknown DPI
482         for (i=0; i<48; i++)  fputc(0, fw);                 // EGA color palette
483         fputc(0, fw); // reserved
484         fputc(1, fw); // number of bit planes
485         fputc(bytesPerLine&0xFF, fw);  fputc(bytesPerLine>>8, fw);
486         fputc(1, fw);  fputc(0, fw); // palette info - unused
487         fputc((XMax+1)&0xFF, fw);  fputc((XMax+1)>>8, fw);
488         fputc((YMax+1)&0xFF, fw);  fputc((YMax+1)>>8, fw); // screen resolution
489         for (i=0; i<54; i++)  fputc(0, fw); // unused
490
491         // Instead of using the screen, we should use our internal buffer...
492         SDL_LockSurface(scr);
493
494         uint32 mem = scr->pitch * 8; // Skip first line... WAS:320*8;
495         for (line=0; line<=YMax; line++)
496         {
497                 int count;
498                 int last;
499                 int xpos;
500                 uint8 * pMem = (uint8 *)scr->pixels;
501
502                 xpos = 0;
503                 while (xpos < bytesPerLine)
504                 {
505                         last = pMem[mem++];
506                         xpos++;
507                         count = 1;
508                         while (pMem[mem] == last && xpos < bytesPerLine && count < 63)
509                         {
510                                 mem++;  count++;  xpos++;
511                         }
512
513                         if (count > 1 || (last&0xC0) == 0xC0)
514                         {
515                                 fputc(0xC0 | (count & 0x3F), fw);  fputc(last & 0xFF, fw);
516                         }
517                         else fputc(last & 0xFF, fw);
518                 }
519                 mem += (scr->pitch - 320);  // Handle non-standard line lengths...
520         }
521
522         SDL_UnlockSurface(scr);
523
524         // Write the palette
525
526         fputc(0x0C, fw);
527         for (i=0; i<768; i++)
528                 fputc(palette[i], fw);
529
530         fclose(fw);    // success!
531 }