1 /*  Copyright 2003-2004 Guillaume Duhamel
2     Copyright 2004-2008 Theo Berkau
3     Copyright 2006 Fabien Coulon
4     Copyright 2015 R. Danbrook
5 
6     This file is part of Yabause.
7 
8     Yabause is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     Yabause is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with Yabause; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
21 */
22 
23 /*! \file vidsoft.c
24     \brief Software video renderer interface.
25 */
26 
27 #include "vidsoft.h"
28 #include "ygl.h"
29 #include "vidshared.h"
30 #include "debug.h"
31 #include "vdp2.h"
32 #include "titan/titan.h"
33 
34 #ifdef HAVE_LIBGL
35 #define USE_OPENGL
36 #endif
37 
38 #ifdef USE_OPENGL
39 #include "ygl.h"
40 #endif
41 
42 #include "yui.h"
43 #include "threads.h"
44 
45 #include <stdlib.h>
46 #include <limits.h>
47 
48 #if defined WORDS_BIGENDIAN
COLSAT2YAB16(int priority,u32 temp)49 static INLINE u32 COLSAT2YAB16(int priority,u32 temp)            { return (priority | (temp & 0x7C00) << 1 | (temp & 0x3E0) << 14 | (temp & 0x1F) << 27); }
COLSAT2YAB32(int priority,u32 temp)50 static INLINE u32 COLSAT2YAB32(int priority,u32 temp)            { return (((temp & 0xFF) << 24) | ((temp & 0xFF00) << 8) | ((temp & 0xFF0000) >> 8) | priority); }
COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2)51 static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2)   { return (((temp2 & 0xFF) << 24) | ((temp2 & 0xFF00) << 8) | ((temp1 & 0xFF) << 8) | priority); }
COLSATSTRIPPRIORITY(u32 pixel)52 static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel)              { return (pixel | 0xFF); }
53 #else
COLSAT2YAB16(int priority,u32 temp)54 static INLINE u32 COLSAT2YAB16(int priority,u32 temp) { return (priority << 24 | (temp & 0x1F) << 3 | (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9); }
COLSAT2YAB32(int priority,u32 temp)55 static INLINE u32 COLSAT2YAB32(int priority, u32 temp) { return (priority << 24 | (temp & 0xFF0000) | (temp & 0xFF00) | (temp & 0xFF)); }
COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2)56 static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2)   { return (priority << 24 | ((temp1 & 0xFF) << 16) | (temp2 & 0xFF00) | (temp2 & 0xFF)); }
COLSATSTRIPPRIORITY(u32 pixel)57 static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel) { return (0xFF000000 | pixel); }
58 #endif
59 
60 #define COLOR_ADDt(b)		(b>0xFF?0xFF:(b<0?0:b))
61 #define COLOR_ADDb(b1,b2)	COLOR_ADDt((signed) (b1) + (b2))
62 #ifdef WORDS_BIGENDIAN
63 #define COLOR_ADD(l,r,g,b)      (l & 0xFF) | \
64                                 (COLOR_ADDb((l >> 8) & 0xFF, b) << 8) | \
65                                 (COLOR_ADDb((l >> 16) & 0xFF, g) << 16) | \
66                                 (COLOR_ADDb((l >> 24), r) << 24)
67 #else
68 #define COLOR_ADD(l,r,g,b)	COLOR_ADDb((l & 0xFF), r) | \
69                                 (COLOR_ADDb((l >> 8) & 0xFF, g) << 8) | \
70                                 (COLOR_ADDb((l >> 16) & 0xFF, b) << 16) | \
71 				(l & 0xFF000000)
72 #endif
73 
74 
75 int VIDSoftInit(void);
76 int VIDSoftSetupGL(void);
77 void VIDSoftDeInit(void);
78 void VIDSoftResize(unsigned int, unsigned int, int);
79 int VIDSoftIsFullscreen(void);
80 int VIDSoftVdp1Reset(void);
81 void VIDSoftVdp1DrawStart(void);
82 void VIDSoftVdp1DrawEnd(void);
83 void VIDSoftVdp1NormalSpriteDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
84 void VIDSoftVdp1ScaledSpriteDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
85 void VIDSoftVdp1DistortedSpriteDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
86 void VIDSoftVdp1PolygonDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
87 void VIDSoftVdp1PolylineDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
88 void VIDSoftVdp1LineDraw(u8 * ram, Vdp1 * regs, u8* back_framebuffer);
89 void VIDSoftVdp1UserClipping(u8 * ram, Vdp1 * regs);
90 void VIDSoftVdp1SystemClipping(u8 * ram, Vdp1 * regs);
91 void VIDSoftVdp1LocalCoordinate(u8 * ram, Vdp1 * regs);
92 void VIDSoftVdp1ReadFrameBuffer(u32 type, u32 addr, void * out);
93 void VIDSoftVdp1WriteFrameBuffer(u32 type, u32 addr, u32 val);
94 int VIDSoftVdp2Reset(void);
95 void VIDSoftVdp2DrawStart(void);
96 void VIDSoftVdp2DrawEnd(void);
97 void VIDSoftVdp2DrawScreens(void);
98 void VIDSoftVdp2SetResolution(u16 TVMD);
99 void VIDSoftGetGlSize(int *width, int *height);
100 void VIDSoftVdp1SwapFrameBuffer(void);
101 void VIDSoftVdp1EraseFrameBuffer(Vdp1* regs, u8 * back_framebuffer);
102 void VidsoftDrawSprite(Vdp2 * vdp2_regs, u8 * sprite_window_mask, u8* vdp1_front_framebuffer, u8 * vdp2_ram, Vdp1* vdp1_regs, Vdp2* vdp2_lines, u8*color_ram);
103 void VIDSoftGetNativeResolution(int *width, int *height, int*interlace);
104 void VIDSoftVdp2DispOff(void);
105 
106 VideoInterface_struct VIDSoft = {
107 VIDCORE_SOFT,
108 "Software Video Interface",
109 VIDSoftInit,
110 VIDSoftDeInit,
111 VIDSoftResize,
112 VIDSoftIsFullscreen,
113 VIDSoftVdp1Reset,
114 VIDSoftVdp1DrawStart,
115 VIDSoftVdp1DrawEnd,
116 VIDSoftVdp1NormalSpriteDraw,
117 VIDSoftVdp1ScaledSpriteDraw,
118 VIDSoftVdp1DistortedSpriteDraw,
119 //for the actual hardware, polygons are essentially identical to distorted sprites
120 //the actual hardware draws using diagonal lines, which is why using half-transparent processing
121 //on distorted sprites and polygons is not recommended since the hardware overdraws to prevent gaps
122 //thus, with half-transparent processing some pixels will be processed more than once, producing moire patterns in the drawn shapes
123 VIDSoftVdp1DistortedSpriteDraw,
124 VIDSoftVdp1PolylineDraw,
125 VIDSoftVdp1LineDraw,
126 VIDSoftVdp1UserClipping,
127 VIDSoftVdp1SystemClipping,
128 VIDSoftVdp1LocalCoordinate,
129 VIDSoftVdp1ReadFrameBuffer,
130 VIDSoftVdp1WriteFrameBuffer,
131 VIDSoftVdp2Reset,
132 VIDSoftVdp2DrawStart,
133 VIDSoftVdp2DrawEnd,
134 VIDSoftVdp2DrawScreens,
135 VIDSoftGetGlSize,
136 VIDSoftGetNativeResolution,
137 VIDSoftVdp2DispOff
138 };
139 
140 pixel_t *dispbuffer=NULL;
141 u8 *vdp1framebuffer[2]= { NULL, NULL };
142 u8 *vdp1frontframebuffer;
143 u8 *vdp1backframebuffer;
144 u8 sprite_window_mask[704 * 512];
145 
146 static int vdp1width;
147 static int vdp1height;
148 static int vdp1interlace;
149 static int vdp1pixelsize;
150 int vdp2width;
151 int rbg0width = 0;
152 int vdp2height;
153 
154 #ifdef USE_OPENGL
155 static int outputwidth;
156 static int outputheight;
157 GLuint vao = 0;
158 GLuint vbo = 0;
159 GLuint vshader = 0;
160 GLuint fshader = 0;
161 GLuint gl_shader_prog = 0;
162 GLuint gl_texture_id = 0;
163 #endif
164 extern int VideoUseGL;
165 int vdp2_x_hires = 0;
166 int vdp2_interlace = 0;
167 static int rbg0height = 0;
168 int bilinear = 0;
169 int vidsoft_num_layer_threads = 0;
170 int bad_cycle_setting[6] = { 0 };
171 
172 struct VidsoftVdp1ThreadContext
173 {
174    volatile int draw_finished;
175    volatile int need_draw;
176    Vdp1 regs;
177    u8 ram[0x80000];
178    u8 back_framebuffer[0x40000];
179 }vidsoft_vdp1_thread_context;
180 
181 int vidsoft_vdp1_thread_enabled = 0;
182 
183 typedef struct { s16 x; s16 y; } vdp1vertex;
184 
185 typedef struct
186 {
187    int pagepixelwh, pagepixelwh_bits, pagepixelwh_mask;
188    int planepixelwidth, planepixelwidth_bits, planepixelwidth_mask;
189    int planepixelheight, planepixelheight_bits, planepixelheight_mask;
190    int screenwidth;
191    int screenheight;
192    int oldcellx, oldcelly, oldcellcheck;
193    int xmask, ymask;
194    u32 planetbl[16];
195 } screeninfo_struct;
196 
197 //////////////////////////////////////////////////////////////////////////////
198 
Vdp2ColorRamGetColor(u32 addr,u8 * vdp2_color_ram)199 static INLINE u32 FASTCALL Vdp2ColorRamGetColor(u32 addr, u8* vdp2_color_ram)
200 {
201    switch(Vdp2Internal.ColorMode)
202    {
203       case 0:
204       {
205          u32 tmp;
206          addr <<= 1;
207          tmp = T2ReadWord(vdp2_color_ram, addr & 0xFFF);
208          /* we preserve MSB for special color calculation mode 3 (see Vdp2 user's manual 3.4 and 12.3) */
209          return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)) | ((tmp & 0x8000) << 16);
210       }
211       case 1:
212       {
213          u32 tmp;
214          addr <<= 1;
215          tmp = T2ReadWord(vdp2_color_ram, addr & 0xFFF);
216          /* we preserve MSB for special color calculation mode 3 (see Vdp2 user's manual 3.4 and 12.3) */
217          return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)) | ((tmp & 0x8000) << 16);
218       }
219       case 2:
220       {
221          addr <<= 2;
222          return T2ReadLong(vdp2_color_ram, addr & 0xFFF);
223       }
224       default: break;
225    }
226 
227    return 0;
228 }
229 
230 //////////////////////////////////////////////////////////////////////////////
231 
Vdp2PatternAddr(vdp2draw_struct * info,Vdp2 * regs,u8 * ram)232 static INLINE void Vdp2PatternAddr(vdp2draw_struct *info, Vdp2* regs, u8* ram)
233 {
234    switch(info->patterndatasize)
235    {
236       case 1:
237       {
238          u16 tmp = T1ReadWord(ram, info->addr);
239 
240          info->addr += 2;
241          info->specialfunction = (info->supplementdata >> 9) & 0x1;
242          info->specialcolorfunction = (info->supplementdata >> 8) & 0x1;
243 
244          switch(info->colornumber)
245          {
246             case 0: // in 16 colors
247                info->paladdr = ((tmp & 0xF000) >> 8) | ((info->supplementdata & 0xE0) << 3);
248                break;
249             default: // not in 16 colors
250                info->paladdr = (tmp & 0x7000) >> 4;
251                break;
252          }
253 
254          switch(info->auxmode)
255          {
256             case 0:
257                info->flipfunction = (tmp & 0xC00) >> 10;
258 
259                switch(info->patternwh)
260                {
261                   case 1:
262                      info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10);
263                      break;
264                   case 2:
265                      info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10);
266                      break;
267                }
268                break;
269             case 1:
270                info->flipfunction = 0;
271 
272                switch(info->patternwh)
273                {
274                   case 1:
275                      info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10);
276                      break;
277                   case 2:
278                      info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10);
279                      break;
280                }
281                break;
282          }
283 
284          break;
285       }
286       case 2: {
287          u16 tmp1 = T1ReadWord(ram, info->addr);
288          u16 tmp2 = T1ReadWord(ram, info->addr+2);
289          info->addr += 4;
290          info->charaddr = tmp2 & 0x7FFF;
291          info->flipfunction = (tmp1 & 0xC000) >> 14;
292          switch(info->colornumber) {
293             case 0:
294                info->paladdr = (tmp1 & 0x7F) << 4;
295                break;
296             default:
297                info->paladdr = ((tmp1 & 0x70) << 4);
298                break;
299          }
300          info->specialfunction = (tmp1 & 0x2000) >> 13;
301          info->specialcolorfunction = (tmp1 & 0x1000) >> 12;
302          break;
303       }
304    }
305 
306    if (!(regs->VRSIZE & 0x8000))
307       info->charaddr &= 0x3FFF;
308 
309    info->charaddr *= 0x20; // selon Runik
310    if (info->specialprimode == 1) {
311       info->priority = (info->priority & 0xE) | (info->specialfunction & 1);
312    }
313 }
314 
315 //////////////////////////////////////////////////////////////////////////////
316 
DoNothing(UNUSED void * info,u32 pixel)317 static INLINE u32 FASTCALL DoNothing(UNUSED void *info, u32 pixel)
318 {
319    return pixel;
320 }
321 
322 //////////////////////////////////////////////////////////////////////////////
323 
DoColorOffset(void * info,u32 pixel)324 static INLINE u32 FASTCALL DoColorOffset(void *info, u32 pixel)
325 {
326     return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor,
327                      ((vdp2draw_struct *)info)->cog,
328                      ((vdp2draw_struct *)info)->cob);
329 }
330 
331 //////////////////////////////////////////////////////////////////////////////
332 
ReadVdp2ColorOffset(Vdp2 * regs,vdp2draw_struct * info,int clofmask,int ccmask)333 static INLINE void ReadVdp2ColorOffset(Vdp2 * regs, vdp2draw_struct *info, int clofmask, int ccmask)
334 {
335    if (regs->CLOFEN & clofmask)
336    {
337       // color offset enable
338       if (regs->CLOFSL & clofmask)
339       {
340          // color offset B
341          info->cor = regs->COBR & 0xFF;
342          if (regs->COBR & 0x100)
343             info->cor |= 0xFFFFFF00;
344 
345          info->cog = regs->COBG & 0xFF;
346          if (regs->COBG & 0x100)
347             info->cog |= 0xFFFFFF00;
348 
349          info->cob = regs->COBB & 0xFF;
350          if (regs->COBB & 0x100)
351             info->cob |= 0xFFFFFF00;
352       }
353       else
354       {
355          // color offset A
356          info->cor = regs->COAR & 0xFF;
357          if (regs->COAR & 0x100)
358             info->cor |= 0xFFFFFF00;
359 
360          info->cog = regs->COAG & 0xFF;
361          if (regs->COAG & 0x100)
362             info->cog |= 0xFFFFFF00;
363 
364          info->cob = regs->COAB & 0xFF;
365          if (regs->COAB & 0x100)
366             info->cob |= 0xFFFFFF00;
367       }
368 
369       info->PostPixelFetchCalc = &DoColorOffset;
370    }
371    else // color offset disable
372       info->PostPixelFetchCalc = &DoNothing;
373 
374 }
375 
376 //////////////////////////////////////////////////////////////////////////////
377 
Vdp2FetchPixel(vdp2draw_struct * info,int x,int y,u32 * color,u32 * dot,u8 * ram,int charaddr,int paladdr,u8 * vdp2_color_ram)378 static INLINE int Vdp2FetchPixel(vdp2draw_struct *info, int x, int y, u32 *color, u32 *dot, u8 * ram, int charaddr, int paladdr, u8* vdp2_color_ram)
379 {
380    switch(info->colornumber)
381    {
382       case 0: // 4 BPP
383          *dot = T1ReadByte(ram, ((charaddr + ((y * info->cellw) + x) / 2) & 0x7FFFF));
384          if (!(x & 0x1)) *dot >>= 4;
385          if (!(*dot & 0xF) && info->transparencyenable) return 0;
386          else
387          {
388             *color = Vdp2ColorRamGetColor(info->coloroffset + (paladdr | (*dot & 0xF)),vdp2_color_ram);
389             return 1;
390          }
391       case 1: // 8 BPP
392          *dot = T1ReadByte(ram, ((charaddr + (y * info->cellw) + x) & 0x7FFFF));
393          if (!(*dot & 0xFF) && info->transparencyenable) return 0;
394          else
395          {
396             *color = Vdp2ColorRamGetColor(info->coloroffset + (paladdr | (*dot & 0xFF)), vdp2_color_ram);
397             return 1;
398          }
399       case 2: // 16 BPP(palette)
400          *dot = T1ReadWord(ram, ((charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF));
401          if ((*dot == 0) && info->transparencyenable) return 0;
402          else
403          {
404             *color = Vdp2ColorRamGetColor(info->coloroffset + *dot, vdp2_color_ram);
405             return 1;
406          }
407       case 3: // 16 BPP(RGB)
408          *dot = T1ReadWord(ram, ((charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF));
409          if (!(*dot & 0x8000) && info->transparencyenable) return 0;
410          else
411          {
412             *color = COLSAT2YAB16(0, *dot);
413             return 1;
414          }
415       case 4: // 32 BPP
416          *dot = T1ReadLong(ram, ((charaddr + ((y * info->cellw) + x) * 4) & 0x7FFFF));
417          if (!(*dot & 0x80000000) && info->transparencyenable) return 0;
418          else
419          {
420             *color = COLSAT2YAB32(0, *dot);
421             return 1;
422          }
423       default:
424          return 0;
425    }
426 }
427 
428 //////////////////////////////////////////////////////////////////////////////
429 
TestWindow(int wctl,int enablemask,int inoutmask,clipping_struct * clip,int x,int y)430 static INLINE int TestWindow(int wctl, int enablemask, int inoutmask, clipping_struct *clip, int x, int y)
431 {
432    if (wctl & enablemask)
433    {
434       if (wctl & inoutmask)
435       {
436          // Draw inside of window
437          if (x < clip->xstart || x > clip->xend ||
438              y < clip->ystart || y > clip->yend)
439             return 0;
440       }
441       else
442       {
443          // Draw outside of window
444          if (x >= clip->xstart && x <= clip->xend &&
445              y >= clip->ystart && y <= clip->yend)
446             return 0;
447 
448 		 //it seems to overflow vertically on hardware
449 		 if(clip->yend > vdp2height && (x >= clip->xstart && x <= clip->xend ))
450 			 return 0;
451       }
452       return 1; // return inactive;
453    }
454    return 3; // return disabled | inactive;
455 }
456 
457 //////////////////////////////////////////////////////////////////////////////
458 
TestSpriteWindow(int wctl,int x,int y)459 int TestSpriteWindow(int wctl, int x, int y)
460 {
461    int mask;
462    int addr = (y*vdp2width) + x;
463 
464    if (addr >= (704 * 512))
465       return 0;
466 
467    mask = sprite_window_mask[addr];
468 
469    if (wctl & 0x20)//sprite window enabled on layer
470    {
471       if (wctl & 0x10)//inside or outside
472       {
473          if (mask == 0)
474             return 0;
475       }
476       else
477       {
478          if (mask)
479             return 0;
480       }
481 
482       return 1;
483    }
484    return 3;
485 }
486 
487 //////////////////////////////////////////////////////////////////////////////
488 
WindowLogic(int wctl,int w0,int w1)489 int WindowLogic(int wctl, int w0, int w1)
490 {
491    if (((wctl & 0x80) == 0x80))
492       /* AND logic, returns 0 only if both the windows are active */
493       return w0 || w1;
494    else
495       /* OR logic, returns 0 if one of the windows is active */
496       return w0 && w1;
497 }
498 
499 //////////////////////////////////////////////////////////////////////////////
500 
TestBothWindow(int wctl,clipping_struct * clip,int x,int y)501 static INLINE int TestBothWindow(int wctl, clipping_struct *clip, int x, int y)
502 {
503     int w0 = TestWindow(wctl, 0x2, 0x1, &clip[0], x, y);
504     int w1 = TestWindow(wctl, 0x8, 0x4, &clip[1], x, y);
505     int spr = TestSpriteWindow(wctl, x,y);
506 
507     //all windows disabled
508     if ((wctl & 0x2a) == 0)
509     {
510        if ((wctl & 0x80) == 0x80)
511           return 0;
512        else
513           return 1;
514     }
515 
516     //if only window 0 is enabled
517     if ((w1 & 2) && (spr & 2)) return w0 & 1;
518     //if only window 1 is enabled
519     if ((w0 & 2) && (spr & 2)) return w1 & 1;
520 
521     //window 0 and 1, sprite disabled
522     if ((spr & 2))
523        return WindowLogic(wctl, w0, w1);
524 
525     //if only sprite window is enabled
526     if ((w1 & 2) && (w0 & 2)) return spr & 1;
527 
528     //window 0 and sprite enabled
529     if ((wctl & 0x2a) == 0x22)
530        return WindowLogic(wctl, w0, spr);
531 
532     //window 1 and sprite enabled
533     if ((wctl & 0x2a) == 0x28)
534        return WindowLogic(wctl, w1, spr);
535 
536     //all three windows enabled
537     if ((wctl & 0x2a) == 0x2a)
538     {
539        if ((wctl & 0x80) == 0x80)
540           return w0 || w1 || spr;//and logic
541        else
542           return w0 && w1 && spr;//or logic
543     }
544 
545     return 1;
546 
547 }
548 
549 //////////////////////////////////////////////////////////////////////////////
550 
GeneratePlaneAddrTable(vdp2draw_struct * info,u32 * planetbl,void FASTCALL (* PlaneAddr)(void *,int,Vdp2 *),Vdp2 * regs)551 static INLINE void GeneratePlaneAddrTable(vdp2draw_struct *info, u32 *planetbl, void FASTCALL (* PlaneAddr)(void *, int, Vdp2* ), Vdp2* regs)
552 {
553    int i;
554 
555    for (i = 0; i < (info->mapwh*info->mapwh); i++)
556    {
557       PlaneAddr(info, i, regs);
558       planetbl[i] = info->addr;
559    }
560 }
561 
562 //////////////////////////////////////////////////////////////////////////////
563 
Vdp2MapCalcXY(vdp2draw_struct * info,int * x,int * y,screeninfo_struct * sinfo,Vdp2 * regs,u8 * ram,int bad_cycle)564 static INLINE void FASTCALL Vdp2MapCalcXY(vdp2draw_struct *info, int *x, int *y,
565                                  screeninfo_struct *sinfo, Vdp2* regs, u8 * ram, int bad_cycle)
566 {
567    int planenum;
568    int flipfunction;
569    const int pagesize_bits=info->pagewh_bits*2;
570    const int cellwh=(2 + info->patternwh);
571 
572    const int check = ((y[0] >> cellwh) << 16) | (x[0] >> cellwh);
573    //if ((x[0] >> cellwh) != sinfo->oldcellx || (y[0] >> cellwh) != sinfo->oldcelly)
574    if(check != sinfo->oldcellcheck)
575    {
576       sinfo->oldcellx = x[0] >> cellwh;
577       sinfo->oldcelly = y[0] >> cellwh;
578 	  sinfo->oldcellcheck = (sinfo->oldcelly << 16) | sinfo->oldcellx;
579 
580       // Calculate which plane we're dealing with
581       planenum = ((y[0] >> sinfo->planepixelheight_bits) * info->mapwh) + (x[0] >> sinfo->planepixelwidth_bits);
582       x[0] = (x[0] & sinfo->planepixelwidth_mask);
583       y[0] = (y[0] & sinfo->planepixelheight_mask);
584 
585       // Fetch and decode pattern name data
586       info->addr = sinfo->planetbl[planenum];
587 
588       // Figure out which page it's on(if plane size is not 1x1)
589       info->addr += ((  ((y[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) << info->planew_bits) +
590                      (   (x[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) +
591                      (((y[0] & sinfo->pagepixelwh_mask) >> cellwh) << info->pagewh_bits) +
592                      ((x[0] & sinfo->pagepixelwh_mask) >> cellwh)) << (info->patterndatasize_bits+1);
593 
594       Vdp2PatternAddr(info, regs, ram); // Heh, this could be optimized
595 
596       //pipeline the tiles so that they shift over by 1
597       info->pipe[0] = info->pipe[1];
598 
599       info->pipe[1].paladdr = info->paladdr;
600       info->pipe[1].charaddr = info->charaddr;
601       info->pipe[1].flipfunction = info->flipfunction;
602    }
603 
604    if (bad_cycle)
605    {
606       flipfunction = info->pipe[0].flipfunction;
607    }
608    else
609    {
610       flipfunction = info->flipfunction;
611    }
612 
613    // Figure out which pixel in the tile we want
614    if (info->patternwh == 1)
615    {
616       x[0] &= 8-1;
617       y[0] &= 8-1;
618 
619 	  switch(flipfunction & 0x3)
620 	  {
621 	  case 0: //none
622 		  break;
623 	  case 1: //horizontal flip
624 		  x[0] = 8 - 1 - x[0];
625 		  break;
626 	  case 2: // vertical flip
627          y[0] = 8 - 1 - y[0];
628 		 break;
629 	  case 3: //flip both
630          x[0] = 8 - 1 - x[0];
631 		 y[0] = 8 - 1 - y[0];
632 		 break;
633 	  }
634    }
635    else
636    {
637       if (flipfunction)
638       {
639          y[0] &= 16 - 1;
640          if (flipfunction & 0x2)
641          {
642             if (!(y[0] & 8))
643                y[0] = 8 - 1 - y[0] + 16;
644             else
645                y[0] = 16 - 1 - y[0];
646          }
647          else if (y[0] & 8)
648             y[0] += 8;
649 
650          if (flipfunction & 0x1)
651          {
652             if (!(x[0] & 8))
653                y[0] += 8;
654 
655             x[0] &= 8-1;
656             x[0] = 8 - 1 - x[0];
657          }
658          else if (x[0] & 8)
659          {
660             y[0] += 8;
661             x[0] &= 8-1;
662          }
663          else
664             x[0] &= 8-1;
665       }
666       else
667       {
668          y[0] &= 16 - 1;
669 
670          if (y[0] & 8)
671             y[0] += 8;
672          if (x[0] & 8)
673             y[0] += 8;
674          x[0] &= 8-1;
675       }
676    }
677 }
678 
679 //////////////////////////////////////////////////////////////////////////////
680 
SetupScreenVars(vdp2draw_struct * info,screeninfo_struct * sinfo,void FASTCALL (* PlaneAddr)(void *,int,Vdp2 *),Vdp2 * regs)681 static INLINE void SetupScreenVars(vdp2draw_struct *info, screeninfo_struct *sinfo, void FASTCALL (* PlaneAddr)(void *, int, Vdp2*), Vdp2* regs)
682 {
683    if (!info->isbitmap)
684    {
685       sinfo->pagepixelwh=64*8;
686 	  sinfo->pagepixelwh_bits = 9;
687 	  sinfo->pagepixelwh_mask = 511;
688 
689       sinfo->planepixelwidth=info->planew*sinfo->pagepixelwh;
690 	  sinfo->planepixelwidth_bits = 8+info->planew;
691 	  sinfo->planepixelwidth_mask = (1<<(sinfo->planepixelwidth_bits))-1;
692 
693       sinfo->planepixelheight=info->planeh*sinfo->pagepixelwh;
694 	  sinfo->planepixelheight_bits = 8+info->planeh;
695 	  sinfo->planepixelheight_mask = (1<<(sinfo->planepixelheight_bits))-1;
696 
697       sinfo->screenwidth=info->mapwh*sinfo->planepixelwidth;
698       sinfo->screenheight=info->mapwh*sinfo->planepixelheight;
699       sinfo->oldcellx=-1;
700       sinfo->oldcelly=-1;
701       sinfo->oldcellcheck=-1;
702       sinfo->xmask = sinfo->screenwidth-1;
703       sinfo->ymask = sinfo->screenheight-1;
704       GeneratePlaneAddrTable(info, sinfo->planetbl, PlaneAddr, regs);
705    }
706    else
707    {
708       sinfo->pagepixelwh = 0;
709 	  sinfo->pagepixelwh_bits = 0;
710 	  sinfo->pagepixelwh_mask = 0;
711       sinfo->planepixelwidth=0;
712 	  sinfo->planepixelwidth_bits=0;
713 	  sinfo->planepixelwidth_mask=0;
714       sinfo->planepixelheight=0;
715 	  sinfo->planepixelheight_bits=0;
716 	  sinfo->planepixelheight_mask=0;
717       sinfo->screenwidth=0;
718       sinfo->screenheight=0;
719       sinfo->oldcellx=0;
720       sinfo->oldcelly=0;
721       sinfo->oldcellcheck=0;
722       sinfo->xmask = info->cellw-1;
723       sinfo->ymask = info->cellh-1;
724    }
725 }
726 
727 //////////////////////////////////////////////////////////////////////////////
728 
GetAlpha(vdp2draw_struct * info,u32 color,u32 dot)729 static u8 FASTCALL GetAlpha(vdp2draw_struct * info, u32 color, u32 dot)
730 {
731    if (((info->specialcolormode == 1) || (info->specialcolormode == 2)) && ((info->specialcolorfunction & 1) == 0)) {
732       /* special color calculation mode 1 and 2 enables color calculation only when special color function = 1 */
733       return 0x3F;
734    } else if (info->specialcolormode == 2) {
735       /* special color calculation 2 enables color calculation according to lower bits of the color code */
736       if ((info->specialcode & (1 << ((dot & 0xF) >> 1))) == 0) {
737          return 0x3F;
738       }
739    } else if ((info->specialcolormode == 3) && ((color & 0x80000000) == 0)) {
740       /* special color calculation mode 3 enables color calculation only for dots with MSB = 1 */
741       return 0x3F;
742    }
743    return info->alpha;
744 }
745 
746 //////////////////////////////////////////////////////////////////////////////
747 
PixelIsSpecialPriority(int specialcode,int dot)748 int PixelIsSpecialPriority(int specialcode, int dot)
749 {
750    dot &= 0xf;
751 
752    if (specialcode & 0x01)
753    {
754       if (dot == 0 || dot == 1)
755          return 1;
756    }
757    if (specialcode & 0x02)
758    {
759       if (dot == 2 || dot == 3)
760          return 1;
761    }
762    if (specialcode & 0x04)
763    {
764       if (dot == 4 || dot == 5)
765          return 1;
766    }
767    if (specialcode & 0x08)
768    {
769       if (dot == 6 || dot == 7)
770          return 1;
771    }
772    if (specialcode & 0x10)
773    {
774       if (dot == 8 || dot == 9)
775          return 1;
776    }
777    if (specialcode & 0x20)
778    {
779       if (dot == 0xa || dot == 0xb)
780          return 1;
781    }
782    if (specialcode & 0x40)
783    {
784       if (dot == 0xc || dot == 0xd)
785          return 1;
786    }
787    if (specialcode & 0x80)
788    {
789       if (dot == 0xe || dot == 0xf)
790          return 1;
791    }
792 
793    return 0;
794 }
795 
796 //////////////////////////////////////////////////////////////////////////////
797 
Vdp2GetInterlaceInfo(int * start_line,int * line_increment)798 void Vdp2GetInterlaceInfo(int * start_line, int * line_increment)
799 {
800    if (vdp2_interlace)
801    {
802       if (vdp2_is_odd_frame)
803       {
804          *start_line = 1;
805       }
806       else
807       {
808          *start_line = 0;
809       }
810 
811       *line_increment = 2;
812    }
813    else
814    {
815       *start_line = 0;
816       *line_increment = 1;
817    }
818 }
819 
820 //////////////////////////////////////////////////////////////////////////////
821 
Vdp2DrawScroll(vdp2draw_struct * info,Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)822 static void FASTCALL Vdp2DrawScroll(vdp2draw_struct *info, Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
823 {
824    int i, j;
825    int x, y;
826    clipping_struct clip[2];
827    u32 linewnd0addr, linewnd1addr;
828    u32 line_window_base[2] = { 0 };
829    screeninfo_struct sinfo;
830    int scrolly;
831    int *mosaic_y, *mosaic_x;
832    clipping_struct colorcalcwindow[2];
833    int start_line = 0, line_increment = 0;
834    int bad_cycle = bad_cycle_setting[info->titan_which_layer];
835    int charaddr, paladdr;
836    int output_y = 0;
837    u32 linescrollx_table[512] = { 0 };
838    u32 linescrolly_table[512] = { 0 };
839    float lineszoom_table[512] = { 0 };
840    int num_vertical_cell_scroll_enabled = 0;
841 
842    SetupScreenVars(info, &sinfo, info->PlaneAddr, regs);
843 
844    scrolly = info->y;
845 
846    clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0;
847    clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0;
848    ReadWindowData(info->wctl, clip, regs);
849    linewnd0addr = linewnd1addr = 0;
850    ReadLineWindowData(&info->islinewindow, info->wctl, &linewnd0addr, &linewnd1addr,regs);
851    line_window_base[0] = linewnd0addr;
852    line_window_base[1] = linewnd1addr;
853    /* color calculation window: in => no color calc, out => color calc */
854    ReadWindowData(regs->WCTLD >> 8, colorcalcwindow, regs);
855    {
856 	   static int tables_initialized = 0;
857 	   static int mosaic_table[16][1024];
858 	   if(!tables_initialized)
859 	   {
860 		   tables_initialized = 1;
861 			for(i=0;i<16;i++)
862 			{
863 				int m = i+1;
864 				for(j=0;j<1024;j++)
865 					mosaic_table[i][j] = j/m*m;
866 			}
867 	   }
868 	   mosaic_x = mosaic_table[info->mosaicxmask-1];
869 	   mosaic_y = mosaic_table[info->mosaicymask-1];
870    }
871 
872    Vdp2GetInterlaceInfo(&start_line, &line_increment);
873 
874    if (regs->SCRCTL & 1)
875       num_vertical_cell_scroll_enabled++;
876    if (regs->SCRCTL & 0x100)
877       num_vertical_cell_scroll_enabled++;
878 
879    //pre-generate line scroll tables
880    for (j = start_line; j < vdp2height; j++)
881    {
882       if (info->islinescroll)
883       {
884          //line scroll interval bit
885          int need_increment = ((j != 0) && (((j + 1) % info->lineinc) == 0));
886 
887          //horizontal line scroll
888          if (info->islinescroll & 0x1)
889          {
890             linescrollx_table[j] = (T1ReadLong(ram, info->linescrolltbl) >> 16) & 0x7FF;
891             if (need_increment)
892                info->linescrolltbl += 4;
893          }
894 
895          //vertical line scroll
896          if (info->islinescroll & 0x2)
897          {
898             linescrolly_table[j] = ((T1ReadWord(ram, info->linescrolltbl) & 0x7FF)) + scrolly;
899             if (need_increment)
900                info->linescrolltbl += 4;
901             y = info->y;
902          }
903 
904          //line zoom
905          if (info->islinescroll & 0x4)
906          {
907             lineszoom_table[j] = (T1ReadLong(ram, info->linescrolltbl) & 0x7FF00) / (float)65536.0;
908             if (need_increment)
909                info->linescrolltbl += 4;
910          }
911       }
912    }
913 
914    for (j = start_line; j < vdp2height; j += line_increment)
915    {
916       int Y;
917       int linescrollx = 0;
918       // precalculate the coordinate for the line(it's faster) and do line
919       // scroll
920       if (info->islinescroll)
921       {
922          //horizontal line scroll
923          if (info->islinescroll & 0x1)
924          {
925             linescrollx = linescrollx_table[j];
926          }
927 
928          //vertical line scroll
929          if (info->islinescroll & 0x2)
930          {
931             info->y = linescrolly_table[j];
932             y = info->y;
933          }
934          else
935             //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j)));
936 			y = info->y + info->coordincy*mosaic_y[j];
937 
938          //line zoom
939          if (info->islinescroll & 0x4)
940          {
941             info->coordincx = lineszoom_table[j];
942          }
943       }
944       else
945          //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j)));
946 		 y = info->y + info->coordincy*mosaic_y[j];
947 
948       if (vdp2_interlace)
949       {
950          linewnd0addr = line_window_base[0] + (j * 4);
951          linewnd1addr = line_window_base[1] + (j * 4);
952       }
953 
954       // if line window is enabled, adjust clipping values
955       ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr, ram, regs);
956       y &= sinfo.ymask;
957 
958       if (info->isverticalscroll && (!vdp2_x_hires))//seems to be ignored in hi res
959       {
960          // this is *wrong*, vertical scroll use a different value per cell
961          // info->verticalscrolltbl should be incremented by info->verticalscrollinc
962          // each time there's a cell change and reseted at the end of the line...
963          // or something like that :)
964          u32 scroll_value = 0;
965          int y_value = 0;
966 
967          if (vdp2_interlace)
968             y_value = j / 2;
969          else
970             y_value = j;
971 
972          if (num_vertical_cell_scroll_enabled == 1)
973          {
974             scroll_value = cell_data[y_value].data[0] >> 16;
975          }
976          else
977          {
978             if (info->titan_which_layer == TITAN_NBG0)
979                scroll_value = cell_data[y_value].data[0] >> 16;//reload cell data per line for sonic 2, 2 player mode
980             else if (info->titan_which_layer == TITAN_NBG1)
981                scroll_value = cell_data[y_value].data[1] >> 16;
982          }
983 
984          y += scroll_value;
985          y &= 0x1FF;
986       }
987 
988       Y=y;
989 
990       if (vdp2_interlace)
991          info->LoadLineParams(info, &sinfo, j / 2, lines);
992       else
993          info->LoadLineParams(info, &sinfo, j, lines);
994 
995       if (!info->enable)
996          continue;
997 
998       for (i = 0; i < vdp2width; i++)
999       {
1000          u32 color, dot;
1001          /* I'm really not sure about this... but I think the way we handle
1002          high resolution gets in the way with window process. I may be wrong...
1003          This was added for Cotton Boomerang */
1004 			int priority;
1005 
1006          // See if screen position is clipped, if it isn't, continue
1007          if (!TestBothWindow(info->wctl, clip, i, j))
1008          {
1009             continue;
1010          }
1011 
1012          //x = info->x+((int)(info->coordincx*(float)((info->mosaicxmask > 1) ? (i / info->mosaicxmask * info->mosaicxmask) : i)));
1013 		 x = info->x + mosaic_x[i]*info->coordincx;
1014          x &= sinfo.xmask;
1015 
1016          if (linescrollx) {
1017             x += linescrollx;
1018             x &= 0x3FF;
1019          }
1020 
1021          // Fetch Pixel, if it isn't transparent, continue
1022          if (!info->isbitmap)
1023          {
1024             // Tile
1025             y=Y;
1026             Vdp2MapCalcXY(info, &x, &y, &sinfo, regs, ram, bad_cycle);
1027          }
1028 
1029          if (!bad_cycle)
1030          {
1031             charaddr = info->charaddr;
1032             paladdr = info->paladdr;
1033          }
1034          else
1035          {
1036             charaddr = info->pipe[0].charaddr;
1037             paladdr = info->pipe[0].paladdr;
1038          }
1039 
1040          if (!Vdp2FetchPixel(info, x, y, &color, &dot, ram, charaddr, paladdr,color_ram))
1041          {
1042             continue;
1043          }
1044 
1045          priority = info->priority;
1046 
1047          //per-pixel priority is on
1048          if (info->specialprimode == 2)
1049          {
1050             priority = info->priority & 0xE;
1051 
1052             if (info->specialfunction & 1)
1053             {
1054                if (PixelIsSpecialPriority(info->specialcode,dot))
1055                {
1056                   priority |= 1;
1057                }
1058             }
1059          }
1060 
1061          // Apply color offset and color calculation/special color calculation
1062          // and then continue.
1063          // We almost need to know well ahead of time what the top
1064          // and second pixel is in order to work this.
1065 
1066          {
1067             u8 alpha;
1068             /* if we're in the valid area of the color calculation window, don't do color calculation */
1069             if (!TestBothWindow(regs->WCTLD >> 8, colorcalcwindow, i, j))
1070                alpha = 0x3F;
1071             else
1072                alpha = GetAlpha(info, color, dot);
1073 
1074             TitanPutPixel(priority, i, output_y, info->PostPixelFetchCalc(info, COLSAT2YAB32(alpha, color)), info->linescreen, info);
1075          }
1076       }
1077       output_y++;
1078    }
1079 }
1080 
1081 //////////////////////////////////////////////////////////////////////////////
1082 
Rbg0PutHiresPixel(vdp2draw_struct * info,u32 color,u32 dot,int i,int j)1083 void Rbg0PutHiresPixel(vdp2draw_struct *info, u32 color, u32 dot, int i, int j)
1084 {
1085    u32 pixel = info->PostPixelFetchCalc(info, COLSAT2YAB32(GetAlpha(info, color, dot), color));
1086    int x_pos = i * 2;
1087    TitanPutPixel(info->priority, x_pos, j, pixel, info->linescreen, info);
1088    TitanPutPixel(info->priority, x_pos + 1, j, pixel, info->linescreen, info);
1089 }
1090 
1091 //////////////////////////////////////////////////////////////////////////////
1092 
Rbg0PutPixel(vdp2draw_struct * info,u32 color,u32 dot,int i,int j)1093 void Rbg0PutPixel(vdp2draw_struct *info, u32 color, u32 dot, int i, int j)
1094 {
1095    if (vdp2_x_hires)
1096    {
1097       Rbg0PutHiresPixel(info, color, dot, i, j);
1098    }
1099    else
1100       TitanPutPixel(info->priority, i, j, info->PostPixelFetchCalc(info, COLSAT2YAB32(GetAlpha(info, color, dot), color)), info->linescreen, info);
1101 }
1102 
1103 //////////////////////////////////////////////////////////////////////////////
1104 
CheckBanks(Vdp2 * regs,int compare_value)1105 int CheckBanks(Vdp2* regs, int compare_value)
1106 {
1107    if (((regs->RAMCTL >> 0) & 3) == compare_value)//a0
1108       return 0;
1109    if (((regs->RAMCTL >> 2) & 3) == compare_value)//a1
1110       return 0;
1111    if (((regs->RAMCTL >> 4) & 3) == compare_value)//b0
1112       return 0;
1113    if (((regs->RAMCTL >> 6) & 3) == compare_value)//b1
1114       return 0;
1115 
1116    return 1;//no setting present
1117 }
1118 
Rbg0CheckRam(Vdp2 * regs)1119 int Rbg0CheckRam(Vdp2* regs)
1120 {
1121    if (((regs->RAMCTL >> 8) & 3) == 3)//both banks are divided
1122    {
1123       //ignore delta kax if the coefficient table
1124       //bank is unspecified
1125       if (CheckBanks(regs, 1))
1126          return 1;
1127    }
1128 
1129    return 0;
1130 }
1131 
Vdp2DrawRotationFP(vdp2draw_struct * info,vdp2rotationparameterfp_struct * parameter,Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1132 static void FASTCALL Vdp2DrawRotationFP(vdp2draw_struct *info, vdp2rotationparameterfp_struct *parameter, Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1133 {
1134    int i, j;
1135    int x, y;
1136    screeninfo_struct sinfo;
1137    vdp2rotationparameterfp_struct *p=&parameter[info->rotatenum];
1138    clipping_struct clip[2];
1139    u32 linewnd0addr, linewnd1addr;
1140 
1141    clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0;
1142    clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0;
1143    ReadWindowData(info->wctl, clip, regs);
1144    linewnd0addr = linewnd1addr = 0;
1145    ReadLineWindowData(&info->islinewindow, info->wctl, &linewnd0addr, &linewnd1addr, regs);
1146 
1147    Vdp2ReadRotationTableFP(info->rotatenum, p, regs, ram);
1148 
1149    if (!p->coefenab)
1150    {
1151       fixed32 xmul, ymul, C, F;
1152 
1153       // Since coefficients aren't being used, we can simplify the drawing process
1154       if (IsScreenRotatedFP(p))
1155       {
1156          // No rotation
1157          info->x = touint(mulfixed(p->kx, (p->Xst - p->Px)) + p->Px + p->Mx);
1158          info->y = touint(mulfixed(p->ky, (p->Yst - p->Py)) + p->Py + p->My);
1159          info->coordincx = tofloat(p->kx);
1160          info->coordincy = tofloat(p->ky);
1161       }
1162       else
1163       {
1164          GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F);
1165 
1166          // Do simple rotation
1167          CalculateRotationValuesFP(p);
1168 
1169          SetupScreenVars(info, &sinfo, info->PlaneAddr, regs);
1170 
1171          for (j = 0; j < vdp2height; j++)
1172          {
1173             info->LoadLineParams(info, &sinfo, j, lines);
1174             ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr, ram, regs);
1175 
1176             for (i = 0; i < rbg0width; i++)
1177             {
1178                u32 color, dot;
1179 
1180                if (!TestBothWindow(info->wctl, clip, i, j))
1181                   continue;
1182 
1183                x = GenerateRotatedXPosFP(p, i, xmul, ymul, C) & sinfo.xmask;
1184                y = GenerateRotatedYPosFP(p, i, xmul, ymul, F) & sinfo.ymask;
1185 
1186                // Convert coordinates into graphics
1187                if (!info->isbitmap)
1188                {
1189                   // Tile
1190                   Vdp2MapCalcXY(info, &x, &y, &sinfo, regs, ram,0);
1191                }
1192 
1193                // Fetch pixel
1194                if (!Vdp2FetchPixel(info, x, y, &color, &dot, ram, info->charaddr,info->paladdr, color_ram))
1195                {
1196                   continue;
1197                }
1198 
1199                Rbg0PutPixel(info, color, dot, i, j);
1200             }
1201             xmul += p->deltaXst;
1202             ymul += p->deltaYst;
1203          }
1204 
1205          return;
1206       }
1207    }
1208    else
1209    {
1210       fixed32 xmul, ymul, C, F;
1211       u32 coefx, coefy;
1212       u32 rcoefx, rcoefy;
1213       u32 lineAddr, lineColor, lineInc;
1214       u16 lineColorAddr;
1215 
1216       fixed32 xmul2, ymul2, C2, F2;
1217       u32 coefx2, coefy2;
1218       u32 rcoefx2, rcoefy2;
1219       screeninfo_struct sinfo2;
1220       vdp2rotationparameterfp_struct *p2 = NULL;
1221 
1222       clipping_struct rpwindow[2];
1223       int userpwindow = 0;
1224       int isrplinewindow = 0;
1225       u32 rplinewnd0addr, rplinewnd1addr;
1226 
1227       if ((regs->RPMD & 3) == 2)
1228          p2 = &parameter[1 - info->rotatenum];
1229       else if ((regs->RPMD & 3) == 3)
1230       {
1231          ReadWindowData(regs->WCTLD, rpwindow, regs);
1232          rplinewnd0addr = rplinewnd1addr = 0;
1233          ReadLineWindowData(&isrplinewindow, regs->WCTLD, &rplinewnd0addr, &rplinewnd1addr, regs);
1234          userpwindow = 1;
1235          p2 = &parameter[1 - info->rotatenum];
1236       }
1237 
1238       GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F);
1239 
1240       // Rotation using Coefficient Tables(now this stuff just gets wacky. It
1241       // has to be done in software, no exceptions)
1242       CalculateRotationValuesFP(p);
1243 
1244       SetupScreenVars(info, &sinfo, p->PlaneAddr, regs);
1245       coefx = coefy = 0;
1246       rcoefx = rcoefy = 0;
1247 
1248       if (p2 != NULL)
1249       {
1250          Vdp2ReadRotationTableFP(1 - info->rotatenum, p2, regs, ram);
1251          GenerateRotatedVarFP(p2, &xmul2, &ymul2, &C2, &F2);
1252          CalculateRotationValuesFP(p2);
1253          SetupScreenVars(info, &sinfo2, p2->PlaneAddr, regs);
1254          coefx2 = coefy2 = 0;
1255          rcoefx2 = rcoefy2 = 0;
1256       }
1257 
1258       if (Rbg0CheckRam(regs))//sonic r / all star baseball 97
1259       {
1260          if (p->coefenab && p->coefmode == 0)
1261          {
1262             p->deltaKAx = 0;
1263          }
1264 
1265          if (p2 && p2->coefenab && p2->coefmode == 0)
1266          {
1267             p2->deltaKAx = 0;
1268          }
1269       }
1270 
1271       if (info->linescreen)
1272       {
1273          if ((info->rotatenum == 0) && (regs->KTCTL & 0x10))
1274             info->linescreen = 2;
1275          else if (regs->KTCTL & 0x1000)
1276             info->linescreen = 3;
1277          if (regs->VRSIZE & 0x8000)
1278             lineAddr = (regs->LCTA.all & 0x7FFFF) << 1;
1279          else
1280             lineAddr = (regs->LCTA.all & 0x3FFFF) << 1;
1281 
1282          lineInc = regs->LCTA.part.U & 0x8000 ? 2 : 0;
1283       }
1284 
1285       for (j = 0; j < rbg0height; j++)
1286       {
1287          if (p->deltaKAx == 0)
1288          {
1289             Vdp2ReadCoefficientFP(p,
1290                                   p->coeftbladdr +
1291                                   (coefy + touint(rcoefy)) *
1292                                   p->coefdatasize, ram);
1293          }
1294          if ((p2 != NULL) && p2->coefenab && (p2->deltaKAx == 0))
1295          {
1296             Vdp2ReadCoefficientFP(p2,
1297                                   p2->coeftbladdr +
1298                                   (coefy2 + touint(rcoefy2)) *
1299                                   p2->coefdatasize, ram);
1300          }
1301 
1302          if (info->linescreen > 1)
1303          {
1304             lineColorAddr = (T1ReadWord(ram, lineAddr) & 0x780) | p->linescreen;
1305             lineColor = Vdp2ColorRamGetColor(lineColorAddr, color_ram);
1306             lineAddr += lineInc;
1307             TitanPutLineHLine(info->linescreen, j, COLSAT2YAB32(0x3F, lineColor));
1308          }
1309 
1310          info->LoadLineParams(info, &sinfo, j, lines);
1311          ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr, ram, regs);
1312 
1313          if (userpwindow)
1314             ReadLineWindowClip(isrplinewindow, rpwindow, &rplinewnd0addr, &rplinewnd1addr, ram, regs);
1315 
1316          for (i = 0; i < rbg0width; i++)
1317          {
1318             u32 color, dot;
1319 
1320             if (p->deltaKAx != 0)
1321             {
1322                Vdp2ReadCoefficientFP(p,
1323                                      p->coeftbladdr +
1324                                      (coefy + coefx + toint(rcoefx + rcoefy)) *
1325                                      p->coefdatasize, ram);
1326                coefx += toint(p->deltaKAx);
1327                rcoefx += decipart(p->deltaKAx);
1328             }
1329             if ((p2 != NULL) && p2->coefenab && (p2->deltaKAx != 0))
1330             {
1331                Vdp2ReadCoefficientFP(p2,
1332                                      p2->coeftbladdr +
1333                                      (coefy2 + coefx2 + toint(rcoefx2 + rcoefy2)) *
1334                                      p2->coefdatasize, ram);
1335                coefx2 += toint(p2->deltaKAx);
1336                rcoefx2 += decipart(p2->deltaKAx);
1337             }
1338 
1339             if (!TestBothWindow(info->wctl, clip, i, j))
1340                continue;
1341 
1342             if (((! userpwindow) && p->msb) || (userpwindow && (! TestBothWindow(regs->WCTLD, rpwindow, i, j))))
1343             {
1344                if ((p2 == NULL) || (p2->coefenab && p2->msb)) continue;
1345 
1346                x = GenerateRotatedXPosFP(p2, i, xmul2, ymul2, C2);
1347                y = GenerateRotatedYPosFP(p2, i, xmul2, ymul2, F2);
1348 
1349                switch(p2->screenover) {
1350                   case 0:
1351                      x &= sinfo2.xmask;
1352                      y &= sinfo2.ymask;
1353                      break;
1354                   case 1:
1355                      VDP2LOG("Screen-over mode 1 not implemented");
1356                      x &= sinfo2.xmask;
1357                      y &= sinfo2.ymask;
1358                      break;
1359                   case 2:
1360                      if ((x > sinfo2.xmask) || (y > sinfo2.ymask)) continue;
1361                      break;
1362                   case 3:
1363                      if ((x > 512) || (y > 512)) continue;
1364                }
1365 
1366                // Convert coordinates into graphics
1367                if (!info->isbitmap)
1368                {
1369                   // Tile
1370                   Vdp2MapCalcXY(info, &x, &y, &sinfo2, regs, ram, 0);
1371                }
1372             }
1373             else if (p->msb) continue;
1374             else
1375             {
1376                x = GenerateRotatedXPosFP(p, i, xmul, ymul, C);
1377                y = GenerateRotatedYPosFP(p, i, xmul, ymul, F);
1378 
1379                switch(p->screenover) {
1380                   case 0:
1381                      x &= sinfo.xmask;
1382                      y &= sinfo.ymask;
1383                      break;
1384                   case 1:
1385                      VDP2LOG("Screen-over mode 1 not implemented");
1386                      x &= sinfo.xmask;
1387                      y &= sinfo.ymask;
1388                      break;
1389                   case 2:
1390                      if ((x > sinfo.xmask) || (y > sinfo.ymask)) continue;
1391                      break;
1392                   case 3:
1393                      if ((x > 512) || (y > 512)) continue;
1394                }
1395 
1396                // Convert coordinates into graphics
1397                if (!info->isbitmap)
1398                {
1399                   // Tile
1400                   Vdp2MapCalcXY(info, &x, &y, &sinfo, regs, ram, 0);
1401                }
1402             }
1403 
1404             // Fetch pixel
1405             if (!Vdp2FetchPixel(info, x, y, &color, &dot, ram, info->charaddr, info->paladdr, color_ram))
1406             {
1407                continue;
1408             }
1409 
1410             Rbg0PutPixel(info, color, dot, i, j);
1411          }
1412          xmul += p->deltaXst;
1413          ymul += p->deltaYst;
1414          coefx = 0;
1415          rcoefx = 0;
1416          coefy += toint(p->deltaKAst);
1417          rcoefy += decipart(p->deltaKAst);
1418 
1419          if (p2 != NULL)
1420          {
1421             xmul2 += p2->deltaXst;
1422             ymul2 += p2->deltaYst;
1423             if (p2->coefenab)
1424             {
1425                coefx2 = 0;
1426                rcoefx2 = 0;
1427                coefy2 += toint(p2->deltaKAst);
1428                rcoefy2 += decipart(p2->deltaKAst);
1429             }
1430          }
1431       }
1432       return;
1433    }
1434 
1435    Vdp2DrawScroll(info, lines, regs, ram, color_ram, cell_data);
1436 }
1437 
1438 //////////////////////////////////////////////////////////////////////////////
1439 
Vdp2DrawBackScreen(void)1440 static void Vdp2DrawBackScreen(void)
1441 {
1442    int i, j;
1443 
1444    // Only draw black if TVMD's DISP and BDCLMD bits are cleared
1445    if ((Vdp2Regs->TVMD & 0x8000) == 0 && (Vdp2Regs->TVMD & 0x100) == 0)
1446    {
1447       // Draw Black
1448       for (j = 0; j < vdp2height; j++)
1449          TitanPutBackHLine(j, COLSAT2YAB32(0x3F, 0));
1450    }
1451    else
1452    {
1453       // Draw Back Screen
1454       u32 scrAddr;
1455       u16 dot;
1456       vdp2draw_struct info = { 0 };
1457 
1458       ReadVdp2ColorOffset(Vdp2Regs, &info, (1 << 5), 0);
1459 
1460       if (Vdp2Regs->VRSIZE & 0x8000)
1461          scrAddr = (((Vdp2Regs->BKTAU & 0x7) << 16) | Vdp2Regs->BKTAL) * 2;
1462       else
1463          scrAddr = (((Vdp2Regs->BKTAU & 0x3) << 16) | Vdp2Regs->BKTAL) * 2;
1464 
1465       if (Vdp2Regs->BKTAU & 0x8000)
1466       {
1467          // Per Line
1468          for (i = 0; i < vdp2height; i++)
1469          {
1470             dot = T1ReadWord(Vdp2Ram, scrAddr);
1471             scrAddr += 2;
1472 
1473             TitanPutBackHLine(i, info.PostPixelFetchCalc(&info, COLSAT2YAB16(0x3f, dot)));
1474          }
1475       }
1476       else
1477       {
1478          // Single Color
1479          dot = T1ReadWord(Vdp2Ram, scrAddr);
1480 
1481          for (j = 0; j < vdp2height; j++)
1482             TitanPutBackHLine(j, info.PostPixelFetchCalc(&info, COLSAT2YAB16(0x3f, dot)));
1483       }
1484    }
1485 }
1486 
1487 //////////////////////////////////////////////////////////////////////////////
1488 
Vdp2DrawLineScreen(void)1489 static void Vdp2DrawLineScreen(void)
1490 {
1491    u32 scrAddr;
1492    u16 color;
1493    u32 dot;
1494    int i;
1495 	int alpha;
1496 
1497    /* no need to go further if no screen is using the line screen */
1498    if (Vdp2Regs->LNCLEN == 0)
1499       return;
1500 
1501    if (Vdp2Regs->VRSIZE & 0x8000)
1502       scrAddr = (Vdp2Regs->LCTA.all & 0x7FFFF) << 1;
1503    else
1504       scrAddr = (Vdp2Regs->LCTA.all & 0x3FFFF) << 1;
1505 
1506    alpha = (Vdp2Regs->CCRLB & 0x1f) << 1;
1507 
1508    if (Vdp2Regs->LCTA.part.U & 0x8000)
1509    {
1510       /* per line */
1511       for (i = 0; i < vdp2height; i++)
1512       {
1513          color = T1ReadWord(Vdp2Ram, scrAddr) & 0x7FF;
1514          dot = Vdp2ColorRamGetColor(color, Vdp2ColorRam);
1515          scrAddr += 2;
1516 
1517          TitanPutLineHLine(1, i, COLSAT2YAB32(alpha, dot));
1518       }
1519    }
1520    else
1521    {
1522       /* single color, implemented but not tested... */
1523       color = T1ReadWord(Vdp2Ram, scrAddr) & 0x7FF;
1524       dot = Vdp2ColorRamGetColor(color, Vdp2ColorRam);
1525       for (i = 0; i < vdp2height; i++)
1526          TitanPutLineHLine(1, i, COLSAT2YAB32(alpha, dot));
1527    }
1528 }
1529 
1530 //////////////////////////////////////////////////////////////////////////////
1531 
LoadLineParamsNBG0(vdp2draw_struct * info,screeninfo_struct * sinfo,int line,Vdp2 * lines)1532 static void LoadLineParamsNBG0(vdp2draw_struct * info, screeninfo_struct * sinfo, int line, Vdp2* lines)
1533 {
1534    Vdp2 * regs;
1535 
1536    regs = Vdp2RestoreRegs(line, lines);
1537    if (regs == NULL) return;
1538    ReadVdp2ColorOffset(regs, info, 0x1, 0x1);
1539    info->specialprimode = regs->SFPRMD & 0x3;
1540    info->enable = regs->BGON & 0x1 || regs->BGON & 0x20;//nbg0 or rbg1
1541    GeneratePlaneAddrTable(info, sinfo->planetbl, info->PlaneAddr, regs);//sonic 2, 2 player mode
1542 }
1543 
1544 //////////////////////////////////////////////////////////////////////////////
1545 
Vdp2DrawNBG0(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1546 static void Vdp2DrawNBG0(Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1547 {
1548    vdp2draw_struct info = { 0 };
1549    vdp2rotationparameterfp_struct parameter[2];
1550 
1551    info.titan_which_layer = TITAN_NBG0;
1552    info.titan_shadow_enabled = (regs->SDCTL >> 0) & 1;
1553 
1554    parameter[0].PlaneAddr = (void FASTCALL (*)(void *, int, Vdp2*))&Vdp2ParameterAPlaneAddr;
1555    parameter[1].PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterBPlaneAddr;
1556 
1557    if (regs->BGON & 0x20)
1558    {
1559       // RBG1 mode
1560       info.enable = regs->BGON & 0x20;
1561 
1562       // Read in Parameter B
1563       Vdp2ReadRotationTableFP(1, &parameter[1], regs, ram);
1564 
1565       if((info.isbitmap = regs->CHCTLA & 0x2) != 0)
1566       {
1567          // Bitmap Mode
1568          ReadBitmapSize(&info, regs->CHCTLA >> 2, 0x3);
1569 
1570          info.charaddr = (regs->MPOFR & 0x70) * 0x2000;
1571          info.paladdr = (regs->BMPNA & 0x7) << 8;
1572          info.flipfunction = 0;
1573          info.specialfunction = 0;
1574          info.specialcolorfunction = (regs->BMPNA & 0x10) >> 4;
1575       }
1576       else
1577       {
1578          // Tile Mode
1579          info.mapwh = 4;
1580          ReadPlaneSize(&info, regs->PLSZ >> 12);
1581          ReadPatternData(&info, regs->PNCN0, regs->CHCTLA & 0x1);
1582       }
1583 
1584       info.rotatenum = 1;
1585       info.rotatemode = 0;
1586       info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterBPlaneAddr;
1587    }
1588    else if (regs->BGON & 0x1)
1589    {
1590       // NBG0 mode
1591       info.enable = regs->BGON & 0x1;
1592 
1593       if((info.isbitmap = regs->CHCTLA & 0x2) != 0)
1594       {
1595          // Bitmap Mode
1596          ReadBitmapSize(&info, regs->CHCTLA >> 2, 0x3);
1597 
1598          info.x = regs->SCXIN0 & 0x7FF;
1599          info.y = regs->SCYIN0 & 0x7FF;
1600 
1601          info.charaddr = (regs->MPOFN & 0x7) * 0x20000;
1602          info.paladdr = (regs->BMPNA & 0x7) << 8;
1603          info.flipfunction = 0;
1604          info.specialfunction = 0;
1605          info.specialcolorfunction = (regs->BMPNA & 0x10) >> 4;
1606       }
1607       else
1608       {
1609          // Tile Mode
1610          info.mapwh = 2;
1611 
1612          ReadPlaneSize(&info, regs->PLSZ);
1613 
1614          info.x = regs->SCXIN0 & 0x7FF;
1615          info.y = regs->SCYIN0 & 0x7FF;
1616          ReadPatternData(&info, regs->PNCN0, regs->CHCTLA & 0x1);
1617       }
1618 
1619       info.coordincx = (regs->ZMXN0.all & 0x7FF00) / (float) 65536;
1620       info.coordincy = (regs->ZMYN0.all & 0x7FF00) / (float) 65536;
1621       info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2NBG0PlaneAddr;
1622    }
1623 
1624    info.transparencyenable = !(regs->BGON & 0x100);
1625    info.specialprimode = regs->SFPRMD & 0x3;
1626 
1627    info.colornumber = (regs->CHCTLA & 0x70) >> 4;
1628 
1629    if (regs->CCCTL & 0x201)
1630       info.alpha = ((~regs->CCRNA & 0x1F) << 1) + 1;
1631    else
1632       info.alpha = 0x3F;
1633    if ((regs->CCCTL & 0x201) == 0x201) info.alpha |= 0x80;
1634    else if ((regs->CCCTL & 0x101) == 0x101) info.alpha |= 0x80;
1635    info.specialcolormode = regs->SFCCMD & 0x3;
1636    if (regs->SFSEL & 0x1)
1637       info.specialcode = regs->SFCODE >> 8;
1638    else
1639       info.specialcode = regs->SFCODE & 0xFF;
1640    info.linescreen = 0;
1641    if (regs->LNCLEN & 0x1)
1642       info.linescreen = 1;
1643 
1644    info.coloroffset = (regs->CRAOFA & 0x7) << 8;
1645    ReadVdp2ColorOffset(regs, &info, 0x1, 0x1);
1646    info.priority = regs->PRINA & 0x7;
1647 
1648    if (!(info.enable & Vdp2External.disptoggle))
1649       return;
1650 
1651    ReadMosaicData(&info, 0x1, regs);
1652    ReadLineScrollData(&info, regs->SCRCTL & 0xFF, regs->LSTA0.all);
1653    if (regs->SCRCTL & 1)
1654    {
1655       info.isverticalscroll = 1;
1656       info.verticalscrolltbl = (regs->VCSTA.all & 0x7FFFE) << 1;
1657       if (regs->SCRCTL & 0x100)
1658          info.verticalscrollinc = 8;
1659       else
1660          info.verticalscrollinc = 4;
1661    }
1662    else
1663       info.isverticalscroll = 0;
1664    info.wctl = regs->WCTLA;
1665 
1666    info.LoadLineParams = (void (*)(void *, void *,int ,Vdp2*)) LoadLineParamsNBG0;
1667 
1668    if (info.enable == 1)
1669    {
1670       // NBG0 draw
1671       Vdp2DrawScroll(&info, lines, regs, ram, color_ram, cell_data);
1672    }
1673    else
1674    {
1675       // RBG1 draw
1676       Vdp2DrawRotationFP(&info, parameter, lines, regs, ram, color_ram, cell_data);
1677    }
1678 }
1679 
1680 //////////////////////////////////////////////////////////////////////////////
1681 
LoadLineParamsNBG1(vdp2draw_struct * info,screeninfo_struct * sinfo,int line,Vdp2 * lines)1682 static void LoadLineParamsNBG1(vdp2draw_struct * info, screeninfo_struct * sinfo, int line, Vdp2* lines)
1683 {
1684    Vdp2 * regs;
1685 
1686    regs = Vdp2RestoreRegs(line, lines);
1687    if (regs == NULL) return;
1688    ReadVdp2ColorOffset(regs, info, 0x2, 0x2);
1689    info->specialprimode = (regs->SFPRMD >> 2) & 0x3;
1690    info->enable = regs->BGON & 0x2;//f1 challenge map when zoomed out
1691    GeneratePlaneAddrTable(info, sinfo->planetbl, info->PlaneAddr, regs);
1692 }
1693 
1694 //////////////////////////////////////////////////////////////////////////////
1695 
Vdp2DrawNBG1(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1696 static void Vdp2DrawNBG1(Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1697 {
1698    vdp2draw_struct info = { 0 };
1699 
1700    info.titan_which_layer = TITAN_NBG1;
1701    info.titan_shadow_enabled = (regs->SDCTL >> 1) & 1;
1702 
1703    info.enable = regs->BGON & 0x2;
1704    info.transparencyenable = !(regs->BGON & 0x200);
1705    info.specialprimode = (regs->SFPRMD >> 2) & 0x3;
1706 
1707    info.colornumber = (regs->CHCTLA & 0x3000) >> 12;
1708 
1709    if((info.isbitmap = regs->CHCTLA & 0x200) != 0)
1710    {
1711       ReadBitmapSize(&info, regs->CHCTLA >> 10, 0x3);
1712 
1713       info.x = regs->SCXIN1 & 0x7FF;
1714       info.y = regs->SCYIN1 & 0x7FF;
1715 
1716       info.charaddr = ((regs->MPOFN & 0x70) >> 4) * 0x20000;
1717       info.paladdr = regs->BMPNA & 0x700;
1718       info.flipfunction = 0;
1719       info.specialfunction = 0;
1720       info.specialcolorfunction = (regs->BMPNA & 0x1000) >> 12;
1721    }
1722    else
1723    {
1724       info.mapwh = 2;
1725 
1726       ReadPlaneSize(&info, regs->PLSZ >> 2);
1727 
1728       info.x = regs->SCXIN1 & 0x7FF;
1729       info.y = regs->SCYIN1 & 0x7FF;
1730 
1731       ReadPatternData(&info, regs->PNCN1, regs->CHCTLA & 0x100);
1732    }
1733 
1734    if (regs->CCCTL & 0x202)
1735       info.alpha = ((~regs->CCRNA & 0x1F00) >> 7) + 1;
1736    else
1737       info.alpha = 0x3F;
1738    if ((regs->CCCTL & 0x202) == 0x202) info.alpha |= 0x80;
1739    else if ((regs->CCCTL & 0x102) == 0x102) info.alpha |= 0x80;
1740    info.specialcolormode = (regs->SFCCMD >> 2) & 0x3;
1741    if (regs->SFSEL & 0x2)
1742       info.specialcode = regs->SFCODE >> 8;
1743    else
1744       info.specialcode = regs->SFCODE & 0xFF;
1745    info.linescreen = 0;
1746    if (regs->LNCLEN & 0x2)
1747       info.linescreen = 1;
1748 
1749    info.coloroffset = (regs->CRAOFA & 0x70) << 4;
1750    ReadVdp2ColorOffset(regs, &info, 0x2, 0x2);
1751    info.coordincx = (regs->ZMXN1.all & 0x7FF00) / (float) 65536;
1752    info.coordincy = (regs->ZMYN1.all & 0x7FF00) / (float) 65536;
1753 
1754    info.priority = (regs->PRINA >> 8) & 0x7;
1755    info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2NBG1PlaneAddr;
1756 
1757    if (!(info.enable & Vdp2External.disptoggle) ||
1758        (regs->BGON & 0x1 && (regs->CHCTLA & 0x70) >> 4 == 4)) // If NBG0 16M mode is enabled, don't draw
1759       return;
1760 
1761    ReadMosaicData(&info, 0x2, regs);
1762    ReadLineScrollData(&info, regs->SCRCTL >> 8, regs->LSTA1.all);
1763    if (regs->SCRCTL & 0x100)
1764    {
1765       info.isverticalscroll = 1;
1766       if (regs->SCRCTL & 0x1)
1767       {
1768          info.verticalscrolltbl = 4 + ((regs->VCSTA.all & 0x7FFFE) << 1);
1769          info.verticalscrollinc = 8;
1770       }
1771       else
1772       {
1773          info.verticalscrolltbl = (regs->VCSTA.all & 0x7FFFE) << 1;
1774          info.verticalscrollinc = 4;
1775       }
1776    }
1777    else
1778       info.isverticalscroll = 0;
1779    info.wctl = regs->WCTLA >> 8;
1780 
1781    info.LoadLineParams = (void(*)(void *, void*, int, Vdp2*)) LoadLineParamsNBG1;
1782 
1783    Vdp2DrawScroll(&info, lines, regs, ram, color_ram, cell_data);
1784 }
1785 
1786 //////////////////////////////////////////////////////////////////////////////
1787 
LoadLineParamsNBG2(vdp2draw_struct * info,screeninfo_struct * sinfo,int line,Vdp2 * lines)1788 static void LoadLineParamsNBG2(vdp2draw_struct * info, screeninfo_struct * sinfo, int line, Vdp2* lines)
1789 {
1790    Vdp2 * regs;
1791 
1792    regs = Vdp2RestoreRegs(line, lines);
1793    if (regs == NULL) return;
1794    ReadVdp2ColorOffset(regs, info, 0x4, 0x4);
1795    info->specialprimode = (regs->SFPRMD >> 4) & 0x3;
1796    info->enable = regs->BGON & 0x4;
1797    GeneratePlaneAddrTable(info, sinfo->planetbl, info->PlaneAddr, regs);
1798 }
1799 
1800 //////////////////////////////////////////////////////////////////////////////
1801 
Vdp2DrawNBG2(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1802 static void Vdp2DrawNBG2(Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1803 {
1804    vdp2draw_struct info = { 0 };
1805 
1806    info.titan_which_layer = TITAN_NBG2;
1807    info.titan_shadow_enabled = (regs->SDCTL >> 2) & 1;
1808 
1809    info.enable = regs->BGON & 0x4;
1810    info.transparencyenable = !(regs->BGON & 0x400);
1811    info.specialprimode = (regs->SFPRMD >> 4) & 0x3;
1812 
1813    info.colornumber = (regs->CHCTLB & 0x2) >> 1;
1814    info.mapwh = 2;
1815 
1816    ReadPlaneSize(&info, regs->PLSZ >> 4);
1817    info.x = regs->SCXN2 & 0x7FF;
1818    info.y = regs->SCYN2 & 0x7FF;
1819    ReadPatternData(&info, regs->PNCN2, regs->CHCTLB & 0x1);
1820 
1821    if (regs->CCCTL & 0x204)
1822       info.alpha = ((~regs->CCRNB & 0x1F) << 1) + 1;
1823    else
1824       info.alpha = 0x3F;
1825    if ((regs->CCCTL & 0x204) == 0x204) info.alpha |= 0x80;
1826    else if ((regs->CCCTL & 0x104) == 0x104) info.alpha |= 0x80;
1827    info.specialcolormode = (regs->SFCCMD >> 4) & 0x3;
1828    if (regs->SFSEL & 0x4)
1829       info.specialcode = regs->SFCODE >> 8;
1830    else
1831       info.specialcode = regs->SFCODE & 0xFF;
1832    info.linescreen = 0;
1833    if (regs->LNCLEN & 0x4)
1834       info.linescreen = 1;
1835 
1836    info.coloroffset = regs->CRAOFA & 0x700;
1837    ReadVdp2ColorOffset(regs, &info, 0x4, 0x4);
1838    info.coordincx = info.coordincy = 1;
1839 
1840    info.priority = regs->PRINB & 0x7;
1841    info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2NBG2PlaneAddr;
1842 
1843    if (!(info.enable & Vdp2External.disptoggle) ||
1844       (regs->BGON & 0x1 && (regs->CHCTLA & 0x70) >> 4 >= 2)) // If NBG0 2048/32786/16M mode is enabled, don't draw
1845       return;
1846 
1847    ReadMosaicData(&info, 0x4, regs);
1848    info.islinescroll = 0;
1849    info.isverticalscroll = 0;
1850    info.wctl = regs->WCTLB;
1851    info.isbitmap = 0;
1852 
1853    info.LoadLineParams = (void(*)(void *,void*, int, Vdp2*)) LoadLineParamsNBG2;
1854 
1855    Vdp2DrawScroll(&info, lines, regs, ram, color_ram, cell_data);
1856 }
1857 
1858 //////////////////////////////////////////////////////////////////////////////
1859 
LoadLineParamsNBG3(vdp2draw_struct * info,screeninfo_struct * sinfo,int line,Vdp2 * lines)1860 static void LoadLineParamsNBG3(vdp2draw_struct * info, screeninfo_struct * sinfo, int line, Vdp2* lines)
1861 {
1862    Vdp2 * regs;
1863 
1864    regs = Vdp2RestoreRegs(line, lines);
1865    if (regs == NULL) return;
1866    ReadVdp2ColorOffset(regs, info, 0x8, 0x8);
1867    info->specialprimode = (regs->SFPRMD >> 6) & 0x3;
1868    info->enable = regs->BGON & 0x8;
1869    GeneratePlaneAddrTable(info, sinfo->planetbl, info->PlaneAddr, regs);
1870 }
1871 
1872 //////////////////////////////////////////////////////////////////////////////
1873 
Vdp2DrawNBG3(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1874 static void Vdp2DrawNBG3(Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1875 {
1876    vdp2draw_struct info = { 0 };
1877 
1878    info.titan_which_layer = TITAN_NBG3;
1879    info.titan_shadow_enabled = (regs->SDCTL >> 3) & 1;
1880 
1881    info.enable = regs->BGON & 0x8;
1882    info.transparencyenable = !(regs->BGON & 0x800);
1883    info.specialprimode = (regs->SFPRMD >> 6) & 0x3;
1884 
1885    info.colornumber = (regs->CHCTLB & 0x20) >> 5;
1886 
1887    info.mapwh = 2;
1888 
1889    ReadPlaneSize(&info, regs->PLSZ >> 6);
1890    info.x = regs->SCXN3 & 0x7FF;
1891    info.y = regs->SCYN3 & 0x7FF;
1892    ReadPatternData(&info, regs->PNCN3, regs->CHCTLB & 0x10);
1893 
1894    if (regs->CCCTL & 0x208)
1895       info.alpha = ((~regs->CCRNB & 0x1F00) >> 7) + 1;
1896    else
1897       info.alpha = 0x3F;
1898    if ((regs->CCCTL & 0x208) == 0x208) info.alpha |= 0x80;
1899    else if ((regs->CCCTL & 0x108) == 0x108) info.alpha |= 0x80;
1900    info.specialcolormode = (regs->SFCCMD >> 6) & 0x3;
1901    if (regs->SFSEL & 0x8)
1902       info.specialcode = regs->SFCODE >> 8;
1903    else
1904       info.specialcode = regs->SFCODE & 0xFF;
1905    info.linescreen = 0;
1906    if (regs->LNCLEN & 0x8)
1907       info.linescreen = 1;
1908 
1909    info.coloroffset = (regs->CRAOFA & 0x7000) >> 4;
1910    ReadVdp2ColorOffset(regs, &info, 0x8, 0x8);
1911    info.coordincx = info.coordincy = 1;
1912 
1913    info.priority = (regs->PRINB >> 8) & 0x7;
1914    info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2NBG3PlaneAddr;
1915 
1916    if (!(info.enable & Vdp2External.disptoggle) ||
1917       (regs->BGON & 0x1 && (regs->CHCTLA & 0x70) >> 4 == 4) || // If NBG0 16M mode is enabled, don't draw
1918       (regs->BGON & 0x2 && (regs->CHCTLA & 0x3000) >> 12 >= 2)) // If NBG1 2048/32786 is enabled, don't draw
1919       return;
1920 
1921    ReadMosaicData(&info, 0x8, regs);
1922    info.islinescroll = 0;
1923    info.isverticalscroll = 0;
1924    info.wctl = regs->WCTLB >> 8;
1925    info.isbitmap = 0;
1926 
1927    info.LoadLineParams = (void(*)(void *, void*, int, Vdp2*)) LoadLineParamsNBG3;
1928 
1929    Vdp2DrawScroll(&info, lines, regs, ram, color_ram, cell_data);
1930 }
1931 
1932 //////////////////////////////////////////////////////////////////////////////
1933 
LoadLineParamsRBG0(vdp2draw_struct * info,screeninfo_struct * sinfo,int line,Vdp2 * lines)1934 static void LoadLineParamsRBG0(vdp2draw_struct * info, screeninfo_struct * sinfo, int line, Vdp2* lines)
1935 {
1936    Vdp2 * regs;
1937 
1938    regs = Vdp2RestoreRegs(line, lines);
1939    if (regs == NULL) return;
1940    ReadVdp2ColorOffset(regs, info, 0x10, 0x10);
1941    info->specialprimode = (regs->SFPRMD >> 8) & 0x3;
1942 }
1943 
1944 //////////////////////////////////////////////////////////////////////////////
1945 
Vdp2DrawRBG0(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data)1946 static void Vdp2DrawRBG0(Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data)
1947 {
1948    vdp2draw_struct info = { 0 };
1949    vdp2rotationparameterfp_struct parameter[2];
1950 
1951    info.titan_which_layer = TITAN_RBG0;
1952    info.titan_shadow_enabled = (regs->SDCTL >> 4) & 1;
1953 
1954    parameter[0].PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterAPlaneAddr;
1955    parameter[1].PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterBPlaneAddr;
1956 
1957    info.enable = regs->BGON & 0x10;
1958    info.priority = regs->PRIR & 0x7;
1959    if (!(info.enable & Vdp2External.disptoggle))
1960       return;
1961    info.transparencyenable = !(regs->BGON & 0x1000);
1962    info.specialprimode = (regs->SFPRMD >> 8) & 0x3;
1963 
1964    info.colornumber = (regs->CHCTLB & 0x7000) >> 12;
1965 
1966    // Figure out which Rotation Parameter we're using
1967    switch (regs->RPMD & 0x3)
1968    {
1969       case 0:
1970          // Parameter A
1971          info.rotatenum = 0;
1972          info.rotatemode = 0;
1973          info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterAPlaneAddr;
1974          break;
1975       case 1:
1976          // Parameter B
1977          info.rotatenum = 1;
1978          info.rotatemode = 0;
1979          info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterBPlaneAddr;
1980          break;
1981       case 2:
1982          // Parameter A+B switched via coefficients
1983       case 3:
1984          // Parameter A+B switched via rotation parameter window
1985       default:
1986          info.rotatenum = 0;
1987          info.rotatemode = 1 + (regs->RPMD & 0x1);
1988          info.PlaneAddr = (void FASTCALL(*)(void *, int, Vdp2*))&Vdp2ParameterAPlaneAddr;
1989          break;
1990    }
1991 
1992    Vdp2ReadRotationTableFP(info.rotatenum, &parameter[info.rotatenum], regs, ram);
1993 
1994    if((info.isbitmap = regs->CHCTLB & 0x200) != 0)
1995    {
1996       // Bitmap Mode
1997       ReadBitmapSize(&info, regs->CHCTLB >> 10, 0x1);
1998 
1999       if (info.rotatenum == 0)
2000          // Parameter A
2001          info.charaddr = (regs->MPOFR & 0x7) * 0x20000;
2002       else
2003          // Parameter B
2004          info.charaddr = (regs->MPOFR & 0x70) * 0x2000;
2005 
2006       info.paladdr = (regs->BMPNB & 0x7) << 8;
2007       info.flipfunction = 0;
2008       info.specialfunction = 0;
2009       info.specialcolorfunction = (regs->BMPNB & 0x10) >> 4;
2010    }
2011    else
2012    {
2013       // Tile Mode
2014       info.mapwh = 4;
2015 
2016       if (info.rotatenum == 0)
2017          // Parameter A
2018          ReadPlaneSize(&info, regs->PLSZ >> 8);
2019       else
2020          // Parameter B
2021          ReadPlaneSize(&info, regs->PLSZ >> 12);
2022 
2023       ReadPatternData(&info, regs->PNCR, regs->CHCTLB & 0x100);
2024    }
2025 
2026    if (regs->CCCTL & 0x210)
2027       info.alpha = ((~regs->CCRR & 0x1F) << 1) + 1;
2028    else
2029       info.alpha = 0x3F;
2030    if ((regs->CCCTL & 0x210) == 0x210) info.alpha |= 0x80;
2031    else if ((regs->CCCTL & 0x110) == 0x110) info.alpha |= 0x80;
2032    info.specialcolormode = (regs->SFCCMD >> 8) & 0x3;
2033    if (regs->SFSEL & 0x10)
2034       info.specialcode = regs->SFCODE >> 8;
2035    else
2036       info.specialcode = regs->SFCODE & 0xFF;
2037    info.linescreen = 0;
2038    if (regs->LNCLEN & 0x10)
2039       info.linescreen = 1;
2040 
2041    info.coloroffset = (regs->CRAOFB & 0x7) << 8;
2042 
2043    ReadVdp2ColorOffset(regs, &info, 0x10, 0x10);
2044    info.coordincx = info.coordincy = 1;
2045 
2046    ReadMosaicData(&info, 0x10, regs);
2047    info.islinescroll = 0;
2048    info.isverticalscroll = 0;
2049    info.wctl = regs->WCTLC;
2050 
2051    info.LoadLineParams = (void(*)(void *, void*, int, Vdp2*)) LoadLineParamsRBG0;
2052 
2053    Vdp2DrawRotationFP(&info, parameter, lines, regs, ram, color_ram, cell_data);
2054 }
2055 
2056 //////////////////////////////////////////////////////////////////////////////
2057 
LoadLineParamsSprite(vdp2draw_struct * info,int line,Vdp2 * lines)2058 static void LoadLineParamsSprite(vdp2draw_struct * info, int line, Vdp2* lines)
2059 {
2060    Vdp2 * regs;
2061 
2062    regs = Vdp2RestoreRegs(line, lines);
2063    if (regs == NULL) return;
2064    ReadVdp2ColorOffset(regs, info, 0x40, 0x40);
2065 }
2066 
2067 //////////////////////////////////////////////////////////////////////////////
2068 
2069 struct {
2070    volatile int need_draw[6];
2071    volatile int draw_finished[6];
2072    Vdp2 lines[270];
2073    Vdp2 regs;
2074    u8 ram[0x80000];
2075    u8 color_ram[0x1000];
2076    struct CellScrollData cell_scroll_data[270];
2077 }vidsoft_thread_context;
2078 
2079 #define DECLARE_THREAD(NAME, LAYER, FUNC) \
2080 void NAME(void * data) \
2081 { \
2082    for (;;) \
2083    { \
2084       if (vidsoft_thread_context.need_draw[LAYER]) \
2085       { \
2086          vidsoft_thread_context.need_draw[LAYER] = 0; \
2087          FUNC(vidsoft_thread_context.lines, &vidsoft_thread_context.regs, vidsoft_thread_context.ram, vidsoft_thread_context.color_ram, vidsoft_thread_context.cell_scroll_data); \
2088          vidsoft_thread_context.draw_finished[LAYER] = 1; \
2089       } \
2090       YabThreadSleep(); \
2091    } \
2092 }
2093 
DECLARE_THREAD(VidsoftRbg0Thread,TITAN_RBG0,Vdp2DrawRBG0)2094 DECLARE_THREAD(VidsoftRbg0Thread, TITAN_RBG0, Vdp2DrawRBG0)
2095 DECLARE_THREAD(VidsoftNbg0Thread, TITAN_NBG0, Vdp2DrawNBG0)
2096 DECLARE_THREAD(VidsoftNbg1Thread, TITAN_NBG1, Vdp2DrawNBG1)
2097 DECLARE_THREAD(VidsoftNbg2Thread, TITAN_NBG2, Vdp2DrawNBG2)
2098 DECLARE_THREAD(VidsoftNbg3Thread, TITAN_NBG3, Vdp2DrawNBG3)
2099 
2100 //////////////////////////////////////////////////////////////////////////////
2101 
2102 void VIDSoftSetNumLayerThreads(int num)
2103 {
2104    vidsoft_num_layer_threads = num;
2105 }
2106 
2107 //////////////////////////////////////////////////////////////////////////////
2108 
VidsoftVdp1Thread(void * data)2109 void VidsoftVdp1Thread(void* data)
2110 {
2111    for (;;)
2112    {
2113       if (vidsoft_vdp1_thread_context.need_draw)
2114       {
2115          vidsoft_vdp1_thread_context.need_draw = 0;
2116          Vdp1DrawCommands(vidsoft_vdp1_thread_context.ram, &vidsoft_vdp1_thread_context.regs, vidsoft_vdp1_thread_context.back_framebuffer);
2117          memcpy(vdp1backframebuffer, vidsoft_vdp1_thread_context.back_framebuffer, 0x40000);
2118          vidsoft_vdp1_thread_context.draw_finished = 1;
2119       }
2120 
2121       YabThreadSleep();
2122    }
2123 }
2124 
2125 //////////////////////////////////////////////////////////////////////////////
2126 
VidsoftWaitForVdp1Thread()2127 void VidsoftWaitForVdp1Thread()
2128 {
2129    if (vidsoft_vdp1_thread_enabled)
2130    {
2131       while (!vidsoft_vdp1_thread_context.draw_finished){}
2132    }
2133 }
2134 
2135 //////////////////////////////////////////////////////////////////////////////
2136 
VIDSoftSetVdp1ThreadEnable(int b)2137 void VIDSoftSetVdp1ThreadEnable(int b)
2138 {
2139    vidsoft_vdp1_thread_enabled = b;
2140 
2141 }
2142 
VidsoftSpriteThread(void * data)2143 void VidsoftSpriteThread(void * data)
2144 {
2145    for (;;)
2146    {
2147       if (vidsoft_thread_context.need_draw[TITAN_SPRITE])
2148       {
2149          vidsoft_thread_context.need_draw[TITAN_SPRITE] = 0;
2150          VidsoftDrawSprite(&vidsoft_thread_context.regs, sprite_window_mask, vdp1frontframebuffer, vidsoft_thread_context.ram, Vdp1Regs,vidsoft_thread_context.lines, vidsoft_thread_context.color_ram);
2151          vidsoft_thread_context.draw_finished[TITAN_SPRITE] = 1;
2152       }
2153       YabThreadSleep();
2154    }
2155 }
2156 
2157 //////////////////////////////////////////////////////////////////////////////
2158 
VIDSoftInit(void)2159 int VIDSoftInit(void)
2160 {
2161    int i;
2162 
2163    if (TitanInit() == -1)
2164       return -1;
2165 
2166    if ((dispbuffer = (pixel_t *)calloc(sizeof(pixel_t), 704 * 512)) == NULL)
2167       return -1;
2168 
2169    // Initialize VDP1 framebuffer 1
2170    if ((vdp1framebuffer[0] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL)
2171       return -1;
2172 
2173    // Initialize VDP1 framebuffer 2
2174    if ((vdp1framebuffer[1] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL)
2175       return -1;
2176 
2177    vdp1backframebuffer = vdp1framebuffer[0];
2178    vdp1frontframebuffer = vdp1framebuffer[1];
2179    rbg0width = vdp2width = 320;
2180    vdp2height = 224;
2181 
2182 #ifdef USE_OPENGL
2183    if (VideoUseGL)
2184       if (VIDSoftSetupGL() != 0)
2185          return -1;
2186 #endif
2187 
2188    for (i = 0; i < 6; i++)
2189    {
2190       vidsoft_thread_context.draw_finished[i] = 1;
2191       vidsoft_thread_context.need_draw[i] = 0;
2192    }
2193 
2194    vidsoft_vdp1_thread_context.need_draw = 0;
2195    vidsoft_vdp1_thread_context.draw_finished = 1;
2196    YabThreadStart(YAB_THREAD_VIDSOFT_VDP1, VidsoftVdp1Thread, 0);
2197 
2198    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_RBG0, VidsoftRbg0Thread, 0);
2199    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_NBG0, VidsoftNbg0Thread, 0);
2200    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_NBG1, VidsoftNbg1Thread, 0);
2201    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_NBG2, VidsoftNbg2Thread, 0);
2202    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_NBG3, VidsoftNbg3Thread, 0);
2203    YabThreadStart(YAB_THREAD_VIDSOFT_LAYER_SPRITE, VidsoftSpriteThread, 0);
2204 
2205    return 0;
2206 }
2207 
2208 //////////////////////////////////////////////////////////////////////////////
2209 
VIDSoftSetBilinear(int b)2210 void VIDSoftSetBilinear(int b)
2211 {
2212    bilinear = b;
2213 }
2214 
2215 //////////////////////////////////////////////////////////////////////////////
2216 
VIDSoftSetupGL(void)2217 int VIDSoftSetupGL(void)
2218 {
2219 #ifdef USE_OPENGL
2220    GLint status;
2221    GLint texAttrib;
2222    GLint posAttrib;
2223 
2224    // Shader sources
2225    const GLchar* vshader_src =
2226       "#version 330 core\n"
2227       "in vec2 position;"
2228       "in vec2 texcoord;"
2229       "out vec2 outcoord;"
2230       "void main() {"
2231       "   outcoord = texcoord;"
2232       "   gl_Position = vec4(position, 0.0, 1.0);"
2233       "}";
2234 
2235    const GLchar* fshader_src =
2236       "#version 330 core\n"
2237       "in vec2 outcoord;"
2238       "out vec4 fragcolor;"
2239       "uniform sampler2D sattex;"
2240       "void main() {"
2241       "   fragcolor = texture(sattex, outcoord);"
2242       "}";
2243 
2244    const float vertices[16] = {
2245       -1.0f, -1.0f, // Vertex 1 (X, Y)
2246       -1.0f, 1.0f,  // Vertex 2 (X, Y)
2247       1.0f, -1.0f,  // Vertex 3 (X, Y)
2248       1.0f, 1.0f,   // Vertex 4 (X, Y)
2249       0.0, 1.0,     // Texture 1 (X, Y)
2250       0.0, 0.0,     // Texture 2 (X, Y)
2251       1.0, 1.0,     // Texture 3 (X, Y)
2252       1.0, 0.0      // Texture 4 (X, Y)
2253    };
2254 
2255    outputwidth = vdp2width;
2256    outputheight = vdp2height;
2257 
2258    glewInit();
2259 
2260    glGenVertexArrays(1, &vao);
2261    glBindVertexArray(vao);
2262 
2263    glGenBuffers(1, &vbo);
2264    glBindBuffer(GL_ARRAY_BUFFER, vbo);
2265    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
2266 
2267    vshader = glCreateShader(GL_VERTEX_SHADER);
2268    glShaderSource(vshader, 1, &vshader_src, NULL);
2269    glCompileShader(vshader);
2270 
2271    glGetShaderiv(vshader, GL_COMPILE_STATUS, &status);
2272    if (status == GL_FALSE)
2273    {
2274       YGLLOG("Failed to compile vertex shader\n");
2275       return -1;
2276    }
2277 
2278    fshader = glCreateShader(GL_FRAGMENT_SHADER);
2279    glShaderSource(fshader, 1, &fshader_src, NULL);
2280    glCompileShader(fshader);
2281 
2282    glGetShaderiv(fshader, GL_COMPILE_STATUS, &status);
2283    if (status == GL_FALSE)
2284    {
2285       YGLLOG("Failed to compile fragment shader\n");
2286       return -1;
2287    }
2288 
2289    gl_shader_prog = glCreateProgram();
2290    glAttachShader(gl_shader_prog, vshader);
2291    glAttachShader(gl_shader_prog, fshader);
2292 
2293    glLinkProgram(gl_shader_prog);
2294 
2295    glValidateProgram(gl_shader_prog);
2296    glGetProgramiv(gl_shader_prog, GL_LINK_STATUS, &status);
2297    if (status == GL_FALSE)
2298    {
2299       YGLLOG("Failed to link shader program\n");
2300       return -1;
2301    }
2302 
2303    glUseProgram(gl_shader_prog);
2304 
2305    posAttrib = glGetAttribLocation(gl_shader_prog, "position");
2306    glEnableVertexAttribArray(posAttrib);
2307    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
2308 
2309    texAttrib = glGetAttribLocation(gl_shader_prog, "texcoord");
2310    glEnableVertexAttribArray(texAttrib);
2311    glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, (void*)(8 * sizeof(GLfloat)));
2312 
2313    glGenTextures(1, &gl_texture_id);
2314    glActiveTexture(GL_TEXTURE0);
2315    glBindTexture(GL_TEXTURE_2D, gl_texture_id);
2316 
2317    if (bilinear) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); }
2318    else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); }
2319    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2320 
2321    glViewport(0, 0, outputwidth, outputheight);
2322 
2323    glUniform1i(glGetUniformLocation(gl_shader_prog, "sattex"), 0);
2324 #endif
2325    return 0;
2326 }
2327 
2328 //////////////////////////////////////////////////////////////////////////////
2329 
VIDSoftDeInit(void)2330 void VIDSoftDeInit(void)
2331 {
2332    if (dispbuffer)
2333    {
2334       free(dispbuffer);
2335       dispbuffer = NULL;
2336    }
2337 
2338    if (vdp1framebuffer[0])
2339       free(vdp1framebuffer[0]);
2340 
2341    if (vdp1framebuffer[1])
2342       free(vdp1framebuffer[1]);
2343 #ifdef USE_OPENGL
2344    if (VideoUseGL)
2345    {
2346       if (gl_texture_id) { glDeleteTextures(1, &gl_texture_id); }
2347       if (gl_shader_prog) { glDeleteProgram(gl_shader_prog); }
2348       if (vshader) { glDeleteShader(vshader); }
2349       if (fshader) { glDeleteShader(fshader); }
2350       if (vao) { glDeleteVertexArrays(1, &vao); }
2351       if (vbo) { glDeleteBuffers(1, &vbo); }
2352    }
2353 #endif
2354 }
2355 
2356 //////////////////////////////////////////////////////////////////////////////
2357 
2358 static int IsFullscreen = 0;
2359 
VIDSoftResize(unsigned int w,unsigned int h,int on)2360 void VIDSoftResize(unsigned int w, unsigned int h, int on)
2361 {
2362 #ifdef USE_OPENGL
2363    if (VideoUseGL)
2364    {
2365       IsFullscreen = on;
2366       glClear(GL_COLOR_BUFFER_BIT);
2367       glViewport(0, 0, w, h);
2368       outputwidth = w;
2369       outputheight = h;
2370    }
2371 #endif
2372 }
2373 
2374 //////////////////////////////////////////////////////////////////////////////
2375 
VIDSoftIsFullscreen(void)2376 int VIDSoftIsFullscreen(void) {
2377    return IsFullscreen;
2378 }
2379 
2380 //////////////////////////////////////////////////////////////////////////////
2381 
VIDSoftVdp1Reset(void)2382 int VIDSoftVdp1Reset(void)
2383 {
2384    Vdp1Regs->userclipX1 = Vdp1Regs->systemclipX1 = 0;
2385    Vdp1Regs->userclipY1 = Vdp1Regs->systemclipY1 = 0;
2386    Vdp1Regs->userclipX2 = Vdp1Regs->systemclipX2 = 512;
2387    Vdp1Regs->userclipY2 = Vdp1Regs->systemclipY2 = 256;
2388 
2389    return 0;
2390 }
2391 
2392 //////////////////////////////////////////////////////////////////////////////
2393 
VIDSoftVdp1DrawStartBody(Vdp1 * regs,u8 * back_framebuffer)2394 void VIDSoftVdp1DrawStartBody(Vdp1* regs, u8 * back_framebuffer)
2395 {
2396    if (regs->FBCR & 8)
2397       vdp1interlace = 2;
2398    else
2399       vdp1interlace = 1;
2400    if (regs->TVMR & 0x1)
2401    {
2402       if (regs->TVMR & 0x2)
2403       {
2404          // Rotation 8-bit
2405          vdp1width = 512;
2406          vdp1height = 512;
2407       }
2408       else
2409       {
2410          // Normal 8-bit
2411          vdp1width = 1024;
2412          vdp1height = 256;
2413       }
2414 
2415       vdp1pixelsize = 1;
2416    }
2417    else
2418    {
2419       // Rotation/Normal 16-bit
2420       vdp1width = 512;
2421       vdp1height = 256;
2422       vdp1pixelsize = 2;
2423    }
2424 
2425    VIDSoftVdp1EraseFrameBuffer(regs, back_framebuffer);
2426 
2427    //night warriors doesn't set clipping most frames and uses
2428    //the last part of the vdp1 framebuffer as scratch ram
2429    //the previously set clipping values need to be reused
2430 }
2431 //////////////////////////////////////////////////////////////////////////////
2432 
VIDSoftVdp1DrawStart()2433 void VIDSoftVdp1DrawStart()
2434 {
2435    if (vidsoft_vdp1_thread_enabled)
2436    {
2437       VidsoftWaitForVdp1Thread();
2438 
2439       //take a snapshot of the vdp1 state, to be used by the thread
2440       memcpy(vidsoft_vdp1_thread_context.ram, Vdp1Ram, 0x80000);
2441       memcpy(&vidsoft_vdp1_thread_context.regs, Vdp1Regs, sizeof(Vdp1));
2442       memcpy(vidsoft_vdp1_thread_context.back_framebuffer, vdp1backframebuffer, 0x40000);
2443 
2444       VIDSoftVdp1DrawStartBody(&vidsoft_vdp1_thread_context.regs, vidsoft_vdp1_thread_context.back_framebuffer);
2445 
2446       //start thread
2447       vidsoft_vdp1_thread_context.draw_finished = 0;
2448       vidsoft_vdp1_thread_context.need_draw = 1;
2449       YabThreadWake(YAB_THREAD_VIDSOFT_VDP1);
2450 
2451       Vdp1FakeDrawCommands(Vdp1Ram, Vdp1Regs);
2452    }
2453    else
2454    {
2455       VIDSoftVdp1DrawStartBody(Vdp1Regs, vdp1backframebuffer);
2456       Vdp1DrawCommands(Vdp1Ram, Vdp1Regs, vdp1backframebuffer);
2457    }
2458 }
2459 
2460 //////////////////////////////////////////////////////////////////////////////
2461 
VIDSoftVdp1DrawEnd(void)2462 void VIDSoftVdp1DrawEnd(void)
2463 {
2464 }
2465 
2466 //////////////////////////////////////////////////////////////////////////////
2467 
Vdp1ReadPattern16(u32 base,u32 offset,u8 * ram)2468 static INLINE u16  Vdp1ReadPattern16( u32 base, u32 offset , u8 * ram) {
2469 
2470    u16 dot = T1ReadByte(ram, (base + (offset >> 1)) & 0x7FFFF);
2471   if ((offset & 0x1) == 0) dot >>= 4; // Even pixel
2472   else dot &= 0xF; // Odd pixel
2473   return dot;
2474 }
2475 
Vdp1ReadPattern64(u32 base,u32 offset,u8 * ram)2476 static INLINE u16  Vdp1ReadPattern64(u32 base, u32 offset, u8 * ram) {
2477 
2478    return T1ReadByte(ram, (base + offset) & 0x7FFFF) & 0x3F;
2479 }
2480 
Vdp1ReadPattern128(u32 base,u32 offset,u8 * ram)2481 static INLINE u16  Vdp1ReadPattern128(u32 base, u32 offset, u8 * ram) {
2482 
2483    return T1ReadByte(ram, (base + offset) & 0x7FFFF) & 0x7F;
2484 }
2485 
Vdp1ReadPattern256(u32 base,u32 offset,u8 * ram)2486 static INLINE u16  Vdp1ReadPattern256(u32 base, u32 offset, u8 * ram) {
2487 
2488    return T1ReadByte(ram, (base + offset) & 0x7FFFF) & 0xFF;
2489 }
2490 
Vdp1ReadPattern64k(u32 base,u32 offset,u8 * ram)2491 static INLINE u16  Vdp1ReadPattern64k(u32 base, u32 offset, u8 * ram) {
2492 
2493   return T1ReadWord(ram, ( base + 2*offset) & 0x7FFFF);
2494 }
2495 
2496 ////////////////////////////////////////////////////////////////////////////////
2497 
alphablend16(u32 d,u32 s,u32 level)2498 static INLINE u32 alphablend16(u32 d, u32 s, u32 level)
2499 {
2500 	int r,g,b,sr,sg,sb,dr,dg,db;
2501 
2502 	int invlevel = 256-level;
2503 	sr = s & 0x001f; dr = d & 0x001f;
2504 	r = (sr*level + dr*invlevel)>>8; r&= 0x1f;
2505 	sg = s & 0x03e0; dg = d & 0x03e0;
2506 	g = (sg*level + dg*invlevel)>>8; g&= 0x03e0;
2507 	sb = s & 0x7c00; db = d & 0x7c00;
2508 	b = (sb*level + db*invlevel)>>8; b&= 0x7c00;
2509 	return r|g|b;
2510 }
2511 
2512 typedef struct _COLOR_PARAMS
2513 {
2514 	double r,g,b;
2515 } COLOR_PARAMS;
2516 
2517 COLOR_PARAMS leftColumnColor;
2518 
2519 
2520 
2521 int currentPixel;
2522 int currentPixelIsVisible;
2523 int characterWidth;
2524 int characterHeight;
2525 
getpixel(int linenumber,int currentlineindex,vdp1cmd_struct * cmd,u8 * ram)2526 static int getpixel(int linenumber, int currentlineindex, vdp1cmd_struct *cmd, u8 * ram) {
2527 
2528 	u32 characterAddress;
2529 	u32 colorlut;
2530 	u16 colorbank;
2531 	u8 SPD;
2532 	int endcode;
2533 	int endcodesEnabled;
2534 	int untexturedColor = 0;
2535 	int isTextured = 1;
2536 	int currentShape = cmd->CMDCTRL & 0x7;
2537 	int flip;
2538 
2539    characterAddress = cmd->CMDSRCA << 3;
2540    colorbank = cmd->CMDCOLR;
2541 	colorlut = (u32)colorbank << 3;
2542    SPD = ((cmd->CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent)
2543    endcodesEnabled = ((cmd->CMDPMOD & 0x80) == 0) ? 1 : 0;
2544    flip = (cmd->CMDCTRL & 0x30) >> 4;
2545 
2546 	//4 polygon, 5 polyline or 6 line
2547 	if(currentShape == 4 || currentShape == 5 || currentShape == 6) {
2548 		isTextured = 0;
2549       untexturedColor = cmd->CMDCOLR;
2550 	}
2551 
2552 	switch( flip ) {
2553 		case 1:
2554 			// Horizontal flipping
2555 			currentlineindex = characterWidth - currentlineindex-1;
2556 			break;
2557 		case 2:
2558 			// Vertical flipping
2559 			linenumber = characterHeight - linenumber-1;
2560 
2561 			break;
2562 		case 3:
2563 			// Horizontal/Vertical flipping
2564 			linenumber = characterHeight - linenumber-1;
2565 			currentlineindex = characterWidth - currentlineindex-1;
2566 			break;
2567 	}
2568 
2569    switch ((cmd->CMDPMOD >> 3) & 0x7)
2570 	{
2571 		case 0x0: //4bpp bank
2572 			endcode = 0xf;
2573 			currentPixel = Vdp1ReadPattern16( characterAddress + (linenumber*(characterWidth>>1)), currentlineindex , ram);
2574 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2575 				return 1;
2576 			if (!((currentPixel == 0) && !SPD))
2577 				currentPixel = (colorbank &0xfff0)| currentPixel;
2578 			currentPixelIsVisible = 0xf;
2579 			break;
2580 
2581 		case 0x1://4bpp lut
2582 			endcode = 0xf;
2583          currentPixel = Vdp1ReadPattern16(characterAddress + (linenumber*(characterWidth >> 1)), currentlineindex, ram);
2584 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2585 				return 1;
2586 			if (!(currentPixel == 0 && !SPD))
2587 				currentPixel = T1ReadWord(ram, (currentPixel * 2 + colorlut) & 0x7FFFF);
2588 			currentPixelIsVisible = 0xffff;
2589 			break;
2590 		case 0x2://8pp bank (64 color)
2591 			//is there a hardware bug with endcodes in this color mode?
2592 			//there are white lines around some characters in scud
2593 			//using an endcode of 63 eliminates the white lines
2594 			//but also causes some dropout due to endcodes being triggered that aren't triggered on hardware
2595 			//the closest thing i can do to match the hardware is make all pixels with color index 63 transparent
2596 			//this needs more hardware testing
2597 
2598 			endcode = 63;
2599          currentPixel = Vdp1ReadPattern64(characterAddress + (linenumber*(characterWidth)), currentlineindex, ram);
2600 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2601 				currentPixel = 0;
2602 		//		return 1;
2603 			if (!((currentPixel == 0) && !SPD))
2604 				currentPixel = (colorbank&0xffc0) | currentPixel;
2605 			currentPixelIsVisible = 0x3f;
2606 			break;
2607 		case 0x3://128 color
2608 			endcode = 0xff;
2609          currentPixel = Vdp1ReadPattern128(characterAddress + (linenumber*characterWidth), currentlineindex, ram);
2610 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2611 				return 1;
2612 			if (!((currentPixel == 0) && !SPD))
2613 				currentPixel = (colorbank&0xff80) | currentPixel;//dead or alive needs colorbank to be masked
2614 			currentPixelIsVisible = 0x7f;
2615 			break;
2616 		case 0x4://256 color
2617 			endcode = 0xff;
2618          currentPixel = Vdp1ReadPattern256(characterAddress + (linenumber*characterWidth), currentlineindex, ram);
2619 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2620 				return 1;
2621 			currentPixelIsVisible = 0xff;
2622 			if (!((currentPixel == 0) && !SPD))
2623 				currentPixel = (colorbank&0xff00) | currentPixel;
2624 			break;
2625 		case 0x5://16bpp bank
2626 		case 0x6://prohibited, used by (at least) Beach de Reach and seems to behave like 0x5
2627 			endcode = 0x7fff;
2628          currentPixel = Vdp1ReadPattern64k(characterAddress + (linenumber*characterWidth * 2), currentlineindex, ram);
2629 			if(isTextured && endcodesEnabled && currentPixel == endcode)
2630 				return 1;
2631 
2632 			/* the transparent pixel in 16bpp is supposed to be 0x0000
2633 			but some games use pixels with invalid values and expect
2634 			them to be transparent (see vdp1 doc p. 92) */
2635 			if (!(currentPixel & 0x8000) && !SPD)
2636 				currentPixel = 0;
2637 
2638 			currentPixelIsVisible = 0xffff;
2639 			break;
2640 	}
2641 
2642 	if(!isTextured)
2643 		currentPixel = untexturedColor;
2644 
2645 	//force the MSB to be on if MSBON is set
2646 	//currentPixel |= cmd.CMDPMOD & (1 << 15);
2647 
2648 	return 0;
2649 }
2650 
gouraudAdjust(int color,int tableValue)2651 static int gouraudAdjust( int color, int tableValue )
2652 {
2653 	color += (tableValue - 0x10);
2654 
2655 	if ( color < 0 ) color = 0;
2656 	if ( color > 0x1f ) color = 0x1f;
2657 
2658 	return color;
2659 }
2660 
2661 
CheckDil(int y,Vdp1 * regs)2662 static int CheckDil(int y, Vdp1 * regs)
2663 {
2664    int dil = (regs->FBCR >> 2) & 1;
2665 
2666    if (vdp1interlace == 2)
2667    {
2668       if (dil)
2669       {
2670          if ((y & 1) == 0)
2671             return 1;
2672       }
2673       else
2674       {
2675          if ((y & 1))
2676             return 1;
2677       }
2678    }
2679 
2680    return 0;
2681 }
2682 
IsUserClipped(int x,int y,Vdp1 * regs)2683 static INLINE int IsUserClipped(int x, int y, Vdp1* regs)
2684 {
2685    return !(x >= regs->userclipX1 &&
2686       x <= regs->userclipX2 &&
2687       y >= regs->userclipY1 &&
2688       y <= regs->userclipY2);
2689 }
2690 
IsSystemClipped(int x,int y,Vdp1 * regs)2691 static INLINE int IsSystemClipped(int x, int y, Vdp1* regs)
2692 {
2693    return !(x >= 0 &&
2694       x <= regs->systemclipX2 &&
2695       y >= 0 &&
2696       y <= regs->systemclipY2);
2697 }
2698 
IsClipped(int x,int y,Vdp1 * regs,vdp1cmd_struct * cmd)2699 int IsClipped(int x, int y, Vdp1* regs, vdp1cmd_struct * cmd)
2700 {
2701    if (cmd->CMDPMOD & 0x0400)//user clipping enabled
2702    {
2703       int is_user_clipped = IsUserClipped(x, y, regs);
2704 
2705       if (((cmd->CMDPMOD >> 9) & 0x3) == 0x3)//outside clipping mode
2706          is_user_clipped = !is_user_clipped;
2707 
2708       return is_user_clipped || IsSystemClipped(x, y, regs);
2709    }
2710    else
2711    {
2712       return IsSystemClipped(x, y, regs);
2713    }
2714 }
2715 
putpixel8(int x,int y,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * back_framebuffer)2716 static void putpixel8(int x, int y, Vdp1 * regs, vdp1cmd_struct *cmd, u8 * back_framebuffer) {
2717 
2718     int y2 = y / vdp1interlace;
2719     u8 * iPix = &back_framebuffer[(y2 * vdp1width) + x];
2720     int mesh = cmd->CMDPMOD & 0x0100;
2721     int SPD = ((cmd->CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent)
2722 
2723     if (iPix >= (back_framebuffer + 0x40000))
2724         return;
2725 
2726     if (CheckDil(y, regs))
2727        return;
2728 
2729     currentPixel &= 0xFF;
2730 
2731     if (mesh && ((x ^ y2) & 1)) {
2732        return;
2733     }
2734 
2735     if (IsClipped(x, y, regs, cmd))
2736        return;
2737 
2738     if ( SPD || (currentPixel & currentPixelIsVisible))
2739     {
2740         switch( cmd->CMDPMOD & 0x7 )//we want bits 0,1,2
2741         {
2742         default:
2743         case 0:	// replace
2744             if (!((currentPixel == 0) && !SPD))
2745                 *(iPix) = currentPixel;
2746             break;
2747         }
2748     }
2749 }
2750 
putpixel(int x,int y,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * back_framebuffer)2751 static void putpixel(int x, int y, Vdp1* regs, vdp1cmd_struct * cmd, u8 * back_framebuffer) {
2752 
2753 	u16* iPix;
2754 	int mesh = cmd->CMDPMOD & 0x0100;
2755 	int SPD = ((cmd->CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent)
2756    int original_y = y;
2757 
2758    if (CheckDil(y, regs))
2759       return;
2760 
2761 	y /= vdp1interlace;
2762    iPix = &((u16 *)back_framebuffer)[(y * vdp1width) + x];
2763 
2764    if (iPix >= (u16*)(back_framebuffer + 0x40000))
2765 		return;
2766 
2767 	if(mesh && (x^y)&1)
2768 		return;
2769 
2770    if (IsClipped(x, original_y, regs, cmd))
2771       return;
2772 
2773 	if (cmd->CMDPMOD & (1 << 15))
2774 	{
2775 		if (currentPixel) {
2776 			*iPix |= 0x8000;
2777 			return;
2778 		}
2779 	}
2780 
2781 	if ( SPD || (currentPixel & currentPixelIsVisible))
2782 	{
2783 		switch( cmd->CMDPMOD & 0x7 )//we want bits 0,1,2
2784 		{
2785 		case 0:	// replace
2786 			if (!((currentPixel == 0) && !SPD))
2787 				*(iPix) = currentPixel;
2788 			break;
2789 		case 1: // shadow
2790 			if (*(iPix) & (1 << 15)) // only if MSB of framebuffer data is set
2791 				*(iPix) = alphablend16(*(iPix), 0, (1 << 7)) | (1 << 15);
2792 			break;
2793 		case 2: // half luminance
2794 			*(iPix) = ((currentPixel & ~0x8421) >> 1) | (1 << 15);
2795 			break;
2796 		case 3: // half transparent
2797 			if ( *(iPix) & (1 << 15) )//only if MSB of framebuffer data is set
2798 				*(iPix) = alphablend16( *(iPix), currentPixel, (1 << 7) ) | (1 << 15);
2799 			else
2800 				*(iPix) = currentPixel;
2801 			break;
2802 		case 4: //gouraud
2803 			#define COLOR(r,g,b)    (((r)&0x1F)|(((g)&0x1F)<<5)|(((b)&0x1F)<<10) |0x8000 )
2804 
2805 			//handle the special case demonstrated in the sgl chrome demo
2806 			//if we are in a paletted bank mode and the other two colors are unused, adjust the index value instead of rgb
2807 			if(
2808 				(((cmd->CMDPMOD >> 3) & 0x7) != 5) &&
2809 				(((cmd->CMDPMOD >> 3) & 0x7) != 1) &&
2810 				(int)leftColumnColor.g == 16 &&
2811 				(int)leftColumnColor.b == 16)
2812 			{
2813 				int c = (int)(leftColumnColor.r-0x10);
2814 				if(c < 0) c = 0;
2815 				currentPixel = currentPixel+c;
2816 				*(iPix) = currentPixel;
2817 				break;
2818 			}
2819 			*(iPix) = COLOR(
2820 				gouraudAdjust(
2821 				currentPixel&0x001F,
2822 				(int)leftColumnColor.r),
2823 
2824 				gouraudAdjust(
2825 				(currentPixel&0x03e0) >> 5,
2826 				(int)leftColumnColor.g),
2827 
2828 				gouraudAdjust(
2829 				(currentPixel&0x7c00) >> 10,
2830 				(int)leftColumnColor.b)
2831 				);
2832 			break;
2833 		default:
2834 			*(iPix) = alphablend16( COLOR((int)leftColumnColor.r,(int)leftColumnColor.g, (int)leftColumnColor.b), currentPixel, (1 << 7) ) | (1 << 15);
2835 			break;
2836 		}
2837 	}
2838 }
2839 
iterateOverLine(int x1,int y1,int x2,int y2,int greedy,void * data,int (* line_callback)(int x,int y,int i,void * data,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * ram,u8 * back_framebuffer),Vdp1 * regs,vdp1cmd_struct * cmd,u8 * ram,u8 * back_framebuffer)2840 static int iterateOverLine(int x1, int y1, int x2, int y2, int greedy, void *data,
2841    int(*line_callback)(int x, int y, int i, void *data, Vdp1* regs, vdp1cmd_struct * cmd, u8* ram, u8* back_framebuffer), Vdp1* regs, vdp1cmd_struct * cmd, u8 * ram, u8* back_framebuffer) {
2842 	int i, a, ax, ay, dx, dy;
2843 
2844 	a = i = 0;
2845 	dx = x2 - x1;
2846 	dy = y2 - y1;
2847 	ax = (dx >= 0) ? 1 : -1;
2848 	ay = (dy >= 0) ? 1 : -1;
2849 
2850 	//burning rangers tries to draw huge shapes
2851 	//this will at least let it run
2852 	if(abs(dx) > 999 || abs(dy) > 999)
2853 		return INT_MAX;
2854 
2855 	if (abs(dx) > abs(dy)) {
2856 		if (ax != ay) dx = -dx;
2857 
2858 		for (; x1 != x2; x1 += ax, i++) {
2859          if (line_callback && line_callback(x1, y1, i, data, regs, cmd, ram, back_framebuffer) != 0) return i + 1;
2860 
2861 			a += dy;
2862 			if (abs(a) >= abs(dx)) {
2863 				a -= dx;
2864 				y1 += ay;
2865 
2866 				// Make sure we 'fill holes' the same as the Saturn
2867 				if (greedy) {
2868 					i ++;
2869 					if (ax == ay) {
2870 						if (line_callback &&
2871                      line_callback(x1 + ax, y1 - ay, i, data, regs, cmd, ram, back_framebuffer) != 0)
2872 							return i + 1;
2873 					} else {
2874 						if (line_callback &&
2875                      line_callback(x1, y1, i, data, regs, cmd, ram, back_framebuffer) != 0)
2876 							return i + 1;
2877 					}
2878 				}
2879 			}
2880 		}
2881 
2882 		// If the line isn't greedy here, we end up with gaps that don't occur on the Saturn
2883 		if (/*(i == 0) || (y1 != y2)*/1) {
2884          if (line_callback) line_callback(x2, y2, i, data, regs, cmd, ram, back_framebuffer);
2885 			i ++;
2886 		}
2887 	} else {
2888 		if (ax != ay) dy = -dy;
2889 
2890 		for (; y1 != y2; y1 += ay, i++) {
2891          if (line_callback && line_callback(x1, y1, i, data, regs, cmd, ram, back_framebuffer) != 0) return i + 1;
2892 
2893 			a += dx;
2894 			if (abs(a) >= abs(dy)) {
2895 				a -= dy;
2896 				x1 += ax;
2897 
2898 				if (greedy) {
2899 					i ++;
2900 					if (ay == ax) {
2901 						if (line_callback &&
2902                      line_callback(x1, y1, i, data, regs, cmd, ram, back_framebuffer) != 0)
2903 							return i + 1;
2904 					} else {
2905 						if (line_callback &&
2906                      line_callback(x1 - ax, y1 + ay, i, data, regs, cmd, ram, back_framebuffer) != 0)
2907 							return i + 1;
2908 					}
2909 				}
2910 			}
2911 		}
2912 
2913 		if (/*(i == 0) || (y1 != y2)*/1) {
2914          if (line_callback) line_callback(x2, y2, i, data, regs, cmd, ram, back_framebuffer);
2915 			i ++;
2916 		}
2917 	}
2918 
2919 	return i;
2920 }
2921 
2922 typedef struct {
2923 	double linenumber;
2924 	double texturestep;
2925 	double xredstep;
2926 	double xgreenstep;
2927 	double xbluestep;
2928 	int endcodesdetected;
2929 	int previousStep;
2930 } DrawLineData;
2931 
DrawLineCallback(int x,int y,int i,void * data,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * ram,u8 * back_framebuffer)2932 static int DrawLineCallback(int x, int y, int i, void *data, Vdp1* regs, vdp1cmd_struct * cmd, u8* ram, u8* back_framebuffer)
2933 {
2934 	int currentStep;
2935 	DrawLineData *linedata = data;
2936 
2937 	leftColumnColor.r += linedata->xredstep;
2938 	leftColumnColor.g += linedata->xgreenstep;
2939 	leftColumnColor.b += linedata->xbluestep;
2940 
2941 	currentStep = (int)i * linedata->texturestep;
2942 	if (getpixel(linedata->linenumber, currentStep, cmd, ram)) {
2943 		if (currentStep != linedata->previousStep) {
2944 			linedata->previousStep = currentStep;
2945 			linedata->endcodesdetected ++;
2946 		}
2947 	} else if (vdp1pixelsize == 2) {
2948 		putpixel(x, y, regs, cmd, back_framebuffer);
2949 	} else {
2950       putpixel8(x, y, regs, cmd, back_framebuffer);
2951     }
2952 
2953 	if (linedata->endcodesdetected == 2) return -1;
2954 
2955 	return 0;
2956 }
2957 
DrawLine(int x1,int y1,int x2,int y2,int greedy,double linenumber,double texturestep,double xredstep,double xgreenstep,double xbluestep,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * ram,u8 * back_framebuffer)2958 static int DrawLine(int x1, int y1, int x2, int y2, int greedy, double linenumber, double texturestep, double xredstep, double xgreenstep, double xbluestep, Vdp1* regs, vdp1cmd_struct *cmd, u8 * ram, u8* back_framebuffer)
2959 {
2960 	DrawLineData data;
2961 
2962 	data.linenumber = linenumber;
2963 	data.texturestep = texturestep;
2964 	data.xredstep = xredstep;
2965 	data.xgreenstep = xgreenstep;
2966 	data.xbluestep = xbluestep;
2967 	data.endcodesdetected = 0;
2968 	data.previousStep = 123456789;
2969 
2970    return iterateOverLine(x1, y1, x2, y2, greedy, &data, DrawLineCallback, regs, cmd, ram, back_framebuffer);
2971 }
2972 
interpolate(double start,double end,int numberofsteps)2973 static INLINE double interpolate(double start, double end, int numberofsteps) {
2974 
2975 	double stepvalue = 0;
2976 
2977 	if(numberofsteps == 0)
2978 		return 1;
2979 
2980 	stepvalue = (end - start) / numberofsteps;
2981 
2982 	return stepvalue;
2983 }
2984 
2985 typedef union _COLOR { // xbgr x555
2986 	struct {
2987 #ifdef WORDS_BIGENDIAN
2988 	u16 x:1;
2989 	u16 b:5;
2990 	u16 g:5;
2991 	u16 r:5;
2992 #else
2993      u16 r:5;
2994      u16 g:5;
2995      u16 b:5;
2996      u16 x:1;
2997 #endif
2998 	};
2999 	u16 value;
3000 } COLOR;
3001 
3002 COLOR gouraudA;
3003 COLOR gouraudB;
3004 COLOR gouraudC;
3005 COLOR gouraudD;
3006 
gouraudTable(u8 * ram,Vdp1 * regs,vdp1cmd_struct * cmd)3007 static void gouraudTable(u8* ram, Vdp1* regs, vdp1cmd_struct * cmd)
3008 {
3009 	int gouraudTableAddress;
3010 
3011 
3012 
3013 	gouraudTableAddress = (((unsigned int)cmd->CMDGRDA) << 3);
3014 
3015    gouraudA.value = T1ReadWord(ram, gouraudTableAddress);
3016    gouraudB.value = T1ReadWord(ram, gouraudTableAddress + 2);
3017    gouraudC.value = T1ReadWord(ram, gouraudTableAddress + 4);
3018    gouraudD.value = T1ReadWord(ram, gouraudTableAddress + 6);
3019 }
3020 
3021 int xleft[1000];
3022 int yleft[1000];
3023 int xright[1000];
3024 int yright[1000];
3025 
3026 static int
storeLineCoords(int x,int y,int i,void * arrays,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * ram,u8 * back_framebuffer)3027 storeLineCoords(int x, int y, int i, void *arrays, Vdp1* regs, vdp1cmd_struct * cmd, u8* ram, u8* back_framebuffer) {
3028 	int **intArrays = arrays;
3029 
3030 	intArrays[0][i] = x;
3031 	intArrays[1][i] = y;
3032 
3033 	return 0;
3034 }
3035 
3036 //skip objects that are completely outside of system clipping
is_pre_clipped(s16 tl_x,s16 tl_y,s16 bl_x,s16 bl_y,s16 tr_x,s16 tr_y,s16 br_x,s16 br_y,Vdp1 * regs)3037 int is_pre_clipped(s16 tl_x, s16 tl_y, s16 bl_x, s16 bl_y, s16 tr_x, s16 tr_y, s16 br_x, s16 br_y, Vdp1* regs)
3038 {
3039    int y_val = regs->systemclipY2;
3040 
3041    if (vdp1interlace)
3042       y_val *= 2;
3043 
3044    //if all x values are to the left of the screen
3045    if ((tl_x < 0) &&
3046       (bl_x < 0) &&
3047       (tr_x < 0) &&
3048       (br_x < 0))
3049       return 1;
3050 
3051    //to the right
3052    if ((tl_x > regs->systemclipX2) &&
3053       (bl_x > regs->systemclipX2) &&
3054       (tr_x > regs->systemclipX2) &&
3055       (br_x > regs->systemclipX2))
3056       return 1;
3057 
3058    //above
3059    if ((tl_y < 0) &&
3060       (bl_y < 0) &&
3061       (tr_y < 0) &&
3062       (br_y < 0))
3063       return 1;
3064 
3065    //below
3066    if ((tl_y > y_val) &&
3067       (bl_y > y_val) &&
3068       (tr_y > y_val) &&
3069       (br_y > y_val))
3070       return 1;
3071 
3072    return 0;
3073 }
3074 
3075 //a real vdp1 draws with arbitrary lines
3076 //this is why endcodes are possible
3077 //this is also the reason why half-transparent shading causes moire patterns
3078 //and the reason why gouraud shading can be applied to a single line draw command
drawQuad(s16 tl_x,s16 tl_y,s16 bl_x,s16 bl_y,s16 tr_x,s16 tr_y,s16 br_x,s16 br_y,u8 * ram,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * back_framebuffer)3079 static void drawQuad(s16 tl_x, s16 tl_y, s16 bl_x, s16 bl_y, s16 tr_x, s16 tr_y, s16 br_x, s16 br_y, u8 * ram, Vdp1* regs, vdp1cmd_struct * cmd, u8* back_framebuffer){
3080 
3081 	int totalleft;
3082 	int totalright;
3083 	int total;
3084 	int i;
3085 	int *intarrays[2];
3086 
3087 	COLOR_PARAMS topLeftToBottomLeftColorStep = {0,0,0}, topRightToBottomRightColorStep = {0,0,0};
3088 
3089 	//how quickly we step through the line arrays
3090 	double leftLineStep = 1;
3091 	double rightLineStep = 1;
3092 
3093 	//a lookup table for the gouraud colors
3094 	COLOR colors[4];
3095 
3096    if (is_pre_clipped(tl_x, tl_y, bl_x, bl_y, tr_x, tr_y, br_x, br_y, regs))
3097       return;
3098 
3099 	characterWidth = ((cmd->CMDSIZE >> 8) & 0x3F) * 8;
3100    characterHeight = cmd->CMDSIZE & 0xFF;
3101 
3102 	intarrays[0] = xleft; intarrays[1] = yleft;
3103    totalleft = iterateOverLine(tl_x, tl_y, bl_x, bl_y, 0, intarrays, storeLineCoords, regs, cmd, ram, back_framebuffer);
3104 	intarrays[0] = xright; intarrays[1] = yright;
3105    totalright = iterateOverLine(tr_x, tr_y, br_x, br_y, 0, intarrays, storeLineCoords, regs, cmd, ram, back_framebuffer);
3106 
3107 	//just for now since burning rangers will freeze up trying to draw huge shapes
3108 	if(totalleft == INT_MAX || totalright == INT_MAX)
3109 		return;
3110 
3111 	total = totalleft > totalright ? totalleft : totalright;
3112 
3113 
3114    if (cmd->CMDPMOD & (1 << 2)) {
3115 
3116 		gouraudTable(ram, regs, cmd);
3117 
3118 		{ colors[0] = gouraudA; colors[1] = gouraudD; colors[2] = gouraudB; colors[3] = gouraudC; }
3119 
3120 		topLeftToBottomLeftColorStep.r = interpolate(colors[0].r,colors[1].r,total);
3121 		topLeftToBottomLeftColorStep.g = interpolate(colors[0].g,colors[1].g,total);
3122 		topLeftToBottomLeftColorStep.b = interpolate(colors[0].b,colors[1].b,total);
3123 
3124 		topRightToBottomRightColorStep.r = interpolate(colors[2].r,colors[3].r,total);
3125 		topRightToBottomRightColorStep.g = interpolate(colors[2].g,colors[3].g,total);
3126 		topRightToBottomRightColorStep.b = interpolate(colors[2].b,colors[3].b,total);
3127 	}
3128 
3129 	//we have to step the equivalent of less than one pixel on the shorter side
3130 	//to make sure textures stretch properly and the shape is correct
3131 	if(total == totalleft && totalleft != totalright) {
3132 		//left side is larger
3133 		leftLineStep = 1;
3134 		rightLineStep = (double)totalright / totalleft;
3135 	}
3136 	else if(totalleft != totalright){
3137 		//right side is larger
3138 		rightLineStep = 1;
3139 		leftLineStep = (double)totalleft / totalright;
3140 	}
3141 
3142 	for(i = 0; i < total; i++) {
3143 
3144 		int xlinelength;
3145 
3146 		double xtexturestep;
3147 		double ytexturestep;
3148 
3149 		COLOR_PARAMS rightColumnColor;
3150 
3151 		COLOR_PARAMS leftToRightStep = {0,0,0};
3152 
3153 		//get the length of the line we are about to draw
3154 		xlinelength = iterateOverLine(
3155 			xleft[(int)(i*leftLineStep)],
3156 			yleft[(int)(i*leftLineStep)],
3157 			xright[(int)(i*rightLineStep)],
3158 			yright[(int)(i*rightLineStep)],
3159          1, NULL, NULL, regs, cmd, ram, back_framebuffer);
3160 
3161 		//so from 0 to the width of the texture / the length of the line is how far we need to step
3162 		xtexturestep=interpolate(0,characterWidth,xlinelength);
3163 
3164 		//now we need to interpolate the y texture coordinate across multiple lines
3165 		ytexturestep=interpolate(0,characterHeight,total);
3166 
3167 		//gouraud interpolation
3168 		if(cmd->CMDPMOD & (1 << 2)) {
3169 
3170 			//for each new line we need to step once more through each column
3171 			//and add the orignal color + the number of steps taken times the step value to the bottom of the shape
3172 			//to get the current colors to use to interpolate across the line
3173 
3174 			leftColumnColor.r = colors[0].r +(topLeftToBottomLeftColorStep.r*i);
3175 			leftColumnColor.g = colors[0].g +(topLeftToBottomLeftColorStep.g*i);
3176 			leftColumnColor.b = colors[0].b +(topLeftToBottomLeftColorStep.b*i);
3177 
3178 			rightColumnColor.r = colors[2].r +(topRightToBottomRightColorStep.r*i);
3179 			rightColumnColor.g = colors[2].g +(topRightToBottomRightColorStep.g*i);
3180 			rightColumnColor.b = colors[2].b +(topRightToBottomRightColorStep.b*i);
3181 
3182 			//interpolate colors across to get the right step values
3183 			leftToRightStep.r = interpolate(leftColumnColor.r,rightColumnColor.r,xlinelength);
3184 			leftToRightStep.g = interpolate(leftColumnColor.g,rightColumnColor.g,xlinelength);
3185 			leftToRightStep.b = interpolate(leftColumnColor.b,rightColumnColor.b,xlinelength);
3186 		}
3187 
3188 		DrawLine(
3189 			xleft[(int)(i*leftLineStep)],
3190 			yleft[(int)(i*leftLineStep)],
3191 			xright[(int)(i*rightLineStep)],
3192 			yright[(int)(i*rightLineStep)],
3193 			1,
3194 			ytexturestep*i,
3195 			xtexturestep,
3196 			leftToRightStep.r,
3197 			leftToRightStep.g,
3198 			leftToRightStep.b,
3199          regs,
3200          cmd,
3201          ram, back_framebuffer
3202 			);
3203 	}
3204 }
3205 
VIDSoftVdp1NormalSpriteDraw(u8 * ram,Vdp1 * regs,u8 * back_framebuffer)3206 void VIDSoftVdp1NormalSpriteDraw(u8 * ram, Vdp1 * regs, u8 * back_framebuffer) {
3207 
3208 	s16 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty;
3209 	int spriteWidth;
3210 	int spriteHeight;
3211    vdp1cmd_struct cmd;
3212 	Vdp1ReadCommand(&cmd, regs->addr, ram);
3213 
3214 	topLeftx = cmd.CMDXA + regs->localX;
3215 	topLefty = cmd.CMDYA + regs->localY;
3216 	spriteWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8;
3217 	spriteHeight = cmd.CMDSIZE & 0xFF;
3218 
3219 	topRightx = topLeftx + (spriteWidth - 1);
3220 	topRighty = topLefty;
3221 	bottomRightx = topLeftx + (spriteWidth - 1);
3222 	bottomRighty = topLefty + (spriteHeight - 1);
3223 	bottomLeftx = topLeftx;
3224 	bottomLefty = topLefty + (spriteHeight - 1);
3225 
3226    drawQuad(topLeftx, topLefty, bottomLeftx, bottomLefty, topRightx, topRighty, bottomRightx, bottomRighty, ram, regs, &cmd, back_framebuffer);
3227 }
3228 
VIDSoftVdp1ScaledSpriteDraw(u8 * ram,Vdp1 * regs,u8 * back_framebuffer)3229 void VIDSoftVdp1ScaledSpriteDraw(u8* ram, Vdp1*regs, u8 * back_framebuffer){
3230 
3231 	s32 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty;
3232 	int x0,y0,x1,y1;
3233    vdp1cmd_struct cmd;
3234    Vdp1ReadCommand(&cmd, regs->addr, ram);
3235 
3236 	x0 = cmd.CMDXA + regs->localX;
3237 	y0 = cmd.CMDYA + regs->localY;
3238 
3239 	switch ((cmd.CMDCTRL >> 8) & 0xF)
3240 	{
3241 	case 0x0: // Only two coordinates
3242 	default:
3243 		x1 = ((int)cmd.CMDXC) - x0 + regs->localX + 1;
3244 		y1 = ((int)cmd.CMDYC) - y0 + regs->localY + 1;
3245 		break;
3246 	case 0x5: // Upper-left
3247 		x1 = ((int)cmd.CMDXB) + 1;
3248 		y1 = ((int)cmd.CMDYB) + 1;
3249 		break;
3250 	case 0x6: // Upper-Center
3251 		x1 = ((int)cmd.CMDXB);
3252 		y1 = ((int)cmd.CMDYB);
3253 		x0 = x0 - x1/2;
3254 		x1++;
3255 		y1++;
3256 		break;
3257 	case 0x7: // Upper-Right
3258 		x1 = ((int)cmd.CMDXB);
3259 		y1 = ((int)cmd.CMDYB);
3260 		x0 = x0 - x1;
3261 		x1++;
3262 		y1++;
3263 		break;
3264 	case 0x9: // Center-left
3265 		x1 = ((int)cmd.CMDXB);
3266 		y1 = ((int)cmd.CMDYB);
3267 		y0 = y0 - y1/2;
3268 		x1++;
3269 		y1++;
3270 		break;
3271 	case 0xA: // Center-center
3272 		x1 = ((int)cmd.CMDXB);
3273 		y1 = ((int)cmd.CMDYB);
3274 		x0 = x0 - x1/2;
3275 		y0 = y0 - y1/2;
3276 		x1++;
3277 		y1++;
3278 		break;
3279 	case 0xB: // Center-right
3280 		x1 = ((int)cmd.CMDXB);
3281 		y1 = ((int)cmd.CMDYB);
3282 		x0 = x0 - x1;
3283 		y0 = y0 - y1/2;
3284 		x1++;
3285 		y1++;
3286 		break;
3287 	case 0xD: // Lower-left
3288 		x1 = ((int)cmd.CMDXB);
3289 		y1 = ((int)cmd.CMDYB);
3290 		y0 = y0 - y1;
3291 		x1++;
3292 		y1++;
3293 		break;
3294 	case 0xE: // Lower-center
3295 		x1 = ((int)cmd.CMDXB);
3296 		y1 = ((int)cmd.CMDYB);
3297 		x0 = x0 - x1/2;
3298 		y0 = y0 - y1;
3299 		x1++;
3300 		y1++;
3301 		break;
3302 	case 0xF: // Lower-right
3303 		x1 = ((int)cmd.CMDXB);
3304 		y1 = ((int)cmd.CMDYB);
3305 		x0 = x0 - x1;
3306 		y0 = y0 - y1;
3307 		x1++;
3308 		y1++;
3309 		break;
3310 	}
3311 
3312 	topLeftx = x0;
3313 	topLefty = y0;
3314 
3315 	topRightx = x1+x0 - 1;
3316 	topRighty = topLefty;
3317 
3318 	bottomRightx = x1+x0 - 1;
3319 	bottomRighty = y1+y0 - 1;
3320 
3321 	bottomLeftx = topLeftx;
3322 	bottomLefty = y1+y0 - 1;
3323 
3324    drawQuad(topLeftx, topLefty, bottomLeftx, bottomLefty, topRightx, topRighty, bottomRightx, bottomRighty, ram, regs, &cmd, back_framebuffer);
3325 }
3326 
VIDSoftVdp1DistortedSpriteDraw(u8 * ram,Vdp1 * regs,u8 * back_framebuffer)3327 void VIDSoftVdp1DistortedSpriteDraw(u8* ram, Vdp1*regs, u8 * back_framebuffer) {
3328 
3329 	s32 xa,ya,xb,yb,xc,yc,xd,yd;
3330    vdp1cmd_struct cmd;
3331 
3332    Vdp1ReadCommand(&cmd, regs->addr, ram);
3333 
3334     xa = (s32)(cmd.CMDXA + regs->localX);
3335     ya = (s32)(cmd.CMDYA + regs->localY);
3336 
3337     xb = (s32)(cmd.CMDXB + regs->localX);
3338     yb = (s32)(cmd.CMDYB + regs->localY);
3339 
3340     xc = (s32)(cmd.CMDXC + regs->localX);
3341     yc = (s32)(cmd.CMDYC + regs->localY);
3342 
3343     xd = (s32)(cmd.CMDXD + regs->localX);
3344     yd = (s32)(cmd.CMDYD + regs->localY);
3345 
3346     drawQuad(xa, ya, xd, yd, xb, yb, xc, yc, ram, regs, &cmd, back_framebuffer);
3347 }
3348 
gouraudLineSetup(double * redstep,double * greenstep,double * bluestep,int length,COLOR table1,COLOR table2,u8 * ram,Vdp1 * regs,vdp1cmd_struct * cmd,u8 * back_framebuffer)3349 static void gouraudLineSetup(double * redstep, double * greenstep, double * bluestep, int length, COLOR table1, COLOR table2, u8* ram, Vdp1* regs, vdp1cmd_struct * cmd, u8 * back_framebuffer) {
3350 
3351 	gouraudTable(ram ,regs, cmd);
3352 
3353 	*redstep =interpolate(table1.r,table2.r,length);
3354 	*greenstep =interpolate(table1.g,table2.g,length);
3355 	*bluestep =interpolate(table1.b,table2.b,length);
3356 
3357 	leftColumnColor.r = table1.r;
3358 	leftColumnColor.g = table1.g;
3359 	leftColumnColor.b = table1.b;
3360 }
3361 
VIDSoftVdp1PolylineDraw(u8 * ram,Vdp1 * regs,u8 * back_framebuffer)3362 void VIDSoftVdp1PolylineDraw(u8* ram, Vdp1*regs, u8 * back_framebuffer)
3363 {
3364 	int X[4];
3365 	int Y[4];
3366 	double redstep = 0, greenstep = 0, bluestep = 0;
3367 	int length;
3368    vdp1cmd_struct cmd;
3369 
3370    Vdp1ReadCommand(&cmd, regs->addr, ram);
3371 
3372 	X[0] = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x0C));
3373 	Y[0] = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x0E));
3374 	X[1] = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x10));
3375 	Y[1] = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x12));
3376 	X[2] = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x14));
3377 	Y[2] = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x16));
3378 	X[3] = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x18));
3379 	Y[3] = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x1A));
3380 
3381    length = iterateOverLine(X[0], Y[0], X[1], Y[1], 1, NULL, NULL, regs, &cmd, ram, back_framebuffer);
3382    gouraudLineSetup(&redstep, &greenstep, &bluestep, length, gouraudA, gouraudB, ram, regs, &cmd, back_framebuffer);
3383    DrawLine(X[0], Y[0], X[1], Y[1], 0, 0, 0, redstep, greenstep, bluestep, regs, &cmd, ram, back_framebuffer);
3384 
3385    length = iterateOverLine(X[1], Y[1], X[2], Y[2], 1, NULL, NULL, regs, &cmd, ram, back_framebuffer);
3386    gouraudLineSetup(&redstep, &greenstep, &bluestep, length, gouraudB, gouraudC, ram, regs, &cmd, back_framebuffer);
3387    DrawLine(X[1], Y[1], X[2], Y[2], 0, 0, 0, redstep, greenstep, bluestep, regs, &cmd, ram, back_framebuffer);
3388 
3389    length = iterateOverLine(X[2], Y[2], X[3], Y[3], 1, NULL, NULL, regs, &cmd, ram, back_framebuffer);
3390    gouraudLineSetup(&redstep, &greenstep, &bluestep, length, gouraudD, gouraudC, ram, regs, &cmd, back_framebuffer);
3391    DrawLine(X[3], Y[3], X[2], Y[2], 0, 0, 0, redstep, greenstep, bluestep, regs, &cmd, ram, back_framebuffer);
3392 
3393    length = iterateOverLine(X[3], Y[3], X[0], Y[0], 1, NULL, NULL, regs, &cmd, ram, back_framebuffer);
3394    gouraudLineSetup(&redstep, &greenstep, &bluestep, length, gouraudA, gouraudD, ram, regs, &cmd, back_framebuffer);
3395    DrawLine(X[0], Y[0], X[3], Y[3], 0, 0, 0, redstep, greenstep, bluestep, regs, &cmd, ram, back_framebuffer);
3396 }
3397 
VIDSoftVdp1LineDraw(u8 * ram,Vdp1 * regs,u8 * back_framebuffer)3398 void VIDSoftVdp1LineDraw(u8* ram, Vdp1*regs, u8* back_framebuffer)
3399 {
3400 	int x1, y1, x2, y2;
3401 	double redstep = 0, greenstep = 0, bluestep = 0;
3402 	int length;
3403    vdp1cmd_struct cmd;
3404 
3405    Vdp1ReadCommand(&cmd, regs->addr, ram);
3406 
3407 	x1 = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x0C));
3408 	y1 = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x0E));
3409 	x2 = (int)regs->localX + (int)((s16)T1ReadWord(ram, regs->addr + 0x10));
3410 	y2 = (int)regs->localY + (int)((s16)T1ReadWord(ram, regs->addr + 0x12));
3411 
3412    length = iterateOverLine(x1, y1, x2, y2, 1, NULL, NULL, regs, &cmd, ram, back_framebuffer);
3413    gouraudLineSetup(&redstep, &bluestep, &greenstep, length, gouraudA, gouraudB, ram, regs, &cmd, back_framebuffer);
3414    DrawLine(x1, y1, x2, y2, 0, 0, 0, redstep, greenstep, bluestep, regs, &cmd, ram, back_framebuffer);
3415 }
3416 
3417 //////////////////////////////////////////////////////////////////////////////
3418 
VIDSoftVdp1UserClipping(u8 * ram,Vdp1 * regs)3419 void VIDSoftVdp1UserClipping(u8* ram, Vdp1*regs)
3420 {
3421    regs->userclipX1 = T1ReadWord(ram, regs->addr + 0xC);
3422    regs->userclipY1 = T1ReadWord(ram, regs->addr + 0xE);
3423    regs->userclipX2 = T1ReadWord(ram, regs->addr + 0x14);
3424    regs->userclipY2 = T1ReadWord(ram, regs->addr + 0x16);
3425 }
3426 
3427 //////////////////////////////////////////////////////////////////////////////
3428 
VIDSoftVdp1SystemClipping(u8 * ram,Vdp1 * regs)3429 void VIDSoftVdp1SystemClipping(u8* ram, Vdp1*regs)
3430 {
3431    regs->systemclipX1 = 0;
3432    regs->systemclipY1 = 0;
3433    regs->systemclipX2 = T1ReadWord(ram, regs->addr + 0x14);
3434    regs->systemclipY2 = T1ReadWord(ram, regs->addr + 0x16);
3435 }
3436 
3437 //////////////////////////////////////////////////////////////////////////////
3438 
VIDSoftVdp1LocalCoordinate(u8 * ram,Vdp1 * regs)3439 void VIDSoftVdp1LocalCoordinate(u8* ram, Vdp1*regs)
3440 {
3441    regs->localX = T1ReadWord(ram, regs->addr + 0xC);
3442    regs->localY = T1ReadWord(ram, regs->addr + 0xE);
3443 }
3444 
3445 //////////////////////////////////////////////////////////////////////////////
3446 
VIDSoftVdp1ReadFrameBuffer(u32 type,u32 addr,void * out)3447 void VIDSoftVdp1ReadFrameBuffer(u32 type, u32 addr, void * out)
3448 {
3449    u32 val;
3450 
3451    VidsoftWaitForVdp1Thread();
3452 
3453    switch (type)
3454    {
3455    case 0:
3456       val = T1ReadByte(vdp1backframebuffer, addr);
3457       *(u8*)out = val;
3458       break;
3459    case 1:
3460       val = T1ReadWord(vdp1backframebuffer, addr);
3461 #ifndef WORDS_BIGENDIAN
3462       val = BSWAP16L(val);
3463 #endif
3464       *(u16*)out = val;
3465       break;
3466    case 2:
3467 #if 0 //enable when burning rangers is fixed
3468       val = T1ReadLong(vdp1backframebuffer, addr);
3469 #ifndef WORDS_BIGENDIAN
3470       val = BSWAP32(val);
3471 #endif
3472       val = (val & 0xffff) << 16 | (val & 0xffff0000) >> 16;
3473       *(u32*)out = val;
3474 #else
3475       *(u32*)out = 0;
3476 #endif
3477       break;
3478    default:
3479       break;
3480    }
3481 }
3482 
3483 //////////////////////////////////////////////////////////////////////////////
3484 
VIDSoftVdp1WriteFrameBuffer(u32 type,u32 addr,u32 val)3485 void VIDSoftVdp1WriteFrameBuffer(u32 type, u32 addr, u32 val)
3486 {
3487    VidsoftWaitForVdp1Thread();
3488 
3489    switch (type)
3490    {
3491    case 0:
3492       T1WriteByte(vdp1backframebuffer, addr, val);
3493       break;
3494    case 1:
3495 #ifndef WORDS_BIGENDIAN
3496       val = BSWAP16L(val);
3497 #endif
3498       T1WriteWord(vdp1backframebuffer, addr, val);
3499       break;
3500    case 2:
3501 #ifndef WORDS_BIGENDIAN
3502       val = BSWAP32(val);
3503 #endif
3504       val = (val & 0xffff) << 16 | (val & 0xffff0000) >> 16;
3505       T1WriteLong(vdp1backframebuffer, addr, val);
3506       break;
3507    default:
3508       break;
3509    }
3510 }
3511 
3512 //////////////////////////////////////////////////////////////////////////////
3513 
VIDSoftVdp2Reset(void)3514 int VIDSoftVdp2Reset(void)
3515 {
3516    return 0;
3517 }
3518 
3519 //////////////////////////////////////////////////////////////////////////////
3520 
VIDSoftVdp2DrawStart(void)3521 void VIDSoftVdp2DrawStart(void)
3522 {
3523    int titanblendmode = TITAN_BLEND_TOP;
3524    if (Vdp2Regs->CCCTL & 0x100) titanblendmode = TITAN_BLEND_ADD;
3525    else if (Vdp2Regs->CCCTL & 0x200) titanblendmode = TITAN_BLEND_BOTTOM;
3526    TitanSetBlendingMode(titanblendmode);
3527 
3528    Vdp2DrawBackScreen();
3529    Vdp2DrawLineScreen();
3530 
3531    //dracula x bad cycle setting
3532    if (Vdp2Regs->CYCA0L == 0x5566 &&
3533       Vdp2Regs->CYCA0U == 0x47ff &&
3534       Vdp2Regs->CYCA1L == 0xffff &&
3535       Vdp2Regs->CYCA1U == 0xffff &&
3536       Vdp2Regs->CYCB0L == 0x12ff &&
3537       Vdp2Regs->CYCB0U == 0x03ff &&
3538       Vdp2Regs->CYCB1L == 0xffff &&
3539       Vdp2Regs->CYCB1U == 0xffff)
3540    {
3541       bad_cycle_setting[TITAN_NBG3] = 1;
3542    }
3543    else
3544       bad_cycle_setting[TITAN_NBG3] = 0;
3545 }
3546 
3547 //////////////////////////////////////////////////////////////////////////////
3548 
3549 
VidsoftDrawSprite(Vdp2 * vdp2_regs,u8 * spr_window_mask,u8 * vdp1_front_framebuffer,u8 * vdp2_ram,Vdp1 * vdp1_regs,Vdp2 * vdp2_lines,u8 * color_ram)3550 void VidsoftDrawSprite(Vdp2 * vdp2_regs, u8 * spr_window_mask, u8* vdp1_front_framebuffer, u8 * vdp2_ram, Vdp1* vdp1_regs, Vdp2* vdp2_lines, u8*color_ram)
3551 {
3552    int i, i2;
3553    u16 pixel;
3554    u8 prioritytable[8];
3555    u32 vdp1coloroffset;
3556    int colormode = vdp2_regs->SPCTL & 0x20;
3557    vdp2draw_struct info = { 0 };
3558    int islinewindow;
3559    clipping_struct clip[2];
3560    u32 linewnd0addr, linewnd1addr;
3561    int wctl;
3562    clipping_struct colorcalcwindow[2];
3563    int framebuffer_readout_y = 0;
3564    int start_line = 0, line_increment = 0;
3565    int sprite_window_enabled = vdp2_regs->SPCTL & 0x10;
3566    int vdp1spritetype = 0;
3567 
3568    if (sprite_window_enabled)
3569    {
3570       memset(spr_window_mask, 0, 704 * 512);
3571    }
3572 
3573    // Figure out whether to draw vdp1 framebuffer or vdp2 framebuffer pixels
3574    // based on priority
3575    if (Vdp1External.disptoggle && (vdp2_regs->TVMD & 0x8000))
3576    {
3577       int SPCCCS = (vdp2_regs->SPCTL >> 12) & 0x3;
3578       int SPCCN = (vdp2_regs->SPCTL >> 8) & 0x7;
3579       u8 colorcalctable[8];
3580       vdp2rotationparameterfp_struct p;
3581       int x, y;
3582       int output_y = 0;
3583 
3584       prioritytable[0] = vdp2_regs->PRISA & 0x7;
3585       prioritytable[1] = (vdp2_regs->PRISA >> 8) & 0x7;
3586       prioritytable[2] = vdp2_regs->PRISB & 0x7;
3587       prioritytable[3] = (vdp2_regs->PRISB >> 8) & 0x7;
3588       prioritytable[4] = vdp2_regs->PRISC & 0x7;
3589       prioritytable[5] = (vdp2_regs->PRISC >> 8) & 0x7;
3590       prioritytable[6] = vdp2_regs->PRISD & 0x7;
3591       prioritytable[7] = (vdp2_regs->PRISD >> 8) & 0x7;
3592       colorcalctable[0] = ((~vdp2_regs->CCRSA & 0x1F) << 1) + 1;
3593       colorcalctable[1] = ((~vdp2_regs->CCRSA >> 7) & 0x3E) + 1;
3594       colorcalctable[2] = ((~vdp2_regs->CCRSB & 0x1F) << 1) + 1;
3595       colorcalctable[3] = ((~vdp2_regs->CCRSB >> 7) & 0x3E) + 1;
3596       colorcalctable[4] = ((~vdp2_regs->CCRSC & 0x1F) << 1) + 1;
3597       colorcalctable[5] = ((~vdp2_regs->CCRSC >> 7) & 0x3E) + 1;
3598       colorcalctable[6] = ((~vdp2_regs->CCRSD & 0x1F) << 1) + 1;
3599       colorcalctable[7] = ((~vdp2_regs->CCRSD >> 7) & 0x3E) + 1;
3600 
3601       vdp1coloroffset = (vdp2_regs->CRAOFB & 0x70) << 4;
3602       vdp1spritetype = vdp2_regs->SPCTL & 0xF;
3603 
3604       ReadVdp2ColorOffset(vdp2_regs, &info, 0x40, 0x40);
3605 
3606       wctl = vdp2_regs->WCTLC >> 8;
3607       clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0;
3608       clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0;
3609       ReadWindowData(wctl, clip, vdp2_regs);
3610       linewnd0addr = linewnd1addr = 0;
3611       ReadLineWindowData(&islinewindow, wctl, &linewnd0addr, &linewnd1addr, vdp2_regs);
3612 
3613       /* color calculation window: in => no color calc, out => color calc */
3614       ReadWindowData(vdp2_regs->WCTLD >> 8, colorcalcwindow, vdp2_regs);
3615 
3616       if (vdp1_regs->TVMR & 2)
3617          Vdp2ReadRotationTableFP(0, &p, vdp2_regs, vdp2_ram);
3618 
3619       info.titan_which_layer = TITAN_SPRITE;
3620 
3621       info.linescreen = (vdp2_regs->LNCLEN >> 5) & 1;
3622 
3623       Vdp2GetInterlaceInfo(&start_line, &line_increment);
3624 
3625       for (i2 = start_line; i2 < vdp2height; i2 += line_increment)
3626       {
3627          float framebuffer_readout_pos = 0;
3628 
3629          ReadLineWindowClip(islinewindow, clip, &linewnd0addr, &linewnd1addr, vdp2_ram, vdp2_regs);
3630 
3631          if (vdp2_interlace)
3632             LoadLineParamsSprite(&info, i2 / 2, vdp2_lines);
3633          else
3634             LoadLineParamsSprite(&info, i2, vdp2_lines);
3635 
3636          if (vdp2_interlace)
3637          {
3638             y = framebuffer_readout_y;
3639             framebuffer_readout_y += 1;
3640          }
3641          else
3642          {
3643             y = i2;
3644          }
3645 
3646          for (i = 0; i < vdp2width; i++)
3647          {
3648 
3649             info.titan_shadow_type = 0;
3650 
3651             // See if screen position is clipped, if it isn't, continue
3652             if (!(vdp2_regs->SPCTL & 0x10))
3653             {
3654                if (!TestBothWindow(wctl, clip, i, i2))
3655                {
3656                   continue;
3657                }
3658             }
3659 
3660             if (vdp1_regs->TVMR & 2) {
3661                x = (touint(p.Xst + i * p.deltaX + i2 * p.deltaXst)) & (vdp1width - 1);
3662                y = (touint(p.Yst + i * p.deltaY + i2 * p.deltaYst)) & (vdp1height - 1);
3663             }
3664             else
3665             {
3666                if (vdp1width == 1024 && vdp2_x_hires)
3667                {
3668                   //hi res vdp1 and hi res vdp2
3669                   //pixels 1:1
3670                   x = (int)framebuffer_readout_pos;
3671                   framebuffer_readout_pos += 1;
3672                }
3673                else if (vdp1width == 512 && vdp2_x_hires)
3674                {
3675                   //low res vdp1,hi res vdp2
3676                   //vdp1 pixel doubling
3677                   x = (int)framebuffer_readout_pos;
3678                   framebuffer_readout_pos += .5;
3679                }
3680                else if (vdp1width == 1024 && (!vdp2_x_hires))
3681                {
3682                   //hi res vdp1, low res vdp2
3683                   //the vdp1 framebuffer is read out at half-res
3684                   x = (int)framebuffer_readout_pos;
3685                   framebuffer_readout_pos += 2;
3686                }
3687                else
3688                   x = i;
3689             }
3690 
3691             if (vdp1pixelsize == 2)
3692             {
3693                // 16-bit pixel size
3694                pixel = ((u16 *)vdp1_front_framebuffer)[(y * vdp1width) + x];
3695 
3696                if (pixel == 0)
3697                   ;
3698                else if (pixel & 0x8000 && colormode)
3699                {
3700                   // 16 BPP
3701                   u8 alpha = 0x3F;
3702                   if (TestBothWindow(vdp2_regs->WCTLD >> 8, colorcalcwindow, i, i2) && (vdp2_regs->CCCTL & 0x40))
3703                   {
3704                      switch (SPCCCS) {
3705                      case 0:
3706                         if (prioritytable[0] <= SPCCN)
3707                         {
3708                            alpha = colorcalctable[0];
3709                            if (vdp2_regs->CCCTL & 0x300) alpha |= 0x80;
3710                         }
3711                         break;
3712                      case 1:
3713                         if (prioritytable[0] == SPCCN)
3714                         {
3715                            alpha = colorcalctable[0];
3716                            if (vdp2_regs->CCCTL & 0x300) alpha |= 0x80;
3717                         }
3718                         break;
3719                      case 2:
3720                         if (prioritytable[0] >= SPCCN)
3721                         {
3722                            alpha = colorcalctable[0];
3723                            if (vdp2_regs->CCCTL & 0x300) alpha |= 0x80;
3724                         }
3725                         break;
3726                      case 3:
3727                         alpha = colorcalctable[0];
3728                         if (vdp2_regs->CCCTL & 0x300) alpha |= 0x80;
3729                         break;
3730                      }
3731                   }
3732 
3733                   // if pixel is 0x8000, only draw pixel if sprite window
3734                   // is disabled/sprite type 2-7. sprite types 0 and 1 are
3735                   // -always- drawn and sprite types 8-F are always
3736                   // transparent.
3737                   if (pixel != 0x8000 || vdp1spritetype < 2 || (vdp1spritetype < 8 && !(vdp2_regs->SPCTL & 0x10)))
3738                      TitanPutPixel(prioritytable[0], i, output_y, info.PostPixelFetchCalc(&info, COLSAT2YAB16(alpha, pixel)), info.linescreen, &info);
3739                }
3740                else
3741                {
3742                   // Color bank
3743                   spritepixelinfo_struct spi;
3744                   u8 alpha = 0x3F;
3745                   u32 dot;
3746 
3747                   Vdp1GetSpritePixelInfo(vdp1spritetype, &pixel, &spi);
3748                   if (spi.normalshadow)
3749                   {
3750                      info.titan_shadow_type = TITAN_NORMAL_SHADOW;
3751                      TitanPutPixel(prioritytable[spi.priority], i, output_y, COLSAT2YAB16(0x3f, 0), info.linescreen, &info);
3752                      continue;
3753                   }
3754 
3755                   dot = Vdp2ColorRamGetColor(vdp1coloroffset + pixel,color_ram);
3756 
3757                   if (TestBothWindow(vdp2_regs->WCTLD >> 8, colorcalcwindow, i, i2) && (vdp2_regs->CCCTL & 0x40))
3758                   {
3759                      int transparent = 0;
3760 
3761                      /* Sprite color calculation */
3762                      switch (SPCCCS) {
3763                      case 0:
3764                         if (prioritytable[spi.priority] <= SPCCN)
3765                            transparent = 1;
3766                         break;
3767                      case 1:
3768                         if (prioritytable[spi.priority] == SPCCN)
3769                            transparent = 1;
3770                         break;
3771                      case 2:
3772                         if (prioritytable[spi.priority] >= SPCCN)
3773                            transparent = 1;
3774                         break;
3775                      case 3:
3776                         if (dot & 0x80000000)
3777                            transparent = 1;
3778                         break;
3779                      }
3780 
3781                      if (vdp2_regs->CCCTL & 0x200) {
3782                         /* "bottom" mode, the alpha channel will be used by another layer,
3783                         so we set it regardless of whether sprites are transparent or not.
3784                         The highest priority bit is only set if the sprite is transparent
3785                         (in this case, it's the alpha channel of the lower priority layer
3786                         that will be used. */
3787                         alpha = colorcalctable[spi.colorcalc];
3788                         if (transparent) alpha |= 0x80;
3789                      }
3790                      else if (transparent) {
3791                         alpha = colorcalctable[spi.colorcalc];
3792                         if (vdp2_regs->CCCTL & 0x100) alpha |= 0x80;
3793                      }
3794                   }
3795                   if (spi.msbshadow)
3796                   {
3797                      if (sprite_window_enabled) {
3798                         spr_window_mask[(y*vdp2width) + x] = 1;
3799                         info.titan_shadow_type = TITAN_MSB_SHADOW;
3800                      }
3801                      else
3802                      {
3803                         info.titan_shadow_type = TITAN_MSB_SHADOW;
3804                      }
3805 
3806                      if (pixel == 0)
3807                      {
3808                         TitanPutPixel(prioritytable[spi.priority], i, output_y, info.PostPixelFetchCalc(&info, COLSAT2YAB32(alpha, 0)), info.linescreen, &info);
3809                         continue;
3810                      }
3811                   }
3812 
3813                   if ((sprite_window_enabled))
3814                   {
3815                      if (!TestBothWindow(wctl, clip, i, i2))
3816                      {
3817                         continue;
3818                      }
3819                   }
3820 
3821                   TitanPutPixel(prioritytable[spi.priority], i, output_y, info.PostPixelFetchCalc(&info, COLSAT2YAB32(alpha, dot)), info.linescreen, &info);
3822                }
3823             }
3824             else
3825             {
3826                // 8-bit pixel size
3827                pixel = vdp1_front_framebuffer[(y * vdp1width) + x];
3828 
3829                if (pixel != 0)
3830                {
3831                   // Color bank(fix me)
3832                   spritepixelinfo_struct spi;
3833                   u8 alpha = 0x3F;
3834                   u32 dot;
3835 
3836                   Vdp1GetSpritePixelInfo(vdp1spritetype, &pixel, &spi);
3837                   if (spi.normalshadow)
3838                   {
3839                      info.titan_shadow_type = TITAN_NORMAL_SHADOW;
3840                      TitanPutPixel(prioritytable[spi.priority], i, output_y, COLSAT2YAB16(0x3f, 0), info.linescreen, &info);
3841                      continue;
3842                   }
3843 
3844                   dot = Vdp2ColorRamGetColor(vdp1coloroffset + pixel, color_ram);
3845 
3846                   if (TestBothWindow(vdp2_regs->WCTLD >> 8, colorcalcwindow, i, i2) && (vdp2_regs->CCCTL & 0x40))
3847                   {
3848                      int transparent = 0;
3849 
3850                      /* Sprite color calculation */
3851                      switch (SPCCCS) {
3852                      case 0:
3853                         if (prioritytable[spi.priority] <= SPCCN)
3854                            transparent = 1;
3855                         break;
3856                      case 1:
3857                         if (prioritytable[spi.priority] == SPCCN)
3858                            transparent = 1;
3859                         break;
3860                      case 2:
3861                         if (prioritytable[spi.priority] >= SPCCN)
3862                            transparent = 1;
3863                         break;
3864                      case 3:
3865                         if (dot & 0x80000000)
3866                            transparent = 1;
3867                         break;
3868                      }
3869 
3870                      if (vdp2_regs->CCCTL & 0x200) {
3871                         /* "bottom" mode, the alpha channel will be used by another layer,
3872                         so we set it regardless of whether sprites are transparent or not.
3873                         The highest priority bit is only set if the sprite is transparent
3874                         (in this case, it's the alpha channel of the lower priority layer
3875                         that will be used. */
3876                         alpha = colorcalctable[spi.colorcalc];
3877                         if (transparent) alpha |= 0x80;
3878                      }
3879                      else if (transparent) {
3880                         alpha = colorcalctable[spi.colorcalc];
3881                         if (vdp2_regs->CCCTL & 0x100) alpha |= 0x80;
3882                      }
3883                   }
3884 
3885                   TitanPutPixel(prioritytable[spi.priority], i, output_y, info.PostPixelFetchCalc(&info, COLSAT2YAB32(alpha, dot)), info.linescreen, &info);
3886                }
3887             }
3888          }
3889 
3890          output_y++;
3891       }
3892    }
3893 }
3894 
VIDSoftVdp2DrawEnd(void)3895 void VIDSoftVdp2DrawEnd(void)
3896 {
3897    if (vidsoft_num_layer_threads > 0)
3898    {
3899       while (!vidsoft_thread_context.draw_finished[TITAN_NBG0]){}
3900       while (!vidsoft_thread_context.draw_finished[TITAN_NBG1]){}
3901       while (!vidsoft_thread_context.draw_finished[TITAN_NBG2]){}
3902       while (!vidsoft_thread_context.draw_finished[TITAN_NBG3]){}
3903       while (!vidsoft_thread_context.draw_finished[TITAN_RBG0]){}
3904       while (!vidsoft_thread_context.draw_finished[TITAN_SPRITE]){}
3905    }
3906 
3907    TitanRender(dispbuffer);
3908 
3909    VIDSoftVdp1SwapFrameBuffer();
3910 
3911    if (OSDUseBuffer())
3912       OSDDisplayMessages(dispbuffer, vdp2width, vdp2height);
3913 
3914 #ifdef USE_OPENGL
3915    if (VideoUseGL)
3916    {
3917       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vdp2width, vdp2height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dispbuffer);
3918       glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3919       glClear(GL_COLOR_BUFFER_BIT);
3920       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3921 
3922       if (! OSDUseBuffer())
3923          OSDDisplayMessages(NULL, -1, -1);
3924    }
3925 #endif
3926 
3927    YuiSwapBuffers();
3928 }
3929 
3930 //////////////////////////////////////////////////////////////////////////////
3931 
VidsoftStartLayerThread(int * layer_priority,int * draw_priority_0,int * num_threads_dispatched,int which_layer,void (* layer_func)(Vdp2 * lines,Vdp2 * regs,u8 * ram,u8 * color_ram,struct CellScrollData * cell_data))3932 void VidsoftStartLayerThread(int * layer_priority, int * draw_priority_0, int * num_threads_dispatched, int which_layer, void(*layer_func) (Vdp2* lines, Vdp2* regs, u8* ram, u8* color_ram, struct CellScrollData * cell_data))
3933 {
3934    if (layer_priority[which_layer] > 0 || draw_priority_0[which_layer])
3935    {
3936       if (*num_threads_dispatched < vidsoft_num_layer_threads)
3937       {
3938          vidsoft_thread_context.need_draw[which_layer] = 1;
3939          vidsoft_thread_context.draw_finished[which_layer] = 0;
3940          YabThreadWake(YAB_THREAD_VIDSOFT_LAYER_NBG3 + which_layer);
3941          *num_threads_dispatched = *num_threads_dispatched + 1;
3942       }
3943       else
3944       {
3945         (*layer_func) (Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
3946       }
3947    }
3948 }
3949 
3950 //////////////////////////////////////////////////////////////////////////////
3951 
IsSpriteWindowEnabled(u16 wtcl)3952 int IsSpriteWindowEnabled(u16 wtcl)
3953 {
3954    if (((wtcl& (1 << 13)) == 0) &&
3955       ((wtcl & (1 << 5)) == 0))
3956       return 0;
3957 
3958    return 1;
3959 }
3960 
CanUseSpriteThread()3961 int CanUseSpriteThread()
3962 {
3963    //check if sprite window is enabled
3964    if ((Vdp2Regs->SPCTL & (1 << 4)) == 0)
3965       return 1;
3966 
3967    //check if any layers are using it
3968    if (IsSpriteWindowEnabled(Vdp2Regs->WCTLA) ||
3969       IsSpriteWindowEnabled(Vdp2Regs->WCTLB) ||
3970       IsSpriteWindowEnabled(Vdp2Regs->WCTLC) ||
3971       IsSpriteWindowEnabled(Vdp2Regs->WCTLD))
3972    {
3973       //thread cannot be used
3974       return 0;
3975    }
3976 
3977    return 1;
3978 }
3979 
VIDSoftVdp2DrawScreens(void)3980 void VIDSoftVdp2DrawScreens(void)
3981 {
3982    int draw_priority_0[6] = { 0 };
3983    int layer_priority[6] = { 0 };
3984    int num_threads_dispatched = 0;
3985 
3986    VIDSoftVdp2SetResolution(Vdp2Regs->TVMD);
3987    layer_priority[TITAN_NBG0] = Vdp2Regs->PRINA & 0x7;
3988    layer_priority[TITAN_NBG1] = ((Vdp2Regs->PRINA >> 8) & 0x7);
3989    layer_priority[TITAN_NBG2] = (Vdp2Regs->PRINB & 0x7);
3990    layer_priority[TITAN_NBG3] = ((Vdp2Regs->PRINB >> 8) & 0x7);
3991    layer_priority[TITAN_RBG0] = (Vdp2Regs->PRIR & 0x7);
3992 
3993    TitanErase();
3994 
3995    if (Vdp2Regs->SFPRMD & 0x3FF)
3996    {
3997       draw_priority_0[TITAN_NBG0] = (Vdp2Regs->SFPRMD >> 0) & 0x3;
3998       draw_priority_0[TITAN_NBG1] = (Vdp2Regs->SFPRMD >> 2) & 0x3;
3999       draw_priority_0[TITAN_NBG2] = (Vdp2Regs->SFPRMD >> 4) & 0x3;
4000       draw_priority_0[TITAN_NBG3] = (Vdp2Regs->SFPRMD >> 6) & 0x3;
4001       draw_priority_0[TITAN_RBG0] = (Vdp2Regs->SFPRMD >> 8) & 0x3;
4002    }
4003 
4004    if (vidsoft_num_layer_threads > 0)
4005    {
4006       memcpy(vidsoft_thread_context.lines, Vdp2Lines, sizeof(Vdp2) * 270);
4007       memcpy(&vidsoft_thread_context.regs, Vdp2Regs, sizeof(Vdp2));
4008       memcpy(vidsoft_thread_context.ram, Vdp2Ram, 0x80000);
4009       memcpy(vidsoft_thread_context.color_ram, Vdp2ColorRam, 0x1000);
4010       memcpy(vidsoft_thread_context.cell_scroll_data, cell_scroll_data, sizeof(struct CellScrollData) * 270);
4011    }
4012 
4013    //draw vdp2 sprite layer on a thread if sprite window is not enabled
4014    if (CanUseSpriteThread() && vidsoft_num_layer_threads > 0)
4015    {
4016       vidsoft_thread_context.need_draw[TITAN_SPRITE] = 1;
4017       vidsoft_thread_context.draw_finished[TITAN_SPRITE] = 0;
4018       YabThreadWake(YAB_THREAD_VIDSOFT_LAYER_SPRITE);
4019       num_threads_dispatched++;
4020    }
4021    else
4022    {
4023       VidsoftDrawSprite(Vdp2Regs, sprite_window_mask, vdp1frontframebuffer, Vdp2Ram, Vdp1Regs, Vdp2Lines, Vdp2ColorRam);
4024    }
4025 
4026    if (vidsoft_num_layer_threads > 0)
4027    {
4028       VidsoftStartLayerThread(layer_priority, draw_priority_0, &num_threads_dispatched, TITAN_NBG0, Vdp2DrawNBG0);
4029       VidsoftStartLayerThread(layer_priority, draw_priority_0, &num_threads_dispatched, TITAN_RBG0, Vdp2DrawRBG0);
4030       VidsoftStartLayerThread(layer_priority, draw_priority_0, &num_threads_dispatched, TITAN_NBG1, Vdp2DrawNBG1);
4031       VidsoftStartLayerThread(layer_priority, draw_priority_0, &num_threads_dispatched, TITAN_NBG2, Vdp2DrawNBG2);
4032       VidsoftStartLayerThread(layer_priority, draw_priority_0, &num_threads_dispatched, TITAN_NBG3, Vdp2DrawNBG3);
4033    }
4034    else
4035    {
4036       Vdp2DrawNBG0(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4037       Vdp2DrawNBG1(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4038       Vdp2DrawNBG2(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4039       Vdp2DrawNBG3(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4040       Vdp2DrawRBG0(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4041    }
4042 }
4043 
4044 //////////////////////////////////////////////////////////////////////////////
4045 
VIDSoftVdp2DrawScreen(int screen)4046 void VIDSoftVdp2DrawScreen(int screen)
4047 {
4048    VIDSoftVdp2SetResolution(Vdp2Regs->TVMD);
4049 
4050    switch(screen)
4051    {
4052       case 0:
4053          Vdp2DrawNBG0(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4054          break;
4055       case 1:
4056          Vdp2DrawNBG1(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4057          break;
4058       case 2:
4059          Vdp2DrawNBG2(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4060          break;
4061       case 3:
4062          Vdp2DrawNBG3(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4063          break;
4064       case 4:
4065          Vdp2DrawRBG0(Vdp2Lines, Vdp2Regs, Vdp2Ram, Vdp2ColorRam, cell_scroll_data);
4066          break;
4067    }
4068 }
4069 
4070 //////////////////////////////////////////////////////////////////////////////
4071 
VIDSoftVdp2SetResolution(u16 TVMD)4072 void VIDSoftVdp2SetResolution(u16 TVMD)
4073 {
4074    // This needs some work
4075 
4076    // Horizontal Resolution
4077    switch (TVMD & 0x7)
4078    {
4079       case 0:
4080          rbg0width = vdp2width = 320;
4081          break;
4082       case 1:
4083          rbg0width = vdp2width = 352;
4084          break;
4085       case 2:
4086          vdp2width = 640;
4087          rbg0width = 320;
4088          break;
4089       case 3:
4090          vdp2width = 704;
4091          rbg0width = 352;
4092          break;
4093       case 4:
4094          rbg0width = vdp2width = 320;
4095          break;
4096       case 5:
4097          rbg0width = vdp2width = 352;
4098          break;
4099       case 6:
4100          vdp2width = 640;
4101          rbg0width = 320;
4102          break;
4103       case 7:
4104          vdp2width = 704;
4105          rbg0width = 352;
4106          break;
4107    }
4108 
4109    if ((vdp2width == 704) || (vdp2width == 640))
4110       vdp2_x_hires = 1;
4111    else
4112       vdp2_x_hires = 0;
4113 
4114    // Vertical Resolution
4115    switch ((TVMD >> 4) & 0x3)
4116    {
4117       case 0:
4118          rbg0height = vdp2height = 224;
4119          break;
4120       case 1:
4121          rbg0height = vdp2height = 240;
4122          break;
4123       case 2:
4124          rbg0height = vdp2height = 256;
4125          break;
4126       default: break;
4127    }
4128 
4129    // Check for interlace
4130    switch ((TVMD >> 6) & 0x3)
4131    {
4132       case 3: // Double-density Interlace
4133          vdp2height *= 2;
4134          vdp2_interlace=1;
4135          break;
4136       case 2: // Single-density Interlace
4137       case 0: // Non-interlace
4138       default:
4139          vdp2_interlace = 0;
4140          break;
4141    }
4142 
4143    TitanSetResolution(vdp2width, vdp2height);
4144 }
4145 
4146 //////////////////////////////////////////////////////////////////////////////
4147 
VIDSoftVdp1SwapFrameBuffer(void)4148 void VIDSoftVdp1SwapFrameBuffer(void)
4149 {
4150    if (((Vdp1Regs->FBCR & 2) == 0) || Vdp1External.manualchange)
4151    {
4152 		u8 *temp;
4153       if (vidsoft_vdp1_thread_enabled)
4154       {
4155          VidsoftWaitForVdp1Thread();
4156       }
4157 
4158       temp = vdp1frontframebuffer;
4159       vdp1frontframebuffer = vdp1backframebuffer;
4160       vdp1backframebuffer = temp;
4161       Vdp1External.manualchange = 0;
4162    }
4163 }
4164 
4165 //////////////////////////////////////////////////////////////////////////////
4166 
VIDSoftVdp1EraseFrameBuffer(Vdp1 * regs,u8 * back_framebuffer)4167 void VIDSoftVdp1EraseFrameBuffer(Vdp1* regs, u8 * back_framebuffer)
4168 {
4169    int i,i2;
4170    int w,h;
4171 
4172    if (((regs->FBCR & 2) == 0) || Vdp1External.manualerase)
4173    {
4174       h = (regs->EWRR & 0x1FF) + 1;
4175       if (h > vdp1height) h = vdp1height;
4176       w = ((regs->EWRR >> 6) & 0x3F8) + 8;
4177       if (w > vdp1width) w = vdp1width;
4178 
4179       if (vdp1pixelsize == 2)
4180       {
4181          for (i2 = (regs->EWLR & 0x1FF); i2 < h; i2++)
4182          {
4183             for (i = ((regs->EWLR >> 6) & 0x1F8); i < w; i++)
4184                ((u16 *)back_framebuffer)[(i2 * vdp1width) + i] = regs->EWDR;
4185          }
4186       }
4187       else
4188       {
4189          w = regs->EWRR >> 9;
4190          w *= 16;
4191 
4192          for (i2 = (regs->EWLR & 0x1FF); i2 < h; i2++)
4193          {
4194             for (i = ((regs->EWLR >> 6) & 0x1F8); i < w; i++)
4195             {
4196                int pos = (i2 * vdp1width) + i;
4197 
4198                if (pos < 0x3FFFF)
4199                   back_framebuffer[pos] = regs->EWDR & 0xFF;
4200             }
4201          }
4202       }
4203       Vdp1External.manualerase = 0;
4204    }
4205 }
4206 
4207 //////////////////////////////////////////////////////////////////////////////
4208 
VIDSoftGetGlSize(int * width,int * height)4209 void VIDSoftGetGlSize(int *width, int *height)
4210 {
4211 #ifdef USE_OPENGL
4212    int usegl = 0;
4213 
4214    if (VideoUseGL)
4215       usegl = 1;
4216 
4217    if (usegl)
4218    {
4219       *width = outputwidth;
4220       *height = outputheight;
4221    }
4222    else
4223 #endif
4224    {
4225       *width = vdp2width;
4226       *height = vdp2height;
4227    }
4228 }
4229 
VIDSoftGetNativeResolution(int * width,int * height,int * interlace)4230 void VIDSoftGetNativeResolution(int *width, int *height, int* interlace)
4231 {
4232    *width = vdp2width;
4233    *height = vdp2height;
4234    *interlace = vdp2_interlace;
4235 }
4236 
VIDSoftVdp2DispOff()4237 void VIDSoftVdp2DispOff()
4238 {
4239    TitanErase();
4240 }
4241