zx spectrum drivers: Restore irq length == 32 / border timings. (#9711)

This commit is contained in:
holub 2022-05-13 03:21:15 -04:00 committed by GitHub
parent 98b14fa8ae
commit 51a0b70208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 36 deletions

View File

@ -217,8 +217,8 @@ void pentagon_state::machine_reset()
void pentagon_state::video_start()
{
m_frame_invert_count = 16;
m_screen_location = m_ram->pointer() + (5 << 14);
spectrum_128_state::video_start();
m_contention_pattern = {};
}
static const gfx_layout spectrum_charlayout =

View File

@ -167,9 +167,9 @@ resulting mess can be seen in the F4 viewer display.
void spectrum_128_state::video_start()
{
m_frame_invert_count = 16;
spectrum_state::video_start();
m_screen_location = m_ram->pointer() + (5 << 14);
m_contention_pattern = {6, 5, 4, 3, 2, 1, 0, 0};
m_border4t_render_at = 6;
}
uint8_t spectrum_128_state::spectrum_128_pre_opcode_fetch_r(offs_t offset)
@ -230,8 +230,6 @@ void spectrum_128_state::spectrum_128_port_7ffd_w(offs_t offset, uint8_t data)
/* disable paging? */
if (m_port_7ffd_data & 0x20) return;
if ((m_port_7ffd_data ^ data) & 0x08) m_screen->update_now();
/* store new state */
m_port_7ffd_data = data;
@ -293,6 +291,10 @@ void spectrum_128_state::spectrum_128_fetch(address_map &map)
void spectrum_128_state::machine_start()
{
spectrum_state::machine_start();
save_item(NAME(m_port_7ffd_data));
/* rom 0 is 128K rom, rom 1 is 48 BASIC */
memory_region *rom = memregion("maincpu");
m_bank_rom[0]->configure_entries(0, 2, rom->base() + 0x10000, 0x4000);
@ -313,22 +315,19 @@ void spectrum_128_state::machine_reset()
/* set initial ram config */
m_port_7ffd_data = 0;
m_port_1ffd_data = -1;
spectrum_128_update_memory();
}
bool spectrum_128_state::is_vram_write(offs_t offset) {
// TODO respect banks 2,5 mapped to 0xc000
return (BIT(m_port_7ffd_data, 3))
? offset >= 0x8000 && offset < 0x9b00
return (BIT(m_port_7ffd_data, 3) && m_bank_ram[3]->entry() == 7)
? offset >= 0xc000 && offset < 0xdb00
: spectrum_state::is_vram_write(offset);
}
bool spectrum_128_state::is_contended(offs_t offset) {
// Memory banks 1,3,5 and 7 are contended
u8 bank = m_bank_ram[3]->entry();
return spectrum_state::is_contended(offset)
|| ((offset >= 0xc000 && offset <= 0xffff) && (bank && 1));
|| ((offset >= 0xc000 && offset <= 0xffff) && (bank && 1)); // Memory banks 1,3,5 and 7 are contended
}
static const gfx_layout spectrum_charlayout =

View File

@ -266,8 +266,6 @@ void specpls3_state::port_7ffd_w(offs_t offset, uint8_t data)
/* paging disabled? */
if (m_port_7ffd_data & 0x20) return;
if ((m_port_7ffd_data ^ data) & 0x08) m_screen->update_now();
/* store new state */
m_port_7ffd_data = data;
@ -342,6 +340,8 @@ void specpls3_state::machine_start()
{
spectrum_128_state::machine_start();
save_item(NAME(m_port_1ffd_data));
// reconfigure ROMs
memory_region *rom = memregion("maincpu");
m_bank_rom[0]->configure_entries(0, 4, rom->base() + 0x10000, 0x4000);
@ -352,6 +352,7 @@ void specpls3_state::machine_start()
void specpls3_state::machine_reset()
{
/* Initial configuration */
m_port_fe_data = -1;
m_port_7ffd_data = 0;
m_port_1ffd_data = 0;
plus3_update_memory();

View File

@ -727,10 +727,14 @@ void spectrum_state::init_spectrum()
void spectrum_state::machine_start()
{
save_item(NAME(m_port_fe_data));
save_item(NAME(m_int_at));
}
void spectrum_state::machine_reset()
{
/* Initial value/behaviour of FE port is not confirmed. Startup of real devices produce 'random' border
color which need to be investigated. */
m_port_fe_data = -1;
m_port_7ffd_data = -1;
m_port_1ffd_data = -1;
}
@ -756,8 +760,10 @@ void spectrum_state::device_timer(emu_timer &timer, device_timer_id id, int para
switch (id)
{
case TIMER_IRQ_ON:
m_maincpu->set_input_line(0, HOLD_LINE);
timer_set(m_maincpu->clocks_to_attotime(32), TIMER_IRQ_OFF, 0);
m_int_at = m_maincpu->total_cycles();
m_int_at -= m_maincpu->attotime_to_cycles(m_maincpu->local_time() - machine().time());
m_maincpu->set_input_line(0, ASSERT_LINE);
m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
break;
case TIMER_IRQ_OFF:
m_maincpu->set_input_line(0, CLEAR_LINE);
@ -767,13 +773,9 @@ void spectrum_state::device_timer(emu_timer &timer, device_timer_id id, int para
}
}
attotime spectrum_state::time_until_int() {
return m_screen->time_until_pos(0, get_screen_area().left());
};
INTERRUPT_GEN_MEMBER(spectrum_state::spec_interrupt)
{
timer_set(time_until_int(), TIMER_IRQ_ON, 0);
timer_set(m_screen->time_until_pos(0, get_screen_area().left()), TIMER_IRQ_ON);
}
void spectrum_state::spectrum_common(machine_config &config)

View File

@ -115,8 +115,13 @@ protected:
int m_ROMSelection = 0; // FIXME: this is used for various things in derived classes, but not by this base class, and should be removed
std::vector<u8> m_contention_pattern;
/* Pixel offset in 8px chunk (4T) when current chunk is rendered. */
u8 m_border4t_render_at = 0;
/* Defines offset in CPU cycles from screen left side. Early model (48/128/+2) typically use -1, later (+2A/+3) +1 */
s8 m_contention_offset = -1;
u64 m_int_at;
emu_timer *m_irq_off_timer;
uint8_t m_ram_disabled_by_beta;
uint8_t pre_opcode_fetch_r(offs_t offset);
@ -140,7 +145,6 @@ protected:
void spectrum_palette(palette_device &palette) const;
virtual u32 screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
INTERRUPT_GEN_MEMBER(spec_interrupt);
virtual attotime time_until_int();
DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);
DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);

View File

@ -755,7 +755,7 @@ void tsconf_state::update_frame_timer()
INTERRUPT_GEN_MEMBER(tsconf_state::tsconf_vblank_interrupt)
{
update_frame_timer();
m_line_irq_timer->adjust(m_screen->time_until_pos(0));
m_line_irq_timer->adjust(attotime::zero);
}
void tsconf_state::dma_ready(int line)
@ -763,7 +763,7 @@ void tsconf_state::dma_ready(int line)
if (BIT(m_regs[INT_MASK], 4))
{
m_maincpu->set_input_line_and_vector(line, ASSERT_LINE, 0xfb);
timer_set(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))), TIMER_IRQ_OFF, 0);
m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))));
}
}
@ -776,7 +776,7 @@ void tsconf_state::device_timer(emu_timer &timer, device_timer_id id, int param)
if (BIT(m_regs[INT_MASK], 0))
{
m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, 0xff);
timer_set(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))), TIMER_IRQ_OFF, 0);
m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))));
}
break;
}
@ -788,12 +788,12 @@ void tsconf_state::device_timer(emu_timer &timer, device_timer_id id, int param)
{
m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, 0xfd);
// Not quite precise. Scanline can't be skipped.
timer_set(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))), TIMER_IRQ_OFF, 0);
m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))));
}
if (BIT(m_regs[INT_MASK], 0) && OFFS_512(VS_INT_L) == screen_vpos && m_regs[HS_INT] == 0)
{
m_maincpu->set_input_line_and_vector(0, ASSERT_LINE, 0xff);
timer_set(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))), TIMER_IRQ_OFF, 0);
m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 * (1 << (m_regs[SYS_CONFIG] & 0x03))));
}
m_screen->update_now();

View File

@ -87,7 +87,7 @@ void agat7video_device::device_start()
void agat7video_device::device_reset()
{
// XXX to be confirmed
// TODO to be confirmed
m_video_mode = TEXT_LORES;
m_start_address = 0x7800;
}

View File

@ -135,7 +135,7 @@ void agat9video_device::device_start()
void agat9video_device::device_reset()
{
// XXX to be confirmed
// TODO to be confirmed
m_video_mode = TEXT_LORES;
m_start_address = 0x7800;
m_mode = palette_index = 0;
@ -152,7 +152,7 @@ uint8_t agat9video_device::read(offs_t offset)
{
if(!machine().side_effects_disabled())
do_io(offset);
// XXX only 'Moscow' revision
// FIXME only 'Moscow' revision
return m_mode;
}

View File

@ -24,9 +24,13 @@
***************************************************************************/
void spectrum_state::video_start()
{
m_irq_off_timer = timer_alloc(TIMER_IRQ_OFF);
m_frame_invert_count = 16;
m_screen_location = m_video_ram;
m_contention_pattern = {6, 5, 4, 3, 2, 1, 0, 0};
m_contention_offset = -1;
m_border4t_render_at = 2;
}
/***************************************************************************
@ -116,14 +120,17 @@ u32 spectrum_state::screen_update_spectrum(screen_device &screen, bitmap_ind16 &
void spectrum_state::spectrum_update_border(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &border)
{
u8 mod = m_contention_pattern.empty() ? 1 : m_contention_pattern.size();
u8 mod = m_contention_pattern.empty() ? 1 : 8;
u8 at = m_contention_pattern.empty() ? 0 : m_border4t_render_at;
for (auto y = border.top(); y <= border.bottom(); y++)
{
u16 *pix = &(bitmap.pix(y, border.left()));
for (auto x = border.left(); x <= border.right(); )
{
if (x % mod == 0)
if (x % mod == at)
{
pix -= at;
x -= at;
for (auto m = 0; m < mod; m++, x++)
*pix++ = get_border_color(y, x);
}
@ -136,6 +143,14 @@ void spectrum_state::spectrum_update_border(screen_device &screen, bitmap_ind16
}
}
/* ULA reads screen data in 16px (8T) chunks as following:
T: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
px: | 0 | 1 | 2 | 3 |*4*| 5 | 6 | 7 |*0*| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| << !right << | char1 | attr1 | char2 | attr2 | >> right >> |
TODO Curren implementation only tracks char switch position. In order to track both (char and attr) we need to share
some state between screen->update() events.
*/
void spectrum_state::spectrum_update_screen(screen_device &screen_d, bitmap_ind16 &bitmap, const rectangle &screen)
{
u8 *attrs_location = m_screen_location + 0x1800;
@ -144,17 +159,26 @@ void spectrum_state::spectrum_update_screen(screen_device &screen_d, bitmap_ind1
{
u16 hpos = screen.left();
u16 x = hpos - get_screen_area().left();
if (x % 8)
bool chunk_right = x & 8;
if (x % 8 <= (chunk_right ? 0 : 4))
{
u8 shift = x % 8;
x -= shift;
hpos -= shift;
}
else
{
u8 shift = 8 - (x % 8);
x += shift;
hpos += shift;
chunk_right = !chunk_right;
}
u16 y = vpos - get_screen_area().top();
u8 *scr = &m_screen_location[((y & 7) << 8) | ((y & 0x38) << 2) | ((y & 0xc0) << 5) | (x >> 3)];
u8 *attr = &attrs_location[((y & 0xf8) << 2) | (x >> 3)];
u16 *pix = &(bitmap.pix(vpos, hpos));
while (hpos <= screen.right())
while ((hpos + (chunk_right ? 0 : 4)) <= screen.right())
{
u16 ink = ((*attr >> 3) & 0x08) | (*attr & 0x07);
u16 pap = (*attr >> 3) & 0x0f;
@ -164,6 +188,7 @@ void spectrum_state::spectrum_update_screen(screen_device &screen_d, bitmap_ind1
*pix++ = (pix8 & b) ? ink : pap;
scr++;
attr++;
chunk_right = !chunk_right;
}
}
}
@ -182,7 +207,7 @@ void spectrum_state::content_early(s8 shift)
if (m_contention_pattern.empty() || vpos < get_screen_area().top() || vpos > get_screen_area().bottom())
return;
u64 now = m_maincpu->attotime_to_clocks(m_screen->frame_period() - time_until_int()) + shift;
u64 now = m_maincpu->total_cycles() - m_int_at + shift;
u64 cf = vpos * m_screen->width() * m_maincpu->clock() / m_screen->clock() + m_contention_offset;
u64 ct = cf + get_screen_area().width() * m_maincpu->clock() / m_screen->clock();
@ -200,7 +225,7 @@ void spectrum_state::content_late()
if (m_contention_pattern.empty() || vpos < get_screen_area().top() || vpos > get_screen_area().bottom())
return;
u64 now = m_maincpu->attotime_to_clocks(m_screen->frame_period() - time_until_int()) + 1;
u64 now = m_maincpu->total_cycles() - m_int_at + 1;
u64 cf = vpos * m_screen->width() * m_maincpu->clock() / m_screen->clock() + m_contention_offset;
u64 ct = cf + get_screen_area().width() * m_maincpu->clock() / m_screen->clock();
for(auto i = 0x04; i; i >>= 1)

View File

@ -29,6 +29,8 @@ inline void tc2048_state::spectrum_plot_pixel(bitmap_ind16 &bitmap, int x, int y
/* Update FLASH status for ts2068. Assumes flash update every 1/2s. */
void ts2068_state::video_start()
{
m_irq_off_timer = timer_alloc(TIMER_IRQ_OFF);
m_frame_invert_count = 30;
m_screen_location = m_video_ram;
}