diff --git a/.gitattributes b/.gitattributes index 8eb91406cad..b7a6d6ced97 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/src/emu/emu.mak b/src/emu/emu.mak index ac0cb44164e..5619a125470 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -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 \ diff --git a/src/emu/video/upd3301.c b/src/emu/video/upd3301.c new file mode 100644 index 00000000000..00c7cea5baa --- /dev/null +++ b/src/emu/video/upd3301.c @@ -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(static_config()); + if (intf != NULL) + *static_cast(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(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)); + } +} diff --git a/src/emu/video/upd3301.h b/src/emu/video/upd3301.h new file mode 100644 index 00000000000..742a0c2777c --- /dev/null +++ b/src/emu/video/upd3301.h @@ -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