From b0f6984405e0522585bf7fe73ef94e655527b053 Mon Sep 17 00:00:00 2001 From: Andrei Holub Date: Sun, 10 Aug 2025 14:38:16 -0400 Subject: [PATCH 1/2] sinclair/spectrum_ula.cpp: Moved ULA contention related code to separate helper device. Added preliminary ULA early/late timings configuration --- src/mame/sinclair/atm.cpp | 10 +- src/mame/sinclair/byte.cpp | 6 +- src/mame/sinclair/chloe.cpp | 22 +-- src/mame/sinclair/elwro800.cpp | 2 + src/mame/sinclair/pentagon.cpp | 8 +- src/mame/sinclair/scorpion.cpp | 2 +- src/mame/sinclair/spec128.cpp | 30 ++-- src/mame/sinclair/spec128.h | 1 - src/mame/sinclair/specnext.cpp | 46 +++--- src/mame/sinclair/specpls3.cpp | 39 ++--- src/mame/sinclair/specpls3.h | 1 - src/mame/sinclair/spectrum.cpp | 63 ++++---- src/mame/sinclair/spectrum.h | 16 +- src/mame/sinclair/spectrum_ula.cpp | 232 +++++++++++++++++++++++++++++ src/mame/sinclair/spectrum_ula.h | 142 ++++++++++++++++++ src/mame/sinclair/spectrum_v.cpp | 84 ++--------- src/mame/sinclair/sprinter.cpp | 2 +- src/mame/sinclair/tsconf.cpp | 3 +- 18 files changed, 499 insertions(+), 210 deletions(-) create mode 100644 src/mame/sinclair/spectrum_ula.cpp create mode 100644 src/mame/sinclair/spectrum_ula.h diff --git a/src/mame/sinclair/atm.cpp b/src/mame/sinclair/atm.cpp index f69653f3237f8..eedf340f21ae8 100644 --- a/src/mame/sinclair/atm.cpp +++ b/src/mame/sinclair/atm.cpp @@ -453,7 +453,6 @@ void atm_state::video_start() m_screen_location = m_ram->pointer() + (5 << 14); m_char_location = m_char_rom; subdevice("gfxdecode")->gfx(0)->set_source(m_char_location); - m_contention_pattern = {}; } /* F4 Character Displayer */ @@ -508,6 +507,7 @@ void atm_state::atm(machine_config &config) m_screen->set_raw(X1_128_SINCLAIR / 5, 448, 312, {get_screen_area().left() - 40, get_screen_area().right() + 40, get_screen_area().top() - 40, get_screen_area().bottom() + 40}); subdevice("gfxdecode")->set_info(gfx_atm); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); BETA_DISK(config, m_beta, 0); ATA_INTERFACE(config, m_ata).options(atm_ata_devices, nullptr, nullptr, false); @@ -596,7 +596,7 @@ ROM_START( atmtb2plus ) ROM_LOAD( "sgen.rom", 0x0000, 0x0800, CRC(1f4387d6) SHA1(93b3774dc8a486643a1bdd48c606b0c84fa0e22b)) ROM_END -/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ -COMP( 1991, atm, spec128, 0, atm, spec_plus, atm_state, empty_init, "MicroART", "ATM-Turbo (ATM-CP)", MACHINE_NOT_WORKING) -COMP( 1992, atmtb2, spec128, 0, atmtb2, spec_plus, atm_state, empty_init, "MicroART", "ATM-Turbo 2", MACHINE_SUPPORTS_SAVE) -COMP( 1993, atmtb2plus, spec128, 0, atmtb2plus, spec_plus, atm_state, empty_init, "MicroART", "ATM-Turbo 2+", MACHINE_SUPPORTS_SAVE) +/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ +COMP( 1991, atm, spec128, 0, atm, spec_plus2a, atm_state, empty_init, "MicroART", "ATM-Turbo (ATM-CP)", MACHINE_NOT_WORKING) +COMP( 1992, atmtb2, spec128, 0, atmtb2, spec_plus2a, atm_state, empty_init, "MicroART", "ATM-Turbo 2", MACHINE_SUPPORTS_SAVE) +COMP( 1993, atmtb2plus, spec128, 0, atmtb2plus, spec_plus2a, atm_state, empty_init, "MicroART", "ATM-Turbo 2+", MACHINE_SUPPORTS_SAVE) diff --git a/src/mame/sinclair/byte.cpp b/src/mame/sinclair/byte.cpp index f5a4caf4a067c..4c18d42e68b5a 100644 --- a/src/mame/sinclair/byte.cpp +++ b/src/mame/sinclair/byte.cpp @@ -85,11 +85,7 @@ void byte_state::map_io(address_map &map) u8 byte_state::kbd_fe_r(offs_t offset) { - if (!machine().side_effects_disabled()) - { - if (is_contended(offset)) content_early(); - content_early(1); - } + m_ula->ula_r(offset); u8 lines = offset >> 8; u8 data = 0xff; diff --git a/src/mame/sinclair/chloe.cpp b/src/mame/sinclair/chloe.cpp index cb750d04ad12a..7e11949f8a864 100644 --- a/src/mame/sinclair/chloe.cpp +++ b/src/mame/sinclair/chloe.cpp @@ -50,7 +50,7 @@ class chloe_state : public spectrum_128_state , m_bank1_view(*this, "bank1_view") , m_regs_map(*this, "regs_map") , m_palette(*this, "palette") - , m_ula(*this, "ula") + , m_ula_scr(*this, "ula_scr") , m_sdcard(*this, "sdcard") , m_io_line(*this, "IO_LINE%u", 0U) , m_io_mouse(*this, "mouse_input%u", 1U) @@ -103,7 +103,7 @@ class chloe_state : public spectrum_128_state memory_view m_bank0_view, m_bank1_view; required_device m_regs_map; required_device m_palette; - required_device m_ula; + required_device m_ula_scr; required_device m_sdcard; required_ioport_array<8> m_io_line; required_ioport_array<3> m_io_mouse; @@ -139,7 +139,7 @@ class chloe_state : public spectrum_128_state void chloe_state::update_memory() { m_screen->update_now(); - m_ula->ula_shadow_en_w(BIT(m_port_7ffd_data, 3)); + m_ula_scr->ula_shadow_en_w(BIT(m_port_7ffd_data, 3)); const bool ext = BIT(m_port_ff_data, 7); // 0 - DOC 7xxxx=28+; 1 - EXT 6xxxx=24+ m_bank0_view.disable(); @@ -225,10 +225,10 @@ u32 chloe_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, cons clip256x192 &= cliprect; screen.priority().fill(0, cliprect); - m_ula->draw_border(bitmap, cliprect, m_port_fe_data & 0x07); + m_ula_scr->draw_border(bitmap, cliprect, m_port_fe_data & 0x07); const bool flash = u64(screen.frame_number() / m_frame_invert_count) & 1; - m_ula->draw(screen, bitmap, clip256x192, flash, 0); + m_ula_scr->draw(screen, bitmap, clip256x192, flash, 0); return 0; } @@ -246,7 +246,7 @@ void chloe_state::port_7ffd_w(u8 data) void chloe_state::port_ff_w(u8 data) { - m_ula->port_ff_reg_w(data); + m_ula_scr->port_ff_reg_w(data); m_port_ff_data = data; update_memory(); @@ -525,7 +525,7 @@ void chloe_state::map_io(address_map &map) } else if ((m_palpen_selected & 0xc0) == 0x40) { - m_ula->ulap_en_w(data & 1); + m_ula_scr->ulap_en_w(data & 1); } })); map(0xfc3b, 0xfc3b).lrw8(NAME([this]() { return m_reg_selected; }) @@ -889,10 +889,9 @@ GFXDECODE_END void chloe_state::video_start() { spectrum_128_state::video_start(); - m_contention_pattern = {}; // Has no contention const u8 *ram = m_ram->pointer(); - m_ula->set_host_ram_ptr(ram); + m_ula_scr->set_host_ram_ptr(ram); } @@ -931,9 +930,10 @@ void chloe_state::chloe(machine_config &config) m_screen->set_raw(25.175_MHz_XTAL, CYCLES_HORIZ, CYCLES_VERT, SCR_FULL); // VGA m_screen->set_screen_update(FUNC(chloe_state::screen_update)); m_screen->set_no_palette(); - PALETTE(config, m_palette, FUNC(chloe_state::spectrum_palette), 256); - SCREEN_ULA_PLUS(config, m_ula, 0).set_raster_offset(SCR_256x192.left(), SCR_256x192.top()).set_palette(m_palette->device().tag(), 0x000, 0x000); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); + + SCREEN_ULA_PLUS(config, m_ula_scr, 0).set_raster_offset(SCR_256x192.left(), SCR_256x192.top()).set_palette(m_palette->device().tag(), 0x000, 0x000); SPEAKER(config.replace(), "speakers", 2).front(); diff --git a/src/mame/sinclair/elwro800.cpp b/src/mame/sinclair/elwro800.cpp index 5dfbe80a3464c..2f4588758607c 100644 --- a/src/mame/sinclair/elwro800.cpp +++ b/src/mame/sinclair/elwro800.cpp @@ -567,6 +567,8 @@ void elwro800_state::elwro800(machine_config &config) screen.set_palette("palette"); screen.screen_vblank().set_inputline(m_maincpu, 0, HOLD_LINE); + SPECTRUM_ULA_UNCONTENDED(config, m_ula); // dummy for required + PALETTE(config, "palette", FUNC(elwro800_state::spectrum_palette), 16); GFXDECODE(config, "gfxdecode", "palette", gfx_elwro800); diff --git a/src/mame/sinclair/pentagon.cpp b/src/mame/sinclair/pentagon.cpp index 7167446df4491..54501adef906c 100644 --- a/src/mame/sinclair/pentagon.cpp +++ b/src/mame/sinclair/pentagon.cpp @@ -167,7 +167,6 @@ void pentagon_state::machine_reset() void pentagon_state::video_start() { spectrum_128_state::video_start(); - m_contention_pattern = {}; } static const gfx_layout spectrum_charlayout = @@ -200,6 +199,7 @@ void pentagon_state::pentagon(machine_config &config) m_screen->set_raw(14_MHz_XTAL / 2, 448, 320, {get_screen_area().left() - 48, get_screen_area().right() + 48, get_screen_area().top() - 48, get_screen_area().bottom() + 48}); subdevice("gfxdecode")->set_info(gfx_pentagon); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); BETA_DISK(config, m_beta, 0); @@ -329,6 +329,6 @@ ROM_END } // Anonymous namespace -// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS -COMP( 1991, pentagon, spec128, 0, pentagon, spec_plus, pentagon_state, empty_init, "Vladimir Drozdov", "Pentagon 128K", 0 ) -COMP( 2005, pent1024, spec128, 0, pent1024, spec_plus, pent1024_state, empty_init, "Alex Zhabin", "Pentagon 1024SL", 0 ) +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +COMP( 1991, pentagon, spec128, 0, pentagon, spec_plus2a, pentagon_state, empty_init, "Vladimir Drozdov", "Pentagon 128K", 0 ) +COMP( 2005, pent1024, spec128, 0, pent1024, spec_plus2a, pent1024_state, empty_init, "Alex Zhabin", "Pentagon 1024SL", 0 ) diff --git a/src/mame/sinclair/scorpion.cpp b/src/mame/sinclair/scorpion.cpp index 7c0fd92086848..a6d9f27ddd815 100644 --- a/src/mame/sinclair/scorpion.cpp +++ b/src/mame/sinclair/scorpion.cpp @@ -400,7 +400,6 @@ void scorpion_state::video_start() { spectrum_state::video_start(); m_screen_location = m_ram->pointer() + (5 << 14); - m_contention_pattern = {}; } /* F4 Character Displayer */ @@ -489,6 +488,7 @@ void scorpion_state::scorpion(machine_config &config) m_maincpu->nomreq_cb().remove(); subdevice("gfxdecode")->set_info(gfx_scorpion); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); SPEAKER(config.replace(), "speakers", 2).front(); diff --git a/src/mame/sinclair/spec128.cpp b/src/mame/sinclair/spec128.cpp index 08f429a011378..91bfb3ec3d61d 100644 --- a/src/mame/sinclair/spec128.cpp +++ b/src/mame/sinclair/spec128.cpp @@ -169,13 +169,11 @@ void spectrum_128_state::video_start() { spectrum_state::video_start(); m_screen_location = m_ram->pointer() + (5 << 14); - m_border4t_render_at = 3; } uint8_t spectrum_128_state::spectrum_128_pre_opcode_fetch_r(offs_t offset) { - m_is_m1_rd_contended = false; - if (!machine().side_effects_disabled() && is_contended(offset)) content_early(); + m_ula->m1(offset); /* this allows expansion devices to act upon opcode fetches from MEM addresses for example, interface1 detection fetches requires fetches at 0008 / 0708 to @@ -202,7 +200,7 @@ u8 spectrum_128_state::spectrum_128_rom_r(offs_t offset) template void spectrum_128_state::spectrum_128_ram_w(offs_t offset, u8 data) { u16 addr = 0x4000 * Bank + offset; - if (is_contended(addr)) content_early(); + m_ula->data_w(addr); if (is_vram_write(addr)) m_screen->update_now(); ((u8*)m_bank_ram[Bank]->base())[offset] = data; @@ -214,7 +212,7 @@ template void spectrum_128_state::spectrum_128_ram_w<0>(offs_t offset, u8 data); template u8 spectrum_128_state::spectrum_128_ram_r(offs_t offset) { u16 addr = 0x4000 * Bank + offset; - if (!machine().side_effects_disabled() && is_contended(addr)) content_early(); + m_ula->data_r(addr); return ((u8*)m_bank_ram[Bank]->base())[offset]; } @@ -225,8 +223,7 @@ template u8 spectrum_128_state::spectrum_128_ram_r(offs_t offset) // D5: Disable paging void spectrum_128_state::spectrum_128_port_7ffd_w(offs_t offset, uint8_t data) { - if (is_contended(offset)) content_early(); - content_early(1); + m_ula->ula_w(offset); // disable paging? if (m_port_7ffd_data & 0x20) return; @@ -245,6 +242,7 @@ void spectrum_128_state::spectrum_128_update_memory() m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4)); // select ram at 0x0c000-0x0ffff m_bank_ram[3]->set_entry(m_port_7ffd_data & 0x07); + m_ula->bank3_pg_w(m_port_7ffd_data & 0x07); m_screen->update_now(); if (BIT(m_port_7ffd_data, 3)) @@ -255,11 +253,7 @@ void spectrum_128_state::spectrum_128_update_memory() uint8_t spectrum_128_state::spectrum_port_r(offs_t offset) { - if (!machine().side_effects_disabled() && is_contended(offset)) - { - content_early(); - content_late(); - } + m_ula->io_r(offset); // Pass through to expansion device if present if (m_exp->get_card_device()) @@ -381,12 +375,6 @@ bool spectrum_128_state::is_vram_write(offs_t offset) { : spectrum_state::is_vram_write(offset); } -bool spectrum_128_state::is_contended(offs_t offset) { - u8 pg = m_bank_ram[3]->entry(); - return spectrum_state::is_contended(offset) - || ((offset >= 0xc000 && offset <= 0xffff) && (pg & 1)); // Memory pages 1,3,5 and 7 are contended -} - u8 *spectrum_128_state::snow_pattern1_base(u8 i_reg) { const bool is_alt_scr_selected = BIT(m_port_7ffd_data, 3); @@ -427,7 +415,7 @@ void spectrum_128_state::spectrum_128(machine_config &config) m_maincpu->set_m1_map(&spectrum_128_state::spectrum_128_fetch); m_maincpu->set_vblank_int("screen", FUNC(spectrum_128_state::spec_interrupt)); m_maincpu->refresh_cb().set(FUNC(spectrum_128_state::spectrum_refresh_w)); - m_maincpu->nomreq_cb().set(FUNC(spectrum_128_state::spectrum_nomreq)); + m_maincpu->nomreq_cb().set("ula", FUNC(spectrum_ula_device::nomem_rq)); m_maincpu->busack_cb().set("dma", FUNC(dma_slot_device::bai_w)); config.set_maximum_quantum(attotime::from_hz(60)); @@ -439,6 +427,10 @@ void spectrum_128_state::spectrum_128(machine_config &config) subdevice("gfxdecode")->set_info(spec128); + SPECTRUM_ULA_128K(config.replace(), m_ula); + m_ula->set_z80(m_maincpu); + m_ula->set_screen(m_screen, get_screen_area()); + // sound hardware AY_SLOT(config, "ay_slot", X1_128_SINCLAIR / 20, default_ay_slot_devices, "ay_ay8912") .add_route(ALL_OUTPUTS, "speakers", 0.25); diff --git a/src/mame/sinclair/spec128.h b/src/mame/sinclair/spec128.h index e3532b22cad3d..8b30092ffd80d 100644 --- a/src/mame/sinclair/spec128.h +++ b/src/mame/sinclair/spec128.h @@ -36,7 +36,6 @@ class spectrum_128_state : public spectrum_state virtual void spectrum_128_update_memory() override; virtual rectangle get_screen_area() override; - virtual bool is_contended(offs_t offset) override; virtual bool is_vram_write(offs_t offset) override; virtual u8 *snow_pattern1_base(u8 i_reg) override; diff --git a/src/mame/sinclair/specnext.cpp b/src/mame/sinclair/specnext.cpp index 594736f71601e..e6727020380d5 100644 --- a/src/mame/sinclair/specnext.cpp +++ b/src/mame/sinclair/specnext.cpp @@ -89,7 +89,7 @@ class specnext_state : public spectrum_128_state , m_regs_map(*this, "regs_map") , m_mf(*this, "multiface") , m_divmmc(*this, "divmmc") - , m_ula(*this, "ula") + , m_ula_scr(*this, "ula_scr") , m_tiles(*this, "tiles") , m_layer2(*this, "layer2") , m_lores(*this, "lores") @@ -189,25 +189,25 @@ class specnext_state : public spectrum_128_state void nr_19_sprite_clip_x2_w(u8 data) { m_nr_19_sprite_clip_x2 = data; m_sprites->clip_x2_w(m_nr_19_sprite_clip_x2); } void nr_19_sprite_clip_y1_w(u8 data) { m_nr_19_sprite_clip_y1 = data; m_sprites->clip_y1_w(m_nr_19_sprite_clip_y1); } void nr_19_sprite_clip_y2_w(u8 data) { m_nr_19_sprite_clip_y2 = data; m_sprites->clip_y2_w(m_nr_19_sprite_clip_y2); } - void nr_1a_ula_clip_x1_w(u8 data) { m_nr_1a_ula_clip_x1 = data; m_ula->ula_clip_x1_w(m_nr_1a_ula_clip_x1); m_lores->clip_x1_w(m_nr_1a_ula_clip_x1); } - void nr_1a_ula_clip_x2_w(u8 data) { m_nr_1a_ula_clip_x2 = data; m_ula->ula_clip_x2_w(m_nr_1a_ula_clip_x2); m_lores->clip_x2_w(m_nr_1a_ula_clip_x2); } - void nr_1a_ula_clip_y1_w(u8 data) { m_nr_1a_ula_clip_y1 = data; m_ula->ula_clip_y1_w(m_nr_1a_ula_clip_y1); m_lores->clip_y1_w(m_nr_1a_ula_clip_y1); } + void nr_1a_ula_clip_x1_w(u8 data) { m_nr_1a_ula_clip_x1 = data; m_ula_scr->ula_clip_x1_w(m_nr_1a_ula_clip_x1); m_lores->clip_x1_w(m_nr_1a_ula_clip_x1); } + void nr_1a_ula_clip_x2_w(u8 data) { m_nr_1a_ula_clip_x2 = data; m_ula_scr->ula_clip_x2_w(m_nr_1a_ula_clip_x2); m_lores->clip_x2_w(m_nr_1a_ula_clip_x2); } + void nr_1a_ula_clip_y1_w(u8 data) { m_nr_1a_ula_clip_y1 = data; m_ula_scr->ula_clip_y1_w(m_nr_1a_ula_clip_y1); m_lores->clip_y1_w(m_nr_1a_ula_clip_y1); } void nr_1a_ula_clip_y2_w(u8 data); void nr_1b_tm_clip_x1_w(u8 data) { m_nr_1b_tm_clip_x1 = data; m_tiles->clip_x1_w(m_nr_1b_tm_clip_x1); } void nr_1b_tm_clip_x2_w(u8 data) { m_nr_1b_tm_clip_x2 = data; m_tiles->clip_x2_w(m_nr_1b_tm_clip_x2); } void nr_1b_tm_clip_y1_w(u8 data) { m_nr_1b_tm_clip_y1 = data; m_tiles->clip_y1_w(m_nr_1b_tm_clip_y1); } void nr_1b_tm_clip_y2_w(u8 data) { m_nr_1b_tm_clip_y2 = data; m_tiles->clip_y2_w(m_nr_1b_tm_clip_y2); } - void nr_26_ula_scrollx_w(u8 data) { m_nr_26_ula_scrollx = data; m_ula->ula_scroll_x_w(m_nr_26_ula_scrollx); } - void nr_27_ula_scrolly_w(u8 data) { m_nr_27_ula_scrolly = data; m_ula->ula_scroll_y_w(m_nr_27_ula_scrolly); } + void nr_26_ula_scrollx_w(u8 data) { m_nr_26_ula_scrollx = data; m_ula_scr->ula_scroll_x_w(m_nr_26_ula_scrollx); } + void nr_27_ula_scrolly_w(u8 data) { m_nr_27_ula_scrolly = data; m_ula_scr->ula_scroll_y_w(m_nr_27_ula_scrolly); } void nr_30_tm_scrollx_w(u16 data) { m_nr_30_tm_scrollx = data; m_tiles->tm_scroll_x_w(m_nr_30_tm_scrollx); } void nr_31_tm_scrolly_w(u8 data) { m_nr_31_tm_scrolly = data; m_tiles->tm_scroll_y_w(m_nr_31_tm_scrolly); } void nr_32_lores_scrollx_w(u8 data) { m_nr_32_lores_scrollx = data; m_lores->scroll_x_w(m_nr_32_lores_scrollx); } void nr_33_lores_scrolly_w(u8 data) { m_nr_33_lores_scrolly = data; m_lores->scroll_y_w(m_nr_33_lores_scrolly); } - void nr_42_ulanext_format_w(u8 data) { m_nr_42_ulanext_format = data; m_ula->ulanext_format_w(m_nr_42_ulanext_format); } - void nr_43_ulanext_en_w(bool data) { m_nr_43_ulanext_en = data; m_ula->ulanext_en_w(m_nr_43_ulanext_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); } - void nr_43_active_ula_palette_w(bool data) { m_nr_43_active_ula_palette = data; m_ula->ula_palette_select_w(m_nr_43_active_ula_palette); m_lores->lores_palette_select_w(m_nr_43_active_ula_palette); } + void nr_42_ulanext_format_w(u8 data) { m_nr_42_ulanext_format = data; m_ula_scr->ulanext_format_w(m_nr_42_ulanext_format); } + void nr_43_ulanext_en_w(bool data) { m_nr_43_ulanext_en = data; m_ula_scr->ulanext_en_w(m_nr_43_ulanext_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); } + void nr_43_active_ula_palette_w(bool data) { m_nr_43_active_ula_palette = data; m_ula_scr->ula_palette_select_w(m_nr_43_active_ula_palette); m_lores->lores_palette_select_w(m_nr_43_active_ula_palette); } void nr_43_active_layer2_palette_w(bool data) { m_nr_43_active_layer2_palette = data; m_layer2->layer2_palette_select_w(m_nr_43_active_layer2_palette); } void nr_43_active_sprite_palette_w(bool data) { m_nr_43_active_sprite_palette = data; m_sprites->sprite_palette_select_w(m_nr_43_active_sprite_palette); } @@ -215,7 +215,7 @@ class specnext_state : public spectrum_128_state void nr_4c_tm_transparent_index_w(u8 data) { m_nr_4c_tm_transparent_index = data; m_tiles->transp_colour_w(m_nr_4c_tm_transparent_index); } void nr_62_copper_mode_w(u8 data) { m_nr_62_copper_mode = data; m_copper->copper_en_w(m_nr_62_copper_mode); } - void nr_68_ula_fine_scroll_x_w(bool data) { m_nr_68_ula_fine_scroll_x = data; m_ula->ula_fine_scroll_x_w(m_nr_68_ula_fine_scroll_x); } + void nr_68_ula_fine_scroll_x_w(bool data) { m_nr_68_ula_fine_scroll_x = data; m_ula_scr->ula_fine_scroll_x_w(m_nr_68_ula_fine_scroll_x); } void nr_6a_lores_radastan_w(bool data) { m_nr_6a_lores_radastan = data; m_lores->mode_w(m_nr_6a_lores_radastan); } void nr_6a_lores_radastan_xor_w(bool data) { m_nr_6a_lores_radastan_xor = data; m_lores->dfile_w(BIT(m_port_ff_data, 0) != m_nr_6a_lores_radastan_xor); } @@ -281,7 +281,7 @@ class specnext_state : public spectrum_128_state void port_123b_layer2_en_w(bool data) { m_screen->update_now(); m_port_123b_layer2_en = data; m_layer2->layer2_en_w(m_port_123b_layer2_en); } - void port_ff3b_ulap_en_w(bool data) { m_port_ff3b_ulap_en = data; m_ula->ulap_en_w(m_port_ff3b_ulap_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); } + void port_ff3b_ulap_en_w(bool data) { m_port_ff3b_ulap_en = data; m_ula_scr->ulap_en_w(m_port_ff3b_ulap_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); } u16 nr_palette_dat(); void port_e3_reg_w(u8 data); @@ -302,7 +302,7 @@ class specnext_state : public spectrum_128_state required_device m_regs_map; required_device m_mf; required_device m_divmmc; - required_device m_ula; + required_device m_ula_scr; required_device m_tiles; required_device m_layer2; required_device m_lores; @@ -821,7 +821,7 @@ u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, c // background if (m_nr_68_ula_en) { - m_ula->draw_border(bitmap, cliprect, m_port_fe_data & 0x07); + m_ula_scr->draw_border(bitmap, cliprect, m_port_fe_data & 0x07); } else { bitmap.fill(m_palette->pen_color(UTM_FALLBACK_PEN), cliprect); @@ -844,7 +844,7 @@ u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, c if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3)) { if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, l[0]); - else m_ula->draw(screen, bitmap, clip256x192, flash, l[0]); + else m_ula_scr->draw(screen, bitmap, clip256x192, flash, l[0]); } if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), l[0]); m_layer2->draw(screen, bitmap, clip320x256, l[1], l[2]); @@ -859,7 +859,7 @@ u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, c if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3)) { if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1); - else m_ula->draw(screen, bitmap, clip256x192, flash, 1); + else m_ula_scr->draw(screen, bitmap, clip256x192, flash, 1); } if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2); if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 8); @@ -877,7 +877,7 @@ u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, c if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3)) { if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1); - else m_ula->draw(screen, bitmap, clip256x192, flash, 1); + else m_ula_scr->draw(screen, bitmap, clip256x192, flash, 1); } if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2); if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 2); @@ -932,7 +932,7 @@ u8 specnext_state::port_ff_r() void specnext_state::port_ff_w(u8 data) { m_port_ff_data = data; // ==port_ff_dat_tmx - m_ula->port_ff_reg_w(m_port_ff_data); + m_ula_scr->port_ff_reg_w(m_port_ff_data); nr_6a_lores_radastan_xor_w(m_nr_6a_lores_radastan_xor); // TODO confirm this @@ -945,7 +945,7 @@ void specnext_state::port_ff_w(u8 data) void specnext_state::port_7ffd_reg_w(u8 data) { m_port_7ffd_data = data; - m_ula->ula_shadow_en_w(port_7ffd_shadow()); + m_ula_scr->ula_shadow_en_w(port_7ffd_shadow()); } void specnext_state::port_e3_reg_w(u8 data) @@ -2250,7 +2250,7 @@ void specnext_state::nr_14_global_transparent_rgb_w(u8 data) { m_nr_14_global_transparent_rgb = data; m_global_transparent = (m_nr_14_global_transparent_rgb << 1) | BIT(m_nr_14_global_transparent_rgb, 1) | BIT(m_nr_14_global_transparent_rgb, 0); - m_ula->set_global_transparent(data); + m_ula_scr->set_global_transparent(data); m_lores->set_global_transparent(data); m_layer2->set_global_transparent(data); } @@ -2259,7 +2259,7 @@ void specnext_state::nr_1a_ula_clip_y2_w(u8 data) { m_nr_1a_ula_clip_y2 = data; const u8 ula_clip_y2_0 = ((m_nr_1a_ula_clip_y2 & 0xc0) == 0xc0) ? 0xbf : m_nr_1a_ula_clip_y2; - m_ula->ula_clip_y2_w(ula_clip_y2_0); + m_ula_scr->ula_clip_y2_w(ula_clip_y2_0); m_lores->clip_y2_w(ula_clip_y2_0); } @@ -2816,7 +2816,7 @@ void specnext_state::machine_start() m_bank_boot_rom->configure_entry(0, memregion("maincpu")->base()); const u8 *ram = m_ram->pointer() + 0x40000; - m_ula->set_host_ram_ptr(ram); + m_ula_scr->set_host_ram_ptr(ram); m_tiles->set_host_ram_ptr(ram); m_layer2->set_host_ram_ptr(ram); m_lores->set_host_ram_ptr(ram); @@ -3432,7 +3432,6 @@ GFXDECODE_END void specnext_state::video_start() { spectrum_128_state::video_start(); - m_contention_pattern = {}; // No contention for now address_space &prg = m_maincpu->space(AS_PROGRAM); prg.install_write_tap(0x0000, 0xbfff, "shadow_w", [this](offs_t offset, u8 &data, u8 mem_mask) @@ -3525,10 +3524,11 @@ void specnext_state::tbblue(machine_config &config) PALETTE(config.replace(), m_palette, palette_device::BLACK, 512 * 4 + 1); // ulatm, l2s, +1 == fallback subdevice("gfxdecode")->set_info(gfx_tbblue); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); const u16 left = SCR_256x192.left(); const u16 top = SCR_256x192.top(); - SCREEN_ULA_NEXT (config, m_ula, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x000, 0x100); + SCREEN_ULA_NEXT (config, m_ula_scr, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x000, 0x100); SPECNEXT_LORES (config, m_lores, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x000, 0x100); SPECNEXT_TILES (config, m_tiles, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x200, 0x300); SPECNEXT_LAYER2 (config, m_layer2, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x400, 0x500); diff --git a/src/mame/sinclair/specpls3.cpp b/src/mame/sinclair/specpls3.cpp index d228aabacef80..b2ab297c99e57 100644 --- a/src/mame/sinclair/specpls3.cpp +++ b/src/mame/sinclair/specpls3.cpp @@ -233,6 +233,7 @@ void specpls3_state::plus3_update_memory() m_bank_ram[3]->set_entry(ram_page); LOG("RAM at 0xc000: %02x\n", ram_page); } + m_ula->bank3_pg_w(m_bank_ram[3]->entry()); } @@ -310,9 +311,6 @@ void specpls3_state::port_1ffd_w(offs_t offset, uint8_t data) void specpls3_state::video_start() { spectrum_128_state::video_start(); - m_contention_pattern = {1, 0, 7, 6, 5, 4, 3, 2}; - m_contention_offset = 1; - m_border4t_render_at = 5; } /* ports are not decoded full. @@ -337,6 +335,16 @@ void specpls3_state::plus3_mem(address_map &map) map(0xc000, 0xffff).rw(FUNC(specpls3_state::spectrum_128_ram_r<3>), FUNC(specpls3_state::spectrum_128_ram_w<3>)); } +INPUT_PORTS_START( spec_plus2a ) + PORT_INCLUDE( spec_plus ) + + PORT_MODIFY("CONFIG") + PORT_CONFNAME( 0x80, 0x00, "Hardware Version" ) + PORT_CONFSETTING( 0x00, "Issue 2" ) + PORT_CONFSETTING( 0x80, "Issue 3" ) + PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED) +INPUT_PORTS_END + void specpls3_state::machine_start() { spectrum_128_state::machine_start(); @@ -379,13 +387,6 @@ void specpls3_state::floppy_formats(format_registration &fr) fr.add(FLOPPY_IPF_FORMAT); } -bool specpls3_state::is_contended(offs_t offset) -{ - u8 bank = m_bank_ram[3]->entry(); - return spectrum_state::is_contended(offset) - || ((offset >= 0xc000 && offset <= 0xffff) && (bank & 4)); // Memory banks 4, 5, 6 and 7 are contended -} - /* F4 Character Displayer */ static const gfx_layout spectrum_charlayout = { @@ -414,6 +415,10 @@ void specpls3_state::spectrum_plus2(machine_config &config) subdevice("gfxdecode")->set_info(specpls3); + SPECTRUM_ULA_PLUS2A(config.replace(), m_ula); + m_ula->set_z80(m_maincpu); + m_ula->set_screen(m_screen, get_screen_area()); + SPECTRUM_EXPANSION_SLOT(config.replace(), m_exp, specpls3_expansion_devices, nullptr); m_exp->irq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0); m_exp->nmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI); @@ -519,10 +524,10 @@ ROM_START(sp3eata) ROMX_LOAD("3ezxaes.rom",0x10000,0x10000, CRC(8f0ae91a) SHA1(71693e18b30c90914be58cba26682ca025c924ea), ROM_BIOS(1)) ROM_END -/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ -COMP( 1987, specpl2a, 0, 0, spectrum_plus2, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +2a", 0 ) -COMP( 1987, specpls3, specpl2a, 0, spectrum_plus3, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3", 0 ) -COMP( 2000, specpl3e, 0, 0, spectrum_plus3, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e", MACHINE_UNOFFICIAL ) -COMP( 2002, sp3e8bit, 0, 0, spectrum_plus3, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit IDE", MACHINE_UNOFFICIAL ) -COMP( 2002, sp3eata, 0, 0, spectrum_plus3, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXATASP", MACHINE_UNOFFICIAL ) -COMP( 2002, sp3ezcf, 0, 0, spectrum_plus3, spec_plus, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXCF", MACHINE_UNOFFICIAL ) +/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ +COMP( 1987, specpl2a, 0, 0, spectrum_plus2, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +2a", 0 ) +COMP( 1987, specpls3, specpl2a, 0, spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3", 0 ) +COMP( 2000, specpl3e, 0, 0, spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e", MACHINE_UNOFFICIAL ) +COMP( 2002, sp3e8bit, 0, 0, spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit IDE", MACHINE_UNOFFICIAL ) +COMP( 2002, sp3eata, 0, 0, spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXATASP", MACHINE_UNOFFICIAL ) +COMP( 2002, sp3ezcf, 0, 0, spectrum_plus3, spec_plus2a, specpls3_state, empty_init, "Amstrad plc", "ZX Spectrum +3e 8bit ZXCF", MACHINE_UNOFFICIAL ) diff --git a/src/mame/sinclair/specpls3.h b/src/mame/sinclair/specpls3.h index e95d72416ab3c..29ddf7e4d32ff 100644 --- a/src/mame/sinclair/specpls3.h +++ b/src/mame/sinclair/specpls3.h @@ -34,7 +34,6 @@ class specpls3_state : public spectrum_128_state virtual void machine_start() override ATTR_COLD; virtual void machine_reset() override ATTR_COLD; virtual void plus3_update_memory() override; - virtual bool is_contended(offs_t offset) override; private: void rom_w(offs_t offset, uint8_t data); diff --git a/src/mame/sinclair/spectrum.cpp b/src/mame/sinclair/spectrum.cpp index 7782181041eda..ea283f3b8ad26 100644 --- a/src/mame/sinclair/spectrum.cpp +++ b/src/mame/sinclair/spectrum.cpp @@ -294,8 +294,7 @@ SamRam uint8_t spectrum_state::pre_opcode_fetch_r(offs_t offset) { - m_is_m1_rd_contended = false; - if (!machine().side_effects_disabled() && is_contended(offset)) content_early(); + m_ula->m1(offset); /* this allows expansion devices to act upon opcode fetches from MEM addresses for example, interface1 detection fetches requires fetches at 0008 / 0708 to @@ -309,7 +308,7 @@ uint8_t spectrum_state::pre_opcode_fetch_r(offs_t offset) uint8_t spectrum_state::spectrum_data_r(offs_t offset) { - if (!machine().side_effects_disabled() && is_contended(offset)) content_early(); + m_ula->data_r(offset); m_exp->pre_data_fetch(offset); uint8_t retval = m_specmem->read8(offset); @@ -319,7 +318,7 @@ uint8_t spectrum_state::spectrum_data_r(offs_t offset) void spectrum_state::spectrum_data_w(offs_t offset, uint8_t data) { - if (is_contended(offset)) content_early(); + m_ula->data_w(offset); if (is_vram_write(offset)) m_screen->update_now(); m_specmem->write8(offset,data); @@ -345,8 +344,7 @@ uint8_t spectrum_state::spectrum_rom_r(offs_t offset) */ void spectrum_state::spectrum_ula_w(offs_t offset, uint8_t data) { - if (is_contended(offset)) content_early(); - content_early(1); + m_ula->ula_w(offset); u8 changed = m_port_fe_data ^ data; @@ -373,11 +371,7 @@ void spectrum_state::spectrum_ula_w(offs_t offset, uint8_t data) /* DJR: Spectrum+ keys added */ uint8_t spectrum_state::spectrum_ula_r(offs_t offset) { - if (!machine().side_effects_disabled()) - { - if (is_contended(offset)) content_early(); - content_early(1); - } + m_ula->ula_r(offset); int lines = offset >> 8; int data = 0xff; @@ -453,11 +447,7 @@ uint8_t spectrum_state::spectrum_ula_r(offs_t offset) void spectrum_state::spectrum_port_w(offs_t offset, uint8_t data) { - if (is_contended(offset)) - { - content_early(); - content_late(); - } + m_ula->io_w(offset); // Pass through to expansion device if present if (m_exp->get_card_device()) @@ -466,11 +456,7 @@ void spectrum_state::spectrum_port_w(offs_t offset, uint8_t data) uint8_t spectrum_state::spectrum_port_r(offs_t offset) { - if (!machine().side_effects_disabled() && is_contended(offset)) - { - content_early(); - content_late(); - } + m_ula->io_r(offset); // Pass through to expansion device if present if (m_exp->get_card_device()) @@ -515,22 +501,21 @@ uint8_t spectrum_state::floating_bus_r() */ u8 data = 0xff; - u64 vpos = m_screen->vpos(); // peek into attribute ram when beam is in display area // ula always returns ff when in border area (or h/vblank) - rectangle screen = get_screen_area(); - if (!m_contention_pattern.empty() && vpos >= screen.top() && vpos <= screen.bottom()) + if (m_ula->is_in_contended_area()) { - u64 now = m_maincpu->total_cycles() - m_int_at; - u64 cf = vpos * m_screen->width() * m_maincpu->clock() / m_screen->clock() + m_contention_offset; - u64 ct = cf + screen.width() * m_maincpu->clock() / m_screen->clock(); + const u64 vpos = m_screen->vpos(); + const u64 now = m_maincpu->total_cycles() - m_ula->get_irq_at(); + const u64 cf = vpos * m_ula->get_video_line_clocks() + m_ula->get_raster_contention_offset(); + const u64 ct = cf + m_ula->get_raster_line_clocks(); if (cf <= now && now < ct) { u64 clocks = now - cf; if (!BIT(clocks, 2)) { - u16 y = vpos - screen.top(); + u16 y = vpos - get_screen_area().top(); u16 x = (clocks >> 2) + BIT(clocks, 1); data = clocks & 1 ? m_screen_location[0x1800 + (((y & 0xf8) << 2) | x)] @@ -675,9 +660,12 @@ INPUT_PORTS_START( spectrum ) PORT_START("CONFIG") PORT_CONFNAME( 0x80, 0x00, "Hardware Version" ) - PORT_CONFSETTING( 0x00, "Issue 2" ) - PORT_CONFSETTING( 0x80, "Issue 3" ) - PORT_BIT(0x7f, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_CONFSETTING( 0x00, "Issue 2" ) + PORT_CONFSETTING( 0x80, "Issue 3" ) + PORT_CONFNAME( 0x01, 0x00, "Contention" ) PORT_CHANGED_MEMBER("ula", FUNC(spectrum_ula_device::on_contention_changed), 0) + PORT_CONFSETTING( 0x00, "Early" ) + PORT_CONFSETTING( 0x01, "Late" ) + PORT_BIT(0x7e, IP_ACTIVE_LOW, IPT_UNUSED) INPUT_PORTS_END /* Machine initialization */ @@ -689,8 +677,6 @@ void spectrum_state::init_spectrum() void spectrum_state::machine_start() { save_item(NAME(m_port_fe_data)); - save_item(NAME(m_int_at)); - save_item(NAME(m_is_m1_rd_contended)); m_maincpu->space(AS_PROGRAM).specific(m_program); m_maincpu->space(AS_IO).specific(m_io); @@ -703,7 +689,6 @@ void spectrum_state::machine_reset() m_port_fe_data = -1; m_port_7ffd_data = -1; m_port_1ffd_data = -1; - m_is_m1_rd_contended = false; m_irq_on_timer->adjust(attotime::never); m_irq_off_timer->adjust(attotime::never); } @@ -726,10 +711,9 @@ GFXDECODE_END TIMER_CALLBACK_MEMBER(spectrum_state::irq_on) { - m_int_at = m_maincpu->total_cycles(); - m_int_at -= m_maincpu->attotime_to_cycles(m_maincpu->local_time() - machine().time()); + m_ula->on_irq(); m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); - m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32)); + m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32 + m_ula->get_irq_ext_length())); } TIMER_CALLBACK_MEMBER(spectrum_state::irq_off) @@ -751,7 +735,7 @@ void spectrum_state::spectrum_common(machine_config &config) m_maincpu->set_io_map(&spectrum_state::spectrum_io); m_maincpu->set_vblank_int("screen", FUNC(spectrum_state::spec_interrupt)); m_maincpu->refresh_cb().set(FUNC(spectrum_state::spectrum_refresh_w)); - m_maincpu->nomreq_cb().set(FUNC(spectrum_state::spectrum_nomreq)); + m_maincpu->nomreq_cb().set("ula", FUNC(spectrum_ula_device::nomem_rq)); m_maincpu->busack_cb().set("dma", FUNC(dma_slot_device::bai_w)); ADDRESS_MAP_BANK(config, m_specmem).set_map(&spectrum_state::spectrum_map).set_options(ENDIANNESS_LITTLE, 8, 16, 0x10000); @@ -773,6 +757,9 @@ void spectrum_state::spectrum_common(machine_config &config) PALETTE(config, "palette", FUNC(spectrum_state::spectrum_palette), 16); GFXDECODE(config, "gfxdecode", "palette", gfx_spectrum); + SPECTRUM_ULA_48K(config, m_ula); + m_ula->set_screen(m_screen, get_screen_area()); + m_ula->set_z80(m_maincpu); /* sound hardware */ SPEAKER(config, "speakers").front_center(); diff --git a/src/mame/sinclair/spectrum.h b/src/mame/sinclair/spectrum.h index 7df0d5e29bb70..b6ffe37eebc1d 100644 --- a/src/mame/sinclair/spectrum.h +++ b/src/mame/sinclair/spectrum.h @@ -12,6 +12,7 @@ #pragma once #include "spec_snqk.h" +#include "spectrum_ula.h" #include "machine/bankdev.h" #include "bus/spectrum/exp.h" #include "imagedev/cassette.h" @@ -54,6 +55,7 @@ class spectrum_state : public driver_device m_video_ram(*this, "video_ram"), m_maincpu(*this, "maincpu"), m_screen(*this, "screen"), + m_ula(*this, "ula"), m_cassette(*this, "cassette"), m_rom(*this, "maincpu"), m_ram(*this, RAM_TAG), @@ -117,27 +119,15 @@ class spectrum_state : public driver_device optional_shared_ptr m_video_ram; uint8_t *m_screen_location; - std::vector 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; - bool m_is_m1_rd_contended = false; - u64 m_int_at; - uint8_t pre_opcode_fetch_r(offs_t offset); void spectrum_rom_w(offs_t offset, uint8_t data); virtual uint8_t spectrum_rom_r(offs_t offset); uint8_t spectrum_data_r(offs_t offset); void spectrum_data_w(offs_t offset, uint8_t data); - virtual bool is_contended(offs_t offset); virtual bool is_vram_write(offs_t offset); - void content_early(s8 shift = 0); - void content_late(); virtual u8 *snow_pattern1_base(u8 i_reg); void spectrum_refresh_w(offs_t offset, uint8_t data); - void spectrum_nomreq(offs_t offset, uint8_t data); void spectrum_ula_w(offs_t offset, uint8_t data); uint8_t spectrum_ula_r(offs_t offset); void spectrum_port_w(offs_t offset, uint8_t data); @@ -154,6 +144,7 @@ class spectrum_state : public driver_device required_device m_maincpu; required_device m_screen; + required_device m_ula; void spectrum_io(address_map &map) ATTR_COLD; void spectrum_clone_io(address_map &map) ATTR_COLD; @@ -227,5 +218,6 @@ class spectrum_state : public driver_device INPUT_PORTS_EXTERN( spectrum ); INPUT_PORTS_EXTERN( spec128 ); INPUT_PORTS_EXTERN( spec_plus ); +INPUT_PORTS_EXTERN( spec_plus2a ); #endif // MAME_SINCLAIR_SPECTRUM_H diff --git a/src/mame/sinclair/spectrum_ula.cpp b/src/mame/sinclair/spectrum_ula.cpp new file mode 100644 index 0000000000000..77ca8bd93c4f2 --- /dev/null +++ b/src/mame/sinclair/spectrum_ula.cpp @@ -0,0 +1,232 @@ +// license:BSD-3-Clause +// copyright-holders:Andrei I. Holub +/********************************************************************** + Spectrum ULA Contention helper device +**********************************************************************/ + +#include "emu.h" + +#include "spectrum_ula.h" + +DEFINE_DEVICE_TYPE(SPECTRUM_ULA_UNCONTENDED, spectrum_ula_device, "ula", "Spectrum ULA Contention :: Uncontended") + +spectrum_ula_device::spectrum_ula_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock) + , m_maincpu(*this, finder_base::DUMMY_TAG) + , m_screen(*this, finder_base::DUMMY_TAG) +{ +} + +spectrum_ula_device::spectrum_ula_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : spectrum_ula_device(mconfig, SPECTRUM_ULA_UNCONTENDED, tag, owner, clock) +{ +} + +void spectrum_ula_device::device_start() +{ + save_item(NAME(m_bank3_page)); +} + + +spectrum_ula_contended_device::spectrum_ula_contended_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : spectrum_ula_device(mconfig, type, tag, owner, clock) +{ + m_is_timings_late = false; +} + +INPUT_CHANGED_MEMBER(spectrum_ula_contended_device::on_contention_changed) +{ + m_is_timings_late = newval & 1; +} + +void spectrum_ula_contended_device::nomem_rq(offs_t offset, u8 data) +{ + if (is_contended(offset)) + content_early(); +} + +void spectrum_ula_contended_device::m1(offs_t offset) +{ + m_is_m1_rd_contended = false; + if (!machine().side_effects_disabled() && is_contended(offset)) + content_early(); +} + +void spectrum_ula_contended_device::io_r(offs_t offset) +{ + if (!machine().side_effects_disabled() && is_contended(offset)) + { + content_early(); + content_late(); + } +} + +void spectrum_ula_contended_device::io_w(offs_t offset) +{ + if (is_contended(offset)) + { + content_early(); + content_late(); + } +} + +void spectrum_ula_contended_device::ula_r(offs_t offset) +{ + if (!machine().side_effects_disabled()) + { + if (is_contended(offset)) + content_early(); + content_early(1); + } +} + +void spectrum_ula_contended_device::ula_w(offs_t offset) +{ + if (is_contended(offset)) + content_early(); + content_early(1); +} + +void spectrum_ula_contended_device::data_r(offs_t offset) +{ + if (!machine().side_effects_disabled() && is_contended(offset)) + content_early(); +} + +void spectrum_ula_contended_device::data_w(offs_t offset) +{ + if (is_contended(offset)) + content_early(); +} + +void spectrum_ula_contended_device::content_early(s8 shift) +{ + const u64 vpos = m_screen->vpos(); + if (vpos < m_screen_area.top() || vpos > m_screen_area.bottom()) + return; + + const u64 now = m_maincpu->total_cycles() - m_int_at + shift; + const u64 cf = vpos * m_video_line_clocks + get_raster_contention_offset(); + const u64 ct = cf + m_raster_line_clocks; + + if(cf <= now && now < ct) + { + m_is_m1_rd_contended = true; // make sure M1 sets it to false before + const u64 clocks = now - cf; + const u8 c = m_pattern[clocks % 8]; + m_maincpu->adjust_icount(-c); + } +} + +void spectrum_ula_contended_device::content_late() +{ + const u64 vpos = m_screen->vpos(); + if (vpos < m_screen_area.top() || vpos > m_screen_area.bottom()) + return; + + u64 now = m_maincpu->total_cycles() - m_int_at + 1; + const u64 cf = vpos * m_video_line_clocks + get_raster_contention_offset(); + const u64 ct = cf + m_raster_line_clocks; + for(auto i = 0x04; i; i >>= 1) + { + if(cf <= now && now < ct) + { + const u64 clocks = now - cf; + const u8 c = m_pattern[clocks % 8]; + m_maincpu->adjust_icount(-c); + now += c; + } + now++; + } +} + +void spectrum_ula_contended_device::on_irq() +{ + m_int_at = m_maincpu->total_cycles() - m_maincpu->attotime_to_cycles(m_maincpu->local_time() - machine().time()); +} + +bool spectrum_ula_contended_device::is_snow_possible(u16 addr) +{ + return is_contended(addr) && !m_is_m1_rd_contended && m_screen_area.contains(m_screen->hpos(), m_screen->vpos()); +} + +bool spectrum_ula_contended_device::is_in_contended_area() +{ + const u64 vpos = m_screen->vpos(); + return vpos >= m_screen_area.top() && vpos <= m_screen_area.bottom(); +} + +void spectrum_ula_contended_device::device_start() +{ + spectrum_ula_device::device_start(); + + save_item(NAME(m_is_timings_late)); + save_item(NAME(m_int_at)); + save_item(NAME(m_is_m1_rd_contended)); + + m_video_line_clocks = m_screen->width() * m_maincpu->clock() / m_screen->clock(); + m_raster_line_clocks = m_screen_area.width() * m_maincpu->clock() / m_screen->clock(); +} + +void spectrum_ula_contended_device::device_reset() +{ + m_is_m1_rd_contended = false; +} + + +DEFINE_DEVICE_TYPE(SPECTRUM_ULA_48K, spectrum_ula_48k_device, "ula48", "Spectrum ULA Contention :: 48K") + +spectrum_ula_48k_device::spectrum_ula_48k_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : spectrum_ula_contended_device(mconfig, SPECTRUM_ULA_48K, tag, owner, clock) +{ + const u8 pattern[] = {6, 5, 4, 3, 2, 1, 0, 0}; + memcpy(m_pattern, pattern, 8); + m_btime = 2; + m_base_offset = -1; +} + +bool spectrum_ula_48k_device::is_contended(offs_t offset) +{ + return offset >= 0x4000 && offset < 0x8000; +} + + +DEFINE_DEVICE_TYPE(SPECTRUM_ULA_128K, spectrum_ula_128k_device, "ula128", "Spectrum ULA Contention :: 128K/+2") + +spectrum_ula_128k_device::spectrum_ula_128k_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : spectrum_ula_contended_device(mconfig, SPECTRUM_ULA_128K, tag, owner, clock) +{ + const u8 pattern[] = {6, 5, 4, 3, 2, 1, 0, 0}; + memcpy(m_pattern, pattern, 8); + m_btime = 3; + m_base_offset = -3; +} + +bool spectrum_ula_128k_device::is_contended(offs_t offset) +{ + return (offset >= 0x4000 && offset < 0x8000) + || ((offset >= 0xc000 && offset <= 0xffff) && (m_bank3_page & 1)); // Memory pages 1,3,5 and 7 are contended +} + + +DEFINE_DEVICE_TYPE(SPECTRUM_ULA_PLUS2A, spectrum_ula_plus2a_device, "ulaplus2a", "Spectrum ULA Contention :: +2A/+3") + +spectrum_ula_plus2a_device::spectrum_ula_plus2a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : spectrum_ula_contended_device(mconfig, SPECTRUM_ULA_PLUS2A, tag, owner, clock) +{ + const u8 pattern[] = {1, 0, 7, 6, 5, 4, 3, 2}; + memcpy(m_pattern, pattern, 8); + m_btime = 5; + m_base_offset = 1; +} + +INPUT_CHANGED_MEMBER(spectrum_ula_plus2a_device::on_contention_changed) +{ + m_is_timings_late = false; +} + +bool spectrum_ula_plus2a_device::is_contended(offs_t offset) +{ + return (offset >= 0x4000 && offset < 0x8000) + || ((offset >= 0xc000 && offset <= 0xffff) && (m_bank3_page & 4)); // Memory banks 4, 5, 6 and 7 are contended +} diff --git a/src/mame/sinclair/spectrum_ula.h b/src/mame/sinclair/spectrum_ula.h new file mode 100644 index 0000000000000..af1c07950b8ab --- /dev/null +++ b/src/mame/sinclair/spectrum_ula.h @@ -0,0 +1,142 @@ +// license:BSD-3-Clause +// copyright-holders:Andrei I. Holub +#ifndef MAME_SINCLAIR_SPECTRUM_ULA_H +#define MAME_SINCLAIR_SPECTRUM_ULA_H + +#pragma once + +#include "cpu/z80/z80.h" +#include "screen.h" + +class spectrum_ula_device : public device_t +{ +public: + spectrum_ula_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + template void set_z80(T &&cpu_tag) { m_maincpu.set_tag(std::forward(cpu_tag)); } + template void set_screen(T &&screen_tag, rectangle screen_area) { m_screen.set_tag(std::forward(screen_tag)); m_screen_area = screen_area; } + virtual INPUT_CHANGED_MEMBER(on_contention_changed) {}; + + virtual void nomem_rq(offs_t offset, u8 data) {} + virtual void m1(offs_t offset) {} + virtual void io_r(offs_t offset) {} + virtual void io_w(offs_t offset) {} + virtual void data_r(offs_t offset) {} + virtual void data_w(offs_t offset) {} + virtual void ula_r(offs_t offset) {} + virtual void ula_w(offs_t offset) {} + + virtual void on_irq() {} + void bank3_pg_w(u8 bank3_page) { m_bank3_page = bank3_page; } + + virtual bool is_snow_possible(u16 addr) { return false; } + virtual bool is_in_contended_area() { return false; } + virtual u64 get_video_line_clocks() { assert(false); return -1; } // video line clocks data is undefined for uncontended case and must be guarded with "if" condition + virtual u64 get_raster_line_clocks() { assert(false); return -1; } // raster line clocks data is undefined + virtual u64 get_irq_at() { assert(false); return -1; } // irq time is undefined + virtual u8 get_irq_ext_length() { return 0; } + virtual u8 get_border_chunk_size() { return 1; } + virtual u8 get_btime() { return 0; } + virtual s8 get_raster_contention_offset() { return 0; } + +protected: + spectrum_ula_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + virtual void device_reset() override ATTR_COLD {} + virtual void device_start() override ATTR_COLD; + + optional_device m_maincpu; + optional_device m_screen; + + rectangle m_screen_area; + u8 m_bank3_page; +}; + + +class spectrum_ula_contended_device : public spectrum_ula_device +{ +public: + virtual INPUT_CHANGED_MEMBER(on_contention_changed) override; + + virtual void nomem_rq(offs_t offset, u8 data) override; + virtual void m1(offs_t offset) override; + virtual void io_r(offs_t offset) override; + virtual void io_w(offs_t offset) override; + virtual void ula_r(offs_t offset) override; + virtual void ula_w(offs_t offset) override; + virtual void data_r(offs_t offset) override; + virtual void data_w(offs_t offset) override; + + virtual void on_irq() override; + + virtual bool is_snow_possible(u16 addr) override; + virtual bool is_in_contended_area() override; + virtual u64 get_video_line_clocks() override { return m_video_line_clocks; } + virtual u64 get_raster_line_clocks() override { return m_raster_line_clocks; } + virtual u64 get_irq_at() override { return m_int_at; } + virtual u8 get_irq_ext_length() override { return m_is_timings_late; }; + virtual u8 get_border_chunk_size() override { return 8; } + virtual u8 get_btime() override { return m_btime; } + virtual s8 get_raster_contention_offset() override { return m_base_offset + m_is_timings_late; } + +protected: + spectrum_ula_contended_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); + + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + + virtual bool is_contended(offs_t offset) = 0; + + u8 m_pattern[8]; + u8 m_btime; // Pixel offset in 16px chunk (8T) when current [b]order attribute is applied. + s8 m_base_offset; // Defines offset in CPU cycles from screen left side. Early model (48/128/+2) typically use -1, later (+2A/+3) +1 + + u64 m_video_line_clocks; + u64 m_raster_line_clocks; + bool m_is_timings_late; + u64 m_int_at; + bool m_is_m1_rd_contended; + +private: + void content_early(s8 shift = 0); + void content_late(); + +}; + +class spectrum_ula_48k_device : public spectrum_ula_contended_device +{ +public: + spectrum_ula_48k_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + +protected: + virtual bool is_contended(offs_t offset) override; +}; + + +class spectrum_ula_128k_device : public spectrum_ula_contended_device +{ +public: + spectrum_ula_128k_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + +protected: + virtual bool is_contended(offs_t offset) override; +}; + + +class spectrum_ula_plus2a_device : public spectrum_ula_contended_device +{ +public: + spectrum_ula_plus2a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + virtual INPUT_CHANGED_MEMBER(on_contention_changed) override; + +protected: + virtual bool is_contended(offs_t offset) override; +}; + + +DECLARE_DEVICE_TYPE(SPECTRUM_ULA_UNCONTENDED, spectrum_ula_device) +DECLARE_DEVICE_TYPE(SPECTRUM_ULA_48K, spectrum_ula_48k_device) +DECLARE_DEVICE_TYPE(SPECTRUM_ULA_128K, spectrum_ula_128k_device) +DECLARE_DEVICE_TYPE(SPECTRUM_ULA_PLUS2A, spectrum_ula_plus2a_device) +#endif // MAME_SINCLAIR_SPECTRUM_ULA_H diff --git a/src/mame/sinclair/spectrum_v.cpp b/src/mame/sinclair/spectrum_v.cpp index 178b940ff8251..75250291fba61 100644 --- a/src/mame/sinclair/spectrum_v.cpp +++ b/src/mame/sinclair/spectrum_v.cpp @@ -47,9 +47,6 @@ void spectrum_state::video_start() 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; } /*************************************************************************** @@ -115,7 +112,7 @@ u8 spectrum_state::get_border_color(u16 hpos, u16 vpos) u32 spectrum_state::screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { rectangle scr = get_screen_area(); - rectangle vis = screen.visible_area(); + const rectangle vis = screen.visible_area(); if (vis != scr) { rectangle bsides[4] = { @@ -141,17 +138,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 : 8; - u8 at = m_contention_pattern.empty() ? 0 : m_border4t_render_at; + const u8 mod = m_ula->get_border_chunk_size(); + const u8 btime = m_ula->get_btime(); 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 == at) + if (x % mod == btime) { - pix -= at; - x -= at; + pix -= btime; + x -= btime; for (auto m = 0; m < mod; m++, x++) *pix++ = get_border_color(y, x); } @@ -164,7 +161,7 @@ void spectrum_state::spectrum_update_border(screen_device &screen, bitmap_ind16 } } -/* ULA reads screen data in 16px (8T) chunks as following: +/* ULA reads screen data in 16px (8T) chunks as following (48K Spectrum case): T: | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ULA contention: | | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 0 | opcode read T: | 0 | 1 | 2 RSH | 3 RSH | | | | | | ULA reads lower address for char1/attr1 from R6:0 @@ -177,7 +174,7 @@ TODO Curren implementation only tracks char switch position. In order to track b */ void spectrum_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { - bool invert_attrs = u64(screen.frame_number() / m_frame_invert_count) & 1; + const bool invert_attrs = u64(screen.frame_number() / m_frame_invert_count) & 1; for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++) { u16 hpos = cliprect.left(); @@ -220,71 +217,15 @@ bool spectrum_state::is_vram_write(offs_t offset) { return offset >= 0x4000 && offset < 0x5b00; } -bool spectrum_state::is_contended(offs_t offset) { - return offset >= 0x4000 && offset < 0x8000; -} - -void spectrum_state::content_early(s8 shift) -{ - u64 vpos = m_screen->vpos(); - if (m_contention_pattern.empty() || vpos < get_screen_area().top() || vpos > get_screen_area().bottom()) - return; - - 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(); - - if(cf <= now && now < ct) - { - m_is_m1_rd_contended = true; // make sure M1 sets it to false before - u64 clocks = now - cf; - u8 c = m_contention_pattern[clocks % m_contention_pattern.size()]; - m_maincpu->adjust_icount(-c); - } -} - -void spectrum_state::content_late() -{ - u64 vpos = m_screen->vpos(); - if (m_contention_pattern.empty() || vpos < get_screen_area().top() || vpos > get_screen_area().bottom()) - return; - - 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) - { - if(cf <= now && now < ct) - { - u64 clocks = now - cf; - u8 c = m_contention_pattern[clocks % m_contention_pattern.size()]; - m_maincpu->adjust_icount(-c); - now += c; - } - now++; - } -} - -void spectrum_state::spectrum_nomreq(offs_t offset, uint8_t data) -{ - if (is_contended(offset)) content_early(); -} - // see: spectrum_state::spectrum_update_screen() void spectrum_state::spectrum_refresh_w(offs_t offset, uint8_t data) { - const u8 i = offset >> 8; - bool is_snow_possible = !m_contention_pattern.empty() && is_contended(i << 8); - if (!is_snow_possible || m_is_m1_rd_contended) - return; - - const u64 hpos = m_screen->hpos(); - const u64 vpos = m_screen->vpos(); - if (hpos < get_screen_area().left() || hpos > get_screen_area().right() || vpos < get_screen_area().top() || vpos > get_screen_area().bottom()) + if (!m_ula->is_snow_possible(offset)) return; - u8 x = hpos - get_screen_area().left(); - const u16 y = vpos - get_screen_area().top(); + const rectangle screen = get_screen_area(); + u8 x = m_screen->hpos() - screen.left(); + const u16 y = m_screen->vpos() - screen.top(); // icount is not adjusted yet, everything below is happening during // the last REFRESH cycle: +2px(1t of 3.5MHz) @@ -315,6 +256,7 @@ void spectrum_state::spectrum_refresh_w(offs_t offset, uint8_t data) if (snow_pattern == 1) { + const u8 i = offset >> 8; const u8 r = (offset + 1) & 0x7f; // R must be already incremented during refresh. Consider z80 update. const u8 *const base = snow_pattern1_base(i); m_screen_location[px_addr_hi | addr_lo] = base[px_addr_hi | r]; diff --git a/src/mame/sinclair/sprinter.cpp b/src/mame/sinclair/sprinter.cpp index eca9b88e91338..5ae2a4af5888a 100644 --- a/src/mame/sinclair/sprinter.cpp +++ b/src/mame/sinclair/sprinter.cpp @@ -1621,7 +1621,6 @@ void sprinter_state::video_start() m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(sprinter_state::get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32); - m_contention_pattern = {}; init_taps(); m_acc_timer = timer_alloc(FUNC(sprinter_state::acc_tick), this); @@ -1960,6 +1959,7 @@ void sprinter_state::sprinter(machine_config &config) m_screen->set_screen_update(FUNC(sprinter_state::screen_update)); PALETTE(config, "palette", palette_device::BLACK).set_entries(256 * 8); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); PC_KBDC(config, m_kbd, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL); m_kbd->out_data_cb().set(m_maincpu, FUNC(z84c015_device::rxa_w)); // KBD_DATR diff --git a/src/mame/sinclair/tsconf.cpp b/src/mame/sinclair/tsconf.cpp index 19c07b7b34af8..8ca882870965a 100644 --- a/src/mame/sinclair/tsconf.cpp +++ b/src/mame/sinclair/tsconf.cpp @@ -150,7 +150,6 @@ GFXDECODE_END void tsconf_state::video_start() { spectrum_128_state::video_start(); - m_contention_pattern = {}; // disable inherited contention m_ts_tilemap[TM_TS_CHAR] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_txt)), TILEMAP_SCAN_ROWS, 8, 8, 128, 64); @@ -323,6 +322,8 @@ void tsconf_state::tsconf(machine_config &config) m_screen->set_no_palette(); subdevice("gfxdecode")->set_info(gfx_tsconf); + SPECTRUM_ULA_UNCONTENDED(config.replace(), m_ula); + RAM(config, m_cram).set_default_size("512").set_default_value(0); RAM(config, m_sfile).set_default_size("512").set_default_value(0); // 85*6 From 6ea433ad0e7f73adcd1f13bef22bbe074145bc62 Mon Sep 17 00:00:00 2001 From: Andrei Holub Date: Wed, 13 Aug 2025 13:14:38 -0400 Subject: [PATCH 2/2] added vars for atime; keep to previously tuned values --- src/mame/sinclair/spectrum_ula.cpp | 8 +++++++- src/mame/sinclair/spectrum_ula.h | 6 ++++++ src/mame/sinclair/spectrum_v.cpp | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/mame/sinclair/spectrum_ula.cpp b/src/mame/sinclair/spectrum_ula.cpp index 77ca8bd93c4f2..58151977327b9 100644 --- a/src/mame/sinclair/spectrum_ula.cpp +++ b/src/mame/sinclair/spectrum_ula.cpp @@ -182,6 +182,8 @@ spectrum_ula_48k_device::spectrum_ula_48k_device(const machine_config &mconfig, const u8 pattern[] = {6, 5, 4, 3, 2, 1, 0, 0}; memcpy(m_pattern, pattern, 8); m_btime = 2; + m_atime_left = 4; + m_atime_right = 0; m_base_offset = -1; } @@ -199,7 +201,9 @@ spectrum_ula_128k_device::spectrum_ula_128k_device(const machine_config &mconfig const u8 pattern[] = {6, 5, 4, 3, 2, 1, 0, 0}; memcpy(m_pattern, pattern, 8); m_btime = 3; - m_base_offset = -3; + m_atime_left = 4; + m_atime_right = 0; + m_base_offset = -1; // leave it one for now, but according to Timings_Test it must be -3 } bool spectrum_ula_128k_device::is_contended(offs_t offset) @@ -217,6 +221,8 @@ spectrum_ula_plus2a_device::spectrum_ula_plus2a_device(const machine_config &mco const u8 pattern[] = {1, 0, 7, 6, 5, 4, 3, 2}; memcpy(m_pattern, pattern, 8); m_btime = 5; + m_atime_left = 4; + m_atime_right = 0; m_base_offset = 1; } diff --git a/src/mame/sinclair/spectrum_ula.h b/src/mame/sinclair/spectrum_ula.h index af1c07950b8ab..392b8c86805d8 100644 --- a/src/mame/sinclair/spectrum_ula.h +++ b/src/mame/sinclair/spectrum_ula.h @@ -37,6 +37,8 @@ class spectrum_ula_device : public device_t virtual u8 get_irq_ext_length() { return 0; } virtual u8 get_border_chunk_size() { return 1; } virtual u8 get_btime() { return 0; } + virtual u8 get_atime_left() { return 0; } + virtual u8 get_atime_right() { return 0; } virtual s8 get_raster_contention_offset() { return 0; } protected: @@ -77,6 +79,8 @@ class spectrum_ula_contended_device : public spectrum_ula_device virtual u8 get_irq_ext_length() override { return m_is_timings_late; }; virtual u8 get_border_chunk_size() override { return 8; } virtual u8 get_btime() override { return m_btime; } + virtual u8 get_atime_left() override { return m_atime_left; } + virtual u8 get_atime_right() override { return m_atime_right; } virtual s8 get_raster_contention_offset() override { return m_base_offset + m_is_timings_late; } protected: @@ -89,6 +93,8 @@ class spectrum_ula_contended_device : public spectrum_ula_device u8 m_pattern[8]; u8 m_btime; // Pixel offset in 16px chunk (8T) when current [b]order attribute is applied. + u8 m_atime_left; // Pixel offset when [a]ttribute is applied on the left side. + u8 m_atime_right; // Pixel offset when [a]ttribute is applied on the right side. s8 m_base_offset; // Defines offset in CPU cycles from screen left side. Early model (48/128/+2) typically use -1, later (+2A/+3) +1 u64 m_video_line_clocks; diff --git a/src/mame/sinclair/spectrum_v.cpp b/src/mame/sinclair/spectrum_v.cpp index 75250291fba61..894a1800f9091 100644 --- a/src/mame/sinclair/spectrum_v.cpp +++ b/src/mame/sinclair/spectrum_v.cpp @@ -180,7 +180,7 @@ void spectrum_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 u16 hpos = cliprect.left(); u16 x = hpos - get_screen_area().left(); bool chunk_right = x & 8; - if (x % 8 <= (chunk_right ? 0 : 4)) + if (x % 8 <= (chunk_right ? m_ula->get_atime_right() : m_ula->get_atime_left())) { u8 shift = x % 8; x -= shift; @@ -198,7 +198,7 @@ void spectrum_state::spectrum_update_screen(screen_device &screen, bitmap_ind16 u8 *attr = &m_screen_location[0x1800 | (((y & 0xf8) << 2) | (x >> 3))]; u16 *pix = &(bitmap.pix(vpos, hpos)); - while ((hpos + (chunk_right ? 0 : 4)) <= cliprect.right()) + while ((hpos + (chunk_right ? m_ula->get_atime_right() : m_ula->get_atime_left())) <= cliprect.right()) { u16 ink = ((*attr >> 3) & 0x08) | (*attr & 0x07); u16 pap = (*attr >> 3) & 0x0f;