docs: address units resolved from MAME source (map is in BITS; bytes = bit>>3) + two corrections

This commit is contained in:
SDD committed 2026-10-16 10:18:01 +03:00
1 parent 93fb92d807
commit 99ca146fe3
2 files changed
+114 -60

No files matched your search

+50 -2
View File
@@ -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.
+64 -58
View File
@@ -70,6 +70,7 @@
#include <cstring>
#include <map>
#include <string>
#include <tuple>
#include <utility>
#include "vt1200_board.h"
@@ -160,21 +161,28 @@ private:
optional_shared_ptr<u16> m_vram;
// VT1200_CAL=<path>
// 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<std::pair<u32, u32>, u32> m_cal_seen;
std::map<std::tuple<u32, u32, u32>, u32> m_cal_seen; // (base, off, pc) -> count
std::vector<u16> 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=<path>
// 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