// license:BSD-3-Clause
// copyright-holders:Peter Trauner
/***************************************************************************
Video Interface Chip (4567)
original emulation by PeT (mess@utanet.at)
2010-02: converted to be a device and split from vic II
TODO:
- plenty of cleanups
- emulate variants of the vic chip
- update vic III to use the new vic6567.c code for the vic II comaptibility
***************************************************************************/
#include "emu.h"
#include "vic4567.h"
#include "screen.h"
#include <algorithm>
/*****************************************************************************
CONSTANTS
*****************************************************************************/
#define VERBOSE_LEVEL 0
#define DBG_LOG(N,M,A) \
do { \
if(VERBOSE_LEVEL >= N) \
{ \
if( M ) \
logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
logerror A; \
} \
} while (0)
#define VREFRESHINLINES 28
#define VIC2_YPOS 50
#define RASTERLINE_2_C64(a) (a)
#define C64_2_RASTERLINE(a) (a)
#define XPOS (VIC2_STARTVISIBLECOLUMNS + (VIC2_VISIBLECOLUMNS - VIC2_HSIZE) / 2)
#define YPOS (VIC2_STARTVISIBLELINES /* + (VIC2_VISIBLELINES - VIC2_VSIZE) / 2 */)
#define FIRSTLINE 10 /* 36 ((VIC2_VISIBLELINES - VIC2_VSIZE)/2) */
#define FIRSTCOLUMN 50
/* 2008-05 FP: lightpen code needs to read input port from c64.c and cbmb.c */
#define LIGHTPEN_BUTTON (m_lightpen_button_cb(0))
#define LIGHTPEN_X_VALUE (m_lightpen_x_cb(0))
#define LIGHTPEN_Y_VALUE (m_lightpen_y_cb(0))
/* lightpen delivers values from internal counters; they do not start with the visual area or frame area */
#define VIC2_MAME_XPOS 0
#define VIC2_MAME_YPOS 0
#define VIC6567_X_BEGIN 38
#define VIC6567_Y_BEGIN -6 /* first 6 lines after retrace not for lightpen! */
#define VIC6569_X_BEGIN 38
#define VIC6569_Y_BEGIN -6
#define VIC2_X_BEGIN ((m_type == vic3_type::PAL) ? VIC6569_X_BEGIN : VIC6567_X_BEGIN)
#define VIC2_Y_BEGIN ((m_type == vic3_type::PAL) ? VIC6569_Y_BEGIN : VIC6567_Y_BEGIN)
#define VIC2_X_VALUE ((LIGHTPEN_X_VALUE + VIC2_X_BEGIN + VIC2_MAME_XPOS) / 2)
#define VIC2_Y_VALUE ((LIGHTPEN_Y_VALUE + VIC2_Y_BEGIN + VIC2_MAME_YPOS))
#define VIC2E_K0_LEVEL (m_reg[0x2f] & 0x01)
#define VIC2E_K1_LEVEL (m_reg[0x2f] & 0x02)
#define VIC2E_K2_LEVEL (m_reg[0x2f] & 0x04)
/*#define VIC3_P5_LEVEL (m_reg[0x30] & 0x20) */
#define VIC3_BITPLANES (m_reg[0x31] & 0x10)
#define VIC3_80COLUMNS (m_reg[0x31] & 0x80)
#define VIC3_LINES ((m_reg[0x31] & 0x19) == 0x19 ? 400 : 200)
#define VIC3_BITPLANES_WIDTH (m_reg[0x31] & 0x80 ? 640 : 320)
/*#define VIC2E_TEST (vic2[0x30] & 2) */
#define DOUBLE_CLOCK (m_reg[0x30] & 0x01)
/* sprites 0 .. 7 */
#define SPRITEON(nr) (m_reg[0x15] & (1 << nr))
#define SPRITE_Y_EXPAND(nr) (m_reg[0x17] & (1 << nr))
#define SPRITE_Y_SIZE(nr) (SPRITE_Y_EXPAND(nr) ? 2 * 21 : 21)
#define SPRITE_X_EXPAND(nr) (m_reg[0x1d] & (1 << nr))
#define SPRITE_X_SIZE(nr) (SPRITE_X_EXPAND(nr) ? 2 * 24 : 24)
#define SPRITE_X_POS(nr) ((m_reg[(nr) * 2] | (m_reg[0x10] & (1 <<(nr)) ? 0x100 : 0)) - 24 + XPOS)
#define SPRITE_X_POS2(nr) (m_reg[(nr) * 2] | (m_reg[0x10] & (1 <<(nr)) ? 0x100 : 0))
#define SPRITE_Y_POS(nr) (m_reg[1+2*(nr)] - 50 + YPOS)
#define SPRITE_Y_POS2(nr) (m_reg[1 + 2 *(nr)])
#define SPRITE_MULTICOLOR(nr) (m_reg[0x1c] & (1 << nr))
#define SPRITE_PRIORITY(nr) (m_reg[0x1b] & (1 << nr))
#define SPRITE_MULTICOLOR1 (m_reg[0x25] & 0x0f)
#define SPRITE_MULTICOLOR2 (m_reg[0x26] & 0x0f)
#define SPRITE_COLOR(nr) (m_reg[0x27+nr] & 0x0f)
#define SPRITE_ADDR(nr) (m_videoaddr | 0x3f8 | nr)
#define SPRITE_BG_COLLISION(nr) (m_reg[0x1f] & (1 << nr))
#define SPRITE_COLLISION(nr) (m_reg[0x1e] & (1 << nr))
#define SPRITE_SET_BG_COLLISION(nr) (m_reg[0x1f] |= (1 << nr))
#define SPRITE_SET_COLLISION(nr) (m_reg[0x1e] |= (1 << nr))
#define SPRITE_COLL (m_reg[0x1e])
#define SPRITE_BG_COLL (m_reg[0x1f])
#define GFXMODE ((m_reg[0x11] & 0x60) | (m_reg[0x16] & 0x10)) >> 4
#define SCREENON (m_reg[0x11] & 0x10)
#define VERTICALPOS (m_reg[0x11] & 0x07)
#define HORIZONTALPOS (m_reg[0x16] & 0x07)
#define ECMON (m_reg[0x11] & 0x40)
#define HIRESON (m_reg[0x11] & 0x20)
#define MULTICOLORON (m_reg[0x16] & 0x10)
#define LINES25 (m_reg[0x11] & 0x08) /* else 24 Lines */
#define LINES (LINES25 ? 25 : 24)
#define YSIZE (LINES * 8)
#define COLUMNS40 (m_reg[0x16] & 0x08) /* else 38 Columns */
#define COLUMNS (COLUMNS40 ? 40 : 38)
#define XSIZE (COLUMNS * 8)
#define VIDEOADDR ((m_reg[0x18] & 0xf0) << (10 - 4))
#define CHARGENADDR ((m_reg[0x18] & 0x0e) << 10)
#define BITMAPADDR ((data & 0x08) << 10)
#define RASTERLINE (((m_reg[0x11] & 0x80) << 1) | m_reg[0x12])
#define FRAMECOLOR (m_reg[0x20] & 0x0f)
#define BACKGROUNDCOLOR (m_reg[0x21] & 0x0f)
#define MULTICOLOR1 (m_reg[0x22] & 0x0f)
#define MULTICOLOR2 (m_reg[0x23] & 0x0f)
#define FOREGROUNDCOLOR (m_reg[0x24] & 0x0f)
#define VIC2_LINES (m_type == vic3_type::PAL ? VIC6569_LINES : VIC6567_LINES)
#define VIC2_VISIBLELINES (m_type == vic3_type::PAL ? VIC6569_VISIBLELINES : VIC6567_VISIBLELINES)
#define VIC2_VISIBLECOLUMNS (m_type == vic3_type::PAL ? VIC6569_VISIBLECOLUMNS : VIC6567_VISIBLECOLUMNS)
#define VIC2_STARTVISIBLELINES ((VIC2_LINES - VIC2_VISIBLELINES)/2)
#define VIC2_FIRSTRASTERLINE (m_type == vic3_type::PAL ? VIC6569_FIRSTRASTERLINE : VIC6567_FIRSTRASTERLINE)
#define VIC2_COLUMNS (m_type == vic3_type::PAL ? VIC6569_COLUMNS : VIC6567_COLUMNS)
#define VIC2_STARTVISIBLECOLUMNS ((VIC2_COLUMNS - VIC2_VISIBLECOLUMNS)/2)
#define VIC3_BITPLANES_MASK (m_reg[0x32])
/* bit 0, 4 not used !?*/
/* I think hinibbles contains the banknumbers for interlaced modes */
/* if hinibble set then x&1==0 should be in bank1 (0x10000), x&1==1 in bank 0 */
#define VIC3_BITPLANE_ADDR_HELPER(x) ((m_reg[0x33 + x] & 0x0f) << 12)
#define VIC3_BITPLANE_ADDR(x) (x & 1 ? VIC3_BITPLANE_ADDR_HELPER(x) + 0x10000 : VIC3_BITPLANE_ADDR_HELPER(x) )
#define VIC3_BITPLANE_IADDR_HELPER(x) ((m_reg[0x33 + x] & 0xf0) << 8)
#define VIC3_BITPLANE_IADDR(x) (x & 1 ? VIC3_BITPLANE_IADDR_HELPER(x) + 0x10000 : VIC3_BITPLANE_IADDR_HELPER(x))
DEFINE_DEVICE_TYPE(VIC3, vic3_device, "vic3", "CSG 4567 VIC-III")
vic3_device::vic3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, VIC3, tag, owner, clock)
, device_palette_interface(mconfig, *this)
, device_video_interface(mconfig, *this)
, m_type(vic3_type::NTSC)
, m_cpu(*this, finder_base::DUMMY_TAG)
, m_dma_read_cb(*this, 0)
, m_dma_read_color_cb(*this, 0)
, m_interrupt_cb(*this)
, m_port_changed_cb(*this)
, m_lightpen_button_cb(*this, 0)
, m_lightpen_x_cb(*this, 0)
, m_lightpen_y_cb(*this, 0)
, m_c64_mem_r_cb(*this, 0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void vic3_device::device_start()
{
int width = screen().width();
int height = screen().height();
m_bitmap = std::make_unique<bitmap_ind16>(width, height);
m_screendata = std::make_unique<uint8_t []>(216 * 656 / 8);
m_screenptr[0] = m_screendata.get();
for (int i = 1; i < 216; i++)
m_screenptr[i] = m_screenptr[i - 1] + 656 / 8;
for (int i = 0; i < 256; i++)
{
m_foreground[i] = 0;
if ((i & 3) > 1)
m_foreground[i] |= 0x3;
if ((i & 0xc) > 0x4)
m_foreground[i] |= 0xc;
if ((i & 0x30) > 0x10)
m_foreground[i] |= 0x30;
if ((i & 0xc0) > 0x40)
m_foreground[i] |= 0xc0;
}
for (int i = 0; i < 256; i++)
{
m_expandx[i] = 0;
if (i & 1)
m_expandx[i] |= 3;
if (i & 2)
m_expandx[i] |= 0xc;
if (i & 4)
m_expandx[i] |= 0x30;
if (i & 8)
m_expandx[i] |= 0xc0;
if (i & 0x10)
m_expandx[i] |= 0x300;
if (i & 0x20)
m_expandx[i] |= 0xc00;
if (i & 0x40)
m_expandx[i] |= 0x3000;
if (i & 0x80)
m_expandx[i] |= 0xc000;
}
for (int i = 0; i < 256; i++)
{
m_expandx_multi[i] = 0;
if (i & 1)
m_expandx_multi[i] |= 5;
if (i & 2)
m_expandx_multi[i] |= 0xa;
if (i & 4)
m_expandx_multi[i] |= 0x50;
if (i & 8)
m_expandx_multi[i] |= 0xa0;
if (i & 0x10)
m_expandx_multi[i] |= 0x500;
if (i & 0x20)
m_expandx_multi[i] |= 0xa00;
if (i & 0x40)
m_expandx_multi[i] |= 0x5000;
if (i & 0x80)
m_expandx_multi[i] |= 0xa000;
}
save_item(NAME(m_reg));
save_item(NAME(m_on));
//save_item(NAME(m_bitmap));
save_item(NAME(m_lines));
save_item(NAME(m_chargenaddr));
save_item(NAME(m_videoaddr));
save_item(NAME(m_bitmapaddr));
save_item(NAME(m_x_begin));
save_item(NAME(m_x_end));
save_item(NAME(m_y_begin));
save_item(NAME(m_y_end));
save_item(NAME(m_c64_bitmap));
save_item(NAME(m_bitmapmulti));
save_item(NAME(m_mono));
save_item(NAME(m_multi));
save_item(NAME(m_ecmcolor));
save_item(NAME(m_colors));
save_item(NAME(m_spritemulti));
save_item(NAME(m_lastline));
save_item(NAME(m_rasterline));
save_item(NAME(m_interlace));
save_item(NAME(m_columns));
save_item(NAME(m_rows));
save_item(NAME(m_shift));
save_item(NAME(m_foreground));
save_item(NAME(m_multi_collision));
save_item(NAME(m_palette_red));
save_item(NAME(m_palette_green));
save_item(NAME(m_palette_blue));
save_item(NAME(m_palette_dirty));
for (int i = 0; i < 8; i++)
{
save_item(NAME(m_sprites[i].x), i);
save_item(NAME(m_sprites[i].y), i);
save_item(NAME(m_sprites[i].repeat), i);
save_item(NAME(m_sprites[i].line), i);
save_item(NAME(m_sprites[i].paintedline), i);
save_item(NAME(m_sprites[i].bitmap[0]), i);
save_item(NAME(m_sprites[i].bitmap[1]), i);
save_item(NAME(m_sprites[i].bitmap[2]), i);
save_item(NAME(m_sprites[i].bitmap[3]), i);
save_item(NAME(m_sprites[i].bitmap[4]), i);
save_item(NAME(m_sprites[i].bitmap[5]), i);
save_item(NAME(m_sprites[i].bitmap[6]), i);
save_item(NAME(m_sprites[i].bitmap[7]), i);
}
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void vic3_device::device_reset()
{
std::fill(std::begin(m_reg), std::end(m_reg), 0);
m_on = 1;
m_interlace = 0;
m_columns = 640;
m_rows = 200;
m_lines = VIC2_LINES;
memset(&m_sprites, 0, sizeof(m_sprites));
m_chargenaddr = 0;
m_videoaddr = 0;
m_bitmapaddr = 0;
m_x_begin = 0;
m_x_end = 0;
m_y_begin = 0;
m_y_end = 0;
for (int i = 0; i < 2; i++)
{
m_c64_bitmap[i] = 0;
m_mono[i] = 0;
m_ecmcolor[i] = 0;
}
for (int i = 0; i < 4; i++)
{
m_bitmapmulti[i] = 0;
m_multi[i] = 0;
m_colors[i] = 0;
m_spritemulti[i] = 0;
}
m_lastline = 0;
m_rasterline = 0;
std::fill(std::begin(m_shift), std::end(m_shift), 0);
std::fill(std::begin(m_multi_collision), std::end(m_multi_collision), 0);
for (int i = 0; i < 256; i++)
{
m_palette_red[i] = m_palette_green[i] = m_palette_blue[i] = 0;
set_pen_color(i, rgb_t::black());
}
m_palette_dirty = 0;
}
/*****************************************************************************
IMPLEMENTATION
*****************************************************************************/
inline int vic3_device::getforeground( int y, int x )
{
return ((m_screenptr[y][x >> 3] << 8) | (m_screenptr[y][(x >> 3) + 1])) >> (8 - (x & 7));
}
inline int vic3_device::getforeground16( int y, int x )
{
return ((m_screenptr[y][x >> 3] << 16) | (m_screenptr[y][(x >> 3) + 1] << 8) | (m_screenptr[y][(x >> 3) + 2])) >> (8 - (x & 7));
}
void vic3_device::set_interrupt( int mask )
{
if (((m_reg[0x19] ^ mask) & m_reg[0x1a] & 0xf))
{
if (!(m_reg[0x19] & 0x80))
{
//DBG_LOG(2, "vic2", ("irq start %.2x\n", mask));
m_reg[0x19] |= 0x80;
m_interrupt_cb(1);
}
}
m_reg[0x19] |= mask;
}
void vic3_device::clear_interrupt( int mask )
{
m_reg[0x19] &= ~mask;
if ((m_reg[0x19] & 0x80) && !(m_reg[0x19] & m_reg[0x1a] & 0xf))
{
//DBG_LOG(2, "vic2", ("irq end %.2x\n", mask));
m_reg[0x19] &= ~0x80;
m_interrupt_cb(0);
}
}
TIMER_CALLBACK_MEMBER( vic3_device::timer_timeout )
{
int which = param;
//DBG_LOG(3, "vic3 ", ("timer %d timeout\n", which));
switch (which)
{
case 1: /* light pen */
/* and diode must recognize light */
if (1)
{
m_reg[0x13] = VIC2_X_VALUE;
m_reg[0x14] = VIC2_Y_VALUE;
}
set_interrupt(8);
break;
}
}
void vic3_device::draw_character( int ybegin, int yend, int ch, int yoff, int xoff, uint16_t *color, int start_x, int end_x )
{
for (int y = ybegin; y <= yend; y++)
{
int code = m_dma_read_cb(m_chargenaddr + ch * 8 + y);
m_screenptr[y + yoff][xoff >> 3] = code;
if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 0) = color[code >> 7];
if ((xoff + 1 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 1) = color[(code >> 6) & 1];
if ((xoff + 2 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 2) = color[(code >> 5) & 1];
if ((xoff + 3 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 3) = color[(code >> 4) & 1];
if ((xoff + 4 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 4) = color[(code >> 3) & 1];
if ((xoff + 5 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 5) = color[(code >> 2) & 1];
if ((xoff + 6 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 6) = color[(code >> 1) & 1];
if ((xoff + 7 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 7) = color[code & 1];
}
}
void vic3_device::draw_character_multi( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
{
for (int y = ybegin; y <= yend; y++)
{
int code = m_dma_read_cb(m_chargenaddr + ch * 8 + y);
m_screenptr[y + yoff][xoff >> 3] = m_foreground[code];
if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 0) = m_multi[code >> 6];
if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 1) = m_multi[code >> 6];
if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 2) = m_multi[(code >> 4) & 3];
if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 3) = m_multi[(code >> 4) & 3];
if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 4) = m_multi[(code >> 2) & 3];
if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 5) = m_multi[(code >> 2) & 3];
if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 6) = m_multi[code & 3];
if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 7) = m_multi[code & 3];
}
}
void vic3_device::draw_bitmap( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
{
for (int y = ybegin; y <= yend; y++)
{
int code = m_dma_read_cb((m_chargenaddr & 0x2000) + ch * 8 + y);
m_screenptr[y + yoff][xoff >> 3] = code;
if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 0) = m_c64_bitmap[code >> 7];
if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 1) = m_c64_bitmap[(code >> 6) & 1];
if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 2) = m_c64_bitmap[(code >> 5) & 1];
if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 3) = m_c64_bitmap[(code >> 4) & 1];
if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 4) = m_c64_bitmap[(code >> 3) & 1];
if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 5) = m_c64_bitmap[(code >> 2) & 1];
if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 6) = m_c64_bitmap[(code >> 1) & 1];
if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 7) = m_c64_bitmap[code & 1];
}
}
void vic3_device::draw_bitmap_multi( int ybegin, int yend, int ch, int yoff, int xoff, int start_x, int end_x )
{
for (int y = ybegin; y <= yend; y++)
{
int code = m_dma_read_cb((m_chargenaddr & 0x2000) + ch * 8 + y);
m_screenptr[y + yoff][xoff >> 3] = m_foreground[code];
if ((xoff + 0 > start_x) && (xoff + 0 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 0) = m_bitmapmulti[code >> 6];
if ((xoff + 1 > start_x) && (xoff + 1 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 1) = m_bitmapmulti[code >> 6];
if ((xoff + 2 > start_x) && (xoff + 2 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 2) = m_bitmapmulti[(code >> 4) & 3];
if ((xoff + 3 > start_x) && (xoff + 3 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 3) = m_bitmapmulti[(code >> 4) & 3];
if ((xoff + 4 > start_x) && (xoff + 4 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 4) = m_bitmapmulti[(code >> 2) & 3];
if ((xoff + 5 > start_x) && (xoff + 5 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 5) = m_bitmapmulti[(code >> 2) & 3];
if ((xoff + 6 > start_x) && (xoff + 6 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 6) = m_bitmapmulti[code & 3];
if ((xoff + 7 > start_x) && (xoff + 7 < end_x)) m_bitmap->pix(y + yoff + FIRSTLINE, xoff + 7) = m_bitmapmulti[code & 3];
}
}
void vic3_device::draw_sprite_code( int y, int xbegin, int code, int color, int start_x, int end_x )
{
if ((y < YPOS) || (y >= (VIC2_STARTVISIBLELINES + VIC2_VISIBLELINES)) || (xbegin <= 1) || (xbegin >= (VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS)))
return;
for (int x = 0, mask = 0x80; x < 8; x++, mask >>= 1)
{
if (code & mask)
{
if ((xbegin + x > start_x) && (xbegin + x < end_x))
m_bitmap->pix(y + FIRSTLINE, xbegin + x) = color;
}
}
}
void vic3_device::draw_sprite_code_multi( int y, int xbegin, int code, int prior, int start_x, int end_x )
{
if ((y < YPOS) || (y >= (VIC2_STARTVISIBLELINES + VIC2_VISIBLELINES)) || (xbegin <= 1) || (xbegin >= (VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS)))
return;
for (int x = 0, mask = 0xc0, shift = 6; x < 8; x += 2, mask >>= 2, shift -= 2)
{
if (code & mask)
{
switch ((prior & mask) >> shift)
{
case 1:
if ((xbegin + x + 1 > start_x) && (xbegin + x + 1 < end_x))
m_bitmap->pix(y + FIRSTLINE, xbegin + x + 1) = m_spritemulti[(code >> shift) & 3];
break;
case 2:
if ((xbegin + x > start_x) && (xbegin + x < end_x))
m_bitmap->pix(y + FIRSTLINE, xbegin + x) = m_spritemulti[(code >> shift) & 3];
break;
case 3:
if ((xbegin + x > start_x) && (xbegin + x < end_x))
m_bitmap->pix(y + FIRSTLINE, xbegin + x) = m_spritemulti[(code >> shift) & 3];
if ((xbegin + x + 1> start_x) && (xbegin + x + 1< end_x))
m_bitmap->pix(y + FIRSTLINE, xbegin + x + 1) = m_spritemulti[(code >> shift) & 3];
break;
}
}
}
}
void vic3_device::sprite_collision( int nr, int y, int x, int mask )
{
for (int i = 7; i > nr; i--)
{
if (!SPRITEON(i) || !m_sprites[i].paintedline[y] || (SPRITE_COLLISION(i) && SPRITE_COLLISION(nr)))
continue;
if ((x + 7 < SPRITE_X_POS(i)) || (x >= SPRITE_X_POS(i) + SPRITE_X_SIZE(i)))
continue;
int xdiff = x - SPRITE_X_POS(i);
int value;
if ((x & 7) == (SPRITE_X_POS(i) & 7))
value = m_sprites[i].bitmap[y][xdiff >> 3];
else if (xdiff < 0)
value = m_sprites[i].bitmap[y][0] >> (-xdiff);
else {
uint8_t *vp = m_sprites[i].bitmap[y]+(xdiff >> 3);
value = ((vp[1] | (*vp << 8)) >> (8 - (xdiff & 7) )) & 0xff;
}
if (value & mask)
{
SPRITE_SET_COLLISION(i);
SPRITE_SET_COLLISION(nr);
set_interrupt(4);
}
}
}
void vic3_device::draw_sprite( int nr, int yoff, int ybegin, int yend, int start_x, int end_x )
{
int y, i, addr, xbegin, color, prior, collision;
int value, value3 = 0;
xbegin = SPRITE_X_POS(nr);
addr = m_dma_read_cb(SPRITE_ADDR(nr)) << 6;
color = SPRITE_COLOR(nr);
prior = SPRITE_PRIORITY(nr);
collision = SPRITE_BG_COLLISION(nr);
if (SPRITE_X_EXPAND(nr))
{
for (y = ybegin; y <= yend; y++)
{
m_sprites[nr].paintedline[y] = 1;
for (i = 0; i < 3; i++)
{
value = m_expandx[m_dma_read_cb(addr + m_sprites[nr].line * 3 + i)];
m_sprites[nr].bitmap[y][i * 2] = value >> 8;
m_sprites[nr].bitmap[y][i * 2 + 1] = value & 0xff;
sprite_collision(nr, y, xbegin + i * 16, value >> 8);
sprite_collision(nr, y, xbegin + i * 16 + 8, value & 0xff);
if (prior || !collision)
value3 = getforeground16(yoff + y, xbegin + i * 16 - 7);
if (!collision && (value & value3))
{
collision = 1;
SPRITE_SET_BG_COLLISION(nr);
set_interrupt(2);
}
if (prior)
value &= ~value3;
draw_sprite_code(yoff + y, xbegin + i * 16, value >> 8, color, start_x, end_x);
draw_sprite_code(yoff + y, xbegin + i * 16 + 8, value & 0xff, color, start_x, end_x);
}
m_sprites[nr].bitmap[y][i * 2]=0; //easier sprite collision detection
if (SPRITE_Y_EXPAND(nr))
{
if (m_sprites[nr].repeat)
{
m_sprites[nr].line++;
m_sprites[nr].repeat = 0;
}
else
m_sprites[nr].repeat = 1;
}
else
{
m_sprites[nr].line++;
}
}
}
else
{
for (y = ybegin; y <= yend; y++)
{
m_sprites[nr].paintedline[y] = 1;
for (i = 0; i < 3; i++)
{
value = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i);
m_sprites[nr].bitmap[y][i] = value;
sprite_collision(nr, y, xbegin + i * 8, value);
if (prior || !collision)
value3 = getforeground(yoff + y, xbegin + i * 8 - 7);
if (!collision && (value & value3))
{
collision = 1;
SPRITE_SET_BG_COLLISION(nr);
set_interrupt(2);
}
if (prior)
value &= ~value3;
draw_sprite_code(yoff + y, xbegin + i * 8, value, color, start_x, end_x);
}
m_sprites[nr].bitmap[y][i]=0; //easier sprite collision detection
if (SPRITE_Y_EXPAND(nr))
{
if (m_sprites[nr].repeat)
{
m_sprites[nr].line++;
m_sprites[nr].repeat = 0;
}
else
m_sprites[nr].repeat = 1;
}
else
{
m_sprites[nr].line++;
}
}
}
}
void vic3_device::draw_sprite_multi( int nr, int yoff, int ybegin, int yend, int start_x, int end_x )
{
int y, i, prior, addr, xbegin, collision;
int value, value2, value3 = 0, bg/*, color[2]*/;
xbegin = SPRITE_X_POS(nr);
addr = m_dma_read_cb(SPRITE_ADDR(nr)) << 6;
m_spritemulti[2] = SPRITE_COLOR(nr);
prior = SPRITE_PRIORITY(nr);
collision = SPRITE_BG_COLLISION(nr);
//color[0] = 0;
//color[1] = 1;
if (SPRITE_X_EXPAND(nr))
{
for (y = ybegin; y <= yend; y++)
{
m_sprites[nr].paintedline[y] = 1;
for (i = 0; i < 3; i++)
{
value = m_expandx_multi[bg = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i)];
value2 = m_expandx[m_multi_collision[bg]];
m_sprites[nr].bitmap[y][i * 2] = value2 >> 8;
m_sprites[nr].bitmap[y][i * 2 + 1] = value2 & 0xff;
sprite_collision(nr, y, xbegin + i * 16, value2 >> 8);
sprite_collision(nr, y, xbegin + i * 16 + 8, value2 & 0xff);
if (prior || !collision)
{
value3 = getforeground16(yoff + y, xbegin + i * 16 - 7);
}
if (!collision && (value2 & value3))
{
collision = 1;
SPRITE_SET_BG_COLLISION(nr);
set_interrupt(2);
}
if (prior)
{
draw_sprite_code_multi(yoff + y, xbegin + i * 16, value >> 8, (value3 >> 8) ^ 0xff, start_x, end_x);
draw_sprite_code_multi(yoff + y, xbegin + i * 16 + 8, value & 0xff, (value3 & 0xff) ^ 0xff, start_x, end_x);
}
else
{
draw_sprite_code_multi(yoff + y, xbegin + i * 16, value >> 8, 0xff, start_x, end_x);
draw_sprite_code_multi(yoff + y, xbegin + i * 16 + 8, value & 0xff, 0xff, start_x, end_x);
}
}
m_sprites[nr].bitmap[y][i * 2]=0; //easier sprite collision detection
if (SPRITE_Y_EXPAND(nr))
{
if (m_sprites[nr].repeat)
{
m_sprites[nr].line++;
m_sprites[nr].repeat = 0;
}
else
m_sprites[nr].repeat = 1;
}
else
{
m_sprites[nr].line++;
}
}
}
else
{
for (y = ybegin; y <= yend; y++)
{
m_sprites[nr].paintedline[y] = 1;
for (i = 0; i < 3; i++)
{
value = m_dma_read_cb(addr + m_sprites[nr].line * 3 + i);
m_sprites[nr].bitmap[y][i] = value2 = m_multi_collision[value];
sprite_collision(nr, y, xbegin + i * 8, value2);
if (prior || !collision)
{
value3 = getforeground(yoff + y, xbegin + i * 8 - 7);
}
if (!collision && (value2 & value3))
{
collision = 1;
SPRITE_SET_BG_COLLISION(nr);
set_interrupt(2);
}
if (prior)
{
draw_sprite_code_multi(yoff + y, xbegin + i * 8, value, value3 ^ 0xff, start_x, end_x);
}
else
{
draw_sprite_code_multi(yoff + y, xbegin + i * 8, value, 0xff, start_x, end_x);
}
}
m_sprites[nr].bitmap[y][i] = 0; //easier sprite collision detection
if (SPRITE_Y_EXPAND(nr))
{
if (m_sprites[nr].repeat)
{
m_sprites[nr].line++;
m_sprites[nr].repeat = 0;
}
else
m_sprites[nr].repeat = 1;
}
else
{
m_sprites[nr].line++;
}
}
}
}
void vic3_device::drawlines( int first, int last, int start_x, int end_x )
{
int line, vline, end;
int attr, ch, ecm;
int syend;
int offs, yoff, xoff, ybegin, yend, xbegin, xend;
int x_end2;
int i, j;
if (first == last)
return;
m_lastline = last;
/* top part of display not rastered */
first -= VIC2_YPOS - YPOS;
last -= VIC2_YPOS - YPOS;
if ((first >= last) || (last <= 0))
{
for (i = 0; i < 8; i++)
m_sprites[i].repeat = m_sprites[i].line = 0;
return;
}
if (first < 0)
first = 0;
if (!SCREENON)
{
for (line = first; (line < last) && (line < m_bitmap->height()); line++)
{
std::fill_n(&m_bitmap->pix(line + FIRSTLINE), m_bitmap->width(), 0);
}
return;
}
if (COLUMNS40)
xbegin = XPOS, xend = xbegin + 640;
else
xbegin = XPOS + 7, xend = xbegin + 624;
if (last < m_y_begin)
end = last;
else
end = m_y_begin + YPOS;
for (line = first; line < end; line++)
{
std::fill_n(&m_bitmap->pix(line + FIRSTLINE), m_bitmap->width(), FRAMECOLOR);
}
if (LINES25)
{
vline = line - m_y_begin - YPOS;
}
else
{
vline = line - m_y_begin - YPOS + 8 - VERTICALPOS;
}
if (last < m_y_end + YPOS)
end = last;
else
end = m_y_end + YPOS;
x_end2 = m_x_end * 2;
for (; line < end; vline = (vline + 8) & ~7, line = line + 1 + yend - ybegin)
{
offs = (vline >> 3) * 80;
ybegin = vline & 7;
yoff = line - ybegin;
yend = (yoff + 7 < end) ? 7 : (end - yoff - 1);
/* rendering 39 characters */
/* left and right borders are overwritten later */
m_shift[line] = HORIZONTALPOS;
for (xoff = m_x_begin + XPOS; xoff < x_end2 + XPOS; xoff += 8, offs++)
{
ch = m_dma_read_cb(m_videoaddr + offs);
attr = m_dma_read_color_cb(m_videoaddr + offs);
if (HIRESON)
{
m_bitmapmulti[1] = m_c64_bitmap[1] = ch >> 4;
m_bitmapmulti[2] = m_c64_bitmap[0] = ch & 0xf;
if (MULTICOLORON)
{
m_bitmapmulti[3] = attr;
draw_bitmap_multi(ybegin, yend, offs, yoff, xoff, start_x, end_x);
}
else
{
draw_bitmap(ybegin, yend, offs, yoff, xoff, start_x, end_x);
}
}
else if (ECMON)
{
ecm = ch >> 6;
m_ecmcolor[0] = m_colors[ecm];
m_ecmcolor[1] = attr;
draw_character(ybegin, yend, ch & ~0xC0, yoff, xoff, m_ecmcolor, start_x, end_x);
}
else if (MULTICOLORON && (attr & 8))
{
m_multi[3] = attr & 7;
draw_character_multi(ybegin, yend, ch, yoff, xoff, start_x, end_x);
}
else
{
m_mono[1] = attr;
draw_character(ybegin, yend, ch, yoff, xoff, m_mono, start_x, end_x);
}
}
/* sprite priority, sprite overwrites lowerprior pixels */
for (i = 7; i >= 0; i--)
{
if (m_sprites[i].line || m_sprites[i].repeat)
{
syend = yend;
if (SPRITE_Y_EXPAND(i))
{
if ((21 - m_sprites[i].line) * 2 - m_sprites[i].repeat < yend - ybegin + 1)
syend = ybegin + (21 - m_sprites[i].line) * 2 - m_sprites[i].repeat - 1;
}
else
{
if (m_sprites[i].line + yend - ybegin + 1 > 20)
syend = ybegin + 20 - m_sprites[i].line;
}
if (yoff + syend > YPOS + 200)
syend = YPOS + 200 - yoff - 1;
if (SPRITE_MULTICOLOR(i))
draw_sprite_multi(i, yoff, ybegin, syend, start_x, end_x);
else
draw_sprite(i, yoff, ybegin, syend, start_x, end_x);
if ((syend != yend) || (m_sprites[i].line > 20))
{
m_sprites[i].line = m_sprites[i].repeat = 0;
for (j = syend; j <= yend; j++)
m_sprites[i].paintedline[j] = 0;
}
}
// sprite wrap y at the top of the screen
else if (SPRITEON(i) && (yoff == 1 + yend - ybegin) && (SPRITE_Y_POS(i) < 1 + yend - ybegin))
{
int wrapped = 1 + yend - ybegin - SPRITE_Y_POS(i);
syend = yend;
if (SPRITE_Y_EXPAND(i))
{
if (wrapped & 1) m_sprites[i].repeat = 1;
wrapped >>= 1;
syend = 21 * 2 - 1 - wrapped * 2;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
else
{
syend = 21 - 1 - wrapped;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
m_sprites[i].line = wrapped;
if (SPRITE_MULTICOLOR(i))
draw_sprite_multi(i, yoff, 0 , syend, start_x, end_x);
else
draw_sprite(i, yoff, 0 , syend, start_x, end_x);
if ((syend != yend) || (m_sprites[i].line > 20))
{
for (j = syend; j <= yend; j++)
m_sprites[i].paintedline[j] = 0;
m_sprites[i].line = m_sprites[i].repeat = 0;
}
}
else if (SPRITEON(i) && (yoff + ybegin <= SPRITE_Y_POS(i))
&& (yoff + yend >= SPRITE_Y_POS(i)))
{
syend = yend;
if (SPRITE_Y_EXPAND(i))
{
if (21 * 2 < yend - ybegin + 1)
syend = ybegin + 21 * 2 - 1;
}
else
{
if (yend - ybegin + 1 > 21)
syend = ybegin + 21 - 1;
}
if (yoff + syend >= YPOS + 200)
syend = YPOS + 200 - yoff - 1;
for (j = 0; j < SPRITE_Y_POS(i) - yoff; j++)
m_sprites[i].paintedline[j] = 0;
if (SPRITE_MULTICOLOR(i))
draw_sprite_multi(i, yoff, SPRITE_Y_POS(i) - yoff, syend, start_x, end_x);
else
draw_sprite(i, yoff, SPRITE_Y_POS(i) - yoff, syend, start_x, end_x);
if ((syend != yend) || (m_sprites[i].line > 20))
{
for (j = syend; j <= yend; j++)
m_sprites[i].paintedline[j] = 0;
m_sprites[i].line = m_sprites[i].repeat = 0;
}
}
else
{
memset (m_sprites[i].paintedline, 0, sizeof (m_sprites[i].paintedline));
}
}
for (i = ybegin; i <= yend; i++)
{
m_bitmap->plot_box(0, yoff + ybegin + FIRSTLINE, xbegin, yend - ybegin + 1, FRAMECOLOR);
m_bitmap->plot_box(xend, yoff + ybegin + FIRSTLINE, m_bitmap->width() - xend, yend - ybegin + 1, FRAMECOLOR);
}
}
if (last < m_bitmap->height())
end = last;
else
end = m_bitmap->height();
for (; line < end; line++)
{
std::fill_n(&m_bitmap->pix(line + FIRSTLINE), m_bitmap->width(), FRAMECOLOR);
}
}
void vic3_device::vic2_drawlines( int first, int last, int start_x, int end_x )
{
if (VIC3_BITPLANES)
return;
/* temporary allowing vic3 displaying 80 columns */
if (m_reg[0x31] & 0x80)
{
drawlines(first, first + 1, start_x, end_x);
return;
}
/* otherwise, draw VIC II output (currently using the old code, not the new one from vic6567.c) */
/* top part of display not rastered */
first -= VIC2_YPOS - YPOS;
int xbegin = VIC2_STARTVISIBLECOLUMNS;
int xend = xbegin + VIC2_VISIBLECOLUMNS;
if (!SCREENON)
{
xbegin = VIC2_STARTVISIBLECOLUMNS;
xend = xbegin + VIC2_VISIBLECOLUMNS;
if ((start_x <= xbegin) && (end_x >= xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x >= xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
if ((start_x <= xbegin) && (end_x < xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x < xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
return;
}
if (COLUMNS40)
{
xbegin = XPOS;
xend = xbegin + 320;
}
else
{
xbegin = XPOS + 7;
xend = xbegin + 304;
}
int end;
if (first + 1 < m_y_begin)
end = first + 1;
else
end = m_y_begin + YPOS;
int line = first;
// top border
if (line < end)
{
if ((start_x <= xbegin) && (end_x >= xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x >= xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
if ((start_x <= xbegin) && (end_x < xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x < xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
line = end;
}
int vline = line - YPOS + 3 - VERTICALPOS;
if (first + 1 < m_y_end + YPOS)
end = first + 1;
else
end = m_y_end + YPOS;
if (line < end)
{
int offs = (vline >> 3) * 40;
int ybegin = vline & 7;
int yoff = line - ybegin;
int yend = (yoff + 7 < end) ? 7 : (end - yoff - 1);
/* rendering 39 characters */
/* left and right borders are overwritten later */
m_shift[line] = HORIZONTALPOS;
for (int xoff = m_x_begin + XPOS; xoff < m_x_end + XPOS; xoff += 8, offs++)
{
int ch = m_dma_read_cb(m_videoaddr + offs);
#if 0
int attr = m_dma_read_color_cb(m_videoaddr + offs);
#else
/* temporary until vic3 finished */
int attr = m_dma_read_color_cb((m_videoaddr + offs)&0x3ff)&0x0f;
#endif
if (HIRESON)
{
m_bitmapmulti[1] = m_c64_bitmap[1] = ch >> 4;
m_bitmapmulti[2] = m_c64_bitmap[0] = ch & 0xf;
if (MULTICOLORON)
{
m_bitmapmulti[3] = attr;
draw_bitmap_multi(ybegin, yend, offs, yoff, xoff, start_x, end_x);
}
else
{
draw_bitmap(ybegin, yend, offs, yoff, xoff, start_x, end_x);
}
}
else if (ECMON)
{
int ecm = ch >> 6;
m_ecmcolor[0] = m_colors[ecm];
m_ecmcolor[1] = attr;
draw_character(ybegin, yend, ch & ~0xC0, yoff, xoff, m_ecmcolor, start_x, end_x);
}
else if (MULTICOLORON && (attr & 8))
{
m_multi[3] = attr & 7;
draw_character_multi(ybegin, yend, ch, yoff, xoff, start_x, end_x);
}
else
{
m_mono[1] = attr;
draw_character(ybegin, yend, ch, yoff, xoff, m_mono, start_x, end_x);
}
}
/* sprite priority, sprite overwrites lowerprior pixels */
for (int i = 7; i >= 0; i--)
{
if (SPRITEON (i) &&
(yoff + ybegin >= SPRITE_Y_POS (i)) &&
(yoff + ybegin - SPRITE_Y_POS (i) < (SPRITE_Y_EXPAND (i)? 21 * 2 : 21 )) &&
(SPRITE_Y_POS (i) < 0))
{
int wrapped = - SPRITE_Y_POS (i) + 6;
int syend = yend;
if (SPRITE_Y_EXPAND (i))
{
if (wrapped & 1) m_sprites[i].repeat = 1;
wrapped >>= 1;
syend = 21 * 2 - 1 - wrapped * 2;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
else
{
syend = 21 - 1 - wrapped;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
m_sprites[i].line = wrapped;
if (SPRITE_MULTICOLOR (i))
draw_sprite_multi(i, 0, 0 , syend, start_x, end_x);
else
draw_sprite(i, 0, 0 , syend, start_x, end_x);
}
else if (SPRITEON (i) &&
(yoff + ybegin >= SPRITE_Y_POS (i)) &&
(yoff + ybegin - SPRITE_Y_POS (i) < (SPRITE_Y_EXPAND (i)? 21 * 2 : 21 )) &&
(SPRITE_Y_POS (i) >= 0))
{
int wrapped = yoff + ybegin - SPRITE_Y_POS (i);
int syend = yend;
if (SPRITE_Y_EXPAND (i))
{
if (wrapped & 1) m_sprites[i].repeat = 1;
wrapped >>= 1;
syend = 21 * 2 - 1 - wrapped * 2;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
else
{
syend = 21 - 1 - wrapped;
if (syend > (yend - ybegin)) syend = yend - ybegin;
}
m_sprites[i].line = wrapped;
if (SPRITE_MULTICOLOR (i))
draw_sprite_multi(i, yoff + ybegin, 0, 0, start_x, end_x);
else
draw_sprite(i, yoff + ybegin, 0, 0, start_x, end_x);
}
else
{
memset(m_sprites[i].paintedline, 0, sizeof (m_sprites[i].paintedline));
}
}
line += 1 + yend - ybegin;
}
// left border
if ((start_x <= VIC2_STARTVISIBLECOLUMNS) && (end_x >= xbegin))
m_bitmap->plot_box(VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xbegin - VIC2_STARTVISIBLECOLUMNS, 1, FRAMECOLOR);
else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x >= xbegin))
m_bitmap->plot_box(start_x, first + FIRSTLINE, xbegin - start_x, 1, FRAMECOLOR);
else if ((start_x <= VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
m_bitmap->plot_box(VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x, 1, FRAMECOLOR);
else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
m_bitmap->plot_box(start_x, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
// right border
if ((start_x <= xend) && (end_x >= VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
m_bitmap->plot_box(xend, first + FIRSTLINE, VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS - xend, 1, FRAMECOLOR);
else if ((start_x > xend) && (end_x >= VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
m_bitmap->plot_box(start_x, first + FIRSTLINE, VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS - start_x, 1, FRAMECOLOR);
else if ((start_x <= xend) && (end_x < VIC2_STARTVISIBLECOLUMNS + VIC2_VISIBLECOLUMNS))
m_bitmap->plot_box(xend, first + FIRSTLINE, end_x - xend, 1, FRAMECOLOR);
else if ((start_x > VIC2_STARTVISIBLECOLUMNS) && (end_x < xbegin))
m_bitmap->plot_box(start_x, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
if (first + 1 < m_bitmap->height())
end = first + 1;
else
end = m_bitmap->height();
// bottom border
if (line < end)
{
if ((start_x <= xbegin) && (end_x >= xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, xend - xbegin, 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x >= xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, xend - start_x, 1, FRAMECOLOR);
if ((start_x <= xbegin) && (end_x < xend))
m_bitmap->plot_box(xbegin, first + FIRSTLINE, end_x - xbegin , 1, FRAMECOLOR);
if ((start_x > xbegin) && (end_x < xend))
m_bitmap->plot_box(start_x - VIC2_STARTVISIBLECOLUMNS, first + FIRSTLINE, end_x - start_x, 1, FRAMECOLOR);
line = end;
}
}
/*****************************************************************************
I/O HANDLERS
*****************************************************************************/
void vic3_device::palette_w(offs_t offset, uint8_t data)
{
if (offset < 0x100)
m_palette_red[offset] = data;
else if (offset < 0x200)
m_palette_green[offset & 0xff] = data;
else
m_palette_blue[offset & 0xff] = data;
m_palette_dirty = 1;
}
void vic3_device::port_w(offs_t offset, uint8_t data)
{
DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
offset &= 0x7f;
/* offsets 0x00 -> 0x2e coincide with VICII */
switch (offset)
{
case 0x01:
case 0x03:
case 0x05:
case 0x07:
case 0x09:
case 0x0b:
case 0x0d:
case 0x0f:
/* sprite y positions */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_sprites[offset / 2].y = SPRITE_Y_POS(offset / 2);
}
break;
case 0x00:
case 0x02:
case 0x04:
case 0x06:
case 0x08:
case 0x0a:
case 0x0c:
case 0x0e:
/* sprite x positions */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_sprites[offset / 2].x = SPRITE_X_POS(offset / 2);
}
break;
case 0x10: /* sprite x positions */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_sprites[0].x = SPRITE_X_POS(0);
m_sprites[1].x = SPRITE_X_POS(1);
m_sprites[2].x = SPRITE_X_POS(2);
m_sprites[3].x = SPRITE_X_POS(3);
m_sprites[4].x = SPRITE_X_POS(4);
m_sprites[5].x = SPRITE_X_POS(5);
m_sprites[6].x = SPRITE_X_POS(6);
m_sprites[7].x = SPRITE_X_POS(7);
}
break;
case 0x17: /* sprite y size */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x1d: /* sprite x size */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x1b: /* sprite background priority */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x1c: /* sprite multicolor mode select */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
/* sprite colors */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x25: /* sprite multicolor */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_spritemulti[1] = SPRITE_MULTICOLOR1;
}
break;
case 0x26: /* sprite multicolor */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_spritemulti[3] = SPRITE_MULTICOLOR2;
}
break;
case 0x19:
clear_interrupt(data & 0x0f);
break;
case 0x1a: /* irq mask */
m_reg[offset] = data;
set_interrupt(0); // beamrider needs this
break;
case 0x11:
if (m_reg[offset] != data)
{
m_reg[offset] = data;
if (LINES25)
{
m_y_begin = 0;
m_y_end = m_y_begin + 200;
}
else
{
m_y_begin = 4;
m_y_end = m_y_begin + 192;
}
}
break;
case 0x12:
if (data != m_reg[offset])
{
m_reg[offset] = data;
}
break;
case 0x16:
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_x_begin = HORIZONTALPOS;
m_x_end = m_x_begin + 320;
}
break;
case 0x18:
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_videoaddr = VIDEOADDR;
m_chargenaddr = CHARGENADDR;
m_bitmapaddr = BITMAPADDR;
}
break;
case 0x21: /* background color */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_mono[0] = m_bitmapmulti[0] = m_multi[0] = m_colors[0] = BACKGROUNDCOLOR;
}
break;
case 0x22: /* background color 1 */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_multi[1] = m_colors[1] = MULTICOLOR1;
}
break;
case 0x23: /* background color 2 */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_multi[2] = m_colors[2] = MULTICOLOR2;
}
break;
case 0x24: /* background color 3 */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
m_colors[3] = FOREGROUNDCOLOR;
}
break;
case 0x20: /* framecolor */
if (m_reg[offset] != data)
{
m_reg[offset] = data;
}
break;
case 0x2f:
DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
m_reg[offset] = data;
break;
case 0x30:
DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
m_reg[offset] = data;
m_port_changed_cb(offs_t(0), data);
break;
case 0x31:
m_reg[offset] = data;
if (data & 0x40)
m_cpu->set_clock_scale(1.0);
else
m_cpu->set_clock_scale(1.0/3.5);
break;
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
m_reg[offset] = data;
DBG_LOG(2, "vic write", ("%.2x:%.2x\n", offset, data));
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x45:
case 0x46:
case 0x47:
DBG_LOG(2, "vic plane write", ("%.2x:%.2x\n", offset, data));
break;
default:
m_reg[offset] = data;
break;
}
}
uint8_t vic3_device::port_r(offs_t offset)
{
int val = 0;
offset &= 0x7f;
/* offsets 0x00 -> 0x2e coincide with VICII */
switch (offset)
{
case 0x11:
val = (m_reg[offset] & ~0x80) | ((m_rasterline & 0x100) >> 1);
break;
case 0x12:
val = m_rasterline & 0xff;
break;
case 0x16:
val = m_reg[offset] | 0xc0;
break;
case 0x18:
val = m_reg[offset] | 0x01;
break;
case 0x19: /* interrupt flag register */
/* vic2_clear_interrupt(0xf); */
val = m_reg[offset] | 0x70;
break;
case 0x1a:
val = m_reg[offset] | 0xf0;
break;
case 0x1e: /* sprite to sprite collision detect */
val = m_reg[offset];
m_reg[offset] = 0;
clear_interrupt(4);
break;
case 0x1f: /* sprite to background collision detect */
val = m_reg[offset];
m_reg[offset] = 0;
clear_interrupt(2);
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
val = m_reg[offset];
break;
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
case 0x10:
case 0x17:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x2c:
case 0x2d:
case 0x2e:
val = m_reg[offset];
break;
case 0x2f:
case 0x30:
val = m_reg[offset];
DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
break;
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f: /* not used */
val = m_reg[offset];
DBG_LOG(2, "vic read", ("%.2x:%.2x\n", offset, val));
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x45:
case 0x46:
case 0x47:
DBG_LOG(2, "vic3 plane read", ("%.2x:%.2x\n", offset, val));
break;
default:
val = m_reg[offset];
}
return val;
}
#define VIC3_MASK(M) \
if (M) \
{ \
if (M & 0x01) \
colors[0] = m_c64_mem_r_cb(VIC3_ADDR(0) + offset); \
if (M & 0x02) \
colors[1] = m_c64_mem_r_cb(VIC3_ADDR(1) + offset) << 1; \
if (M & 0x04) \
colors[2] = m_c64_mem_r_cb(VIC3_ADDR(2) + offset) << 2; \
if (M & 0x08) \
colors[3] = m_c64_mem_r_cb(VIC3_ADDR(3) + offset) << 3; \
if (M & 0x10) \
colors[4] = m_c64_mem_r_cb(VIC3_ADDR(4) + offset) << 4; \
if (M & 0x20) \
colors[5] = m_c64_mem_r_cb(VIC3_ADDR(5) + offset) << 5; \
if (M & 0x40) \
colors[6] = m_c64_mem_r_cb(VIC3_ADDR(6) + offset) << 6; \
if (M & 0x80) \
colors[7] = m_c64_mem_r_cb(VIC3_ADDR(7) + offset) << 7; \
for (int i = 7; i >= 0; i--) \
{ \
int p = 0; \
if (M & 0x01) \
{ \
p = colors[0] & 0x01; \
colors[0] >>= 1; \
} \
if (M & 0x02) \
{ \
p |= colors[1] & 0x02; \
colors[1] >>= 1; \
} \
if (M & 0x04) \
{ \
p |= colors[2] & 0x04; \
colors[2] >>= 1; \
} \
if (M & 0x08) \
{ \
p |= colors[3] & 0x08; \
colors[3] >>= 1; \
} \
if (M & 0x10) \
{ \
p |= colors[4] & 0x10; \
colors[4] >>= 1; \
} \
if (M & 0x20) \
{ \
p |= colors[5] & 0x20; \
colors[5] >>= 1; \
} \
if (M & 0x40) \
{ \
p |= colors[6] & 0x40; \
colors[6] >>= 1; \
} \
if (M & 0x80) \
{ \
p |= colors[7] & 0x80; \
colors[7] >>= 1; \
} \
m_bitmap->pix(YPOS + y, XPOS + x + i) = p; \
} \
}
#define VIC3_ADDR(a) VIC3_BITPLANE_IADDR(a)
void vic3_device::interlace_draw_block( int x, int y, int offset )
{
int colors[8] = {0};
switch (VIC3_BITPLANES_MASK)
{
case 0x05:
VIC3_MASK(0x05)
break;
case 0x07:
VIC3_MASK(0x07)
break;
case 0x0f:
VIC3_MASK(0x0f)
break;
case 0x1f:
VIC3_MASK(0x1f)
break;
case 0x7f:
VIC3_MASK(0x7f)
break;
case 0xff:
VIC3_MASK(0xff)
break;
default:
if (VIC3_BITPLANES_MASK & 0x01)
colors[0] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(0) + offset);
if (VIC3_BITPLANES_MASK & 0x02)
colors[1] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(1) + offset) << 1;
if (VIC3_BITPLANES_MASK & 0x04)
colors[2] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(2) + offset) << 2;
if (VIC3_BITPLANES_MASK & 0x08)
colors[3] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(3) + offset) << 3;
if (VIC3_BITPLANES_MASK & 0x10)
colors[4] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(4) + offset) << 4;
if (VIC3_BITPLANES_MASK & 0x20)
colors[5] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(5) + offset) << 5;
if (VIC3_BITPLANES_MASK & 0x40)
colors[6] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(6) + offset) << 6;
if (VIC3_BITPLANES_MASK & 0x80)
colors[7] = m_c64_mem_r_cb(VIC3_BITPLANE_IADDR(7) + offset) << 7;
for (int i = 7; i >= 0; i--)
{
m_bitmap->pix(YPOS + y, XPOS + x + i) =
(colors[0] & 0x01) | (colors[1] & 0x02) |
(colors[2] & 0x04) | (colors[3] & 0x08) |
(colors[4] & 0x10) | (colors[5] & 0x20) |
(colors[6] & 0x40) | (colors[7] & 0x80);
colors[0] >>= 1;
colors[1] >>= 1;
colors[2] >>= 1;
colors[3] >>= 1;
colors[4] >>= 1;
colors[5] >>= 1;
colors[6] >>= 1;
colors[7] >>= 1;
}
}
}
#undef VIC3_ADDR
#define VIC3_ADDR(a) VIC3_BITPLANE_ADDR(a)
void vic3_device::draw_block( int x, int y, int offset )
{
int colors[8] = {0};
switch (VIC3_BITPLANES_MASK)
{
case 5:
VIC3_MASK(0x05)
break;
case 7:
VIC3_MASK(0x07)
break;
case 0xf:
VIC3_MASK(0x0f)
break;
case 0x1f:
VIC3_MASK(0x1f)
break;
case 0x7f:
VIC3_MASK(0x7f)
break;
case 0xff:
VIC3_MASK(0xff)
break;
default:
if (VIC3_BITPLANES_MASK & 0x01)
colors[0] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(0) + offset);
if (VIC3_BITPLANES_MASK & 0x02)
colors[1] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(1) + offset) << 1;
if (VIC3_BITPLANES_MASK & 0x04)
colors[2] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(2) + offset) << 2;
if (VIC3_BITPLANES_MASK & 0x08)
colors[3] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(3) + offset) << 3;
if (VIC3_BITPLANES_MASK & 0x10)
colors[4] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(4) + offset) << 4;
if (VIC3_BITPLANES_MASK & 0x20)
colors[5] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(5) + offset) << 5;
if (VIC3_BITPLANES_MASK & 0x40)
colors[6] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(6) + offset) << 6;
if (VIC3_BITPLANES_MASK & 0x80)
colors[7] = m_c64_mem_r_cb(VIC3_BITPLANE_ADDR(7) + offset) << 7;
for (int i = 7; i >= 0; i--)
{
m_bitmap->pix(YPOS + y, XPOS + x + i) =
(colors[0] & 0x01) | (colors[1] & 0x02) |
(colors[2] & 0x04) | (colors[3] & 0x08) |
(colors[4] & 0x10) | (colors[5] & 0x20) |
(colors[6] & 0x40) | (colors[7] & 0x80);
colors[0] >>= 1;
colors[1] >>= 1;
colors[2] >>= 1;
colors[3] >>= 1;
colors[4] >>= 1;
colors[5] >>= 1;
colors[6] >>= 1;
colors[7] >>= 1;
}
}
}
void vic3_device::draw_bitplanes()
{
const rectangle &visarea = screen().visible_area();
if (VIC3_LINES == 400)
{ /* interlaced! */
for (int y1s = 0, offset = 0; y1s < 400; y1s += 16)
{
for (int x = 0; x < VIC3_BITPLANES_WIDTH; x += 8)
{
for (int y = y1s; y < y1s + 16; y += 2, offset++)
{
if (m_interlace)
draw_block(x, y, offset);
else
interlace_draw_block(x, y + 1, offset);
}
}
}
m_interlace ^= 1;
}
else
{
for (int y1s = 0, offset = 0; y1s < 200; y1s += 8)
{
for (int x = 0; x < VIC3_BITPLANES_WIDTH; x += 8)
{
for (int y = y1s; y < y1s + 8; y++, offset++)
{
draw_block(x, y, offset);
}
}
}
}
if (XPOS > 0)
{
rectangle vis(0, XPOS - 1, 0, visarea.bottom());
m_bitmap->fill(FRAMECOLOR, vis);
}
if (XPOS + VIC3_BITPLANES_WIDTH < visarea.right())
{
rectangle vis(XPOS + VIC3_BITPLANES_WIDTH, visarea.right(), 0, visarea.bottom());
m_bitmap->fill(FRAMECOLOR, vis);
}
if (YPOS > 0)
{
rectangle vis(0, visarea.right(), 0, YPOS - 1);
m_bitmap->fill(FRAMECOLOR, vis);
}
if (YPOS + VIC3_LINES < visarea.bottom())
{
rectangle vis(0, visarea.right(), YPOS + VIC3_LINES, visarea.bottom());
m_bitmap->fill(FRAMECOLOR, vis);
}
}
void vic3_device::raster_interrupt_gen()
{
int new_columns, new_rows;
int i;
m_rasterline++;
if (m_rasterline >= m_lines)
{
m_rasterline = 0;
if (m_palette_dirty)
for (i = 0; i < 256; i++)
set_pen_color(i, m_palette_red[i] << 4, m_palette_green[i] << 4, m_palette_blue[i] << 4);
if (m_palette_dirty)
{
m_spritemulti[1] = SPRITE_MULTICOLOR1;
m_spritemulti[3] = SPRITE_MULTICOLOR2;
m_mono[0] = m_bitmapmulti[0] = m_multi[0] = m_colors[0] = BACKGROUNDCOLOR;
m_multi[1] = m_colors[1] = MULTICOLOR1;
m_multi[2] = m_colors[2] = MULTICOLOR2;
m_colors[3] = FOREGROUNDCOLOR;
m_palette_dirty = 0;
}
new_rows = 200;
if (VIC3_BITPLANES)
{
new_columns = VIC3_BITPLANES_WIDTH;
if (new_columns < 320)
new_columns = 320; /*sprites resolution about 320x200 */
new_rows = VIC3_LINES;
}
else if (VIC3_80COLUMNS)
{
new_columns = 640;
}
else
{
new_columns = 320;
}
if ((new_columns != m_columns) || (new_rows != m_rows))
{
m_rows = new_rows;
m_columns = new_columns;
if (m_type == vic3_type::PAL)
screen().set_visible_area(
VIC2_STARTVISIBLECOLUMNS + 32,
VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1,
VIC2_STARTVISIBLELINES + 34,
VIC2_STARTVISIBLELINES + 34 + m_rows + 16 - 1);
else
screen().set_visible_area(
VIC2_STARTVISIBLECOLUMNS + 34,
VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1,
VIC2_STARTVISIBLELINES + 10,
VIC2_STARTVISIBLELINES + 10 + m_rows + 16 - 1);
}
if (VIC3_BITPLANES)
{
draw_bitplanes();
}
else
{
if (m_type == vic3_type::PAL)
{
if (m_on)
vic2_drawlines(m_lastline, m_lines, VIC2_STARTVISIBLECOLUMNS + 32, VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1);
}
else
{
if (m_on)
vic2_drawlines(m_lastline, m_lines, VIC2_STARTVISIBLECOLUMNS + 34, VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1);
}
}
for (i = 0; i < 8; i++)
m_sprites[i].repeat = m_sprites[i].line = 0;
m_lastline = 0;
if (LIGHTPEN_BUTTON)
{
/* lightpen timer start */
machine().scheduler().synchronize(timer_expired_delegate(FUNC(vic3_device::timer_timeout), this), 1);
}
}
if (m_rasterline == C64_2_RASTERLINE(RASTERLINE))
{
set_interrupt(1);
}
if (m_on)
if ((m_rasterline >= VIC2_FIRSTRASTERLINE) && (m_rasterline < (VIC2_FIRSTRASTERLINE + VIC2_VISIBLELINES)))
{
if (m_type == vic3_type::PAL)
{
if (m_on)
vic2_drawlines(m_rasterline - 1, m_rasterline, VIC2_STARTVISIBLECOLUMNS + 32, VIC2_STARTVISIBLECOLUMNS + 32 + m_columns + 16 - 1);
}
else
{
if (m_on)
vic2_drawlines(m_rasterline - 1, m_rasterline, VIC2_STARTVISIBLECOLUMNS + 34, VIC2_STARTVISIBLECOLUMNS + 34 + m_columns + 16 - 1);
}
}
}
uint32_t vic3_device::video_update( bitmap_ind16 &bitmap, const rectangle &cliprect )
{
copybitmap(bitmap, *m_bitmap, 0, 0, 0, 0, cliprect);
return 0;
}