/***************************************************************************
Cinemat/Leland driver
Leland video hardware
***************************************************************************/
#include "driver.h"
#include "leland.h"
#include "sound/dac.h"
/* constants */
#define VRAM_SIZE 0x10000
#define QRAM_SIZE 0x10000
#define VIDEO_WIDTH 0x28
#define VIDEO_HEIGHT 0x1e
/* debugging */
#define LOG_COMM 0
struct vram_state_data
{
UINT16 addr;
UINT8 latch[2];
};
/* video RAM */
UINT8 *ataxx_qram;
UINT8 leland_last_scanline_int;
static mame_bitmap *fgbitmap;
static UINT8 *leland_video_ram;
/* video RAM bitmap drawing */
static struct vram_state_data vram_state[2];
static UINT8 sync_next_write;
/* partial screen updating */
static int next_update_scanline;
/* scroll background registers */
static UINT16 xscroll;
static UINT16 yscroll;
static UINT8 gfxbank;
/*************************************
*
* Start video hardware
*
*************************************/
VIDEO_START( leland )
{
/* allocate memory */
leland_video_ram = auto_malloc(VRAM_SIZE);
fgbitmap = auto_bitmap_alloc(VIDEO_WIDTH * 8, VIDEO_HEIGHT * 8, machine->screen[0].format);
/* reset videoram */
memset(leland_video_ram, 0, VRAM_SIZE);
}
VIDEO_START( ataxx )
{
/* first do the standard stuff */
video_start_leland(machine);
/* allocate memory */
ataxx_qram = auto_malloc(QRAM_SIZE);
/* reset QRAM */
memset(ataxx_qram, 0, QRAM_SIZE);
}
/*************************************
*
* Scrolling and banking
*
*************************************/
WRITE8_HANDLER( leland_gfx_port_w )
{
/* adjust the proper scroll value */
switch (offset)
{
case -1:
if (gfxbank != data)
{
video_screen_update_partial(0, leland_last_scanline_int);
gfxbank = data;
}
break;
case 0:
if ((xscroll & 0xff) != data)
{
video_screen_update_partial(0, leland_last_scanline_int);
xscroll = (xscroll & 0xff00) | (data & 0x00ff);
}
break;
case 1:
if ((xscroll >> 8) != data)
{
video_screen_update_partial(0, leland_last_scanline_int);
xscroll = (xscroll & 0x00ff) | ((data << 8) & 0xff00);
}
break;
case 2:
if ((yscroll & 0xff) != data)
{
video_screen_update_partial(0, leland_last_scanline_int);
yscroll = (yscroll & 0xff00) | (data & 0x00ff);
}
break;
case 3:
if ((yscroll >> 8) != data)
{
video_screen_update_partial(0, leland_last_scanline_int);
yscroll = (yscroll & 0x00ff) | ((data << 8) & 0xff00);
}
break;
}
}
/*************************************
*
* Video address setting
*
*************************************/
static void leland_video_addr_w(int offset, int data, int num)
{
struct vram_state_data *state = vram_state + num;
if (!offset)
state->addr = (state->addr & 0xfe00) | ((data << 1) & 0x01fe);
else
state->addr = ((data << 9) & 0xfe00) | (state->addr & 0x01fe);
if (num == 0)
sync_next_write = (state->addr >= 0xf000);
}
/*************************************
*
* Flush data from VRAM into our copy
*
*************************************/
static void update_for_scanline(int scanline)
{
int i, j;
/* skip if we're behind the times */
if (scanline <= next_update_scanline)
return;
/* update all scanlines */
for (i = next_update_scanline; i < scanline; i++)
if (i < VIDEO_HEIGHT * 8)
{
UINT8 scandata[VIDEO_WIDTH * 8];
UINT8 *dst = scandata;
UINT8 *src = &leland_video_ram[i * 256];
for (j = 0; j < VIDEO_WIDTH * 8 / 2; j++)
{
UINT8 pix = *src++;
*dst++ = pix >> 4;
*dst++ = pix & 15;
}
draw_scanline8(fgbitmap, 0, i, VIDEO_WIDTH * 8, scandata, NULL, -1);
}
/* also update the DACs */
if (scanline >= VIDEO_HEIGHT * 8)
scanline = 256;
for (i = next_update_scanline; i < scanline; i++)
{
if (!(leland_dac_control & 0x01))
leland_dac_update(0, leland_video_ram[i * 256 + 160]);
if (!(leland_dac_control & 0x02))
leland_dac_update(1, leland_video_ram[i * 256 + 161]);
}
/* set the new last update */
next_update_scanline = scanline;
}
/*************************************
*
* Common video RAM read
*
*************************************/
static int leland_vram_port_r(int offset, int num)
{
struct vram_state_data *state = vram_state + num;
int addr = state->addr;
int inc = (offset >> 2) & 2;
int ret;
switch (offset & 7)
{
case 3: /* read hi/lo (alternating) */
ret = leland_video_ram[addr];
addr += inc & (addr << 1);
addr ^= 1;
break;
case 5: /* read hi */
ret = leland_video_ram[addr | 1];
addr += inc;
break;
case 6: /* read lo */
ret = leland_video_ram[addr & ~1];
addr += inc;
break;
default:
logerror("CPU #%d %04x Warning: Unknown video port %02x read (address=%04x)\n",
cpu_getactivecpu(),activecpu_get_pc(), offset, addr);
ret = 0;
break;
}
state->addr = addr;
if (LOG_COMM && addr >= 0xf000)
logerror("%04X:%s comm read %04X = %02X\n", activecpu_get_previouspc(), num ? "slave" : "master", addr, ret);
return ret;
}
/*************************************
*
* Common video RAM write
*
*************************************/
static void leland_vram_port_w(int offset, int data, int num)
{
struct vram_state_data *state = vram_state + num;
int addr = state->addr;
int inc = (offset >> 2) & 2;
int trans = (offset >> 4) & num;
/* if we're writing "behind the beam", make sure we've cached what was there */
if (addr < 0xf000)
{
int cur_scanline = video_screen_get_vpos(0);
int mod_scanline = addr / 256;
if (cur_scanline != next_update_scanline && mod_scanline < cur_scanline)
update_for_scanline(cur_scanline);
}
if (LOG_COMM && addr >= 0xf000)
logerror("%04X:%s comm write %04X = %02X\n", activecpu_get_previouspc(), num ? "slave" : "master", addr, data);
/* based on the low 3 bits of the offset, update the destination */
switch (offset & 7)
{
case 1: /* write hi = data, lo = latch */
leland_video_ram[addr & ~1] = state->latch[0];
leland_video_ram[addr | 1] = data;
addr += inc;
break;
case 2: /* write hi = latch, lo = data */
leland_video_ram[addr & ~1] = data;
leland_video_ram[addr | 1] = state->latch[1];
addr += inc;
break;
case 3: /* write hi/lo = data (alternating) */
if (trans)
{
if (!(data & 0xf0)) data |= leland_video_ram[addr] & 0xf0;
if (!(data & 0x0f)) data |= leland_video_ram[addr] & 0x0f;
}
leland_video_ram[addr] = data;
addr += inc & (addr << 1);
addr ^= 1;
break;
case 5: /* write hi = data */
state->latch[1] = data;
if (trans)
{
if (!(data & 0xf0)) data |= leland_video_ram[addr | 1] & 0xf0;
if (!(data & 0x0f)) data |= leland_video_ram[addr | 1] & 0x0f;
}
leland_video_ram[addr | 1] = data;
addr += inc;
break;
case 6: /* write lo = data */
state->latch[0] = data;
if (trans)
{
if (!(data & 0xf0)) data |= leland_video_ram[addr & ~1] & 0xf0;
if (!(data & 0x0f)) data |= leland_video_ram[addr & ~1] & 0x0f;
}
leland_video_ram[addr & ~1] = data;
addr += inc;
break;
default:
logerror("CPU #%d %04x Warning: Unknown video port write (address=%04x value=%02x)\n",
cpu_getactivecpu(),activecpu_get_pc(), offset, addr);
break;
}
/* update the address and plane */
state->addr = addr;
}
/*************************************
*
* Master video RAM read/write
*
*************************************/
WRITE8_HANDLER( leland_master_video_addr_w )
{
leland_video_addr_w(offset, data, 0);
}
static TIMER_CALLBACK( leland_delayed_mvram_w )
{
int num = (param >> 16) & 1;
int offset = (param >> 8) & 0xff;
int data = param & 0xff;
leland_vram_port_w(offset, data, num);
}
WRITE8_HANDLER( leland_mvram_port_w )
{
if (sync_next_write)
{
timer_call_after_resynch(0x00000 | (offset << 8) | data, leland_delayed_mvram_w);
sync_next_write = 0;
}
else
leland_vram_port_w(offset, data, 0);
}
READ8_HANDLER( leland_mvram_port_r )
{
return leland_vram_port_r(offset, 0);
}
/*************************************
*
* Slave video RAM read/write
*
*************************************/
WRITE8_HANDLER( leland_slave_video_addr_w )
{
leland_video_addr_w(offset, data, 1);
}
WRITE8_HANDLER( leland_svram_port_w )
{
leland_vram_port_w(offset, data, 1);
}
READ8_HANDLER( leland_svram_port_r )
{
return leland_vram_port_r(offset, 1);
}
/*************************************
*
* Ataxx master video RAM read/write
*
*************************************/
WRITE8_HANDLER( ataxx_mvram_port_w )
{
offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10);
if (sync_next_write)
{
timer_call_after_resynch(0x00000 | (offset << 8) | data, leland_delayed_mvram_w);
sync_next_write = 0;
}
else
leland_vram_port_w(offset, data, 0);
}
WRITE8_HANDLER( ataxx_svram_port_w )
{
offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10);
leland_vram_port_w(offset, data, 1);
}
/*************************************
*
* Ataxx slave video RAM read/write
*
*************************************/
READ8_HANDLER( ataxx_mvram_port_r )
{
offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10);
return leland_vram_port_r(offset, 0);
}
READ8_HANDLER( ataxx_svram_port_r )
{
offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10);
return leland_vram_port_r(offset, 1);
}
/*************************************
*
* End-of-frame routine
*
*************************************/
static TIMER_CALLBACK( scanline_reset )
{
/* flush the remaining scanlines */
next_update_scanline = 0;
/* turn off the DACs at the start of the frame */
leland_dac_control = 3;
}
VIDEO_EOF( leland )
{
/* update anything remaining */
update_for_scanline(VIDEO_HEIGHT * 8);
/* set a timer to go off at the top of the frame */
timer_set(video_screen_get_time_until_pos(0, 0, 0), 0, scanline_reset);
}
/*************************************
*
* ROM-based refresh routine
*
*************************************/
VIDEO_UPDATE( leland )
{
const UINT8 *background_prom = memory_region(REGION_USER1);
const gfx_element *gfx = machine->gfx[0];
int char_bank = ((gfxbank >> 4) & 0x03) * 0x0400;
int prom_bank = ((gfxbank >> 3) & 0x01) * 0x2000;
int xcoarse = xscroll / 8;
int ycoarse = yscroll / 8;
int xfine = xscroll % 8;
int yfine = yscroll % 8;
int x, y;
/* update anything remaining */
update_for_scanline(cliprect->max_y);
/* draw what's visible to the main bitmap */
for (y = cliprect->min_y / 8; y < cliprect->max_y / 8 + 2; y++)
{
int ysum = ycoarse + y;
for (x = 0; x < VIDEO_WIDTH + 1; x++)
{
int xsum = xcoarse + x;
int offs = ((xsum << 0) & 0x000ff) |
((ysum << 8) & 0x01f00) |
prom_bank |
((ysum << 9) & 0x1c000);
int code = background_prom[offs] |
((ysum << 2) & 0x300) |
char_bank;
int color = (code >> 5) & 7;
/* draw to the bitmap */
drawgfx(bitmap, gfx,
code, 8 * color, 0, 0,
8 * x - xfine, 8 * y - yfine,
cliprect, TRANSPARENCY_NONE_RAW, 0);
}
}
/* Merge the two bitmaps together */
copybitmap(bitmap, fgbitmap, 0, 0, 0, 0, cliprect, TRANSPARENCY_BLEND, 6);
return 0;
}
/*************************************
*
* RAM-based refresh routine
*
*************************************/
VIDEO_UPDATE( ataxx )
{
const gfx_element *gfx = machine->gfx[0];
int xcoarse = xscroll / 8;
int ycoarse = yscroll / 8;
int xfine = xscroll % 8;
int yfine = yscroll % 8;
int x, y;
/* update anything remaining */
update_for_scanline(cliprect->max_y);
/* draw what's visible to the main bitmap */
for (y = cliprect->min_y / 8; y < cliprect->max_y / 8 + 2; y++)
{
int ysum = ycoarse + y;
for (x = 0; x < VIDEO_WIDTH + 1; x++)
{
int xsum = xcoarse + x;
int offs = ((ysum & 0x40) << 9) + ((ysum & 0x3f) << 8) + (xsum & 0xff);
int code = ataxx_qram[offs] | ((ataxx_qram[offs + 0x4000] & 0x7f) << 8);
/* draw to the bitmap */
drawgfx(bitmap, gfx,
code, 0, 0, 0,
8 * x - xfine, 8 * y - yfine,
cliprect, TRANSPARENCY_NONE_RAW, 0);
}
}
/* Merge the two bitmaps together */
copybitmap(bitmap, fgbitmap, 0, 0, 0, 0, cliprect, TRANSPARENCY_BLEND, 6);
return 0;
}