From 99ca146fe304cae9af3d3221603a05ff4a16a525 Mon Sep 17 00:00:00 2001 From: SDD Date: Fri, 16 Oct 2026 10:18:01 +0300 Subject: [PATCH] docs: address units resolved from MAME source (map is in BITS; bytes = bit>>3) + two corrections --- harness/mame/BRINGUP_LOG.md | 52 ++++++++++++++- harness/mame/vt1200.cpp | 122 +++++++++++++++++++----------------- 2 files changed, 114 insertions(+), 60 deletions(-) diff --git a/harness/mame/BRINGUP_LOG.md b/harness/mame/BRINGUP_LOG.md index 50c0dba..1146b09 100644 --- a/harness/mame/BRINGUP_LOG.md +++ b/harness/mame/BRINGUP_LOG.md @@ -721,9 +721,57 @@ cross-check: run the catch-all and a layered proxy over the **same** range in th run, and see which one reports. Whichever is trustworthy calibrates the rest; the other gets fixed or discarded. +## ADDRESS UNITS RESOLVED — the map is in BITS (2026-10-16) + +Authoritative, from MAME's own source rather than inference: + +``` +src/devices/cpu/tms34010/tms34010.h:991 + memory_access<32, 1, 3, ENDIANNESS_LITTLE>::specific m_program; + ^ data_width = 1 BIT + +src/emu/emumem.h:2078 + inline offs_t addr2byte(offs_t address) const { + return (m_addr_shift < 0) ? (address << -m_addr_shift) : (address >> m_addr_shift); + } +``` + +* The TMS34010 program space declares **`data_width = 1`**, so **address-map units are + BITS**, not bytes and not words. +* `addr2byte(addr) = addr >> 3` → **bytes = bit address / 8**. +* This reconciles the measurement that a `.ram()` over `0x00000000-0x01FFFFFF` reports + `bytes() == 0x400000`: `0x2000000 >> 3 == 0x400000` (4 MiB). The units were consistent + all along; only my interpretation of them was not. + +**Read every address in this file, in the driver's map, and in the logs as a BIT address, +and divide by 8 for bytes.** So the video-region traffic the catch-all reported at +`0x400000`-`0x40D7FF` means bytes `0x80000`-`0x81AFF` — a **64 KiB** region, which is a +plausible VRAM size and the first time that number has rested on something solid. + +### Two corrections this forced + +1. **`DETECT_OVERLAPPING_MEMORY` is `(0)`** (`src/emu/addrmap.cpp:20`). MAME silently + permits overlapping map entries and never warns. An earlier step in this project used + "no overlap warning appeared" as evidence that two entries did not overlap — **that + evidence was void.** +2. **Handlers receive the offset within their own entry.** `populate_map_entry` installs + entries in list order (`emumem_mview.cpp:478-481`) and MAME subtracts the entry's start, + so a start-`0` entry (RAM, the catch-all, the calibration proxies) sees the absolute + address, while the board windows (start `0x06200000`) see `address - 0x06200000`. + +### Still open — and now sharply posed + +With bit units, RAM genuinely covers bit `0x400000`, so the catch-all should never have seen +traffic there; yet it logged 123,411 accesses. The precedence rule must be subtler than +"later wins". MAME has a distinct path, `populate_mismatched(...)`, for handler-tree +conflicts (`emumem_mview.cpp:257` and `:293`) — the open question is whether resolution +differs when the overlapping handlers have the **same** width versus **different** widths. +A dedicated cross-check is measuring exactly that. + --- ## Next steps -CLOSE THE INSTRUMENT DISAGREEMENT FIRST (one cross-check run). -Then model video memory and re-run the level-1 check via the console capture. +1. Settle the overlap/precedence rule (cross-check in flight). +2. Model video memory using BIT addresses; the region is ~64 KiB starting at bit 0x400000. +3. Re-run the level-1 console check after each change. diff --git a/harness/mame/vt1200.cpp b/harness/mame/vt1200.cpp index d5ecd30..6fbfb1d 100644 --- a/harness/mame/vt1200.cpp +++ b/harness/mame/vt1200.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include #include "vt1200_board.h" @@ -160,21 +161,28 @@ private: optional_shared_ptr m_vram; // VT1200_CAL= - // Address-unit and map-precedence calibration. Registers a PROXY over: - // (a) a low-RAM slice 0x00000000-0x0000FFFF -- functionally identical to - // RAM (a backing array) but logging. If this logs, later entries win - // over earlier ones and handlers can be layered; if it never logs, - // earlier entries win. - // (b) three candidate encodings of the video region, to discover which one - // the blitter actually addresses. - // All registered at the END of program_map so precedence is unambiguous. + // Address-unit and map-precedence cross-check. Each probe is registered at + // the END of program_map (so, if precedence is last-wins, it wins its + // slice) and logs the delegate offset `off` it is handed. It also prints + // the space address reconstructed under the two candidate unit rules: + // space4 = base + (off << 4) (delegate off = space-bit-address >> 4) + // space0 = base + off (delegate off = space-bit-address) + // Whichever rule yields a coherent picture for the positive control is the + // real one. See XCROSS_REPORT.md. FILE *m_cal_log = nullptr; - std::map, u32> m_cal_seen; + std::map, u32> m_cal_seen; // (base, off, pc) -> count std::vector m_cal_ram; - int m_cal_ram_lo = 0; - u16 cal_r(offs_t off); - void cal_w(offs_t off, u16 data); - void log_cal(const char *tag, char kind, u32 addr); + void log_probe(const char *tag, char kind, u32 off, u32 base); + + // One read/write pair per probe so each knows its own base address. These + // bases are SPACE (bit) addresses, matching the driver's map entries. + u16 calctrl_r(offs_t off); void calctrl_w(offs_t off, u16 data); // 0x00000000 control + u16 calvida_r(offs_t off); void calvida_w(offs_t off, u16 data); // 0x00400000 (old VID-A) + u16 calvid4m_r(offs_t off); void calvid4m_w(offs_t off, u16 data); // 0x04000000 (0x400000<<4) + u16 calram80_r(offs_t off); void calram80_w(offs_t off, u16 data); // 0x00800000 + u16 calram100_r(offs_t off); void calram100_w(offs_t off, u16 data); // 0x01000000 + u16 calram180_r(offs_t off); void calram180_w(offs_t off, u16 data); // 0x01800000 + u16 calram200_r(offs_t off); void calram200_w(offs_t off, u16 data); // 0x02000000 (outside RAM) // VT1200_LOG_UNMAPPED= // A catch-all mapping over the whole address space, registered FIRST so every @@ -294,54 +302,46 @@ void vt1200_state::machine_start() m_log ? "on" : "off", m_render ? 1 : 0, m_dump_frame); } -void vt1200_state::log_cal(const char *tag, char kind, u32 addr) +void vt1200_state::log_probe(const char *tag, char kind, u32 off, u32 base) { if (!m_cal_log) return; const u32 p = (u32)m_maincpu->state_int(TMS34010_PC); - auto key = std::make_pair(addr, ((p << 8) | (u8)kind)); + auto key = std::make_tuple(base, off, (p << 8) | (u8)kind); if (m_cal_seen.find(key) == m_cal_seen.end()) { m_cal_seen.emplace(key, 1); - fprintf(m_cal_log, "%s %c %08X pc=%08X\n", tag, kind, addr, p); + // space4: the rule "delegate off = space address >> 4". space0: off is + // already the space address. Only one of them can be right for a run. + fprintf(m_cal_log, "%s %c off=%08X space4=%08X space0=%08X pc=%08X\n", + tag, kind, off, base + (off << 4), base + off, p); fflush(m_cal_log); } } -static const char *cal_tag(u32 addr) -{ - if (addr >= 0x00400000 && addr <= 0x00400fff) return "VID-A"; - if (addr >= 0x00040000 && addr <= 0x00040fff) return "VID-B"; - if (addr >= 0x00004000 && addr <= 0x00004fff) return "VID-C"; - // Catch-all address 0x400000 (reported in bits) divided by 8: - if (addr >= 0x00080000 && addr <= 0x00080fff) return "VID-D"; - if (addr >= 0x00020000 && addr <= 0x00020fff) return "VID-E"; - return "ABOVE2M"; -} +// The control probe shadows the very bottom of RAM (bit 0..0xffff = byte +// 0..0x1fff). It behaves like RAM via a small backing array so it perturbs as +// little as possible, and it MUST fire if last-wins precedence holds. +u16 vt1200_state::calctrl_r(offs_t off) { log_probe("CTRL", 'R', (u32)off, 0x00000000); return m_cal_ram[(u32)off % m_cal_ram.size()]; } +void vt1200_state::calctrl_w(offs_t off, u16 data) { log_probe("CTRL", 'W', (u32)off, 0x00000000); m_cal_ram[(u32)off % m_cal_ram.size()] = data; } -u16 vt1200_state::cal_r(offs_t off) -{ - const u32 addr = (u32)off; - if (addr < 0x10000) - { - log_cal("RAMSLICE", 'R', addr); - return m_cal_ram[addr]; - } - log_cal(cal_tag(addr), 'R', addr); - return 0; -} +u16 vt1200_state::calvida_r(offs_t off) { log_probe("VID-A-0x00400000", 'R', (u32)off, 0x00400000); return 0; } +void vt1200_state::calvida_w(offs_t off, u16 data) { log_probe("VID-A-0x00400000", 'W', (u32)off, 0x00400000); } -void vt1200_state::cal_w(offs_t off, u16 data) -{ - const u32 addr = (u32)off; - if (addr < 0x10000) - { - log_cal("RAMSLICE", 'W', addr); - m_cal_ram[addr] = data; - return; - } - log_cal(cal_tag(addr), 'W', addr); -} +u16 vt1200_state::calvid4m_r(offs_t off) { log_probe("VID-4M-0x04000000", 'R', (u32)off, 0x04000000); return 0; } +void vt1200_state::calvid4m_w(offs_t off, u16 data) { log_probe("VID-4M-0x04000000", 'W', (u32)off, 0x04000000); } + +u16 vt1200_state::calram80_r(offs_t off) { log_probe("RAM-0x00800000", 'R', (u32)off, 0x00800000); return 0; } +void vt1200_state::calram80_w(offs_t off, u16 data) { log_probe("RAM-0x00800000", 'W', (u32)off, 0x00800000); } + +u16 vt1200_state::calram100_r(offs_t off) { log_probe("RAM-0x01000000", 'R', (u32)off, 0x01000000); return 0; } +void vt1200_state::calram100_w(offs_t off, u16 data) { log_probe("RAM-0x01000000", 'W', (u32)off, 0x01000000); } + +u16 vt1200_state::calram180_r(offs_t off) { log_probe("RAM-0x01800000", 'R', (u32)off, 0x01800000); return 0; } +void vt1200_state::calram180_w(offs_t off, u16 data) { log_probe("RAM-0x01800000", 'W', (u32)off, 0x01800000); } + +u16 vt1200_state::calram200_r(offs_t off) { log_probe("RAM-0x02000000", 'R', (u32)off, 0x02000000); return 0; } +void vt1200_state::calram200_w(offs_t off, u16 data) { log_probe("RAM-0x02000000", 'W', (u32)off, 0x02000000); } void vt1200_state::machine_reset() { @@ -417,19 +417,25 @@ void vt1200_state::program_map(address_map &map) map(0xbf200000, 0xbfffffff).rom().region("maincpu", 0); map(0xff200000, 0xffffffff).rom().region("maincpu", 0); - // Calibration probes. Registered last so precedence is unambiguous. + // Calibration cross-check. Every probe is registered LAST, so under + // last-wins it wins its own slice. Bases are SPACE (bit) addresses. if (getenv("VT1200_CAL")) { m_cal_ram.assign(0x10000, 0); - map(0x00000000, 0x0000ffff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - map(0x00400000, 0x00400fff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - map(0x00040000, 0x00040fff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - map(0x00004000, 0x00004fff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - map(0x00080000, 0x00080fff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - map(0x00020000, 0x00020fff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); - // Broad net: everything above 2 MiB that we do not already map. RAM - // rarely reaches here, so a log-only proxy is survivable. - map(0x00200000, 0x01ffffff).rw(FUNC(vt1200_state::cal_r), FUNC(vt1200_state::cal_w)); + // Positive control: proven to fire. Last-wins -> this shadows the + // bottom 64K bits of RAM. + map(0x00000000, 0x0000ffff).rw(FUNC(vt1200_state::calctrl_r), FUNC(vt1200_state::calctrl_w)); + // The original disputed "video" range (a space address inside RAM). + map(0x00400000, 0x004fffff).rw(FUNC(vt1200_state::calvida_r), FUNC(vt1200_state::calvida_w)); + // The catch-all's logged 0x400000 read as a space address, times 16: + // 0x400000 << 4 = 0x04000000. + map(0x04000000, 0x040fffff).rw(FUNC(vt1200_state::calvid4m_r), FUNC(vt1200_state::calvid4m_w)); + // Where does RAM actually end? Probes at three MiB marks of the + // declared 4 MiB range, plus one OUTSIDE it (0x02000000). + map(0x00800000, 0x00800fff).rw(FUNC(vt1200_state::calram80_r), FUNC(vt1200_state::calram80_w)); + map(0x01000000, 0x01000fff).rw(FUNC(vt1200_state::calram100_r), FUNC(vt1200_state::calram100_w)); + map(0x01800000, 0x01800fff).rw(FUNC(vt1200_state::calram180_r), FUNC(vt1200_state::calram180_w)); + map(0x02000000, 0x02000fff).rw(FUNC(vt1200_state::calram200_r), FUNC(vt1200_state::calram200_w)); } // Undecoded board windows. Left unmapped unless an experiment asks for