Imported UPD3301 CRTC from MESS. (no whatsnew)

This commit is contained in:
Curt Coder 2011-04-08 06:15:46 +00:00
parent b681899978
commit 1fcf326181
4 changed files with 893 additions and 0 deletions

2
.gitattributes vendored
View File

@ -1243,6 +1243,8 @@ src/emu/video/tms9927.c svneol=native#text/plain
src/emu/video/tms9927.h svneol=native#text/plain
src/emu/video/tms9928a.c svneol=native#text/plain
src/emu/video/tms9928a.h svneol=native#text/plain
src/emu/video/upd3301.c svneol=native#text/plain
src/emu/video/upd3301.h svneol=native#text/plain
src/emu/video/v9938.c svneol=native#text/plain
src/emu/video/v9938.h svneol=native#text/plain
src/emu/video/v9938mod.c svneol=native#text/plain

View File

@ -250,6 +250,7 @@ EMUVIDEOOBJS = \
$(EMUVIDEO)/tms34061.o \
$(EMUVIDEO)/tms9927.o \
$(EMUVIDEO)/tms9928a.o \
$(EMUVIDEO)/upd3301.o \
$(EMUVIDEO)/v9938.o \
$(EMUVIDEO)/vector.o \
$(EMUVIDEO)/voodoo.o \

668
src/emu/video/upd3301.c Normal file
View File

@ -0,0 +1,668 @@
/**********************************************************************
NEC uPD3301 Programmable CRT Controller emulation
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************/
/*
TODO:
- attributes
- N interrupt
- light pen
- reset counters
- proper DMA timing (now the whole screen is transferred at the end of the frame,
accurate timing requires CCLK timer which kills performance)
*/
#include "emu.h"
#include "upd3301.h"
#include "machine/devhelpr.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define LOG 0
#define COMMAND_MASK 0xe0
#define COMMAND_RESET 0x00
#define COMMAND_START_DISPLAY 0x20
#define COMMAND_SET_INTERRUPT_MASK 0x40
#define COMMAND_READ_LIGHT_PEN 0x60 // not supported
#define COMMAND_LOAD_CURSOR_POSITION 0x80
#define COMMAND_RESET_INTERRUPT 0xa0
#define COMMAND_RESET_COUNTERS 0xc0 // not supported
#define STATUS_VE 0x10
#define STATUS_U 0x08 // not supported
#define STATUS_N 0x04 // not supported
#define STATUS_E 0x02
#define STATUS_LP 0x01 // not supported
enum
{
MODE_NONE,
MODE_RESET,
MODE_READ_LIGHT_PEN,
MODE_LOAD_CURSOR_POSITION,
MODE_RESET_COUNTERS
};
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// devices
const device_type UPD3301 = upd3301_device_config::static_alloc_device_config;
//**************************************************************************
// DEVICE CONFIGURATION
//**************************************************************************
GENERIC_DEVICE_CONFIG_SETUP(upd3301, "UPD3301")
//-------------------------------------------------
// device_config_complete - perform any
// operations now that the configuration is
// complete
//-------------------------------------------------
void upd3301_device_config::device_config_complete()
{
// inherit a copy of the static data
const upd3301_interface *intf = reinterpret_cast<const upd3301_interface *>(static_config());
if (intf != NULL)
*static_cast<upd3301_interface *>(this) = *intf;
// or initialize to defaults if none provided
else
{
memset(&m_out_int_func, 0, sizeof(m_out_int_func));
memset(&m_out_drq_func, 0, sizeof(m_out_drq_func));
memset(&m_out_hrtc_func, 0, sizeof(m_out_hrtc_func));
memset(&m_out_vrtc_func, 0, sizeof(m_out_vrtc_func));
}
}
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
// set_interrupt -
//-------------------------------------------------
inline void upd3301_device::set_interrupt(int state)
{
if (LOG) logerror("UPD3301 '%s' Interrupt: %u\n", tag(), state);
devcb_call_write_line(&m_out_int_func, state);
if (!state)
{
m_status &= ~(STATUS_N | STATUS_E);
}
}
//-------------------------------------------------
// set_drq -
//-------------------------------------------------
inline void upd3301_device::set_drq(int state)
{
if (LOG) logerror("UPD3301 '%s' DRQ: %u\n", tag(), state);
devcb_call_write_line(&m_out_drq_func, state);
}
//-------------------------------------------------
// set_display -
//-------------------------------------------------
inline void upd3301_device::set_display(int state)
{
if (state)
{
m_status |= STATUS_VE;
}
else
{
m_status &= ~STATUS_VE;
}
}
//-------------------------------------------------
// reset_counters -
//-------------------------------------------------
inline void upd3301_device::reset_counters()
{
set_interrupt(0);
set_drq(0);
}
//-------------------------------------------------
// update_hrtc_timer -
//-------------------------------------------------
inline void upd3301_device::update_hrtc_timer(int state)
{
int y = m_screen->vpos();
int next_x = state ? m_h : 0;
int next_y = state ? y : ((y + 1) % ((m_l + m_v) * m_config.m_width));
attotime duration = m_screen->time_until_pos(next_y, next_x);
m_hrtc_timer->adjust(duration, !state);
}
//-------------------------------------------------
// update_vrtc_timer -
//-------------------------------------------------
inline void upd3301_device::update_vrtc_timer(int state)
{
int next_y = state ? (m_l * m_r) : 0;
attotime duration = m_screen->time_until_pos(next_y, 0);
m_vrtc_timer->adjust(duration, !state);
}
//-------------------------------------------------
// recompute_parameters -
//-------------------------------------------------
inline void upd3301_device::recompute_parameters()
{
int horiz_pix_total = (m_h + m_z) * m_config.m_width;
int vert_pix_total = (m_l + m_v) * m_r;
attoseconds_t refresh = HZ_TO_ATTOSECONDS(clock()) * horiz_pix_total * vert_pix_total;
rectangle visarea;
visarea.min_x = 0;
visarea.min_y = 0;
visarea.max_x = (m_h * m_config.m_width) - 1;
visarea.max_y = (m_l * m_r) - 1;
if (LOG)
{
if (LOG) logerror("UPD3301 '%s' Screen: %u x %u @ %f Hz\n", tag(), horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
if (LOG) logerror("UPD3301 '%s' Visible Area: (%u, %u) - (%u, %u)\n", tag(), visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
}
m_screen->configure(horiz_pix_total, vert_pix_total, visarea, refresh);
update_hrtc_timer(0);
update_vrtc_timer(0);
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// upd3301_device - constructor
//-------------------------------------------------
upd3301_device::upd3301_device(running_machine &_machine, const upd3301_device_config &config)
: device_t(_machine, config),
m_status(0),
m_param_count(0),
m_data_fifo_pos(0),
m_attr_fifo_pos(0),
m_input_fifo(0),
m_h(80),
m_l(20),
m_r(10),
m_v(6),
m_z(32),
m_config(config)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void upd3301_device::device_start()
{
// allocate timers
m_hrtc_timer = timer_alloc(TIMER_HRTC);
m_vrtc_timer = timer_alloc(TIMER_VRTC);
m_drq_timer = timer_alloc(TIMER_DRQ);
// resolve callbacks
devcb_resolve_write_line(&m_out_int_func, &m_config.m_out_int_func, this);
devcb_resolve_write_line(&m_out_drq_func, &m_config.m_out_drq_func, this);
devcb_resolve_write_line(&m_out_hrtc_func, &m_config.m_out_hrtc_func, this);
devcb_resolve_write_line(&m_out_vrtc_func, &m_config.m_out_vrtc_func, this);
// get the screen device
m_screen = m_machine.device<screen_device>(m_config.m_screen_tag);
assert(m_screen != NULL);
// state saving
save_item(NAME(m_y));
save_item(NAME(m_hrtc));
save_item(NAME(m_vrtc));
save_item(NAME(m_mode));
save_item(NAME(m_status));
save_item(NAME(m_param_count));
save_item(NAME(m_data_fifo_pos));
save_item(NAME(m_attr_fifo_pos));
save_item(NAME(m_input_fifo));
save_item(NAME(m_mn));
save_item(NAME(m_me));
save_item(NAME(m_dma_mode));
save_item(NAME(m_h));
save_item(NAME(m_b));
save_item(NAME(m_l));
save_item(NAME(m_s));
save_item(NAME(m_c));
save_item(NAME(m_r));
save_item(NAME(m_v));
save_item(NAME(m_z));
save_item(NAME(m_at1));
save_item(NAME(m_at0));
save_item(NAME(m_sc));
save_item(NAME(m_attr));
save_item(NAME(m_attr_blink));
save_item(NAME(m_attr_frame));
save_item(NAME(m_cm));
save_item(NAME(m_cx));
save_item(NAME(m_cy));
save_item(NAME(m_cursor_blink));
save_item(NAME(m_cursor_frame));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void upd3301_device::device_reset()
{
set_interrupt(0);
set_drq(0);
recompute_parameters();
}
//-------------------------------------------------
// device_clock_changed - handle clock change
//-------------------------------------------------
void upd3301_device::device_clock_changed()
{
recompute_parameters();
}
//-------------------------------------------------
// device_timer - handle timer events
//-------------------------------------------------
void upd3301_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_HRTC:
if (LOG) logerror("UPD3301 '%s' HRTC: %u\n", tag(), param);
devcb_call_write_line(&m_out_hrtc_func, param);
m_hrtc = param;
update_hrtc_timer(param);
break;
case TIMER_VRTC:
if (LOG) logerror("UPD3301 '%s' VRTC: %u\n", tag(), param);
devcb_call_write_line(&m_out_vrtc_func, param);
m_vrtc = param;
if (param && !m_me)
{
m_status |= STATUS_E;
set_interrupt(1);
}
update_vrtc_timer(param);
break;
case TIMER_DRQ:
break;
}
}
//-------------------------------------------------
// read -
//-------------------------------------------------
READ8_MEMBER( upd3301_device::read )
{
UINT8 data = 0;
switch (offset & 0x01)
{
case 0: // data
break;
case 1: // status
data = m_status;
m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U);
break;
}
return data;
}
//-------------------------------------------------
// write -
//-------------------------------------------------
WRITE8_MEMBER( upd3301_device::write )
{
switch (offset & 0x01)
{
case 0: // data
switch (m_mode)
{
case MODE_RESET:
switch (m_param_count)
{
case 0:
m_dma_mode = BIT(data, 7);
m_h = (data & 0x7f) + 2;
if (LOG) logerror("UPD3301 '%s' DMA Mode: %s\n", tag(), m_dma_mode ? "character" : "burst");
if (LOG) logerror("UPD3301 '%s' H: %u\n", tag(), m_h);
break;
case 1:
m_b = ((data >> 6) + 1) * 16;
m_l = (data & 0x3f) + 1;
if (LOG) logerror("UPD3301 '%s' B: %u\n", tag(), m_b);
if (LOG) logerror("UPD3301 '%s' L: %u\n", tag(), m_l);
break;
case 2:
m_s = BIT(data, 7);
m_c = (data >> 4) & 0x03;
m_r = (data & 0x1f) + 1;
if (LOG) logerror("UPD3301 '%s' S: %u\n", tag(), m_s);
if (LOG) logerror("UPD3301 '%s' C: %u\n", tag(), m_c);
if (LOG) logerror("UPD3301 '%s' R: %u\n", tag(), m_r);
break;
case 3:
m_v = (data >> 5) + 1;
m_z = (data & 0x1f) + 2;
if (LOG) logerror("UPD3301 '%s' V: %u\n", tag(), m_v);
if (LOG) logerror("UPD3301 '%s' Z: %u\n", tag(), m_z);
recompute_parameters();
break;
case 4:
m_at1 = BIT(data, 7);
m_at0 = BIT(data, 6);
m_sc = BIT(data, 5);
m_attr = (data & 0x1f) + 1;
if (LOG) logerror("UPD3301 '%s' AT1: %u\n", tag(), m_at1);
if (LOG) logerror("UPD3301 '%s' AT0: %u\n", tag(), m_at0);
if (LOG) logerror("UPD3301 '%s' SC: %u\n", tag(), m_sc);
if (LOG) logerror("UPD3301 '%s' ATTR: %u\n", tag(), m_attr);
m_mode = MODE_NONE;
break;
}
m_param_count++;
break;
case MODE_LOAD_CURSOR_POSITION:
switch (m_param_count)
{
case 0:
m_cx = data & 0x7f;
if (LOG) logerror("UPD3301 '%s' CX: %u\n", tag(), m_cx);
break;
case 1:
m_cy = data & 0x3f;
if (LOG) logerror("UPD3301 '%s' CY: %u\n", tag(), m_cy);
m_mode = MODE_NONE;
break;
}
m_param_count++;
break;
default:
if (LOG) logerror("UPD3301 '%s' Invalid Parameter Byte %02x!\n", tag(), data);
}
break;
case 1: // command
m_mode = MODE_NONE;
m_param_count = 0;
switch (data & 0xe0)
{
case COMMAND_RESET:
if (LOG) logerror("UPD3301 '%s' Reset\n", tag());
m_mode = MODE_RESET;
set_display(0);
set_interrupt(0);
break;
case COMMAND_START_DISPLAY:
if (LOG) logerror("UPD3301 '%s' Start Display\n", tag());
set_display(1);
reset_counters();
break;
case COMMAND_SET_INTERRUPT_MASK:
if (LOG) logerror("UPD3301 '%s' Set Interrupt Mask\n", tag());
m_me = BIT(data, 0);
m_mn = BIT(data, 1);
if (LOG) logerror("UPD3301 '%s' ME: %u\n", tag(), m_me);
if (LOG) logerror("UPD3301 '%s' MN: %u\n", tag(), m_mn);
break;
case COMMAND_READ_LIGHT_PEN:
if (LOG) logerror("UPD3301 '%s' Read Light Pen\n", tag());
m_mode = MODE_READ_LIGHT_PEN;
break;
case COMMAND_LOAD_CURSOR_POSITION:
if (LOG) logerror("UPD3301 '%s' Load Cursor Position\n", tag());
m_mode = MODE_LOAD_CURSOR_POSITION;
m_cm = BIT(data, 0);
if (LOG) logerror("UPD3301 '%s' CM: %u\n", tag(), m_cm);
break;
case COMMAND_RESET_INTERRUPT:
if (LOG) logerror("UPD3301 '%s' Reset Interrupt\n", tag());
set_interrupt(0);
break;
case COMMAND_RESET_COUNTERS:
if (LOG) logerror("UPD3301 '%s' Reset Counters\n", tag());
m_mode = MODE_RESET_COUNTERS;
reset_counters();
break;
}
break;
}
}
//-------------------------------------------------
// dack_w -
//-------------------------------------------------
WRITE8_MEMBER( upd3301_device::dack_w )
{
if (m_y >= (m_l * m_r))
{
return;
}
if (m_data_fifo_pos < m_h)
{
m_data_fifo[m_data_fifo_pos][m_input_fifo] = data;
m_data_fifo_pos++;
}
else
{
m_attr_fifo[m_attr_fifo_pos][m_input_fifo] = data;
m_attr_fifo_pos++;
}
if ((m_data_fifo_pos == m_h) && (m_attr_fifo_pos == (m_attr << 1)))
{
m_input_fifo = !m_input_fifo;
m_data_fifo_pos = 0;
m_attr_fifo_pos = 0;
draw_scanline();
if (m_y == (m_l * m_r))
{
// end DMA transfer
set_drq(0);
}
}
}
//-------------------------------------------------
// lpen_w -
//-------------------------------------------------
WRITE_LINE_MEMBER( upd3301_device::lpen_w )
{
}
//-------------------------------------------------
// hrtc_r -
//-------------------------------------------------
READ_LINE_MEMBER( upd3301_device::hrtc_r )
{
return m_hrtc;
}
//-------------------------------------------------
// vrtc_r -
//-------------------------------------------------
READ_LINE_MEMBER( upd3301_device::vrtc_r )
{
return m_vrtc;
}
//-------------------------------------------------
// draw_scanline -
//-------------------------------------------------
void upd3301_device::draw_scanline()
{
for (int lc = 0; lc < m_r; lc++)
{
for (int sx = 0; sx < m_h; sx++)
{
int y = m_y + lc;
UINT8 cc = m_data_fifo[sx][!m_input_fifo];
int hlgt = 0; // TODO
int rvv = 0; // TODO
int vsp = 0; // TODO
int sl0 = 0; // TODO
int sl12 = 0; // TODO
int csr = m_cm && m_cursor_blink && ((y / m_r) == m_cy) && (sx == m_cx);
int gpa = 0; // TODO
m_config.m_display_func(this, m_bitmap, y, sx, cc, lc, hlgt, rvv, vsp, sl0, sl12, csr, gpa);
}
}
m_y += m_r;
}
//-------------------------------------------------
// update_screen -
//-------------------------------------------------
void upd3301_device::update_screen(bitmap_t *bitmap, const rectangle *cliprect)
{
if (m_status & STATUS_VE)
{
m_y = 0;
m_bitmap = bitmap;
m_data_fifo_pos = 0;
m_attr_fifo_pos = 0;
m_cursor_frame++;
if (m_cursor_frame == m_b)
{
m_cursor_frame = 0;
m_cursor_blink = !m_cursor_blink;
}
m_attr_frame++;
if (m_attr_frame == (m_b << 1))
{
m_attr_frame = 0;
m_attr_blink = !m_attr_blink;
}
// start DMA transfer
set_drq(1);
}
else
{
bitmap_fill(bitmap, cliprect, get_black_pen(m_machine));
}
}

222
src/emu/video/upd3301.h Normal file
View File

@ -0,0 +1,222 @@
/**********************************************************************
NEC uPD3301 Programmable CRT Controller emulation
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************
_____ _____
VRTC 1 |* \_/ | 40 Vcc
RVV 2 | | 39 SL0
CSR 3 | | 38 LC0
LPEN 4 | | 37 LC1
INT 5 | | 36 LC2
DRQ 6 | | 35 LC3
_DACK 7 | | 34 VSP
A0 8 | | 33 SL12
_RD 9 | | 32 GPA
_WR 10 | uPD3301 | 31 HLGT
_CS 11 | | 30 CC7
DB0 12 | | 29 CC6
DB1 13 | | 28 CC5
DB2 14 | | 27 CC4
DB3 15 | | 26 CC3
DB4 16 | | 25 CC2
DB5 17 | | 24 CC1
DB6 18 | | 23 CC0
DB7 19 | | 22 CCLK
GND 20 |_____________| 21 HRTC
**********************************************************************/
#pragma once
#ifndef __UPD3301__
#define __UPD3301__
#include "emu.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
//**************************************************************************
// INTERFACE CONFIGURATION MACROS
//**************************************************************************
#define MCFG_UPD3301_ADD(_tag, _clock, _intrf) \
MCFG_DEVICE_ADD(_tag, UPD3301, _clock) \
MCFG_DEVICE_CONFIG(_intrf)
#define UPD3301_INTERFACE(name) \
const upd3301_interface (name) =
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> upd3301_display_pixels_func
typedef void (*upd3301_display_pixels_func)(device_t *device, bitmap_t *bitmap, int y, int sx, UINT8 cc, UINT8 lc, int hlgt, int rvv, int vsp, int sl0, int sl12, int csr, int gpa);
#define UPD3301_DISPLAY_PIXELS(name) void name(device_t *device, bitmap_t *bitmap, int y, int sx, UINT8 cc, UINT8 lc, int hlgt, int rvv, int vsp, int sl0, int sl12, int csr, int gpa)
// ======================> upd3301_interface
struct upd3301_interface
{
const char *m_screen_tag; // screen we are acting on
int m_width; // char width in pixels
upd3301_display_pixels_func m_display_func;
devcb_write_line m_out_int_func;
devcb_write_line m_out_drq_func;
devcb_write_line m_out_hrtc_func;
devcb_write_line m_out_vrtc_func;
};
// ======================> upd3301_device_config
class upd3301_device_config : public device_config,
public upd3301_interface
{
friend class upd3301_device;
// construction/destruction
upd3301_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock);
public:
// allocators
static device_config *static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock);
virtual device_t *alloc_device(running_machine &machine) const;
protected:
// device_config overrides
virtual void device_config_complete();
};
// ======================> upd3301_device
class upd3301_device : public device_t
{
friend class upd3301_device_config;
// construction/destruction
upd3301_device(running_machine &_machine, const upd3301_device_config &_config);
public:
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
DECLARE_WRITE8_MEMBER( dack_w );
DECLARE_WRITE_LINE_MEMBER( lpen_w );
DECLARE_READ_LINE_MEMBER( hrtc_r );
DECLARE_READ_LINE_MEMBER( vrtc_r );
void update_screen(bitmap_t *bitmap, const rectangle *cliprect);
protected:
// device-level overrides
virtual void device_start();
virtual void device_reset();
virtual void device_clock_changed();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
private:
static const device_timer_id TIMER_HRTC = 0;
static const device_timer_id TIMER_VRTC = 1;
static const device_timer_id TIMER_DRQ = 2;
inline void set_interrupt(int state);
inline void set_drq(int state);
inline void set_display(int state);
inline void reset_counters();
inline void update_hrtc_timer(int state);
inline void update_vrtc_timer(int state);
inline void recompute_parameters();
void draw_scanline();
devcb_resolved_write_line m_out_int_func;
devcb_resolved_write_line m_out_drq_func;
devcb_resolved_write_line m_out_hrtc_func;
devcb_resolved_write_line m_out_vrtc_func;
screen_device *m_screen;
// screen drawing
bitmap_t *m_bitmap; // bitmap
int m_y; // current scanline
int m_hrtc; // horizontal retrace
int m_vrtc; // vertical retrace
// live state
int m_mode; // command mode
UINT8 m_status; // status register
int m_param_count; // parameter count
// FIFOs
UINT8 m_data_fifo[80][2]; // row data FIFO
UINT8 m_attr_fifo[40][2]; // attribute FIFO
int m_data_fifo_pos; // row data FIFO position
int m_attr_fifo_pos; // attribute FIFO position
int m_input_fifo; // which FIFO is in input mode
// interrupts
int m_mn; // disable special character interrupt
int m_me; // disable end of screen interrupt
int m_dma_mode; // DMA mode
// screen geometry
int m_h; // characters per line
int m_b; // cursor blink time
int m_l; // lines per screen
int m_s; // display every other line
int m_c; // cursor mode
int m_r; // lines per character
int m_v; // vertical blanking height
int m_z; // horizontal blanking width
// attributes
int m_at1; //
int m_at0; //
int m_sc; //
int m_attr; // attributes per row
int m_attr_blink; // attribute blink
int m_attr_frame; // attribute blink frame counter
// cursor
int m_cm; // cursor visible
int m_cx; // cursor column
int m_cy; // cursor row
int m_cursor_blink; // cursor blink
int m_cursor_frame; // cursor blink frame counter
// timers
emu_timer *m_hrtc_timer;
emu_timer *m_vrtc_timer;
emu_timer *m_drq_timer;
const upd3301_device_config &m_config;
};
// device type definition
extern const device_type UPD3301;
#endif