// license:BSD-3-Clause // copyright-holders:Curt Coder, AJR /********************************************************************** Zilog Z8 Single-Chip MCU emulation **********************************************************************/ /* TODO: - strobed I/O - instruction pipeline - internal diagnostic ROM in data space (requires high voltage reset) - what really happens when register pairs are unaligned? */ #include "emu.h" #include "z8.h" #include "z8dasm.h" #include "debugger.h" /*************************************************************************** CONSTANTS ***************************************************************************/ #define Z8_P3_DAV0 0x04 /* not supported */ #define Z8_P3_DAV1 0x08 /* not supported */ #define Z8_P3_DAV2 0x02 /* not supported */ #define Z8_P3_RDY0 0x20 /* not supported */ #define Z8_P3_RDY1 0x10 /* not supported */ #define Z8_P3_RDY2 0x40 /* not supported */ #define Z8_P3_IRQ0 0x04 #define Z8_P3_IRQ1 0x08 #define Z8_P3_IRQ2 0x02 #define Z8_P3_IRQ3 0x01 #define Z8_P3_SIN 0x01 #define Z8_P3_SOUT 0x80 #define Z8_P3_TIN 0x02 #define Z8_P3_TOUT 0x40 #define Z8_P3_DM 0x10 /* not supported */ #define Z8_PRE0_COUNT_MODULO_N 0x01 #define Z8_PRE1_COUNT_MODULO_N 0x01 #define Z8_PRE1_INTERNAL_CLOCK 0x02 #define Z8_TMR_LOAD_T0 0x01 #define Z8_TMR_ENABLE_T0 0x02 #define Z8_TMR_LOAD_T1 0x04 #define Z8_TMR_ENABLE_T1 0x08 #define Z8_TMR_TIN_MASK 0x30 #define Z8_TMR_TIN_EXTERNAL_CLK 0x00 #define Z8_TMR_TIN_GATE 0x10 #define Z8_TMR_TIN_TRIGGER 0x20 #define Z8_TMR_TIN_RETRIGGER 0x30 #define Z8_TMR_TOUT_MASK 0xc0 #define Z8_TMR_TOUT_OFF 0x00 #define Z8_TMR_TOUT_T0 0x40 #define Z8_TMR_TOUT_T1 0x80 #define Z8_TMR_TOUT_INTERNAL_CLK 0xc0 /* not supported */ #define Z8_P01M_P0L_MODE_MASK 0x03 #define Z8_P01M_P0L_MODE_OUTPUT 0x00 #define Z8_P01M_P0L_MODE_INPUT 0x01 #define Z8_P01M_P0L_MODE_A8_A11 0x02 #define Z8_P01M_INTERNAL_STACK 0x04 #define Z8_P01M_P1_MODE_MASK 0x18 #define Z8_P01M_P1_MODE_OUTPUT 0x00 #define Z8_P01M_P1_MODE_INPUT 0x08 #define Z8_P01M_P1_MODE_AD0_AD7 0x10 /* not supported */ #define Z8_P01M_P1_MODE_HI_Z 0x18 /* not supported */ #define Z8_P01M_EXTENDED_TIMING 0x20 /* not supported */ #define Z8_P01M_P0H_MODE_MASK 0xc0 #define Z8_P01M_P0H_MODE_OUTPUT 0x00 #define Z8_P01M_P0H_MODE_INPUT 0x40 #define Z8_P01M_P0H_MODE_A12_A15 0x80 #define Z8_P3M_P2_ACTIVE_PULLUPS 0x01 #define Z8_P3M_P0_STROBED 0x04 /* not supported */ #define Z8_P3M_P33_P34_MASK 0x18 #define Z8_P3M_P33_P34_INPUT_OUTPUT 0x00 #define Z8_P3M_P33_P34_INPUT_DM 0x08 /* not supported */ #define Z8_P3M_P33_P34_INPUT_DM_2 0x10 /* not supported */ #define Z8_P3M_P33_P34_DAV1_RDY1 0x18 /* not supported */ #define Z8_P3M_P2_STROBED 0x20 /* not supported */ #define Z8_P3M_P3_SERIAL 0x40 /* not supported */ #define Z8_P3M_PARITY 0x80 /* not supported */ #define Z8_IMR_ENABLE 0x80 #define Z8_IMR_RAM_PROTECT 0x40 /* not supported */ #define Z8_IRQ_MASK 0x3f #define Z8_FLAGS_F1 0x01 #define Z8_FLAGS_F2 0x02 #define Z8_FLAGS_H 0x04 #define Z8_FLAGS_D 0x08 #define Z8_FLAGS_V 0x10 #define Z8_FLAGS_S 0x20 #define Z8_FLAGS_Z 0x40 #define Z8_FLAGS_C 0x80 enum { CC_F = 0, CC_LT, CC_LE, CC_ULE, CC_OV, CC_MI, CC_Z, CC_C, CC_T, CC_GE, CC_GT, CC_UGT, CC_NOV, CC_PL, CC_NZ, CC_NC }; /*************************************************************************** MACROS ***************************************************************************/ DEFINE_DEVICE_TYPE(Z8601, z8601_device, "z8601", "Zilog Z8601") DEFINE_DEVICE_TYPE(UB8830D, ub8830d_device, "ub8830d", "UB8830D") DEFINE_DEVICE_TYPE(Z8611, z8611_device, "z8611", "Zilog Z8611") DEFINE_DEVICE_TYPE(Z8671, z8671_device, "z8671", "Zilog Z8671") DEFINE_DEVICE_TYPE(Z8681, z8681_device, "z8681", "Zilog Z8681") DEFINE_DEVICE_TYPE(Z8682, z8682_device, "z8682", "Zilog Z8682") /*************************************************************************** ADDRESS MAPS ***************************************************************************/ void z8_device::program_map(address_map &map) { if (m_rom_size > 0) map(0x0000, m_rom_size - 1).rom().region(DEVICE_SELF, 0); } void z8_device::preprogrammed_map(address_map &map) { map(0x0000, m_rom_size - 1).rom().region("internal", 0); } void z8_device::register_map(address_map &map) { map(0x00, 0x00).rw(FUNC(z8_device::p0_read), FUNC(z8_device::p0_write)); map(0x01, 0x01).rw(FUNC(z8_device::p1_read), FUNC(z8_device::p1_write)); map(0x02, 0x02).rw(FUNC(z8_device::p2_read), FUNC(z8_device::p2_write)); map(0x03, 0x03).rw(FUNC(z8_device::p3_read), FUNC(z8_device::p3_write)); map(0x04, 0x7f).ram(); map(0xf0, 0xf0).rw(FUNC(z8_device::sio_read), FUNC(z8_device::sio_write)); map(0xf1, 0xf1).rw(FUNC(z8_device::tmr_read), FUNC(z8_device::tmr_write)); map(0xf2, 0xf2).rw(FUNC(z8_device::t1_read), FUNC(z8_device::t1_write)); map(0xf3, 0xf3).w(FUNC(z8_device::pre1_write)); map(0xf4, 0xf4).rw(FUNC(z8_device::t0_read), FUNC(z8_device::t0_write)); map(0xf5, 0xf5).w(FUNC(z8_device::pre0_write)); map(0xf6, 0xf6).w(FUNC(z8_device::p2m_write)); map(0xf7, 0xf7).w(FUNC(z8_device::p3m_write)); map(0xf8, 0xf8).w(FUNC(z8_device::p01m_write)); map(0xf9, 0xf9).w(FUNC(z8_device::ipr_write)); map(0xfa, 0xfa).rw(FUNC(z8_device::irq_read), FUNC(z8_device::irq_write)); map(0xfb, 0xfb).rw(FUNC(z8_device::imr_read), FUNC(z8_device::imr_write)); map(0xfc, 0xfc).rw(FUNC(z8_device::flags_read), FUNC(z8_device::flags_write)); map(0xfd, 0xfd).rw(FUNC(z8_device::rp_read), FUNC(z8_device::rp_write)); map(0xfe, 0xfe).rw(FUNC(z8_device::sph_read), FUNC(z8_device::sph_write)); map(0xff, 0xff).rw(FUNC(z8_device::spl_read), FUNC(z8_device::spl_write)); } z8_device::z8_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint32_t rom_size, bool preprogrammed) : cpu_device(mconfig, type, tag, owner, clock) , m_program_config("program", ENDIANNESS_BIG, 8, 16, 0, preprogrammed ? address_map_constructor(FUNC(z8_device::preprogrammed_map), this) : address_map_constructor(FUNC(z8_device::program_map), this)) , m_data_config("data", ENDIANNESS_BIG, 8, 16, 0) , m_register_config("register", ENDIANNESS_BIG, 8, 8, 0, address_map_constructor(FUNC(z8_device::register_map), this)) , m_input_cb(*this) , m_output_cb(*this) , m_rom_size(rom_size) , m_input{0xff, 0xff, 0xff, 0x0f} { assert(((rom_size - 1) & rom_size) == 0); } z8601_device::z8601_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, Z8601, tag, owner, clock, 0x800, false) { } ub8830d_device::ub8830d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, UB8830D, tag, owner, clock, 0x800, false) { } z8611_device::z8611_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, Z8611, tag, owner, clock, 0x1000, false) { } z8671_device::z8671_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, Z8671, tag, owner, clock, 0x800, true) { } ROM_START(z8671) ROM_REGION(0x0800, "internal", 0) ROM_LOAD("z8671.bin", 0x0000, 0x0800, CRC(3fceeb76) SHA1(290a24c77debd2e280fe31380287838c5fb7cabd)) ROM_END const tiny_rom_entry *z8671_device::device_rom_region() const { return ROM_NAME(z8671); } z8681_device::z8681_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, Z8681, tag, owner, clock, 0, false) { } z8682_device::z8682_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : z8_device(mconfig, Z8682, tag, owner, clock, 0x800, true) { } ROM_START(z8682) // Zilog admits that this nominally ROMless type uses a "small internal ROM" ROM_REGION(0x0800, "internal", ROMREGION_ERASEFF) ROM_LOAD("testrom.bin", 0x0000, 0x0038, CRC(b2239f28) SHA1(9d27957ba0f15657eac5a7295157af6ee51cb261) BAD_DUMP) // typed in from "Z8 MCU Test Mode" application note ROM_END const tiny_rom_entry *z8682_device::device_rom_region() const { return ROM_NAME(z8682); } std::unique_ptr z8_device::create_disassembler() { return std::make_unique(); } device_memory_interface::space_config_vector z8_device::memory_space_config() const { // Separate data space is optional if (has_configured_map(AS_DATA)) { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_DATA, &m_data_config), std::make_pair(AS_IO, &m_register_config) }; } else { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_IO, &m_register_config) }; } } /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ uint16_t z8_device::mask_external_address(uint16_t addr) { switch (m_p01m & (Z8_P01M_P0L_MODE_A8_A11 | Z8_P01M_P0H_MODE_A12_A15)) { case 0: addr = (addr & 0x00ff) | p0_read() << 8; break; case Z8_P01M_P0L_MODE_A8_A11: addr = (addr & 0x0fff) | (p0_read() & 0xf0) << 8; break; case Z8_P01M_P0H_MODE_A12_A15: addr = (addr & 0xf0ff) | (p0_read() & 0x0f) << 8; break; } return addr; } uint8_t z8_device::fetch() { uint16_t real_pc = (m_pc < m_rom_size) ? m_pc : mask_external_address(m_pc); uint8_t data = m_cache->read_byte(real_pc); m_pc++; return data; } uint8_t z8_device::fetch_opcode() { m_ppc = (m_pc < m_rom_size) ? m_pc : mask_external_address(m_pc); debugger_instruction_hook(m_ppc); uint8_t data = m_cache->read_byte(m_ppc); m_pc++; return data; } uint16_t z8_device::fetch_word() { // ensure correct order of operations by using separate instructions uint16_t data = fetch() << 8; data |= fetch(); return data; } uint8_t z8_device::p0_read() { uint8_t data = 0xff; uint8_t mask = 0; switch (m_p01m & Z8_P01M_P0L_MODE_MASK) { case Z8_P01M_P0L_MODE_OUTPUT: data = m_output[0] & 0x0f; break; case Z8_P01M_P0L_MODE_INPUT: mask = 0x0f; break; default: /* A8...A11 */ data = 0x0f; break; } switch (m_p01m & Z8_P01M_P0H_MODE_MASK) { case Z8_P01M_P0H_MODE_OUTPUT: data |= m_output[0] & 0xf0; break; case Z8_P01M_P0H_MODE_INPUT: mask |= 0xf0; break; default: /* A12...A15 */ data |= 0xf0; break; } if (!(m_p3m & Z8_P3M_P0_STROBED)) { if (mask) m_input[0] = m_input_cb[0](0, mask); } data |= m_input[0] & mask; return data; } void z8_device::p0_write(uint8_t data) { uint8_t mask = 0; m_output[0] = data; if ((m_p01m & Z8_P01M_P0L_MODE_MASK) == Z8_P01M_P0L_MODE_OUTPUT) mask |= 0x0f; if ((m_p01m & Z8_P01M_P0H_MODE_MASK) == Z8_P01M_P0H_MODE_OUTPUT) mask |= 0xf0; if (mask) m_output_cb[0](0, data & mask, mask); } uint8_t z8_device::p1_read() { uint8_t data = 0xff; uint8_t mask = 0; switch (m_p01m & Z8_P01M_P1_MODE_MASK) { case Z8_P01M_P1_MODE_OUTPUT: data = m_output[1]; break; case Z8_P01M_P1_MODE_INPUT: mask = 0xff; break; default: /* AD0..AD7 */ data = 0xff; break; } if ((m_p3m & Z8_P3M_P33_P34_MASK) != Z8_P3M_P33_P34_DAV1_RDY1) { if (mask) m_input[1] = m_input_cb[1](0, mask); } data |= m_input[1] & mask; return data; } void z8_device::p1_write(uint8_t data) { uint8_t mask = 0; m_output[1] = data; if ((m_p01m & Z8_P01M_P1_MODE_MASK) == Z8_P01M_P1_MODE_OUTPUT) mask = 0xff; if (mask) m_output_cb[1](0, data & mask, mask); } uint8_t z8_device::p2_read() { uint8_t mask = m_p2m; // open drain lines can be externally driven where data = 1 if (!(m_p3m & Z8_P3M_P2_ACTIVE_PULLUPS)) mask |= m_output[2]; if (!(m_p3m & Z8_P3M_P2_STROBED)) { if (mask) m_input[2] = m_input_cb[2](0, mask); } return (m_input[2] & mask) | (m_output[2] & ~mask); } void z8_device::p2_write(uint8_t data) { uint8_t mask = m_p2m ^ 0xff; m_output[2] = data; if (mask) m_output_cb[2](0, data & mask, mask); } void z8_device::p3_update_output() { uint8_t output = m_output[3] & 0xf0; if ((m_tmr & Z8_TMR_TOUT_MASK) != Z8_TMR_TOUT_OFF) output = (output & ~Z8_P3_TOUT) | (m_tout ? Z8_P3_TOUT : 0); if ((m_p3m & Z8_P3M_P3_SERIAL) != 0) output = (output & ~Z8_P3_SOUT) | ((m_transmit_sr == 0 || BIT(m_transmit_sr, 0)) ? Z8_P3_SOUT : 0); if (m_p3_output != output) { m_output_cb[3](0, output, output ^ m_p3_output); m_p3_output = output; } } uint8_t z8_device::p3_read() { uint8_t mask = 0x0f; uint8_t inputs = m_input[3] & m_input_cb[3](0, mask); // TODO: special port 3 modes //if (!(m_p3m & 0x7c)) //{ //} return (inputs & mask) | (m_p3_output & ~mask); } void z8_device::p3_write(uint8_t data) { m_output[3] = data & 0xf0; // TODO: special port 3 modes //if (!(m_p3m & 0x7c)) //{ //} p3_update_output(); } bool z8_device::get_serial_in() { return (m_input[3] & Z8_P3_SIN) != 0; } void z8_device::sio_receive() { if (m_receive_started) { m_receive_count = (m_receive_count + 1) & 15; if (m_receive_count == 8) { if (m_receive_sr == 0) { if (!get_serial_in()) { // start bit validated m_receive_sr |= 1 << 9; m_receive_parity = false; } else { // false start bit m_receive_started = false; m_receive_count = 0; } } else { // shift in data, parity or stop bit m_receive_sr >>= 1; if (get_serial_in()) m_receive_sr |= 1 << 9; if (BIT(m_receive_sr, 0)) { // received full character m_receive_buffer = (m_receive_sr & 0x1fe) >> 1; request_interrupt(3); m_receive_started = false; m_receive_count = 0; } else { if (BIT(m_receive_sr, 9)) m_receive_parity = !m_receive_parity; // parity replaces received bit 7 if selected if (BIT(m_receive_sr, 1) && (m_p3m & Z8_P3M_PARITY) != 0) { if (m_receive_parity) m_receive_sr |= 1 << 9; else m_receive_sr &= ~(1 << 9); } } } } } else { // start bit is high-low transition m_receive_sr >>= 1; if (get_serial_in()) m_receive_sr |= 1 << 9; else if (BIT(m_receive_sr, 8)) { m_receive_started = true; m_receive_sr = 0; m_receive_count = 0; } } } void z8_device::sio_transmit() { if (m_transmit_sr == 0) return; m_transmit_count = (m_transmit_count + 1) & 15; if (m_transmit_count == 0) { m_transmit_sr >>= 1; if (m_transmit_sr == 0) request_interrupt(4); else { // parity replaces received bit 7 if selected if ((m_transmit_sr >> 1) == 3 && (m_p3m & Z8_P3M_PARITY) != 0) { if (m_transmit_parity) m_transmit_sr |= 1; else m_transmit_sr &= ~1; } else if (BIT(m_transmit_sr, 0)) m_transmit_parity = !m_transmit_parity; // serial output p3_update_output(); } } } uint8_t z8_device::sio_read() { return m_receive_buffer; } void z8_device::sio_write(uint8_t data) { // overwrite shift register with data + 1 start bit + 2 stop bits m_transmit_sr = (m_transmit_sr & 1) | (uint16_t(data) << 2) | (3 << 10); m_transmit_parity = false; // synchronize the shift clock m_transmit_count = 15; } template void z8_device::timer_start() { unsigned prescaler = (m_pre[T] >> 2) ? (m_pre[T] >> 2) : 64; unsigned full_count = (m_count[T] ? m_count[T] - 1 : 255) * prescaler + (m_pre_count[T] ? m_pre_count[T] : 64); m_internal_timer[T]->adjust(cycles_to_attotime(4 * full_count)); } template void z8_device::timer_stop() { if (!m_internal_timer[T]->enabled()) return; unsigned prescaler = (m_pre[T] >> 2) ? (m_pre[T] >> 2) : 64; unsigned remaining = attotime_to_cycles(m_internal_timer[T]->remaining() / 4); m_count[T] = remaining / prescaler + 1; m_pre_count[T] = (remaining % prescaler + 1) & 0x3f; m_internal_timer[T]->enable(false); } template void z8_device::timer_end() { if ((m_tmr & Z8_TMR_TOUT_MASK) == (T == 0 ? Z8_TMR_TOUT_T0 : Z8_TMR_TOUT_T1)) tout_toggle(); if (T == 0 && (m_p3m & Z8_P3M_P3_SERIAL) != 0) { sio_receive(); sio_transmit(); } else request_interrupt(4 + T); m_pre_count[T] = m_pre[T] >> 2; if (m_pre[T] & Z8_PRE0_COUNT_MODULO_N) m_count[T] = m_t[T]; else m_tmr &= ~(T == 0 ? Z8_TMR_ENABLE_T0 : Z8_TMR_ENABLE_T1); } void z8_device::t1_trigger() { switch (m_tmr & Z8_TMR_TIN_MASK) { case Z8_TMR_TIN_EXTERNAL_CLK: m_pre_count[1]--; if (m_pre_count[1] == 0) { m_pre_count[1] = m_pre[1]; if ((m_tmr & Z8_TMR_ENABLE_T1) != 0) { m_count[1]--; if (m_count[1] == 0) timer_end<1>(); } } break; case Z8_TMR_TIN_GATE: timer_stop<1>(); break; case Z8_TMR_TIN_TRIGGER: if (m_internal_timer[1]->enabled()) break; case Z8_TMR_TIN_RETRIGGER: if ((m_tmr & Z8_TMR_ENABLE_T1) != 0) { m_count[1] = m_t[1]; m_pre_count[1] = m_pre[1] >> 2; timer_start<1>(); } break; } } void z8_device::tout_init() { m_tout = true; p3_update_output(); } void z8_device::tout_toggle() { m_tout = !m_tout; p3_update_output(); } uint8_t z8_device::tmr_read() { return m_tmr; } void z8_device::tmr_write(uint8_t data) { m_tmr = data & ~(Z8_TMR_LOAD_T0 | Z8_TMR_LOAD_T1); // actually reset on next internal clock bool t1_internal = (m_pre[1] & Z8_PRE1_INTERNAL_CLOCK) != 0; bool t0_load = (data & Z8_TMR_LOAD_T0) != 0; bool t1_load = (data & Z8_TMR_LOAD_T1) != 0; bool t0_enable = (data & Z8_TMR_ENABLE_T0) != 0; bool t1_enable = (data & Z8_TMR_ENABLE_T1) != 0; if (!t1_internal && ((data & Z8_TMR_TIN_MASK) == Z8_TMR_TIN_GATE)) { if ((m_input[3] & Z8_P3_TIN) != 0) t1_internal = true; else t1_enable = false; } if (t0_load) { m_count[0] = m_t[0]; m_pre_count[0] = m_pre[0] >> 2; if ((m_pre[0] & Z8_PRE0_COUNT_MODULO_N) != 0) { unsigned prescaler = (m_pre[0] >> 2) ? (m_pre[0] >> 2) : 64; unsigned count = (m_t[0] ? m_t[0] : 256) * prescaler; logerror("(%04X): Load T0 at %.2f Hz\n", m_ppc, clock() / 8.0 / count); } if ((data & Z8_TMR_TOUT_MASK) == Z8_TMR_TOUT_T0) tout_init(); } if (t0_enable) { if (t0_load || !m_internal_timer[0]->enabled()) timer_start<0>(); } else timer_stop<0>(); if (t1_load) { m_count[1] = m_t[1]; m_pre_count[1] = m_pre[1] >> 2; if (t1_internal && (m_pre[1] & Z8_PRE0_COUNT_MODULO_N) != 0) { unsigned prescaler = (m_pre[1] >> 2) ? (m_pre[1] >> 2) : 64; unsigned count = (m_t[1] ? m_t[1] : 256) * prescaler; logerror("(%04X): Load T1 at %.2f Hz\n", m_ppc, clock() / 8.0 / count); } if ((data & Z8_TMR_TOUT_MASK) == Z8_TMR_TOUT_T1) tout_init(); } if (t1_enable) { if (t1_internal && (t1_load || !m_internal_timer[1]->enabled())) timer_start<1>(); } else timer_stop<1>(); } uint8_t z8_device::t0_read() { if (!m_internal_timer[0]->enabled()) return m_count[0]; unsigned prescaler = (m_pre[0] >> 2) ? (m_pre[0] >> 2) : 64; unsigned remaining = attotime_to_cycles(m_internal_timer[0]->remaining() / 4); return remaining / prescaler + 1; } void z8_device::t0_write(uint8_t data) { m_t[0] = data; } uint8_t z8_device::t1_read() { if (!m_internal_timer[1]->enabled()) return m_count[1]; unsigned prescaler = (m_pre[1] >> 2) ? (m_pre[1] >> 2) : 64; unsigned remaining = attotime_to_cycles(m_internal_timer[1]->remaining() / 4); return remaining / prescaler + 1; } void z8_device::t1_write(uint8_t data) { m_t[1] = data; } void z8_device::pre0_write(uint8_t data) { if (m_internal_timer[0]->enabled()) { timer_stop<0>(); m_pre[0] = data; timer_start<0>(); } else m_pre[0] = data; } void z8_device::pre1_write(uint8_t data) { bool was_enabled = m_internal_timer[1]->enabled(); if (was_enabled) timer_stop<1>(); m_pre[1] = data; if ((data & Z8_PRE1_INTERNAL_CLOCK) != 0 ? (m_tmr & Z8_TMR_ENABLE_T1) != 0 : was_enabled && (m_tmr & Z8_TMR_TIN_MASK) != Z8_TMR_TIN_EXTERNAL_CLK) timer_start<1>(); } void z8_device::p01m_write(uint8_t data) { m_p01m = data; } void z8_device::p2m_write(uint8_t data) { m_p2m = data; } void z8_device::p3m_write(uint8_t data) { if ((data & Z8_P3M_P3_SERIAL) == 0) { m_transmit_sr = 0; m_transmit_count = 0; m_receive_started = false; m_receive_count = 0; } m_p3m = data; p3_update_output(); } void z8_device::ipr_write(uint8_t data) { m_ipr = data; } uint8_t z8_device::irq_read() { return m_irq; } void z8_device::irq_write(uint8_t data) { if (m_irq_initialized) m_irq = data; } uint8_t z8_device::imr_read() { return m_imr; } void z8_device::imr_write(uint8_t data) { m_imr = data; } uint8_t z8_device::flags_read() { return m_flags; } void z8_device::flags_write(uint8_t data) { m_flags = data; } uint8_t z8_device::rp_read() { return m_rp; } void z8_device::rp_write(uint8_t data) { m_rp = data; } uint8_t z8_device::sph_read() { return m_sp.b.h; } void z8_device::sph_write(uint8_t data) { m_sp.b.h = data; } uint8_t z8_device::spl_read() { return m_sp.b.l; } void z8_device::spl_write(uint8_t data) { m_sp.b.l = data; } uint16_t z8_device::register_pair_read(uint8_t offset) { return m_regs->read_word_unaligned(offset); } void z8_device::register_pair_write(uint8_t offset, uint16_t data) { m_regs->write_word_unaligned(offset, data); } uint8_t z8_device::get_working_register(int offset) const { return (m_rp & 0xf0) | (offset & 0x0f); } uint8_t z8_device::get_register(uint8_t offset) const { if ((offset & 0xf0) == 0xe0) return get_working_register(offset & 0x0f); else return offset; } uint8_t z8_device::get_intermediate_register(int offset) { return register_read(get_register(offset)); } void z8_device::stack_push_byte(uint8_t src) { if (m_p01m & Z8_P01M_INTERNAL_STACK) { // SP <- SP - 1 (predecrement) uint8_t sp = m_sp.b.l - 1; m_sp.b.l = sp; // @SP <- src register_write(sp, src); } else { // SP <- SP - 1 (predecrement) uint16_t sp = m_sp.w - 1; m_sp.w = sp; // @SP <- src m_data->write_byte(mask_external_address(sp), src); } } void z8_device::stack_push_word(uint16_t src) { if (m_p01m & Z8_P01M_INTERNAL_STACK) { // SP <- SP - 2 (predecrement) uint8_t sp = m_sp.b.l - 2; m_sp.b.l = sp; // @SP <- src register_pair_write(sp, src); } else { // SP <- SP - 2 (predecrement) uint16_t sp = m_sp.w - 2; m_sp.w = sp; // @SP <- src m_data->write_word_unaligned(mask_external_address(sp), src); } } uint8_t z8_device::stack_pop_byte() { if (m_p01m & Z8_P01M_INTERNAL_STACK) { // @SP <- src uint8_t sp = m_sp.b.l; uint8_t byte = register_read(sp); // SP <- SP + 1 (postincrement) m_sp.b.l = sp + 1; return byte; } else { // @SP <- src uint16_t sp = m_sp.w; uint8_t byte = m_data->read_byte(mask_external_address(sp)); // SP <- SP + 1 (postincrement) m_sp.w = sp + 1; return byte; } } uint16_t z8_device::stack_pop_word() { if (m_p01m & Z8_P01M_INTERNAL_STACK) { // @SP <- src uint8_t sp = m_sp.b.l; uint16_t word = register_pair_read(sp); // SP <- SP + 2 (postincrement) m_sp.b.l = sp + 2; return word; } else { // @SP <- src uint16_t sp = m_sp.w; uint16_t word = m_data->read_word_unaligned(mask_external_address(sp)); // SP <- SP + 2 (postincrement) m_sp.w = sp + 2; return word; } } void z8_device::set_flag(uint8_t flag, int state) { if (state) m_flags |= flag; else m_flags &= ~flag; } #define set_flag_h(state) set_flag(Z8_FLAGS_H, state); #define set_flag_d(state) set_flag(Z8_FLAGS_D, state); #define set_flag_v(state) set_flag(Z8_FLAGS_V, state); #define set_flag_s(state) set_flag(Z8_FLAGS_S, state); #define set_flag_z(state) set_flag(Z8_FLAGS_Z, state); #define set_flag_c(state) set_flag(Z8_FLAGS_C, state); /*************************************************************************** OPCODE HANDLERS ***************************************************************************/ #define INSTRUCTION(mnemonic) void z8_device::mnemonic(uint8_t opcode, int *cycles) INSTRUCTION( illegal ) { logerror("Z8: PC = %04x, Illegal opcode = %02x\n", m_pc - 1, opcode); } #include "z8ops.hxx" /*************************************************************************** OPCODE TABLES ***************************************************************************/ const z8_device::z8_opcode_map z8_device::Z8601_OPCODE_MAP[256] = { { &z8_device::dec_R1, 6, 5 }, { &z8_device::dec_IR1, 6, 5 }, { &z8_device::add_r1_r2, 10, 5 }, { &z8_device::add_r1_Ir2, 10, 5 }, { &z8_device::add_R2_R1, 10, 5 }, { &z8_device::add_IR2_R1, 10, 5 }, { &z8_device::add_R1_IM, 10, 5 }, { &z8_device::add_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::rlc_R1, 6, 5 }, { &z8_device::rlc_IR1, 6, 5 }, { &z8_device::adc_r1_r2, 6, 5 }, { &z8_device::adc_r1_Ir2, 6, 5 }, { &z8_device::adc_R2_R1, 10, 5 }, { &z8_device::adc_IR2_R1, 10, 5 }, { &z8_device::adc_R1_IM, 10, 5 }, { &z8_device::adc_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::inc_R1, 6, 5 }, { &z8_device::inc_IR1, 6, 5 }, { &z8_device::sub_r1_r2, 6, 5 }, { &z8_device::sub_r1_Ir2, 6, 5 }, { &z8_device::sub_R2_R1, 10, 5 }, { &z8_device::sub_IR2_R1, 10, 5 }, { &z8_device::sub_R1_IM, 10, 5 }, { &z8_device::sub_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::jp_IRR1, 8, 0 }, { &z8_device::srp_IM, 6, 1 }, { &z8_device::sbc_r1_r2, 6, 5 }, { &z8_device::sbc_r1_Ir2, 6, 5 }, { &z8_device::sbc_R2_R1, 10, 5 }, { &z8_device::sbc_IR2_R1, 10, 5 }, { &z8_device::sbc_R1_IM, 10, 5 }, { &z8_device::sbc_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::da_R1, 8, 5 }, { &z8_device::da_IR1, 8, 5 }, { &z8_device::or_r1_r2, 6, 5 }, { &z8_device::or_r1_Ir2, 6, 5 }, { &z8_device::or_R2_R1, 10, 5 }, { &z8_device::or_IR2_R1, 10, 5 }, { &z8_device::or_R1_IM, 10, 5 }, { &z8_device::or_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::pop_R1, 10, 5 }, { &z8_device::pop_IR1, 10, 5 }, { &z8_device::and_r1_r2, 6, 5 }, { &z8_device::and_r1_Ir2, 6, 5 }, { &z8_device::and_R2_R1, 10, 5 }, { &z8_device::and_IR2_R1, 10, 5 }, { &z8_device::and_R1_IM, 10, 5 }, { &z8_device::and_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::com_R1, 6, 5 }, { &z8_device::com_IR1, 6, 5 }, { &z8_device::tcm_r1_r2, 6, 5 }, { &z8_device::tcm_r1_Ir2, 6, 5 }, { &z8_device::tcm_R2_R1, 10, 5 }, { &z8_device::tcm_IR2_R1, 10, 5 }, { &z8_device::tcm_R1_IM, 10, 5 }, { &z8_device::tcm_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::push_R2, 10, 1 }, { &z8_device::push_IR2, 12, 1 },{ &z8_device::tm_r1_r2, 6, 5 }, { &z8_device::tm_r1_Ir2, 6, 5 }, { &z8_device::tm_R2_R1, 10, 5 }, { &z8_device::tm_IR2_R1, 10, 5 }, { &z8_device::tm_R1_IM, 10, 5 }, { &z8_device::tm_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::decw_RR1, 10, 5 },{ &z8_device::decw_IR1, 10, 5 },{ &z8_device::lde_r1_Irr2, 12, 0 }, { &z8_device::ldei_Ir1_Irr2, 18, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::di, 6, 1 }, { &z8_device::rl_R1, 6, 5 }, { &z8_device::rl_IR1, 6, 5 }, { &z8_device::lde_r2_Irr1, 12, 0 }, { &z8_device::ldei_Ir2_Irr1, 18, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::ei, 6, 1 }, { &z8_device::incw_RR1, 10, 5 },{ &z8_device::incw_IR1, 10, 5 },{ &z8_device::cp_r1_r2, 6, 5 }, { &z8_device::cp_r1_Ir2, 6, 5 }, { &z8_device::cp_R2_R1, 10, 5 }, { &z8_device::cp_IR2_R1, 10, 5 }, { &z8_device::cp_R1_IM, 10, 5 }, { &z8_device::cp_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::ret, 14, 0 }, { &z8_device::clr_R1, 6, 5 }, { &z8_device::clr_IR1, 6, 5 }, { &z8_device::xor_r1_r2, 6, 5 }, { &z8_device::xor_r1_Ir2, 6, 5 }, { &z8_device::xor_R2_R1, 10, 5 }, { &z8_device::xor_IR2_R1, 10, 5 }, { &z8_device::xor_R1_IM, 10, 5 }, { &z8_device::xor_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::iret, 16, 0 }, { &z8_device::rrc_R1, 6, 5 }, { &z8_device::rrc_IR1, 6, 5 }, { &z8_device::ldc_r1_Irr2, 12, 0 }, { &z8_device::ldci_Ir1_Irr2, 18, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_r1_x_R2, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::rcf, 6, 5 }, { &z8_device::sra_R1, 6, 5 }, { &z8_device::sra_IR1, 6, 5 }, { &z8_device::ldc_r2_Irr1, 12, 0 }, { &z8_device::ldci_Ir2_Irr1, 18, 0 }, { &z8_device::call_IRR1, 20, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::call_DA, 20, 0 }, { &z8_device::ld_r2_x_R1, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::scf, 6, 5 }, { &z8_device::rr_R1, 6, 5 }, { &z8_device::rr_IR1, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_r1_Ir2, 6, 5 }, { &z8_device::ld_R2_R1, 10, 5 }, { &z8_device::ld_IR2_R1, 10, 5 }, { &z8_device::ld_R1_IM, 10, 5 }, { &z8_device::ld_IR1_IM, 10, 5 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::ccf, 6, 5 }, { &z8_device::swap_R1, 8, 5 }, { &z8_device::swap_IR1, 8, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_Ir1_r2, 6, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_R2_IR1, 10, 5 }, { &z8_device::illegal, 0, 0 }, { &z8_device::illegal, 0, 0 }, { &z8_device::ld_r1_R2, 6, 5 }, { &z8_device::ld_r2_R1, 6, 5 }, { &z8_device::djnz_r1_RA, 10, 5 }, { &z8_device::jr_cc_RA, 10, 0 }, { &z8_device::ld_r1_IM, 6, 5 }, { &z8_device::jp_cc_DA, 10, 0 }, { &z8_device::inc_r1, 6, 5 }, { &z8_device::nop, 6, 0 } }; /*************************************************************************** TIMER CALLBACKS ***************************************************************************/ template TIMER_CALLBACK_MEMBER(z8_device::timeout) { timer_end(); if (m_pre[T] & Z8_PRE0_COUNT_MODULO_N) timer_start(); else { m_count[T] = 0; m_internal_timer[T]->enable(false); } } /*************************************************************************** INITIALIZATION ***************************************************************************/ void z8_device::device_start() { m_input_cb.resolve_all_safe(0xff); m_output_cb.resolve_all_safe(); /* set up the state table */ { state_add(Z8_PC, "PC", m_pc).callimport(); state_add(STATE_GENPC, "GENPC", m_pc).callimport().noshow(); state_add(STATE_GENPCBASE, "CURPC", m_ppc).callimport().noshow(); state_add(Z8_SP, "SP", m_sp.w); state_add(STATE_GENSP, "GENSP", m_sp.w).noshow(); state_add(Z8_RP, "RP", m_rp); state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).noshow().formatstr("%6s"); state_add(Z8_IMR, "IMR", m_imr); state_add(Z8_IRQ, "IRQ", m_irq); state_add(Z8_IPR, "IPR", m_ipr); state_add(Z8_P0, "P0", m_output[0]); if (m_rom_size > 0) state_add(Z8_P1, "P1", m_output[1]); state_add(Z8_P2, "P2", m_output[2]); state_add(Z8_P3, "P3", m_output[3]).mask(0xf0); state_add(Z8_P01M, "P01M", m_p01m); state_add(Z8_P2M, "P2M", m_p2m); state_add(Z8_P3M, "P3M", m_p3m); state_add(Z8_PRE0, "PRE0", m_pre[0]); state_add(Z8_T0, "T0", m_t[0]); state_add(Z8_PRE1, "PRE1", m_pre[1]); state_add(Z8_T1, "T1", m_t[1]); state_add(Z8_TMR, "TMR", m_tmr); state_add(Z8_TOUT, "TOUT", m_tout); for (int regnum = 0; regnum < 16; regnum++) { state_add(Z8_R0 + regnum, string_format("R%d", regnum).c_str(), [this, regnum]() { auto dis = machine().disable_side_effects(); return register_read((m_rp & 0xf0) | regnum); }, [this, regnum](uint8_t val) { auto dis = machine().disable_side_effects(); register_write((m_rp & 0xf0) | regnum, val); }); } for (int regnum = 0; regnum < 16; regnum += 2) { state_add(Z8_RR0 + (regnum / 2), string_format("RR%d", regnum).c_str(), [this, regnum]() { auto dis = machine().disable_side_effects(); return register_pair_read((m_rp & 0xf0) | regnum); }, [this, regnum](uint16_t val) { auto dis = machine().disable_side_effects(); register_pair_write((m_rp & 0xf0) | regnum, val); }).noshow(); } } /* find address spaces */ m_program = &space(AS_PROGRAM); m_cache = m_program->cache<0, 0, ENDIANNESS_BIG>(); m_data = has_space(AS_DATA) ? &space(AS_DATA) : m_program; m_regs = &space(AS_IO); /* allocate timers */ m_internal_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z8_device::timeout<0>), this)); m_internal_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z8_device::timeout<1>), this)); /* Clear state */ std::fill(std::begin(m_irq_line), std::end(m_irq_line), CLEAR_LINE); std::fill(std::begin(m_output), std::end(m_output), 0); std::fill(std::begin(m_t), std::end(m_t), 0); std::fill(std::begin(m_count), std::end(m_count), 0); std::fill(std::begin(m_pre), std::end(m_pre), 0); std::fill(std::begin(m_pre_count), std::end(m_pre_count), 0); m_pc = 0; m_ppc = 0; m_sp.w = 0; m_rp = 0; m_flags = 0; m_p01m = 0; m_p2m = 0; m_p3m = 0; m_p3_output = 0; m_tmr = 0; m_tout = true; m_transmit_sr = 0; m_transmit_count = 0; m_transmit_parity = false; m_receive_buffer = 0; m_receive_sr = 0; m_receive_count = 0; m_receive_parity = false; m_receive_started = false; m_irq_taken = false; m_irq_initialized = false; /* register for state saving */ save_item(NAME(m_pc)); save_item(NAME(m_ppc)); save_item(NAME(m_sp.w)); save_item(NAME(m_rp)); save_item(NAME(m_flags)); save_item(NAME(m_input)); save_item(NAME(m_output)); save_item(NAME(m_p01m)); save_item(NAME(m_p2m)); save_item(NAME(m_p3m)); save_item(NAME(m_p3_output)); save_item(NAME(m_tmr)); save_item(NAME(m_t)); save_item(NAME(m_tout)); save_item(NAME(m_transmit_sr)); save_item(NAME(m_transmit_count)); save_item(NAME(m_transmit_parity)); save_item(NAME(m_receive_buffer)); save_item(NAME(m_receive_sr)); save_item(NAME(m_receive_count)); save_item(NAME(m_receive_parity)); save_item(NAME(m_receive_started)); save_item(NAME(m_count)); save_item(NAME(m_pre)); save_item(NAME(m_pre_count)); save_item(NAME(m_irq_line)); save_item(NAME(m_irq_taken)); save_item(NAME(m_irq_initialized)); set_icountptr(m_icount); } /*************************************************************************** INTERRUPTS ***************************************************************************/ void z8_device::request_interrupt(int irq) { assert(irq >= 0 && irq < 6); if (m_irq_initialized) m_irq |= 1 << irq; } void z8_device::take_interrupt(int irq) { //logerror("Taking IRQ%d (previous PC = %04X)\n", irq, m_pc); m_irq_taken = true; // disable interrupts m_imr &= ~Z8_IMR_ENABLE; // acknowledge the IRQ m_irq &= ~(1 << irq); standard_irq_callback(irq); // get the interrupt vector address uint16_t vector = irq * 2; if (m_rom_size == 0) vector = mask_external_address(vector); // push registers onto stack stack_push_word(m_pc); stack_push_byte(m_flags); // branch to the vector m_pc = m_cache->read_byte(vector) << 8; m_pc |= m_cache->read_byte(vector + 1); } void z8_device::process_interrupts() { m_irq_taken = false; uint8_t pending_irqs = m_imr & m_irq & Z8_IRQ_MASK; if (!(m_imr & Z8_IMR_ENABLE) || pending_irqs == 0) return; int group_a[2] = { 5, 3 }; int group_b[2] = { 2, 0 }; int group_c[2] = { 1, 4 }; if (BIT(m_ipr, 5)) std::swap(group_a[0], group_a[1]); if (BIT(m_ipr, 2)) std::swap(group_b[0], group_b[1]); if (BIT(m_ipr, 1)) std::swap(group_c[0], group_c[1]); switch ((m_ipr & 0x18) >> 2 | (m_ipr & 0x01)) { case 0: // (000) reserved according to Zilog (but must process at least IRQ4) case 1: // (001) C > A > B if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); else if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); else if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); break; case 2: // (010) A > B > C if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); else if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); else if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); break; case 3: // (011) A > C > B if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); else if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); else if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); break; case 4: // (100) B > C > A if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); else if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); else if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); break; case 5: // (101) C > B > A if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); else if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); else if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); break; case 6: // (110) B > A > C case 7: // (111) reserved according to Zilog if (BIT(pending_irqs, group_b[0])) take_interrupt(group_b[0]); else if (BIT(pending_irqs, group_b[1])) take_interrupt(group_b[1]); else if (BIT(pending_irqs, group_a[0])) take_interrupt(group_a[0]); else if (BIT(pending_irqs, group_a[1])) take_interrupt(group_a[1]); else if (BIT(pending_irqs, group_c[0])) take_interrupt(group_c[0]); else if (BIT(pending_irqs, group_c[1])) take_interrupt(group_c[1]); break; } } /*************************************************************************** EXECUTION ***************************************************************************/ void z8_device::execute_run() { do { process_interrupts(); if (m_irq_taken) { // interrupt processing takes 58 external clock cycles m_icount -= 27; } else { /* fetch opcode */ uint8_t opcode = fetch_opcode(); int cycles = Z8601_OPCODE_MAP[opcode].execution_cycles; /* execute instruction */ (this->*(Z8601_OPCODE_MAP[opcode].function))(opcode, &cycles); m_icount -= cycles; } } while (m_icount > 0); } /*************************************************************************** RESET ***************************************************************************/ void z8_device::device_reset() { m_pc = 0x000c; m_rp = 0x00; m_irq = 0x00; m_imr &= ~Z8_IMR_ENABLE; m_irq_initialized = false; m_pre[0] &= ~Z8_PRE0_COUNT_MODULO_N; m_pre[1] &= ~(Z8_PRE1_COUNT_MODULO_N | Z8_PRE1_INTERNAL_CLOCK); m_tmr = 0x00; timer_stop<0>(); timer_stop<1>(); m_output[3] = 0xf0; p01m_write(0x4d); p2m_write(0xff); p3m_write(0x00); } /************************************************************************** * STATE IMPORT/EXPORT **************************************************************************/ void z8_device::state_import(const device_state_entry &entry) { switch (entry.index()) { case STATE_GENPC: case Z8_PC: m_ppc = m_pc; break; case STATE_GENPCBASE: m_pc = m_ppc; break; default: fatalerror("CPU_IMPORT_STATE(z8) called for unexpected value\n"); } } void z8_device::state_string_export(const device_state_entry &entry, std::string &str) const { switch (entry.index()) { case STATE_GENFLAGS: str = string_format("%c%c%c%c%c%c", m_flags & Z8_FLAGS_C ? 'C' : '.', m_flags & Z8_FLAGS_Z ? 'Z' : '.', m_flags & Z8_FLAGS_S ? 'S' : '.', m_flags & Z8_FLAGS_V ? 'V' : '.', m_flags & Z8_FLAGS_D ? 'D' : '.', m_flags & Z8_FLAGS_H ? 'H' : '.'); break; } } void z8_device::execute_set_input(int inputnum, int state) { switch ( inputnum ) { case INPUT_LINE_IRQ0: if (state != CLEAR_LINE && m_irq_line[0] == CLEAR_LINE) request_interrupt(0); m_irq_line[0] = state; if (state != CLEAR_LINE && (m_input[3] & Z8_P3_IRQ0) != 0) m_input[3] &= ~Z8_P3_IRQ0; else if (state == CLEAR_LINE && (m_input[3] & Z8_P3_IRQ0) == 0) m_input[3] |= Z8_P3_IRQ0; break; case INPUT_LINE_IRQ1: if (state != CLEAR_LINE && m_irq_line[1] == CLEAR_LINE) request_interrupt(1); m_irq_line[1] = state; if (state != CLEAR_LINE && (m_input[3] & Z8_P3_IRQ1) != 0) m_input[3] &= ~Z8_P3_IRQ1; else if (state == CLEAR_LINE && (m_input[3] & Z8_P3_IRQ1) == 0) m_input[3] |= Z8_P3_IRQ1; break; case INPUT_LINE_IRQ2: if (state != CLEAR_LINE && m_irq_line[2] == CLEAR_LINE) request_interrupt(2); m_irq_line[2] = state; if (state != CLEAR_LINE && (m_input[3] & Z8_P3_IRQ2) != 0) { m_input[3] &= ~Z8_P3_IRQ2; if ((m_pre[1] & Z8_PRE1_INTERNAL_CLOCK) == 0) t1_trigger(); } else if (state == CLEAR_LINE && (m_input[3] & Z8_P3_IRQ2) == 0) { m_input[3] |= Z8_P3_IRQ2; if ((m_pre[1] & Z8_PRE1_INTERNAL_CLOCK) == 0 && (m_tmr & Z8_TMR_TIN_MASK) == Z8_TMR_TIN_GATE) timer_start<1>(); } break; case INPUT_LINE_IRQ3: if (state != CLEAR_LINE && m_irq_line[3] == CLEAR_LINE && (m_p3m & Z8_P3M_P3_SERIAL) == 0) request_interrupt(3); m_irq_line[3] = state; if (state != CLEAR_LINE && (m_input[3] & Z8_P3_IRQ3) != 0) m_input[3] &= ~Z8_P3_IRQ3; else if (state == CLEAR_LINE && (m_input[3] & Z8_P3_IRQ3) == 0) m_input[3] |= Z8_P3_IRQ3; break; } }