diff options
Diffstat (limited to 'src/devices/bus/multibus/robotron_k7070.cpp')
-rw-r--r-- | src/devices/bus/multibus/robotron_k7070.cpp | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/src/devices/bus/multibus/robotron_k7070.cpp b/src/devices/bus/multibus/robotron_k7070.cpp new file mode 100644 index 00000000000..301b2ec48c3 --- /dev/null +++ b/src/devices/bus/multibus/robotron_k7070.cpp @@ -0,0 +1,423 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev + +/* + * Robotron K7070 and K7072 combo + * + * K7070 KGS - graphics terminal (K7070.10 model) + * K7072 ABG - dumb grayscale framebuffer + * + * Reference: http://www.tiffe.de/Robotron/MMS16/ + * Internal test of KGS -- in KGS-K7070.pdf, pp. 19-23 + * + * To do: + * - palette + * - vertical raster split and text mode + */ + +#include "emu.h" +#include "robotron_k7070.h" + +#define VERBOSE 0 +#include "logmacro.h" + +enum kgs_st : u8 +{ + KGS_ST_OBF = 0x01, + KGS_ST_IBF = 0x02, + KGS_ST_INT = 0x04, + KGS_ST_ERR = 0x80, +}; + +DEFINE_DEVICE_TYPE(ROBOTRON_K7070, robotron_k7070_device, "robotron_k7070", "Robotron K7070 KGS") + +robotron_k7070_device::robotron_k7070_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : device_t(mconfig, ROBOTRON_K7070, tag, owner, clock) + , device_multibus_interface(mconfig, *this) + , m_cpu(*this, "cpu") + , m_ctc(*this, "ctc") + , m_sio(*this, "sio") + , m_serial(*this, "serial%u", 0U) + , m_palette(*this, "palette") + , m_ram(*this, "vram0") + , m_dsel(*this, "DSEL%u", 0U) + , m_view_lo(*this, "view_lo") + , m_view_hi(*this, "view_hi") +{ + m_abg_msel = m_kgs_iml = 0; + m_abg_func = m_abg_split = 0; + m_abg_addr = 0; + m_kgs_datao = m_kgs_datai = m_kgs_ctrl = 0; + m_nmi_enable = 0; +} + +ROM_START(robotron_k7070) + ROM_REGION(0x2000, "eprom", ROMREGION_ERASEFF) + ROM_DEFAULT_BIOS("152") + + ROM_SYSTEM_BIOS(0, "152", "Version 152") // ROM from A7100 + ROMX_LOAD("kgs7070-152.bin", 0x0000, 0x2000, CRC(403f4235) SHA1(d07ccd40f8b600651d513f588bcf1ea4f15ed094), ROM_BIOS(0)) + + ROM_SYSTEM_BIOS(1, "153", "Version 153") + ROMX_LOAD("kgs7070-153.rom", 0x0000, 0x2000, CRC(a72fe820) SHA1(4b77ab2b59ea8c3632986847ff359df26b16196b), ROM_BIOS(1)) + + ROM_SYSTEM_BIOS(2, "154", "Version 154") + ROMX_LOAD("kgs7070-154.rom", 0x0000, 0x2000, CRC(2995ade0) SHA1(62516f2e1cb62698445f80fd823d39a1a78a7807), ROM_BIOS(2)) +ROM_END + +static INPUT_PORTS_START(robotron_k7070) + PORT_START("DSEL0") + PORT_DIPNAME(0x01, 0x01, "Codepoint 0x24") + PORT_DIPSETTING(0x00, "Currency sign" ) + PORT_DIPSETTING(0x01, "Dollar sign" ) + PORT_DIPNAME(0x02, 0x02, "Perform I/O test") + PORT_DIPSETTING(0x00, DEF_STR(No) ) + PORT_DIPSETTING(0x02, DEF_STR(Yes) ) + PORT_DIPNAME(0x04, 0x00, "Perform VRAM test") + PORT_DIPSETTING(0x00, DEF_STR(Yes) ) + PORT_DIPSETTING(0x04, DEF_STR(No) ) + + PORT_START("DSEL1") + PORT_DIPNAME(0x03, 0x02, "V.24 Parity") + PORT_DIPSETTING(0x00, "No parity" ) + PORT_DIPSETTING(0x01, "Odd" ) + PORT_DIPSETTING(0x02, "No parity" ) + PORT_DIPSETTING(0x03, "Even" ) + PORT_DIPNAME(0x04, 0x04, "V.24 Character size") + PORT_DIPSETTING(0x00, "7 bits") + PORT_DIPSETTING(0x04, "8 bits") + PORT_DIPNAME(0x38, 0x38, "V.24 Baud rate") + PORT_DIPSETTING(0x38, "19200") + PORT_DIPSETTING(0x30, "9600") + PORT_DIPSETTING(0x28, "4800") + PORT_DIPSETTING(0x20, "2400") + PORT_DIPSETTING(0x18, "1200") + PORT_DIPSETTING(0x10, "600") + PORT_DIPSETTING(0x08, "300") + PORT_DIPNAME(0x40, 0x40, "IFSS Parity") + PORT_DIPSETTING(0x00, "Odd" ) + PORT_DIPSETTING(0x40, "Even" ) + PORT_DIPNAME(0x80, 0x80, "IFSS Baud rate") + PORT_DIPSETTING(0x00, "9600") + PORT_DIPSETTING(0x80, "Same as V.24") +INPUT_PORTS_END + +const tiny_rom_entry *robotron_k7070_device::device_rom_region() const +{ + return ROM_NAME(robotron_k7070); +} + +ioport_constructor robotron_k7070_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(robotron_k7070); +} + +void robotron_k7070_device::device_start() +{ + save_item(NAME(m_start)); + save_item(NAME(m_abg_msel)); + save_item(NAME(m_kgs_iml)); + save_item(NAME(m_kgs_datao)); + save_item(NAME(m_kgs_datai)); + save_item(NAME(m_kgs_ctrl)); + save_item(NAME(m_nmi_enable)); + + m_bus->space(AS_IO).install_readwrite_handler(0x200, 0x203, read16s_delegate(*this, FUNC(robotron_k7070_device::io_r)), write16s_delegate(*this, FUNC(robotron_k7070_device::io_w))); +} + +void robotron_k7070_device::device_reset() +{ + m_start = 0; + m_kgs_ctrl = KGS_ST_IBF | KGS_ST_OBF; + m_kgs_datao = m_kgs_datai = 0; + m_kgs_iml = m_abg_msel = 0; + m_nmi_enable = false; + kgs_memory_remap(); +} + +static const z80_daisy_config k7070_daisy_chain[] = +{ + { "sio" }, + { "ctc" }, + { nullptr } +}; + +void robotron_k7070_device::device_add_mconfig(machine_config &config) +{ + Z80(config, m_cpu, XTAL(16'000'000) / 4); + m_cpu->set_addrmap(AS_PROGRAM, &robotron_k7070_device::cpu_mem); + m_cpu->set_addrmap(AS_IO, &robotron_k7070_device::cpu_pio); + m_cpu->set_daisy_config(k7070_daisy_chain); + + Z80CTC(config, m_ctc, 16_MHz_XTAL / 3); + m_ctc->intr_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0); + m_ctc->set_clk<0>(1230750); + m_ctc->set_clk<1>(1230750); + m_ctc->set_clk<2>(1230750); + m_ctc->set_clk<3>(1230750); + m_ctc->zc_callback<0>().set(m_sio, FUNC(z80sio_device::rxca_w)); + m_ctc->zc_callback<0>().append(m_sio, FUNC(z80sio_device::txca_w)); + m_ctc->zc_callback<1>().set(m_sio, FUNC(z80sio_device::rxtxcb_w)); + + Z80SIO(config, m_sio, XTAL(16'000'000) / 4); + m_sio->out_int_callback().set_inputline(m_cpu, INPUT_LINE_IRQ0); + m_sio->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd)); + m_sio->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr)); + m_sio->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts)); + m_sio->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd)); + m_sio->out_dtrb_callback().set([this] (bool state) { m_kgs_iml = !state; kgs_memory_remap(); }); + + // V.24 port for tablet + RS232_PORT(config, m_serial[0], default_rs232_devices, "loopback"); + m_serial[0]->rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w)); + m_serial[0]->dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w)); + m_serial[0]->cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w)); + + // IFSS (current loop) port for keyboard (unused on A7150) + RS232_PORT(config, m_serial[1], default_rs232_devices, "loopback"); + m_serial[1]->rxd_handler().set(m_sio, FUNC(z80sio_device::rxb_w)); + + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); + screen.set_color(rgb_t::green()); + screen.set_raw(XTAL(16'000'000), 737, 0, 640, 431, 0, 400); + screen.set_screen_update(FUNC(robotron_k7070_device::screen_update_k7072)); + screen.set_palette(m_palette); + screen.screen_vblank().set([this] (bool state) { if (m_nmi_enable) m_cpu->set_input_line(INPUT_LINE_NMI, state); }); + + PALETTE(config, m_palette, palette_device::MONOCHROME); +} + +void robotron_k7070_device::cpu_mem(address_map &map) +{ + map.unmap_value_high(); + + map(0x0000, 0x1fff).view(m_view_lo); + m_view_lo[0](0x0000, 0x1fff).rom().region("eprom", 0); + m_view_lo[1](0x0000, 0x1fff).ram().share("kgs_ram0"); + + map(0x2000, 0x7fff).ram().share("kgs_ram1"); + +// FIXME handle IML=0 and MSEL=1 (no access to VRAM) + map(0x8000, 0xffff).view(m_view_hi); + m_view_hi[0](0x8000, 0xffff).ram().share("kgs_ram2"); + m_view_hi[1](0x8000, 0xffff).ram().share("vram0"); +} + +void robotron_k7070_device::cpu_pio(address_map &map) +{ + map.unmap_value_high(); + map.global_mask(0xff); + + map(0x0000, 0x0003).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write)); + map(0x0008, 0x000b).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w)); + map(0x0010, 0x0017).rw(FUNC(robotron_k7070_device::kgs_host_r), FUNC(robotron_k7070_device::kgs_host_w)); // p. 11 of KGS-K7070.pdf + + // p. 6 of ABG-K7072.pdf + map(0x0020, 0x0021).w(FUNC(robotron_k7070_device::abg_addr_w)); + map(0x0022, 0x0022).w(FUNC(robotron_k7070_device::abg_func_w)); + map(0x0023, 0x0023).w(FUNC(robotron_k7070_device::abg_split_w)); + map(0x0030, 0x003f).noprw(); // palette register + + map(0x0080, 0x0080).w(FUNC(robotron_k7070_device::abg_misc_w)); +} + +void robotron_k7070_device::palette_init(palette_device &palette) +{ + // 0=black, 1=dim, 2=normal, 3=bold + + palette.set_pen_color(0, rgb_t::black()); + palette.set_pen_color(1, rgb_t(0x80, 0x80, 0x80)); + palette.set_pen_color(2, rgb_t(0xc0, 0xc0, 0xc0)); + palette.set_pen_color(3, rgb_t::white()); +} + +uint16_t robotron_k7070_device::io_r(offs_t offset, uint16_t mem_mask) +{ + uint8_t data = 0; + + switch (offset) + { + case 0: + data = m_kgs_ctrl; + break; + + case 1: + data = m_kgs_datai; + if (!machine().side_effects_disabled()) + m_kgs_ctrl &= ~KGS_ST_OBF; + break; + } + + if (offset && !machine().side_effects_disabled()) + { + LOG("%s: KGS %d == %02x '%c'\n", machine().describe_context(), + offset, data, (data > 0x1f && data < 0x7f) ? data : 0x20); + } + + return data; +} + +void robotron_k7070_device::io_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + if (offset) + { + LOG("%s: KGS %d <- %02x '%c', ctrl %02x\n", machine().describe_context(), + offset, data, (data > 0x1f && data < 0x7f) ? data : 0x20, m_kgs_ctrl); + } + + switch (offset) + { + case 0: + m_kgs_ctrl &= ~(KGS_ST_ERR | KGS_ST_INT); + int_w(7, CLEAR_LINE); + break; + + case 1: + if (m_kgs_ctrl & KGS_ST_IBF) + { + m_kgs_ctrl |= KGS_ST_ERR; + } + else + { + m_kgs_datao = data; + m_kgs_ctrl |= KGS_ST_IBF; + } + break; + } +} + +uint32_t robotron_k7070_device::screen_update_k7072(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + int addr = m_start; + + for (int y = 0; y < 400; y++) + { + int horpos = 0; + for (int x = 0; x < 80; x++) + { + uint8_t code = m_ram[addr++ % 32768]; + for (int b = 0; b < 8; b++) + { + bitmap.pix(y, horpos++) = BIT(code, 7 - b); + } + } + } + + return 0; +} + + +uint8_t robotron_k7070_device::kgs_host_r(offs_t offset) +{ + uint8_t data = 0; + + switch (offset) + { + case 0: + data = m_kgs_datao; + if (!machine().side_effects_disabled()) + m_kgs_ctrl &= ~(KGS_ST_ERR | KGS_ST_IBF); + break; + + case 2: + data = m_kgs_ctrl; + break; + + case 6: + data = m_dsel[0]->read(); + break; + + case 7: + data = m_dsel[1]->read(); + break; + + default: + break; + } + + if (offset != 2 && offset != 5 && !machine().side_effects_disabled()) + { + LOG("%s: kgs %d == %02x '%c'\n", machine().describe_context(), + offset, data, (data > 0x1f && data < 0x7f) ? data : ' '); + } + + return data; +} + +void robotron_k7070_device::kgs_host_w(offs_t offset, uint8_t data) +{ + if (offset != 2 && offset != 5) + { + LOG("%s: kgs %d <- %02x '%c', ctrl %02x\n", machine().describe_context(), + offset, data, (data > 0x1f && data < 0x7f) ? data : ' ', m_kgs_ctrl); + } + + switch (offset) + { + case 1: + if (m_kgs_ctrl & KGS_ST_OBF) + { + m_kgs_ctrl |= KGS_ST_ERR; + } + else + { + m_kgs_datai = data; + m_kgs_ctrl |= KGS_ST_OBF; + } + break; + + case 3: + m_kgs_ctrl |= KGS_ST_INT; + int_w(7, ASSERT_LINE); + break; + + case 4: + m_kgs_ctrl |= KGS_ST_ERR; + break; + + case 5: + m_abg_msel = data; + kgs_memory_remap(); + break; + } +} + +void robotron_k7070_device::abg_addr_w(offs_t offset, uint8_t data) +{ + if (offset == 0) + m_start |= ((data & 127) << 8); + else + m_start = data; +} + +/* + * bit 0: GRAF This bit must be set if both video levels are to be output in graphics display mode or if the palette registers are to be programmed. + * bit 1: /PALP This bit must be set to program the palette register. + * bit 2: /KGSP If this bit is reset, the frame buffer can be accessed asynchronously. + * bit 3: BLNK Switching this bit causes the points (byte by byte) in which the attribute bit of video level 2 (bit 1) is set to flash in alphanumeric display mode. + */ +void robotron_k7070_device::abg_func_w(offs_t offset, uint8_t data) +{ + LOG("%s: abg func <- %02x\n", machine().describe_context(), data); + m_abg_func = data; +} + +void robotron_k7070_device::abg_split_w(offs_t offset, uint8_t data) +{ + LOG("%s: abg split <- %02x (%d -> %d)\n", machine().describe_context(), data, data, data*2); + m_abg_split = data; +} + +void robotron_k7070_device::abg_misc_w(offs_t offset, uint8_t data) +{ + LOG("%s: abg misc <- %02x\n", machine().describe_context(), data); + m_nmi_enable = BIT(data, 7); +} + +void robotron_k7070_device::kgs_memory_remap() +{ + LOG("%s: kgs memory: iml %d msel %d\n", machine().describe_context(), m_kgs_iml, m_abg_msel); + + m_view_lo.select(m_kgs_iml); + m_view_hi.select(m_abg_msel != 0); +} |