// license:BSD-3-Clause
// copyright-holders:James Wallace
/**********************************************************************
Rockwell 10937/10957 interface and emulation by J.Wallace
OKI MSC1937 is a clone of this chip
**********************************************************************/
#include "emu.h"
#include "roc10937.h"
#include <algorithm>
/*
Rockwell 10937 16 segment charset lookup table
0 1
---- ----
|\ | /|
7 | \F |8 /9| 2
| \ | / |
-E-- --A-
| / | \ |
6 | /D |C \B| 3
|/ | \|
---- ---- .11
5 4 ,10
In 14 segment mode, 0 represents the whole top line,
and 5 the bottom line, allowing both modes to share
a charset.
Note that, although we call this a '16 segment' display,
we actually have 18 segments, including the semicolon portions.
16-bit tables are used to hold the main characters, the rest are OR'd in
*/
static const uint16_t roc10937charset[]=
{ // FEDC BA98 7654 3210
0x507F, // 0101 0000 0111 1111 @.
0x44CF, // 0100 0100 1100 1111 A.
0x153F, // 0001 0101 0011 1111 B.
0x00F3, // 0000 0000 1111 0011 C.
0x113F, // 0001 0001 0011 1111 D.
0x40F3, // 0100 0000 1111 0011 E.
0x40C3, // 0100 0000 1100 0011 F.
0x04FB, // 0000 0100 1111 1011 G.
0x44CC, // 0100 0100 1100 1100 H.
0x1133, // 0001 0001 0011 0011 I.
0x007C, // 0000 0000 0111 1100 J.
0x4AC0, // 0100 1010 1100 0000 K.
0x00F0, // 0000 0000 1111 0000 L.
0x82CC, // 1000 0010 1100 1100 M.
0x88CC, // 1000 1000 1100 1100 N.
0x00FF, // 0000 0000 1111 1111 O.
0x44C7, // 0100 0100 1100 0111 P.
0x08FF, // 0000 1000 1111 1111 Q.
0x4CC7, // 0100 1100 1100 0111 R.
0x44BB, // 0100 0100 1011 1011 S.
0x1103, // 0001 0001 0000 0011 T.
0x00FC, // 0000 0000 1111 1100 U.
0x22C0, // 0010 0010 1100 0000 V.
0x28CC, // 0010 1000 1100 1100 W.
0xAA00, // 1010 1010 0000 0000 X.
0x9200, // 1001 0010 0000 0000 Y.
0x2233, // 0010 0010 0011 0011 Z.
0x00E1, // 0000 0000 1110 0001 [.
0x8800, // 1000 1000 0000 0000 \.
0x001E, // 0000 0000 0001 1110 ].
0x2800, // 0010 1000 0000 0000 ^.
0x0030, // 0000 0000 0011 0000 _.
0x0000, // 0000 0000 0000 0000 dummy.
0x8121, // 1000 0001 0010 0001 !.
0x0180, // 0000 0001 1000 0000 ".
0x553C, // 0101 0101 0011 1100 #.
0x55BB, // 0101 0101 1011 1011 $.
0x7799, // 0111 0111 1001 1001 %.
0xC979, // 1100 1001 0111 1001 &.
0x0200, // 0000 0010 0000 0000 '.
0x0A00, // 0000 1010 0000 0000 (.
0xA050, // 1010 0000 0000 0000 ).
0xFF00, // 1111 1111 0000 0000 *.
0x5500, // 0101 0101 0000 0000 +.
0x0000, // 0000 0000 0000 0000 ;. (Set separately)
0x4400, // 0100 0100 0000 0000 --.
0x0000, // 0000 0000 0000 0000 . .(Set separately)
0x2200, // 0010 0010 0000 0000 /.
0x22FF, // 0010 0010 1111 1111 0.
0x1100, // 0001 0001 0000 0000 1.
0x4477, // 0100 0100 0111 0111 2.
0x443F, // 0100 0100 0011 1111 3.
0x448C, // 0100 0100 1000 1100 4.
0x44BB, // 0100 0100 1011 1011 5.
0x44FB, // 0100 0100 1111 1011 6.
0x000F, // 0000 0000 0000 1111 7.
0x44FF, // 0100 0100 1111 1111 8.
0x44BF, // 0100 0100 1011 1111 9.
0x0021, // 0000 0000 0010 0001 -
// -.
0x2001, // 0010 0000 0000 0001 -
// /.
0x2230, // 0010 0010 0011 0000 <.
0x4430, // 0100 0100 0011 0000 =.
0x8830, // 1000 1000 0011 0000 >.
0x1407, // 0001 0100 0000 0111 ?.
};
///////////////////////////////////////////////////////////////////////////
static const int roc10937poslut[]=
{
1,//0
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
0//15
};
rocvfd_device::rocvfd_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
m_outputs(),
m_port_val(0)
{
}
void rocvfd_device::device_start()
{
m_outputs = std::make_unique<output_finder<16> >(*this, "vfd%u", unsigned(m_port_val * 16));
m_outputs->resolve();
save_item(NAME(m_cursor_pos));
save_item(NAME(m_window_size));
save_item(NAME(m_shift_count));
save_item(NAME(m_shift_data));
save_item(NAME(m_pcursor_pos));
save_item(NAME(m_chars));
save_item(NAME(m_brightness));
save_item(NAME(m_count));
save_item(NAME(m_sclk));
save_item(NAME(m_data));
save_item(NAME(m_duty));
save_item(NAME(m_disp));
}
void rocvfd_device::device_reset()
{
m_cursor_pos = 0;
m_window_size = 16;
m_shift_count = 0;
m_shift_data = 0;
m_pcursor_pos = 0;
m_brightness =31;
m_count=0;
m_duty=31;
m_disp = 0;
m_sclk = 0;
m_data = 0;
std::fill(std::begin(m_chars), std::end(m_chars), 0);
std::fill(std::begin(*m_outputs), std::end(*m_outputs), 0);
}
///////////////////////////////////////////////////////////////////////////
uint32_t rocvfd_device::set_display(uint32_t segin)
{
return bitswap<32>(segin, 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,11,9,15,13,12,8,10,14,7,6,5,4,3,2,1,0);
}
///////////////////////////////////////////////////////////////////////////
void rocvfd_device::device_post_load()
{
update_display();
}
void rocvfd_device::update_display()
{
std::transform(std::begin(m_chars), std::end(m_chars), std::begin(*m_outputs), set_display);
}
WRITE_LINE_MEMBER( rocvfd_device::sclk )
{
shift_clock(state);
}
WRITE_LINE_MEMBER( rocvfd_device::data )
{
m_data = state;
}
WRITE_LINE_MEMBER( rocvfd_device::por )
{
//If line goes low, reset mode is engaged, until such a time as it goes high again.
if (!state)
{
reset();
}
}
void rocvfd_device::shift_clock(int state)
{
if (m_sclk != state)
{
//Clock data on FALLING edge
if (!m_sclk)
{
m_shift_data <<= 1;
if ( m_data ) m_shift_data |= 1;
if ( ++m_shift_count >= 8 )
{
write_char(m_shift_data);
m_shift_count = 0;
m_shift_data = 0;
}
update_display();
}
}
m_sclk = state;
}
///////////////////////////////////////////////////////////////////////////
DEFINE_DEVICE_TYPE(ROC10937, roc10937_device, "roc10937", "Rockwell 10937 VFD controller") // and compatible
DEFINE_DEVICE_TYPE(MSC1937, msc1937_device, "msc1937", "OKI MSC1937 VFD controller")
DEFINE_DEVICE_TYPE(MIC10937, mic10937_device, "mic10937", "Micrel MIC10937 VFD controller")
DEFINE_DEVICE_TYPE(ROC10957, roc10957_device, "roc10957", "Rockwell 10957 VFD controller") // and compatible
DEFINE_DEVICE_TYPE(S16LF01, s16lf01_device, "s16lf01", "Samsung 16LF01 Series VFD controller") // and compatible
roc10937_device::roc10937_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: rocvfd_device(mconfig, ROC10937, tag, owner, clock)
{
}
msc1937_device::msc1937_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: rocvfd_device(mconfig, MSC1937, tag, owner, clock)
{
}
mic10937_device::mic10937_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: rocvfd_device(mconfig, MIC10937, tag, owner, clock)
{
}
s16lf01_device::s16lf01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: rocvfd_device(mconfig, S16LF01, tag, owner, clock)
{
}
void rocvfd_device::write_char(int data)
{
if ( data & 0x80 )
{ // Control data received
if ( (data & 0xF0) == 0xA0 ) // 1010 xxxx
{ // 1 010 xxxx Buffer Pointer control
m_cursor_pos = roc10937poslut[data & 0x0F];
}
else if ( (data & 0xF0) == 0xC0 ) // 1100 xxxx
{ // 1100 xxxx Set number of digits
data &= 0x0F;
if ( data == 0 ) m_window_size = 16;
else m_window_size = data;
}
else if ( (data & 0xE0) == 0xE0 ) // 111x xxxx
{ // 111x xxxx Set duty cycle ( brightness )
m_brightness = (data & 0x1F);
}
else if ( (data & 0xE0) == 0x80 ) // 100x ---
{ // 100x xxxx Test mode
m_duty =4;
}
}
else
{ // Display data
// data &= 0x3F;
switch ( data )
{
case 0x2C: // ;
m_chars[m_pcursor_pos] |= (1<<16);//.
m_chars[m_pcursor_pos] |= (1<<17);//,
break;
case 0x2E: //
m_chars[m_pcursor_pos] |= (1<<16);//.
break;
default :
m_pcursor_pos = m_cursor_pos;
m_chars[m_cursor_pos] = roc10937charset[data & 0x3F];
m_cursor_pos++;
if ( m_cursor_pos > (m_window_size -1) )
{
m_cursor_pos = 0;
}
break;
}
}
}
roc10957_device::roc10957_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: rocvfd_device(mconfig, ROC10957, tag, owner, clock)
{
}
void roc10957_device::write_char(int data)
{
if ( data & 0x80 )
{ // Control data received
if ( (data & 0xF0) == 0xA0 ) // 1010 xxxx
{ // 1 010 xxxx Buffer Pointer control
m_cursor_pos = roc10937poslut[data & 0x0F];
}
else if ( (data & 0xF0) == 0xC0 ) // 1100 xxxx
{ // 1100 xxxx Set number of digits
data &= 0x0F;
if ( data == 0 ) m_window_size = 16;
else m_window_size = data;
}
else if ( (data & 0xE0) == 0xE0 ) // 111x xxxx
{ // 111x xxxx Set duty cycle ( brightness )
m_brightness = (data & 0x1F);
}
else if ( (data & 0xE0) == 0x80 ) // 100x ---
{ // 100x xxxx Test mode
popmessage("TEST MODE ENABLED!");
m_duty = 4;
}
}
else
{ // Display data. Bit 6 is a "don't care" bit except for PNT and TAIL.
data &= 0x7F;
switch ( data )
{
case 0x2C: // ;
m_chars[m_pcursor_pos] |= (1<<16);//.
m_chars[m_pcursor_pos] |= (1<<17);//,
break;
case 0x2E: //
m_chars[m_pcursor_pos] |= (1<<16);//.
break;
case 0x6C: // ;
m_chars[m_pcursor_pos] |= (1<<16);//.
break;
case 0x6E: //
{
m_chars[m_pcursor_pos] = 0;
}
break;
default :
m_pcursor_pos = m_cursor_pos;
m_chars[m_cursor_pos] = roc10937charset[data & 0x3F];
m_cursor_pos++;
if ( m_cursor_pos > (m_window_size -1) )
{
m_cursor_pos = 0;
}
break;
}
}
}