/***************************************************************************
Cinemat/Leland driver
Leland video hardware
***************************************************************************/
#include "driver.h"
#include "leland.h"
/* constants */
#define VRAM_SIZE (0x10000)
#define QRAM_SIZE (0x10000)
#define VIDEO_WIDTH (320)
/* debugging */
#define LOG_COMM 0
struct vram_state_data
{
UINT16 addr;
UINT8 latch[2];
};
/* video RAM */
UINT8 *ataxx_qram;
static UINT8 *leland_video_ram;
/* video RAM bitmap drawing */
static struct vram_state_data vram_state[2];
/* scroll background registers */
static UINT16 xscroll;
static UINT16 yscroll;
static UINT8 gfxbank;
static UINT16 last_scanline;
static emu_timer *scanline_timer;
/*************************************
*
* Scanline callback
*
*************************************/
static TIMER_CALLBACK( scanline_callback )
{
int scanline = param;
/* update the DACs */
if (!(leland_dac_control & 0x01))
leland_dac_update(0, leland_video_ram[(last_scanline) * 256 + 160]);
if (!(leland_dac_control & 0x02))
leland_dac_update(1, leland_video_ram[(last_scanline) * 256 + 161]);
last_scanline = scanline;
scanline = (scanline+1) % 256;
if (scanline == 0)
{
/* turn off the DACs at the start of the frame */
leland_dac_control = 3;
}
/* come back at the next appropriate scanline */
timer_adjust_oneshot(scanline_timer, video_screen_get_time_until_pos(machine->primary_screen, scanline, 0), scanline);
}
/*************************************
*
* Start video hardware
*
*************************************/
static VIDEO_START( leland )
{
/* allocate memory */
leland_video_ram = auto_malloc(VRAM_SIZE);
/* reset videoram */
memset(leland_video_ram, 0, VRAM_SIZE);
/* scanline timer */
scanline_timer = timer_alloc(scanline_callback, NULL);
timer_adjust_oneshot(scanline_timer, video_screen_get_time_until_pos(machine->primary_screen, 0, 0), 0);
}
static VIDEO_START( ataxx )
{
/* first do the standard stuff */
VIDEO_START_CALL(leland);
/* allocate memory */
ataxx_qram = auto_malloc(QRAM_SIZE);
/* reset QRAM */
memset(ataxx_qram, 0, QRAM_SIZE);
}
/*************************************
*
* Scrolling and banking
*
*************************************/
WRITE8_HANDLER( leland_scroll_w )
{
int scanline = video_screen_get_vpos(machine->primary_screen);
if (scanline > 0)
video_screen_update_partial(machine->primary_screen, scanline - 1);
/* adjust the proper scroll value */
switch (offset)
{
case 0:
xscroll = (xscroll & 0xff00) | (data & 0x00ff);
break;
case 1:
xscroll = (xscroll & 0x00ff) | ((data << 8) & 0xff00);
break;
case 2:
yscroll = (yscroll & 0xff00) | (data & 0x00ff);
break;
case 3:
yscroll = (yscroll & 0x00ff) | ((data << 8) & 0xff00);
break;
default:
fatalerror("Unexpected leland_gfx_port_w");
break;
}
}
WRITE8_HANDLER( leland_gfx_port_w )
{
video_screen_update_partial(machine->primary_screen, video_screen_get_vpos(machine->primary_screen));
gfxbank = data;
}
/*************************************
*
* 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);
}
/*************************************
*
* 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(running_machine *machine, 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;
/* don't fully understand why this is needed. Isn't the
video RAM just one big RAM? */
int scanline = video_screen_get_vpos(machine->primary_screen);
if (scanline > 0)
video_screen_update_partial(machine->primary_screen, scanline - 1);
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(machine, offset, data, num);
}
WRITE8_HANDLER( leland_mvram_port_w )
{
timer_call_after_resynch(NULL, 0x00000 | (offset << 8) | data, leland_delayed_mvram_w);
}
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(machine, 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);
timer_call_after_resynch(NULL, 0x00000 | (offset << 8) | data, leland_delayed_mvram_w);
}
WRITE8_HANDLER( ataxx_svram_port_w )
{
offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10);
leland_vram_port_w(machine, 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);
}
/*************************************
*
* ROM-based refresh routine
*
*************************************/
static VIDEO_UPDATE( leland )
{
int y;
const UINT8 *bg_prom = memory_region(screen->machine, "user1");
const UINT8 *bg_gfx = memory_region(screen->machine, "gfx1");
offs_t bg_gfx_bank_page_size = memory_region_length(screen->machine, "gfx1") / 3;
offs_t char_bank = (((gfxbank >> 4) & 0x03) * 0x2000) & (bg_gfx_bank_page_size - 1);
offs_t prom_bank = ((gfxbank >> 3) & 0x01) * 0x2000;
/* for each scanline in the visible region */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
int x;
UINT8 fg_data = 0;
UINT16 *dst = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *fg_src = &leland_video_ram[y << 8];
/* for each pixel on the scanline */
for (x = 0; x < VIDEO_WIDTH; x++)
{
/* compute the effective scrolled pixel coordinates */
UINT16 sx = (x + xscroll) & 0x07ff;
UINT16 sy = (y + yscroll) & 0x07ff;
/* get the byte address this background pixel comes from */
offs_t bg_prom_offs = (sx >> 3) |
((sy << 5) & 0x01f00) |
prom_bank |
((sy << 6) & 0x1c000);
offs_t bg_gfx_offs = (sy & 0x07) |
(bg_prom[bg_prom_offs] << 3) |
((sy << 2) & 0x1800) |
char_bank;
/* build the pen, background is d0-d5 */
pen_t pen = (((bg_gfx[bg_gfx_offs + (2 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 7) | /* d0 */
(((bg_gfx[bg_gfx_offs + (1 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 6) | /* d1 */
(((bg_gfx[bg_gfx_offs + (0 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 5) | /* d2 */
((bg_prom[bg_prom_offs] & 0xe0) >> 2); /* d3-d5 */
/* foreground is d6-d9 */
if (x & 0x01)
pen = pen | ((fg_data & 0x0f) << 6);
else
{
fg_data = *fg_src++;
pen = pen | ((fg_data & 0xf0) << 2);
}
*dst++ = pen;
}
}
return 0;
}
/*************************************
*
* RAM-based refresh routine
*
*************************************/
static VIDEO_UPDATE( ataxx )
{
int y;
const UINT8 *bg_gfx = memory_region(screen->machine, "gfx1");
offs_t bg_gfx_bank_page_size = memory_region_length(screen->machine, "gfx1") / 6;
offs_t bg_gfx_offs_mask = bg_gfx_bank_page_size - 1;
/* for each scanline in the visible region */
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
int x;
UINT8 fg_data = 0;
UINT16 *dst = BITMAP_ADDR16(bitmap, y, 0);
UINT8 *fg_src = &leland_video_ram[y << 8];
/* for each pixel on the scanline */
for (x = 0; x < VIDEO_WIDTH; x++)
{
/* compute the effective scrolled pixel coordinates */
UINT16 sx = (x + xscroll) & 0x07ff;
UINT16 sy = (y + yscroll) & 0x07ff;
/* get the byte address this background pixel comes from */
offs_t qram_offs = (sx >> 3) |
((sy << 5) & 0x3f00) |
((sy << 6) & 0x8000);
offs_t bg_gfx_offs = ((sy & 0x07) |
(ataxx_qram[qram_offs] << 3) |
((ataxx_qram[0x4000 | qram_offs] & 0x7f) << 11)) & bg_gfx_offs_mask;
/* build the pen, background is d0-d5 */
pen_t pen = (((bg_gfx[bg_gfx_offs + (0 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 7) | /* d0 */
(((bg_gfx[bg_gfx_offs + (1 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 6) | /* d1 */
(((bg_gfx[bg_gfx_offs + (2 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 5) | /* d2 */
(((bg_gfx[bg_gfx_offs + (3 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 4) | /* d3 */
(((bg_gfx[bg_gfx_offs + (4 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 3) | /* d4 */
(((bg_gfx[bg_gfx_offs + (5 * bg_gfx_bank_page_size)] << (sx & 0x07)) & 0x80) >> 2); /* d5 */
/* foreground is d6-d9 */
if (x & 0x01)
pen = pen | ((fg_data & 0x0f) << 6);
else
{
fg_data = *fg_src++;
pen = pen | ((fg_data & 0xf0) << 2);
}
*dst++ = pen;
}
}
return 0;
}
/*************************************
*
* Machine drivers
*
*************************************/
MACHINE_DRIVER_START( leland_video )
MDRV_VIDEO_ATTRIBUTES(VIDEO_ALWAYS_UPDATE)
MDRV_VIDEO_START(leland)
MDRV_VIDEO_UPDATE(leland)
MDRV_PALETTE_LENGTH(1024)
MDRV_SCREEN_ADD("main", RASTER)
MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
MDRV_SCREEN_SIZE(40*8, 32*8)
MDRV_SCREEN_VISIBLE_AREA(0*8, 40*8-1, 0*8, 30*8-1)
MDRV_SCREEN_REFRESH_RATE(60)
MACHINE_DRIVER_END
MACHINE_DRIVER_START( ataxx_video )
MDRV_IMPORT_FROM(leland_video)
MDRV_VIDEO_START(ataxx)
MDRV_VIDEO_UPDATE(ataxx)
MACHINE_DRIVER_END