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