Add an option to downscale hi-res views
[pcsx_rearmed.git] / plugins / gpu_unai / gpulib_if.cpp
1 /***************************************************************************
2 *   Copyright (C) 2010 PCSX4ALL Team                                      *
3 *   Copyright (C) 2010 Unai                                               *
4 *   Copyright (C) 2011 notaz                                              *
5 *   Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com)          *
6 *                                                                         *
7 *   This program is free software; you can redistribute it and/or modify  *
8 *   it under the terms of the GNU General Public License as published by  *
9 *   the Free Software Foundation; either version 2 of the License, or     *
10 *   (at your option) any later version.                                   *
11 *                                                                         *
12 *   This program is distributed in the hope that it will be useful,       *
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15 *   GNU General Public License for more details.                          *
16 *                                                                         *
17 *   You should have received a copy of the GNU General Public License     *
18 *   along with this program; if not, write to the                         *
19 *   Free Software Foundation, Inc.,                                       *
20 *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
21 ***************************************************************************/
22
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "../gpulib/gpu.h"
28 //#include "port.h"
29 #include "gpu_unai.h"
30
31 // GPU fixed point math
32 #include "gpu_fixedpoint.h"
33
34 // Inner loop driver instantiation file
35 #include "gpu_inner.h"
36
37 // GPU internal image drawing functions
38 #include "gpu_raster_image.h"
39
40 // GPU internal line drawing functions
41 #include "gpu_raster_line.h"
42
43 // GPU internal polygon drawing functions
44 #include "gpu_raster_polygon.h"
45
46 // GPU internal sprite drawing functions
47 #include "gpu_raster_sprite.h"
48
49 // GPU command buffer execution/store
50 #include "gpu_command.h"
51
52 /////////////////////////////////////////////////////////////////////////////
53
54 #define DOWNSCALE_VRAM_SIZE (1024 * 512 * 2 * 2 + 4096)
55
56 INLINE void scale_640_to_320(uint16_t *dest, const uint16_t *src, bool isRGB24) {
57   size_t uCount = 320;
58
59   if(isRGB24) {
60     const uint8_t* src8 = (const uint8_t *)src;
61     uint8_t* dst8 = (uint8_t *)dest;
62
63     do {
64       *dst8++ = *src8++;
65       *dst8++ = *src8++;
66       *dst8++ = *src8;
67       src8 += 4;
68     } while(--uCount);
69   } else {
70     const uint16_t* src16 = src;
71     uint16_t* dst16 = dest;
72
73     do {
74       *dst16++ = *src16;
75       src16 += 2;
76     } while(--uCount);
77   }
78 }
79
80 INLINE void scale_512_to_320(uint16_t *dest, const uint16_t *src, bool isRGB24) {
81   size_t uCount = 64;
82
83   if(isRGB24) {
84     const uint8_t* src8 = (const uint8_t *)src;
85     uint8_t* dst8 = (uint8_t *)dest;
86
87     do {
88       *dst8++ = *src8++;
89       *dst8++ = *src8++;
90       *dst8++ = *src8++;
91       *dst8++ = *src8++;
92       *dst8++ = *src8++;
93       *dst8++ = *src8;
94       src8 += 4;
95       *dst8++ = *src8++;
96       *dst8++ = *src8++;
97       *dst8++ = *src8++;
98       *dst8++ = *src8++;
99       *dst8++ = *src8++;
100       *dst8++ = *src8;
101       src8 += 4;
102       *dst8++ = *src8++;
103       *dst8++ = *src8++;
104       *dst8++ = *src8;
105       src8 += 4;
106     } while(--uCount);
107   } else {
108     const uint16_t* src16 = src;
109     uint16_t* dst16 = dest;
110
111     do {
112       *dst16++ = *src16++;
113       *dst16++ = *src16;
114       src16 += 2;
115       *dst16++ = *src16++;
116       *dst16++ = *src16;
117       src16 += 2;
118       *dst16++ = *src16;
119       src16 += 2;
120     } while(--uCount);
121   }
122 }
123
124 static uint16_t *get_downscale_buffer(int *x, int *y, int *w, int *h, int *vram_h)
125 {
126   uint16_t *dest = gpu_unai.downscale_vram;
127   const uint16_t *src = gpu_unai.vram;
128   bool isRGB24 = (gpu_unai.GPU_GP1 & 0x00200000 ? true : false);
129   int stride = 1024, dstride = 1024, lines = *h, orig_w = *w;
130
131   // PS1 fb read wraps around (fixes black screen in 'Tobal no. 1')
132   unsigned int fb_mask = 1024 * 512 - 1;
133
134   if (*h > 240) {
135     *h /= 2;
136     stride *= 2;
137     lines = *h;
138
139     // Ensure start at a non-skipped line
140     while (*y & gpu_unai.ilace_mask) ++*y;
141   }
142
143   unsigned int fb_offset_src = (*y * dstride + *x) & fb_mask;
144   unsigned int fb_offset_dest = fb_offset_src;
145
146   if (*w == 512 || *w == 640) {
147     *w = 320;
148   }
149
150   switch(orig_w) {
151   case 640:
152     do {
153       scale_640_to_320(dest + fb_offset_dest, src + fb_offset_src, isRGB24);
154       fb_offset_src = (fb_offset_src + stride) & fb_mask;
155       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
156     } while(--lines);
157
158     break;
159   case 512:
160     do {
161       scale_512_to_320(dest + fb_offset_dest, src + fb_offset_src, isRGB24);
162       fb_offset_src = (fb_offset_src + stride) & fb_mask;
163       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
164     } while(--lines);
165     break;
166   default:
167     size_t size = isRGB24 ? *w * 3 : *w * 2;
168
169     do {
170       memcpy(dest + fb_offset_dest, src + fb_offset_src, size);
171       fb_offset_src = (fb_offset_src + stride) & fb_mask;
172       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
173     } while(--lines);
174     break;
175   }
176
177   return gpu_unai.downscale_vram;
178 }
179
180 static void map_downscale_buffer(void)
181 {
182   if (gpu_unai.downscale_vram)
183     return;
184
185   gpu_unai.downscale_vram = (uint16_t*)gpu.mmap(DOWNSCALE_VRAM_SIZE);
186
187   if (gpu_unai.downscale_vram == NULL) {
188     fprintf(stderr, "failed to map downscale buffer\n");
189     gpu.get_downscale_buffer = NULL;
190   }
191   else {
192     gpu.get_downscale_buffer = get_downscale_buffer;
193   }
194 }
195
196 static void unmap_downscale_buffer(void)
197 {
198   if (gpu_unai.downscale_vram == NULL)
199     return;
200
201   gpu.munmap(gpu_unai.downscale_vram, DOWNSCALE_VRAM_SIZE);
202   gpu_unai.downscale_vram = NULL;
203   gpu.get_downscale_buffer = NULL;
204 }
205
206 int renderer_init(void)
207 {
208   memset((void*)&gpu_unai, 0, sizeof(gpu_unai));
209   gpu_unai.vram = (u16*)gpu.vram;
210
211   // Original standalone gpu_unai initialized TextureWindow[]. I added the
212   //  same behavior here, since it seems unsafe to leave [2],[3] unset when
213   //  using HLE and Rearmed gpu_neon sets this similarly on init. -senquack
214   gpu_unai.TextureWindow[0] = 0;
215   gpu_unai.TextureWindow[1] = 0;
216   gpu_unai.TextureWindow[2] = 255;
217   gpu_unai.TextureWindow[3] = 255;
218   //senquack - new vars must be updated whenever texture window is changed:
219   //           (used for polygon-drawing in gpu_inner.h, gpu_raster_polygon.h)
220   const u32 fb = FIXED_BITS;  // # of fractional fixed-pt bits of u4/v4
221   gpu_unai.u_msk = (((u32)gpu_unai.TextureWindow[2]) << fb) | ((1 << fb) - 1);
222   gpu_unai.v_msk = (((u32)gpu_unai.TextureWindow[3]) << fb) | ((1 << fb) - 1);
223
224   // Configuration options
225   gpu_unai.config = gpu_unai_config_ext;
226   //senquack - disabled, not sure this is needed and would require modifying
227   // sprite-span functions, perhaps unnecessarily. No Abe Oddysey hack was
228   // present in latest PCSX4ALL sources we were using.
229   //gpu_unai.config.enableAbbeyHack = gpu_unai_config_ext.abe_hack;
230   gpu_unai.ilace_mask = gpu_unai.config.ilace_force;
231
232 #ifdef GPU_UNAI_USE_INT_DIV_MULTINV
233   // s_invTable
234   for(int i=1;i<=(1<<TABLE_BITS);++i)
235   {
236     double v = 1.0 / double(i);
237 #ifdef GPU_TABLE_10_BITS
238     v *= double(0xffffffff>>1);
239 #else
240     v *= double(0x80000000);
241 #endif
242     s_invTable[i-1]=s32(v);
243   }
244 #endif
245
246   SetupLightLUT();
247   SetupDitheringConstants();
248
249   if (gpu_unai.config.scale_hires) {
250     map_downscale_buffer();
251   }
252
253   return 0;
254 }
255
256 void renderer_finish(void)
257 {
258   unmap_downscale_buffer();
259 }
260
261 void renderer_notify_res_change(void)
262 {
263   if (PixelSkipEnabled()) {
264     // Set blit_mask for high horizontal resolutions. This allows skipping
265     //  rendering pixels that would never get displayed on low-resolution
266     //  platforms that use simple pixel-dropping scaler.
267
268     switch (gpu.screen.hres)
269     {
270       case 512: gpu_unai.blit_mask = 0xa4; break; // GPU_BlitWWSWWSWS
271       case 640: gpu_unai.blit_mask = 0xaa; break; // GPU_BlitWS
272       default:  gpu_unai.blit_mask = 0;    break;
273     }
274   } else {
275     gpu_unai.blit_mask = 0;
276   }
277
278   if (LineSkipEnabled()) {
279     // Set rendering line-skip (only render every other line in high-res
280     //  480 vertical mode, or, optionally, force it for all video modes)
281
282     if (gpu.screen.vres == 480) {
283       if (gpu_unai.config.ilace_force) {
284         gpu_unai.ilace_mask = 3; // Only need 1/4 of lines
285       } else {
286         gpu_unai.ilace_mask = 1; // Only need 1/2 of lines
287       }
288     } else {
289       // Vert resolution changed from 480 to lower one
290       gpu_unai.ilace_mask = gpu_unai.config.ilace_force;
291     }
292   } else {
293     gpu_unai.ilace_mask = 0;
294   }
295
296   /*
297   printf("res change hres: %d   vres: %d   depth: %d   ilace_mask: %d\n",
298       gpu.screen.hres, gpu.screen.vres, gpu.status.rgb24 ? 24 : 15,
299       gpu_unai.ilace_mask);
300   */
301 }
302
303 #ifdef USE_GPULIB
304 // Handles GP0 draw settings commands 0xE1...0xE6
305 static void gpuGP0Cmd_0xEx(gpu_unai_t &gpu_unai, u32 cmd_word)
306 {
307   // Assume incoming GP0 command is 0xE1..0xE6, convert to 1..6
308   u8 num = (cmd_word >> 24) & 7;
309   gpu.ex_regs[num] = cmd_word; // Update gpulib register
310   switch (num) {
311     case 1: {
312       // GP0(E1h) - Draw Mode setting (aka "Texpage")
313       u32 cur_texpage = gpu_unai.GPU_GP1 & 0x7FF;
314       u32 new_texpage = cmd_word & 0x7FF;
315       if (cur_texpage != new_texpage) {
316         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x7FF) | new_texpage;
317         gpuSetTexture(gpu_unai.GPU_GP1);
318       }
319     } break;
320
321     case 2: {
322       // GP0(E2h) - Texture Window setting
323       if (cmd_word != gpu_unai.TextureWindowCur) {
324         static const u8 TextureMask[32] = {
325           255, 7, 15, 7, 31, 7, 15, 7, 63, 7, 15, 7, 31, 7, 15, 7,
326           127, 7, 15, 7, 31, 7, 15, 7, 63, 7, 15, 7, 31, 7, 15, 7
327         };
328         gpu_unai.TextureWindowCur = cmd_word;
329         gpu_unai.TextureWindow[0] = ((cmd_word >> 10) & 0x1F) << 3;
330         gpu_unai.TextureWindow[1] = ((cmd_word >> 15) & 0x1F) << 3;
331         gpu_unai.TextureWindow[2] = TextureMask[(cmd_word >> 0) & 0x1F];
332         gpu_unai.TextureWindow[3] = TextureMask[(cmd_word >> 5) & 0x1F];
333         gpu_unai.TextureWindow[0] &= ~gpu_unai.TextureWindow[2];
334         gpu_unai.TextureWindow[1] &= ~gpu_unai.TextureWindow[3];
335
336         // Inner loop vars must be updated whenever texture window is changed:
337         const u32 fb = FIXED_BITS;  // # of fractional fixed-pt bits of u4/v4
338         gpu_unai.u_msk = (((u32)gpu_unai.TextureWindow[2]) << fb) | ((1 << fb) - 1);
339         gpu_unai.v_msk = (((u32)gpu_unai.TextureWindow[3]) << fb) | ((1 << fb) - 1);
340
341         gpuSetTexture(gpu_unai.GPU_GP1);
342       }
343     } break;
344
345     case 3: {
346       // GP0(E3h) - Set Drawing Area top left (X1,Y1)
347       gpu_unai.DrawingArea[0] = cmd_word         & 0x3FF;
348       gpu_unai.DrawingArea[1] = (cmd_word >> 10) & 0x3FF;
349     } break;
350
351     case 4: {
352       // GP0(E4h) - Set Drawing Area bottom right (X2,Y2)
353       gpu_unai.DrawingArea[2] = (cmd_word         & 0x3FF) + 1;
354       gpu_unai.DrawingArea[3] = ((cmd_word >> 10) & 0x3FF) + 1;
355     } break;
356
357     case 5: {
358       // GP0(E5h) - Set Drawing Offset (X,Y)
359       gpu_unai.DrawingOffset[0] = ((s32)cmd_word<<(32-11))>>(32-11);
360       gpu_unai.DrawingOffset[1] = ((s32)cmd_word<<(32-22))>>(32-11);
361     } break;
362
363     case 6: {
364       // GP0(E6h) - Mask Bit Setting
365       gpu_unai.Masking  = (cmd_word & 0x2) <<  1;
366       gpu_unai.PixelMSB = (cmd_word & 0x1) <<  8;
367     } break;
368   }
369 }
370 #endif
371
372 extern const unsigned char cmd_lengths[256];
373
374 int do_cmd_list(u32 *list, int list_len, int *last_cmd)
375 {
376   u32 cmd = 0, len, i;
377   u32 *list_start = list;
378   u32 *list_end = list + list_len;
379
380   //TODO: set ilace_mask when resolution changes instead of every time,
381   // eliminate #ifdef below.
382   gpu_unai.ilace_mask = gpu_unai.config.ilace_force;
383
384 #ifdef HAVE_PRE_ARMV7 /* XXX */
385   gpu_unai.ilace_mask |= gpu.status.interlace;
386 #endif
387   if (gpu_unai.config.scale_hires) {
388     gpu_unai.ilace_mask |= gpu.status.interlace;
389   }
390
391   for (; list < list_end; list += 1 + len)
392   {
393     cmd = *list >> 24;
394     len = cmd_lengths[cmd];
395     if (list + 1 + len > list_end) {
396       cmd = -1;
397       break;
398     }
399
400     #define PRIM cmd
401     gpu_unai.PacketBuffer.U4[0] = list[0];
402     for (i = 1; i <= len; i++)
403       gpu_unai.PacketBuffer.U4[i] = list[i];
404
405     PtrUnion packet = { .ptr = (void*)&gpu_unai.PacketBuffer };
406
407     switch (cmd)
408     {
409       case 0x02:
410         gpuClearImage(packet);
411         break;
412
413       case 0x20:
414       case 0x21:
415       case 0x22:
416       case 0x23: {          // Monochrome 3-pt poly
417         PP driver = gpuPolySpanDrivers[
418           (gpu_unai.blit_mask?1024:0) |
419           Blending_Mode |
420           gpu_unai.Masking | Blending | gpu_unai.PixelMSB
421         ];
422         gpuDrawPolyF(packet, driver, false);
423       } break;
424
425       case 0x24:
426       case 0x25:
427       case 0x26:
428       case 0x27: {          // Textured 3-pt poly
429         gpuSetCLUT   (gpu_unai.PacketBuffer.U4[2] >> 16);
430         gpuSetTexture(gpu_unai.PacketBuffer.U4[4] >> 16);
431
432         u32 driver_idx =
433           (gpu_unai.blit_mask?1024:0) |
434           Dithering |
435           Blending_Mode | gpu_unai.TEXT_MODE |
436           gpu_unai.Masking | Blending | gpu_unai.PixelMSB;
437
438         if (!FastLightingEnabled()) {
439           driver_idx |= Lighting;
440         } else {
441           if (!((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F)))
442             driver_idx |= Lighting;
443         }
444
445         PP driver = gpuPolySpanDrivers[driver_idx];
446         gpuDrawPolyFT(packet, driver, false);
447       } break;
448
449       case 0x28:
450       case 0x29:
451       case 0x2A:
452       case 0x2B: {          // Monochrome 4-pt poly
453         PP driver = gpuPolySpanDrivers[
454           (gpu_unai.blit_mask?1024:0) |
455           Blending_Mode |
456           gpu_unai.Masking | Blending | gpu_unai.PixelMSB
457         ];
458         gpuDrawPolyF(packet, driver, true); // is_quad = true
459       } break;
460
461       case 0x2C:
462       case 0x2D:
463       case 0x2E:
464       case 0x2F: {          // Textured 4-pt poly
465         gpuSetCLUT   (gpu_unai.PacketBuffer.U4[2] >> 16);
466         gpuSetTexture(gpu_unai.PacketBuffer.U4[4] >> 16);
467
468         u32 driver_idx =
469           (gpu_unai.blit_mask?1024:0) |
470           Dithering |
471           Blending_Mode | gpu_unai.TEXT_MODE |
472           gpu_unai.Masking | Blending | gpu_unai.PixelMSB;
473
474         if (!FastLightingEnabled()) {
475           driver_idx |= Lighting;
476         } else {
477           if (!((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F)))
478             driver_idx |= Lighting;
479         }
480
481         PP driver = gpuPolySpanDrivers[driver_idx];
482         gpuDrawPolyFT(packet, driver, true); // is_quad = true
483       } break;
484
485       case 0x30:
486       case 0x31:
487       case 0x32:
488       case 0x33: {          // Gouraud-shaded 3-pt poly
489         //NOTE: The '129' here is CF_GOURAUD | CF_LIGHT, however
490         // this is an untextured poly, so CF_LIGHT (texture blend)
491         // shouldn't apply. Until the original array of template
492         // instantiation ptrs is fixed, we're stuck with this. (TODO)
493         PP driver = gpuPolySpanDrivers[
494           (gpu_unai.blit_mask?1024:0) |
495           Dithering |
496           Blending_Mode |
497           gpu_unai.Masking | Blending | 129 | gpu_unai.PixelMSB
498         ];
499         gpuDrawPolyG(packet, driver, false);
500       } break;
501
502       case 0x34:
503       case 0x35:
504       case 0x36:
505       case 0x37: {          // Gouraud-shaded, textured 3-pt poly
506         gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
507         gpuSetTexture (gpu_unai.PacketBuffer.U4[5] >> 16);
508         PP driver = gpuPolySpanDrivers[
509           (gpu_unai.blit_mask?1024:0) |
510           Dithering |
511           Blending_Mode | gpu_unai.TEXT_MODE |
512           gpu_unai.Masking | Blending | ((Lighting)?129:0) | gpu_unai.PixelMSB
513         ];
514         gpuDrawPolyGT(packet, driver, false);
515       } break;
516
517       case 0x38:
518       case 0x39:
519       case 0x3A:
520       case 0x3B: {          // Gouraud-shaded 4-pt poly
521         // See notes regarding '129' for 0x30..0x33 further above -senquack
522         PP driver = gpuPolySpanDrivers[
523           (gpu_unai.blit_mask?1024:0) |
524           Dithering |
525           Blending_Mode |
526           gpu_unai.Masking | Blending | 129 | gpu_unai.PixelMSB
527         ];
528         gpuDrawPolyG(packet, driver, true); // is_quad = true
529       } break;
530
531       case 0x3C:
532       case 0x3D:
533       case 0x3E:
534       case 0x3F: {          // Gouraud-shaded, textured 4-pt poly
535         gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
536         gpuSetTexture (gpu_unai.PacketBuffer.U4[5] >> 16);
537         PP driver = gpuPolySpanDrivers[
538           (gpu_unai.blit_mask?1024:0) |
539           Dithering |
540           Blending_Mode | gpu_unai.TEXT_MODE |
541           gpu_unai.Masking | Blending | ((Lighting)?129:0) | gpu_unai.PixelMSB
542         ];
543         gpuDrawPolyGT(packet, driver, true); // is_quad = true
544       } break;
545
546       case 0x40:
547       case 0x41:
548       case 0x42:
549       case 0x43: {          // Monochrome line
550         // Shift index right by one, as untextured prims don't use lighting
551         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
552         PSD driver = gpuPixelSpanDrivers[driver_idx];
553         gpuDrawLineF(packet, driver);
554       } break;
555
556       case 0x48 ... 0x4F: { // Monochrome line strip
557         u32 num_vertexes = 1;
558         u32 *list_position = &(list[2]);
559
560         // Shift index right by one, as untextured prims don't use lighting
561         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
562         PSD driver = gpuPixelSpanDrivers[driver_idx];
563         gpuDrawLineF(packet, driver);
564
565         while(1)
566         {
567           gpu_unai.PacketBuffer.U4[1] = gpu_unai.PacketBuffer.U4[2];
568           gpu_unai.PacketBuffer.U4[2] = *list_position++;
569           gpuDrawLineF(packet, driver);
570
571           num_vertexes++;
572           if(list_position >= list_end) {
573             cmd = -1;
574             goto breakloop;
575           }
576           if((*list_position & 0xf000f000) == 0x50005000)
577             break;
578         }
579
580         len += (num_vertexes - 2);
581       } break;
582
583       case 0x50:
584       case 0x51:
585       case 0x52:
586       case 0x53: {          // Gouraud-shaded line
587         // Shift index right by one, as untextured prims don't use lighting
588         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
589         // Index MSB selects Gouraud-shaded PixelSpanDriver:
590         driver_idx |= (1 << 5);
591         PSD driver = gpuPixelSpanDrivers[driver_idx];
592         gpuDrawLineG(packet, driver);
593       } break;
594
595       case 0x58 ... 0x5F: { // Gouraud-shaded line strip
596         u32 num_vertexes = 1;
597         u32 *list_position = &(list[2]);
598
599         // Shift index right by one, as untextured prims don't use lighting
600         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
601         // Index MSB selects Gouraud-shaded PixelSpanDriver:
602         driver_idx |= (1 << 5);
603         PSD driver = gpuPixelSpanDrivers[driver_idx];
604         gpuDrawLineG(packet, driver);
605
606         while(1)
607         {
608           gpu_unai.PacketBuffer.U4[0] = gpu_unai.PacketBuffer.U4[2];
609           gpu_unai.PacketBuffer.U4[1] = gpu_unai.PacketBuffer.U4[3];
610           gpu_unai.PacketBuffer.U4[2] = *list_position++;
611           gpu_unai.PacketBuffer.U4[3] = *list_position++;
612           gpuDrawLineG(packet, driver);
613
614           num_vertexes++;
615           if(list_position >= list_end) {
616             cmd = -1;
617             goto breakloop;
618           }
619           if((*list_position & 0xf000f000) == 0x50005000)
620             break;
621         }
622
623         len += (num_vertexes - 2) * 2;
624       } break;
625
626       case 0x60:
627       case 0x61:
628       case 0x62:
629       case 0x63: {          // Monochrome rectangle (variable size)
630         PT driver = gpuTileSpanDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
631         gpuDrawT(packet, driver);
632       } break;
633
634       case 0x64:
635       case 0x65:
636       case 0x66:
637       case 0x67: {          // Textured rectangle (variable size)
638         gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
639         u32 driver_idx = Blending_Mode | gpu_unai.TEXT_MODE | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>1);
640
641         //senquack - Only color 808080h-878787h allows skipping lighting calculation:
642         // This fixes Silent Hill running animation on loading screens:
643         // (On PSX, color values 0x00-0x7F darken the source texture's color,
644         //  0x81-FF lighten textures (ultimately clamped to 0x1F),
645         //  0x80 leaves source texture color unchanged, HOWEVER,
646         //   gpu_unai uses a simple lighting LUT whereby only the upper
647         //   5 bits of an 8-bit color are used, so 0x80-0x87 all behave as
648         //   0x80.
649         // 
650         // NOTE: I've changed all textured sprite draw commands here and
651         //  elsewhere to use proper behavior, but left poly commands
652         //  alone, I don't want to slow rendering down too much. (TODO)
653         //if ((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F))
654         // Strip lower 3 bits of each color and determine if lighting should be used:
655         if ((gpu_unai.PacketBuffer.U4[0] & 0xF8F8F8) != 0x808080)
656           driver_idx |= Lighting;
657         PS driver = gpuSpriteSpanDrivers[driver_idx];
658         gpuDrawS(packet, driver);
659       } break;
660
661       case 0x68:
662       case 0x69:
663       case 0x6A:
664       case 0x6B: {          // Monochrome rectangle (1x1 dot)
665         gpu_unai.PacketBuffer.U4[2] = 0x00010001;
666         PT driver = gpuTileSpanDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
667         gpuDrawT(packet, driver);
668       } break;
669
670       case 0x70:
671       case 0x71:
672       case 0x72:
673       case 0x73: {          // Monochrome rectangle (8x8)
674         gpu_unai.PacketBuffer.U4[2] = 0x00080008;
675         PT driver = gpuTileSpanDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
676         gpuDrawT(packet, driver);
677       } break;
678
679       case 0x74:
680       case 0x75:
681       case 0x76:
682       case 0x77: {          // Textured rectangle (8x8)
683         gpu_unai.PacketBuffer.U4[3] = 0x00080008;
684         gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
685         u32 driver_idx = Blending_Mode | gpu_unai.TEXT_MODE | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>1);
686
687         //senquack - Only color 808080h-878787h allows skipping lighting calculation:
688         //if ((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F))
689         // Strip lower 3 bits of each color and determine if lighting should be used:
690         if ((gpu_unai.PacketBuffer.U4[0] & 0xF8F8F8) != 0x808080)
691           driver_idx |= Lighting;
692         PS driver = gpuSpriteSpanDrivers[driver_idx];
693         gpuDrawS(packet, driver);
694       } break;
695
696       case 0x78:
697       case 0x79:
698       case 0x7A:
699       case 0x7B: {          // Monochrome rectangle (16x16)
700         gpu_unai.PacketBuffer.U4[2] = 0x00100010;
701         PT driver = gpuTileSpanDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
702         gpuDrawT(packet, driver);
703       } break;
704
705       case 0x7C:
706       case 0x7D:
707 #ifdef __arm__
708         if ((gpu_unai.GPU_GP1 & 0x180) == 0 && (gpu_unai.Masking | gpu_unai.PixelMSB) == 0)
709         {
710           gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
711           gpuDrawS16(packet);
712           break;
713         }
714         // fallthrough
715 #endif
716       case 0x7E:
717       case 0x7F: {          // Textured rectangle (16x16)
718         gpu_unai.PacketBuffer.U4[3] = 0x00100010;
719         gpuSetCLUT    (gpu_unai.PacketBuffer.U4[2] >> 16);
720         u32 driver_idx = Blending_Mode | gpu_unai.TEXT_MODE | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>1);
721         //senquack - Only color 808080h-878787h allows skipping lighting calculation:
722         //if ((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F))
723         // Strip lower 3 bits of each color and determine if lighting should be used:
724         if ((gpu_unai.PacketBuffer.U4[0] & 0xF8F8F8) != 0x808080)
725           driver_idx |= Lighting;
726         PS driver = gpuSpriteSpanDrivers[driver_idx];
727         gpuDrawS(packet, driver);
728       } break;
729
730       case 0x80:          //  vid -> vid
731         gpuMoveImage(packet);
732         break;
733
734 #ifdef TEST
735       case 0xA0:          //  sys -> vid
736       {
737         u32 load_width = list[2] & 0xffff;
738         u32 load_height = list[2] >> 16;
739         u32 load_size = load_width * load_height;
740
741         len += load_size / 2;
742       } break;
743
744       case 0xC0:
745         break;
746 #else
747       case 0xA0:          //  sys ->vid
748       case 0xC0:          //  vid -> sys
749         // Handled by gpulib
750         goto breakloop;
751 #endif
752       case 0xE1 ... 0xE6: { // Draw settings
753         gpuGP0Cmd_0xEx(gpu_unai, gpu_unai.PacketBuffer.U4[0]);
754       } break;
755     }
756   }
757
758 breakloop:
759   gpu.ex_regs[1] &= ~0x1ff;
760   gpu.ex_regs[1] |= gpu_unai.GPU_GP1 & 0x1ff;
761
762   *last_cmd = cmd;
763   return list - list_start;
764 }
765
766 void renderer_sync_ecmds(uint32_t *ecmds)
767 {
768   int dummy;
769   do_cmd_list(&ecmds[1], 6, &dummy);
770 }
771
772 void renderer_update_caches(int x, int y, int w, int h)
773 {
774 }
775
776 void renderer_flush_queues(void)
777 {
778 }
779
780 void renderer_set_interlace(int enable, int is_odd)
781 {
782 }
783
784 #include "../../frontend/plugin_lib.h"
785 // Handle any gpulib settings applicable to gpu_unai:
786 void renderer_set_config(const struct rearmed_cbs *cbs)
787 {
788   gpu_unai.vram = (u16*)gpu.vram;
789   gpu_unai.config.ilace_force   = cbs->gpu_unai.ilace_force;
790   gpu_unai.config.pixel_skip    = cbs->gpu_unai.pixel_skip;
791   gpu_unai.config.lighting      = cbs->gpu_unai.lighting;
792   gpu_unai.config.fast_lighting = cbs->gpu_unai.fast_lighting;
793   gpu_unai.config.blending      = cbs->gpu_unai.blending;
794   gpu_unai.config.dithering     = cbs->gpu_unai.dithering;
795   gpu_unai.config.scale_hires   = cbs->gpu_unai.scale_hires;
796
797   gpu.state.downscale_enable    = gpu_unai.config.scale_hires;
798   if (gpu_unai.config.scale_hires) {
799     map_downscale_buffer();
800   } else {
801     unmap_downscale_buffer();
802   }
803 }
804
805 // vim:shiftwidth=2:expandtab