1 // license:LGPL-2.1+
2 // copyright-holders:David Haywood, Angelo Salese, Olivier Galibert, Mariusz Wojcieszek, R. Belmont
3 /*
4
5 STV - VDP1
6
7 the vdp1 draws to the FRAMEBUFFER which is mapped in memory
8
9 -------------------------- WARNING WARNING WARNING --------------------------
10 This is a legacy core, all game based notes are for a future device rewrite.
11 Please don't remove them if for no reason you truly want to mess with this.
12 -------------------------- WARNING WARNING WARNING --------------------------
13
14 Framebuffer todo:
15 - finish manual erase
16 - add proper framebuffer erase
17 - 8 bpp support - now we always draw as 16 bpp, but this is not a problem since
18 VDP2 interprets framebuffer as 8 bpp in these cases
19
20 */
21
22
23 #include "emu.h"
24 #include "includes/saturn.h" // FIXME: this is a dependency from devices on MAME
25
26
27 #define VDP1_LOG 0
28
29
30 enum { FRAC_SHIFT = 16 };
31
32 struct spoint {
33 int32_t x, y;
34 int32_t u, v;
35 };
36
37 struct shaded_point
38 {
39 int32_t x,y;
40 int32_t r,g,b;
41 };
42
43 #define RGB_R(_color) (_color & 0x1f)
44 #define RGB_G(_color) ((_color >> 5) & 0x1f)
45 #define RGB_B(_color) ((_color >> 10) & 0x1f)
46
47 #define SWAP_INT32(_a,_b) \
48 { \
49 int32_t t; \
50 t = _a; \
51 _a = _b; \
52 _b = t; \
53 }
54
55 #define SWAP_INT32PTR(_p1, _p2) \
56 { \
57 int32_t *p; \
58 p = _p1; \
59 _p1 = _p2; \
60 _p2 = p; \
61 }
62
63 /*TV Mode Selection Register */
64 /*
65 xxxx xxxx xxxx ---- | UNUSED
66 ---- ---- ---- x--- | VBlank Erase/Write (VBE)
67 ---- ---- ---- -xxx | TV Mode (TVM)
68 TV-Mode:
69 This sets the Frame Buffer size,the rotation of the Frame Buffer & the bit width.
70 bit 2 HDTV disable(0)/enable(1)
71 bit 1 non-rotation/rotation(1)
72 bit 0 16(0)/8(1) bits per pixel
73 Size of the Frame Buffer:
74 7 invalid
75 6 invalid
76 5 invalid
77 4 512x256
78 3 512x512
79 2 512x256
80 1 1024x256
81 0 512x256
82 */
83
84 /*Frame Buffer Change Mode Register*/
85 /*
86 xxxx xxxx xxx- ---- | UNUSED
87 ---- ---- ---x ---- | Even/Odd Coordinate Select Bit (EOS)
88 ---- ---- ---- x--- | Double Interlace Mode (DIE)
89 ---- ---- ---- -x-- | Double Interlace Draw Line (DIL)
90 ---- ---- ---- --x- | Frame Buffer Change Trigger (FCM)
91 ---- ---- ---- ---x | Frame Buffer Change Mode (FCT)
92 */
93 #define STV_VDP1_FBCR ((m_vdp1_regs[0x002/2] >> 0)&0xffff)
94 #define STV_VDP1_EOS ((STV_VDP1_FBCR & 0x0010) >> 4)
95 #define STV_VDP1_DIE ((STV_VDP1_FBCR & 0x0008) >> 3)
96 #define STV_VDP1_DIL ((STV_VDP1_FBCR & 0x0004) >> 2)
97 #define STV_VDP1_FCM ((STV_VDP1_FBCR & 0x0002) >> 1)
98 #define STV_VDP1_FCT ((STV_VDP1_FBCR & 0x0001) >> 0)
99
100 /*Plot Trigger Register*/
101 /*
102 xxxx xxxx xxxx xx-- | UNUSED
103 ---- ---- ---- --xx | Plot Trigger Mode (PTM)
104
105 Plot Trigger Mode:
106 3 Invalid
107 2 Automatic draw
108 1 VDP1 draw by request
109 0 VDP1 Idle (no access)
110 */
111 #define STV_VDP1_PTMR ((m_vdp1_regs[0x004/2])&0xffff)
112 #define STV_VDP1_PTM ((STV_VDP1_PTMR & 0x0003) >> 0)
113 #define PTM_0 m_vdp1_regs[0x004/2]&=~0x0001
114
115 /*
116 Erase/Write Data Register
117 16 bpp = data
118 8 bpp = erase/write data for even/odd X coordinates
119 */
120 #define STV_VDP1_EWDR ((m_vdp1_regs[0x006/2])&0xffff)
121
122 /*Erase/Write Upper-Left register*/
123 /*
124 x--- ---- ---- ---- | UNUSED
125 -xxx xxx- ---- ---- | X1 register
126 ---- ---x xxxx xxxx | Y1 register
127
128 */
129 #define STV_VDP1_EWLR ((m_vdp1_regs[0x008/2])&0xffff)
130 #define STV_VDP1_EWLR_X1 ((STV_VDP1_EWLR & 0x7e00) >> 9)
131 #define STV_VDP1_EWLR_Y1 ((STV_VDP1_EWLR & 0x01ff) >> 0)
132 /*Erase/Write Lower-Right register*/
133 /*
134 xxxx xxx- ---- ---- | X3 register
135 ---- ---x xxxx xxxx | Y3 register
136
137 */
138 #define STV_VDP1_EWRR ((m_vdp1_regs[0x00a/2])&0xffff)
139 #define STV_VDP1_EWRR_X3 ((STV_VDP1_EWRR & 0xfe00) >> 9)
140 #define STV_VDP1_EWRR_Y3 ((STV_VDP1_EWRR & 0x01ff) >> 0)
141 /*Transfer End Status Register*/
142 /*
143 xxxx xxxx xxxx xx-- | UNUSED
144 ---- ---- ---- --x- | CEF
145 ---- ---- ---- ---x | BEF
146
147 */
148 #define STV_VDP1_EDSR ((m_vdp1_regs[0x010/2])&0xffff)
149 #define STV_VDP1_CEF (STV_VDP1_EDSR & 2)
150 #define STV_VDP1_BEF (STV_VDP1_EDSR & 1)
151 /**/
152
153
154
saturn_vdp1_regs_r(offs_t offset)155 uint16_t saturn_state::saturn_vdp1_regs_r(offs_t offset)
156 {
157 //logerror ("%s VDP1: Read from Registers, Offset %04x\n", machine().describe_context(), offset);
158
159 switch(offset)
160 {
161 case 0x02/2:
162 return 0;
163 case 0x10/2:
164 break;
165 case 0x12/2: return m_vdp1.lopr;
166 case 0x14/2: return m_vdp1.copr;
167 /* MODR register, read register for the other VDP1 regs
168 (Shienryu SS version abuses of this during intro) */
169 case 0x16/2:
170 uint16_t modr;
171
172 modr = 0x1000; //vdp1 VER
173 modr |= (STV_VDP1_PTM >> 1) << 8; // PTM1
174 modr |= STV_VDP1_EOS << 7; // EOS
175 modr |= STV_VDP1_DIE << 6; // DIE
176 modr |= STV_VDP1_DIL << 5; // DIL
177 modr |= STV_VDP1_FCM << 4; //FCM
178 modr |= STV_VDP1_VBE << 3; //VBE
179 modr |= STV_VDP1_TVM & 7; //TVM
180
181 return modr;
182 default:
183 if(!machine().side_effects_disabled())
184 logerror("%s VDP1: Read from Registers, Offset %04x\n", machine().describe_context(), offset*2);
185 break;
186 }
187
188 return m_vdp1_regs[offset]; //TODO: write-only regs should return open bus or zero
189 }
190
191 /* TODO: TVM & 1 is just a kludgy work-around, the VDP1 actually needs to be rewritten from scratch. */
192 /* Daisenryaku Strong Style (daisenss) uses it */
stv_clear_framebuffer(int which_framebuffer)193 void saturn_state::stv_clear_framebuffer( int which_framebuffer )
194 {
195 int start_x, end_x, start_y, end_y;
196
197 start_x = STV_VDP1_EWLR_X1 * ((STV_VDP1_TVM & 1) ? 16 : 8);
198 start_y = STV_VDP1_EWLR_Y1 * (m_vdp1.framebuffer_double_interlace+1);
199 end_x = STV_VDP1_EWRR_X3 * ((STV_VDP1_TVM & 1) ? 16 : 8);
200 end_y = (STV_VDP1_EWRR_Y3+1) * (m_vdp1.framebuffer_double_interlace+1);
201 // popmessage("%d %d %d %d %d",STV_VDP1_EWLR_X1,STV_VDP1_EWLR_Y1,STV_VDP1_EWRR_X3,STV_VDP1_EWRR_Y3,m_vdp1.framebuffer_double_interlace);
202
203 if(STV_VDP1_TVM & 1)
204 {
205 for(int y=start_y;y<end_y;y++)
206 for(int x=start_x;x<end_x;x++)
207 m_vdp1.framebuffer[ which_framebuffer ][((x&1023)+(y&511)*1024)] = m_vdp1.ewdr;
208 }
209 else
210 {
211 for(int y=start_y;y<end_y;y++)
212 for(int x=start_x;x<end_x;x++)
213 m_vdp1.framebuffer[ which_framebuffer ][((x&511)+(y&511)*512)] = m_vdp1.ewdr;
214 }
215
216 if ( VDP1_LOG ) logerror( "Clearing %d framebuffer\n", m_vdp1.framebuffer_current_draw );
217 // memset( m_vdp1.framebuffer[ which_framebuffer ], m_vdp1.ewdr, 1024 * 256 * sizeof(uint16_t) * 2 );
218 }
219
220
stv_prepare_framebuffers(void)221 void saturn_state::stv_prepare_framebuffers( void )
222 {
223 int i,rowsize;
224
225 rowsize = m_vdp1.framebuffer_width;
226 if ( m_vdp1.framebuffer_current_draw == 0 )
227 {
228 for ( i = 0; i < m_vdp1.framebuffer_height; i++ )
229 {
230 m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][ i * rowsize ];
231 m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][ i * rowsize ];
232 }
233 for ( ; i < 512; i++ )
234 {
235 m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][0];
236 m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][0];
237 }
238 }
239 else
240 {
241 for ( i = 0; i < m_vdp1.framebuffer_height; i++ )
242 {
243 m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[1][ i * rowsize ];
244 m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[0][ i * rowsize ];
245 }
246 for ( ; i < 512; i++ )
247 {
248 m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[1][0];
249 m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[0][0];
250 }
251
252 }
253
254 for ( ; i < 512; i++ )
255 {
256 m_vdp1.framebuffer_draw_lines[i] = &m_vdp1.framebuffer[0][0];
257 m_vdp1.framebuffer_display_lines[i] = &m_vdp1.framebuffer[1][0];
258 }
259
260 }
261
stv_vdp1_change_framebuffers(void)262 void saturn_state::stv_vdp1_change_framebuffers( void )
263 {
264 m_vdp1.framebuffer_current_display ^= 1;
265 m_vdp1.framebuffer_current_draw ^= 1;
266 // "this bit is reset to 0 when the frame buffers are changed"
267 CEF_0;
268 if ( VDP1_LOG ) logerror( "Changing framebuffers: %d - draw, %d - display\n", m_vdp1.framebuffer_current_draw, m_vdp1.framebuffer_current_display );
269 stv_prepare_framebuffers();
270 }
271
stv_set_framebuffer_config(void)272 void saturn_state::stv_set_framebuffer_config( void )
273 {
274 if ( m_vdp1.framebuffer_mode == STV_VDP1_TVM &&
275 m_vdp1.framebuffer_double_interlace == STV_VDP1_DIE ) return;
276
277 if ( VDP1_LOG ) logerror( "Setting framebuffer config\n" );
278 m_vdp1.framebuffer_mode = STV_VDP1_TVM;
279 m_vdp1.framebuffer_double_interlace = STV_VDP1_DIE;
280 switch( m_vdp1.framebuffer_mode )
281 {
282 case 0: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
283 case 1: m_vdp1.framebuffer_width = 1024; m_vdp1.framebuffer_height = 256; break;
284 case 2: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
285 case 3: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 512; break;
286 case 4: m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
287 default: logerror( "Invalid framebuffer config %x\n", STV_VDP1_TVM ); m_vdp1.framebuffer_width = 512; m_vdp1.framebuffer_height = 256; break;
288 }
289 if ( STV_VDP1_DIE ) m_vdp1.framebuffer_height *= 2; /* double interlace */
290
291 m_vdp1.framebuffer_current_draw = 0;
292 m_vdp1.framebuffer_current_display = 1;
293 stv_prepare_framebuffers();
294 }
295
saturn_vdp1_regs_w(offs_t offset,uint16_t data,uint16_t mem_mask)296 void saturn_state::saturn_vdp1_regs_w(offs_t offset, uint16_t data, uint16_t mem_mask)
297 {
298 COMBINE_DATA(&m_vdp1_regs[offset]);
299
300 switch(offset)
301 {
302 case 0x00/2:
303 stv_set_framebuffer_config();
304 if ( VDP1_LOG ) logerror( "VDP1: Access to register TVMR = %1X\n", STV_VDP1_TVMR );
305
306 break;
307 case 0x02/2:
308 stv_set_framebuffer_config();
309 if ( VDP1_LOG ) logerror( "VDP1: Access to register FBCR = %1X\n", STV_VDP1_FBCR );
310 m_vdp1.fbcr_accessed = 1;
311 break;
312 case 0x04/2:
313 if ( VDP1_LOG ) logerror( "VDP1: Access to register PTMR = %1X\n", STV_VDP1_PTM );
314 if ( STV_VDP1_PTMR == 1 )
315 stv_vdp1_process_list();
316
317 break;
318 case 0x06/2:
319 if ( VDP1_LOG ) logerror( "VDP1: Erase data set %08X\n", data );
320
321 m_vdp1.ewdr = STV_VDP1_EWDR;
322 break;
323 case 0x08/2:
324 if ( VDP1_LOG ) logerror( "VDP1: Erase upper-left coord set: %08X\n", data );
325 break;
326 case 0x0a/2:
327 if ( VDP1_LOG ) logerror( "VDP1: Erase lower-right coord set: %08X\n", data );
328 break;
329 case 0x0c/2:
330 case 0x0e/2: // After Burner 2 / Out Run / Fantasy Zone writes here with a dword ...
331 if ( VDP1_LOG ) logerror( "VDP1: Draw forced termination register write: %08X %08X\n", offset*2, data );
332 break;
333 default:
334 printf("Warning: write to unknown VDP1 reg %08x %08x\n",offset*2,data);
335 break;
336 }
337
338 }
339
saturn_vdp1_vram_r(offs_t offset)340 uint32_t saturn_state::saturn_vdp1_vram_r(offs_t offset)
341 {
342 return m_vdp1_vram[offset];
343 }
344
345
saturn_vdp1_vram_w(offs_t offset,uint32_t data,uint32_t mem_mask)346 void saturn_state::saturn_vdp1_vram_w(offs_t offset, uint32_t data, uint32_t mem_mask)
347 {
348 uint8_t *vdp1 = m_vdp1.gfx_decode.get();
349
350 COMBINE_DATA (&m_vdp1_vram[offset]);
351
352 // if (((offset * 4) > 0xdf) && ((offset * 4) < 0x140))
353 // {
354 // logerror("%s: VRAM dword write to %08X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
355 // }
356
357 data = m_vdp1_vram[offset];
358 /* put in gfx region for easy decoding */
359 vdp1[offset*4+0] = (data & 0xff000000) >> 24;
360 vdp1[offset*4+1] = (data & 0x00ff0000) >> 16;
361 vdp1[offset*4+2] = (data & 0x0000ff00) >> 8;
362 vdp1[offset*4+3] = (data & 0x000000ff) >> 0;
363 }
364
saturn_vdp1_framebuffer0_w(offs_t offset,uint32_t data,uint32_t mem_mask)365 void saturn_state::saturn_vdp1_framebuffer0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
366 {
367 //popmessage ("STV VDP1 Framebuffer 0 WRITE offset %08x data %08x",offset, data);
368 if ( STV_VDP1_TVM & 1 )
369 {
370 /* 8-bit mode */
371 //printf("VDP1 8-bit mode %08x %02x\n",offset,data);
372 if ( ACCESSING_BITS_24_31 )
373 {
374 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] &= 0x00ff;
375 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] |= data & 0xff00;
376 }
377 if ( ACCESSING_BITS_16_23 )
378 {
379 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] &= 0xff00;
380 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] |= data & 0x00ff;
381 }
382 if ( ACCESSING_BITS_8_15 )
383 {
384 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] &= 0x00ff;
385 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] |= data & 0xff00;
386 }
387 if ( ACCESSING_BITS_0_7 )
388 {
389 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] &= 0xff00;
390 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] |= data & 0x00ff;
391 }
392 }
393 else
394 {
395 /* 16-bit mode */
396 if ( ACCESSING_BITS_16_31 )
397 {
398 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] = (data >> 16) & 0xffff;
399 }
400 if ( ACCESSING_BITS_0_15 )
401 {
402 m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] = data & 0xffff;
403 }
404 }
405 }
406
saturn_vdp1_framebuffer0_r(offs_t offset,uint32_t mem_mask)407 uint32_t saturn_state::saturn_vdp1_framebuffer0_r(offs_t offset, uint32_t mem_mask)
408 {
409 uint32_t result = 0;
410 //popmessage ("STV VDP1 Framebuffer 0 READ offset %08x",offset);
411 if ( STV_VDP1_TVM & 1 )
412 {
413 /* 8-bit mode */
414 //printf("VDP1 8-bit mode %08x\n",offset);
415 if ( ACCESSING_BITS_24_31 )
416 result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] & 0xff00) << 16);
417 if ( ACCESSING_BITS_16_23 )
418 result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] & 0x00ff) << 16);
419 if ( ACCESSING_BITS_8_15 )
420 result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] & 0xff00));
421 if ( ACCESSING_BITS_0_7 )
422 result |= ((m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1] & 0x00ff));
423 }
424 else
425 {
426 /* 16-bit mode */
427 if ( ACCESSING_BITS_16_31 )
428 {
429 result |= (m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2] << 16);
430 }
431 if ( ACCESSING_BITS_0_15 )
432 {
433 result |= (m_vdp1.framebuffer[m_vdp1.framebuffer_current_draw][offset*2+1]);
434 }
435
436 }
437
438 return result;
439 }
440
441 #ifdef UNUSED_FUNCTION
saturn_vdp1_framebuffer1_w(offs_t offset,uint32_t data)442 void saturn_state::saturn_vdp1_framebuffer1_w(offs_t offset, uint32_t data)
443 {
444 //popmessage ("STV VDP1 Framebuffer 1 WRITE offset %08x data %08x",offset, data);
445 }
446
saturn_vdp1_framebuffer1_r()447 uint32_t saturn_state::saturn_vdp1_framebuffer1_r()
448 {
449 //popmessage ("STV VDP1 Framebuffer 1 READ offset %08x",offset);
450 return 0xffff;
451 }
452 #endif
453
454
455 /*
456
457 there is a command every 0x20 bytes
458 the first word is the control word
459 the rest are data used by it
460
461 ---
462 00 CMDCTRL
463 e--- ---- ---- ---- | end bit (15)
464 -jjj ---- ---- ---- | jump select bits (12-14)
465 ---- zzzz ---- ---- | zoom point / hotspot (8-11)
466 ---- ---- 00-- ---- | UNUSED
467 ---- ---- --dd ---- | character read direction (4,5)
468 ---- ---- ---- cccc | command bits (0-3)
469
470 02 CMDLINK
471 llll llll llll ll-- | link
472 ---- ---- ---- --00 | UNUSED
473
474 04 CMDPMOD
475 m--- ---- ---- ---- | MON (looks at MSB and apply shadows etc.)
476 -00- ---- ---- ---- | UNUSED
477 ---h ---- ---- ---- | HSS (High Speed Shrink)
478 ---- p--- ---- ---- | PCLIP (Pre Clipping Disable)
479 ---- -c-- ---- ---- | CLIP (Clipping Mode Bit)
480 ---- --m- ---- ---- | CMOD (User Clipping Enable Bit)
481 ---- ---M ---- ---- | MESH (Mesh Enable Bit)
482 ---- ---- e--- ---- | ECD (End Code Disable)
483 ---- ---- -S-- ---- | SPD (Transparent Pixel Disable)
484 ---- ---- --cc c--- | Colour Mode
485 ---- ---- ---- -CCC | Colour Calculation bits
486 ---- ---- ---- -1-- | Gouraud shading enable
487 ---- ---- ---- --1- | 1/2 original GFX enable
488 ---- ---- ---- ---1 | 1/2 background enable
489
490 06 CMDCOLR
491 mmmm mmmm mmmm mmmm | Colour Bank, Colour Lookup /8
492
493 08 CMDSRCA (Character Address)
494 aaaa aaaa aaaa aa-- | Character Address
495 ---- ---- ---- --00 | UNUSED
496
497 0a CMDSIZE (Character Size)
498 00-- ---- ---- ---- | UNUSED
499 --xx xxxx ---- ---- | Character Size (X)
500 ---- ---- yyyy yyyy | Character Size (Y)
501
502 0c CMDXA (used for normal sprite)
503 eeee ee-- ---- ---- | extension bits
504 ---- --xx xxxx xxxx | x position
505
506 0e CMDYA (used for normal sprite)
507 eeee ee-- ---- ---- | extension bits
508 ---- --yy yyyy yyyy | y position
509
510 10 CMDXB
511 12 CMDYB
512 14 CMDXC
513 16 CMDYC
514 18 CMDXD
515 1a CMDYD
516 1c CMDGRDA (Gouraud Shading Table)
517 1e UNUSED
518 ---
519
520
521 */
522
stv_clear_gouraud_shading(void)523 void saturn_state::stv_clear_gouraud_shading(void)
524 {
525 memset( &stv_gouraud_shading, 0, sizeof( stv_gouraud_shading ) );
526 }
527
stv_read_gouraud_table(void)528 uint8_t saturn_state::stv_read_gouraud_table( void )
529 {
530 int gaddr;
531
532 if ( stv2_current_sprite.CMDPMOD & 0x4 )
533 {
534 gaddr = stv2_current_sprite.CMDGRDA * 8;
535 stv_gouraud_shading.GA = (m_vdp1_vram[gaddr/4] >> 16) & 0xffff;
536 stv_gouraud_shading.GB = (m_vdp1_vram[gaddr/4] >> 0) & 0xffff;
537 stv_gouraud_shading.GC = (m_vdp1_vram[gaddr/4 + 1] >> 16) & 0xffff;
538 stv_gouraud_shading.GD = (m_vdp1_vram[gaddr/4 + 1] >> 0) & 0xffff;
539 return 1;
540 }
541 else
542 {
543 return 0;
544 }
545 }
546
_shading(int32_t color,int32_t correction)547 static inline int32_t _shading( int32_t color, int32_t correction )
548 {
549 correction = (correction >> 16) & 0x1f;
550 color += (correction - 16);
551
552 if ( color < 0 ) color = 0;
553 if ( color > 0x1f ) color = 0x1f;
554
555 return color;
556 }
557
stv_vdp1_apply_gouraud_shading(int x,int y,uint16_t pix)558 uint16_t saturn_state::stv_vdp1_apply_gouraud_shading( int x, int y, uint16_t pix )
559 {
560 int32_t r,g,b, msb;
561
562 msb = pix & 0x8000;
563
564 #ifdef MAME_DEBUG
565 if ( (stv_vdp1_shading_data->scanline[y].x[0] >> 16) != x )
566 {
567 logerror( "ERROR in computing x coordinates (line %d, x = %x, %d, xc = %x, %d)\n", y, x, x, stv_vdp1_shading_data->scanline[y].x[0], stv_vdp1_shading_data->scanline[y].x[0] >> 16 );
568 };
569 #endif
570
571 b = RGB_B(pix);
572 g = RGB_G(pix);
573 r = RGB_R(pix);
574
575 b = _shading( b, stv_vdp1_shading_data->scanline[y].b[0] );
576 g = _shading( g, stv_vdp1_shading_data->scanline[y].g[0] );
577 r = _shading( r, stv_vdp1_shading_data->scanline[y].r[0] );
578
579 stv_vdp1_shading_data->scanline[y].b[0] += stv_vdp1_shading_data->scanline[y].db;
580 stv_vdp1_shading_data->scanline[y].g[0] += stv_vdp1_shading_data->scanline[y].dg;
581 stv_vdp1_shading_data->scanline[y].r[0] += stv_vdp1_shading_data->scanline[y].dr;
582
583 stv_vdp1_shading_data->scanline[y].x[0] += 1 << FRAC_SHIFT;
584
585 return msb | b << 10 | g << 5 | r;
586 }
587
stv_vdp1_setup_shading_for_line(int32_t y,int32_t x1,int32_t x2,int32_t r1,int32_t g1,int32_t b1,int32_t r2,int32_t g2,int32_t b2)588 void saturn_state::stv_vdp1_setup_shading_for_line(int32_t y, int32_t x1, int32_t x2,
589 int32_t r1, int32_t g1, int32_t b1,
590 int32_t r2, int32_t g2, int32_t b2)
591 {
592 int xx1 = x1>>FRAC_SHIFT;
593 int xx2 = x2>>FRAC_SHIFT;
594
595
596 if ( xx1 > xx2 )
597 {
598 SWAP_INT32(xx1, xx2);
599 SWAP_INT32(r1, r2);
600 SWAP_INT32(g1, g2);
601 SWAP_INT32(b1, b2);
602 }
603
604 if ( (y >= 0) && (y < 512) )
605 {
606 int32_t dx;
607 int32_t gbd, ggd, grd;
608
609 dx = xx2 - xx1;
610
611 if ( dx == 0 )
612 {
613 gbd = ggd = grd = 0;
614 }
615 else
616 {
617 gbd = abs(b2 - b1) / dx;
618 if (b2 < b1) gbd = -gbd;
619 ggd = abs(g2 - g1) / dx;
620 if (g2 < g1) ggd = -ggd;
621 grd = abs(r2 - r1) / dx;
622 if (r2 < r1) grd = -grd;
623 }
624
625 stv_vdp1_shading_data->scanline[y].x[0] = x1;
626 stv_vdp1_shading_data->scanline[y].x[1] = x2;
627
628 stv_vdp1_shading_data->scanline[y].b[0] = b1;
629 stv_vdp1_shading_data->scanline[y].g[0] = g1;
630 stv_vdp1_shading_data->scanline[y].r[0] = r1;
631 stv_vdp1_shading_data->scanline[y].b[1] = b2;
632 stv_vdp1_shading_data->scanline[y].g[1] = g2;
633 stv_vdp1_shading_data->scanline[y].r[1] = r2;
634
635 stv_vdp1_shading_data->scanline[y].db = gbd;
636 stv_vdp1_shading_data->scanline[y].dg = ggd;
637 stv_vdp1_shading_data->scanline[y].dr = grd;
638
639 }
640 }
641
stv_vdp1_setup_shading_for_slope(int32_t x1,int32_t x2,int32_t sl1,int32_t sl2,int32_t * nx1,int32_t * nx2,int32_t r1,int32_t r2,int32_t slr1,int32_t slr2,int32_t * nr1,int32_t * nr2,int32_t g1,int32_t g2,int32_t slg1,int32_t slg2,int32_t * ng1,int32_t * ng2,int32_t b1,int32_t b2,int32_t slb1,int32_t slb2,int32_t * nb1,int32_t * nb2,int32_t _y1,int32_t y2)642 void saturn_state::stv_vdp1_setup_shading_for_slope(
643 int32_t x1, int32_t x2, int32_t sl1, int32_t sl2, int32_t *nx1, int32_t *nx2,
644 int32_t r1, int32_t r2, int32_t slr1, int32_t slr2, int32_t *nr1, int32_t *nr2,
645 int32_t g1, int32_t g2, int32_t slg1, int32_t slg2, int32_t *ng1, int32_t *ng2,
646 int32_t b1, int32_t b2, int32_t slb1, int32_t slb2, int32_t *nb1, int32_t *nb2,
647 int32_t _y1, int32_t y2)
648 {
649 if(x1 > x2 || (x1==x2 && sl1 > sl2)) {
650 SWAP_INT32(x1,x2);
651 SWAP_INT32(sl1,sl2);
652 SWAP_INT32PTR(nx1, nx2);
653 SWAP_INT32(r1,r2);
654 SWAP_INT32(slr1, slr2);
655 SWAP_INT32PTR(nr1, nr2);
656 SWAP_INT32(g1, g2);
657 SWAP_INT32(slg1, slg2);
658 SWAP_INT32PTR(ng1, ng2);
659 SWAP_INT32(b1, b2);
660 SWAP_INT32(slb1, slb2);
661 SWAP_INT32PTR(nb1, nb2);
662 }
663
664 while(_y1 < y2)
665 {
666 stv_vdp1_setup_shading_for_line(_y1, x1, x2, r1, g1, b1, r2, g2, b2);
667 x1 += sl1;
668 r1 += slr1;
669 g1 += slg1;
670 b1 += slb1;
671
672 x2 += sl2;
673 r2 += slr2;
674 g2 += slg2;
675 b2 += slb2;
676 _y1++;
677 }
678 *nx1 = x1;
679 *nr1 = r1;
680 *ng1 = g1;
681 *nb1 = b1;
682
683 *nx2 = x2;
684 *nr2 = r2;
685 *nb2 = b2;
686 *ng2 = g2;
687 }
688
stv_vdp1_setup_shading(const struct spoint * q,const rectangle & cliprect)689 void saturn_state::stv_vdp1_setup_shading(const struct spoint* q, const rectangle &cliprect)
690 {
691 int32_t x1, x2, delta, cury, limy;
692 int32_t r1, g1, b1, r2, g2, b2;
693 int32_t sl1, slg1, slb1, slr1;
694 int32_t sl2, slg2, slb2, slr2;
695 int pmin, pmax, i, ps1, ps2;
696 struct shaded_point p[8];
697 uint16_t gd[4];
698
699 if ( stv_read_gouraud_table() == 0 ) return;
700
701 gd[0] = stv_gouraud_shading.GA;
702 gd[1] = stv_gouraud_shading.GB;
703 gd[2] = stv_gouraud_shading.GC;
704 gd[3] = stv_gouraud_shading.GD;
705
706 for(i=0; i<4; i++) {
707 p[i].x = p[i+4].x = q[i].x << FRAC_SHIFT;
708 p[i].y = p[i+4].y = q[i].y;
709 p[i].r = p[i+4].r = RGB_R(gd[i]) << FRAC_SHIFT;
710 p[i].g = p[i+4].g = RGB_G(gd[i]) << FRAC_SHIFT;
711 p[i].b = p[i+4].b = RGB_B(gd[i]) << FRAC_SHIFT;
712 }
713
714 pmin = pmax = 0;
715 for(i=1; i<4; i++) {
716 if(p[i].y < p[pmin].y)
717 pmin = i;
718 if(p[i].y > p[pmax].y)
719 pmax = i;
720 }
721
722 cury = p[pmin].y;
723 limy = p[pmax].y;
724
725 stv_vdp1_shading_data->sy = cury;
726 stv_vdp1_shading_data->ey = limy;
727
728 if(cury == limy) {
729 x1 = x2 = p[0].x;
730 ps1 = ps2 = 0;
731 for(i=1; i<4; i++) {
732 if(p[i].x < x1) {
733 x1 = p[i].x;
734 ps1 = i;
735 }
736 if(p[i].x > x2) {
737 x2 = p[i].x;
738 ps2 = i;
739 }
740 }
741 stv_vdp1_setup_shading_for_line(cury, x1, x2, p[ps1].r, p[ps1].g, p[ps1].b, p[ps2].r, p[ps2].g, p[ps2].b);
742 goto finish;
743 }
744
745 ps1 = pmin+4;
746 ps2 = pmin;
747
748 goto startup;
749
750 for(;;) {
751 if(p[ps1-1].y == p[ps2+1].y) {
752 stv_vdp1_setup_shading_for_slope(
753 x1, x2, sl1, sl2, &x1, &x2,
754 r1, r2, slr1, slr2, &r1, &r2,
755 g1, g2, slg1, slg2, &g1, &g2,
756 b1, b2, slb1, slb2, &b1, &b2,
757 cury, p[ps1-1].y);
758 cury = p[ps1-1].y;
759 if(cury >= limy)
760 break;
761 ps1--;
762 ps2++;
763
764 startup:
765 while(p[ps1-1].y == cury)
766 ps1--;
767 while(p[ps2+1].y == cury)
768 ps2++;
769 x1 = p[ps1].x;
770 r1 = p[ps1].r;
771 g1 = p[ps1].g;
772 b1 = p[ps1].b;
773 x2 = p[ps2].x;
774 r2 = p[ps2].r;
775 g2 = p[ps2].g;
776 b2 = p[ps2].b;
777
778 delta = cury-p[ps1-1].y;
779 sl1 = (x1-p[ps1-1].x)/delta;
780 slr1 = (r1-p[ps1-1].r)/delta;
781 slg1 = (g1-p[ps1-1].g)/delta;
782 slb1 = (b1-p[ps1-1].b)/delta;
783
784 delta = cury-p[ps2+1].y;
785 sl2 = (x2-p[ps2+1].x)/delta;
786 slr2 = (r2-p[ps2+1].r)/delta;
787 slg2 = (g2-p[ps2+1].g)/delta;
788 slb2 = (b2-p[ps2+1].b)/delta;
789 } else if(p[ps1-1].y < p[ps2+1].y) {
790 stv_vdp1_setup_shading_for_slope(
791 x1, x2, sl1, sl2, &x1, &x2,
792 r1, r2, slr1, slr2, &r1, &r2,
793 g1, g2, slg1, slg2, &g1, &g2,
794 b1, b2, slb1, slb2, &b1, &b2,
795 cury, p[ps1-1].y);
796 cury = p[ps1-1].y;
797 if(cury >= limy)
798 break;
799 ps1--;
800 while(p[ps1-1].y == cury)
801 ps1--;
802 x1 = p[ps1].x;
803 r1 = p[ps1].r;
804 g1 = p[ps1].g;
805 b1 = p[ps1].b;
806
807 delta = cury-p[ps1-1].y;
808 sl1 = (x1-p[ps1-1].x)/delta;
809 slr1 = (r1-p[ps1-1].r)/delta;
810 slg1 = (g1-p[ps1-1].g)/delta;
811 slb1 = (b1-p[ps1-1].b)/delta;
812 } else {
813 stv_vdp1_setup_shading_for_slope(
814 x1, x2, sl1, sl2, &x1, &x2,
815 r1, r2, slr1, slr2, &r1, &r2,
816 g1, g2, slg1, slg2, &g1, &g2,
817 b1, b2, slb1, slb2, &b1, &b2,
818 cury, p[ps2+1].y);
819 cury = p[ps2+1].y;
820 if(cury >= limy)
821 break;
822 ps2++;
823 while(p[ps2+1].y == cury)
824 ps2++;
825 x2 = p[ps2].x;
826 r2 = p[ps2].r;
827 g2 = p[ps2].g;
828 b2 = p[ps2].b;
829
830 delta = cury-p[ps2+1].y;
831 sl2 = (x2-p[ps2+1].x)/delta;
832 slr2 = (r2-p[ps2+1].r)/delta;
833 slg2 = (g2-p[ps2+1].g)/delta;
834 slb2 = (b2-p[ps2+1].b)/delta;
835 }
836 }
837 if(cury == limy)
838 stv_vdp1_setup_shading_for_line(cury, x1, x2, r1, g1, b1, r2, g2, b2 );
839
840 finish:
841
842 if ( stv_vdp1_shading_data->sy < 0 ) stv_vdp1_shading_data->sy = 0;
843 if ( stv_vdp1_shading_data->sy >= 512 ) return;
844 if ( stv_vdp1_shading_data->ey < 0 ) return;
845 if ( stv_vdp1_shading_data->ey >= 512 ) stv_vdp1_shading_data->ey = 511;
846
847 for ( cury = stv_vdp1_shading_data->sy; cury <= stv_vdp1_shading_data->ey; cury++ )
848 {
849 while( (stv_vdp1_shading_data->scanline[cury].x[0] >> 16) < cliprect.min_x )
850 {
851 stv_vdp1_shading_data->scanline[cury].x[0] += (1 << FRAC_SHIFT);
852 stv_vdp1_shading_data->scanline[cury].b[0] += stv_vdp1_shading_data->scanline[cury].db;
853 stv_vdp1_shading_data->scanline[cury].g[0] += stv_vdp1_shading_data->scanline[cury].dg;
854 stv_vdp1_shading_data->scanline[cury].r[0] += stv_vdp1_shading_data->scanline[cury].dr;
855 }
856 }
857
858 }
859
860 /* note that if we're drawing
861 to the framebuffer we CAN'T frameskip the vdp1 drawing as the hardware can READ the framebuffer
862 and if we skip the drawing the content could be incorrect when it reads it, although i have no idea
863 why they would want to */
864
865
866
drawpixel_poly(int x,int y,int patterndata,int offsetcnt)867 void saturn_state::drawpixel_poly(int x, int y, int patterndata, int offsetcnt)
868 {
869 /* Capcom Collection Dai 4 uses a dummy polygon to clear VDP1 framebuffer that goes over our current max size ... */
870 if(x >= 1024 || y >= 512)
871 return;
872
873 m_vdp1.framebuffer_draw_lines[y][x] = stv2_current_sprite.CMDCOLR;
874 }
875
drawpixel_8bpp_trans(int x,int y,int patterndata,int offsetcnt)876 void saturn_state::drawpixel_8bpp_trans(int x, int y, int patterndata, int offsetcnt)
877 {
878 uint16_t pix;
879
880 pix = m_vdp1.gfx_decode[patterndata+offsetcnt] & 0xff;
881 if ( pix != 0 )
882 {
883 m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
884 }
885 }
886
drawpixel_4bpp_notrans(int x,int y,int patterndata,int offsetcnt)887 void saturn_state::drawpixel_4bpp_notrans(int x, int y, int patterndata, int offsetcnt)
888 {
889 uint16_t pix;
890
891 pix = m_vdp1.gfx_decode[patterndata+offsetcnt/2];
892 pix = offsetcnt&1 ? (pix & 0x0f) : ((pix & 0xf0)>>4);
893 m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
894 }
895
drawpixel_4bpp_trans(int x,int y,int patterndata,int offsetcnt)896 void saturn_state::drawpixel_4bpp_trans(int x, int y, int patterndata, int offsetcnt)
897 {
898 uint16_t pix;
899
900 pix = m_vdp1.gfx_decode[patterndata+offsetcnt/2];
901 pix = offsetcnt&1 ? (pix & 0x0f) : ((pix & 0xf0)>>4);
902 if ( pix != 0 )
903 m_vdp1.framebuffer_draw_lines[y][x] = pix | m_sprite_colorbank;
904 }
905
drawpixel_generic(int x,int y,int patterndata,int offsetcnt)906 void saturn_state::drawpixel_generic(int x, int y, int patterndata, int offsetcnt)
907 {
908 int pix,transpen, spd = stv2_current_sprite.CMDPMOD & 0x40;
909 // int mode;
910 int mesh = stv2_current_sprite.CMDPMOD & 0x100;
911 int raw,endcode;
912
913 if ( mesh && !((x ^ y) & 1) )
914 {
915 return;
916 }
917
918 if(x >= 1024 || y >= 512)
919 return;
920
921 if ( stv2_current_sprite.ispoly )
922 {
923 raw = pix = stv2_current_sprite.CMDCOLR&0xffff;
924
925 transpen = 0;
926 endcode = 0xffff;
927 #if 0
928 if ( pix & 0x8000 )
929 {
930 mode = 5;
931 }
932 else
933 {
934 mode = 1;
935 }
936 #endif
937 }
938 else
939 {
940 switch (stv2_current_sprite.CMDPMOD&0x0038)
941 {
942 case 0x0000: // mode 0 16 colour bank mode (4bits) (hanagumi blocks)
943 // most of the shienryu sprites use this mode
944 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt/2) & 0xfffff];
945 raw = offsetcnt&1 ? (raw & 0x0f) : ((raw & 0xf0)>>4);
946 pix = raw+((stv2_current_sprite.CMDCOLR&0xfff0));
947 //mode = 0;
948 transpen = 0;
949 endcode = 0xf;
950 break;
951 case 0x0008: // mode 1 16 colour lookup table mode (4bits)
952 // shienryu explosions (and some enemies) use this mode
953 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt/2) & 0xfffff];
954 raw = offsetcnt&1 ? (raw & 0x0f) : ((raw & 0xf0)>>4);
955 pix = raw&1 ?
956 ((((m_vdp1_vram[(((stv2_current_sprite.CMDCOLR&0xffff)*8)>>2)+((raw&0xfffe)/2)])) & 0x0000ffff) >> 0):
957 ((((m_vdp1_vram[(((stv2_current_sprite.CMDCOLR&0xffff)*8)>>2)+((raw&0xfffe)/2)])) & 0xffff0000) >> 16);
958 //mode = 5;
959 transpen = 0;
960 endcode = 0xf;
961 break;
962 case 0x0010: // mode 2 64 colour bank mode (8bits) (character select portraits on hanagumi)
963 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff] & 0xff;
964 //mode = 2;
965 pix = raw+(stv2_current_sprite.CMDCOLR&0xffc0);
966 transpen = 0;
967 endcode = 0xff;
968 // Notes of interest:
969 // Scud: the disposable assassin wants transparent pen on 0
970 // sasissu: racing stage background clouds
971 break;
972 case 0x0018: // mode 3 128 colour bank mode (8bits) (little characters on hanagumi use this mode)
973 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff] & 0xff;
974 pix = raw+(stv2_current_sprite.CMDCOLR&0xff80);
975 transpen = 0;
976 endcode = 0xff;
977 //mode = 3;
978 break;
979 case 0x0020: // mode 4 256 colour bank mode (8bits) (hanagumi title)
980 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt) & 0xfffff] & 0xff;
981 pix = raw+(stv2_current_sprite.CMDCOLR&0xff00);
982 transpen = 0;
983 endcode = 0xff;
984 //mode = 4;
985 break;
986 case 0x0028: // mode 5 32,768 colour RGB mode (16bits)
987 raw = m_vdp1.gfx_decode[(patterndata+offsetcnt*2+1) & 0xfffff] | (m_vdp1.gfx_decode[(patterndata+offsetcnt*2) & 0xfffff]<<8);
988 //mode = 5;
989 // TODO: 0x1-0x7ffe reserved (color bank)
990 pix = raw;
991 transpen = 0;
992 endcode = 0x7fff;
993 break;
994 case 0x0038: // invalid
995 // game tengoku uses this on hi score screen (tate mode)
996 // according to Charles, reads from VRAM address 0
997 raw = pix = m_vdp1.gfx_decode[1] | (m_vdp1.gfx_decode[0]<<8) ;
998 // TODO: check transpen
999 transpen = 0;
1000 endcode = -1;
1001 break;
1002 default: // other settings illegal
1003 pix = machine().rand();
1004 raw = pix & 0xff; // just mimic old driver behavior
1005 //mode = 0;
1006 transpen = 0;
1007 endcode = 0xff;
1008 popmessage("Illegal Sprite Mode %02x, contact MAMEdev",stv2_current_sprite.CMDPMOD&0x0038);
1009 }
1010
1011
1012 // preliminary end code disable support
1013 if ( ((stv2_current_sprite.CMDPMOD & 0x80) == 0) &&
1014 (raw == endcode) )
1015 {
1016 return;
1017 }
1018 }
1019
1020 /* MSBON */
1021 // TODO: does this always applies to the frame buffer regardless of the mode?
1022 pix |= stv2_current_sprite.CMDPMOD & 0x8000;
1023 /*
1024 TODO: from docs:
1025 "Except for the color calculation of replace and shadow, color calculation can only be performed when the color code of the original picture is RGB code.
1026 Color calculation can be executed when the color code is color bank code, but the results are not guaranteed."
1027 Currently no idea about the "result not guaranteed" part, let's disable this branch for the time being ...
1028 */
1029 #if 0
1030 if ( mode != 5 )
1031 {
1032 if ( (raw != transpen) || spd )
1033 {
1034 m_vdp1.framebuffer_draw_lines[y][x] = pix;
1035 }
1036 }
1037 else
1038 #endif
1039 {
1040 if ( (raw != transpen) || spd )
1041 {
1042 if ( stv2_current_sprite.CMDPMOD & 0x4 ) /* Gouraud shading */
1043 pix = stv_vdp1_apply_gouraud_shading( x, y, pix );
1044
1045 switch( stv2_current_sprite.CMDPMOD & 0x3 )
1046 {
1047 case 0: /* replace */
1048 m_vdp1.framebuffer_draw_lines[y][x] = pix;
1049 break;
1050 case 1: /* shadow */
1051 if ( m_vdp1.framebuffer_draw_lines[y][x] & 0x8000 )
1052 {
1053 m_vdp1.framebuffer_draw_lines[y][x] = ((m_vdp1.framebuffer_draw_lines[y][x] & ~0x8421) >> 1) | 0x8000;
1054 }
1055 break;
1056 case 2: /* half luminance */
1057 m_vdp1.framebuffer_draw_lines[y][x] = ((pix & ~0x8421) >> 1) | 0x8000;
1058 break;
1059 case 3: /* half transparent */
1060 if ( m_vdp1.framebuffer_draw_lines[y][x] & 0x8000 )
1061 {
1062 m_vdp1.framebuffer_draw_lines[y][x] = alpha_blend_r16( m_vdp1.framebuffer_draw_lines[y][x], pix, 0x80 ) | 0x8000;
1063 }
1064 else
1065 {
1066 m_vdp1.framebuffer_draw_lines[y][x] = pix;
1067 }
1068 break;
1069 //case 4: /* Gouraud shading */
1070 // TODO: Pro Yakyuu Team mo Tsukurou (during team creation, on PR girl select)
1071 //case 6:
1072 // break;
1073 //case 7: /* Gouraud-shading + half-transparent */
1074 // Lupin the 3rd Pyramid no Kenja enemy shadows
1075 // Death Crimson lives indicators
1076 // TODO: latter looks really bad.
1077 default:
1078 // TODO: mode 5: prohibited, mode 6: gouraud shading + half-luminance, mode 7: gouraud-shading + half-transparent
1079 popmessage("VDP1 PMOD = %02x, contact MAMEdev",stv2_current_sprite.CMDPMOD & 0x7);
1080 m_vdp1.framebuffer_draw_lines[y][x] = pix;
1081 break;
1082 }
1083 }
1084 }
1085 }
1086
1087
stv_vdp1_set_drawpixel(void)1088 void saturn_state::stv_vdp1_set_drawpixel( void )
1089 {
1090 int sprite_type = stv2_current_sprite.CMDCTRL & 0x000f;
1091 int sprite_mode = stv2_current_sprite.CMDPMOD&0x0038;
1092 int spd = stv2_current_sprite.CMDPMOD & 0x40;
1093 int mesh = stv2_current_sprite.CMDPMOD & 0x100;
1094 int ecd = stv2_current_sprite.CMDPMOD & 0x80;
1095
1096 if ( mesh || !ecd || ((stv2_current_sprite.CMDPMOD & 0x7) != 0) )
1097 {
1098 drawpixel = &saturn_state::drawpixel_generic;
1099 return;
1100 }
1101
1102 if(stv2_current_sprite.CMDPMOD & 0x8000)
1103 {
1104 drawpixel = &saturn_state::drawpixel_generic;
1105 return;
1106 }
1107
1108 // polygon / polyline / line with replace case
1109 if (sprite_type & 4 && ((stv2_current_sprite.CMDPMOD & 0x7) == 0))
1110 {
1111 drawpixel = &saturn_state::drawpixel_poly;
1112 }
1113 else if ( (sprite_mode == 0x20) && !spd )
1114 {
1115 m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xff00);
1116 drawpixel = &saturn_state::drawpixel_8bpp_trans;
1117 }
1118 else if ((sprite_mode == 0x00) && spd)
1119 {
1120 m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xfff0);
1121 drawpixel = &saturn_state::drawpixel_4bpp_notrans;
1122 }
1123 else if (sprite_mode == 0x00 && !spd )
1124 {
1125 m_sprite_colorbank = (stv2_current_sprite.CMDCOLR&0xfff0);
1126 drawpixel = &saturn_state::drawpixel_4bpp_trans;
1127 }
1128 else
1129 {
1130 drawpixel = &saturn_state::drawpixel_generic;
1131 }
1132 }
1133
1134
vdp1_fill_slope(const rectangle & cliprect,int patterndata,int xsize,int32_t x1,int32_t x2,int32_t sl1,int32_t sl2,int32_t * nx1,int32_t * nx2,int32_t u1,int32_t u2,int32_t slu1,int32_t slu2,int32_t * nu1,int32_t * nu2,int32_t v1,int32_t v2,int32_t slv1,int32_t slv2,int32_t * nv1,int32_t * nv2,int32_t _y1,int32_t y2)1135 void saturn_state::vdp1_fill_slope(const rectangle &cliprect, int patterndata, int xsize,
1136 int32_t x1, int32_t x2, int32_t sl1, int32_t sl2, int32_t *nx1, int32_t *nx2,
1137 int32_t u1, int32_t u2, int32_t slu1, int32_t slu2, int32_t *nu1, int32_t *nu2,
1138 int32_t v1, int32_t v2, int32_t slv1, int32_t slv2, int32_t *nv1, int32_t *nv2,
1139 int32_t _y1, int32_t y2)
1140 {
1141 if(_y1 > cliprect.max_y)
1142 return;
1143
1144 if(y2 <= cliprect.min_y) {
1145 int delta = y2-_y1;
1146 *nx1 = x1+delta*sl1;
1147 *nu1 = u1+delta*slu1;
1148 *nv1 = v1+delta*slv1;
1149 *nx2 = x2+delta*sl2;
1150 *nu2 = u2+delta*slu2;
1151 *nv2 = v2+delta*slv2;
1152 return;
1153 }
1154
1155 if(y2 > cliprect.max_y)
1156 y2 = cliprect.max_y+1;
1157
1158 if(_y1 < cliprect.min_y) {
1159 int delta = cliprect.min_y - _y1;
1160 x1 += delta*sl1;
1161 u1 += delta*slu1;
1162 v1 += delta*slv1;
1163 x2 += delta*sl2;
1164 u2 += delta*slu2;
1165 v2 += delta*slv2;
1166 _y1 = cliprect.min_y;
1167 }
1168
1169 if(x1 > x2 || (x1==x2 && sl1 > sl2)) {
1170 int32_t t, *tp;
1171 t = x1;
1172 x1 = x2;
1173 x2 = t;
1174 t = sl1;
1175 sl1 = sl2;
1176 sl2 = t;
1177 tp = nx1;
1178 nx1 = nx2;
1179 nx2 = tp;
1180
1181 t = u1;
1182 u1 = u2;
1183 u2 = t;
1184 t = slu1;
1185 slu1 = slu2;
1186 slu2 = t;
1187 tp = nu1;
1188 nu1 = nu2;
1189 nu2 = tp;
1190
1191 t = v1;
1192 v1 = v2;
1193 v2 = t;
1194 t = slv1;
1195 slv1 = slv2;
1196 slv2 = t;
1197 tp = nv1;
1198 nv1 = nv2;
1199 nv2 = tp;
1200 }
1201
1202 while(_y1 < y2) {
1203 if(_y1 >= cliprect.min_y) {
1204 int32_t slux = 0, slvx = 0;
1205 int xx1 = x1>>FRAC_SHIFT;
1206 int xx2 = x2>>FRAC_SHIFT;
1207 int32_t u = u1;
1208 int32_t v = v1;
1209 if(xx1 != xx2) {
1210 int delta = xx2-xx1;
1211 slux = (u2-u1)/delta;
1212 slvx = (v2-v1)/delta;
1213 }
1214 if(xx1 <= cliprect.max_x || xx2 >= cliprect.min_x) {
1215 if(xx1 < cliprect.min_x) {
1216 int delta = cliprect.min_x-xx1;
1217 u += slux*delta;
1218 v += slvx*delta;
1219 xx1 = cliprect.min_x;
1220 }
1221 if(xx2 > cliprect.max_x)
1222 xx2 = cliprect.max_x;
1223
1224 while(xx1 <= xx2) {
1225 (this->*drawpixel)(xx1,_y1, patterndata, (v>>FRAC_SHIFT)*xsize+(u>>FRAC_SHIFT));
1226 xx1++;
1227 u += slux;
1228 v += slvx;
1229 }
1230 }
1231 }
1232
1233 x1 += sl1;
1234 u1 += slu1;
1235 v1 += slv1;
1236 x2 += sl2;
1237 u2 += slu2;
1238 v2 += slv2;
1239 _y1++;
1240 }
1241 *nx1 = x1;
1242 *nu1 = u1;
1243 *nv1 = v1;
1244 *nx2 = x2;
1245 *nu2 = u2;
1246 *nv2 = v2;
1247 }
1248
vdp1_fill_line(const rectangle & cliprect,int patterndata,int xsize,int32_t y,int32_t x1,int32_t x2,int32_t u1,int32_t u2,int32_t v1,int32_t v2)1249 void saturn_state::vdp1_fill_line(const rectangle &cliprect, int patterndata, int xsize, int32_t y,
1250 int32_t x1, int32_t x2, int32_t u1, int32_t u2, int32_t v1, int32_t v2)
1251 {
1252 int xx1 = x1>>FRAC_SHIFT;
1253 int xx2 = x2>>FRAC_SHIFT;
1254
1255 if(y > cliprect.max_y || y < cliprect.min_y)
1256 return;
1257
1258 if(xx1 <= cliprect.max_x || xx2 >= cliprect.min_x) {
1259 int32_t slux = 0, slvx = 0;
1260 int32_t u = u1;
1261 int32_t v = v1;
1262 if(xx1 != xx2) {
1263 int delta = xx2-xx1;
1264 slux = (u2-u1)/delta;
1265 slvx = (v2-v1)/delta;
1266 }
1267 if(xx1 < cliprect.min_x) {
1268 int delta = cliprect.min_x-xx1;
1269 u += slux*delta;
1270 v += slvx*delta;
1271 xx1 = cliprect.min_x;
1272 }
1273 if(xx2 > cliprect.max_x)
1274 xx2 = cliprect.max_x;
1275
1276 while(xx1 <= xx2) {
1277 (this->*drawpixel)(xx1,y,patterndata,(v>>FRAC_SHIFT)*xsize+(u>>FRAC_SHIFT));
1278 xx1++;
1279 u += slux;
1280 v += slvx;
1281 }
1282 }
1283 }
1284
vdp1_fill_quad(const rectangle & cliprect,int patterndata,int xsize,const struct spoint * q)1285 void saturn_state::vdp1_fill_quad(const rectangle &cliprect, int patterndata, int xsize, const struct spoint *q)
1286 {
1287 int32_t sl1, sl2, slu1, slu2, slv1, slv2, cury, limy, x1, x2, u1, u2, v1, v2, delta;
1288 int pmin, pmax, i, ps1, ps2;
1289 struct spoint p[8];
1290
1291 for(i=0; i<4; i++) {
1292 p[i].x = p[i+4].x = q[i].x << FRAC_SHIFT;
1293 p[i].y = p[i+4].y = q[i].y;
1294 p[i].u = p[i+4].u = q[i].u << FRAC_SHIFT;
1295 p[i].v = p[i+4].v = q[i].v << FRAC_SHIFT;
1296 }
1297
1298 pmin = pmax = 0;
1299 for(i=1; i<4; i++) {
1300 if(p[i].y < p[pmin].y)
1301 pmin = i;
1302 if(p[i].y > p[pmax].y)
1303 pmax = i;
1304 }
1305
1306 cury = p[pmin].y;
1307 limy = p[pmax].y;
1308
1309 if(cury == limy) {
1310 x1 = x2 = p[0].x;
1311 u1 = u2 = p[0].u;
1312 v1 = v2 = p[0].v;
1313 for(i=1; i<4; i++) {
1314 if(p[i].x < x1) {
1315 x1 = p[i].x;
1316 u1 = p[i].u;
1317 v1 = p[i].v;
1318 }
1319 if(p[i].x > x2) {
1320 x2 = p[i].x;
1321 u2 = p[i].u;
1322 v2 = p[i].v;
1323 }
1324 }
1325 vdp1_fill_line(cliprect, patterndata, xsize, cury, x1, x2, u1, u2, v1, v2);
1326 return;
1327 }
1328
1329 if(cury > cliprect.max_y)
1330 return;
1331 if(limy <= cliprect.min_y)
1332 return;
1333
1334 if(limy > cliprect.max_y)
1335 limy = cliprect.max_y;
1336
1337 ps1 = pmin+4;
1338 ps2 = pmin;
1339
1340 goto startup;
1341
1342 for(;;) {
1343 if(p[ps1-1].y == p[ps2+1].y) {
1344 vdp1_fill_slope(cliprect, patterndata, xsize,
1345 x1, x2, sl1, sl2, &x1, &x2,
1346 u1, u2, slu1, slu2, &u1, &u2,
1347 v1, v2, slv1, slv2, &v1, &v2,
1348 cury, p[ps1-1].y);
1349 cury = p[ps1-1].y;
1350 if(cury >= limy)
1351 break;
1352 ps1--;
1353 ps2++;
1354
1355 startup:
1356 while(p[ps1-1].y == cury)
1357 ps1--;
1358 while(p[ps2+1].y == cury)
1359 ps2++;
1360 x1 = p[ps1].x;
1361 u1 = p[ps1].u;
1362 v1 = p[ps1].v;
1363 x2 = p[ps2].x;
1364 u2 = p[ps2].u;
1365 v2 = p[ps2].v;
1366
1367 delta = cury-p[ps1-1].y;
1368 sl1 = (x1-p[ps1-1].x)/delta;
1369 slu1 = (u1-p[ps1-1].u)/delta;
1370 slv1 = (v1-p[ps1-1].v)/delta;
1371
1372 delta = cury-p[ps2+1].y;
1373 sl2 = (x2-p[ps2+1].x)/delta;
1374 slu2 = (u2-p[ps2+1].u)/delta;
1375 slv2 = (v2-p[ps2+1].v)/delta;
1376 } else if(p[ps1-1].y < p[ps2+1].y) {
1377 vdp1_fill_slope(cliprect, patterndata, xsize,
1378 x1, x2, sl1, sl2, &x1, &x2,
1379 u1, u2, slu1, slu2, &u1, &u2,
1380 v1, v2, slv1, slv2, &v1, &v2,
1381 cury, p[ps1-1].y);
1382 cury = p[ps1-1].y;
1383 if(cury >= limy)
1384 break;
1385 ps1--;
1386 while(p[ps1-1].y == cury)
1387 ps1--;
1388 x1 = p[ps1].x;
1389 u1 = p[ps1].u;
1390 v1 = p[ps1].v;
1391
1392 delta = cury-p[ps1-1].y;
1393 sl1 = (x1-p[ps1-1].x)/delta;
1394 slu1 = (u1-p[ps1-1].u)/delta;
1395 slv1 = (v1-p[ps1-1].v)/delta;
1396 } else {
1397 vdp1_fill_slope(cliprect, patterndata, xsize,
1398 x1, x2, sl1, sl2, &x1, &x2,
1399 u1, u2, slu1, slu2, &u1, &u2,
1400 v1, v2, slv1, slv2, &v1, &v2,
1401 cury, p[ps2+1].y);
1402 cury = p[ps2+1].y;
1403 if(cury >= limy)
1404 break;
1405 ps2++;
1406 while(p[ps2+1].y == cury)
1407 ps2++;
1408 x2 = p[ps2].x;
1409 u2 = p[ps2].u;
1410 v2 = p[ps2].v;
1411
1412 delta = cury-p[ps2+1].y;
1413 sl2 = (x2-p[ps2+1].x)/delta;
1414 slu2 = (u2-p[ps2+1].u)/delta;
1415 slv2 = (v2-p[ps2+1].v)/delta;
1416 }
1417 }
1418 if(cury == limy)
1419 vdp1_fill_line(cliprect, patterndata, xsize, cury, x1, x2, u1, u2, v1, v2);
1420 }
1421
x2s(int v)1422 int saturn_state::x2s(int v)
1423 {
1424 return (int32_t)(int16_t)v + m_vdp1.local_x;
1425 }
1426
y2s(int v)1427 int saturn_state::y2s(int v)
1428 {
1429 return (int32_t)(int16_t)v + m_vdp1.local_y;
1430 }
1431
stv_vdp1_draw_line(const rectangle & cliprect)1432 void saturn_state::stv_vdp1_draw_line(const rectangle &cliprect)
1433 {
1434 struct spoint q[4];
1435
1436 q[0].x = x2s(stv2_current_sprite.CMDXA);
1437 q[0].y = y2s(stv2_current_sprite.CMDYA);
1438 q[1].x = x2s(stv2_current_sprite.CMDXB);
1439 q[1].y = y2s(stv2_current_sprite.CMDYB);
1440 q[2].x = x2s(stv2_current_sprite.CMDXA);
1441 q[2].y = y2s(stv2_current_sprite.CMDYA);
1442 q[3].x = x2s(stv2_current_sprite.CMDXB);
1443 q[3].y = y2s(stv2_current_sprite.CMDYB);
1444
1445 q[0].u = q[3].u = q[1].u = q[2].u = 0;
1446 q[0].v = q[1].v = q[2].v = q[3].v = 0;
1447
1448 vdp1_fill_quad(cliprect, 0, 1, q);
1449 }
1450
stv_vdp1_draw_poly_line(const rectangle & cliprect)1451 void saturn_state::stv_vdp1_draw_poly_line(const rectangle &cliprect)
1452 {
1453 struct spoint q[4];
1454
1455 q[0].x = x2s(stv2_current_sprite.CMDXA);
1456 q[0].y = y2s(stv2_current_sprite.CMDYA);
1457 q[1].x = x2s(stv2_current_sprite.CMDXB);
1458 q[1].y = y2s(stv2_current_sprite.CMDYB);
1459 q[2].x = x2s(stv2_current_sprite.CMDXA);
1460 q[2].y = y2s(stv2_current_sprite.CMDYA);
1461 q[3].x = x2s(stv2_current_sprite.CMDXB);
1462 q[3].y = y2s(stv2_current_sprite.CMDYB);
1463
1464 q[0].u = q[3].u = q[1].u = q[2].u = 0;
1465 q[0].v = q[1].v = q[2].v = q[3].v = 0;
1466
1467 vdp1_fill_quad(cliprect, 0, 1, q);
1468
1469 q[0].x = x2s(stv2_current_sprite.CMDXB);
1470 q[0].y = y2s(stv2_current_sprite.CMDYB);
1471 q[1].x = x2s(stv2_current_sprite.CMDXC);
1472 q[1].y = y2s(stv2_current_sprite.CMDYC);
1473 q[2].x = x2s(stv2_current_sprite.CMDXB);
1474 q[2].y = y2s(stv2_current_sprite.CMDYB);
1475 q[3].x = x2s(stv2_current_sprite.CMDXC);
1476 q[3].y = y2s(stv2_current_sprite.CMDYC);
1477
1478 q[0].u = q[3].u = q[1].u = q[2].u = 0;
1479 q[0].v = q[1].v = q[2].v = q[3].v = 0;
1480
1481 vdp1_fill_quad(cliprect, 0, 1, q);
1482
1483 q[0].x = x2s(stv2_current_sprite.CMDXC);
1484 q[0].y = y2s(stv2_current_sprite.CMDYC);
1485 q[1].x = x2s(stv2_current_sprite.CMDXD);
1486 q[1].y = y2s(stv2_current_sprite.CMDYD);
1487 q[2].x = x2s(stv2_current_sprite.CMDXC);
1488 q[2].y = y2s(stv2_current_sprite.CMDYC);
1489 q[3].x = x2s(stv2_current_sprite.CMDXD);
1490 q[3].y = y2s(stv2_current_sprite.CMDYD);
1491
1492 q[0].u = q[3].u = q[1].u = q[2].u = 0;
1493 q[0].v = q[1].v = q[2].v = q[3].v = 0;
1494
1495 vdp1_fill_quad(cliprect, 0, 1, q);
1496
1497 q[0].x = x2s(stv2_current_sprite.CMDXD);
1498 q[0].y = y2s(stv2_current_sprite.CMDYD);
1499 q[1].x = x2s(stv2_current_sprite.CMDXA);
1500 q[1].y = y2s(stv2_current_sprite.CMDYA);
1501 q[2].x = x2s(stv2_current_sprite.CMDXD);
1502 q[2].y = y2s(stv2_current_sprite.CMDYD);
1503 q[3].x = x2s(stv2_current_sprite.CMDXA);
1504 q[3].y = y2s(stv2_current_sprite.CMDYA);
1505
1506 q[0].u = q[3].u = q[1].u = q[2].u = 0;
1507 q[0].v = q[1].v = q[2].v = q[3].v = 0;
1508
1509 stv_vdp1_setup_shading(q, cliprect);
1510 vdp1_fill_quad(cliprect, 0, 1, q);
1511
1512 }
1513
stv_vdp1_draw_distorted_sprite(const rectangle & cliprect)1514 void saturn_state::stv_vdp1_draw_distorted_sprite(const rectangle &cliprect)
1515 {
1516 struct spoint q[4];
1517
1518 int xsize, ysize;
1519 int direction;
1520 int patterndata;
1521
1522 direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
1523
1524 if ( stv2_current_sprite.ispoly )
1525 {
1526 xsize = ysize = 1;
1527 patterndata = 0;
1528 }
1529 else
1530 {
1531 xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
1532 xsize = xsize * 8;
1533 if (xsize == 0) return; /* setting prohibited */
1534
1535 ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
1536 if (ysize == 0) return; /* setting prohibited */
1537
1538 patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
1539 patterndata = patterndata * 0x8;
1540
1541 }
1542
1543
1544 q[0].x = x2s(stv2_current_sprite.CMDXA);
1545 q[0].y = y2s(stv2_current_sprite.CMDYA);
1546 q[1].x = x2s(stv2_current_sprite.CMDXB);
1547 q[1].y = y2s(stv2_current_sprite.CMDYB);
1548 q[2].x = x2s(stv2_current_sprite.CMDXC);
1549 q[2].y = y2s(stv2_current_sprite.CMDYC);
1550 q[3].x = x2s(stv2_current_sprite.CMDXD);
1551 q[3].y = y2s(stv2_current_sprite.CMDYD);
1552
1553 if(direction & 1) { // xflip
1554 q[0].u = q[3].u = xsize-1;
1555 q[1].u = q[2].u = 0;
1556 } else {
1557 q[0].u = q[3].u = 0;
1558 q[1].u = q[2].u = xsize-1;
1559 }
1560 if(direction & 2) { // yflip
1561 q[0].v = q[1].v = ysize-1;
1562 q[2].v = q[3].v = 0;
1563 } else {
1564 q[0].v = q[1].v = 0;
1565 q[2].v = q[3].v = ysize-1;
1566 }
1567
1568 stv_vdp1_setup_shading(q, cliprect);
1569 vdp1_fill_quad(cliprect, patterndata, xsize, q);
1570 }
1571
stv_vdp1_draw_scaled_sprite(const rectangle & cliprect)1572 void saturn_state::stv_vdp1_draw_scaled_sprite(const rectangle &cliprect)
1573 {
1574 struct spoint q[4];
1575
1576 int xsize, ysize;
1577 int direction;
1578 int patterndata;
1579 int zoompoint;
1580 int x,y;
1581 int x2,y2;
1582 int screen_width,screen_height,screen_height_negative = 0;
1583
1584 direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
1585
1586 xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
1587 xsize = xsize * 8;
1588
1589 ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
1590
1591 patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
1592 patterndata = patterndata * 0x8;
1593
1594 zoompoint = (stv2_current_sprite.CMDCTRL & 0x0f00)>>8;
1595
1596 x = stv2_current_sprite.CMDXA;
1597 y = stv2_current_sprite.CMDYA;
1598
1599 screen_width = (int16_t)stv2_current_sprite.CMDXB;
1600 if ( (screen_width < 0) && zoompoint)
1601 {
1602 screen_width = -screen_width;
1603 direction |= 1;
1604 }
1605
1606 screen_height = (int16_t)stv2_current_sprite.CMDYB;
1607 if ( (screen_height < 0) && zoompoint )
1608 {
1609 screen_height_negative = 1;
1610 screen_height = -screen_height;
1611 direction |= 2;
1612 }
1613
1614 x2 = stv2_current_sprite.CMDXC; // second co-ordinate set x
1615 y2 = stv2_current_sprite.CMDYC; // second co-ordinate set y
1616
1617 switch (zoompoint)
1618 {
1619 case 0x0: // specified co-ordinates
1620 break;
1621 case 0x5: // up left
1622 break;
1623 case 0x6: // up center
1624 x -= screen_width/2 ;
1625 break;
1626 case 0x7: // up right
1627 x -= screen_width;
1628 break;
1629
1630 case 0x9: // center left
1631 y -= screen_height/2 ;
1632 break;
1633 case 0xa: // center center
1634 y -= screen_height/2 ;
1635 x -= screen_width/2 ;
1636
1637 break;
1638
1639 case 0xb: // center right
1640 y -= screen_height/2 ;
1641 x -= screen_width;
1642 break;
1643
1644 case 0xd: // center left
1645 y -= screen_height;
1646 break;
1647
1648 case 0xe: // center center
1649 y -= screen_height;
1650 x -= screen_width/2 ;
1651 break;
1652
1653 case 0xf: // center right
1654 y -= screen_height;
1655 x -= screen_width;
1656 break;
1657
1658 default: // illegal
1659 break;
1660
1661 }
1662
1663 /* 0----1
1664 | |
1665 | |
1666 3----2 */
1667
1668 if (zoompoint)
1669 {
1670 q[0].x = x2s(x);
1671 q[0].y = y2s(y);
1672 q[1].x = x2s(x)+screen_width;
1673 q[1].y = y2s(y);
1674 q[2].x = x2s(x)+screen_width;
1675 q[2].y = y2s(y)+screen_height;
1676 q[3].x = x2s(x);
1677 q[3].y = y2s(y)+screen_height;
1678
1679 if ( screen_height_negative )
1680 {
1681 q[0].y += screen_height;
1682 q[1].y += screen_height;
1683 q[2].y += screen_height;
1684 q[3].y += screen_height;
1685 }
1686 }
1687 else
1688 {
1689 q[0].x = x2s(x);
1690 q[0].y = y2s(y);
1691 q[1].x = x2s(x2);
1692 q[1].y = y2s(y);
1693 q[2].x = x2s(x2);
1694 q[2].y = y2s(y2);
1695 q[3].x = x2s(x);
1696 q[3].y = y2s(y2);
1697 }
1698
1699
1700 if(direction & 1) { // xflip
1701 q[0].u = q[3].u = xsize-1;
1702 q[1].u = q[2].u = 0;
1703 } else {
1704 q[0].u = q[3].u = 0;
1705 q[1].u = q[2].u = xsize-1;
1706 }
1707 if(direction & 2) { // yflip
1708 q[0].v = q[1].v = ysize-1;
1709 q[2].v = q[3].v = 0;
1710 } else {
1711 q[0].v = q[1].v = 0;
1712 q[2].v = q[3].v = ysize-1;
1713 }
1714
1715 stv_vdp1_setup_shading(q, cliprect);
1716 vdp1_fill_quad(cliprect, patterndata, xsize, q);
1717 }
1718
1719
1720
1721
stv_vdp1_draw_normal_sprite(const rectangle & cliprect,int sprite_type)1722 void saturn_state::stv_vdp1_draw_normal_sprite(const rectangle &cliprect, int sprite_type)
1723 {
1724 int y, ysize, drawypos;
1725 int x, xsize, drawxpos;
1726 int direction;
1727 int patterndata;
1728 uint8_t shading;
1729 int su, u, dux, duy;
1730 int maxdrawypos, maxdrawxpos;
1731
1732 x = x2s(stv2_current_sprite.CMDXA);
1733 y = y2s(stv2_current_sprite.CMDYA);
1734
1735 direction = (stv2_current_sprite.CMDCTRL & 0x0030)>>4;
1736
1737 xsize = (stv2_current_sprite.CMDSIZE & 0x3f00) >> 8;
1738 xsize = xsize * 8;
1739
1740 ysize = (stv2_current_sprite.CMDSIZE & 0x00ff);
1741
1742 patterndata = (stv2_current_sprite.CMDSRCA) & 0xffff;
1743 patterndata = patterndata * 0x8;
1744
1745 if (VDP1_LOG) logerror ("Drawing Normal Sprite x %04x y %04x xsize %04x ysize %04x patterndata %06x\n",x,y,xsize,ysize,patterndata);
1746
1747 if ( x > cliprect.max_x ) return;
1748 if ( y > cliprect.max_y ) return;
1749
1750 shading = stv_read_gouraud_table();
1751 if ( shading )
1752 {
1753 struct spoint q[4];
1754 q[0].x = x; q[0].y = y;
1755 q[1].x = x + xsize; q[1].y = y;
1756 q[2].x = x + xsize; q[2].y = y + ysize;
1757 q[3].x = x; q[3].y = y + ysize;
1758
1759 stv_vdp1_setup_shading( q, cliprect );
1760 }
1761
1762 u = 0;
1763 dux = 1;
1764 duy = xsize;
1765 if ( direction & 0x1 ) //xflip
1766 {
1767 dux = -1;
1768 u = xsize - 1;
1769 }
1770 if ( direction & 0x2 ) //yflip
1771 {
1772 duy = -xsize;
1773 u += xsize*(ysize-1);
1774 }
1775 if ( y < cliprect.min_y ) //clip y
1776 {
1777 u += xsize*(cliprect.min_y - y);
1778 ysize -= (cliprect.min_y - y);
1779 y = cliprect.min_y;
1780 }
1781 if ( x < cliprect.min_x ) //clip x
1782 {
1783 u += dux*(cliprect.min_x - x);
1784 xsize -= (cliprect.min_x - x);
1785 x = cliprect.min_x;
1786 }
1787 maxdrawypos = std::min(y+ysize-1,cliprect.max_y);
1788 maxdrawxpos = std::min(x+xsize-1,cliprect.max_x);
1789 for (drawypos = y; drawypos <= maxdrawypos; drawypos++ )
1790 {
1791 //destline = m_vdp1.framebuffer_draw_lines[drawypos];
1792 su = u;
1793 for (drawxpos = x; drawxpos <= maxdrawxpos; drawxpos++ )
1794 {
1795 (this->*drawpixel)( drawxpos, drawypos, patterndata, u );
1796 u += dux;
1797 }
1798 u = su + duy;
1799 }
1800 }
1801
TIMER_CALLBACK_MEMBER(saturn_state::vdp1_draw_end)1802 TIMER_CALLBACK_MEMBER(saturn_state::vdp1_draw_end )
1803 {
1804 /* set CEF to 1*/
1805 CEF_1;
1806
1807 // TODO: temporary for Batman Forever, presumably anonymous timer not behaving well.
1808 #if 0
1809 if(!(m_scu.ism & IRQ_VDP1_END))
1810 {
1811 m_maincpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d); // SH2
1812 scu_do_transfer(6);
1813 }
1814 else
1815 m_scu.ist |= (IRQ_VDP1_END);
1816 #endif
1817 }
1818
1819
stv_vdp1_process_list(void)1820 void saturn_state::stv_vdp1_process_list( void )
1821 {
1822 int position;
1823 int spritecount;
1824 int vdp1_nest;
1825 rectangle *cliprect;
1826
1827 spritecount = 0;
1828 position = 0;
1829
1830 if (VDP1_LOG) logerror ("Sprite List Process START\n");
1831
1832 vdp1_nest = -1;
1833
1834 stv_clear_gouraud_shading();
1835
1836 /*Set CEF bit to 0*/
1837 CEF_0;
1838
1839 // TODO: is there an actual limit for this?
1840 while (spritecount<16383) // max 16383 with texture or max 16384 without texture - vitrually unlimited
1841 {
1842 int draw_this_sprite;
1843
1844 draw_this_sprite = 1;
1845
1846 // if (position >= ((0x80000/0x20)/4)) // safety check
1847 // {
1848 // if (VDP1_LOG) logerror ("Sprite List Position Too High!\n");
1849 // position = 0;
1850 // }
1851
1852 spritecount++;
1853
1854 stv2_current_sprite.CMDCTRL = (m_vdp1_vram[position * (0x20/4)+0] & 0xffff0000) >> 16;
1855
1856 if (stv2_current_sprite.CMDCTRL == 0x8000)
1857 {
1858 if (VDP1_LOG) logerror ("List Terminator (0x8000) Encountered, Sprite List Process END\n");
1859 goto end; // end of list
1860 }
1861
1862 stv2_current_sprite.CMDLINK = (m_vdp1_vram[position * (0x20/4)+0] & 0x0000ffff) >> 0;
1863 stv2_current_sprite.CMDPMOD = (m_vdp1_vram[position * (0x20/4)+1] & 0xffff0000) >> 16;
1864 stv2_current_sprite.CMDCOLR = (m_vdp1_vram[position * (0x20/4)+1] & 0x0000ffff) >> 0;
1865 stv2_current_sprite.CMDSRCA = (m_vdp1_vram[position * (0x20/4)+2] & 0xffff0000) >> 16;
1866 stv2_current_sprite.CMDSIZE = (m_vdp1_vram[position * (0x20/4)+2] & 0x0000ffff) >> 0;
1867 stv2_current_sprite.CMDXA = (m_vdp1_vram[position * (0x20/4)+3] & 0xffff0000) >> 16;
1868 stv2_current_sprite.CMDYA = (m_vdp1_vram[position * (0x20/4)+3] & 0x0000ffff) >> 0;
1869 stv2_current_sprite.CMDXB = (m_vdp1_vram[position * (0x20/4)+4] & 0xffff0000) >> 16;
1870 stv2_current_sprite.CMDYB = (m_vdp1_vram[position * (0x20/4)+4] & 0x0000ffff) >> 0;
1871 stv2_current_sprite.CMDXC = (m_vdp1_vram[position * (0x20/4)+5] & 0xffff0000) >> 16;
1872 stv2_current_sprite.CMDYC = (m_vdp1_vram[position * (0x20/4)+5] & 0x0000ffff) >> 0;
1873 stv2_current_sprite.CMDXD = (m_vdp1_vram[position * (0x20/4)+6] & 0xffff0000) >> 16;
1874 stv2_current_sprite.CMDYD = (m_vdp1_vram[position * (0x20/4)+6] & 0x0000ffff) >> 0;
1875 stv2_current_sprite.CMDGRDA = (m_vdp1_vram[position * (0x20/4)+7] & 0xffff0000) >> 16;
1876 // stv2_current_sprite.UNUSED = (m_vdp1_vram[position * (0x20/4)+7] & 0x0000ffff) >> 0;
1877
1878 /* proecess jump / skip commands, set position for next sprite */
1879 switch (stv2_current_sprite.CMDCTRL & 0x7000)
1880 {
1881 case 0x0000: // jump next
1882 if (VDP1_LOG) logerror ("Sprite List Process + Next (Normal)\n");
1883 position++;
1884 break;
1885 case 0x1000: // jump assign
1886 if (VDP1_LOG) logerror ("Sprite List Process + Jump Old %06x New %06x\n", position, (stv2_current_sprite.CMDLINK>>2));
1887 position= (stv2_current_sprite.CMDLINK>>2);
1888 break;
1889 case 0x2000: // jump call
1890 if (vdp1_nest == -1)
1891 {
1892 if (VDP1_LOG) logerror ("Sprite List Process + Call Old %06x New %06x\n",position, (stv2_current_sprite.CMDLINK>>2));
1893 vdp1_nest = position+1;
1894 position = (stv2_current_sprite.CMDLINK>>2);
1895 }
1896 else
1897 {
1898 if (VDP1_LOG) logerror ("Sprite List Nested Call, ignoring\n");
1899 position++;
1900 }
1901 break;
1902 case 0x3000:
1903 if (vdp1_nest != -1)
1904 {
1905 if (VDP1_LOG) logerror ("Sprite List Process + Return\n");
1906 position = vdp1_nest;
1907 vdp1_nest = -1;
1908 }
1909 else
1910 {
1911 if (VDP1_LOG) logerror ("Attempted return from no subroutine, aborting\n");
1912 position++;
1913 goto end; // end of list
1914 }
1915 break;
1916 case 0x4000:
1917 draw_this_sprite = 0;
1918 position++;
1919 break;
1920 case 0x5000:
1921 if (VDP1_LOG) logerror ("Sprite List Skip + Jump Old %06x New %06x\n", position, (stv2_current_sprite.CMDLINK>>2));
1922 draw_this_sprite = 0;
1923 position= (stv2_current_sprite.CMDLINK>>2);
1924
1925 break;
1926 case 0x6000:
1927 draw_this_sprite = 0;
1928 if (vdp1_nest == -1)
1929 {
1930 if (VDP1_LOG) logerror ("Sprite List Skip + Call To Subroutine Old %06x New %06x\n",position, (stv2_current_sprite.CMDLINK>>2));
1931
1932 vdp1_nest = position+1;
1933 position = (stv2_current_sprite.CMDLINK>>2);
1934 }
1935 else
1936 {
1937 if (VDP1_LOG) logerror ("Sprite List Nested Call, ignoring\n");
1938 position++;
1939 }
1940 break;
1941 case 0x7000:
1942 draw_this_sprite = 0;
1943 if (vdp1_nest != -1)
1944 {
1945 if (VDP1_LOG) logerror ("Sprite List Skip + Return from Subroutine\n");
1946
1947 position = vdp1_nest;
1948 vdp1_nest = -1;
1949 }
1950 else
1951 {
1952 if (VDP1_LOG) logerror ("Attempted return from no subroutine, aborting\n");
1953 position++;
1954 goto end; // end of list
1955 }
1956 break;
1957 }
1958
1959 /* continue to draw this sprite only if the command wasn't to skip it */
1960 if (draw_this_sprite ==1)
1961 {
1962 if ( stv2_current_sprite.CMDPMOD & 0x0400 )
1963 {
1964 //if(stv2_current_sprite.CMDPMOD & 0x0200) /* TODO: Bio Hazard inventory screen uses outside cliprect */
1965 // cliprect = &m_vdp1.system_cliprect;
1966 //else
1967 cliprect = &m_vdp1.user_cliprect;
1968 }
1969 else
1970 {
1971 cliprect = &m_vdp1.system_cliprect;
1972 }
1973
1974 stv_vdp1_set_drawpixel();
1975
1976 switch (stv2_current_sprite.CMDCTRL & 0x000f)
1977 {
1978 case 0x0000:
1979 if (VDP1_LOG) logerror ("Sprite List Normal Sprite (%d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
1980 stv2_current_sprite.ispoly = 0;
1981 stv_vdp1_draw_normal_sprite(*cliprect, 0);
1982 break;
1983
1984 case 0x0001:
1985 if (VDP1_LOG) logerror ("Sprite List Scaled Sprite (%d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
1986 stv2_current_sprite.ispoly = 0;
1987 stv_vdp1_draw_scaled_sprite(*cliprect);
1988 break;
1989
1990 case 0x0002:
1991 case 0x0003: // used by Hardcore 4x4
1992 if (VDP1_LOG) logerror ("Sprite List Distorted Sprite\n");
1993 if (VDP1_LOG) logerror ("(A: %d %d)\n",stv2_current_sprite.CMDXA,stv2_current_sprite.CMDYA);
1994 if (VDP1_LOG) logerror ("(B: %d %d)\n",stv2_current_sprite.CMDXB,stv2_current_sprite.CMDYB);
1995 if (VDP1_LOG) logerror ("(C: %d %d)\n",stv2_current_sprite.CMDXC,stv2_current_sprite.CMDYC);
1996 if (VDP1_LOG) logerror ("(D: %d %d)\n",stv2_current_sprite.CMDXD,stv2_current_sprite.CMDYD);
1997 if (VDP1_LOG) logerror ("CMDPMOD = %04x\n",stv2_current_sprite.CMDPMOD);
1998
1999 stv2_current_sprite.ispoly = 0;
2000 stv_vdp1_draw_distorted_sprite(*cliprect);
2001 break;
2002
2003 case 0x0004:
2004 if (VDP1_LOG) logerror ("Sprite List Polygon\n");
2005 stv2_current_sprite.ispoly = 1;
2006 stv_vdp1_draw_distorted_sprite(*cliprect);
2007 break;
2008
2009 case 0x0005:
2010 // case 0x0007: // mirror? Baroque uses it, crashes for whatever reason
2011 if (VDP1_LOG) logerror ("Sprite List Polyline\n");
2012 stv2_current_sprite.ispoly = 1;
2013 stv_vdp1_draw_poly_line(*cliprect);
2014 break;
2015
2016 case 0x0006:
2017 if (VDP1_LOG) logerror ("Sprite List Line\n");
2018 stv2_current_sprite.ispoly = 1;
2019 stv_vdp1_draw_line(*cliprect);
2020 break;
2021
2022 case 0x0008:
2023 // case 0x000b: // mirror? Bug 2
2024 if (VDP1_LOG) logerror ("Sprite List Set Command for User Clipping (%d,%d),(%d,%d)\n", stv2_current_sprite.CMDXA, stv2_current_sprite.CMDYA, stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYC);
2025 m_vdp1.user_cliprect.set(stv2_current_sprite.CMDXA, stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYA, stv2_current_sprite.CMDYC);
2026 break;
2027
2028 case 0x0009:
2029 if (VDP1_LOG) logerror ("Sprite List Set Command for System Clipping (0,0),(%d,%d)\n", stv2_current_sprite.CMDXC, stv2_current_sprite.CMDYC);
2030 m_vdp1.system_cliprect.set(0, stv2_current_sprite.CMDXC, 0, stv2_current_sprite.CMDYC);
2031 break;
2032
2033 case 0x000a:
2034 if (VDP1_LOG) logerror ("Sprite List Local Co-Ordinate Set (%d %d)\n",(int16_t)stv2_current_sprite.CMDXA,(int16_t)stv2_current_sprite.CMDYA);
2035 m_vdp1.local_x = (int16_t)stv2_current_sprite.CMDXA;
2036 m_vdp1.local_y = (int16_t)stv2_current_sprite.CMDYA;
2037 break;
2038
2039 default:
2040 popmessage ("VDP1: Sprite List Illegal %02x (%d), contact MAMEdev",stv2_current_sprite.CMDCTRL & 0xf,spritecount);
2041 m_vdp1.lopr = (position * 0x20) >> 3;
2042 //m_vdp1.copr = (position * 0x20) >> 3;
2043 // prematurely kill the VDP1 process if an illegal opcode is executed
2044 // Sexy Parodius calls multiple illegals and expects VDP1 irq to be fired anyway!
2045 goto end;
2046 }
2047 }
2048
2049 }
2050
2051
2052 end:
2053 m_vdp1.copr = (position * 0x20) >> 3;
2054
2055
2056 /* TODO: what's the exact formula? Guess it should be a mix between number of pixels written and actual command data fetched. */
2057 // if spritecount = 10000 don't send a vdp1 draw end
2058 // if(spritecount < 10000)
2059 machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(spritecount*16), timer_expired_delegate(FUNC(saturn_state::vdp1_draw_end),this));
2060
2061 if (VDP1_LOG) logerror ("End of list processing!\n");
2062 }
2063
video_update_vdp1(void)2064 void saturn_state::video_update_vdp1( void )
2065 {
2066 int framebuffer_changed = 0;
2067
2068 // int enable;
2069 // if (machine.input().code_pressed (KEYCODE_R)) VDP1_LOG = 1;
2070 // if (machine.input().code_pressed (KEYCODE_T)) VDP1_LOG = 0;
2071
2072 // if (machine.input().code_pressed (KEYCODE_Y)) VDP1_LOG = 0;
2073 // {
2074 // FILE *fp;
2075 //
2076 // fp=fopen("vdp1_ram.dmp", "w+b");
2077 // if (fp)
2078 // {
2079 // fwrite(stv_vdp1, 0x00100000, 1, fp);
2080 // fclose(fp);
2081 // }
2082 // }
2083 if (VDP1_LOG) logerror("video_update_vdp1 called\n");
2084 if (VDP1_LOG) logerror( "FBCR = %0x, accessed = %d\n", STV_VDP1_FBCR, m_vdp1.fbcr_accessed );
2085
2086 if(STV_VDP1_CEF)
2087 BEF_1;
2088 else
2089 BEF_0;
2090
2091 if ( m_vdp1.framebuffer_clear_on_next_frame )
2092 {
2093 if ( ((STV_VDP1_FBCR & 0x3) == 3) &&
2094 m_vdp1.fbcr_accessed )
2095 {
2096 stv_clear_framebuffer(m_vdp1.framebuffer_current_display);
2097 m_vdp1.framebuffer_clear_on_next_frame = 0;
2098 }
2099 }
2100
2101 switch( STV_VDP1_FBCR & 0x3 )
2102 {
2103 case 0: /* Automatic mode */
2104 stv_vdp1_change_framebuffers();
2105 stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
2106 framebuffer_changed = 1;
2107 break;
2108 case 1: /* Setting prohibited */
2109 break;
2110 case 2: /* Manual mode - erase */
2111 if ( m_vdp1.fbcr_accessed )
2112 {
2113 m_vdp1.framebuffer_clear_on_next_frame = 1;
2114 }
2115 break;
2116 case 3: /* Manual mode - change */
2117 if ( m_vdp1.fbcr_accessed )
2118 {
2119 stv_vdp1_change_framebuffers();
2120 if ( STV_VDP1_VBE )
2121 {
2122 stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
2123 }
2124 /* TODO: Slam n Jam 96 & Cross Romance doesn't like this, investigate. */
2125 framebuffer_changed = 1;
2126 }
2127 // framebuffer_changed = 1;
2128 break;
2129 }
2130 m_vdp1.fbcr_accessed = 0;
2131
2132 if (VDP1_LOG) logerror( "PTM = %0x, TVM = %x\n", STV_VDP1_PTM, STV_VDP1_TVM );
2133 /*Set CEF bit to 0*/
2134 //CEF_0;
2135 switch(STV_VDP1_PTM & 3)
2136 {
2137 case 0:/*Idle Mode*/
2138 /*Set CEF bit to 0*/
2139 //CEF_0;
2140 break;
2141 case 1:/*Draw by request*/
2142 /*Set CEF bit to 0*/
2143 //CEF_0;
2144 break;
2145 case 2:/*Automatic Draw*/
2146 if ( framebuffer_changed || VDP1_LOG )
2147 {
2148 /*set CEF to 1*/
2149 stv_vdp1_process_list();
2150 }
2151 break;
2152 case 3: /*<invalid>*/
2153 logerror("Warning: Invalid PTM mode set for VDP1!\n");
2154 break;
2155 }
2156 //popmessage("%04x %04x",STV_VDP1_EWRR_X3,STV_VDP1_EWRR_Y3);
2157 }
2158
stv_vdp1_state_save_postload(void)2159 void saturn_state::stv_vdp1_state_save_postload( void )
2160 {
2161 uint8_t *vdp1 = m_vdp1.gfx_decode.get();
2162 int offset;
2163 uint32_t data;
2164
2165 m_vdp1.framebuffer_mode = -1;
2166 m_vdp1.framebuffer_double_interlace = -1;
2167
2168 stv_set_framebuffer_config();
2169
2170 for (offset = 0; offset < 0x80000/4; offset++ )
2171 {
2172 data = m_vdp1_vram[offset];
2173 /* put in gfx region for easy decoding */
2174 vdp1[offset*4+0] = (data & 0xff000000) >> 24;
2175 vdp1[offset*4+1] = (data & 0x00ff0000) >> 16;
2176 vdp1[offset*4+2] = (data & 0x0000ff00) >> 8;
2177 vdp1[offset*4+3] = (data & 0x000000ff) >> 0;
2178 }
2179 }
2180
stv_vdp1_start(void)2181 int saturn_state::stv_vdp1_start ( void )
2182 {
2183 m_vdp1_regs = make_unique_clear<uint16_t[]>(0x020/2 );
2184 m_vdp1_vram = make_unique_clear<uint32_t[]>(0x100000/4 );
2185 m_vdp1.gfx_decode = std::make_unique<uint8_t[]>(0x100000 );
2186
2187 stv_vdp1_shading_data = std::make_unique<struct stv_vdp1_poly_scanline_data>();
2188
2189 m_vdp1.framebuffer[0] = std::make_unique<uint16_t[]>(1024 * 256 * 2 ); /* *2 is for double interlace */
2190 m_vdp1.framebuffer[1] = std::make_unique<uint16_t[]>(1024 * 256 * 2 );
2191
2192 m_vdp1.framebuffer_display_lines = auto_alloc_array(machine(), uint16_t *, 512);
2193 m_vdp1.framebuffer_draw_lines = auto_alloc_array(machine(), uint16_t *, 512);
2194
2195 m_vdp1.framebuffer_width = m_vdp1.framebuffer_height = 0;
2196 m_vdp1.framebuffer_mode = -1;
2197 m_vdp1.framebuffer_double_interlace = -1;
2198 m_vdp1.fbcr_accessed = 0;
2199 m_vdp1.framebuffer_current_display = 0;
2200 m_vdp1.framebuffer_current_draw = 1;
2201 stv_clear_framebuffer(m_vdp1.framebuffer_current_draw);
2202 m_vdp1.framebuffer_clear_on_next_frame = 0;
2203
2204 m_vdp1.system_cliprect.set(0, 0, 0, 0);
2205 /* Kidou Senshi Z Gundam - Zenpen Zeta no Kodou loves to use the user cliprect vars in an undefined state ... */
2206 m_vdp1.user_cliprect.set(0, 512, 0, 256);
2207
2208 // save state
2209 save_pointer(NAME(m_vdp1_regs), 0x020/2);
2210 save_pointer(NAME(m_vdp1_vram), 0x100000/4);
2211 save_item(NAME(m_vdp1.fbcr_accessed));
2212 save_item(NAME(m_vdp1.framebuffer_current_display));
2213 save_item(NAME(m_vdp1.framebuffer_current_draw));
2214 save_item(NAME(m_vdp1.framebuffer_clear_on_next_frame));
2215 save_item(NAME(m_vdp1.local_x));
2216 save_item(NAME(m_vdp1.local_y));
2217 machine().save().register_postload(save_prepost_delegate(FUNC(saturn_state::stv_vdp1_state_save_postload), this));
2218 return 0;
2219 }
2220