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