141 lines
6.1 KiB
Diff
141 lines
6.1 KiB
Diff
--- a/src/mame/sharp/x68k.h 2026-08-04 16:54:53.872522860 -0500
|
|
+++ b/src/mame/sharp/x68k.h 2026-08-04 16:54:53.876522842 -0500
|
|
@@ -195,6 +195,12 @@
|
|
bool m_exp_nmi[2]{};
|
|
uint8_t m_current_ipl = 0;
|
|
int m_led_state = 0;
|
|
+ // Extra cycles charged per VRAM access, over and above a main-memory
|
|
+ // access, in 16.16 fixed point. Set from measured values in machine_start
|
|
+ // for the machines those measurements cover; 0 (unmodelled) otherwise.
|
|
+ uint32_t m_gvram_wait = 0;
|
|
+ uint32_t m_tvram_wait = 0;
|
|
+ uint32_t m_vram_wait_accum = 0; // 16.16 carry between accesses
|
|
emu_timer* m_mouse_timer = nullptr;
|
|
emu_timer* m_led_timer = nullptr;
|
|
unsigned char m_scc_prev = 0;
|
|
@@ -265,6 +271,15 @@
|
|
uint16_t exp_r(offs_t offset, uint16_t mem_mask = ~0);
|
|
void exp_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
|
|
|
+ // VRAM wait states. The X68000's video RAM is slower than main memory;
|
|
+ // MAME charged nothing for it, so VRAM-bound code ran optimistically
|
|
+ // fast. These trampolines charge the difference and forward to the CRTC.
|
|
+ void charge_vram_wait(uint32_t increment);
|
|
+ uint16_t gvram_wait_r(offs_t offset);
|
|
+ void gvram_wait_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
|
+ uint16_t tvram_wait_r(offs_t offset);
|
|
+ void tvram_wait_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
|
+
|
|
uint16_t spritereg_r(offs_t offset);
|
|
void spritereg_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
|
uint16_t spriteram_r(offs_t offset);
|
|
--- a/src/mame/sharp/x68k.cpp 2026-08-04 16:54:53.871522864 -0500
|
|
+++ b/src/mame/sharp/x68k.cpp 2026-08-04 16:54:53.876522842 -0500
|
|
@@ -992,12 +992,80 @@
|
|
// Is this an undocumented MB89352 feature, an ASIC register, an original code bug or a bad dump?
|
|
}
|
|
|
|
+// VRAM wait states.
|
|
+//
|
|
+// The 68000 in an X68000 does not reach video RAM as fast as it reaches main
|
|
+// memory, and MAME modelled no wait at all -- so any VRAM-bound inner loop ran
|
|
+// optimistically fast, which is exactly the code a graphics library cares
|
|
+// about. The figures used here are Makoto Kamada's real-hardware measurements
|
|
+// distributed with XEiJ (credited there to uchopon, tnb and ita), taken by
|
|
+// timing tst.w against each region on five 10 MHz X68000 variants:
|
|
+//
|
|
+// region tst.w time (us) derived wait (cycles)
|
|
+// main memory 1.642 .. 1.663 0.123
|
|
+// GVRAM 1.734 .. 1.770 1.145
|
|
+// TVRAM 1.829 .. 1.850 2.046
|
|
+//
|
|
+// MAME charges nothing for main memory, so what is added here is the wait
|
|
+// RELATIVE to main memory: 1.145 - 0.123 = 1.022 cycles for GVRAM and
|
|
+// 2.046 - 0.123 = 1.923 for TVRAM. Cross-check against the raw measurements:
|
|
+// GVRAM - main memory averages ~1.03 cycles and TVRAM - main memory ~1.92
|
|
+// across the five machines, which agrees.
|
|
+//
|
|
+// Main memory's own 0.123 cycles (DRAM refresh) is deliberately NOT modelled:
|
|
+// it would mean interposing on the hottest path in the driver to recover ~0.1
|
|
+// cycles per access, and it is a uniform offset that does not change the
|
|
+// relative cost of VRAM versus RAM, which is the thing being measured.
|
|
+//
|
|
+// The wait is fractional, so it accumulates in 16.16 fixed point and whole
|
|
+// cycles are charged as they carry out -- the same technique the apple2gs
|
|
+// driver uses for its 1 MHz cycles (see slow_cycle() there).
|
|
+#define X68K_GVRAM_WAIT 0x000105a2 // 1.022 cycles, 16.16
|
|
+#define X68K_TVRAM_WAIT 0x0001ec4a // 1.923 cycles, 16.16
|
|
+
|
|
+void x68k_state::charge_vram_wait(uint32_t increment)
|
|
+{
|
|
+ // side_effects_disabled() covers debugger and Lua reads, which must not
|
|
+ // perturb the timing they are measuring.
|
|
+ if (increment == 0 || machine().side_effects_disabled())
|
|
+ return;
|
|
+ m_vram_wait_accum += increment;
|
|
+ int const cycles = m_vram_wait_accum >> 16;
|
|
+ m_vram_wait_accum &= 0xffff;
|
|
+ if (cycles != 0)
|
|
+ m_maincpu->adjust_icount(-cycles);
|
|
+}
|
|
+
|
|
+uint16_t x68k_state::gvram_wait_r(offs_t offset)
|
|
+{
|
|
+ charge_vram_wait(m_gvram_wait);
|
|
+ return m_crtc->gvram_r(offset);
|
|
+}
|
|
+
|
|
+void x68k_state::gvram_wait_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
|
+{
|
|
+ charge_vram_wait(m_gvram_wait);
|
|
+ m_crtc->gvram_w(offset, data, mem_mask);
|
|
+}
|
|
+
|
|
+uint16_t x68k_state::tvram_wait_r(offs_t offset)
|
|
+{
|
|
+ charge_vram_wait(m_tvram_wait);
|
|
+ return m_crtc->tvram_r(offset);
|
|
+}
|
|
+
|
|
+void x68k_state::tvram_wait_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
|
+{
|
|
+ charge_vram_wait(m_tvram_wait);
|
|
+ m_crtc->tvram_w(offset, data, mem_mask);
|
|
+}
|
|
+
|
|
void x68k_state::x68k_base_map(address_map &map)
|
|
{
|
|
map(0x000000, 0xbffffb).rw(FUNC(x68k_state::emptyram_r), FUNC(x68k_state::emptyram_w));
|
|
map(0xbffffc, 0xbfffff).rw(FUNC(x68k_state::rom0_r), FUNC(x68k_state::rom0_w));
|
|
- map(0xc00000, 0xdfffff).rw(m_crtc, FUNC(x68k_crtc_device::gvram_r), FUNC(x68k_crtc_device::gvram_w));
|
|
- map(0xe00000, 0xe7ffff).rw(m_crtc, FUNC(x68k_crtc_device::tvram_r), FUNC(x68k_crtc_device::tvram_w));
|
|
+ map(0xc00000, 0xdfffff).rw(FUNC(x68k_state::gvram_wait_r), FUNC(x68k_state::gvram_wait_w));
|
|
+ map(0xe00000, 0xe7ffff).rw(FUNC(x68k_state::tvram_wait_r), FUNC(x68k_state::tvram_wait_w));
|
|
map(0xe80000, 0xe81fff).rw(m_crtc, FUNC(x68k_crtc_device::crtc_r), FUNC(x68k_crtc_device::crtc_w));
|
|
map(0xe82400, 0xe83fff).rw(FUNC(x68k_state::vid_r), FUNC(x68k_state::vid_w));
|
|
map(0xe84000, 0xe85fff).rw(m_hd63450, FUNC(hd63450_device::read), FUNC(hd63450_device::write));
|
|
@@ -1155,6 +1223,24 @@
|
|
m_spriteram = (uint16_t*)(memregion("user1")->base());
|
|
space.install_ram(0x000000,m_ram->size()-1,m_ram->pointer());
|
|
|
|
+ // VRAM wait states, applied only to the machine the measurements cover.
|
|
+ // The published figures are for a 10 MHz 68000 X68000; XEiJ's table also
|
|
+ // lists a 25 MHz X68030 (GVRAM 6.377, TVRAM 6.623) but those machines are
|
|
+ // MACHINE_NOT_WORKING here and cannot be validated, so they are left
|
|
+ // unmodelled rather than given invented numbers.
|
|
+ if (m_maincpu->clock() == 10'000'000)
|
|
+ {
|
|
+ m_gvram_wait = X68K_GVRAM_WAIT;
|
|
+ m_tvram_wait = X68K_TVRAM_WAIT;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m_gvram_wait = 0;
|
|
+ m_tvram_wait = 0;
|
|
+ }
|
|
+ m_vram_wait_accum = 0;
|
|
+ save_item(NAME(m_vram_wait_accum));
|
|
+
|
|
// start mouse timer
|
|
m_mouse_timer->adjust(attotime::zero, 0, attotime::from_msec(1)); // a guess for now
|
|
m_mouse.inputtype = 0;
|