mirror of
https://github.com/holub/mame
synced 2025-04-23 17:00:53 +03:00
vt52: Draw characters on screen; add notes about processor architecture
vt50dasm: Slightly smarter jump decoding; note another (unemulated) VT52 difference (nw)
This commit is contained in:
parent
a7e88ab526
commit
2b4c203d88
@ -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;
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
class vt5x_cpu_device : public cpu_device
|
||||
class vt5x_cpu_device : public cpu_device, public device_video_interface
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
@ -31,12 +31,17 @@ public:
|
||||
auto cen_callback() { return m_cen_callback.bind(); }
|
||||
auto ccf_callback() { return m_ccf_callback.bind(); }
|
||||
auto csf_callback() { return m_csf_callback.bind(); }
|
||||
auto char_data_callback() { return m_char_data_callback.bind(); }
|
||||
|
||||
// screen update method
|
||||
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
|
||||
protected:
|
||||
// construction/destruction
|
||||
vt5x_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int bbits, int ybits);
|
||||
|
||||
// device-level overrides
|
||||
virtual void device_config_complete() override;
|
||||
virtual void device_resolve_objects() override;
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
@ -50,6 +55,9 @@ protected:
|
||||
// device_state_interface overrides
|
||||
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
|
||||
|
||||
// video helpers
|
||||
void draw_char_line();
|
||||
|
||||
// address translation
|
||||
offs_t translate_xy() const;
|
||||
|
||||
@ -83,6 +91,7 @@ protected:
|
||||
devcb_write_line m_cen_callback;
|
||||
devcb_read_line m_csf_callback;
|
||||
devcb_read_line m_ccf_callback;
|
||||
devcb_read8 m_char_data_callback;
|
||||
|
||||
// register dimensions
|
||||
const u8 m_bbits;
|
||||
@ -116,6 +125,11 @@ protected:
|
||||
u8 m_horiz_count;
|
||||
u16 m_vert_count;
|
||||
bool m_top_of_screen;
|
||||
u16 m_current_line;
|
||||
u16 m_first_line;
|
||||
|
||||
// display bitmap
|
||||
bitmap_rgb32 m_bitmap;
|
||||
};
|
||||
|
||||
class vt50_cpu_device : public vt5x_cpu_device
|
||||
|
@ -34,7 +34,7 @@ const char *const vt5x_disassembler::s_opcodes_e[8] = {
|
||||
};
|
||||
|
||||
const char *const vt5x_disassembler::s_opcodes_f[8] = {
|
||||
"DXDY", "IA", "IA1", "IY", "DY", "IROM", "DX", "DA"
|
||||
"DXDY", "IA", "IA1", "IY", "DY", "IROM", "DX", "DA" // IA is functionally duplicated
|
||||
};
|
||||
|
||||
const char *const vt50_disassembler::s_opcodes_g[8] = {
|
||||
@ -45,11 +45,16 @@ const char *const vt52_disassembler::s_opcodes_g[8] = {
|
||||
"M2A", "A2M", "M2U", "B2M", "M2X", "U2M", "M2B", "GRPH"
|
||||
};
|
||||
|
||||
const char *const vt5x_disassembler::s_jumps_h[2][8] = {
|
||||
const char *const vt50_disassembler::s_jumps_h[2][8] = {
|
||||
{ "PSC", "TAB", "KCL", "FRQ", "PRQ", "TRU", "UT", "TOS" }, // mode 0
|
||||
{ "UR", "AEM", "ALM", "ADX", "AEM2", nullptr, "VSC", "KEY" } // mode 1
|
||||
};
|
||||
|
||||
const char *const vt52_disassembler::s_jumps_h[2][8] = {
|
||||
{ "PSC", "TAB", "KCL", "FRQ", "PRQ", "COP", "UT", "TOS" }, // mode 0
|
||||
{ "UR", "AEM", "ALM", "ADX", "AEM2", "TRU", "VSC", "KEY" } // mode 1
|
||||
};
|
||||
|
||||
const char *const vt5x_disassembler::s_opcodes_w[8] = {
|
||||
"SCFF", "SVID", "B2Y", "CBFF", "ZCAV", "LPB", "EPR", "HPR!ZY"
|
||||
};
|
||||
@ -99,12 +104,21 @@ offs_t vt5x_disassembler::disassemble(std::ostream &stream, offs_t pc, const vt5
|
||||
{
|
||||
if (!first)
|
||||
stream << "!";
|
||||
util::stream_format(stream, "%sJ", m_jumps_h[0][(opcode & 0160) >> 4]);
|
||||
if (m_jumps_h[1][(opcode & 0160) >> 4] != nullptr)
|
||||
util::stream_format(stream, "/%sJ", m_jumps_h[1][(opcode & 0160) >> 4]);
|
||||
bool m0 = (opcode & 0170) != 0130;
|
||||
bool m1 = (opcode & 0170) != 0170;
|
||||
if (m_jumps_h[1][(opcode & 0160) >> 4] == nullptr)
|
||||
m0 = std::exchange(m1, false);
|
||||
if (m0)
|
||||
{
|
||||
util::stream_format(stream, "%sJ", m_jumps_h[0][(opcode & 0160) >> 4]);
|
||||
if (m1)
|
||||
stream << "/";
|
||||
}
|
||||
if (m1)
|
||||
util::stream_format(stream, "%sJ", m_jumps_h[1][(opcode & 0160) >> 4]);
|
||||
|
||||
u16 nextpc = pc + 1;
|
||||
if ((opcode & 0164) == 0124) // IROM!TRUJ adjustment
|
||||
if ((opcode & 0164) == 0124) // IROM adjustment
|
||||
nextpc += 0400;
|
||||
util::stream_format(stream, " %04o", (nextpc & 01400) | opcodes.r8(pc + 1));
|
||||
return 2;
|
||||
|
@ -18,7 +18,6 @@ protected:
|
||||
// tables
|
||||
static const char *const s_opcodes_e[8];
|
||||
static const char *const s_opcodes_f[8];
|
||||
static const char *const s_jumps_h[2][8];
|
||||
static const char *const s_opcodes_w[8];
|
||||
|
||||
private:
|
||||
@ -38,6 +37,7 @@ public:
|
||||
private:
|
||||
// tables
|
||||
static const char *const s_opcodes_g[8];
|
||||
static const char *const s_jumps_h[2][8];
|
||||
};
|
||||
|
||||
class vt52_disassembler : public vt5x_disassembler
|
||||
@ -49,6 +49,7 @@ public:
|
||||
private:
|
||||
// tables
|
||||
static const char *const s_opcodes_g[8];
|
||||
static const char *const s_jumps_h[2][8];
|
||||
};
|
||||
|
||||
#endif // MAME_CPU_VT50_VT50DASM_H
|
||||
|
@ -14,8 +14,8 @@
|
||||
character generator (on a board of its own). VT50 and VT52 each had
|
||||
minor variants differing in keyboard function and printer availability.
|
||||
|
||||
The VT55 was a graphical terminal ostensibly in the same family as the
|
||||
VT50 and VT52. Its hardware commonalities and differences are unknown.
|
||||
The VT55 DECgraphic Scope was a graphical terminal based on the same
|
||||
main boards as the VT50 and VT52.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
@ -37,6 +37,7 @@ public:
|
||||
, m_keys(*this, "KEY%d", 0U)
|
||||
, m_baud_sw(*this, "BAUD")
|
||||
, m_data_sw(*this, "DATABITS")
|
||||
, m_chargen(*this, "chargen")
|
||||
{
|
||||
}
|
||||
|
||||
@ -56,6 +57,7 @@ private:
|
||||
void uart_xd_w(u8 data);
|
||||
DECLARE_WRITE_LINE_MEMBER(serial_out_w);
|
||||
DECLARE_READ_LINE_MEMBER(xrdy_eoc_r);
|
||||
u8 chargen_r(offs_t offset);
|
||||
|
||||
void rom_1k(address_map &map);
|
||||
void ram_2k(address_map &map);
|
||||
@ -65,6 +67,7 @@ private:
|
||||
required_ioport_array<8> m_keys;
|
||||
required_ioport m_baud_sw;
|
||||
required_ioport m_data_sw;
|
||||
required_region_ptr<u8> m_chargen;
|
||||
};
|
||||
|
||||
void vt52_state::machine_reset()
|
||||
@ -150,6 +153,12 @@ READ_LINE_MEMBER(vt52_state::xrdy_eoc_r)
|
||||
return m_uart->tbmt_r() || m_uart->eoc_r();
|
||||
}
|
||||
|
||||
u8 vt52_state::chargen_r(offs_t offset)
|
||||
{
|
||||
// ROM is on its own board, shared only with 7404 inverters
|
||||
return ~m_chargen[offset];
|
||||
}
|
||||
|
||||
void vt52_state::rom_1k(address_map &map)
|
||||
{
|
||||
map(00000, 01777).rom().region("program", 0);
|
||||
@ -306,6 +315,7 @@ void vt52_state::vt52(machine_config &mconfig)
|
||||
VT52_CPU(mconfig, m_maincpu, 13.824_MHz_XTAL);
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &vt52_state::rom_1k);
|
||||
m_maincpu->set_addrmap(AS_DATA, &vt52_state::ram_2k);
|
||||
m_maincpu->set_screen("screen");
|
||||
m_maincpu->baud_9600_callback().set(FUNC(vt52_state::baud_9600_w));
|
||||
m_maincpu->vert_count_callback().set(FUNC(vt52_state::vert_count_w));
|
||||
m_maincpu->uart_rd_callback().set(m_uart, FUNC(ay51013_device::receive));
|
||||
@ -317,14 +327,12 @@ void vt52_state::vt52(machine_config &mconfig)
|
||||
m_maincpu->kclk_callback().set_ioport("KEYCLICK");
|
||||
m_maincpu->frq_callback().set_ioport("60HJ");
|
||||
m_maincpu->bell_callback().set("bell", FUNC(speaker_sound_device::level_w));
|
||||
m_maincpu->char_data_callback().set(FUNC(vt52_state::chargen_r));
|
||||
|
||||
AY51013(mconfig, m_uart); // TR1402 or equivalent
|
||||
m_uart->write_so_callback().set(FUNC(vt52_state::serial_out_w));
|
||||
|
||||
screen_device &screen(SCREEN(mconfig, "screen", SCREEN_TYPE_RASTER));
|
||||
screen.set_raw(13.824_MHz_XTAL, 900, 0, 720, 256, 0, 240);
|
||||
//screen.set_raw(13.824_MHz_XTAL, 900, 0, 720, 307.2, 0, 264); // not a whole number of scans
|
||||
screen.set_screen_update(FUNC(vt52_state::screen_update));
|
||||
SCREEN(mconfig, "screen", SCREEN_TYPE_RASTER);
|
||||
|
||||
SPEAKER(mconfig, "mono").front_center();
|
||||
SPEAKER_SOUND(mconfig, "bell").add_route(ALL_OUTPUTS, "mono", 1.0); // FIXME: uses a flyback diode circuit
|
||||
@ -337,8 +345,8 @@ ROM_START(vt52)
|
||||
ROM_LOAD_NIB_LOW( "23-126a9.e37", 0x200, 0x200, CRC(4883a600) SHA1(c5d9b0c21493065c75b4a7d52d5bd47f9851dfe7))
|
||||
ROM_LOAD_NIB_HIGH("23-127a9.e21", 0x200, 0x200, CRC(56c1c0d6) SHA1(ab0eb6e7bbafcc3d28481b62de3d3490f01c0174))
|
||||
|
||||
ROM_REGION(0x400, "chargen", 0) // 2608 character generator
|
||||
ROM_REGION(0x400, "chargen", 0) // 2608 (non-JEDEC) character generator
|
||||
ROM_LOAD("23-002b4.e1", 0x000, 0x400, CRC(b486500c) SHA1(029f07424d6c23ee083db42d9f9c252ac728ccd0))
|
||||
ROM_END
|
||||
|
||||
COMP(1975, vt52, 0, 0, vt52, vt52, vt52_state, empty_init, "Digital Equipment Corporation", "VT52 Video Display Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)
|
||||
COMP(1975, vt52, 0, 0, vt52, vt52, vt52_state, empty_init, "Digital Equipment Corporation", "VT52 Video Display Terminal", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND | MACHINE_NODEVICE_PRINTER)
|
||||
|
Loading…
Reference in New Issue
Block a user