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