mirror of
https://github.com/holub/mame
synced 2025-07-05 18:08:04 +03:00
dp8350: More complete line-by-line emulation of timing outputs (nw)
This commit is contained in:
parent
ceb6c25436
commit
c920ea8593
@ -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);
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user