mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
harness: locate video memory at 0x00400000 via an unmapped-access catch-all
This commit is contained in:
1 parent
170d07f0a2
commit
fe978dee18
7 files changed
+482475
-1
No files matched your search
@@ -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.
|
||||
|
||||
+123413
File diff suppressed because it is too large.
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
Reference in new issue
Block a user