// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller
/**********************************************************************
Rockwell 10788 General Purpose Keyboard and Display circuit
REGISTER DESCRIPTION
[ Opcodes IOL, I2 ]
NAME W/IO CS I/O CMD I/O Names
--------------------------------------------------------------
KTR 1 1 x x x 1 1 0 0 Transfer Keyboard Return
KTS 1 1 x x x 1 0 1 0 Transfer Keyboard Strobe
KLA 1 1 x x x 1 1 1 0 Load Display Register A
KLB 1 1 x x x 1 1 0 1 Load Display Register B
KDN 1 1 x x x 0 0 1 1 Turn On Display
KAF 1 1 x x x 1 0 1 1 Turn Off A
KBF 1 1 x x x 0 1 1 1 Turn Off B
KER 1 1 x x x 0 1 1 0 Reset Keyboard Error
Notes:
1.) W/IO is generated by the first word of the PPS IOL instruction.
2.) Polarities of I/O7, I/O6 and I/O5 must be the same as the
polarities of the chip select straps SC7, SC6 and SC5.
3.) KLA resets DA1-DA4 and DB1 and DB2 to VSS level. KLB resets
DB3 and DB4 to VSS level.
4.) KAF and KBF is used to blank the display without changing the
contents of display data registers.
5.) KAF resets output lines DA1, DA2, DA3, DA4, DB1 and DB2 to
VSS level. KBF resets output lines DB3 and DB4 to VSS level.
6.) KAF stops the circulation of the display register A, and KBF
stops the circulation of the display register B.
7.) KER takes a maximum of 10-bit times to complete (= 80 clocks)
Therefore, there must be at least 10 bit times between KER
and the next KTS instruction.
8.) This device has only been tested on the gts1 driver. It does
not use the keyboard. The digit data is inverted (so it stores
6 when we want to display 9).
**********************************************************************/
#include "emu.h"
#include "r10788.h"
//#define VERBOSE 1
#include "logmacro.h"
/*************************************
*
* Device interface
*
*************************************/
DEFINE_DEVICE_TYPE(R10788, r10788_device, "r10788", "Rockwell 10788 KDC")
r10788_device::r10788_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, R10788, tag, owner, clock)
, m_ktr(0), m_kts(0), m_kla(0), m_klb(0), m_mask_a(15), m_mask_b(15), m_ker(0)
, m_io_counter(0), m_scan_counter(0)
, m_display(*this)
{
}
/**
* @brief r10788_device::device_start device-specific startup
*/
void r10788_device::device_start()
{
save_item(NAME(m_reg));
save_item(NAME(m_ktr));
save_item(NAME(m_kts));
save_item(NAME(m_kla));
save_item(NAME(m_klb));
save_item(NAME(m_mask_a));
save_item(NAME(m_mask_b));
save_item(NAME(m_ker));
save_item(NAME(m_io_counter));
save_item(NAME(m_scan_counter));
m_timer = timer_alloc(FUNC(r10788_device::display_update), this);
// recurring timer every 36 cycles
m_timer->adjust(clocks_to_attotime(36), 0, clocks_to_attotime(36));
}
/**
* @brief r10788_device::device_reset device-specific reset
*/
void r10788_device::device_reset()
{
memset(m_reg, 0x00, sizeof(m_reg));
m_ktr = 0;
m_kts = 0;
m_kla = 0;
m_klb = 0;
m_mask_a = 15;
m_mask_b = 15;
m_ker = 0;
m_scan_counter = 0;
}
TIMER_CALLBACK_MEMBER(r10788_device::display_update)
{
uint8_t data = ((m_reg[1][m_scan_counter] & m_mask_b) << 4) | (m_reg[0][m_scan_counter] & m_mask_a);
LOG("display_update: scan counter:%2d data:%02x\n", m_scan_counter, data);
m_display(m_scan_counter, data, 0xff);
m_scan_counter = (m_scan_counter + 1) % 16;
}
/*************************************
*
* Constants
*
*************************************/
/*************************************
*
* Command access handlers
*
*************************************/
void r10788_device::io_w(offs_t offset, uint8_t data)
{
offset &= 15;
switch (offset)
{
case KTR: // Transfer Keyboard Return
LOG("%s: KTR data:%02x\n", __FUNCTION__, data);
m_ktr = data;
break;
case KTS: // Transfer Keyboard Strobe
LOG("%s: KTS data:%02x\n", __FUNCTION__, data);
m_kts = data;
break;
case KLA: // Load Display Register A
m_io_counter = (m_io_counter + 1) % 16;
LOG("%s: KLA [%2d] data:%02x\n", __FUNCTION__, m_io_counter, data);
m_kla = data;
m_reg[0][m_io_counter] = m_kla;
break;
case KLB: // Load Display Register B
LOG("%s: KLB [%2d] data:%02x\n", __FUNCTION__, m_io_counter, data);
m_klb = data;
m_reg[1][m_io_counter] = m_klb;
break;
case KDN: // Turn On Display
LOG("%s: KDN data:%02x\n", __FUNCTION__, data);
m_mask_a = 15;
m_mask_b = 15;
m_io_counter = 15;
break;
case KAF: // Turn Off A
LOG("%s: KAF data:%02x\n", __FUNCTION__, data);
m_mask_a = 0;
m_mask_b &= 12;
break;
case KBF: // Turn Off B
LOG("%s: KBF data:%02x\n", __FUNCTION__, data);
m_mask_b &= 3;
break;
case KER: // Reset Keyboard Error
LOG("%s: KER data:%02x\n", __FUNCTION__, data);
m_ker = 10;
break;
}
}
uint8_t r10788_device::io_r(offs_t offset)
{
offset &= 15;
uint8_t data = 0xf;
switch (offset)
{
case KTR: // Transfer Keyboard Return
data = m_ktr;
LOG("%s: KTR data:%02x\n", __FUNCTION__, data);
break;
case KTS: // Transfer Keyboard Strobe
data = m_kts;
LOG("%s: KTS data:%02x\n", __FUNCTION__, data);
break;
case KLA: // Load Display Register A
m_kla = m_reg[0][m_io_counter];
data = m_kla;
LOG("%s: KLA [%2d] data:%02x\n", __FUNCTION__, m_io_counter, data);
break;
case KLB: // Load Display Register B
m_klb = m_reg[1][m_io_counter];
data = m_klb;
LOG("%s: KLB [%2d] data:%02x\n", __FUNCTION__, m_io_counter, data);
break;
case KDN: // Turn On Display
LOG("%s: KDN data:%02x\n", __FUNCTION__, data);
break;
case KAF: // Turn Off A
LOG("%s: KAF data:%02x\n", __FUNCTION__, data);
break;
case KBF: // Turn Off B
LOG("%s: KBF data:%02x\n", __FUNCTION__, data);
break;
case KER: // Reset Keyboard Error
LOG("%s: KER data:%02x\n", __FUNCTION__, data);
break;
}
return data;
}