// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************
Zilog Z8536 Counter/Timer and Parallel I/O emulation
**********************************************************************/
/*
TODO:
- interrupts
- vector
- status affects vector
- IE/IP/IUS
- acknowledge
- daisy chain
- port I/O
- counters/timers
*/
#include "emu.h"
#include "z8536.h"
// device type definition
const device_type Z8536 = device_creator<z8536_device>;
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define LOG 0
static const char *PMS_PTS[] = { "Bit", "Input", "Output", "Bidirectional" };
static const char *PMS_PMS[] = { "Disabled", "AND", "OR", "OR-PEV" };
static const char *CTMS_DCS[] = { "Pulse", "One-shot", "Square Wave", "Do not use" };
// master interrupt control register
#define MICR_RESET 0x01 // reset
#define MICR_RJA 0x02 // right justified address
#define MICR_CT_VIS 0x04 // counter/timer vector includes status
#define MICR_PB_VIS 0x08 // port B vector includes status
#define MICR_PA_VIS 0x10 // port A vector includes status
#define MICR_NV 0x20 // no vector
#define MICR_DLC 0x40 // disable lower chain
#define MICR_MIE 0x80 // master interrupt enable
// master configuration control register
#define MCCR_LC_MASK 0x03 // counter/timer link controls
#define MCCR_PAE 0x04 // port A enable
#define MCCR_PLC 0x08 // port link control
#define MCCR_PCE_CT3E 0x10 // port C and counter/timer 3 enable
#define MCCR_CT2E 0x20 // counter/timer 2 enable
#define MCCR_CT1E 0x40 // counter/timer 1 enable
#define MCCR_PBE 0x80 // port B enable
// port mode specification registers
#define PMS_LPM 0x01 // latch on pattern match
#define PMS_DTE 0x01 // deskew timer enable
#define PMS_PMS_MASK 0x06 // pattern mode specification
#define PMS_IMO 0x08 // interrupt on match only
#define PMS_SB 0x10 // single buffer
#define PMS_ITB 0x20 // interrupt on two bytes
#define PMS_PTS_MASK 0xc0 // port type select
// port handshake specification registers
#define PHS_DTS_MASK 0x07 // deskew time specification
#define PHS_RWS_MASK 0x38 // request/wait specification
#define PHS_HTS_MASK 0xc0 // handshake type specification
// port command and status registers
#define PCS_IOE 0x01 // interrupt on error
#define PCS_PMF 0x02 // pattern match flag (read only)
#define PCS_IRF 0x04 // input register full (read only)
#define PCS_ORE 0x08 // output register empty (read only)
#define PCS_ERR 0x10 // interrupt error (read only)
#define PCS_IP 0x20 // interrupt pending
#define PCS_IE 0x40 // interrupt enable
#define PCS_IUS 0x80 // interrupt under service
// counter/timer mode specification registers
#define CTMS_DCS_MASK 0x03 // output duty cycle
#define CTMS_REB 0x04 // retrigger enable bit
#define CTMS_EDE 0x08 // external gate enable
#define CTMS_ETE 0x10 // external trigger enable
#define CTMS_ECE 0x20 // external count enable
#define CTMS_EOE 0x40 // external output enable
#define CTMS_CSC 0x80 // continuous/single cycle
// counter/timer command and status registers
#define CTCS_CIP 0x01 // count in progress (read only)
#define CTCS_TCB 0x02 // trigger command bit (write only - read returns 0)
#define CTCS_GCB 0x04 // gate command bit
#define CTCS_RCC 0x08 // read counter control (read/set only - cleared by reading CCR LSB)
#define CTCS_ERR 0x10 // interrupt error (read only)
#define CTCS_IP 0x20 // interrupt pending
#define CTCS_IE 0x40 // interrupt enable
#define CTCS_IUS 0x80 // interrupt under service
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
// get_interrupt_vector -
//-------------------------------------------------
void z8536_device::get_interrupt_vector()
{
uint8_t vector = 0xff;
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_MIE)
{
if ((m_register[COUNTER_TIMER_3_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE))
{
vector = m_register[COUNTER_TIMER_INTERRUPT_VECTOR];
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_CT_VIS)
{
vector = (vector & 0xf9) | 0;
}
}
else if ((m_register[PORT_A_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE | PCS_IUS)) == (PCS_IP | PCS_IE))
{
vector = m_register[PORT_A_INTERRUPT_VECTOR];
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_PA_VIS)
{
vector &= 0xf1;
if (((m_register[PORT_A_MODE_SPECIFICATION] & PMS_PMS_MASK) >> 1) == PMS_OR_PEV)
{
if (m_match[PORT_A] & 0x80) vector |= 7 << 1;
else if (m_match[PORT_A] & 0x40) vector |= 6 << 1;
else if (m_match[PORT_A] & 0x20) vector |= 5 << 1;
else if (m_match[PORT_A] & 0x10) vector |= 4 << 1;
else if (m_match[PORT_A] & 0x08) vector |= 3 << 1;
else if (m_match[PORT_A] & 0x04) vector |= 2 << 1;
else if (m_match[PORT_A] & 0x02) vector |= 1 << 1;
else if (m_match[PORT_A] & 0x01) vector |= 0 << 1;
}
else
{
vector |= (m_register[PORT_A_COMMAND_AND_STATUS] & 0x0e);
}
}
}
else if ((m_register[COUNTER_TIMER_2_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE))
{
vector = m_register[COUNTER_TIMER_INTERRUPT_VECTOR];
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_CT_VIS)
{
vector = (vector & 0xf9) | 2;
}
}
else if ((m_register[PORT_B_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE | PCS_IUS)) == (PCS_IP | PCS_IE))
{
vector = m_register[PORT_B_INTERRUPT_VECTOR];
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_PB_VIS)
{
vector &= 0xf1;
if (((m_register[PORT_B_MODE_SPECIFICATION] & PMS_PMS_MASK) >> 1) == PMS_OR_PEV)
{
if (m_match[PORT_B] & 0x80) vector |= 7 << 1;
else if (m_match[PORT_B] & 0x40) vector |= 6 << 1;
else if (m_match[PORT_B] & 0x20) vector |= 5 << 1;
else if (m_match[PORT_B] & 0x10) vector |= 4 << 1;
else if (m_match[PORT_B] & 0x08) vector |= 3 << 1;
else if (m_match[PORT_B] & 0x04) vector |= 2 << 1;
else if (m_match[PORT_B] & 0x02) vector |= 1 << 1;
else if (m_match[PORT_B] & 0x01) vector |= 0 << 1;
}
else
{
vector |= (m_register[PORT_B_COMMAND_AND_STATUS] & 0x0e);
}
}
}
else if ((m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE))
{
vector = m_register[COUNTER_TIMER_INTERRUPT_VECTOR];
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_CT_VIS)
{
vector = (vector & 0xf9) | 4;
}
}
}
m_register[CURRENT_VECTOR] = vector;
}
//-------------------------------------------------
// check_interrupt - check interrupt status
//-------------------------------------------------
void z8536_device::check_interrupt()
{
int state;
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_MIE)
{
if (((m_register[COUNTER_TIMER_3_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE)) ||
((m_register[PORT_A_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE | PCS_IUS)) == (PCS_IP | PCS_IE)) ||
((m_register[COUNTER_TIMER_2_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE)) ||
((m_register[PORT_B_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE | PCS_IUS)) == (PCS_IP | PCS_IE)) ||
((m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE | CTCS_IUS)) == (CTCS_IP | CTCS_IE)))
{
state = ASSERT_LINE;
}
else
{
state = CLEAR_LINE;
}
}
else
{
state = CLEAR_LINE;
}
if (m_irq != state)
{
if (LOG) logerror("%s Z8536 '%s' Interrupt: %u\n", machine().describe_context(), tag(), state);
m_irq = state;
m_write_irq(state);
}
}
//-------------------------------------------------
// read_register - read from register
//-------------------------------------------------
uint8_t z8536_device::read_register(offs_t offset)
{
uint8_t data;
switch (offset)
{
case PORT_A_DATA:
data = m_read_pa(0);
break;
case PORT_B_DATA:
data = m_read_pb(0);
break;
case PORT_C_DATA:
data = 0xf0 | (m_read_pc(0) & 0x0f);
break;
case COUNTER_TIMER_1_CURRENT_COUNT_MS_BYTE:
case COUNTER_TIMER_2_CURRENT_COUNT_MS_BYTE:
case COUNTER_TIMER_3_CURRENT_COUNT_MS_BYTE:
{
int timer = (offset - COUNTER_TIMER_1_CURRENT_COUNT_MS_BYTE) >> 1;
if (m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + timer] & CTCS_RCC)
{
// read frozen value
data = m_register[offset];
}
else
{
// read current count
data = m_counter[timer] >> 8;
}
}
break;
case COUNTER_TIMER_1_CURRENT_COUNT_LS_BYTE:
case COUNTER_TIMER_2_CURRENT_COUNT_LS_BYTE:
case COUNTER_TIMER_3_CURRENT_COUNT_LS_BYTE:
{
int timer = (offset - COUNTER_TIMER_1_CURRENT_COUNT_MS_BYTE) >> 1;
if (m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + timer] & CTCS_RCC)
{
// read frozen value
data = m_register[offset];
}
else
{
// read current count
data = m_counter[timer] & 0xff;
}
// clear RCC bit
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + timer] &= ~CTCS_RCC;
}
break;
case CURRENT_VECTOR:
get_interrupt_vector();
data = m_register[offset];
break;
default:
data = m_register[offset];
break;
}
return data;
}
//-------------------------------------------------
// read_register - masked read from register
//-------------------------------------------------
uint8_t z8536_device::read_register(offs_t offset, uint8_t mask)
{
return read_register(offset) & mask;
}
//-------------------------------------------------
// write_register - write to register
//-------------------------------------------------
void z8536_device::write_register(offs_t offset, uint8_t data)
{
switch (offset)
{
case MASTER_INTERRUPT_CONTROL:
if (data & MICR_RESET)
{
if (LOG) logerror("%s Z8536 '%s' Reset\n", machine().describe_context(), tag());
device_reset();
}
else
{
if (m_state == STATE_RESET)
{
m_state = STATE_0;
}
if (LOG)
{
if (LOG) logerror("%s Z8536 '%s' Master Interrupt Enable: %u\n", machine().describe_context(), tag(), (data & MICR_MIE) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Disable Lower Chain: %u\n", machine().describe_context(), tag(), (data & MICR_DLC) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' No Vector: %u\n", machine().describe_context(), tag(), (data & MICR_NV) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Port A Vector Includes Status: %u\n", machine().describe_context(), tag(), (data & MICR_PA_VIS) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Port B Vector Includes Status: %u\n", machine().describe_context(), tag(), (data & MICR_PB_VIS) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer Vector Includes Status: %u\n", machine().describe_context(), tag(), (data & MICR_CT_VIS) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Right Justified Address: %u\n", machine().describe_context(), tag(), (data & MICR_RJA) ? 1 : 0);
}
m_register[offset] = data;
}
break;
case MASTER_CONFIGURATION_CONTROL:
if (LOG)
{
if (LOG) logerror("%s Z8536 '%s' Port B Enable: %u\n", machine().describe_context(), tag(), (data & MCCR_PBE) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer 1 Enable: %u\n", machine().describe_context(), tag(), (data & MCCR_CT1E) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer 2 Enable: %u\n", machine().describe_context(), tag(), (data & MCCR_CT2E) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Port C and Counter/Timer 3 Enable: %u\n", machine().describe_context(), tag(), (data & MCCR_PCE_CT3E) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Port A Enable: %u\n", machine().describe_context(), tag(), (data & MCCR_PAE) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Port Link Control: %u\n", machine().describe_context(), tag(), (data & MCCR_PLC) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer Link Controls: %u\n", machine().describe_context(), tag(), data & MCCR_LC_MASK);
}
m_register[offset] = data;
for (int counter = 0; counter < 3; counter++)
{
// clear RCC bit if counter disabled
if (!counter_enabled(counter)) m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + counter] &= ~CTCS_RCC;
}
break;
case PORT_A_INTERRUPT_VECTOR:
if (LOG) logerror("%s Z8536 '%s' Port A Interrupt Vector: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case PORT_B_INTERRUPT_VECTOR:
if (LOG) logerror("%s Z8536 '%s' Port B Interrupt Vector: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case COUNTER_TIMER_INTERRUPT_VECTOR:
if (LOG) logerror("%s Z8536 '%s' Counter/Timer Interrupt Vector: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case PORT_C_DATA_PATH_POLARITY:
if (LOG) logerror("%s Z8536 '%s' Port C Data Path Polarity: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case PORT_C_DATA_DIRECTION:
if (LOG) logerror("%s Z8536 '%s' Port C Data Direction: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case PORT_C_SPECIAL_IO_CONTROL:
if (LOG) logerror("%s Z8536 '%s' Port C Special I/O Control: %02x\n", machine().describe_context(), tag(), data);
m_register[offset] = data;
break;
case PORT_A_COMMAND_AND_STATUS:
case PORT_B_COMMAND_AND_STATUS:
{
char port = 'A' + offset - PORT_A_COMMAND_AND_STATUS;
if (LOG) logerror("%s Z8536 '%s' Port %c Interrupt on Error: %u\n", machine().describe_context(), tag(), port, (data & PCS_IOE) ? 1 : 0);
switch (data >> 5)
{
case IC_CLEAR_IP_IUS: m_register[offset] &= ~(PCS_IP | PCS_IUS); if (LOG) logerror("%s Z8536 '%s' Port %c Clear IP/IUS\n", machine().describe_context(), tag(), port); break;
case IC_SET_IUS: m_register[offset] |= PCS_IUS; if (LOG) logerror("%s Z8536 '%s' Port %c Set IUS\n", machine().describe_context(), tag(), port); break;
case IC_CLEAR_IUS: m_register[offset] &= ~PCS_IUS; if (LOG) logerror("%s Z8536 '%s' Port %c Clear IUS\n", machine().describe_context(), tag(), port); break;
case IC_SET_IP: m_register[offset] |= PCS_IP; if (LOG) logerror("%s Z8536 '%s' Port %c Set IP\n", machine().describe_context(), tag(), port); break;
case IC_CLEAR_IP: m_register[offset] &= ~PCS_IP; if (LOG) logerror("%s Z8536 '%s' Port %c Clear IP\n", machine().describe_context(), tag(), port); break;
case IC_SET_IE: m_register[offset] |= PCS_IE; if (LOG) logerror("%s Z8536 '%s' Port %c Set IE\n", machine().describe_context(), tag(), port); break;
case IC_CLEAR_IE: m_register[offset] &= ~PCS_IE; if (LOG) logerror("%s Z8536 '%s' Port %c Clear IE\n", machine().describe_context(), tag(), port); break;
}
m_register[offset] = (m_register[offset] & ~PCS_IOE) | (data & PCS_IOE);
match_pattern(offset - PORT_A_COMMAND_AND_STATUS);
check_interrupt();
}
break;
case COUNTER_TIMER_1_COMMAND_AND_STATUS:
case COUNTER_TIMER_2_COMMAND_AND_STATUS:
case COUNTER_TIMER_3_COMMAND_AND_STATUS:
{
int counter = offset - COUNTER_TIMER_1_COMMAND_AND_STATUS;
if (LOG)
{
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Trigger Command Bit: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTCS_TCB) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Gate Command Bit: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTCS_GCB) ? 1 : 0);
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Read Counter Control: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTCS_RCC) ? 1 : 0);
}
switch (data >> 5)
{
case IC_CLEAR_IP_IUS: m_register[offset] &= ~(CTCS_IP | CTCS_IUS);if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Clear IP/IUS\n", machine().describe_context(), tag(), counter + 1); break;
case IC_SET_IUS: m_register[offset] |= CTCS_IUS; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Set IUS\n", machine().describe_context(), tag(), counter + 1); break;
case IC_CLEAR_IUS: m_register[offset] &= ~CTCS_IUS; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Clear IUS\n", machine().describe_context(), tag(), counter + 1); break;
case IC_SET_IP: m_register[offset] |= CTCS_IP; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Set IP\n", machine().describe_context(), tag(), counter + 1); break;
case IC_CLEAR_IP: m_register[offset] &= ~CTCS_IP; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Clear IP\n", machine().describe_context(), tag(), counter + 1); break;
case IC_SET_IE: m_register[offset] |= CTCS_IE; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Set IE\n", machine().describe_context(), tag(), counter + 1); break;
case IC_CLEAR_IE: m_register[offset] &= ~CTCS_IE; if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Clear IE\n", machine().describe_context(), tag(), counter + 1); break;
}
// gate command bit
m_register[offset] = (m_register[offset] & ~CTCS_GCB) | (data & CTCS_GCB);
// trigger command bit
if (data & CTCS_TCB)
{
trigger(counter);
}
// read counter control
if (counter_enabled(counter) && (data & CTCS_RCC))
{
// freeze current count register
m_register[offset] |= CTCS_RCC;
m_register[COUNTER_TIMER_1_CURRENT_COUNT_MS_BYTE + (counter << 1)] = m_counter[counter] >> 8;
m_register[COUNTER_TIMER_1_CURRENT_COUNT_LS_BYTE + (counter << 1)] = m_counter[counter] & 0xff;
}
check_interrupt();
}
break;
case PORT_A_DATA:
m_write_pa((offs_t)0, data);
break;
case PORT_B_DATA:
m_write_pb((offs_t)0, data);
break;
case PORT_C_DATA:
{
uint8_t mask = (data & 0xf0) | (data >> 4);
m_output[PORT_C] = (m_output[PORT_C] & mask) | ((data & 0x0f) & (mask ^ 0xff));
m_write_pc((offs_t)0, m_output[PORT_C]);
}
break;
case COUNTER_TIMER_1_TIME_CONSTANT_MS_BYTE:
case COUNTER_TIMER_2_TIME_CONSTANT_MS_BYTE:
case COUNTER_TIMER_3_TIME_CONSTANT_MS_BYTE:
if (LOG)
{
int counter = (offset - COUNTER_TIMER_1_TIME_CONSTANT_MS_BYTE) >> 1;
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Time Constant MSB: %02x\n", machine().describe_context(), tag(), counter + 1, data);
}
m_register[offset] = data;
break;
case COUNTER_TIMER_1_TIME_CONSTANT_LS_BYTE:
case COUNTER_TIMER_2_TIME_CONSTANT_LS_BYTE:
case COUNTER_TIMER_3_TIME_CONSTANT_LS_BYTE:
if (LOG)
{
int counter = (offset - COUNTER_TIMER_1_TIME_CONSTANT_LS_BYTE) >> 1;
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Time Constant LSB: %02x\n", machine().describe_context(), tag(), counter + 1, data);
}
m_register[offset] = data;
break;
case COUNTER_TIMER_1_MODE_SPECIFICATION:
case COUNTER_TIMER_2_MODE_SPECIFICATION:
case COUNTER_TIMER_3_MODE_SPECIFICATION:
if (LOG)
{
int counter = offset - COUNTER_TIMER_1_MODE_SPECIFICATION;
int dcs = data & CTMS_DCS_MASK;
logerror("%s Z8536 '%s' Counter/Timer %u Mode: %s\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_CSC) ? "Continuous" : "Single Cycle");
logerror("%s Z8536 '%s' Counter/Timer %u External Output Enable: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_EOE) ? 1 : 0);
logerror("%s Z8536 '%s' Counter/Timer %u External Count Enable: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_ECE) ? 1 : 0);
logerror("%s Z8536 '%s' Counter/Timer %u External Trigger Enable: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_ETE) ? 1 : 0);
logerror("%s Z8536 '%s' Counter/Timer %u External Gate Enable: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_EDE) ? 1 : 0);
logerror("%s Z8536 '%s' Counter/Timer %u Retrigger Enable: %u\n", machine().describe_context(), tag(), counter + 1, (data & CTMS_REB) ? 1 : 0);
logerror("%s Z8536 '%s' Counter/Timer %u Output Duty Cycle: %s\n", machine().describe_context(), tag(), counter + 1, CTMS_DCS[dcs]);
}
m_register[offset] = data;
break;
case PORT_A_MODE_SPECIFICATION:
case PORT_B_MODE_SPECIFICATION:
if (LOG)
{
char port = BIT(offset, 3) ? 'B' : 'A';
int pts = (data & PMS_PTS_MASK) >> 6;
int pms = (data & PMS_PMS_MASK) >> 1;
logerror("%s Z8536 '%s' Port %c Port Type: %s\n", machine().describe_context(), tag(), port, PMS_PTS[pts]);
logerror("%s Z8536 '%s' Port %c Interrupt on 2 Bytes: %u\n", machine().describe_context(), tag(), port, (data & PMS_ITB) ? 1 : 0);
logerror("%s Z8536 '%s' Port %c Single Buffer: %u\n", machine().describe_context(), tag(), port, (data & PMS_SB) ? 1 : 0);
logerror("%s Z8536 '%s' Port %c Interrupt on Match Only: %u\n", machine().describe_context(), tag(), port, (data & PMS_IMO) ? 1 : 0);
logerror("%s Z8536 '%s' Port %c Pattern Mode: %s\n", machine().describe_context(), tag(), port, PMS_PMS[pms]);
if (pts == PTS_BIT)
logerror("%s Z8536 '%s' Port %c Latch on Pattern Match: %u\n", machine().describe_context(), tag(), port, (data & PMS_LPM) ? 1 : 0);
else
logerror("%s Z8536 '%s' Port %c Deskew Timer Enable: %u\n", machine().describe_context(), tag(), port, (data & PMS_DTE) ? 1 : 0);
}
m_register[offset] = data;
break;
case PORT_A_HANDSHAKE_SPECIFICATION:
case PORT_B_HANDSHAKE_SPECIFICATION:
// TODO
break;
case PORT_A_DATA_PATH_POLARITY:
case PORT_B_DATA_PATH_POLARITY:
if (LOG) logerror("%s Z8536 '%s' Port %c Data Path Polarity: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
break;
case PORT_A_DATA_DIRECTION:
case PORT_B_DATA_DIRECTION:
if (LOG) logerror("%s Z8536 '%s' Port %c Data Direction: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
break;
case PORT_A_SPECIAL_IO_CONTROL:
case PORT_B_SPECIAL_IO_CONTROL:
if (LOG) logerror("%s Z8536 '%s' Port %c Special I/O Control: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
break;
case PORT_A_PATTERN_POLARITY:
case PORT_B_PATTERN_POLARITY:
if (LOG) logerror("%s Z8536 '%s' Port %c Pattern Polarity: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
break;
case PORT_A_PATTERN_TRANSITION:
case PORT_B_PATTERN_TRANSITION:
if (LOG) logerror("%s Z8536 '%s' Port %c Pattern Transition: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
break;
case PORT_A_PATTERN_MASK:
case PORT_B_PATTERN_MASK:
if (LOG) logerror("%s Z8536 '%s' Port %c Pattern Mask: %02x\n", machine().describe_context(), tag(), BIT(offset, 3) ? 'B' : 'A', data);
m_register[offset] = data;
match_pattern(BIT(offset, 3));
check_interrupt();
break;
default:
logerror("%s: Z8536 '%s' Unimplemented write %02x to register %u\n", machine().describe_context(), tag(), data, offset);
m_register[offset] = data;
}
}
//-------------------------------------------------
// write_register - masked write to register
//-------------------------------------------------
void z8536_device::write_register(offs_t offset, uint8_t data, uint8_t mask)
{
uint8_t combined_data = (data & mask) | (m_register[offset] & (mask ^ 0xff));
write_register(offset, combined_data);
}
//-------------------------------------------------
// counter_enabled - is counter enabled?
//-------------------------------------------------
bool z8536_device::counter_enabled(device_timer_id id)
{
bool enabled = false;
switch (id)
{
case TIMER_1:
enabled = (m_register[MASTER_CONFIGURATION_CONTROL] & MCCR_CT1E) ? true : false;
break;
case TIMER_2:
enabled = (m_register[MASTER_CONFIGURATION_CONTROL] & MCCR_CT2E) ? true : false;
break;
case TIMER_3:
enabled = (m_register[MASTER_CONFIGURATION_CONTROL] & MCCR_PCE_CT3E) ? true : false;
break;
}
return enabled;
}
//-------------------------------------------------
// counter_external_output -
//-------------------------------------------------
bool z8536_device::counter_external_output(device_timer_id id)
{
return (m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_EOE) ? true : false;
}
//-------------------------------------------------
// counter_external_count -
//-------------------------------------------------
bool z8536_device::counter_external_count(device_timer_id id)
{
return (m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_ECE) ? true : false;
}
//-------------------------------------------------
// counter_external_trigger -
//-------------------------------------------------
bool z8536_device::counter_external_trigger(device_timer_id id)
{
return (m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_ETE) ? true : false;
}
//-------------------------------------------------
// counter_external_gate -
//-------------------------------------------------
bool z8536_device::counter_external_gate(device_timer_id id)
{
return (m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_EDE) ? true : false;
}
//-------------------------------------------------
// counter_gated -
//-------------------------------------------------
bool z8536_device::counter_gated(device_timer_id id)
{
return (m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] & CTCS_GCB) ? true : false;
}
//-------------------------------------------------
// count - count down
//-------------------------------------------------
void z8536_device::count(device_timer_id id)
{
if (!counter_gated(id)) return;
if (!(m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] & CTCS_CIP)) return;
// count down
m_counter[id]--;
if (m_counter[id] == 0)
{
if (m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] & CTCS_IP)
{
// set interrupt error bit
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] |= CTCS_ERR;
}
else
{
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Interrupt Pending\n", machine().describe_context(), tag(), id + 1);
// set interrupt pending bit
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] |= CTCS_IP;
}
if (m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_CSC)
{
// reload counter with time constant
m_counter[id] = (m_register[COUNTER_TIMER_1_TIME_CONSTANT_MS_BYTE + (id << 1)] << 8) | m_register[COUNTER_TIMER_1_TIME_CONSTANT_LS_BYTE + (id << 1)];
}
else
{
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Terminal Count\n", machine().describe_context(), tag(), id + 1);
// clear count in progress bit
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] &= ~CTCS_CIP;
}
check_interrupt();
}
}
//-------------------------------------------------
// trigger -
//-------------------------------------------------
void z8536_device::trigger(device_timer_id id)
{
// ignore triggers during countdown if retrigger is disabled
if (!(m_register[COUNTER_TIMER_1_MODE_SPECIFICATION + id] & CTMS_REB) && (m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] & CTCS_CIP)) return;
if (LOG) logerror("%s Z8536 '%s' Counter/Timer %u Trigger\n", machine().describe_context(), tag(), id + 1);
// load counter with time constant
m_counter[id] = (m_register[COUNTER_TIMER_1_TIME_CONSTANT_MS_BYTE + (id << 1)] << 8) | m_register[COUNTER_TIMER_1_TIME_CONSTANT_LS_BYTE + (id << 1)];
// set count in progress bit
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS + id] |= CTCS_CIP;
}
//-------------------------------------------------
// gate -
//-------------------------------------------------
void z8536_device::gate(device_timer_id id, int state)
{
// TODO
}
//-------------------------------------------------
// match_pattern -
//-------------------------------------------------
void z8536_device::match_pattern(int port)
{
uint8_t pms = m_register[PORT_A_MODE_SPECIFICATION + (port << 3)];
uint8_t pm = m_register[PORT_A_PATTERN_MASK + (port << 3)];
uint8_t ddr = m_register[PORT_A_DATA_DIRECTION + (port << 3)];
switch ((pms & PMS_PMS_MASK) >> 1)
{
case PMS_OR_PEV:
m_match[port] = m_input[port] & ddr & pm;
if (m_match[port])
{
if (LOG) logerror("%s Z8536 '%s' Port %c Interrupt Pending\n", machine().describe_context(), tag(), 'A' + port);
m_register[PORT_A_COMMAND_AND_STATUS + port] |= PCS_IP;
check_interrupt();
}
break;
}
}
//-------------------------------------------------
// external_port_w - external port write
//-------------------------------------------------
void z8536_device::external_port_w(int port, int bit, int state)
{
switch (port)
{
case PORT_A:
case PORT_B:
{
assert((PORT_A_DATA_DIRECTION + (port << 3)) >= 0 && (PORT_A_DATA_DIRECTION + (port << 3)) < ARRAY_LENGTH(m_register));
uint8_t ddr = m_register[PORT_A_DATA_DIRECTION + (port << 3)];
if (!BIT(ddr, bit)) return;
if (LOG) logerror("%s Z8536 '%s' Port %c Bit %u: %u\n", machine().describe_context(), tag(), 'A' + port, bit, state);
m_input[port] = (m_input[port] & ~(1 << bit)) | (state << bit);
match_pattern(port);
}
break;
case PORT_C:
break;
}
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// z8536_device - constructor
//-------------------------------------------------
z8536_device::z8536_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, Z8536, "Zilog Z8536", tag, owner, clock, "z8536", __FILE__),
device_z80daisy_interface(mconfig, *this),
m_write_irq(*this),
m_read_pa(*this),
m_write_pa(*this),
m_read_pb(*this),
m_write_pb(*this),
m_read_pc(*this),
m_write_pc(*this),
m_irq(CLEAR_LINE)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void z8536_device::device_start()
{
for (int i = 0; i < 3; i++)
{
m_input[i] = 0;
m_output[i] = 0;
m_buffer[i] = 0;
m_match[i] = 0;
}
// allocate timer
m_timer = timer_alloc();
m_timer->adjust(attotime::from_hz(clock() / 2), 0, attotime::from_hz(clock() / 2));
// resolve callbacks
m_write_irq.resolve_safe();
m_read_pa.resolve_safe(0);
m_write_pa.resolve_safe();
m_read_pb.resolve_safe(0);
m_write_pb.resolve_safe();
m_read_pc.resolve_safe(0);
m_write_pc.resolve_safe();
}
//-------------------------------------------------
// device_start - device-specific reset
//-------------------------------------------------
void z8536_device::device_reset()
{
m_state = STATE_RESET;
for (auto & elem : m_register)
{
elem = 0;
}
m_register[MASTER_INTERRUPT_CONTROL] = MICR_RESET;
m_register[PORT_A_COMMAND_AND_STATUS] = PCS_ORE;
m_register[PORT_B_COMMAND_AND_STATUS] = PCS_ORE;
m_register[CURRENT_VECTOR] = 0xff;
m_pointer = 0;
check_interrupt();
}
//-------------------------------------------------
// device_timer - handler timer events
//-------------------------------------------------
void z8536_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
if (counter_enabled(TIMER_1) && !counter_external_count(TIMER_1))
{
count(TIMER_1);
}
if (counter_enabled(TIMER_2) && !counter_external_count(TIMER_2))
{
count(TIMER_2);
}
if (counter_enabled(TIMER_3) && !counter_external_count(TIMER_3))
{
count(TIMER_3);
}
}
//**************************************************************************
// DAISY CHAIN INTERFACE
//**************************************************************************
//-------------------------------------------------
// z80daisy_irq_state - return the overall IRQ
// state for this device
//-------------------------------------------------
int z8536_device::z80daisy_irq_state()
{
return 0;
}
//-------------------------------------------------
// z80daisy_irq_ack - acknowledge an IRQ and
// return the appropriate vector
//-------------------------------------------------
int z8536_device::z80daisy_irq_ack()
{
return intack_r();
}
//-------------------------------------------------
// z80daisy_irq_reti - clear the interrupt
// pending state to allow other interrupts through
//-------------------------------------------------
void z8536_device::z80daisy_irq_reti()
{
}
//**************************************************************************
// INTERNAL STATE MANAGEMENT
//**************************************************************************
//-------------------------------------------------
// read - register read
//-------------------------------------------------
READ8_MEMBER( z8536_device::read )
{
uint8_t data = 0;
if (m_state == STATE_RESET)
{
// read RESET bit
data = read_register(m_pointer, 0x01);
}
else
{
switch (offset & 0x03)
{
case 0:
data = read_register(PORT_C_DATA);
break;
case 1:
data = read_register(PORT_B_DATA);
break;
case 2:
data = read_register(PORT_A_DATA);
break;
case 3:
switch (m_state)
{
case STATE_1:
m_state = STATE_0;
// fallthru
case STATE_0:
data = read_register(m_pointer);
break;
}
break;
}
}
return data;
}
//-------------------------------------------------
// write - register write
//-------------------------------------------------
WRITE8_MEMBER( z8536_device::write )
{
if (m_state == STATE_RESET)
{
// write RESET bit
write_register(m_pointer, data, 0x01);
}
else
{
switch (offset & 0x03)
{
case PORT_C:
write_register(PORT_C_DATA, data);
break;
case PORT_B:
write_register(PORT_B_DATA, data);
break;
case PORT_A:
write_register(PORT_A_DATA, data);
break;
case CONTROL:
switch (m_state)
{
case STATE_0:
m_pointer = data;
m_state = STATE_1;
break;
case STATE_1:
write_register(m_pointer, data);
m_state = STATE_0;
}
break;
}
}
}
//-------------------------------------------------
// intack_r - interrupt acknowledge
//-------------------------------------------------
int z8536_device::intack_r()
{
get_interrupt_vector();
int data = m_register[CURRENT_VECTOR];
if (LOG) logerror("%s Z8536 '%s' Interrupt Acknowledge: %02x\n", machine().describe_context(), tag(), data);
// set interrupt under service bit
if ((m_register[COUNTER_TIMER_3_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE)) == (CTCS_IP | CTCS_IE))
{
m_register[COUNTER_TIMER_3_COMMAND_AND_STATUS] |= CTCS_IUS;
}
else if ((m_register[PORT_A_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE)) == (PCS_IP | PCS_IE))
{
m_register[PORT_A_COMMAND_AND_STATUS] |= PCS_IUS;
}
else if ((m_register[COUNTER_TIMER_2_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE)) == (CTCS_IP | CTCS_IE))
{
m_register[COUNTER_TIMER_2_COMMAND_AND_STATUS] |= CTCS_IUS;
}
else if ((m_register[PORT_B_COMMAND_AND_STATUS] & (PCS_IP | PCS_IE)) == (PCS_IP | PCS_IE))
{
m_register[PORT_B_COMMAND_AND_STATUS] |= PCS_IUS;
}
else if ((m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS] & (CTCS_IP | CTCS_IE)) == (CTCS_IP | CTCS_IE))
{
m_register[COUNTER_TIMER_1_COMMAND_AND_STATUS] |= CTCS_IUS;
}
check_interrupt();
if (m_register[MASTER_INTERRUPT_CONTROL] & MICR_NV)
{
// no vector
data = -1;
}
return data;
}