diff options
Diffstat (limited to 'src/devices/cpu/vt50/vt50.cpp')
-rw-r--r-- | src/devices/cpu/vt50/vt50.cpp | 155 |
1 files changed, 150 insertions, 5 deletions
diff --git a/src/devices/cpu/vt50/vt50.cpp b/src/devices/cpu/vt50/vt50.cpp index d2ed58dde79..b12b75e79da 100644 --- a/src/devices/cpu/vt50/vt50.cpp +++ b/src/devices/cpu/vt50/vt50.cpp @@ -2,13 +2,69 @@ // copyright-holders:AJR /*************************************************************************** - DEC VT50/VT52 CPU skeleton + 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") @@ -16,6 +72,7 @@ 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_rom_cache(nullptr) @@ -34,6 +91,7 @@ vt5x_cpu_device::vt5x_cpu_device(const machine_config &mconfig, device_type type , m_cen_callback(*this) , m_csf_callback(*this) , m_ccf_callback(*this) + , m_char_data_callback(*this) , m_bbits(bbits) , m_ybits(ybits) , m_pc(0) @@ -59,6 +117,8 @@ vt5x_cpu_device::vt5x_cpu_device(const machine_config &mconfig, device_type type , 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; @@ -93,6 +153,18 @@ device_memory_interface::space_config_vector vt5x_cpu_device::memory_space_confi }; } +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_resolve_objects() { // resolve callbacks @@ -110,6 +182,7 @@ void vt5x_cpu_device::device_resolve_objects() m_cen_callback.resolve_safe(); m_csf_callback.resolve_safe(1); m_ccf_callback.resolve_safe(1); + m_char_data_callback.resolve_safe(0177); } void vt52_cpu_device::device_resolve_objects() @@ -125,6 +198,7 @@ void vt5x_cpu_device::device_start() m_rom_cache = space(AS_PROGRAM).cache<0, 0, ENDIANNESS_LITTLE>(); m_ram_cache = space(AS_DATA).cache<0, 0, ENDIANNESS_LITTLE>(); + screen().register_screen_bitmap(m_bitmap); set_icountptr(m_icount); state_add(VT5X_PC, "PC", m_pc).formatstr("%04O").mask(01777); @@ -168,6 +242,12 @@ void vt5x_cpu_device::device_start() 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() @@ -184,11 +264,45 @@ void vt5x_cpu_device::device_reset() 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.pix32(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 @@ -525,7 +639,8 @@ void vt5x_cpu_device::execute_th(u8 inst) break; case 0120: - // TRUJ + // M0: COPJ (TODO?) + // M1: TRUJ m_load_pc = true; break; @@ -599,7 +714,27 @@ void vt5x_cpu_device::clock_video_counters() m_horiz_count++; if (m_horiz_count == 8) { - m_top_of_screen = false; + 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) @@ -632,8 +767,18 @@ void vt5x_cpu_device::execute_run() 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 (m_video_process && u8(m_horiz_count - 2) >= 2 * 16) - m_x = (m_x + 1) & 0177; + 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; |