harness: locate video memory at 0x00400000 via an unmapped-access catch-all

This commit is contained in:
SDD committed 2026-10-16 09:45:28 +03:00
1 parent 170d07f0a2
commit fe978dee18
7 files changed
+482475 -1

No files matched your search

+358898
View File
File diff suppressed because it is too large. Load diff
+52 -1
View File
@@ -543,11 +543,62 @@ So each window is a **same-shaped controller**:
That means the model is **one generic register file instantiated per window**, not four
unrelated blocks — and it explains why the same shapes keep appearing.
## Video memory FOUND — `0x00400000` (2026-10-16)
Found empirically, not by static sweep. A new catch-all mapping
(`VT1200_LOG_UNMAPPED=<path>`) logs every access to memory the driver does **not**
model. With it, the firmware's unmapped traffic collapses to exactly one region:
```
range: 0x00400000 .. 0x0040D7FF (map units; 1 unit = 1 bit, 8 per byte)
writers: 117,002 W from pc=3F204A20 (file 0x944 -- the POST blit, right after FILL XY)
readers: 6,409 R from pc=3FDF9A00 (file 0x17F340 -- the display/blit code,
reading at an 8-byte stride)
also: pc=3FDF97A0 (file 0x17F2F4)
```
That is the region the spike independently ranked as its VRAM candidate
(`MOVI 0x4000000,B4` in video init). Two independent methods agree.
**Why the screen is black:** we never mapped it, so the blitter's writes vanished and its
reads returned zero.
**Now mapped** under `VT1200_VRAM=1` (`map(0x00400000, 0x0047ffff).ram().share("vram")`,
i.e. 512 K bits = 64 KiB of video memory, gated so the no-env baseline is untouched), with
`VT1200_DUMP_VRAM=<frame>` to dump it.
**Dumped at frames 5, 30, 120 and 400: all zero.** That is *consistent*, not a bug — the
writes are clearing/testing video RAM (a memory test writes and reads back), and the
firmware has not yet drawn anything because it is stuck early. So the blitter region is
identified, but **no picture can appear until the firmware gets far enough to draw one.**
### Method notes worth keeping
- `map()` entries are built at machine-**configuration** time, so a mapping gated on a
pointer initialised in `machine_start()` is never registered. Gate on `getenv()`.
- The register-first catch-all works: real regions added after it take precedence, which
is why the firmware still boots unchanged while the catch-all is installed.
- Map units are **bits** (8 per byte) — confirmed by a 0x80000-unit `.ram()` reporting
`bytes() == 65536`.
---
## Next steps
(Design work now — see the design spec once approved.)
(Design work — see the design spec.)
1. **Getting a picture requires unblocking the firmware.** Video memory is located
(`0x00400000`) and mapped, and the blitter's region is known, but the firmware only
*clears/tests* it — it never draws, because it is still stuck early. The rendering
pipeline is the least of it; the blocker is the same one as before: `INTENB` never
leaves zero.
2. **Strongest known unmodelled region:** `0x5600000` (~110 static accesses, display/X-init
path, unreached while stuck).
3. **Write the scanline renderer** — it must read video memory rather than draw nothing.
Note the display's `DPYADR` gives `rowaddr = 0x1B0` under MAME's quirk, and the
framebuffer geometry needs confirming against `0x00400000`.
4. Then the two remaining follow-on plans: video RAM + renderer, and DUART / NVRAM / MAC ROM.
1. **Design the generic register-file model** for the four windows: STATUS at `+0x10`,
COMMAND/DATA at `+0x20`, mixed I/O at `+0xD0`, latched outputs elsewhere.
File diff suppressed because it is too large. Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
+112
View File
@@ -93,6 +93,7 @@ public:
, m_screen(*this, "screen")
, m_palette(*this, "palette")
, m_mainram(*this, "mainram")
, m_vram(*this, "vram")
{
}
@@ -106,6 +107,7 @@ public:
const tms340x0_device::display_params *params);
void dump_frame(screen_device &screen, bitmap_ind16 &bitmap);
void dump_ram();
void dump_vram();
// We drive the TMS34010 renderer ourselves so the frame dump does not
// depend on the device's scanline-callback plumbing.
@@ -150,6 +152,24 @@ private:
// vt1200-ram-<frame>.bin so the framebuffer can be located offline.
long m_dump_ram = -1;
bool m_dumped_ram = false;
// VT1200_DUMP_VRAM=<frame> writes the video memory to vram-<frame>.bin so the
// framebuffer layout can be determined offline.
long m_dump_vram = -1;
bool m_dumped_vram = false;
optional_shared_ptr<u16> m_vram;
// VT1200_LOG_UNMAPPED=<path>
// A catch-all mapping over the whole address space, registered FIRST so every
// real region added afterwards takes precedence. Anything the firmware
// touches that we do not model lands here, and is logged once per distinct
// (address, PC, direction). This is how we find regions we have never
// mapped -- the video memory in particular.
FILE *m_unmapped_log = nullptr;
std::map<std::pair<u32, u32>, u32> m_unmapped_seen; // (addr, pc|dir) -> count
u16 unmapped_r(offs_t off);
void unmapped_w(offs_t off, u16 data);
void log_unmapped(const char *kind, u32 addr, const char *pc);
};
void vt1200_state::machine_start()
@@ -226,9 +246,20 @@ void vt1200_state::machine_start()
m_dump_frame = strtol(f, nullptr, 0);
if (const char *f = getenv("VT1200_DUMP_RAM"))
m_dump_ram = strtol(f, nullptr, 0);
if (const char *f = getenv("VT1200_DUMP_VRAM"))
m_dump_vram = strtol(f, nullptr, 0);
if (const char *d = getenv("VT1200_DUMP_DIR"))
m_dump_dir = d;
if (const char *p = getenv("VT1200_LOG_UNMAPPED"))
{
m_unmapped_log = fopen(p, "w");
if (m_unmapped_log)
fprintf(m_unmapped_log,
"# kind addr pc -- accesses to memory the driver does NOT model\n"
"# address units are the address-map's; firmware operand = addr << 4\n");
}
logerror("vt1200: board model=%s busy_ticks=%s busy_w2=%s busy_w3=%s dma_ticks=%s switch=%s log=%s render=%d dump=%ld\n",
getenv("VT1200_BOARD_MODEL") ? getenv("VT1200_BOARD_MODEL") : "off",
getenv("VT1200_BOARD_BUSY_TICKS") ? getenv("VT1200_BOARD_BUSY_TICKS") : "0",
@@ -243,10 +274,62 @@ void vt1200_state::machine_reset()
{
if (m_log)
fflush(m_log);
if (m_unmapped_log)
fflush(m_unmapped_log);
}
void vt1200_state::log_unmapped(const char *kind, u32 addr, const char *pc)
{
if (!m_unmapped_log)
return;
const u32 p = (u32)strtoul(pc, nullptr, 16);
const char d = (kind[0] == 'W') ? 1u : 0u;
auto key = std::make_pair(addr, (p << 1) | d);
auto it = m_unmapped_seen.find(key);
if (it == m_unmapped_seen.end())
{
m_unmapped_seen.emplace(key, 1);
fprintf(m_unmapped_log, "%s %08X pc=%08X\n", kind, addr, p);
fflush(m_unmapped_log);
}
}
u16 vt1200_state::unmapped_r(offs_t off)
{
char pc[16];
snprintf(pc, sizeof(pc), "%08X", (unsigned)m_maincpu->state_int(TMS34010_PC));
log_unmapped("R", (u32)off, pc);
return 0x0000;
}
void vt1200_state::unmapped_w(offs_t off, u16 data)
{
char pc[16];
snprintf(pc, sizeof(pc), "%08X", (unsigned)m_maincpu->state_int(TMS34010_PC));
log_unmapped("W", (u32)off, pc);
}
void vt1200_state::program_map(address_map &map)
{
// Catch-all for anything we do not model -- registered FIRST so every region
// below takes precedence. Anything still landing here is memory the
// firmware touches that the driver has never mapped; it is logged.
// NOTE: gated on the environment variable, not on m_unmapped_log, because
// program_map() runs at machine-configuration time and machine_start()
// (which opens the file) has not run yet.
if (getenv("VT1200_LOG_UNMAPPED"))
map(0x00000000, 0xffffffff).rw(FUNC(vt1200_state::unmapped_r), FUNC(vt1200_state::unmapped_w));
// Video memory. Discovered empirically: the firmware's blit paths
// (PC 3F204A20 right after FILL XY, and 3FDF97A0 / 3FDF9A00 in the display
// code) read and write 0x00400000..0x0040D7FF -- memory the driver never
// mapped, which is exactly why the screen renders black: the blits went
// nowhere and reads returned 0. Gated on an env var while we characterise
// its size and layout, so the established no-env baseline is preserved.
if (getenv("VT1200_VRAM"))
map(0x00400000, 0x0047ffff).ram().share("vram");
// Main RAM: 4 MiB (1 MiB onboard + 3 x 1 MiB SIMMs on the real machine).
// The TMS34010 is bit-addressed, so 4 MiB of bytes == 0x2000000 address
// units. (The earlier 0x200000 range modelled only 256 KiB, which is
@@ -354,6 +437,11 @@ void vt1200_state::scanline(screen_device &screen, bitmap_ind16 &bitmap, int sca
m_dumped_ram = true;
dump_ram();
}
if (m_dump_vram >= 0 && !m_dumped_vram && (long)screen.frame_number() >= m_dump_vram)
{
m_dumped_vram = true;
dump_vram();
}
}
}
@@ -439,6 +527,30 @@ void vt1200_state::dump_ram()
n, m_dump_ram, path);
}
void vt1200_state::dump_vram()
{
char path[1024];
snprintf(path, sizeof(path), "%s/vram-%ld.bin", m_dump_dir.c_str(), m_dump_vram);
FILE *fp = fopen(path, "wb");
if (!fp)
{
osd_printf_info("vt1200: cannot write %s\n", path);
return;
}
if (!m_vram)
{
osd_printf_info("vt1200: VT1200_VRAM not mapped; nothing to dump\n");
fclose(fp);
return;
}
const u8 *base = reinterpret_cast<const u8 *>(m_vram.target());
const size_t n = m_vram.bytes();
fwrite(base, 1, n, fp);
fclose(fp);
osd_printf_info("vt1200: dumped %zu bytes of video RAM (frame %ld) -> %s\n",
n, m_dump_vram, path);
}
void vt1200_state::vt1200(machine_config &config)
{
// Clocks, derived from the real machine's 1280x1024 @ 72 Hz mono display and