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