// 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 }; //-------------------------------------------------------------------------------------------------------------------------------------------- void esq2x16_sq1_device::device_add_mconfig(machine_config &config) { config.set_default_layout(layout_esq2by16); } //-------------------------------------------------------------------------------------------------------------------------------------------- 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; }