593 lines
14 KiB
C
593 lines
14 KiB
C
// license:BSD-3-Clause
|
|
// copyright-holders:Curt Coder
|
|
/**********************************************************************
|
|
|
|
VideoBrain UV201/UV202 video chip emulation
|
|
|
|
Copyright MESS Team.
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "uv201.h"
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// MACROS / CONSTANTS
|
|
//**************************************************************************
|
|
|
|
#define LOG 1
|
|
|
|
|
|
// screen parameters
|
|
#define SCREEN_WIDTH 232
|
|
#define SCREEN_HEIGHT 262
|
|
#define VISAREA_WIDTH 193
|
|
#define VBLANK_WIDTH 21
|
|
#define HBLANK_WIDTH 39
|
|
#define HSYNC_WIDTH 18
|
|
#define HFP_WIDTH 16
|
|
#define HBP_WIDTH 5
|
|
#define HBLANK_END HSYNC_WIDTH + HFP_WIDTH
|
|
#define HBLANK_START HBLANK_END + VISAREA_WIDTH
|
|
|
|
|
|
// write-only registers
|
|
#define REGISTER_COMMAND 0xf7
|
|
#define REGISTER_BACKGROUND 0xf5
|
|
#define REGISTER_FINAL_MODIFIER 0xf2
|
|
#define REGISTER_Y_INTERRUPT 0xf0
|
|
|
|
|
|
// read-only registers
|
|
#define REGISTER_X_FREEZE 0xf8
|
|
#define REGISTER_Y_FREEZE_LOW 0xf9
|
|
#define REGISTER_Y_FREEZE_HIGH 0xfa
|
|
#define REGISTER_CURRENT_Y_LOW 0xfb
|
|
|
|
|
|
// read/write registers - RAM memory
|
|
#define RAM_RP_LO 0x00 // cartridge pointer low order
|
|
#define RAM_RP_HI_COLOR 0x10 // cartridge pointer high order and color
|
|
#define RAM_DX_INT_XCOPY 0x20 // dX, intensity, X-copy
|
|
#define RAM_DY 0x30 // dY
|
|
#define RAM_X 0x40 // X value
|
|
#define RAM_Y_LO_A 0x50 // Y value low order list A
|
|
#define RAM_Y_LO_B 0x60 // Y value low order list B
|
|
#define RAM_XY_HI_A 0x70 // Y value high order and X order list A
|
|
#define RAM_XY_HI_B 0x80 // Y value high order and X order list B
|
|
|
|
|
|
// command register bits
|
|
#define COMMAND_YINT_H_O 0x80
|
|
#define COMMAND_A_B 0x40
|
|
#define COMMAND_Y_ZM 0x20
|
|
#define COMMAND_KBD 0x10
|
|
#define COMMAND_INT 0x08
|
|
#define COMMAND_ENB 0x04
|
|
#define COMMAND_FRZ 0x02
|
|
#define COMMAND_X_ZM 0x01
|
|
|
|
|
|
#define IS_CHANGED(_bit) \
|
|
((m_cmd & _bit) != (data & _bit))
|
|
|
|
#define RAM(_offset) \
|
|
m_ram[_offset + i]
|
|
|
|
#define RAM_XORD(_offset) \
|
|
m_ram[_offset + xord]
|
|
|
|
#define IS_VISIBLE(_y) \
|
|
((_y >= cliprect.min_y) && (_y <= cliprect.max_y))
|
|
|
|
#define DRAW_PIXEL(_scanline, _dot) \
|
|
if (IS_VISIBLE(_scanline)) bitmap.pix32((_scanline), HSYNC_WIDTH + HFP_WIDTH + _dot) = m_palette_val[pixel];
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// LIVE DEVICE
|
|
//**************************************************************************
|
|
|
|
// device type definition
|
|
const device_type UV201 = &device_creator<uv201_device>;
|
|
|
|
|
|
//-------------------------------------------------
|
|
// uv201_device - constructor
|
|
//-------------------------------------------------
|
|
|
|
uv201_device::uv201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
|
|
device_t(mconfig, UV201, "UV201", tag, owner, clock, "uv201", __FILE__),
|
|
device_video_interface(mconfig, *this),
|
|
m_write_ext_int(*this),
|
|
m_write_hblank(*this),
|
|
m_read_db(*this)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::device_start()
|
|
{
|
|
// resolve callbacks
|
|
m_write_ext_int.resolve_safe();
|
|
m_write_hblank.resolve_safe();
|
|
m_read_db.resolve_safe(0);
|
|
|
|
// allocate timers
|
|
m_timer_y_odd = timer_alloc(TIMER_Y_ODD);
|
|
m_timer_y_even = timer_alloc(TIMER_Y_EVEN);
|
|
m_timer_hblank_on = timer_alloc(TIMER_HBLANK_ON);
|
|
m_timer_hblank_off = timer_alloc(TIMER_HBLANK_OFF);
|
|
|
|
initialize_palette();
|
|
|
|
memset(m_ram, 0x00, sizeof(m_ram));
|
|
m_y_int = 0;
|
|
m_fmod = 0;
|
|
m_bg = 0;
|
|
m_cmd = 0;
|
|
m_freeze_x = 0;
|
|
m_freeze_y = 0;
|
|
m_field = 0;
|
|
|
|
// state saving
|
|
save_item(NAME(m_ram));
|
|
save_item(NAME(m_y_int));
|
|
save_item(NAME(m_fmod));
|
|
save_item(NAME(m_bg));
|
|
save_item(NAME(m_cmd));
|
|
save_item(NAME(m_freeze_x));
|
|
save_item(NAME(m_freeze_y));
|
|
save_item(NAME(m_field));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_reset - device-specific reset
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::device_reset()
|
|
{
|
|
m_write_ext_int(CLEAR_LINE);
|
|
|
|
m_write_hblank(1);
|
|
m_timer_hblank_off->adjust(attotime::from_ticks( HBLANK_END, m_clock ));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_timer - handle timer events
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
|
{
|
|
int scanline = m_screen->vpos();
|
|
|
|
switch (id)
|
|
{
|
|
case TIMER_Y_ODD:
|
|
case TIMER_Y_EVEN:
|
|
if ((m_cmd & COMMAND_INT) && !(m_cmd & COMMAND_FRZ))
|
|
{
|
|
if (LOG) logerror("Y-Interrupt at scanline %u\n", scanline);
|
|
|
|
m_freeze_y = scanline;
|
|
|
|
m_write_ext_int(ASSERT_LINE);
|
|
m_write_ext_int(CLEAR_LINE);
|
|
}
|
|
break;
|
|
|
|
case TIMER_HBLANK_ON:
|
|
m_write_hblank(1);
|
|
|
|
m_timer_hblank_off->adjust(attotime::from_ticks( HBLANK_WIDTH, m_clock ) );
|
|
break;
|
|
|
|
case TIMER_HBLANK_OFF:
|
|
m_write_hblank(0);
|
|
|
|
m_timer_hblank_on->adjust(attotime::from_ticks( VISAREA_WIDTH, m_clock ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// initialize_palette -
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::initialize_palette()
|
|
{
|
|
UINT8 offlointensity = 0x00;
|
|
UINT8 offhiintensity = 0xc0;
|
|
|
|
UINT8 onlointensity = 0xa0;
|
|
UINT8 onhiintensity = 0xff;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int offset = i * 8;
|
|
UINT8 onvalue, offvalue;
|
|
|
|
if (offset < 16)
|
|
{
|
|
offvalue = offlointensity;
|
|
onvalue = onlointensity;
|
|
}
|
|
else
|
|
{
|
|
offvalue = offhiintensity;
|
|
onvalue = onhiintensity;
|
|
}
|
|
|
|
m_palette_val[offset + 0] = rgb_t(offvalue, offvalue, offvalue); // black
|
|
m_palette_val[offset + 1] = rgb_t(onvalue, offvalue, offvalue); // red
|
|
m_palette_val[offset + 2] = rgb_t(offvalue, onvalue, offvalue); // green
|
|
m_palette_val[offset + 3] = rgb_t(onvalue, onvalue, offvalue); // red-green
|
|
m_palette_val[offset + 4] = rgb_t(offvalue, offvalue, onvalue); // blue
|
|
m_palette_val[offset + 5] = rgb_t(onvalue, offvalue, onvalue); // red-blue
|
|
m_palette_val[offset + 6] = rgb_t(offvalue, onvalue, onvalue); // green-blue
|
|
m_palette_val[offset + 7] = rgb_t(onvalue, onvalue, onvalue); // white
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_field_vpos - get scanline within field
|
|
//-------------------------------------------------
|
|
|
|
int uv201_device::get_field_vpos()
|
|
{
|
|
int vpos = m_screen->vpos();
|
|
|
|
if (vpos >= SCREEN_HEIGHT)
|
|
{
|
|
// even field
|
|
vpos -= SCREEN_HEIGHT;
|
|
}
|
|
|
|
return vpos;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_field - get video field
|
|
//-------------------------------------------------
|
|
|
|
int uv201_device::get_field()
|
|
{
|
|
return m_screen->vpos() < SCREEN_HEIGHT;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_y_interrupt - set Y interrupt timer
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::set_y_interrupt()
|
|
{
|
|
int scanline = ((m_cmd & COMMAND_YINT_H_O) << 1) | m_y_int;
|
|
|
|
m_timer_y_odd->adjust(m_screen->time_until_pos(scanline), 0, m_screen->frame_period());
|
|
//m_timer_y_even->adjust(m_screen->time_until_pos(scanline + SCREEN_HEIGHT), 0, m_screen->frame_period());
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// do_partial_update - update screen
|
|
//-------------------------------------------------
|
|
|
|
void uv201_device::do_partial_update()
|
|
{
|
|
int vpos = m_screen->vpos();
|
|
|
|
if (LOG) logerror("Partial screen update at scanline %u\n", vpos);
|
|
|
|
m_screen->update_partial(vpos);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// read -
|
|
//-------------------------------------------------
|
|
|
|
READ8_MEMBER( uv201_device::read )
|
|
{
|
|
UINT8 data = 0xff;
|
|
|
|
switch (offset)
|
|
{
|
|
case REGISTER_X_FREEZE:
|
|
data = m_freeze_x;
|
|
|
|
if (LOG) logerror("X-Freeze %02x\n", data);
|
|
break;
|
|
|
|
case REGISTER_Y_FREEZE_LOW:
|
|
data = m_freeze_y & 0xff;
|
|
|
|
if (LOG) logerror("Y-Freeze Low %02x\n", data);
|
|
break;
|
|
|
|
case REGISTER_Y_FREEZE_HIGH:
|
|
/*
|
|
|
|
bit signal description
|
|
|
|
0 Y-F8 Y freeze high order (MSB) bit
|
|
1 Y-C8 current Y counter high order (MSB) bit
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7 O/_E odd/even field
|
|
|
|
*/
|
|
|
|
data = (get_field() << 7) | (BIT(get_field_vpos(), 8) << 1) | BIT(m_freeze_y, 8);
|
|
|
|
if (LOG) logerror("Y-Freeze High %02x\n", data);
|
|
break;
|
|
|
|
case REGISTER_CURRENT_Y_LOW:
|
|
data = get_field_vpos() & 0xff;
|
|
|
|
if (LOG) logerror("Current-Y Low %02x\n", data);
|
|
break;
|
|
|
|
default:
|
|
if (offset < 0x90)
|
|
data = m_ram[offset];
|
|
else
|
|
if (LOG) logerror("Unknown VLSI read from %02x!\n", offset);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// write -
|
|
//-------------------------------------------------
|
|
|
|
WRITE8_MEMBER( uv201_device::write )
|
|
{
|
|
switch (offset)
|
|
{
|
|
case REGISTER_Y_INTERRUPT:
|
|
if (LOG) logerror("Y-Interrupt %02x\n", data);
|
|
|
|
if (m_y_int != data)
|
|
{
|
|
m_y_int = data;
|
|
set_y_interrupt();
|
|
}
|
|
break;
|
|
|
|
case REGISTER_FINAL_MODIFIER:
|
|
/*
|
|
|
|
bit signal description
|
|
|
|
0 RED red
|
|
1 GREEN green
|
|
2 BLUE blue
|
|
3 INT 0 intensity 0
|
|
4 INT 1 intensity 1
|
|
5 not used
|
|
6 not used
|
|
7 not used
|
|
|
|
*/
|
|
|
|
if (LOG) logerror("Final Modifier %02x\n", data);
|
|
|
|
do_partial_update();
|
|
m_fmod = data & 0x1f;
|
|
break;
|
|
|
|
case REGISTER_BACKGROUND:
|
|
/*
|
|
|
|
bit signal description
|
|
|
|
0 RED red
|
|
1 GREEN green
|
|
2 BLUE blue
|
|
3 INT 0 intensity 0
|
|
4 INT 1 intensity 1
|
|
5 not used
|
|
6 not used
|
|
7 not used
|
|
|
|
*/
|
|
|
|
if (LOG) logerror("Background %02x\n", data);
|
|
|
|
do_partial_update();
|
|
m_bg = data & 0x1f;
|
|
break;
|
|
|
|
case REGISTER_COMMAND:
|
|
/*
|
|
|
|
bit signal description
|
|
|
|
0 X-ZM X zoom
|
|
1 FRZ freeze
|
|
2 ENB video enable
|
|
3 INT interrupt enable
|
|
4 KBD general purpose output
|
|
5 Y-ZM Y zoom
|
|
6 A/_B list selection
|
|
7 YINT H.O. Y COMMAND_INT register high order bit
|
|
|
|
*/
|
|
|
|
if (LOG) logerror("Command %02x\n", data);
|
|
|
|
if (IS_CHANGED(COMMAND_YINT_H_O))
|
|
{
|
|
set_y_interrupt();
|
|
}
|
|
|
|
if (IS_CHANGED(COMMAND_A_B) || IS_CHANGED(COMMAND_Y_ZM) || IS_CHANGED(COMMAND_X_ZM))
|
|
{
|
|
do_partial_update();
|
|
}
|
|
|
|
m_cmd = data;
|
|
break;
|
|
|
|
default:
|
|
if (offset < 0x90)
|
|
m_ram[offset] = data;
|
|
else
|
|
logerror("Unknown VLSI write %02x to %02x!\n", data, offset);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ext_int_w - external interrupt write
|
|
//-------------------------------------------------
|
|
|
|
WRITE_LINE_MEMBER( uv201_device::ext_int_w )
|
|
{
|
|
if (!state && (m_cmd & COMMAND_FRZ))
|
|
{
|
|
m_freeze_y = get_field_vpos();
|
|
m_freeze_x = m_screen->hpos();
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// kbd_r - keyboard select read
|
|
//-------------------------------------------------
|
|
|
|
READ_LINE_MEMBER( uv201_device::kbd_r )
|
|
{
|
|
return (m_cmd & COMMAND_KBD) ? 1 : 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// screen_update -
|
|
//-------------------------------------------------
|
|
|
|
UINT32 uv201_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
|
|
{
|
|
bitmap.fill(rgb_t(0x00,0x00,0x00), cliprect);
|
|
|
|
if (!(m_cmd & COMMAND_ENB))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (int y = 0; y < SCREEN_HEIGHT; y++)
|
|
{
|
|
for (int x = 0; x < VISAREA_WIDTH; x++)
|
|
{
|
|
int pixel = m_bg;
|
|
DRAW_PIXEL(y, x);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
UINT8 xy_hi = (m_cmd & COMMAND_A_B) ? RAM(RAM_XY_HI_A) : RAM(RAM_XY_HI_B);
|
|
UINT8 y_lo = (m_cmd & COMMAND_A_B) ? RAM(RAM_Y_LO_A) : RAM(RAM_Y_LO_B);
|
|
UINT16 y = (BIT(xy_hi, 7) << 8) | y_lo;
|
|
int xord = xy_hi & 0x0f;
|
|
|
|
UINT8 rp_hi_color = RAM_XORD(RAM_RP_HI_COLOR);
|
|
UINT8 rp_lo = RAM_XORD(RAM_RP_LO);
|
|
UINT16 rp = ((rp_hi_color << 8) | rp_lo) & 0x1fff;
|
|
|
|
if (rp < 0x800) rp |= 0x2000;
|
|
|
|
UINT8 dx_int_xcopy = RAM_XORD(RAM_DX_INT_XCOPY);
|
|
int color = ((dx_int_xcopy & 0x60) >> 2) | (BIT(rp_hi_color, 5) << 2) | (BIT(rp_hi_color, 6) << 1) | (BIT(rp_hi_color, 7));
|
|
UINT8 dx = dx_int_xcopy & 0x1f;
|
|
UINT8 dy = RAM_XORD(RAM_DY);
|
|
int xcopy = BIT(dx_int_xcopy, 7);
|
|
UINT8 x = RAM_XORD(RAM_X);
|
|
|
|
if (LOG) logerror("Object %u xord %u y %u x %u dy %u dx %u xcopy %u color %u rp %04x\n", i, xord, y, x, dy, dx, xcopy, color, rp);
|
|
|
|
if (rp == 0) continue;
|
|
if (y > SCREEN_HEIGHT) continue;
|
|
|
|
for (int sy = 0; sy < dy; sy++)
|
|
{
|
|
for (int sx = 0; sx < dx; sx++)
|
|
{
|
|
UINT8 data = m_read_db(rp);
|
|
|
|
for (int bit = 0; bit < 8; bit++)
|
|
{
|
|
int pixel = ((BIT(data, 7) ? color : m_bg) ^ m_fmod) & 0x1f;
|
|
|
|
if (m_cmd & COMMAND_Y_ZM)
|
|
{
|
|
int scanline = y + (sy * 2);
|
|
|
|
if (m_cmd & COMMAND_X_ZM)
|
|
{
|
|
int dot = (x * 2) + (sx * 16) + (bit * 2);
|
|
|
|
DRAW_PIXEL(scanline, dot);
|
|
DRAW_PIXEL(scanline, dot + 1);
|
|
DRAW_PIXEL(scanline + 1, dot);
|
|
DRAW_PIXEL(scanline + 1, dot + 1);
|
|
}
|
|
else
|
|
{
|
|
int dot = x + (sx * 8) + bit;
|
|
|
|
DRAW_PIXEL(scanline, dot);
|
|
DRAW_PIXEL(scanline + 1, dot);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int scanline = y + sy;
|
|
|
|
if (m_cmd & COMMAND_X_ZM)
|
|
{
|
|
int dot = (x * 2) + (sx * 16) + (bit * 2);
|
|
|
|
DRAW_PIXEL(scanline, dot);
|
|
DRAW_PIXEL(scanline, dot + 1);
|
|
}
|
|
else
|
|
{
|
|
int dot = x + (sx * 8) + bit;
|
|
|
|
DRAW_PIXEL(scanline, dot);
|
|
}
|
|
}
|
|
|
|
data <<= 1;
|
|
}
|
|
|
|
if (!xcopy) rp++;
|
|
}
|
|
|
|
if (xcopy) rp++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|