// 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<gfx_element>(&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);
}