// license:BSD-3-Clause // copyright-holders:Carl #include "emu.h" #include "pcd.h" #include "cpu/mcs48/mcs48.h" #include "cpu/mcs51/mcs51.h" #include "screen.h" DEFINE_DEVICE_TYPE(PCD_VIDEO, pcd_video_device, "pcd_video", "Siemens PC-D Video") DEFINE_DEVICE_TYPE(PCX_VIDEO, pcx_video_device, "pcx_video", "Siemens PC-X Video") pcdx_video_device::pcdx_video_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock), device_gfx_interface(mconfig, *this, nullptr, "palette"), m_maincpu(*this, ":maincpu"), m_mcu(*this, "graphics"), m_pic2(*this, ":pic2") { } pcd_video_device::pcd_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : pcdx_video_device(mconfig, PCD_VIDEO, tag, owner, clock), m_crtc(*this, "crtc"), m_mouse_btn(*this, "MOUSE"), m_mouse_x(*this, "MOUSEX"), m_mouse_y(*this, "MOUSEY"), m_vram(32*1024), m_charram(8*1024) { } pcx_video_device::pcx_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : pcdx_video_device(mconfig, PCX_VIDEO, tag, owner, clock), device_serial_interface(mconfig, *this), m_crtc(*this, "crtc"), m_charrom(*this, "char"), m_txd_handler(*this) { } ROM_START( pcd_video ) ROM_REGION(0x400, "graphics", 0) ROM_LOAD("s36361-d321-v1.bin", 0x000, 0x400, CRC(69baeb2a) SHA1(98b9cd0f38c51b4988a3aed0efcf004bedd115ff)) ROM_END const tiny_rom_entry *pcd_video_device::device_rom_region() const { return ROM_NAME( pcd_video ); } ROM_START( pcx_video ) ROM_REGION(0x2000, "char", 0) ROM_LOAD("d12-graka.bin", 0x0000, 0x2000, CRC(e4933c16) SHA1(932ae1f0cd2b029b7f5fc3d2d1679e70b25c0828)) ROM_REGION(0x6000, "graphics", 0) ROM_LOAD("d40-graka.bin", 0x0000, 0x2000, CRC(dce48252) SHA1(0d9a575b2d001168a36864d7bd5db1c3aca5fb8d)) ROM_LOAD("d39-graka.bin", 0x4000, 0x2000, CRC(02920e25) SHA1(145a6648d75c1dc4788f9bc7790281ef7e8f8426)) ROM_END const tiny_rom_entry *pcx_video_device::device_rom_region() const { return ROM_NAME( pcx_video ); } static const gfx_layout pcd_charlayout = { 8, 14, /* 8 x 14 characters */ 512, /* 512 characters */ 1, /* 1 bits per pixel */ { 0 }, /* no bitplanes */ /* x offsets */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* y offsets */ { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8 }, 8*16 }; static GFXDECODE_START( gfx_pcx ) GFXDECODE_DEVICE( "char", 0x0000, pcd_charlayout, 0, 1 ) GFXDECODE_END static INPUT_PORTS_START( pcd_mouse ) PORT_START("MOUSE") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Right Mouse Button") PORT_CODE(MOUSECODE_BUTTON1) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Left Mouse Button") PORT_CODE(MOUSECODE_BUTTON2) PORT_START("MOUSEX") PORT_BIT( 0xfff, 0x000, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) PORT_START("MOUSEY") PORT_BIT( 0xfff, 0x000, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) INPUT_PORTS_END ioport_constructor pcd_video_device::device_input_ports() const { return INPUT_PORTS_NAME(pcd_mouse); } void pcd_video_device::device_add_mconfig(machine_config &config) { i8741a_device &mcu(I8741A(config, "graphics", 16_MHz_XTAL / 2)); // NEC D8741AD mcu.p1_in_cb().set(FUNC(pcd_video_device::p1_r)); mcu.p2_out_cb().set(FUNC(pcd_video_device::p2_w)); mcu.t1_in_cb().set(FUNC(pcd_video_device::t1_r)); // video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); screen.set_raw(16_MHz_XTAL, 832, 0, 640, 381, 0, 350); screen.set_screen_update("crtc", FUNC(scn2674_device::screen_update)); PALETTE(config, "palette", FUNC(pcd_video_device::pcdx_palette), 3); SCN2674(config, m_crtc, 16_MHz_XTAL / 16); m_crtc->set_character_width(16); m_crtc->set_display_callback(FUNC(pcd_video_device::display_pixels)); m_crtc->set_screen("screen"); TIMER(config, "mouse_timer").configure_periodic(FUNC(pcd_video_device::mouse_timer), attotime::from_hz(15000)); // guess } void pcx_video_device::pcx_vid_map(address_map &map) { map(0x0000, 0x5fff).rom().region("graphics", 0); } void pcx_video_device::pcx_vid_io(address_map &map) { map(0x8000, 0x8007).rw(m_crtc, FUNC(scn2672_device::read), FUNC(scn2672_device::write)); map(0x8008, 0x8008).r(FUNC(pcx_video_device::unk_r)); map(0xa000, 0xa000).rw(m_crtc, FUNC(scn2672_device::buffer_r), FUNC(scn2672_device::buffer_w)); map(0xa001, 0xa001).rw(m_crtc, FUNC(scn2672_device::attr_buffer_r), FUNC(scn2672_device::attr_buffer_w)); map(0xa002, 0xa003).rw(FUNC(pcx_video_device::term_mcu_r), FUNC(pcx_video_device::term_mcu_w)); map(0xc000, 0xc7ff).ram(); } void pcx_video_device::pcx_char_ram(address_map &map) { map(0x0000, 0x07ff).ram(); } void pcx_video_device::pcx_attr_ram(address_map &map) { map(0x0000, 0x07ff).ram(); } void pcx_video_device::device_add_mconfig(machine_config &config) { i8031_device &gfx(I8031(config, "graphics", 24_MHz_XTAL / 2)); gfx.set_addrmap(AS_PROGRAM, &pcx_video_device::pcx_vid_map); gfx.set_addrmap(AS_IO, &pcx_video_device::pcx_vid_io); gfx.port_out_cb<1>().set(FUNC(pcx_video_device::p1_w)); gfx.serial_tx_cb().set(FUNC(pcx_video_device::tx_callback)); gfx.serial_rx_cb().set(FUNC(pcx_video_device::rx_callback)); // video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); screen.set_raw(24_MHz_XTAL, 1272, 0, 960, 381, 0, 350); screen.set_screen_update("crtc", FUNC(scn2672_device::screen_update)); PALETTE(config, "palette", palette_device::MONOCHROME); SCN2672(config, m_crtc, 24_MHz_XTAL / 12); // used with SCB2673B m_crtc->intr_callback().set_inputline("graphics", MCS51_INT0_LINE); m_crtc->set_character_width(12); m_crtc->set_display_callback(FUNC(pcx_video_device::display_pixels)); m_crtc->set_screen("screen"); m_crtc->set_addrmap(0, &pcx_video_device::pcx_char_ram); m_crtc->set_addrmap(1, &pcx_video_device::pcx_attr_ram); } SCN2674_DRAW_CHARACTER_MEMBER(pcd_video_device::display_pixels) { address <<= 1; if(lg) { uint16_t data = m_vram[address + 1] | (m_vram[address] << 8); if(m_p2 & 8) data = ~data; for(int i = 0; i < 16; i++) { bitmap.pix32(y, x++) = palette().pen(BIT(data, 15) ? 1 : 0); data <<= 1; } } else { uint8_t data, attr; int bgnd = 0, fgnd = 1; data = m_charram[m_vram[address] * 16 + linecount]; attr = m_vram[address + 1]; if(cursor) data = 0xff; if(ul && (attr & 0x20)) data = 0xff; if(attr & 0x10) data = ~data; if(m_p2 & 8) { fgnd = 0; bgnd = (attr & 8) ? 2 : 1; } else if(attr & 8) bgnd = 2; for(int i = 0; i < 8; i++) { rgb_t pix = palette().pen(BIT(data, 7) ? fgnd : bgnd); bitmap.pix32(y, x++) = pix; bitmap.pix32(y, x++) = pix; data <<= 1; } } } SCN2672_DRAW_CHARACTER_MEMBER(pcx_video_device::display_pixels) { uint16_t data = m_charrom[charcode * 16 + linecount + (attrcode & 0x20 ? 4096 : 0)]; if (cursor) data = 0x7ff; else { data <<= 1; data |= data << 1; } if (BIT(m_p1, 5)) data ^= 0x7ff; for (int i = 0; i < 12; i++) { rgb_t pix = palette().pen(BIT(data, 10) ? 1 : 0); bitmap.pix32(y, x++) = pix; if (i != 1) data <<= 1; } } void pcdx_video_device::pcdx_palette(palette_device &palette) const { palette.set_pen_color(0, rgb_t::black()); palette.set_pen_color(1, rgb_t::white()); palette.set_pen_color(2, rgb_t(128, 128, 128)); } TIMER_DEVICE_CALLBACK_MEMBER(pcd_video_device::mouse_timer) { m_t1 = (m_t1 == CLEAR_LINE) ? ASSERT_LINE : CLEAR_LINE; if(m_t1) { switch(m_mouse.phase) { case 0: m_mouse.xa = m_mouse.x > m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE; m_mouse.xb = m_mouse.x < m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE; m_mouse.ya = m_mouse.y > m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE; m_mouse.yb = m_mouse.y < m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE; break; case 1: m_mouse.xa = m_mouse.xb = m_mouse.x != m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE; m_mouse.ya = m_mouse.yb = m_mouse.y != m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE; break; case 2: m_mouse.xa = m_mouse.x < m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE; m_mouse.xb = m_mouse.x > m_mouse.prev_x ? CLEAR_LINE : ASSERT_LINE; m_mouse.ya = m_mouse.y < m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE; m_mouse.yb = m_mouse.y > m_mouse.prev_y ? CLEAR_LINE : ASSERT_LINE; break; case 3: m_mouse.xa = m_mouse.xb = ASSERT_LINE; m_mouse.ya = m_mouse.yb = ASSERT_LINE; m_mouse.prev_x = m_mouse.x; m_mouse.prev_y = m_mouse.y; m_mouse.x = m_mouse_x->read(); m_mouse.y = m_mouse_y->read(); break; } m_mouse.phase = (m_mouse.phase + 1) & 3; } } WRITE8_MEMBER(pcd_video_device::vram_w) { if(m_vram_sw) m_vram[offset] = data; else if(!(offset & 1)) { offset >>= 1; m_charram[offset & 0x1fff] = data; gfx(0)->mark_dirty(offset/16); } } READ8_MEMBER(pcd_video_device::vram_r) { return m_vram[offset]; } WRITE8_MEMBER(pcd_video_device::vram_sw_w) { m_vram_sw = data & 1; } READ_LINE_MEMBER(pcd_video_device::t1_r) { return m_t1; } READ8_MEMBER(pcd_video_device::p1_r) { uint8_t data = (m_mouse_btn->read() & 0x30) | 0x80; // char ram/rom jumper? data |= (m_mouse.xa != CLEAR_LINE ? 0 : 1); data |= (m_mouse.xb != CLEAR_LINE ? 0 : 2); data |= (m_mouse.ya != CLEAR_LINE ? 0 : 4); data |= (m_mouse.yb != CLEAR_LINE ? 0 : 8); return data; } WRITE8_MEMBER(pcd_video_device::p2_w) { m_p2 = data; m_pic2->ir7_w((data & 0x80) ? CLEAR_LINE : ASSERT_LINE); } READ8_MEMBER(pcx_video_device::term_r) { switch(offset) { case 0: m_pic2->ir0_w(CLEAR_LINE); m_term_stat &= ~2; return m_term_key; case 1: m_pic2->ir0_w(CLEAR_LINE); return m_term_stat >> 1; } return 0xff; } WRITE8_MEMBER(pcx_video_device::term_w) { if(!offset) { m_mcu->set_input_line(MCS51_INT1_LINE, ASSERT_LINE); m_term_char = data; m_term_stat |= 4; } } READ8_MEMBER(pcx_video_device::term_mcu_r) { switch(offset) { case 0: m_mcu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE); m_pic2->ir0_w(ASSERT_LINE); m_term_stat &= ~4; return m_term_char; case 1: return m_term_stat; } return 0; } WRITE8_MEMBER(pcx_video_device::term_mcu_w) { if(!offset) { m_term_key = data; m_pic2->ir0_w(ASSERT_LINE); m_term_stat |= 2; } } READ8_MEMBER(pcdx_video_device::detect_r) { return 0; } WRITE8_MEMBER(pcdx_video_device::detect_w) { } READ8_MEMBER(pcx_video_device::unk_r) { return 0x80; } WRITE8_MEMBER(pcx_video_device::p1_w) { m_p1 = data; } void pcd_video_device::device_start() { m_maincpu->space(AS_IO).install_readwrite_handler(0xfb00, 0xfb01, read8_delegate(*this, FUNC(pcdx_video_device::detect_r)), write8_delegate(*this, FUNC(pcdx_video_device::detect_w)), 0xff00); m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xf0000, 0xf7fff, read8_delegate(*this, FUNC(pcd_video_device::vram_r)), write8_delegate(*this, FUNC(pcd_video_device::vram_w)), 0xffff); set_gfx(0, std::make_unique(&palette(), pcd_charlayout, &m_charram[0], 0, 1, 0)); } void pcd_video_device::device_reset() { m_mouse.phase = 0; m_mouse.xa = m_mouse.xb = ASSERT_LINE; m_mouse.ya = m_mouse.yb = ASSERT_LINE; m_mouse.x = m_mouse.y = 0; m_mouse.prev_x = m_mouse.prev_y = 0; m_vram_sw = 1; m_t1 = CLEAR_LINE; } void pcd_video_device::map(address_map &map) { map(0x00, 0x0f).w(m_crtc, FUNC(scn2674_device::write)).umask16(0x00ff); map(0x00, 0x0f).r(m_crtc, FUNC(scn2674_device::read)).umask16(0xff00); map(0x20, 0x20).w(FUNC(pcd_video_device::vram_sw_w)); map(0x30, 0x33).rw("graphics", FUNC(i8741a_device::upi41_master_r), FUNC(i8741a_device::upi41_master_w)).umask16(0x00ff); } void pcx_video_device::device_start() { m_maincpu->space(AS_IO).install_readwrite_handler(0xfb00, 0xfb01, read8_delegate(*this, FUNC(pcdx_video_device::detect_r)), write8_delegate(*this, FUNC(pcdx_video_device::detect_w)), 0x00ff); m_txd_handler.resolve_safe(); set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); set_rate(600*2); // FIXME: fix the keyboard when the mc2661 baud rate calc is fixed decode_gfx(gfx_pcx); } void pcx_video_device::device_reset() { m_term_key = 0; m_term_stat = 0; transmit_register_reset(); receive_register_reset(); m_txd_handler(1); } void pcx_video_device::map(address_map &map) { map(0x0, 0xf).rw(FUNC(pcx_video_device::term_r), FUNC(pcx_video_device::term_w)); } READ8_MEMBER(pcx_video_device::rx_callback) { return get_received_char(); } WRITE8_MEMBER(pcx_video_device::tx_callback) { transmit_register_setup(data); } void pcx_video_device::tra_callback() { m_txd_handler(transmit_register_get_data_bit()); } void pcx_video_device::rcv_complete() { receive_register_extract(); m_mcu->set_input_line(MCS51_RX_LINE, ASSERT_LINE); m_mcu->set_input_line(MCS51_RX_LINE, CLEAR_LINE); }