]> Shamusworld >> Repos - thunder/blob - src/screen.cpp
dda0f6e665eb42f349723213b4b688e370e8384c
[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                                 DrawChar(chr, ram, sx, sy, scp0, (charbase ? 0x20000 : 0x00000), hScrollOffset, vScrollOffset, false);
91
92                         scp0 += 0x80;
93                         scp0 = 0x0000 | (scp0 & 0x0FFF);
94                 }
95
96                 // Draw sprites at lowest layer...
97                 DrawSprites(0x40);
98
99                 // Layer 1
100                 for(uint8_t sy=0; sy<29; sy++)
101                 {
102                         for(uint8_t sx=0; sx<37; sx++)
103                                 DrawChar(chr, ram, sx, sy, scp1, (charbase ? 0x30000 : 0x10000), hScrollOffset, vScrollOffset);
104
105                         scp1 += 0x80;
106                         scp1 = 0x1000 | (scp1 & 0x0FFF);
107                 }
108
109                 // Draw sprites under layer #2...
110                 DrawSprites(0x80);
111
112                 // Layer 2
113                 for(uint8_t sy=0; sy<29; sy++)
114                 {
115                         for(uint8_t sx=0; sx<37; sx++)
116                                 DrawChar(chr, ram, sx, sy, scp2, 0x40000, hScrollOffset, vScrollOffset);
117
118                         scp2 += 0x80;
119                         scp2 = 0x2000 | (scp2 & 0x0FFF);
120                 }
121
122                 // Draw highest priority sprites...
123                 DrawSprites(0xC0);
124
125                 // Layer 3 (top layer)
126                 for(uint8_t sy=0; sy<28; sy++)
127                 {
128                         for(uint8_t sx=0; sx<36; sx++)
129                                 DrawChar(chr, ram, sx, sy, scp3, 0x50000);
130
131                         scp3 += 0x80;
132                 }
133         }
134
135         // Draw a msg if needed...
136         if (show_text)
137                 DrawText();
138
139         // Show GUI if active...
140         if (ShowGUI())
141                 DrawGUI();
142
143         // Rolling Thunder screen size is 288 x 224. Virtual is this, real may not
144         // be... (and we don't have to care about that, the OpenGL backend takes
145         // care of it.)
146
147         uint32_t src = 0;
148
149         for(int i=0; i<VIRTUAL_SCREEN_HEIGHT; i++)
150         {
151                 for (int j=0; j<VIRTUAL_SCREEN_WIDTH; j++)
152                 {
153                         scrBuffer[src] = palette[my_scr[src]];
154                         src++;
155                 }
156         }
157
158         RenderScreenBuffer();
159 }
160
161
162 //
163 // Draw character on screen
164 //
165 //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*/)
166 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*/)
167 {
168         uint8_t scp_lo = (scp + (sx << 1)) & 0x7F;// Let LO byte wrap only...
169         uint16_t sp2 = (scp & 0xFF80) | scp_lo;
170         uint8_t tile  = ram[sp2++];
171         uint8_t index = ram[sp2] & 0x03;
172         uint8_t color = ram[sp2];
173         uint32_t chind = baseAddr + (((index << 8) + tile) * 64);
174         int xStart = (int)(sx * 8) - xScroll;
175         int yStart = (int)(sy * 8) - yScroll;
176         int32_t sc_addr = xStart + (yStart * 288);
177
178         for(int y=0; y<8; y++)
179         {
180                 for(int x=0; x<8; x++)
181                 {
182                         if (((xStart + x) < 0) || ((xStart + x) >= 288)
183                                 || ((yStart + y) < 0) || ((yStart + y) >= 224))
184                         {
185                                 sc_addr++;
186                                 chind++;
187                                 continue;
188                         }
189
190                         if (transparent)
191                         {
192                                 if (chr[chind] != 7)
193                                         my_scr[sc_addr] = ccolor[color][chr[chind]];
194                         }
195                         else
196                                 my_scr[sc_addr] = ccolor[color][chr[chind]];
197
198                         sc_addr++;
199                         chind++;
200                 }
201
202                 sc_addr += (288 - 8);           // Do next line of char...
203         }
204 }
205
206
207 //
208 // Draw sprites at priority level (new code)
209 //
210 void DrawSprites(uint8_t priority)
211 {
212 // Sprite blocks:
213 //
214 // Offset  Note
215 // ------  --------------------------------------------------------------------
216 // 4       h.fb .nnn (f = horz. flip, h = horz. expand, b = sprite offset lo
217 //         bit, nnn = upper bits of sprite #)
218 // 5       Lower 7 bits of sprite #
219 // 6       Sprite color index (top 7 bits only), bottom bit is bit 8 of X
220 //         position
221 // 7       Sprite X position (bits 0-7)
222 // 8       Top two bits are sprite priority, bits 4 & 2 are sprite offset hi
223 //         bit, vert. expand
224 // 9       Sprite Y position (192 - value)
225
226         extern uint8_t gram1[];                                                 // Game RAM space
227
228         for(uint16_t i=0x5800; i<0x6000; i+=0x10)
229         {
230                 if ((gram1[i + 8] & 0xC0) == priority)          // Check for correct layer...
231                 {
232                         spr_color_index = gram1[i + 6] >> 1;    // Set color...
233                         uint16_t x = ((gram1[i + 6] & 0x01) << 8) | gram1[i + 7];
234
235                         if (x > 512 - 32)
236                                 x -= 512;                                                       // Handle neg x values
237
238                         uint16_t y = 192 - gram1[i + 9];
239                         uint8_t flip = gram1[i + 4] & 0x20;             // Horizontal flip
240                         uint32_t spr_num = ((gram1[i + 4] & 0x07) << 9)
241                                 | ((gram1[i + 5] & 0x7F) << 2)
242                                 | ((gram1[i + 4] & 0x10) >> 4)
243                                 | ((gram1[i + 8] & 0x10) >> 3);
244
245                         // Draw sprite...
246                         Sprite(spr_num, x, y, flip, gram1[i + 4] & 0x80, gram1[i + 8] & 0x04);
247                 }
248         }
249 }
250
251
252 static inline void DrawSpriteBlock(uint32_t & sprnum, uint16_t x, uint16_t y, uint16_t xStart, uint16_t xEnd, int16_t xInc)
253 {
254         extern uint8_t spr_rom[];
255
256         for(uint16_t sy=0; sy<16; sy++)
257         {
258                 for(uint16_t sx=xStart; sx<xEnd; sx+=xInc)
259                 {
260                         uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
261                         uint16_t spy = y + sy, spx = x + sx;    // Need to optimize this clipping! [eh?]
262
263                         // This handles negative values, by casting as unsigned
264                         uint32_t sc_addr = ((spy >= 224) || (spx >= 288) ? 0x13FFE : spx + (spy * 288));
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
277
278 static inline void DrawSpriteBlock2(uint32_t & sprnum, uint16_t x, uint16_t y, uint16_t xStart, uint16_t xEnd, int16_t xInc)
279 {
280         extern uint8_t spr_rom[];
281
282         for(uint16_t sy=0; sy<16; sy++)
283         {
284                 for(uint16_t sx=xStart; sx!=xEnd; sx+=xInc)
285                 {
286                         uint8_t b1 = spr_rom[sprnum] >> 4, b2 = spr_rom[sprnum++] & 0x0F;
287                         uint16_t spy = y + sy, spx = x + sx;    // Need to optimize this clipping! [eh?]
288
289                         // This handles negative values, by casting as unsigned
290                         uint32_t sc_addr = ((spy >= 224) || (spx >= 288) ? 0x13FFE : spx + (spy * 288));
291
292                         if (b2 != 15)
293                                 my_scr[sc_addr] = scolor[spr_color_index][b2];  // Store it
294
295                         sc_addr++;
296
297                         if (b1 != 15)
298                                 my_scr[sc_addr] = scolor[spr_color_index][b1];  // Store it
299                 }
300         }
301 }
302
303
304 //
305 // Sprite handler
306 //
307 void Sprite(uint32_t sprnum, uint16_t x, uint16_t y, uint8_t flip,
308         uint8_t horiz_bl, uint8_t vert_bl)
309 {
310         // 128 bytes per sprite (16 x 16 chunks, 4 bits per pixel)
311         sprnum <<= 7;
312
313         if (!vert_bl)
314                 y += 16;
315
316         if (!flip)
317         {
318                 DrawSpriteBlock(sprnum, x, y, 0, 16, 2);
319
320                 if (horiz_bl)
321                         DrawSpriteBlock(sprnum, x, y, 16, 32, 2);
322                 else
323                         sprnum += 128;  // Advance to next...
324
325                 if (vert_bl)
326                 {
327                         y += 16;                // Do next row...
328
329                         DrawSpriteBlock(sprnum, x, y, 0, 16, 2);
330
331                         if (horiz_bl)
332                                 DrawSpriteBlock(sprnum, x, y, 16, 32, 2);
333                 }
334         }
335         else    // Flip
336         {
337                 if (horiz_bl)
338                         DrawSpriteBlock2(sprnum, x, y, 30, 14, -2);
339
340                 DrawSpriteBlock2(sprnum, x, y, 14, 0xFFFE, -2);
341
342                 if (!horiz_bl)
343                         sprnum += 128;  // If single, skip sprite...
344
345                 if (vert_bl)
346                 {
347                         y += 16;                // Adjust Y coord...
348
349                         if (horiz_bl)
350                                 DrawSpriteBlock2(sprnum, x, y, 30, 14, -2);
351
352                         DrawSpriteBlock2(sprnum, x, y, 14, 0xFFFE, -2);
353                 }
354         }
355 }
356
357
358 int FindPCXName(void)
359 {
360         static int pcxNum = -1; // This needs to go elsewhere... (or does it?)
361         char filename[30];
362         FILE * fr;
363
364         pcxNum++;
365
366         while (pcxNum < 10000)
367         {
368                 sprintf(filename, "thnd%04i.pcx", pcxNum);
369
370                 // file does not exist - we can create it
371                 if ((fr = fopen(filename, "r")) == NULL)
372                         return pcxNum;
373
374                 pcxNum++;
375         }
376
377         return -1;
378 }
379
380
381 void SavePCXSnapshot(void)
382 {
383         char filename[30];
384         int xMax = 287;
385         int yMax = 223;
386         int bytesPerLine = 288;
387         int i = FindPCXName();
388
389         if (i < 0)
390                 return;
391
392         sprintf(filename, "thnd%04i.pcx", i);
393         FILE * fw = fopen(filename, "wb");
394
395         if (fw == NULL)
396                 return;
397
398         // Write the header
399
400         fputc(0x0A, fw);        // PCX signature
401         fputc(0x05, fw);        // Version 5
402         fputc(0x01, fw);        // RLE encoding
403         fputc(0x08, fw);        // Bits per pixel
404         fputc(0, fw);
405         fputc(0, fw);
406         fputc(0, fw);
407         fputc(0, fw);           // XMin=0,YMin=0
408         fputc(xMax & 0xFF, fw);
409         fputc(xMax >> 8, fw);
410         fputc(yMax & 0xFF, fw);
411         fputc(yMax >> 8, fw);
412         fputc(0, fw);
413         fputc(0, fw);
414         fputc(0, fw);
415         fputc(0, fw);           // Unknown DPI
416
417         // EGA color palette
418         for(i=0; i<48; i++)
419                 fputc(0, fw);
420
421         fputc(0, fw);           // Reserved
422         fputc(1, fw);           // Number of bit planes
423         fputc(bytesPerLine & 0xFF, fw);
424         fputc(bytesPerLine >> 8, fw);
425         fputc(1, fw);
426         fputc(0, fw);           // Palette info - unused
427         fputc((xMax + 1) & 0xFF, fw);
428         fputc((xMax + 1) >> 8, fw);
429         fputc((yMax + 1) & 0xFF, fw);
430         fputc((yMax + 1) >> 8, fw);     // Screen resolution
431
432         // Unused
433         for (i=0; i<54; i++)
434                 fputc(0, fw);
435
436         uint8_t * mem = my_scr;
437
438         for(int line=0; line<=yMax; line++)
439         {
440                 int xpos = 0;
441
442                 while (xpos < bytesPerLine)
443                 {
444                         uint8_t count = 1;
445                         uint8_t last = *mem;
446                         mem++;
447                         xpos++;
448
449                         while ((*mem == last) && (xpos < bytesPerLine) && (count < 63))
450                         {
451                                 mem++;
452                                 count++;
453                                 xpos++;
454                         }
455
456                         if ((count > 1) || ((last & 0xC0) == 0xC0))
457                         {
458                                 fputc(0xC0 | (count & 0x3F), fw);
459                                 fputc(last & 0xFF, fw);
460                         }
461                         else
462                                 fputc(last & 0xFF, fw);
463                 }
464         }
465
466         // Write out the palette
467         fputc(0x0C, fw);
468
469         for(int i=0; i<256; i++)
470         {
471                 fputc(palette[i] & 0xFF, fw);
472                 fputc((palette[i] >> 8) & 0xFF, fw);
473                 fputc((palette[i] >> 16) & 0xFF, fw);
474         }
475
476         // Success!
477         fclose(fw);
478 }
479