summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/vt50/vt50.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/vt50/vt50.cpp')
-rw-r--r--src/devices/cpu/vt50/vt50.cpp802
1 files changed, 802 insertions, 0 deletions
diff --git a/src/devices/cpu/vt50/vt50.cpp b/src/devices/cpu/vt50/vt50.cpp
new file mode 100644
index 00000000000..a81e94b05d9
--- /dev/null
+++ b/src/devices/cpu/vt50/vt50.cpp
@@ -0,0 +1,802 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ DEC VT50/VT52 microprocessor emulation
+
+ The principal components of this custom TTL-based processor are
+ inelegantly divided between two PCBs: ROM, UART & Timing (RUT) and
+ Data Paths, Memory & Decoders. The UART present on the former board
+ is not included in this CPU emulation, which uses callbacks instead
+ (as for the keyboard, which is a separate component in the same case).
+ Opcodes may contain up to four instructions each, which are executed
+ sequentially during defined phases of the instruction cycle.
+
+ The machine cycle time (each instruction takes two cycles) is also the
+ time it takes to display one character. RAM addresses are determined
+ by the contents of the X and Y registers (plus one XOR gate applied to
+ bit 3 of the X output) for both displayed characters and programmed
+ data transfers. During non-blanked portions of of horizontal lines, X
+ (but not Y) is automatically incremented as each character is latched,
+ with the lowest 3 bits of the accumulator defining the character scan
+ line. The firmware uses the tail end of RAM as its scratchpad area.
+
+ The accumulator, X and Y registers are mostly implemented as 74193
+ up/down counters. There is no proper ALU, only a 7485 magnitude
+ comparator and an 8242 equality checker whose output is also used to
+ establish the position of the underline cursor.
+
+ RAM is 7 bits wide, even though the VT50's character generator can
+ only accept 6 bits. Most of the registers are also effectively 7 bits
+ wide, though unused eighth bits are physically present. PC is also
+ physically 12 bits wide, though only up to 10 bits are usable. Y is
+ only 4 bits wide on the VT50, which has a 12-line display; in order
+ to double the quantity of addressable RAM to allow for 24 lines, the
+ VT52 adds an extra flip-flop stage to Y and rejumpers the address
+ encoding circuit.
+
+ The mode flip-flop changes the meanings of the jump conditions and the
+ function of the constant load instruction, whose execution in mode 0
+ is conditioned on equality with the preincremented accumulator. Jumps,
+ if taken, load the highest two bits of the destination (which define
+ the ROM page) from a ripple counter whose value may be incremented by
+ the IROM instruction.
+
+ The done flip-flop is set any time data is committed to memory. Its
+ purpose is to ensure that only one in a sequence of consecutive
+ load instructions in the firmware's keyboard lookup routine is
+ actually executed.
+
+ While horizontal blanking is defined in hardware as 20 characters out
+ of every 100, vertical blanking periods are arbitrarily determined by
+ when the firmware decides to deactivate the video flip-flop, which
+ necessitates an awkward workaround since MAME's screen emulation
+ expects a definite value. The vertical and horizontal synchronization
+ pulses are also generated without regard to each other, which causes
+ the screen refresh period to be 256 lines in 60 Hz mode and 307.2
+ lines in 50 Hz mode. The unorthodox split structure of the timing
+ chain permits it to double as a baud rate generator.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "vt50.h"
+#include "vt50dasm.h"
+#include "screen.h"
+
+#define FIND_FIRST_LINE 0
+
+// device type definitions
+DEFINE_DEVICE_TYPE(VT50_CPU, vt50_cpu_device, "vt50_cpu", "DEC VT50 CPU")
+DEFINE_DEVICE_TYPE(VT52_CPU, vt52_cpu_device, "vt52_cpu", "DEC VT52 CPU")
+
+vt5x_cpu_device::vt5x_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int bbits, int ybits)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , device_video_interface(mconfig, *this)
+ , m_rom_config("program", ENDIANNESS_LITTLE, 8, 10, 0)
+ , m_ram_config("data", ENDIANNESS_LITTLE, 8, 6 + ybits, 0) // actually 7 bits wide
+ , m_baud_9600_callback(*this)
+ , m_vert_count_callback(*this)
+ , m_uart_rd_callback(*this, 0)
+ , m_uart_xd_callback(*this)
+ , m_ur_flag_callback(*this, 0)
+ , m_ut_flag_callback(*this, 0)
+ , m_ruf_callback(*this)
+ , m_key_up_callback(*this, 1)
+ , m_kclk_callback(*this, 1)
+ , m_frq_callback(*this, 1)
+ , m_bell_callback(*this)
+ , m_cen_callback(*this)
+ , m_csf_callback(*this, 1)
+ , m_ccf_callback(*this, 1)
+ , m_char_data_callback(*this, 0177)
+ , m_bbits(bbits)
+ , m_ybits(ybits)
+ , m_pc(0)
+ , m_rom_page(0)
+ , m_mode_ff(false)
+ , m_done_ff(false)
+ , m_ac(0)
+ , m_buffer(0)
+ , m_x(0)
+ , m_y(0)
+ , m_x8(false)
+ , m_cursor_ff(false)
+ , m_cursor_active(false)
+ , m_video_process(false)
+ , m_ram_do(0)
+ , m_t(0)
+ , m_write_ff(false)
+ , m_flag_test_ff(false)
+ , m_m2u_ff(false)
+ , m_bell_ff(false)
+ , m_load_pc(false)
+ , m_icount(0)
+ , m_horiz_count(0)
+ , m_vert_count(0)
+ , m_top_of_screen(false)
+ , m_current_line(0)
+ , m_first_line(~0)
+{
+ m_rom_config.m_is_octal = true;
+ m_ram_config.m_is_octal = true;
+}
+
+vt50_cpu_device::vt50_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : vt5x_cpu_device(mconfig, VT50_CPU, tag, owner, clock, 4, 4)
+{
+}
+
+vt52_cpu_device::vt52_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : vt5x_cpu_device(mconfig, VT52_CPU, tag, owner, clock, 7, 5)
+ , m_graphic_callback(*this)
+{
+}
+
+std::unique_ptr<util::disasm_interface> vt50_cpu_device::create_disassembler()
+{
+ return std::make_unique<vt50_disassembler>();
+}
+
+std::unique_ptr<util::disasm_interface> vt52_cpu_device::create_disassembler()
+{
+ return std::make_unique<vt52_disassembler>();
+}
+
+device_memory_interface::space_config_vector vt5x_cpu_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_rom_config),
+ std::make_pair(AS_DATA, &m_ram_config)
+ };
+}
+
+void vt5x_cpu_device::device_config_complete()
+{
+ if (!has_screen())
+ return;
+
+ if (!screen().has_screen_update())
+ screen().set_screen_update(*this, FUNC(vt5x_cpu_device::screen_update));
+
+ if (!screen().refresh_attoseconds())
+ screen().set_raw(clock(), 900, 128, 848, 256, 4, 244); // 60 Hz default parameters
+}
+
+void vt5x_cpu_device::device_start()
+{
+ // acquire address spaces
+ space(AS_PROGRAM).cache(m_rom_cache);
+ space(AS_DATA).cache(m_ram_cache);
+
+ screen().register_screen_bitmap(m_bitmap);
+ set_icountptr(m_icount);
+
+ state_add(VT5X_PC, "PC", m_pc).formatstr("%04O").mask(01777);
+ state_add(STATE_GENPC, "GENPC", m_pc).mask(01777).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).mask(01777).noshow();
+ state_add<u8>(STATE_GENFLAGS, "CURFLAGS", [this]() {
+ return (m_mode_ff ? 1 : 0) | (m_done_ff ? 2 : 0);
+ }).formatstr("%7s").noshow();
+ state_add(VT5X_PAGE, "PAGE", m_rom_page).mask(3);
+ state_add(VT5X_MODE, "MODE", m_mode_ff).noshow();
+ state_add(VT5X_DONE, "DONE", m_done_ff).noshow();
+ state_add(VT5X_AC, "AC", m_ac).formatstr("%03O").mask(0177);
+ state_add(VT5X_B, "B", m_buffer).formatstr(m_bbits > 6 ? "%03O" : "%02O").mask((1 << m_bbits) - 1);
+ state_add(VT5X_X, "X", m_x).formatstr("%03O").mask(0177);
+ state_add(VT5X_Y, "Y", m_y).formatstr("%02O").mask((1 << m_ybits) - 1);
+ state_add(VT5X_X8, "X8", m_x8);
+ state_add<u16>(VT5X_XYAD, "XYAD", [this]() { return translate_xy(); }).formatstr("%04O").mask((1 << (6 + m_ybits)) - 1);
+ state_add(VT5X_CFF, "CFF", m_cursor_ff);
+ state_add(VT5X_VID, "VID", m_video_process);
+
+ // save state
+ save_item(NAME(m_pc));
+ save_item(NAME(m_rom_page));
+ save_item(NAME(m_mode_ff));
+ save_item(NAME(m_done_ff));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_buffer));
+ save_item(NAME(m_x));
+ save_item(NAME(m_y));
+ save_item(NAME(m_x8));
+ save_item(NAME(m_cursor_ff));
+ save_item(NAME(m_cursor_active));
+ save_item(NAME(m_video_process));
+ save_item(NAME(m_ram_do));
+ save_item(NAME(m_t));
+ save_item(NAME(m_write_ff));
+ save_item(NAME(m_flag_test_ff));
+ save_item(NAME(m_m2u_ff));
+ save_item(NAME(m_bell_ff));
+ save_item(NAME(m_load_pc));
+ save_item(NAME(m_horiz_count));
+ save_item(NAME(m_vert_count));
+ save_item(NAME(m_top_of_screen));
+ save_item(NAME(m_current_line));
+#if FIND_FIRST_LINE
+ save_item(NAME(m_first_line));
+#else
+ (void)m_first_line;
+#endif
+}
+
+void vt5x_cpu_device::device_reset()
+{
+ m_pc = 0;
+ m_rom_page = 0;
+ m_video_process = false;
+
+ // CPU is initialized in weird state that does not allow first instruction to fully execute
+ m_t = 7;
+ m_flag_test_ff = false;
+ m_load_pc = false;
+
+ m_horiz_count = 0;
+ m_vert_count = 0;
+ m_top_of_screen = true;
+ m_current_line = 0;
+
+ m_baud_9600_callback(0);
+ m_vert_count_callback(0);
+}
+
+u32 vt5x_cpu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+void vt5x_cpu_device::draw_char_line()
+{
+ if (m_current_line < screen().visible_area().top() || m_current_line > screen().visible_area().bottom())
+ return;
+
+ u8 hc = (u8(m_horiz_count) >> 4) * 10 + (m_horiz_count & 15);
+ unsigned xc = ((hc >= 22 ? hc : hc + 100) - 22) * 9 + screen().visible_area().left();
+ if (xc > screen().visible_area().right() - 8)
+ return;
+
+ u32 *pix = &m_bitmap.pix(m_current_line, xc);
+ if (m_video_process && m_cursor_ff && m_cursor_active)
+ std::fill_n(pix, 9, rgb_t::white());
+ else if (!m_video_process || m_cursor_ff)
+ std::fill_n(pix, 9, rgb_t::black());
+ else
+ {
+ // CD6 is first shifted out; CD0 is last out
+ u8 vsr = m_char_data_callback(u16(m_ram_do) << 3 | (m_ac & 7)) | 0200;
+ for (int i = 0; i < 9; i++)
+ {
+ *pix++ = BIT(vsr, 7) ? rgb_t::black() : rgb_t::white();
+ vsr = (vsr << 1) | 1;
+ }
+ }
+}
+
+offs_t vt5x_cpu_device::translate_xy() const
+{
+ // A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
+ // Screen RAM, columns 0–63: y3 y2 y1 y0 x5 x4 x3 x2 x1 x0
+ // Screen RAM, columns 64–79: 1 1 y1 y0 y3 y2 x3 x2 x1 x0
+ // Scratchpad (not displayed): 1 1 y1 y0 1 1 x3 x2 x1 x0
+ const u8 x = m_x ^ (m_x8 ? 8 : 0);
+ const offs_t y_shifted = (offs_t(m_y) << (10 - m_ybits)) & 01700;
+ const offs_t page_sel = offs_t(m_y & ((1 << (m_ybits - 4)) - 1)) << 10;
+ if (BIT(x, 6) || (y_shifted & 01400) == 01400)
+ return (x & 0017) | (y_shifted & 01400) >> 4 | y_shifted | 01400 | page_sel;
+ else
+ return x | y_shifted | page_sel;
+}
+
+void vt5x_cpu_device::execute_te(u8 inst)
+{
+ if (BIT(inst, 3) && !BIT(inst, 7))
+ {
+ switch (inst & 0160)
+ {
+ case 0000:
+ // ZXZY
+ m_x = 0;
+ m_y = 0;
+ m_x8 = false;
+ break;
+
+ case 0020:
+ // X8
+ m_x8 = !m_x8;
+ break;
+
+ case 0040:
+ // IXDY
+ m_x = (m_x + 1) & 0177;
+ m_y = (m_y - 1) & ((1 << m_ybits) - 1);
+ break;
+
+ case 0060:
+ // IX
+ m_x = (m_x + 1) & 0177;
+ break;
+
+ case 0100:
+ // ZA
+ m_ac = 0;
+ break;
+
+ case 0120:
+ // M1
+ m_mode_ff = true;
+ break;
+
+ case 0140:
+ // ZX
+ m_x = 0;
+ m_x8 = false;
+ break;
+
+ case 0160:
+ // M0
+ m_mode_ff = false;
+ break;
+ }
+ }
+
+ if (m_m2u_ff)
+ {
+ m_uart_xd_callback(m_ram_do);
+ m_m2u_ff = false;
+ }
+
+ m_flag_test_ff = false;
+ m_load_pc = false;
+ if (!BIT(inst, 7))
+ m_done_ff = false;
+ else if (!m_mode_ff)
+ m_ac = (m_ac + 1) & 0177;
+}
+
+void vt5x_cpu_device::execute_tf(u8 inst)
+{
+ if (BIT(inst, 2) && !BIT(inst, 7))
+ {
+ switch (inst & 0160)
+ {
+ case 0000:
+ // DXDY
+ m_x = (m_x - 1) & 0177;
+ m_y = (m_y - 1) & ((1 << m_ybits) - 1);
+ break;
+
+ case 0020:
+ case 0040:
+ // IA or IA1
+ m_ac = (m_ac + 1) & 0177;
+ break;
+
+ case 0060:
+ // IY
+ m_y = (m_y + 1) & ((1 << m_ybits) - 1);
+ break;
+
+ case 0100:
+ // DY
+ m_y = (m_y - 1) & ((1 << m_ybits) - 1);
+ break;
+
+ case 0120:
+ // IROM
+ m_rom_page = (m_rom_page + 1) & 3;
+ break;
+
+ case 0140:
+ // DX
+ m_x = (m_x - 1) & 0177;
+ break;
+
+ case 0160:
+ // DA
+ m_ac = (m_ac - 1) & 0177;
+ break;
+ }
+ }
+
+ // RUF is transparently latched from U2M decode
+ m_ruf_callback((inst & 0362) == 0122 ? 0 : 1);
+}
+
+void vt5x_cpu_device::execute_tw(u8 inst)
+{
+ if ((inst & 0217) == 0)
+ {
+ switch (inst & 0160)
+ {
+ case 0000:
+ // SCFF
+ m_cursor_ff = true;
+ break;
+
+ case 0020:
+ // SVID
+ m_video_process = true;
+ break;
+
+ case 0040:
+ // B2Y
+ m_y = m_buffer & ((1 << m_ybits) - 1);
+ break;
+
+ case 0060:
+ // CBFF
+ m_bell_ff = !m_bell_ff;
+ m_bell_callback(m_bell_ff);
+ break;
+
+ case 0100:
+ // ZCAV
+ m_cursor_ff = false;
+ m_video_process = false;
+ break;
+
+ case 0120:
+ // LPB (TODO: load print shift register)
+ break;
+
+ case 0140:
+ // EPR
+ m_cen_callback(1);
+ break;
+
+ case 0160:
+ // HPR!ZY
+ m_cen_callback(0);
+ m_y = 0;
+ break;
+ }
+ }
+
+ if (BIT(inst, 0) && !BIT(inst, 7))
+ m_flag_test_ff = true;
+
+ // set FF for instructions that write to RAM
+ if (BIT(inst, 7))
+ m_write_ff = (m_mode_ff || m_ac >= m_ram_do) && !m_done_ff;
+ else
+ m_write_ff = (inst & 0162) == 0022 || (inst & 0162) == 0062 || (inst & 0162) == 0122;
+
+ // DONE is set by any RAM write, not just LD
+ if (m_write_ff)
+ m_done_ff = true;
+}
+
+void vt52_cpu_device::execute_tw(u8 inst)
+{
+ vt5x_cpu_device::execute_tw(inst);
+
+ // ZCAV also borrows from the upper half of AC on the VT52
+ if (inst == 0100)
+ m_ac = (m_ac - 020) & 0177;
+}
+
+void vt50_cpu_device::execute_tg(u8 inst)
+{
+ switch (inst & 0362)
+ {
+ case 0022:
+ // A2M
+ m_ram_do = m_ac;
+ break;
+
+ case 0062:
+ // L40M (http://catb.org/jargon/html/O/octal-forty.html)
+ m_ram_do = 040;
+ break;
+
+ case 0122:
+ // U2M
+ m_ram_do = m_uart_rd_callback() & 0177;
+ break;
+
+ default:
+ // LD (TODO: B/C masking in mode 0 is determined by optional jumpers)
+ m_ram_do = inst & (!m_mode_ff && m_cursor_ff ? 0037 : 0177);
+ break;
+ }
+
+ m_ram_cache.write_byte(translate_xy(), m_ram_do);
+ m_write_ff = false;
+}
+
+void vt52_cpu_device::execute_tg(u8 inst)
+{
+ switch (inst & 0362)
+ {
+ case 0022:
+ // A2M
+ m_ram_do = m_ac;
+ break;
+
+ case 0062:
+ // B2M
+ m_ram_do = m_buffer;
+ break;
+
+ case 0122:
+ // U2M
+ m_ram_do = m_uart_rd_callback() & 0177;
+ break;
+
+ default:
+ // LD (TODO: B/C masking in mode 0 is determined by optional jumpers)
+ m_ram_do = inst & (!m_mode_ff && m_cursor_ff ? 0037 : 0177);
+ break;
+ }
+
+ m_ram_cache.write_byte(translate_xy(), m_ram_do);
+ m_write_ff = false;
+}
+
+void vt5x_cpu_device::execute_th(u8 inst)
+{
+ switch (inst & 0362)
+ {
+ case 0002:
+ // M2A
+ m_ac = m_ram_do;
+ break;
+
+ case 0042:
+ // M2U
+ m_m2u_ff = true;
+ break;
+
+ case 0102:
+ // M2X
+ m_x = m_ram_do;
+ break;
+
+ case 0142:
+ // M2B
+ m_buffer = m_ram_do & ((1 << m_bbits) - 1);
+ break;
+ }
+
+ if (m_flag_test_ff)
+ {
+ switch (inst & 0160)
+ {
+ case 0000:
+ // M0: PSCJ
+ // M1: URJ
+ if (m_mode_ff)
+ m_load_pc = m_ur_flag_callback();
+ else
+ m_load_pc = m_csf_callback();
+ break;
+
+ case 0020:
+ // M0: TABJ (jump on 74H10 NAND of AC0–2; documentation incorrectly suggests the opposite)
+ // M1: AEMJ
+ if (m_mode_ff)
+ m_load_pc = m_ac == m_ram_do;
+ else
+ m_load_pc = (m_ac & 7) != 7;
+ break;
+
+ case 0040:
+ // M0: KCLJ
+ // M1: ALMJ
+ if (m_mode_ff)
+ m_load_pc = m_ac < m_ram_do;
+ else
+ m_load_pc = m_kclk_callback();
+ break;
+
+ case 0060:
+ // M0: FRQJ
+ // M1: ADXJ
+ if (m_mode_ff)
+ m_load_pc = m_ac != (m_x ^ (m_x8 ? 8 : 0));
+ else
+ m_load_pc = m_frq_callback();
+ break;
+
+ case 0100:
+ // M0: PRQJ
+ // M1: AEM2J
+ if (m_mode_ff)
+ m_load_pc = m_ac == m_ram_do;
+ else
+ m_load_pc = m_ccf_callback();
+ break;
+
+ case 0120:
+ // M0: COPJ (TODO?)
+ // M1: TRUJ
+ m_load_pc = true;
+ break;
+
+ case 0140:
+ // M0: UTJ
+ // M1: VSCJ
+ if (m_mode_ff)
+ m_load_pc = m_horiz_count >= 8;
+ else
+ m_load_pc = !m_ut_flag_callback();
+ break;
+
+ case 0160:
+ // M0: TOSJ
+ // M1: KEYJ
+ if (m_mode_ff)
+ m_load_pc = m_key_up_callback(m_ac) & 1;
+ else
+ m_load_pc = !m_top_of_screen;
+ break;
+ }
+ }
+
+ if ((m_pc & 0377) == 0377)
+ m_rom_page = (m_rom_page + 1) & 3;
+ m_pc = (m_pc + 1) & 03777;
+}
+
+void vt52_cpu_device::execute_th(u8 inst)
+{
+ // not actually synchronized to TH (but may be gated externally with EN CYCLE)
+ if ((inst & 0362) == 0162)
+ m_graphic_callback(m_ram_do);
+
+ vt5x_cpu_device::execute_th(inst);
+}
+
+void vt5x_cpu_device::execute_tj(u8 dest)
+{
+ if (m_load_pc)
+ m_pc = u16(m_rom_page) << 8 | dest;
+ else
+ {
+ // Hardware bug: the ROM page counter will not increment on the second byte of a jump not taken.
+ m_pc = (m_pc + 1) & 03777;
+ }
+}
+
+void vt5x_cpu_device::clock_video_counters()
+{
+ if ((m_horiz_count & 9) == 9)
+ {
+ m_horiz_count = (m_horiz_count & (15 * 16)) + 16;
+ if (m_vert_count == 07777)
+ {
+ m_vert_count = m_frq_callback() ? 03000 : 02000;
+ m_horiz_count = 0;
+ m_top_of_screen = true;
+ m_vert_count_callback(0);
+ }
+ else
+ {
+ m_vert_count++;
+ if (m_horiz_count == 10 * 16)
+ m_horiz_count = 0;
+ m_vert_count_callback(m_vert_count & 0177);
+ }
+ }
+ else
+ {
+ m_horiz_count++;
+ if (m_horiz_count == 8)
+ {
+ if (m_top_of_screen)
+ {
+ m_top_of_screen = false;
+
+ // This calculates the number of visible lines, which is actually firmware-defined.
+ bool is_60hz = BIT(m_vert_count, 9);
+ unsigned first_line = is_60hz ? 4 : 32;
+ screen().configure(
+ 900,
+ (010000 - m_vert_count) / 10,
+ rectangle(128, 847, first_line, 24 * (is_60hz ? 10 : 11) + first_line - 1),
+ clocks_to_attotime((010000 - m_vert_count) * 90).as_attoseconds()
+ );
+ screen().reset_origin();
+ m_current_line = 0;
+#if FIND_FIRST_LINE
+ m_first_line = ~0;
+#endif
+ }
+ else
+ m_current_line++;
+ m_baud_9600_callback(0);
+ }
+ else if (m_horiz_count == 4)
+ m_baud_9600_callback(1);
+ }
+}
+
+void vt5x_cpu_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ bool en_cycle = BIT(m_horiz_count, 0);
+ switch (m_t)
+ {
+ case 0:
+ if (!en_cycle)
+ debugger_instruction_hook(m_pc);
+ m_t = 1;
+ break;
+
+ case 1:
+ if (!en_cycle)
+ execute_te(m_rom_cache.read_byte(m_pc));
+ m_t = 2;
+ break;
+
+ case 2:
+ if (!en_cycle)
+ execute_tf(m_rom_cache.read_byte(m_pc));
+ if (!m_write_ff)
+ m_ram_do = m_ram_cache.read_byte(translate_xy()) & 0177;
+ m_cursor_active = m_ac == (m_x ^ (m_x8 ? 8 : 0));
+ if (u8(m_horiz_count - 2) >= 2 * 16)
+ {
+ if (m_video_process)
+ {
+#if FIND_FIRST_LINE
+ if (m_first_line > m_current_line)
+ m_first_line = m_current_line;
+#endif
+ m_x = (m_x + 1) & 0177;
+ }
+ draw_char_line();
+ }
+ m_t = 3;
+ break;
+
+ case 3:
+ if (en_cycle && m_write_ff)
+ execute_tg(m_rom_cache.read_byte(m_pc));
+ m_t = 4;
+ break;
+
+ case 4:
+ if (en_cycle)
+ execute_th(m_rom_cache.read_byte(m_pc));
+ m_t = 5;
+ break;
+
+ case 5:
+ m_t = 6;
+ break;
+
+ case 6:
+ if (en_cycle && m_flag_test_ff)
+ execute_tj(m_rom_cache.read_byte(m_pc));
+ m_t = 7;
+ break;
+
+ case 7:
+ if (!en_cycle)
+ execute_tw(m_rom_cache.read_byte(m_pc));
+ m_t = 8;
+ break;
+
+ case 8:
+ m_t = 0;
+ clock_video_counters();
+ break;
+ }
+
+ m_icount--;
+ }
+}
+
+void vt5x_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("M%d %4s", m_mode_ff ? 1 : 0, m_done_ff ? "DONE" : "");
+ break;
+ }
+}