#include "emu.h"
#include "video/vdc.h"
/* VDC segments */
#define STATE_VSW 0
#define STATE_VDS 1
#define STATE_VDW 2
#define STATE_VCR 3
/* todo: replace this with the PAIR structure */
typedef union
{
#ifdef LSB_FIRST
struct { unsigned char l,h; } b;
#else
struct { unsigned char h,l; } b;
#endif
unsigned short int w;
}pair;
/* the VDC context */
typedef struct
{
int dvssr_write; /* Set when the DVSSR register has been written to */
int physical_width; /* Width of the display */
int physical_height; /* Height of the display */
UINT16 sprite_ram[64*4]; /* Sprite RAM */
int curline; /* the current scanline we're on */
int current_segment; /* current segment of display */
int current_segment_line; /* current line inside a segment of display */
int vblank_triggered; /* to indicate whether vblank has been triggered */
int raster_count; /* counter to compare RCR against */
int satb_countdown; /* scanlines to wait to trigger the SATB irq */
UINT8 *vram;
UINT8 inc;
UINT8 vdc_register;
UINT8 vdc_latch;
pair vdc_data[32];
int status;
int y_scroll;
}VDC;
typedef struct {
UINT8 vce_control; /* VCE control register */
pair vce_address; /* Current address in the palette */
pair vce_data[512]; /* Palette data */
int current_bitmap_line; /* The current line in the display we are on */
bitmap_t *bmp;
}VCE;
typedef struct {
UINT8 prio;
UINT8 vdc0_enabled;
UINT8 vdc1_enabled;
} VPC_PRIO;
typedef struct {
VPC_PRIO vpc_prio[4];
UINT8 prio_map[512]; /* Pre-calculated priority map */
pair priority; /* Priority settings registers */
pair window1; /* Window 1 setting */
pair window2; /* Window 2 setting */
UINT8 vdc_select; /* Which VDC do the ST0, ST1, and ST2 instructions write to */
}VPC;
static VDC vdc[2];
static VCE vce;
static VPC vpc;
/* Function prototypes */
static void vdc_advance_line(running_machine &machine, int which);
static void draw_black_line(running_machine &machine, int line);
static void draw_overscan_line(int line);
static void draw_sgx_overscan_line(int line);
static void pce_refresh_line(int which, int line, int external_input, UINT8 *drawn, UINT16 *line_buffer);
static void pce_refresh_sprites(running_machine &machine, int which, int line, UINT8 *drawn, UINT16 *line_buffer);
static void vdc_do_dma(running_machine &machine, int which);
static void vpc_init( running_machine &machine );
INTERRUPT_GEN( pce_interrupt )
{
/* Draw the last scanline */
if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
{
/* We are in the active display area */
/* First fill the line with the overscan color */
draw_overscan_line(vce.current_bitmap_line );
/* Check if we need to draw more just the overscan color */
if ( vdc[0].current_segment == STATE_VDW )
{
/* 0 - no sprite and background pixels drawn
1 - background pixel drawn
otherwise is 2 + sprite# */
UINT8 drawn[VDC_WPF];
/* our line buffer */
UINT16 *line_buffer = BITMAP_ADDR16( vce.bmp, vce.current_bitmap_line, 86 );
/* clear our priority/sprite collision detection buffer. */
memset(drawn, 0, VDC_WPF);
vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w : ( vdc[0].y_scroll + 1 );
/* Draw VDC #0 background layer */
pce_refresh_line( 0, vdc[0].current_segment_line, 0, drawn, line_buffer);
/* Draw VDC #0 sprite layer */
if(vdc[0].vdc_data[CR].w & CR_SB)
{
pce_refresh_sprites(device->machine(), 0, vdc[0].current_segment_line, drawn, line_buffer);
}
}
}
else
{
/* We are in one of the blanking areas */
draw_black_line(device->machine(), vce.current_bitmap_line );
}
/* bump current scanline */
vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
vdc_advance_line(device->machine(), 0 );
}
INTERRUPT_GEN( sgx_interrupt )
{
/* Draw the last scanline */
if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
{
/* We are in the active display area */
/* First fill the line with the overscan color */
draw_sgx_overscan_line(vce.current_bitmap_line );
/* Check if we need to draw more just the overscan color */
if ( vdc[0].current_segment == STATE_VDW )
{
/* 0 - no sprite and background pixels drawn
1 - background pixel drawn
otherwise is 2 + sprite# */
UINT8 drawn[2][512];
UINT16 *line_buffer;
UINT16 temp_buffer[2][512];
int i;
/* clear our priority/sprite collision detection buffer. */
memset( drawn, 0, sizeof(drawn) );
vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w : ( vdc[0].y_scroll + 1 );
vdc[1].y_scroll = ( vdc[1].current_segment_line == 0 ) ? vdc[1].vdc_data[BYR].w : ( vdc[1].y_scroll + 1 );
/* Draw VDC #0 background layer */
pce_refresh_line( 0, vdc[0].current_segment_line, 0, drawn[0], temp_buffer[0]);
/* Draw VDC #0 sprite layer */
if(vdc[0].vdc_data[CR].w & CR_SB)
{
pce_refresh_sprites(device->machine(), 0, vdc[0].current_segment_line, drawn[0], temp_buffer[0]);
}
/* Draw VDC #1 background layer */
pce_refresh_line( 1, vdc[1].current_segment_line, 1, drawn[1], temp_buffer[1]);
/* Draw VDC #1 sprite layer */
if ( vdc[1].vdc_data[CR].w & CR_SB )
{
pce_refresh_sprites(device->machine(), 1, vdc[1].current_segment_line, drawn[1], temp_buffer[1]);
}
line_buffer = BITMAP_ADDR16( vce.bmp, vce.current_bitmap_line, 86 );
/* Combine the output of both VDCs */
for( i = 0; i < 512; i++ )
{
int cur_prio = vpc.prio_map[i];
if ( vpc.vpc_prio[cur_prio].vdc0_enabled )
{
if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
{
switch( vpc.vpc_prio[cur_prio].prio )
{
case 0: /* BG1 SP1 BG0 SP0 */
if ( drawn[0][i] )
{
line_buffer[i] = temp_buffer[0][i];
}
else if ( drawn[1][i] )
{
line_buffer[i] = temp_buffer[1][i];
}
break;
case 1: /* BG1 BG0 SP1 SP0 */
if ( drawn[0][i] )
{
if ( drawn[0][i] > 1 )
{
line_buffer[i] = temp_buffer[0][i];
}
else
{
if ( drawn[1][i] > 1 )
{
line_buffer[i] = temp_buffer[1][i];
}
else
{
line_buffer[i] = temp_buffer[0][i];
}
}
}
else if ( drawn[1][i] )
{
line_buffer[i] = temp_buffer[1][i];
}
break;
case 2: /* BG1 + SP1 => SP1
BG0 + SP1 => BG0
BG0 + BG1 => BG0
BG0 + SP0 => SP0
BG1 + SP0 => BG1
SP0 + SP1 => SP0 */
if ( drawn[0][i] )
{
if ( drawn[0][i] > 1 )
{
if ( drawn[1][i] == 1 )
{
line_buffer[i] = temp_buffer[1][i];
}
else
{
line_buffer[i] = temp_buffer[0][i];
}
}
else
{
line_buffer[i] = temp_buffer[0][i];
}
}
else if ( drawn[1][i] )
{
line_buffer[i] = temp_buffer[1][i];
}
break;
}
}
else
{
if ( drawn[0][i] )
{
line_buffer[i] = temp_buffer[0][i];
}
}
}
else
{
if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
{
if ( drawn[1][i] )
{
line_buffer[i] = temp_buffer[1][i];
}
}
}
}
}
}
else
{
/* We are in one of the blanking areas */
draw_black_line(device->machine(), vce.current_bitmap_line );
}
/* bump current scanline */
vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
vdc_advance_line(device->machine(), 0 );
vdc_advance_line(device->machine(), 1 );
}
static void vdc_advance_line(running_machine &machine, int which)
{
int ret = 0;
vdc[which].curline += 1;
vdc[which].current_segment_line += 1;
vdc[which].raster_count += 1;
if ( vdc[which].satb_countdown )
{
vdc[which].satb_countdown -= 1;
if ( vdc[which].satb_countdown == 0 )
{
if ( vdc[which].vdc_data[DCR].w & DCR_DSC )
{
vdc[which].status |= VDC_DS; /* set satb done flag */
ret = 1;
}
}
}
if ( vce.current_bitmap_line == 0 )
{
vdc[which].current_segment = STATE_VSW;
vdc[which].current_segment_line = 0;
vdc[which].vblank_triggered = 0;
vdc[which].curline = 0;
}
if ( STATE_VSW == vdc[which].current_segment && vdc[which].current_segment_line >= ( vdc[which].vdc_data[VPR].b.l & 0x1F ) )
{
vdc[which].current_segment = STATE_VDS;
vdc[which].current_segment_line = 0;
}
if ( STATE_VDS == vdc[which].current_segment && vdc[which].current_segment_line >= vdc[which].vdc_data[VPR].b.h )
{
vdc[which].current_segment = STATE_VDW;
vdc[which].current_segment_line = 0;
vdc[which].raster_count = 0x40;
}
if ( STATE_VDW == vdc[which].current_segment && vdc[which].current_segment_line > ( vdc[which].vdc_data[VDW].w & 0x01FF ) )
{
vdc[which].current_segment = STATE_VCR;
vdc[which].current_segment_line = 0;
/* Generate VBlank interrupt, sprite DMA */
vdc[which].vblank_triggered = 1;
if ( vdc[which].vdc_data[CR].w & CR_VR )
{
vdc[which].status |= VDC_VD;
ret = 1;
}
/* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
if( ( vdc[which].vdc_data[DCR].w & DCR_DSR ) || vdc[which].dvssr_write )
{
int i;
vdc[which].dvssr_write = 0;
for( i = 0; i < 256; i++ )
{
vdc[which].sprite_ram[i] = ( vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w << 1 ) + i * 2 + 1 ] << 8 ) | vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w << 1 ) + i * 2 ];
}
/* generate interrupt if needed */
if ( vdc[which].vdc_data[DCR].w & DCR_DSC )
{
vdc[which].satb_countdown = 4;
}
}
}
if ( STATE_VCR == vdc[which].current_segment )
{
if ( vdc[which].current_segment_line >= 3 && vdc[which].current_segment_line >= vdc[which].vdc_data[VCR].b.l )
{
vdc[which].current_segment = STATE_VSW;
vdc[which].current_segment_line = 0;
vdc[which].curline = 0;
}
}
/* generate interrupt on line compare if necessary */
if ( vdc[which].raster_count == vdc[which].vdc_data[RCR].w && vdc[which].vdc_data[CR].w & CR_RC )
{
vdc[which].status |= VDC_RR;
ret = 1;
}
/* handle frame events */
if(vdc[which].curline == 261 && ! vdc[which].vblank_triggered )
{
vdc[which].vblank_triggered = 1;
if(vdc[which].vdc_data[CR].w & CR_VR)
{ /* generate IRQ1 if enabled */
vdc[which].status |= VDC_VD; /* set vblank flag */
ret = 1;
}
/* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
if ( ( vdc[which].vdc_data[DCR].w & DCR_DSR ) || vdc[which].dvssr_write )
{
int i;
vdc[which].dvssr_write = 0;
#ifdef MAME_DEBUG
assert(((vdc[which].vdc_data[DVSSR].w<<1) + 512) <= 0x10000);
#endif
for( i = 0; i < 256; i++ )
{
vdc[which].sprite_ram[i] = ( vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w << 1 ) + i * 2 + 1 ] << 8 ) | vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w << 1 ) + i * 2 ];
}
/* generate interrupt if needed */
if(vdc[which].vdc_data[DCR].w & DCR_DSC)
{
vdc[which].satb_countdown = 4;
}
}
}
if (ret)
cputag_set_input_line(machine, "maincpu", 0, HOLD_LINE);
}
VIDEO_START( pce )
{
logerror("*** pce_vh_start\n");
/* clear context */
memset(&vdc, 0, sizeof(vdc));
memset(&vce, 0, sizeof(vce));
memset(&vpc, 0, sizeof(vpc));
/* allocate VRAM */
vdc[0].vram = auto_alloc_array(machine, UINT8, 0x10000);
vdc[1].vram = auto_alloc_array(machine, UINT8, 0x10000);
memset(vdc[0].vram, 0, 0x10000);
memset(vdc[1].vram, 0, 0x10000);
/* create display bitmap */
vce.bmp = machine.primary_screen->alloc_compatible_bitmap();
vdc[0].inc = 1;
vdc[1].inc = 1;
vpc_init(machine);
}
SCREEN_UPDATE( pce )
{
/* copy our rendering buffer to the display */
copybitmap (bitmap,vce.bmp,0,0,0,0,cliprect);
return 0;
}
static void draw_black_line(running_machine &machine, int line)
{
int i;
/* our line buffer */
UINT16 *line_buffer = BITMAP_ADDR16( vce.bmp, line, 0 );
for( i=0; i< VDC_WPF; i++ )
line_buffer[i] = get_black_pen( machine );
}
static void draw_overscan_line(int line)
{
int i;
/* Are we in greyscale mode or in color mode? */
int color_base = vce.vce_control & 0x80 ? 512 : 0;
/* our line buffer */
UINT16 *line_buffer = BITMAP_ADDR16( vce.bmp, line, 0 );
for ( i = 0; i < VDC_WPF; i++ )
line_buffer[i] = color_base + vce.vce_data[0x100].w;
}
static void draw_sgx_overscan_line(int line)
{
int i;
/* Are we in greyscale mode or in color mode? */
int color_base = vce.vce_control & 0x80 ? 512 : 0;
/* our line buffer */
UINT16 *line_buffer = BITMAP_ADDR16( vce.bmp, line, 0 );
for ( i = 0; i < VDC_WPF; i++ )
line_buffer[i] = color_base + vce.vce_data[0].w;
}
static void vram_write(int which, offs_t offset, UINT8 data)
{
if(offset & 0x10000)
{
logerror("VDC #%d: Write to VRAM offset %05X\n", which, offset);
return;
}
else
{
vdc[which].vram[offset] = data;
}
}
static UINT8 vram_read(int which, offs_t offset)
{
UINT8 temp;
if(offset & 0x10000)
{
temp = vdc[which].vram[offset & 0xFFFF];
}
else
{
temp = vdc[which].vram[offset];
}
return temp;
}
static void vdc_w( running_machine &machine, int which, offs_t offset, UINT8 data )
{
switch(offset&3)
{
case 0x00: /* VDC register select */
vdc[which].vdc_register = (data & 0x1F);
break;
case 0x02: /* VDC data (LSB) */
vdc[which].vdc_data[vdc[which].vdc_register].b.l = data;
switch(vdc[which].vdc_register)
{
case VxR: /* LSB of data to write to VRAM */
vdc[which].vdc_latch = data;
break;
case BYR:
vdc[which].y_scroll=vdc[which].vdc_data[BYR].w;
break;
case HDR:
vdc[which].physical_width = ((data & 0x003F) + 1) << 3;
break;
case VDW:
vdc[which].physical_height &= 0xFF00;
vdc[which].physical_height |= (data & 0xFF);
vdc[which].physical_height &= 0x01FF;
break;
case LENR:
// logerror("LENR LSB = %02X\n", data);
break;
case SOUR:
// logerror("SOUR LSB = %02X\n", data);
break;
case DESR:
// logerror("DESR LSB = %02X\n", data);
break;
}
break;
case 0x03: /* VDC data (MSB) */
vdc[which].vdc_data[vdc[which].vdc_register].b.h = data;
switch(vdc[which].vdc_register)
{
case VxR: /* MSB of data to write to VRAM */
vram_write(which, vdc[which].vdc_data[MAWR].w*2+0, vdc[which].vdc_latch);
vram_write(which, vdc[which].vdc_data[MAWR].w*2+1, data);
vdc[which].vdc_data[MAWR].w += vdc[which].inc;
break;
case CR:
{
static const unsigned char inctab[] = {1, 32, 64, 128};
vdc[which].inc = inctab[(data >> 3) & 3];
}
break;
case VDW:
vdc[which].physical_height &= 0x00FF;
vdc[which].physical_height |= (data << 8);
vdc[which].physical_height &= 0x01FF;
break;
case DVSSR:
/* Force VRAM <> SATB DMA for this frame */
vdc[which].dvssr_write = 1;
break;
case BYR:
vdc[which].y_scroll=vdc[which].vdc_data[BYR].w;
break;
case LENR:
vdc_do_dma( machine, which );
break;
case SOUR:
// logerror("SOUR MSB = %02X\n", data);
break;
case DESR:
// logerror("DESR MSB = %02X\n", data);
break;
}
break;
}
}
static UINT8 vdc_r( running_machine &machine, int which, offs_t offset )
{
int temp = 0;
switch(offset & 3)
{
case 0x00:
temp = vdc[which].status;
vdc[which].status &= ~(VDC_VD | VDC_DV | VDC_DS | VDC_RR | VDC_OR | VDC_CR);
cputag_set_input_line(machine, "maincpu", 0, CLEAR_LINE);
break;
case 0x02:
temp = vram_read(which, vdc[which].vdc_data[MARR].w * 2 + 0);
break;
case 0x03:
temp = vram_read(which, vdc[which].vdc_data[MARR].w * 2 + 1);
if ( vdc[which].vdc_register == VxR )
{
vdc[which].vdc_data[MARR].w += vdc[which].inc;
}
break;
}
return (temp);
}
WRITE8_HANDLER( vdc_0_w ) { vdc_w( space->machine(), 0, offset, data ); }
WRITE8_HANDLER( vdc_1_w ) { vdc_w( space->machine(), 1, offset, data ); }
READ8_HANDLER( vdc_0_r ) { return vdc_r( space->machine(), 0, offset ); }
READ8_HANDLER( vdc_1_r ) { return vdc_r( space->machine(), 1, offset ); }
PALETTE_INIT( vce )
{
int i;
for( i = 0; i < 512; i++ )
{
int r = (( i >> 3) & 7) << 5;
int g = (( i >> 6) & 7) << 5;
int b = (( i ) & 7) << 5;
int y = ( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
palette_set_color_rgb(machine, i, r, g, b);
palette_set_color_rgb(machine, 512+i, y, y, y);
}
}
READ8_HANDLER ( vce_r )
{
int temp = 0xFF;
switch(offset & 7)
{
case 0x04: /* color table data (LSB) */
temp = vce.vce_data[vce.vce_address.w].b.l;
break;
case 0x05: /* color table data (MSB) */
temp = vce.vce_data[vce.vce_address.w].b.h;
temp |= 0xFE;
vce.vce_address.w = (vce.vce_address.w + 1) & 0x01FF;
break;
}
return (temp);
}
WRITE8_HANDLER ( vce_w )
{
switch(offset & 7)
{
case 0x00: /* control reg. */
vce.vce_control = data;
break;
case 0x02: /* color table address (LSB) */
vce.vce_address.b.l = data;
vce.vce_address.w &= 0x1FF;
break;
case 0x03: /* color table address (MSB) */
vce.vce_address.b.h = data;
vce.vce_address.w &= 0x1FF;
break;
case 0x04: /* color table data (LSB) */
vce.vce_data[vce.vce_address.w].b.l = data;
break;
case 0x05: /* color table data (MSB) */
vce.vce_data[vce.vce_address.w].b.h = data & 0x01;
/* bump internal address */
vce.vce_address.w = (vce.vce_address.w + 1) & 0x01FF;
break;
}
}
static void pce_refresh_line(int which, int line, int external_input, UINT8 *drawn, UINT16 *line_buffer)
{
static const int width_table[4] = {5, 6, 7, 7};
int scroll_y = ( vdc[which].y_scroll & 0x01FF);
int scroll_x = (vdc[which].vdc_data[BXR].w & 0x03FF);
int nt_index;
/* is virtual map 32 or 64 characters tall ? (256 or 512 pixels) */
int v_line = (scroll_y) & (vdc[which].vdc_data[MWR].w & 0x0040 ? 0x1FF : 0x0FF);
/* row within character */
int v_row = (v_line & 7);
/* row of characters in BAT */
int nt_row = (v_line >> 3);
/* virtual X size (# bits to shift) */
int v_width = width_table[(vdc[which].vdc_data[MWR].w >> 4) & 3];
/* pointer to the name table (Background Attribute Table) in VRAM */
UINT8 *bat = &(vdc[which].vram[nt_row << (v_width+1)]);
/* Are we in greyscale mode or in color mode? */
int color_base = vce.vce_control & 0x80 ? 512 : 0;
int b0, b1, b2, b3;
int i0, i1, i2, i3;
int cell_pattern_index;
int cell_palette;
int x, c, i;
/* character blanking bit */
if(!(vdc[which].vdc_data[CR].w & CR_BB))
{
return;
}
else
{
int pixel = 0;
int phys_x = - ( scroll_x & 0x07 );
for(i=0;i<(vdc[which].physical_width >> 3) + 1;i++)
{
nt_index = (i + (scroll_x >> 3)) & ((2 << (v_width-1))-1);
nt_index *= 2;
/* get name table data: */
/* palette # = index from 0-15 */
cell_palette = ( bat[nt_index + 1] >> 4 ) & 0x0F;
/* This is the 'character number', from 0-0x0FFF */
/* then it is shifted left 4 bits to form a VRAM address */
/* and one more bit to convert VRAM word offset to a */
/* byte-offset within the VRAM space */
cell_pattern_index = ( ( ( bat[nt_index + 1] << 8 ) | bat[nt_index] ) & 0x0FFF) << 5;
b0 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x00);
b1 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x01);
b2 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x10);
b3 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x11);
for(x=0;x<8;x++)
{
i0 = (b0 >> (7-x)) & 1;
i1 = (b1 >> (7-x)) & 1;
i2 = (b2 >> (7-x)) & 1;
i3 = (b3 >> (7-x)) & 1;
c = (cell_palette << 4 | i3 << 3 | i2 << 2 | i1 << 1 | i0);
/* colour #0 always comes from palette #0 */
if ( ! ( c & 0x0F ) )
c &= 0x0F;
if ( phys_x >= 0 && phys_x < vdc[which].physical_width )
{
drawn[ pixel ] = c ? 1 : 0;
if ( c || ! external_input )
line_buffer[ pixel ] = color_base + vce.vce_data[c].w;
pixel++;
if ( vdc[which].physical_width != 512 )
{
while ( pixel < ( ( ( phys_x + 1 ) * 512 ) / vdc[which].physical_width ) )
{
drawn[ pixel ] = c ? 1 : 0;
if ( c || ! external_input )
line_buffer[ pixel ] = color_base + vce.vce_data[c].w;
pixel++;
}
}
}
phys_x += 1;
}
}
}
}
static void conv_obj(int which, int i, int l, int hf, int vf, char *buf)
{
int b0, b1, b2, b3, i0, i1, i2, i3, x;
int xi;
int tmp;
l &= 0x0F;
if(vf) l = (15 - l);
tmp = l + ( i << 5);
b0 = vram_read(which, (tmp + 0x00)<<1);
b0 |= vram_read(which, ((tmp + 0x00)<<1)+1)<<8;
b1 = vram_read(which, (tmp + 0x10)<<1);
b1 |= vram_read(which, ((tmp + 0x10)<<1)+1)<<8;
b2 = vram_read(which, (tmp + 0x20)<<1);
b2 |= vram_read(which, ((tmp + 0x20)<<1)+1)<<8;
b3 = vram_read(which, (tmp + 0x30)<<1);
b3 |= vram_read(which, ((tmp + 0x30)<<1)+1)<<8;
for(x=0;x<16;x++)
{
if(hf) xi = x; else xi = (15 - x);
i0 = (b0 >> xi) & 1;
i1 = (b1 >> xi) & 1;
i2 = (b2 >> xi) & 1;
i3 = (b3 >> xi) & 1;
buf[x] = (i3 << 3 | i2 << 2 | i1 << 1 | i0);
}
}
static void pce_refresh_sprites(running_machine &machine, int which, int line, UINT8 *drawn, UINT16 *line_buffer)
{
int i;
UINT8 sprites_drawn = 0;
/* Are we in greyscale mode or in color mode? */
int color_base = vce.vce_control & 0x80 ? 512 : 0;
/* count up: Highest priority is Sprite 0 */
for(i = 0; i < 64; i++)
{
static const int cgy_table[] = {16, 32, 64, 64};
int obj_y = (vdc[which].sprite_ram[(i << 2) + 0] & 0x03FF) - 64;
int obj_x = (vdc[which].sprite_ram[(i << 2) + 1] & 0x03FF) - 32;
int obj_i = (vdc[which].sprite_ram[(i << 2) + 2] & 0x07FE);
int obj_a = (vdc[which].sprite_ram[(i << 2) + 3]);
int cgx = (obj_a >> 8) & 1; /* sprite width */
int cgy = (obj_a >> 12) & 3; /* sprite height */
int hf = (obj_a >> 11) & 1; /* horizontal flip */
int vf = (obj_a >> 15) & 1; /* vertical flip */
int palette = (obj_a & 0x000F);
int priority = (obj_a >> 7) & 1;
int obj_h = cgy_table[cgy];
int obj_l = (line - obj_y);
int cgypos;
char buf[16];
if ((obj_y == -64) || (obj_y > line)) continue;
if ((obj_x == -32) || (obj_x >= vdc[which].physical_width)) continue;
/* no need to draw an object that's ABOVE where we are. */
if((obj_y + obj_h) < line) continue;
/* If CGX is set, bit 0 of sprite pattern index is forced to 0 */
if ( cgx )
obj_i &= ~2;
/* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0. */
if ( cgy & 1 )
obj_i &= ~4;
/* If CGY is set to 2 or 3, bit 1 and 2 of the sprite pattern index are forced to 0. */
if ( cgy & 2 )
obj_i &= ~12;
if (obj_l < obj_h)
{
sprites_drawn++;
if(sprites_drawn > 16)
{
if(vdc[which].vdc_data[CR].w & CR_OV)
{
/* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
vdc[which].status |= VDC_OR;
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
}
continue; /* Should cause an interrupt */
}
cgypos = (obj_l >> 4);
if(vf) cgypos = ((obj_h - 1) >> 4) - cgypos;
if(cgx == 0)
{
int x;
int pixel_x = ( ( obj_x * 512 ) / vdc[which].physical_width );
conv_obj(which, obj_i + (cgypos << 2), obj_l, hf, vf, buf);
for(x = 0; x < 16; x++)
{
if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
{
if ( buf[x] )
{
if( drawn[pixel_x] < 2 )
{
if( priority || drawn[pixel_x] == 0 )
{
line_buffer[pixel_x] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
if ( vdc[which].physical_width != 512 )
{
int dp = 1;
while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
{
drawn[pixel_x + dp] = i + 2;
line_buffer[pixel_x + dp] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
dp++;
}
}
}
drawn[pixel_x] = i + 2;
}
/* Check for sprite #0 collision */
else if (drawn[pixel_x] == 2)
{
if(vdc[which].vdc_data[CR].w & CR_CC)
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
vdc[which].status |= VDC_CR;
}
}
}
if ( vdc[which].physical_width != 512 )
{
pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
}
else
{
pixel_x += 1;
}
}
}
else
{
int x;
int pixel_x = ( ( obj_x * 512 ) / vdc[which].physical_width );
conv_obj(which, obj_i + (cgypos << 2) + (hf ? 2 : 0), obj_l, hf, vf, buf);
for(x = 0; x < 16; x++)
{
if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
{
if ( buf[x] )
{
if( drawn[pixel_x] < 2 )
{
if ( priority || drawn[pixel_x] == 0 )
{
line_buffer[pixel_x] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
if ( vdc[which].physical_width != 512 )
{
int dp = 1;
while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
{
drawn[pixel_x + dp] = i + 2;
line_buffer[pixel_x + dp] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
dp++;
}
}
}
drawn[pixel_x] = i + 2;
}
/* Check for sprite #0 collision */
else if ( drawn[pixel_x] == 2 )
{
if(vdc[which].vdc_data[CR].w & CR_CC)
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
vdc[which].status |= VDC_CR;
}
}
}
if ( vdc[which].physical_width != 512 )
{
pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
}
else
{
pixel_x += 1;
}
}
/* 32 pixel wide sprites are counted as 2 sprites and the right half
is only drawn if there are 2 open slots.
*/
sprites_drawn++;
if( sprites_drawn > 16 )
{
if(vdc[which].vdc_data[CR].w&CR_OV)
{
/* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
vdc[which].status |= VDC_OR;
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
}
}
else
{
conv_obj(which, obj_i + (cgypos << 2) + (hf ? 0 : 2), obj_l, hf, vf, buf);
for(x = 0; x < 16; x++)
{
if(((obj_x + 0x10 + x) < (vdc[which].physical_width)) && ((obj_x + 0x10 + x) >= 0))
{
if ( buf[x] )
{
if( drawn[pixel_x] < 2 )
{
if( priority || drawn[pixel_x] == 0 )
{
line_buffer[pixel_x] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
if ( vdc[which].physical_width != 512 )
{
int dp = 1;
while ( pixel_x + dp < ( ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width ) )
{
drawn[pixel_x + dp] = i + 2;
line_buffer[pixel_x + dp] = color_base + vce.vce_data[0x100 + (palette << 4) + buf[x]].w;
dp++;
}
}
}
drawn[pixel_x] = i + 2;
}
/* Check for sprite #0 collision */
else if ( drawn[pixel_x] == 2 )
{
if(vdc[which].vdc_data[CR].w & CR_CC)
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
vdc[which].status |= VDC_CR;
}
}
}
if ( vdc[which].physical_width != 512 )
{
pixel_x = ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width;
}
else
{
pixel_x += 1;
}
}
}
}
}
}
}
static void vdc_do_dma(running_machine &machine, int which)
{
int src = vdc[which].vdc_data[SOUR].w;
int dst = vdc[which].vdc_data[DESR].w;
int len = vdc[which].vdc_data[LENR].w;
int did = (vdc[which].vdc_data[DCR].w >> 3) & 1;
int sid = (vdc[which].vdc_data[DCR].w >> 2) & 1;
int dvc = (vdc[which].vdc_data[DCR].w >> 1) & 1;
do {
UINT8 l, h;
l = vram_read(which, src<<1);
h = vram_read(which, (src<<1) + 1);
vram_write(which, dst<<1,l);
vram_write(which, 1+(dst<<1),h);
if(sid) src = (src - 1) & 0xFFFF;
else src = (src + 1) & 0xFFFF;
if(did) dst = (dst - 1) & 0xFFFF;
else dst = (dst + 1) & 0xFFFF;
len = (len - 1) & 0xFFFF;
} while (len != 0xFFFF);
vdc[which].status |= VDC_DV;
vdc[which].vdc_data[SOUR].w = src;
vdc[which].vdc_data[DESR].w = dst;
vdc[which].vdc_data[LENR].w = len;
if(dvc)
{
cputag_set_input_line(machine, "maincpu", 0, ASSERT_LINE);
}
}
static void vpc_update_prio_map( void )
{
int i;
for( i = 0; i < 512; i++ )
{
vpc.prio_map[i] = 0;
if ( vpc.window1.w < 0x40 || i > vpc.window1.w )
{
vpc.prio_map[i] |= 1;
}
if ( vpc.window2.w < 0x40 || i > vpc.window2.w )
{
vpc.prio_map[i] |= 2;
}
}
}
WRITE8_HANDLER( vpc_w )
{
//if ( offset < 2 )
//printf("VPC write offset %02X, data %02X\n", offset, data );
switch( offset & 0x07 )
{
case 0x00: /* Priority register #0 */
vpc.priority.b.l = data;
vpc.vpc_prio[0].prio = ( data >> 2 ) & 3;
vpc.vpc_prio[0].vdc0_enabled = data & 1;
vpc.vpc_prio[0].vdc1_enabled = data & 2;
vpc.vpc_prio[1].prio = ( data >> 6 ) & 3;
vpc.vpc_prio[1].vdc0_enabled = data & 0x10;
vpc.vpc_prio[1].vdc1_enabled = data & 0x20;
break;
case 0x01: /* Priority register #1 */
vpc.priority.b.h = data;
vpc.vpc_prio[2].prio = ( data >> 2 ) & 3;
vpc.vpc_prio[2].vdc0_enabled = data & 1;
vpc.vpc_prio[2].vdc1_enabled = data & 2;
vpc.vpc_prio[3].prio = ( data >> 6 ) & 3;
vpc.vpc_prio[3].vdc0_enabled = data & 0x10;
vpc.vpc_prio[3].vdc1_enabled = data & 0x20;
break;
case 0x02: /* Window 1 LSB */
vpc.window1.b.l = data;
vpc_update_prio_map();
break;
case 0x03: /* Window 1 MSB */
vpc.window1.b.h = data & 3;
vpc_update_prio_map();
break;
case 0x04: /* Window 2 LSB */
vpc.window2.b.l = data;
vpc_update_prio_map();
break;
case 0x05: /* Window 2 MSB */
vpc.window2.b.h = data & 3;
vpc_update_prio_map();
break;
case 0x06: /* VDC I/O select */
vpc.vdc_select = data & 1;
break;
}
}
READ8_HANDLER( vpc_r )
{
UINT8 data = 0;
switch( offset & 0x07 )
{
case 0x00: /* Priority register #0 */
data = vpc.priority.b.l;
break;
case 0x01: /* Priority register #1 */
data = vpc.priority.b.h;
break;
case 0x02: /* Window 1 LSB */
data = vpc.window1.b.l;
break;
case 0x03: /* Window 1 MSB; high bits are 0 or 1? */
data = vpc.window1.b.h;
break;
case 0x04: /* Window 2 LSB */
data = vpc.window2.b.l;
break;
case 0x05: /* Window 2 MSB; high bits are 0 or 1? */
data = vpc.window2.b.h;
break;
}
return data;
}
static void vpc_init( running_machine &machine )
{
address_space *space = machine.device("maincpu")->memory().space(AS_PROGRAM);
vpc_w( space, 0, 0x11 );
vpc_w( space, 1, 0x11 );
vpc.window1.w = 0;
vpc.window2.w = 0;
vpc.vdc_select = 0;
}
WRITE8_HANDLER( sgx_vdc_w )
{
if ( vpc.vdc_select )
{
vdc_1_w( space, offset, data );
}
else
{
vdc_0_w( space, offset, data );
}
}
READ8_HANDLER( sgx_vdc_r )
{
return ( vpc.vdc_select ) ? vdc_1_r( space, offset ) : vdc_0_r( space, offset );
}