// license:BSD-3-Clause
// copyright-holders:Parduz
/*
Ensoniq LCD dotmatrix Displays
derived by Parduz from the VFD Emulation by R. Belmont
*/
#include "emu.h"
#include "esqlcd.h"
#include "esq2by16.lh"
//#define VERBOSE 1
#include "logmacro.h"
DEFINE_DEVICE_TYPE(ESQ2X16_SQ1, esq2x16_sq1_device, "esq2x16_sq1", "Ensoniq 2x16 VFD (SQ-1 variant)")
// --- SQ1 - Parduz --------------------------------------------------------------------------------------------------------------------------
/*! \file font5x7.h \brief Graphic LCD Font (Ascii Characters). */
//*****************************************************************************
//
// File Name : 'font5x7.h'
// Title : Graphic LCD Font (Ascii Charaters)
// Author : Pascal Stang
// Date : 10/19/2001
// Revised : 10/19/2001
// Version : 0.1
// Target MCU : Atmel AVR
// Editor Tabs : 4
//
//*****************************************************************************
// standard ascii 5x7 font
// defines ascii characters 0x20-0x7F (32-127)
static unsigned char const Font5x7[][5] = {
{0x00, 0x00, 0x08, 0x00, 0x00}, // _Undef_ 0x00 - dots for debug purposes
{0x01, 0x00, 0x00, 0x00, 0x40}, // _Undef_ 0x01 - dots for debug purposes
{0x02, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x02
{0x03, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x03
{0x04, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x04
{0x05, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x05
{0x06, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x06
{0x07, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x07
{0x20, 0x70, 0x3F, 0x00, 0x00}, // Croma 0x08
{0x20, 0x70, 0x3F, 0x02, 0x0C}, // Croma 0x09
{0x20, 0x70, 0x3F, 0x05, 0x0A}, // Croma 0x0A
{0x20, 0x70, 0x3F, 0x15, 0x2A}, // Croma 0x0B
{0x20, 0x50, 0x50, 0x3F, 0x00}, // Croma 0x0C
{0x0D, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x0D
{0x0E, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x0E
{0x0F, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x0F
{0x10, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x10
{0x11, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x11
{0x12, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x12
{0x13, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x13
{0x14, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x14
{0x15, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x15
{0x16, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x16
{0x17, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x17
{0x18, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x18
{0x19, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x19
{0x1A, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1A
{0x1B, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1B
{0x1C, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1C
{0x1D, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1D
{0x1E, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1E
{0x1F, 0x00, 0x00, 0x00, 0x00}, // _Undef_ 0x1F
{0x00, 0x00, 0x00, 0x00, 0x00}, // (space) 0x20
{0x00, 0x00, 0x5F, 0x00, 0x00}, // ! 0x21
{0x00, 0x07, 0x00, 0x07, 0x00}, // " 0x22
{0x14, 0x7F, 0x14, 0x7F, 0x14}, // # 0x23
{0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $ 0x24
{0x23, 0x13, 0x08, 0x64, 0x62}, // % 0x25
{0x36, 0x49, 0x55, 0x22, 0x50}, // & 0x26
{0x00, 0x05, 0x03, 0x00, 0x00}, // ' 0x27
{0x00, 0x1C, 0x22, 0x41, 0x00}, // ( 0x28
{0x00, 0x41, 0x22, 0x1C, 0x00}, // ) 0x29
{0x08, 0x2A, 0x1C, 0x2A, 0x08}, // * 0x2A
{0x08, 0x08, 0x3E, 0x08, 0x08}, // + 0x2B
{0x00, 0x50, 0x30, 0x00, 0x00}, // , 0x2C
{0x08, 0x08, 0x08, 0x08, 0x08}, // - 0x2D
{0x00, 0x60, 0x60, 0x00, 0x00}, // . 0x2E
{0x20, 0x10, 0x08, 0x04, 0x02}, // / 0x2F
{0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 0x30
{0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 0x31
{0x42, 0x61, 0x51, 0x49, 0x46}, // 2 0x32
{0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 0x33
{0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 0x34
{0x27, 0x45, 0x45, 0x45, 0x39}, // 5 0x35
{0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6 0x36
{0x01, 0x71, 0x09, 0x05, 0x03}, // 7 0x37
{0x36, 0x49, 0x49, 0x49, 0x36}, // 8 0x38
{0x06, 0x49, 0x49, 0x29, 0x1E}, // 9 0x39
{0x00, 0x36, 0x36, 0x00, 0x00}, // : 0x3A
{0x00, 0x56, 0x36, 0x00, 0x00}, // ; 0x3B
{0x00, 0x08, 0x14, 0x22, 0x41}, // < 0x3C
{0x14, 0x14, 0x14, 0x14, 0x14}, // = 0x3D
{0x41, 0x22, 0x14, 0x08, 0x00}, // > 0x3E
{0x02, 0x01, 0x51, 0x09, 0x06}, // ? 0x3F
{0x32, 0x49, 0x79, 0x41, 0x3E}, // @ 0x40
{0x7E, 0x11, 0x11, 0x11, 0x7E}, // A 0x41
{0x7F, 0x49, 0x49, 0x49, 0x36}, // B 0x42
{0x3E, 0x41, 0x41, 0x41, 0x22}, // C 0x43
{0x7F, 0x41, 0x41, 0x22, 0x1C}, // D 0x44
{0x7F, 0x49, 0x49, 0x49, 0x41}, // E 0x45
{0x7F, 0x09, 0x09, 0x01, 0x01}, // F 0x46
{0x3E, 0x41, 0x41, 0x51, 0x32}, // G 0x47
{0x7F, 0x08, 0x08, 0x08, 0x7F}, // H 0x48
{0x00, 0x41, 0x7F, 0x41, 0x00}, // I 0x49
{0x20, 0x40, 0x41, 0x3F, 0x01}, // J 0x4A
{0x7F, 0x08, 0x14, 0x22, 0x41}, // K 0x4B
{0x7F, 0x40, 0x40, 0x40, 0x40}, // L 0x4C
{0x7F, 0x02, 0x04, 0x02, 0x7F}, // M 0x4D
{0x7F, 0x04, 0x08, 0x10, 0x7F}, // N 0x4E
{0x3E, 0x41, 0x41, 0x41, 0x3E}, // O 0x4F
{0x7F, 0x09, 0x09, 0x09, 0x06}, // P 0x50
{0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q 0x51
{0x7F, 0x09, 0x19, 0x29, 0x46}, // R 0x52
{0x46, 0x49, 0x49, 0x49, 0x31}, // S 0x53
{0x01, 0x01, 0x7F, 0x01, 0x01}, // T 0x54
{0x3F, 0x40, 0x40, 0x40, 0x3F}, // U 0x55
{0x1F, 0x20, 0x40, 0x20, 0x1F}, // V 0x56
{0x7F, 0x20, 0x18, 0x20, 0x7F}, // W 0x57
{0x63, 0x14, 0x08, 0x14, 0x63}, // X 0x58
{0x03, 0x04, 0x78, 0x04, 0x03}, // Y 0x59
{0x61, 0x51, 0x49, 0x45, 0x43}, // Z 0x5A
{0x00, 0x00, 0x7F, 0x41, 0x41}, // [ 0x5B
{0x02, 0x04, 0x08, 0x10, 0x20}, // \ 0x5C
{0x41, 0x41, 0x7F, 0x00, 0x00}, // ] 0x5D
{0x04, 0x02, 0x01, 0x02, 0x04}, // ^ 0x5E
{0x40, 0x40, 0x40, 0x40, 0x40}, // _ 0x5F
{0x00, 0x01, 0x02, 0x04, 0x00}, // ` 0x60
{0x20, 0x54, 0x54, 0x54, 0x78}, // a 0x61
{0x7F, 0x48, 0x44, 0x44, 0x38}, // b 0x62
{0x38, 0x44, 0x44, 0x44, 0x20}, // c 0x63
{0x38, 0x44, 0x44, 0x48, 0x7F}, // d 0x64
{0x38, 0x54, 0x54, 0x54, 0x18}, // e 0x65
{0x08, 0x7E, 0x09, 0x01, 0x02}, // f 0x66
{0x08, 0x14, 0x54, 0x54, 0x3C}, // g 0x67
{0x7F, 0x08, 0x04, 0x04, 0x78}, // h 0x68
{0x00, 0x44, 0x7D, 0x40, 0x00}, // i 0x69
{0x20, 0x40, 0x44, 0x3D, 0x00}, // j 0x6A
{0x00, 0x7F, 0x10, 0x28, 0x44}, // k 0x6B
{0x00, 0x41, 0x7F, 0x40, 0x00}, // l 0x6C
{0x7C, 0x04, 0x18, 0x04, 0x78}, // m 0x6D
{0x7C, 0x08, 0x04, 0x04, 0x78}, // n 0x6E
{0x38, 0x44, 0x44, 0x44, 0x38}, // o 0x6F
{0x7C, 0x14, 0x14, 0x14, 0x08}, // p 0x70
{0x08, 0x14, 0x14, 0x18, 0x7C}, // q 0x71
{0x7C, 0x08, 0x04, 0x04, 0x08}, // r 0x72
{0x48, 0x54, 0x54, 0x54, 0x20}, // s 0x73
{0x04, 0x3F, 0x44, 0x40, 0x20}, // t 0x74
{0x3C, 0x40, 0x40, 0x20, 0x7C}, // u 0x75
{0x1C, 0x20, 0x40, 0x20, 0x1C}, // v 0x76
{0x3C, 0x40, 0x30, 0x40, 0x3C}, // w 0x77
{0x44, 0x28, 0x10, 0x28, 0x44}, // x 0x78
{0x0C, 0x50, 0x50, 0x50, 0x3C}, // y 0x79
{0x44, 0x64, 0x54, 0x4C, 0x44}, // z 0x7A
{0x00, 0x08, 0x36, 0x41, 0x00}, // { 0x7B
{0x00, 0x00, 0x7F, 0x00, 0x00}, // | 0x7C
{0x00, 0x41, 0x36, 0x08, 0x00}, // } 0x7D
{0x08, 0x08, 0x2A, 0x1C, 0x08}, // -> 0x7E
{0x08, 0x1C, 0x2A, 0x08, 0x08} // <- 0x7F
};
//--------------------------------------------------------------------------------------------------------------------------------------------
MACHINE_CONFIG_START(esq2x16_sq1_device::device_add_mconfig)
MCFG_DEFAULT_LAYOUT(layout_esq2by16)
MACHINE_CONFIG_END
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::write_char(int data)
{
int DisplayCode = data;
int LedState;
// Non-ASCII codes that needs to be treated as ASCII characters
if (
data == 0x08 ||
data == 0x09 ||
data == 0x0A ||
data == 0x0B ||
data == 0x0C
) data = '^'; // musical notes
// Resolve here 2-Bytes commands: the command was saved previously
switch (m_lcd_command) {
case 0:
// No current command.
break;
case 0x87:
// Go To
LOG("LCD %02X: Go To %02X - pos=%02X (%d)\n", m_lcd_command, DisplayCode, m_lcdPos, m_lcdPage);
m_lcdPos = DisplayCode;
m_lcd_command = 0;
return;
case 0x88:
// Save Cursor position - What the second byte (00 or 01) means?
LOG("LCD %02X: Save Pos. (%02X) - pos=%02X (%d)\n", m_lcd_command, DisplayCode, m_lcdPos, m_lcdPage);
m_lcdSavedPos = m_lcdPos;
m_lcd_command = 0;
return;
case 0x89:
// Restore Cursor position - What the second byte (00 or 01) means?
LOG("LCD %02X: Restore Pos. (%02X) - pos=%02X (%d)\n", m_lcd_command, DisplayCode, m_lcdPos, m_lcdPage);
m_lcdPos = m_lcdSavedPos;
m_lcd_command = 0;
return;
case 0x8D:
case 0x8E:
case 0x8F:
// LED OFF, ON, BLINK
LedState = m_lcd_command & 0x03;
if (
DisplayCode >= 16 || // Out of bounds
DisplayCode == 6 || // non-existent
DisplayCode == 7 || // non-existent
DisplayCode == 14 || // non-existent
DisplayCode == 15 // non-existent
)
{
LOG("LCD %02X: Led %02d doesn't exist - pos=%02X (%d)\n", m_lcd_command, DisplayCode, m_lcdPos, m_lcdPage);
}
else
{
m_leds[DisplayCode] = LedState;
update_display();
}
m_lcd_command = 0;
return;
default:
LOG("LCD: Unknown 2-Bytes Command:%02X-%02X - pos=%02X (%d)\n", m_lcd_command, DisplayCode, m_lcdPos, m_lcdPage);
m_lcd_command = 0;
return;
}
if ((data >= 0x20) && (data <= 0x7f))
{
LOG("LCD %02X: \"%c\" - pos=%02X (%d)\n", DisplayCode, data, m_lcdPos, m_lcdPage);
m_lcdpg[m_lcdPage][m_lcdPos++] = DisplayCode;
if (m_lcdPos > 31) m_lcdPos = 31;
update_display();
return;
}
if (DisplayCode >= 0x80)
{
switch (DisplayCode) {
// Known 2-bytes command
case 0x87: // Go To
case 0x88: // Save Cursor Position
case 0x89: // Restore Cursor Position
// Save the command for the next byte
m_lcd_command = DisplayCode;
return;
// Unknown 2-bytes command
case 0x85:
case 0x86:
case 0x95:
// ??? - related to blinking chars? - 2 bytes command
m_lcd_command = DisplayCode;
return;
case 0x8D:
case 0x8E:
case 0x8F:
// LEDs OFF,ON and BLINK - 2 bytes command
m_lcd_command = DisplayCode;
return;
// "Page" selectors (?)
case 0x90: // Blink
case 0x91: // ??
case 0x92: // Normal
m_lcdPage = DisplayCode & 0x03;
LOG("LCD %02X: Page Change ? - pos=%02X (%d)\n", DisplayCode, m_lcdPos, m_lcdPage);
m_lcd_command = 0;
return;
case 0x8C:
LOG("LCD %02X: Lcd Clear - pos=%02X (%d)\n", DisplayCode, m_lcdPos, m_lcdPage);
lcd_reset();
return;
case 0x98:
LOG("LCD %02X: Page Clear ? - pos=%02X (%d)\n", DisplayCode, m_lcdPos, m_lcdPage);
page_reset();
return;
default:
LOG("LCD %02X: Unknown Command - pos=%02X (%d)\n", DisplayCode, m_lcdPos, m_lcdPage);
m_lcd_command = 0;
return;
}
}
else
{
LOG("LCD: Unknown LCD Code: %04X - pos=%02X (%d)\n", data, m_lcdPos, m_lcdPage);
}
}
//--------------------------------------------------------------------------------------------------------------------------------------------
esq2x16_sq1_device::esq2x16_sq1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
esqvfd_device(mconfig, ESQ2X16_SQ1, tag, owner, clock, make_dimensions<2, 16>(*this)),
m_lcdPix(*this, "pg_%u%03u", 1U, 0U),
m_leds(*this, "rLed_%u", 0U)
{
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::update_display()
{
for (int page = 0; page < 4; page++)
{
for (int pos = 0; pos < 32; pos++)
{
// stealed from tecnbras.cpp and modified
for (int rr = 0; rr < 7; rr++)
{
char lcdCharRow = rotate_lcd_char(m_lcdpg[page][pos],rr);
m_lcdPix[page][pos*7 + rr] = 0x1f & lcdCharRow;
}
}
}
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::device_start()
{
esqvfd_device::device_start();
m_lcdPix.resolve();
m_leds.resolve();
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::device_reset()
{
//lcd_reset();
m_lcdPage = m_lcdSavedPos = m_lcdPos = m_lcd_command = 0;
for (auto &led : m_leds) led = 0;
memset(m_lcdpg, 1, sizeof(m_lcdpg)); // Set to 1 for debug: to see what "pages" are set to 0 from the firmware
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::lcd_reset()
{
m_lcdPage = m_lcdSavedPos = m_lcdPos = m_lcd_command = 0;
for (auto &led : m_leds) led = 0;
memset(m_lcdpg, 0, sizeof(m_lcdpg));
}
//--------------------------------------------------------------------------------------------------------------------------------------------
void esq2x16_sq1_device::page_reset()
{
memset(m_lcdpg[m_lcdPage], 0, 32);
m_lcdPos = m_lcd_command = 0;
}
//--------------------------------------------------------------------------------------------------------------------------------------------
char esq2x16_sq1_device::rotate_lcd_char(uint8_t lcdChar, int charRow)
{
char lcdCharRow = 0;
for (int cc=0; cc<5; cc++){
lcdCharRow |= BIT(Font5x7[lcdChar][cc], charRow) ? (1 << (cc)) : 0;
}
return lcdCharRow;
}