forked from opencode.energia.arti.ee/vt1200
101 lines
4.3 KiB
C++
101 lines
4.3 KiB
C++
// VT1200 board register model. Deliberately free of MAME dependencies so it can
|
|
// be unit-tested with plain g++.
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <map>
|
|
|
|
// Window bases in the harness's address units (disassembly operand >> 4).
|
|
static const uint32_t VT1200_WIN0 = 0x05800000; // data port of the DMA pair
|
|
static const uint32_t VT1200_WIN1 = 0x05900000; // command port of the DMA pair
|
|
static const uint32_t VT1200_WIN2 = 0x06200000; // big: STATUS +0x10, CMD +0x20
|
|
static const uint32_t VT1200_WIN3 = 0x06400000; // big, plus the monitor switch
|
|
|
|
// Window-relative register offsets IN HARNESS UNITS.
|
|
//
|
|
// The harness address is the firmware's operand shifted right by 4 (MAME's
|
|
// TMS34010 space is word-unit here), so an operand offset of +0x10 is harness
|
|
// +0x01, and an operand offset of +0xD0 is harness +0x0d. Getting this wrong
|
|
// makes registers silently unreachable, so always use these names.
|
|
//
|
|
// firmware operand harness offset role
|
|
// +0x10 0x01 STATUS (bit 0 = ready/done)
|
|
// +0x20 0x02 COMMAND / DATA
|
|
// +0x30 0x03 (read/written in the same block)
|
|
// +0xD0 0x0d mixed I/O -- monitor switch in bits 4-6
|
|
// +0xE0 0x0e
|
|
// +0xF0 0x0f
|
|
static const uint32_t VT1200_OFF_STATUS = 0x0001;
|
|
static const uint32_t VT1200_OFF_CMD = 0x0002;
|
|
static const uint32_t VT1200_OFF_IO30 = 0x0003;
|
|
static const uint32_t VT1200_OFF_SWITCH = 0x000d;
|
|
static const uint32_t VT1200_OFF_E0 = 0x000e;
|
|
static const uint32_t VT1200_OFF_F0 = 0x000f;
|
|
|
|
class vt1200_board
|
|
{
|
|
public:
|
|
vt1200_board() = default;
|
|
|
|
// Configuration (replaces the test-time env knobs).
|
|
void set_default(uint16_t v) { m_default = v; }
|
|
void set_override(uint32_t addr, uint16_t v) { m_override[addr] = v; }
|
|
void set_model_handshake(bool on) { m_handshake = on; }
|
|
|
|
// Per-window busy counts. Each of the four windows owns its own count, so
|
|
// the two big windows (0x06200000 and 0x06400000) can be tuned
|
|
// independently. set_busy_ticks is shorthand that applies to every window.
|
|
void set_busy_ticks(int n)
|
|
{
|
|
m_busy_ticks_by_win[VT1200_WIN0] = n;
|
|
m_busy_ticks_by_win[VT1200_WIN1] = n;
|
|
m_busy_ticks_by_win[VT1200_WIN2] = n;
|
|
m_busy_ticks_by_win[VT1200_WIN3] = n;
|
|
}
|
|
void set_window_busy_ticks(uint32_t window_base, int n) { m_busy_ticks_by_win[window_base] = n; }
|
|
|
|
// DMA command/data pair at WIN0 (data) / WIN1 (command). A write to the
|
|
// command port is accepted and reads back its value for n ticks; after the
|
|
// ticks elapse the command port reads back 0. n == 0 (the default) leaves
|
|
// the pair with no DMA semantics, so the port just behaves as a latch.
|
|
void set_dma_ticks(int n) { m_dma_ticks = n; }
|
|
|
|
// Monitor switch (bits 4-6 of the switch port). The argument is accepted
|
|
// unambiguously as EITHER the raw register bits (e.g. 0x70) OR a small
|
|
// position 0-7: a value above 0x07 is taken as the raw bit pattern, any
|
|
// value <= 0x07 is a position and is shifted into bits 4-6. The input mix
|
|
// is only applied to reads after this has been called.
|
|
void set_monitor_switch(uint8_t bits_or_position)
|
|
{
|
|
m_switch_bits = (uint16_t)(bits_or_position > 0x07
|
|
? bits_or_position : ((bits_or_position & 0x07) << 4));
|
|
m_switch_set = true;
|
|
}
|
|
|
|
// Bus interface.
|
|
uint16_t read(uint32_t addr);
|
|
void write(uint32_t addr, uint16_t data);
|
|
void tick();
|
|
|
|
private:
|
|
bool is_switch_port(uint32_t addr) const;
|
|
|
|
std::map<uint32_t, uint16_t> m_store; // write-back latches
|
|
std::map<uint32_t, uint16_t> m_override; // fixed values (experiments)
|
|
uint16_t m_default = 0;
|
|
bool m_handshake = false;
|
|
// Busy state machines, keyed by window base (addr & 0xffff0000): how many
|
|
// ticks each window stays busy after a command write, and the remaining
|
|
// count for the command currently in flight.
|
|
std::map<uint32_t, int> m_busy_ticks_by_win;
|
|
std::map<uint32_t, int> m_busy_left;
|
|
// DMA command/data pair. m_dma_ticks is the configured completion time;
|
|
// m_dma_left is how many ticks remain for the command in flight; m_dma_cmd
|
|
// is the value written to the command port. All three only have meaning
|
|
// when m_dma_ticks > 0.
|
|
int m_dma_ticks = 0;
|
|
int m_dma_left = 0;
|
|
uint16_t m_dma_cmd = 0;
|
|
bool m_switch_set = false; // set_monitor_switch was called
|
|
uint16_t m_switch_bits = 0;
|
|
};
|