dp8350: More complete line-by-line emulation of timing outputs (nw)

This commit is contained in:
AJR 2018-10-01 03:26:33 -04:00
parent ceb6c25436
commit c920ea8593
2 changed files with 246 additions and 100 deletions

View File

@ -5,11 +5,20 @@
National Semiconductor DP8350 Series CRT Controllers National Semiconductor DP8350 Series CRT Controllers
These dedicated CRTC devices are programmable by specifying a These dedicated CRTC devices are programmable by specifying a
long list of mask parameters. The number of parameters adjustable long list of mask parameters. Since the dot clock is generated
through software is relatively small, there being no dedicated internally, these parameters determine the character field size
microprocessor data bus. In practice, however, the CPU's D0 and (16 x 16 maximum) as well as the number of characters displayed
D1 are usually tied to the Register Select lines, due to shared on screen and the timing, width and polarity of the sync pulses
use of the address bus for DMA purposes. (though two different vertical timings are incorporated so the
refresh rate can be switched between 60 Hz and 50 Hz).
The number of parameters adjustable through software control is
relatively small, there being no dedicated microprocessor data bus.
In practice, however, the CPU's D0 and D1 are usually tied to the
Register Select lines, due to shared use of the address bus for
DMA purposes. A per-row interrupt can be derived in various ways
from the timing outputs; this can be used to reload the Row Start
Register to point to display memory at nonconsecutive addresses.
National released the DP8350 and a few other standard versions. National released the DP8350 and a few other standard versions.
DP8367 appears to have originally been a custom mask variant DP8367 appears to have originally been a custom mask variant
@ -17,7 +26,9 @@
later datasheets; its parameters have been derived from the later datasheets; its parameters have been derived from the
information presented in Manual Part No. 13220-91087. (The 8367 information presented in Manual Part No. 13220-91087. (The 8367
designation also comes from there, since the actual IC is merely designation also comes from there, since the actual IC is merely
marked with its HP part number, 1820-2373.) marked with its HP part number, 1820-2373.) DP8369 is another
variant ordered by General Terminal Corp. for their SW10 terminal;
option K is its National designation.
Variant Dot rate Monitor type Variant Dot rate Monitor type
------- -------- ------------ ------- -------- ------------
@ -25,6 +36,7 @@
DP8352 7.02 MHz RS-170 compatible DP8352 7.02 MHz RS-170 compatible
DP8353 17.6256 MHz Motorola M3003 DP8353 17.6256 MHz Motorola M3003
DP8367 25.7715 MHz HP 13220 DP8367 25.7715 MHz HP 13220
DP8369 12.2472 MHz GTC SW10
**********************************************************************/ **********************************************************************/
@ -76,12 +88,27 @@ dp835x_device::dp835x_device(const machine_config &mconfig, device_type type, co
, m_hsync_active(hsync_active) , m_hsync_active(hsync_active)
, m_vsync_active(vsync_active) , m_vsync_active(vsync_active)
, m_vblank_active(vblank_active) , m_vblank_active(vblank_active)
, m_dots_per_line(char_width * chars_per_line)
, m_dots_per_row(char_width * chars_per_row)
, m_video_scan_lines(char_height * rows_per_frame)
, m_lrc_callback(*this)
, m_clc_callback(*this)
, m_lc_callback(*this)
, m_lbre_callback(*this)
, m_hsync_callback(*this) , m_hsync_callback(*this)
, m_vsync_callback(*this) , m_vsync_callback(*this)
, m_vblank_callback(*this) , m_vblank_callback(*this)
, m_60hz_refresh(true) , m_60hz_refresh(true)
, m_cgpi(false)
, m_topr(0)
, m_rsr(0)
, m_cr(0)
, m_row_start(0)
, m_lc(0)
{ {
// some parameters are not used yet // some parameters are not used (at least not directly)
(void)m_rows_per_frame;
(void)m_chars_per_line;
(void)m_cursor_on_all_lines; (void)m_cursor_on_all_lines;
(void)m_lbc_0_width; (void)m_lbc_0_width;
(void)m_hsync_serration; (void)m_hsync_serration;
@ -97,7 +124,7 @@ dp8350_device::dp8350_device(const machine_config &mconfig, const char *tag, dev
7, 10, 80, 24, 7, 10, 80, 24,
4, 10, 20, 4, 10, 20,
30, 10, 72, 30, 10, 72,
100, 0, 4, 1, // HSYNC width given in datasheet as an impossible 43 character times 100, 0, 43, 1, // yes, the horizontal sync pulse is more than twice as long as the blanking period
true, 4, 0, true, 4, 0,
true, false, true) true, false, true)
{ {
@ -129,14 +156,11 @@ void dp835x_device::device_config_complete()
{ {
if (has_screen() && screen().refresh_attoseconds() == 0) if (has_screen() && screen().refresh_attoseconds() == 0)
{ {
int dots_per_line = m_char_width * m_chars_per_line; int lines_per_frame = m_video_scan_lines + m_vblank_interval[m_60hz_refresh ? 1 : 0];
int dots_per_row = m_char_width * m_chars_per_row;
int lines_per_frame = m_char_height * m_rows_per_frame + m_vblank_interval[m_60hz_refresh ? 1 : 0];
if (m_half_shift) if (m_half_shift)
screen().set_raw(clock() * 2, dots_per_line * 2, 0, dots_per_row * 2, lines_per_frame, 0, m_char_height * m_rows_per_frame); screen().set_raw(clock() * 2, m_dots_per_line * 2, 0, m_dots_per_row * 2, lines_per_frame, 0, m_video_scan_lines);
else else
screen().set_raw(clock(), dots_per_line, 0, dots_per_row, lines_per_frame, 0, m_char_height * m_rows_per_frame); screen().set_raw(clock(), m_dots_per_line, 0, m_dots_per_row, lines_per_frame, 0, m_video_scan_lines);
} }
} }
@ -149,6 +173,10 @@ void dp835x_device::device_config_complete()
void dp835x_device::device_resolve_objects() void dp835x_device::device_resolve_objects()
{ {
m_lrc_callback.resolve_safe();
m_clc_callback.resolve_safe();
m_lc_callback.resolve_safe();
m_lbre_callback.resolve_safe();
m_hsync_callback.resolve_safe(); m_hsync_callback.resolve_safe();
m_vsync_callback.resolve_safe(); m_vsync_callback.resolve_safe();
m_vblank_callback.resolve_safe(); m_vblank_callback.resolve_safe();
@ -162,15 +190,19 @@ void dp835x_device::device_resolve_objects()
void dp835x_device::device_start() void dp835x_device::device_start()
{ {
// create timers // create timers
m_hblank_start_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hblank_start), this));
m_hblank_near_end_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hblank_near_end), this));
m_hsync_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this)); m_hsync_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this));
m_hsync_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this)); m_hsync_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::hsync_update), this));
m_vsync_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vsync_update), this));
m_vsync_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vsync_update), this));
m_vblank_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vblank_update), this));
m_vblank_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(dp835x_device::vblank_update), this));
// save state // save state
save_item(NAME(m_60hz_refresh)); save_item(NAME(m_60hz_refresh));
save_item(NAME(m_cgpi));
save_item(NAME(m_lc));
save_item(NAME(m_topr));
save_item(NAME(m_rsr));
save_item(NAME(m_cr));
save_item(NAME(m_row_start));
} }
@ -180,6 +212,10 @@ void dp835x_device::device_start()
void dp835x_device::device_reset() void dp835x_device::device_reset()
{ {
m_topr = 0;
m_rsr = 0;
m_cr = 0;
m_row_start = 0;
} }
@ -200,70 +236,68 @@ void dp835x_device::device_clock_changed()
void dp835x_device::reconfigure_screen() void dp835x_device::reconfigure_screen()
{ {
int dots_per_line = m_char_width * m_chars_per_line; int lines_per_frame = m_video_scan_lines + m_vblank_interval[m_60hz_refresh ? 1 : 0];
int dots_per_row = m_char_width * m_chars_per_row; attotime scan_period = clocks_to_attotime(m_dots_per_line);
int lines_per_frame = m_char_height * m_rows_per_frame + m_vblank_interval[m_60hz_refresh ? 1 : 0]; attotime refresh = clocks_to_attotime(lines_per_frame * m_dots_per_line);
attotime refresh = clocks_to_attotime(lines_per_frame * dots_per_line);
if (m_half_shift) if (m_half_shift)
{ {
rectangle visarea(0, 2 * dots_per_row - 1, 0, m_char_height * m_rows_per_frame - 1); rectangle visarea(0, 2 * m_dots_per_row - 1, 0, m_video_scan_lines - 1);
screen().configure(2 * dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds()); screen().configure(2 * m_dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds());
} }
else else
{ {
rectangle visarea(0, dots_per_row - 1, 0, m_char_height * m_rows_per_frame - 1); rectangle visarea(0, m_dots_per_row - 1, 0, m_video_scan_lines - 1);
screen().configure(dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds()); screen().configure(m_dots_per_line, lines_per_frame, visarea, refresh.as_attoseconds());
} }
logerror("Frame rate refresh: %.2f Hz (f%d); horizontal rate scan: %.4f kHz; character rate: %.4f MHz; dot rate: %.5f MHz\n", logerror("Frame rate refresh: %.2f Hz (f%d); horizontal rate scan: %.4f kHz; character rate: %.4f MHz; dot rate: %.5f MHz\n",
ATTOSECONDS_TO_HZ(refresh.as_attoseconds()), ATTOSECONDS_TO_HZ(refresh.as_attoseconds()),
m_60hz_refresh ? 1 : 0, m_60hz_refresh ? 1 : 0,
clock() / (dots_per_line * 1000.0), clock() / (m_dots_per_line * 1000.0),
clock() / (m_char_width * 1000000.0), clock() / (m_char_width * 1000000.0),
clock() / 1000000.0); clock() / 1000000.0);
// get current screen position // get current screen position (note that this method is more accurate than calling hpos and vpos separately)
int hpos = screen().hpos(); u32 dpos = attotime_to_clocks(refresh - screen().time_until_pos(lines_per_frame - 1, m_dots_per_row * (m_half_shift ? 2 : 1)));
int vpos = screen().vpos(); int hpos = (dpos + m_dots_per_row) % m_dots_per_line;
int vpos = dpos / m_dots_per_line;
// set horizontal sync timers // set line rate clock timers
int hsync_begin = (dots_per_row + m_char_width * m_hsync_delay) * (m_half_shift ? 2 : 1); int hblank_begin = m_dots_per_row;
int hsync_end = hsync_begin + m_char_width * m_hsync_width * (m_half_shift ? 2 : 1); int hblank_near_end = m_dots_per_line - m_char_width * 5;
if (hpos > hsync_begin) if (hpos >= hblank_begin)
hsync_begin += dots_per_line * (m_half_shift ? 2 : 1); hblank_begin += m_dots_per_line;
m_hsync_on_timer->adjust(clocks_to_attotime(hsync_begin - hpos) / (m_half_shift ? 2 : 1), m_hsync_active, clocks_to_attotime(dots_per_line)); m_hblank_start_timer->adjust(clocks_to_attotime(hblank_begin - hpos), 0, scan_period);
if (hpos > hsync_end) if (hpos >= hblank_near_end)
hsync_end += dots_per_line * (m_half_shift ? 2 : 1); hblank_near_end += m_dots_per_line;
m_hsync_off_timer->adjust(clocks_to_attotime(hsync_end - hpos) / (m_half_shift ? 2 : 1), !m_hsync_active, clocks_to_attotime(dots_per_line)); m_hblank_near_end_timer->adjust(clocks_to_attotime(hblank_near_end - hpos), 0, scan_period);
// set horizontal sync timers (note that HSYNC may precede horizontal blanking or even outlast it, as on the DP8350)
int hsync_begin = m_dots_per_row + m_char_width * m_hsync_delay;
int hsync_end = hsync_begin + m_char_width * m_hsync_width;
if (hpos >= hsync_begin)
hsync_begin += m_dots_per_line;
m_hsync_on_timer->adjust(clocks_to_attotime(hsync_begin - hpos), m_hsync_active, scan_period);
if (hpos >= hsync_end)
hsync_end += m_dots_per_line;
else if (hpos < hsync_end - m_dots_per_line)
hsync_end -= m_dots_per_line;
m_hsync_off_timer->adjust(clocks_to_attotime(hsync_end - hpos), !m_hsync_active, scan_period);
logerror("hblank_begin: %d; hsync_begin: %d; hsync_end: %d; hpos: %d\n", hblank_begin, hsync_begin, hsync_end, hpos);
// calculate vertical sync and blanking parameters // calculate vertical sync and blanking parameters
int hblank_begin = dots_per_row * (m_half_shift ? 2 : 1); int vsync_begin = m_video_scan_lines + m_vsync_delay[m_60hz_refresh ? 1 : 0];
int vblank_begin = m_char_height * m_rows_per_frame - 1;
int vsync_begin = vblank_begin + m_vsync_delay[m_60hz_refresh ? 1 : 0];
int vsync_end = vsync_begin + m_vsync_width[m_60hz_refresh ? 1 : 0]; int vsync_end = vsync_begin + m_vsync_width[m_60hz_refresh ? 1 : 0];
int vblank_end = lines_per_frame - m_vblank_stop - 1; int vblank_end = lines_per_frame - m_vblank_stop;
logerror("vblank_begin: %d; vsync_begin: %d; vsync_end: %d; vblank_end: %d; vpos: %d\n", vblank_begin, vsync_begin, vsync_end, vblank_end, vpos); logerror("vblank_begin: %d; vsync_begin: %d; vsync_end: %d; vblank_end: %d; vpos: %d\n", m_video_scan_lines, vsync_begin, vsync_end, vblank_end, vpos);
if (hpos > hblank_begin)
{
hblank_begin += dots_per_line * (m_half_shift ? 2 : 1);
vpos++;
}
attotime until_hblank = clocks_to_attotime(hblank_begin - hpos) / (m_half_shift ? 2 : 1);
// set vertical sync and blanking timers // set counters
if (vpos > vsync_begin) m_line = vpos;
vsync_begin += lines_per_frame; if (m_line >= lines_per_frame - m_char_height)
m_vsync_on_timer->adjust(clocks_to_attotime((vsync_begin - vpos) * dots_per_line) + until_hblank, m_vsync_active, refresh); m_lc = m_line - (lines_per_frame - m_char_height);
if (vpos > vsync_end) else
vsync_end += lines_per_frame; m_lc = m_line % m_char_height;
m_vsync_off_timer->adjust(clocks_to_attotime((vsync_end - vpos) * dots_per_line) + until_hblank, !m_vsync_active, refresh);
if (vpos > vblank_begin)
vblank_begin += lines_per_frame;
m_vblank_on_timer->adjust(clocks_to_attotime((vblank_begin - vpos) * dots_per_line) + until_hblank, m_vblank_active, refresh);
if (vpos > vblank_end)
vblank_end += lines_per_frame;
m_vblank_off_timer->adjust(clocks_to_attotime((vblank_end - vpos) * dots_per_line) + until_hblank, !m_vblank_active, refresh);
} }
@ -283,6 +317,21 @@ WRITE_LINE_MEMBER(dp835x_device::refresh_control)
} }
//-------------------------------------------------
// character_generator_program - set or configure
// the character generator program input (CGPI):
// 0 = video begins on scan line 0; new addresses
// loaded on last scan line of previous character
// 1 = video begins on scan line 1; new addresses
// loaded on scan line 0
//-------------------------------------------------
WRITE_LINE_MEMBER(dp835x_device::character_generator_program)
{
m_cgpi = bool(state);
}
//------------------------------------------------- //-------------------------------------------------
// register_load - write to one of three CRTC // register_load - write to one of three CRTC
// address registers // address registers
@ -302,21 +351,45 @@ void dp835x_device::register_load(u8 rs, u16 addr)
case 1: case 1:
// A = 0, B = 1: Top-of-Page // A = 0, B = 1: Top-of-Page
logerror("Top-of-Page Register = %03X\n", addr); logerror("Top-of-Page Register = %03X\n", addr);
m_topr = addr;
break; break;
case 2: case 2:
// A = 1, B = 0: Row Start (also Top-of-Page during vertical blanking) // A = 1, B = 0: Row Start (also Top-of-Page during vertical blanking)
if (m_line >= m_video_scan_lines)
{
logerror("Row Start Register = %03X (redirected to Top-of-Page)\n", addr);
m_topr = addr;
}
else
{
logerror("Row Start Register = %03X\n", addr); logerror("Row Start Register = %03X\n", addr);
m_rsr = addr;
}
break; break;
case 3: case 3:
// A = 1, B = 1: Cursor // A = 1, B = 1: Cursor
logerror("Cursor Register = %03X\n", addr); logerror("Cursor Register = %03X\n", addr);
m_cr = addr;
break; break;
} }
} }
//-------------------------------------------------
// lrc_r - report line rate clock state
//-------------------------------------------------
READ_LINE_MEMBER(dp835x_device::lrc_r)
{
if (m_hblank_start_timer->remaining() > m_hblank_near_end_timer->remaining())
return 0;
else
return 1;
}
//------------------------------------------------- //-------------------------------------------------
// hsync_r - report horizontal sync state // hsync_r - report horizontal sync state
//------------------------------------------------- //-------------------------------------------------
@ -331,15 +404,14 @@ READ_LINE_MEMBER(dp835x_device::hsync_r)
//------------------------------------------------- //-------------------------------------------------
// vblank_r - report vertical sync state // vsync_r - report vertical sync state
//------------------------------------------------- //-------------------------------------------------
READ_LINE_MEMBER(dp835x_device::vsync_r) READ_LINE_MEMBER(dp835x_device::vsync_r)
{ {
if (m_vsync_on_timer->remaining() > m_vsync_off_timer->remaining()) int vsync_begin = m_video_scan_lines + m_vsync_delay[m_60hz_refresh ? 1 : 0];
return m_vsync_active; int vsync_end = vsync_begin + m_vsync_width[m_60hz_refresh ? 1 : 0];
else return (m_line >= vsync_begin && m_line < vsync_end) ? m_vsync_active : !m_vsync_active;
return !m_vsync_active;
} }
@ -349,10 +421,83 @@ READ_LINE_MEMBER(dp835x_device::vsync_r)
READ_LINE_MEMBER(dp835x_device::vblank_r) READ_LINE_MEMBER(dp835x_device::vblank_r)
{ {
if (m_vblank_on_timer->remaining() > m_vblank_off_timer->remaining()) int vblank_end = m_video_scan_lines + m_vblank_interval[m_60hz_refresh ? 1 : 0] - m_vblank_stop;
return m_vblank_active; return (m_line >= m_video_scan_lines && m_line < vblank_end) ? m_vblank_active : !m_vblank_active;
}
//-------------------------------------------------
// hblank_start - update timing outputs at the
// start of horizontal blanking
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(dp835x_device::hblank_start)
{
int lines_per_frame = m_video_scan_lines + m_vblank_interval[m_60hz_refresh ? 1 : 0];
int vsync_begin = m_video_scan_lines + m_vsync_delay[m_60hz_refresh ? 1 : 0];
int vsync_end = vsync_begin + m_vsync_width[m_60hz_refresh ? 1 : 0];
int vblank_end = lines_per_frame - m_vblank_stop;
// falling edge of line rate clock
m_lrc_callback(0);
// increment line counter or reset it
if (m_lc < m_char_height - 1 && m_line != lines_per_frame - m_char_height)
m_lc++;
else else
return !m_vblank_active; {
m_lc = 0;
m_clc_callback(0);
}
m_lc_callback(m_lc);
// increment internal 9-bit scan counter
m_line++;
//assert(m_line == screen().vpos() + 1);
// update line buffer recirculate enable output based on address mode
bool lbre = m_lc != (m_cgpi ? 0 : m_char_height - 1);
m_lbre_callback(lbre ? 1 : 0);
if (m_line >= lines_per_frame - m_char_height && m_line < lines_per_frame)
{
m_row_start = m_rsr = m_topr;
}
else if (!lbre || m_line >= m_video_scan_lines)
{
// calculate starting address of next row (address counter runs continuously during VBLANK)
m_row_start = m_rsr;
m_rsr = (m_row_start + m_chars_per_row) & 0xfff;
}
// update vertical blanking output
if (m_line == m_video_scan_lines)
m_vblank_callback(m_vblank_active);
else if (m_line == vblank_end)
m_vblank_callback(!m_vblank_active);
// update vertical sync output
if (m_line == vsync_begin)
m_vsync_callback(m_vsync_active);
else if (m_line == vsync_end)
m_vsync_callback(!m_vsync_active);
if (m_line == lines_per_frame)
m_line = 0;
}
//-------------------------------------------------
// hblank_near_end - update line rate outputs
// five chars before horizontal blanking ends
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(dp835x_device::hblank_near_end)
{
// rising edge of line rate clock
m_lrc_callback(1);
if (m_lc == 0)
m_clc_callback(1);
} }
@ -365,25 +510,3 @@ TIMER_CALLBACK_MEMBER(dp835x_device::hsync_update)
{ {
m_hsync_callback(param); m_hsync_callback(param);
} }
//-------------------------------------------------
// vsync_update - update state of vertical
// sync output
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(dp835x_device::vsync_update)
{
m_vsync_callback(param);
}
//-------------------------------------------------
// vblank_update - update state of vertical
// blanking output
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(dp835x_device::vblank_update)
{
m_vblank_callback(param);
}

View File

@ -44,6 +44,10 @@ class dp835x_device : public device_t, public device_video_interface
{ {
public: public:
// device configuration // device configuration
auto lrc_callback() { return m_lrc_callback.bind(); }
auto clc_callback() { return m_clc_callback.bind(); }
auto lc_callback() { return m_lc_callback.bind(); }
auto lbre_callback() { return m_lbre_callback.bind(); }
auto hsync_callback() { return m_hsync_callback.bind(); } auto hsync_callback() { return m_hsync_callback.bind(); }
auto vsync_callback() { return m_vsync_callback.bind(); } auto vsync_callback() { return m_vsync_callback.bind(); }
auto vblank_callback() { return m_vblank_callback.bind(); } auto vblank_callback() { return m_vblank_callback.bind(); }
@ -51,9 +55,12 @@ public:
// write handlers // write handlers
DECLARE_WRITE_LINE_MEMBER(refresh_control); DECLARE_WRITE_LINE_MEMBER(refresh_control);
DECLARE_WRITE_LINE_MEMBER(character_generator_program);
void register_load(u8 rs, u16 addr); void register_load(u8 rs, u16 addr);
// read handlers // read handlers
DECLARE_READ_LINE_MEMBER(lrc_r);
u8 lc_r() { return m_lc; }
DECLARE_READ_LINE_MEMBER(hsync_r); DECLARE_READ_LINE_MEMBER(hsync_r);
DECLARE_READ_LINE_MEMBER(vsync_r); DECLARE_READ_LINE_MEMBER(vsync_r);
DECLARE_READ_LINE_MEMBER(vblank_r); DECLARE_READ_LINE_MEMBER(vblank_r);
@ -80,9 +87,9 @@ private:
void reconfigure_screen(); void reconfigure_screen();
// timer callbacks // timer callbacks
TIMER_CALLBACK_MEMBER(hblank_start);
TIMER_CALLBACK_MEMBER(hblank_near_end);
TIMER_CALLBACK_MEMBER(hsync_update); TIMER_CALLBACK_MEMBER(hsync_update);
TIMER_CALLBACK_MEMBER(vsync_update);
TIMER_CALLBACK_MEMBER(vblank_update);
// mask parameters // mask parameters
const int m_char_width; // character field cell size (width in dots) const int m_char_width; // character field cell size (width in dots)
@ -103,24 +110,40 @@ private:
const bool m_vsync_active; // active level of vertical sync pulse const bool m_vsync_active; // active level of vertical sync pulse
const bool m_vblank_active; // active level of vertical blanking pulse const bool m_vblank_active; // active level of vertical blanking pulse
// derived parameters
const int m_dots_per_line; // number of dots per scan line
const int m_dots_per_row; // number of dots displayed in each scan line
const int m_video_scan_lines; // number of active scan lines between VBLANK periods
// misc. configuration // misc. configuration
bool m_half_shift; // adjust screen parameters to allow half-dot shifting bool m_half_shift; // adjust screen parameters to allow half-dot shifting
// device callbacks // device callbacks
devcb_write_line m_lrc_callback; // line rate clock output (active high)
devcb_write_line m_clc_callback; // clear line counter output (active low during blanking)
devcb_write8 m_lc_callback; // line counter output
devcb_write_line m_lbre_callback; // line buffer recirculate enable output (active high)
devcb_write_line m_hsync_callback; // horizontal sync output (polarity may vary by type) devcb_write_line m_hsync_callback; // horizontal sync output (polarity may vary by type)
devcb_write_line m_vsync_callback; // vertical sync output (polarity may vary by type) devcb_write_line m_vsync_callback; // vertical sync output (polarity may vary by type)
devcb_write_line m_vblank_callback; // vertical blanking output (polarity may vary by type) devcb_write_line m_vblank_callback; // vertical blanking output (polarity may vary by type)
// internal registers and control parameters // internal registers and control parameters
bool m_60hz_refresh; // refresh rate selector (true = f1, false = f0) bool m_60hz_refresh; // refresh rate selector (true = f1, false = f0)
bool m_cgpi; // character generator program/address mode input
u16 m_topr; // top-of-page register
u16 m_rsr; // row start register (to be loaded during next row)
u16 m_cr; // cursor register
u16 m_row_start; // start of current row
u16 m_line; // current scan line (starting at HBLANK)
// output state
u8 m_lc; // 4-bit line counter
// timers // timers
emu_timer *m_hblank_start_timer;
emu_timer *m_hblank_near_end_timer;
emu_timer *m_hsync_on_timer; emu_timer *m_hsync_on_timer;
emu_timer *m_hsync_off_timer; emu_timer *m_hsync_off_timer;
emu_timer *m_vsync_on_timer;
emu_timer *m_vsync_off_timer;
emu_timer *m_vblank_on_timer;
emu_timer *m_vblank_off_timer;
}; };
// ======================> dp8350_device // ======================> dp8350_device