diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index d601fd99329..7c6aa7a8167 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -761,6 +761,8 @@ if (BUSES["IEEE488"]~=null) then MAME_DIR .. "src/devices/bus/ieee488/hardbox.h", MAME_DIR .. "src/devices/bus/ieee488/shark.cpp", MAME_DIR .. "src/devices/bus/ieee488/shark.h", + MAME_DIR .. "src/devices/bus/ieee488/hp9895.cpp", + MAME_DIR .. "src/devices/bus/ieee488/hp9895.h", } end diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 59a506c3324..e77228761e8 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1928,6 +1928,18 @@ if (MACHINES["PCKEYBRD"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/phi.h,MACHINES["PHI"] = true +--------------------------------------------------- + +if (MACHINES["PHI"]~=null) then + files { + MAME_DIR .. "src/devices/machine/phi.cpp", + MAME_DIR .. "src/devices/machine/phi.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/pic8259.h,MACHINES["PIC8259"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index a86e39916d4..2f0ce0bd595 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -492,6 +492,7 @@ MACHINES["PCF8593"] = true MACHINES["PCI"] = true MACHINES["PCKEYBRD"] = true MACHINES["PDC"] = true +MACHINES["PHI"] = true MACHINES["PIC8259"] = true MACHINES["PIT68230"] = true MACHINES["PIT8253"] = true diff --git a/src/devices/bus/hp9845_io/98034.cpp b/src/devices/bus/hp9845_io/98034.cpp index ab213c132ee..ae2928e0f6b 100644 --- a/src/devices/bus/hp9845_io/98034.cpp +++ b/src/devices/bus/hp9845_io/98034.cpp @@ -132,6 +132,9 @@ READ16_MEMBER(hp98034_io_card::reg_r) m_force_flg = true; update_flg(); + // PPU yields to let NP see FLG=0 immediately + // (horrible race conditions lurking...) + space.device().execute().yield(); LOG(("read R%u=%04x\n" , offset + 4 , res)); return res; @@ -152,6 +155,9 @@ WRITE16_MEMBER(hp98034_io_card::reg_w) m_force_flg = true; update_flg(); + // PPU yields to let NP see FLG=0 immediately + // (horrible race conditions lurking...) + space.device().execute().yield(); LOG(("write R%u=%04x\n" , offset + 4 , data)); } @@ -356,6 +362,7 @@ static MACHINE_CONFIG_FRAGMENT(hp98034) MCFG_HP_NANO_READ_DC_CB(READ8(hp98034_io_card , dc_r)) MCFG_CPU_IRQ_ACKNOWLEDGE_DRIVER(hp98034_io_card , irq_callback) + MCFG_IEEE488_SLOT_ADD("ieee_dev" , 0 , hp_ieee488_devices , nullptr) MCFG_IEEE488_BUS_ADD() MCFG_IEEE488_IFC_CALLBACK(WRITELINE(hp98034_io_card , ieee488_ctrl_w)) MCFG_IEEE488_ATN_CALLBACK(WRITELINE(hp98034_io_card , ieee488_ctrl_w)) diff --git a/src/devices/bus/ieee488/hp9895.cpp b/src/devices/bus/ieee488/hp9895.cpp new file mode 100644 index 00000000000..e5b1f6de2ec --- /dev/null +++ b/src/devices/bus/ieee488/hp9895.cpp @@ -0,0 +1,208 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + hp9895.cpp + + HP9895 floppy disk drive + + Reference manual: + HP 09895-90030, feb 81, 9895A Flexible Disc Memory Service Manual + +*********************************************************************/ + +#include "hp9895.h" + +// Debugging +#define VERBOSE 1 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +// device type definition +const device_type HP9895 = &device_creator; + +hp9895_device::hp9895_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, HP9895, "HP9895", tag, owner, clock, "HP9895", __FILE__), + device_ieee488_interface(mconfig, *this), + m_cpu(*this , "cpu"), + m_phi(*this , "phi") +{ +} + +#if 0 +ioport_constructor hp9895_device::device_input_ports() const +{ + // TODO: inputs=HPIB address, "S" & "W" switches, "loop" pin +} +#endif +void hp9895_device::device_start() +{ +} + +void hp9895_device::device_reset() +{ + m_cpu_irq = false; +} + +void hp9895_device::ieee488_eoi(int state) +{ + m_phi->eoi_w(state); +} + +void hp9895_device::ieee488_dav(int state) +{ + m_phi->dav_w(state); +} + +void hp9895_device::ieee488_nrfd(int state) +{ + m_phi->nrfd_w(state); +} + +void hp9895_device::ieee488_ndac(int state) +{ + m_phi->ndac_w(state); +} + +void hp9895_device::ieee488_ifc(int state) +{ + m_phi->ifc_w(state); +} + +void hp9895_device::ieee488_srq(int state) +{ + m_phi->srq_w(state); +} + +void hp9895_device::ieee488_atn(int state) +{ + m_phi->atn_w(state); +} + +void hp9895_device::ieee488_ren(int state) +{ + m_phi->ren_w(state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_eoi_w) +{ + m_bus->eoi_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_dav_w) +{ + m_bus->dav_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_nrfd_w) +{ + m_bus->nrfd_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_ndac_w) +{ + m_bus->ndac_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_ifc_w) +{ + m_bus->ifc_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_srq_w) +{ + m_bus->srq_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_atn_w) +{ + m_bus->atn_w(this , state); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_ren_w) +{ + m_bus->ren_w(this , state); +} + +READ8_MEMBER(hp9895_device::phi_dio_r) +{ + return m_bus->dio_r(); +} + +WRITE8_MEMBER(hp9895_device::phi_dio_w) +{ + m_bus->dio_w(this , data); +} + +WRITE_LINE_MEMBER(hp9895_device::phi_int_w) +{ + m_cpu->set_input_line(INPUT_LINE_NMI , state); +} + +READ8_MEMBER(hp9895_device::phi_reg_r) +{ + uint16_t reg = m_phi->reg16_r(space , offset , mem_mask); + + // Reading D1=1 from a register sets the Z80 IRQ line + if (BIT(reg , 14) && !m_cpu_irq) { + m_cpu_irq = true; + m_cpu->set_input_line(INPUT_LINE_IRQ0 , ASSERT_LINE); + } + + return (uint8_t)reg; +} + +WRITE16_MEMBER(hp9895_device::z80_m1_w) +{ + // Every M1 cycle of Z80 clears the IRQ line + if (m_cpu_irq) { + m_cpu_irq = false; + m_cpu->set_input_line(INPUT_LINE_IRQ0 , CLEAR_LINE); + } +} + +ROM_START(hp9895) + ROM_REGION(0x2000 , "cpu" , 0) + ROM_LOAD("1818-1391a.bin" , 0 , 0x2000 , CRC(b50dbfb5) SHA1(96edf9af78be75fbad2a0245b8af43958ba32752)) +ROM_END + +static ADDRESS_MAP_START(z80_program_map , AS_PROGRAM , 8 , hp9895_device) + ADDRESS_MAP_UNMAP_HIGH + AM_RANGE(0x0000 , 0x1fff) AM_ROM AM_REGION("cpu" , 0) + AM_RANGE(0x6000 , 0x63ff) AM_RAM +ADDRESS_MAP_END + +static ADDRESS_MAP_START(z80_io_map , AS_IO , 8 , hp9895_device) + ADDRESS_MAP_UNMAP_HIGH + ADDRESS_MAP_GLOBAL_MASK(0xff) + AM_RANGE(0x10 , 0x17) AM_DEVWRITE("phi" , phi_device , reg8_w) AM_READ(phi_reg_r) + // TODO: 60-67 range +ADDRESS_MAP_END + +static MACHINE_CONFIG_FRAGMENT(hp9895) + MCFG_CPU_ADD("cpu" , Z80 , 4000000) + MCFG_CPU_PROGRAM_MAP(z80_program_map) + MCFG_CPU_IO_MAP(z80_io_map) + MCFG_Z80_SET_REFRESH_CALLBACK(WRITE16(hp9895_device , z80_m1_w)) + + MCFG_DEVICE_ADD("phi" , PHI , 0) + MCFG_PHI_EOI_WRITE_CB(WRITELINE(hp9895_device , phi_eoi_w)) + MCFG_PHI_DAV_WRITE_CB(WRITELINE(hp9895_device , phi_dav_w)) + MCFG_PHI_NRFD_WRITE_CB(WRITELINE(hp9895_device , phi_nrfd_w)) + MCFG_PHI_NDAC_WRITE_CB(WRITELINE(hp9895_device , phi_ndac_w)) + MCFG_PHI_IFC_WRITE_CB(WRITELINE(hp9895_device , phi_ifc_w)) + MCFG_PHI_SRQ_WRITE_CB(WRITELINE(hp9895_device , phi_srq_w)) + MCFG_PHI_ATN_WRITE_CB(WRITELINE(hp9895_device , phi_atn_w)) + MCFG_PHI_REN_WRITE_CB(WRITELINE(hp9895_device , phi_ren_w)) + MCFG_PHI_DIO_READWRITE_CB(READ8(hp9895_device , phi_dio_r) , WRITE8(hp9895_device , phi_dio_w)) + MCFG_PHI_INT_WRITE_CB(WRITELINE(hp9895_device , phi_int_w)) +MACHINE_CONFIG_END + +const tiny_rom_entry *hp9895_device::device_rom_region() const +{ + return ROM_NAME(hp9895); +} + +machine_config_constructor hp9895_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME(hp9895); +} diff --git a/src/devices/bus/ieee488/hp9895.h b/src/devices/bus/ieee488/hp9895.h new file mode 100644 index 00000000000..d7591d684be --- /dev/null +++ b/src/devices/bus/ieee488/hp9895.h @@ -0,0 +1,76 @@ +// license:BSD-3-Clause +// copyright-holders: F. Ulivi +/********************************************************************* + + hp9895.h + + HP9895 floppy disk drive + +*********************************************************************/ + +#pragma once + +#ifndef _HP9895_H_ +#define _HP9895_H_ + +#include "emu.h" +#include "ieee488.h" +#include "cpu/z80/z80.h" +#include "machine/phi.h" + +class hp9895_device : public device_t, + public device_ieee488_interface +{ +public: + // construction/destruction + hp9895_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // device-level overrides + //virtual ioport_constructor device_input_ports() const override; + virtual void device_start() override; + virtual void device_reset() override; + virtual const tiny_rom_entry *device_rom_region() const override; + virtual machine_config_constructor device_mconfig_additions() const override; + + // device_ieee488_interface overrides + virtual void ieee488_eoi(int state) override; + virtual void ieee488_dav(int state) override; + virtual void ieee488_nrfd(int state) override; + virtual void ieee488_ndac(int state) override; + virtual void ieee488_ifc(int state) override; + virtual void ieee488_srq(int state) override; + virtual void ieee488_atn(int state) override; + virtual void ieee488_ren(int state) override; + + // PHI write CBs + DECLARE_WRITE_LINE_MEMBER(phi_eoi_w); + DECLARE_WRITE_LINE_MEMBER(phi_dav_w); + DECLARE_WRITE_LINE_MEMBER(phi_nrfd_w); + DECLARE_WRITE_LINE_MEMBER(phi_ndac_w); + DECLARE_WRITE_LINE_MEMBER(phi_ifc_w); + DECLARE_WRITE_LINE_MEMBER(phi_srq_w); + DECLARE_WRITE_LINE_MEMBER(phi_atn_w); + DECLARE_WRITE_LINE_MEMBER(phi_ren_w); + + // PHI DIO r/w CBs + DECLARE_READ8_MEMBER(phi_dio_r); + DECLARE_WRITE8_MEMBER(phi_dio_w); + + // PHI IRQ/Z80 NMI + DECLARE_WRITE_LINE_MEMBER(phi_int_w); + + // PHI register read & Z80 IRQ + DECLARE_READ8_MEMBER(phi_reg_r); + DECLARE_WRITE16_MEMBER(z80_m1_w); + +private: + required_device m_cpu; + required_device m_phi; + + bool m_cpu_irq; +}; + +// device type definition +extern const device_type HP9895; + +#endif /* _HP9895_H_ */ diff --git a/src/devices/bus/ieee488/ieee488.cpp b/src/devices/bus/ieee488/ieee488.cpp index 385f3a8cd28..4ff457f3f5e 100644 --- a/src/devices/bus/ieee488/ieee488.cpp +++ b/src/devices/bus/ieee488/ieee488.cpp @@ -397,3 +397,14 @@ SLOT_INTERFACE_START( cbm_ieee488_devices ) SLOT_INTERFACE("shark", SHARK) SLOT_INTERFACE("c4023", C4023) SLOT_INTERFACE_END + +//------------------------------------------------- +// SLOT_INTERFACE( hp_ieee488_devices ) +//------------------------------------------------- + +// slot devices +#include "hp9895.h" + +SLOT_INTERFACE_START(hp_ieee488_devices) + SLOT_INTERFACE("hp9895", HP9895) +SLOT_INTERFACE_END diff --git a/src/devices/bus/ieee488/ieee488.h b/src/devices/bus/ieee488/ieee488.h index 13e6b88d61d..ea57aa6ad4a 100644 --- a/src/devices/bus/ieee488/ieee488.h +++ b/src/devices/bus/ieee488/ieee488.h @@ -247,6 +247,7 @@ extern const device_type IEEE488_SLOT; SLOT_INTERFACE_EXTERN( cbm_ieee488_devices ); +SLOT_INTERFACE_EXTERN( hp_ieee488_devices ); diff --git a/src/devices/machine/phi.cpp b/src/devices/machine/phi.cpp new file mode 100644 index 00000000000..34fa8d6117c --- /dev/null +++ b/src/devices/machine/phi.cpp @@ -0,0 +1,1384 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************* + + phi.h + + HP PHI (Processor-to-Hpib-Interface) (1AA6-6x04) + + PHI supports these features of HP-IB: + * SH1 + * AH1 + * T1/TE1 + * L1/LE1 + * SR1 + * RL2 + * PP1 + * DC1 + * DT1 + * C1,C2,C3,C4,C5 + * HP non-standard IDENTIFY sequence + + Fun fact: PHI has no clock input, its FSMs are driven only by + changes in input signals and by a few internal monostables + + Main reference for this ASIC: + HP 12009-90001, sep 82, HP12009A HP-IB Interface Reference Manual + +*********************************************************************/ + +#include "emu.h" +#include "phi.h" + +// Debugging +#define VERBOSE 1 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +// Macros to clear/set single bits +#define BIT_MASK(n) (1U << (n)) +#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n)) +#define BIT_SET(w , n) ((w) |= BIT_MASK(n)) + +// Timers +enum { + SH_DELAY_TMR_ID, + C_DELAY_TMR_ID +}; + +// Register addresses +enum { + REG_R_INT_COND = 0, // R 0: Interrupting conditions + REG_W_INT_COND = 0, // W 0: Interrupting conditions + REG_R_INT_MASK = 1, // R 1: Interrupt mask + REG_W_INT_MASK = 1, // W 1: Interrupt mask + REG_R_INBOUND_FIFO = 2, // R 2: Inbound FIFO + REG_W_OUTBOUND_FIFO = 2,// W 2: Outbound FIFO + REG_R_STATUS = 3, // R 3: Status + REG_W_STATUS = 3, // W 3: Status + REG_R_CONTROL = 4, // R 4: Control + REG_W_CONTROL = 4, // W 4: Control + REG_R_ADDRESS = 5, // R 5: HPIB address + REG_W_ADDRESS = 5, // W 5: HPIB address + REG_R_1ST_ID = 6, // R 6: 1st ID byte + REG_W_1ST_ID = 6, // W 6: 1st ID byte + REG_R_2ND_ID = 7, // R 7: 2nd ID byte + REG_W_2ND_ID = 7 // W 7: 2nd ID byte +}; + +// All valid bits in registers +#define REG_ALL_MASK 0xc0ff + +// D0 & D1 bits +#define REG_D0D1_MASK 0xc000 // Mask of D0/D1 bits +#define REG_D0D1_SHIFT 14 // Position of D0/D1 bits + +// D8-D15 bits +#define REG_D08D15_MASK 0xff // Mask of D8:D15 bits + +// Bits in INT_COND & INT_MASK +#define REG_INT_DEV_CLEAR_BIT 0 // Device clear +#define REG_INT_FIFO_IDLE_BIT 1 // FIFO idle +#define REG_INT_FIFO_AV_BIT 2 // FIFO bytes available +#define REG_INT_FIFO_ROOM_BIT 3 // FIFO room available +#define REG_INT_SRQ_BIT 4 // Service request +#define REG_INT_PP_RESPONSE_BIT 5 // PP response +#define REG_INT_PROC_ABORT_BIT 6 // Processor handshake abort +#define REG_INT_STATUS_CH_BIT 7 // Status change +#define REG_INT_PARITY_ERR_BIT 14 // Parity error +#define REG_INT_PENDING_BIT 15 // Interrupt pending +#define REG_INT_CLEARABLE_MASK 0x40c1 // Mask of clearable bits +#define REG_INT_STATE_MASK 0x803e // Mask of "state" bits + +// Bits in inbound FIFO +#define REG_IFIFO_NORMAL_MASK 0x0000 // Mask of D0/D1 bits for "normal" bytes +#define REG_IFIFO_CNT_EXP_MASK 0x8000 // Mask for a byte that caused byte count to expire +#define REG_IFIFO_LAST_MASK 0xc000 // Mask for last byte in a record +#define REG_IFIFO_2_ADDR_MASK 0x4000 // Mask for secondary addresses +#define REG_IFIFO_TALK_BIT 5 // Bit of "talk" flag + +// Bits in outbound FIFO +#define REG_OFIFO_SPECIAL_BIT 14 // Bit to discriminate between normal bytes and the rest +#define REG_OFIFO_END_BIT 15 // Bit of EOI +#define REG_OFIFO_IFCMD_MASK 0x4000 // Mask of interface commands +#define REG_OFIFO_UNCNT_MASK 0xc000 // Mask of uncounted transfer enable +#define REG_OFIFO_XFER_EN_MASK 0x0000 // Mask of byte transfer enable +#define REG_OFIFO_LF_INH_BIT 15 // Bit of LF detection inhibit + +// Bits in status register +#define REG_STATUS_DATA_FREEZE_BIT 0 // Outbound data freeze +#define REG_STATUS_LISTEN_BIT 1 // Addressed to listen +#define REG_STATUS_TALK_BIT 2 // Addressed to talk or identify +#define REG_STATUS_SYS_CTRL_BIT 3 // System controller +#define REG_STATUS_CONTROLLER_BIT 4 // Current controller +#define REG_STATUS_REMOTE_BIT 5 // Remote state +#define REG_STATUS_D0D1_BIT 6 // D0/D1 bit access +#define REG_STATUS_STATE_MASK 0x3e // Mask of "state" bits + +// Bits in control register +#define REG_CTRL_INIT_OFIFO_BIT 0 // Initialize outbound FIFO +#define REG_CTRL_DMA_FIFO_BIT 1 // DMA FIFO selection +#define REG_CTRL_SERVICE_REQ_BIT 2 // Request service +#define REG_CTRL_PP_RESPONSE_BIT 3 // Respond to PP +#define REG_CTRL_IFC_BIT 4 // IFC value +#define REG_CTRL_REN_BIT 5 // REN value +#define REG_CTRL_PAR_FREEZE_BIT 6 // Parity freeze +#define REG_CTRL_8BIT_PROC_BIT 7 // 8-bit processor + +// Bits in address register +#define REG_ADDR_HPIB_ADDR_BIT 0 // HPIB address +#define REG_ADDR_LA_BIT 5 // Listen always +#define REG_ADDR_TA_BIT 6 // Talk always +#define REG_ADDR_ONLINE_BIT 7 // Online + +// Interface commands +#define IFCMD_MASK 0x7f // Mask of interface commands +#define IFCMD_DCL 0x14 // Device clear +#define IFCMD_GET 0x08 // Group execute trigger +#define IFCMD_GTL 0x01 // Go to local +#define IFCMD_LLO 0x11 // Local lock-out +#define IFCMD_AG_MASK 0x60 // Mask of bits identifying address group commands +#define IFCMD_ADDR_MASK 0x1f // Mask of address in AG commands +#define IFCMD_LAG_VALUE 0x20 // Value of LAG commands +#define IFCMD_TAG_VALUE 0x40 // Value of TAG commands +#define IFCMD_SCG_VALUE 0x60 // Value of SCG commands +#define IFCMD_PPC 0x05 // Parallel poll configure +#define IFCMD_PPX_MASK 0x70 // Mask of PPE/PPD commands +#define IFCMD_PPE_VALUE 0x60 // Parallel poll enable +#define IFCMD_PPE_S_BIT 3 // Position of "S" bit in PPE +#define IFCMD_PPE_PPR_MASK 7 // Mask in PPE of PPR msg no. +#define IFCMD_PPD_VALUE 0x70 // Parallel poll disable +#define IFCMD_PPU 0x15 // Parallel poll unconfigure +#define IFCMD_SDC 0x04 // Selected device clear +#define IFCMD_SPD 0x19 // Serial poll disable +#define IFCMD_SPE 0x18 // Serial poll enable +#define IFCMD_TCT 0x09 // Take control +#define IFCMD_UNL 0x3f // Unlisten +#define IFCMD_UNT 0x5f // Untalk + +// Delays +#define DELAY_T1 2000 // T1: 2 us +#define DELAY_T7 500 // T7: 0.5 us +#define DELAY_T9 1500 // T9: 1.5 us +#define DELAY_T10 1500 // T10: 1.5 us + +// Controller address +#define CONTROLLER_ADDR 0x1e // PHI always has this address when it's a controller + +// Device type definition +const device_type PHI = &device_creator; + +// Constructors +phi_device::phi_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname) + : device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__), + m_dio_read_func(*this), + m_dio_write_func(*this), + m_signal_wr_fns{ + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this) }, + m_int_write_func(*this), + m_dmarq_write_func(*this) +{ +} + +phi_device::phi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PHI, "HP PHI", tag, owner, clock, "PHI", __FILE__), + m_dio_read_func(*this), + m_dio_write_func(*this), + m_signal_wr_fns{ + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this), + devcb_write_line(*this) }, + m_int_write_func(*this), + m_dmarq_write_func(*this) +{ +} + +WRITE_LINE_MEMBER(phi_device::eoi_w) +{ + set_ext_signal(PHI_488_EOI , state); +} + +WRITE_LINE_MEMBER(phi_device::dav_w) +{ + set_ext_signal(PHI_488_DAV , state); +} + +WRITE_LINE_MEMBER(phi_device::nrfd_w) +{ + set_ext_signal(PHI_488_NRFD , state); +} + +WRITE_LINE_MEMBER(phi_device::ndac_w) +{ + set_ext_signal(PHI_488_NDAC , state); +} + +WRITE_LINE_MEMBER(phi_device::ifc_w) +{ + set_ext_signal(PHI_488_IFC , state); +} + +WRITE_LINE_MEMBER(phi_device::srq_w) +{ + set_ext_signal(PHI_488_SRQ , state); +} + +WRITE_LINE_MEMBER(phi_device::atn_w) +{ + set_ext_signal(PHI_488_ATN , state); +} + +WRITE_LINE_MEMBER(phi_device::ren_w) +{ + set_ext_signal(PHI_488_REN , state); +} + + +void phi_device::set_ext_signal(phi_488_signal_t signal , int state) +{ + state = !state; + if (m_ext_signals[ signal ] != state) { + m_ext_signals[ signal ] = state; + LOG(("EXT EOI %d DAV %d NRFD %d NDAC %d IFC %d SRQ %d ATN %d REN %d\n" , + m_ext_signals[ PHI_488_EOI ] , + m_ext_signals[ PHI_488_DAV ] , + m_ext_signals[ PHI_488_NRFD ] , + m_ext_signals[ PHI_488_NDAC ] , + m_ext_signals[ PHI_488_IFC ] , + m_ext_signals[ PHI_488_SRQ ] , + m_ext_signals[ PHI_488_ATN ] , + m_ext_signals[ PHI_488_REN ])); + update_fsm(); + } +} + +WRITE16_MEMBER(phi_device::reg16_w) +{ + int_reg_w(offset , data & REG_ALL_MASK); +} + +READ16_MEMBER(phi_device::reg16_r) +{ + uint16_t res; + + switch (offset) { + case REG_R_INT_COND: + res = m_reg_int_cond & m_reg_int_mask; + break; + + case REG_R_INT_MASK: + res = m_reg_int_mask; + break; + + case REG_R_INBOUND_FIFO: + // TODO: read from PP response + if (m_fifo_in.empty()) { + BIT_SET(m_reg_int_cond, REG_INT_PROC_ABORT_BIT); + res = 0; + } else { + res = m_fifo_in.dequeue(); + } + update_fsm(); + break; + + case REG_R_STATUS: + res = m_reg_status; + break; + + case REG_R_CONTROL: + res = m_reg_control; + break; + + case REG_R_ADDRESS: + res = m_reg_address; + break; + + case REG_R_1ST_ID: + res = m_reg_1st_id; + break; + + case REG_R_2ND_ID: + res = m_reg_2nd_id; + break; + + default: + res = 0; + LOG(("Reading from unmapped address (%u)\n", offset)); + break; + }; + + if (offset != REG_R_STATUS) { + // Store D0/D1 in top bits of status register + m_reg_status = (m_reg_status & ~(3U << REG_STATUS_D0D1_BIT)) | + ((res & REG_D0D1_MASK) >> (REG_D0D1_SHIFT - REG_STATUS_D0D1_BIT)); + } + + //LOG(("R %u=%04x\n" , offset , res)); + return res; +} + +WRITE8_MEMBER(phi_device::reg8_w) +{ + int_reg_w(offset , data); +} + +READ8_MEMBER(phi_device::reg8_r) +{ + return (uint8_t)reg16_r(space , offset , mem_mask); +} + +void phi_device::device_start() +{ + save_item(NAME(m_dio)); + save_item(NAME(m_signals)); + save_item(NAME(m_ext_signals)); + save_item(NAME(m_sys_controller)); + save_item(NAME(m_loopback)); + save_item(NAME(m_id_enabled)); + save_item(NAME(m_sh_state)); + save_item(NAME(m_ah_state)); + save_item(NAME(m_t_state)); + save_item(NAME(m_t_spms)); + save_item(NAME(m_l_state)); + save_item(NAME(m_sr_state)); + save_item(NAME(m_pp_state)); + save_item(NAME(m_pp_pacs)); + save_item(NAME(m_ppr_msg)); + save_item(NAME(m_s_sense)); + save_item(NAME(m_c_state)); + save_item(NAME(m_be_counter)); + save_item(NAME(m_reg_status)); + save_item(NAME(m_reg_int_cond)); + save_item(NAME(m_reg_int_mask)); + save_item(NAME(m_reg_1st_id)); + save_item(NAME(m_reg_2nd_id)); + save_item(NAME(m_reg_control)); + save_item(NAME(m_reg_address)); + save_item(NAME(m_nba_origin)); + + m_dio_read_func.resolve_safe(0xff); + m_dio_write_func.resolve_safe(); + for (auto& f : m_signal_wr_fns) { + f.resolve_safe(); + } + m_int_write_func.resolve_safe(); + m_dmarq_write_func.resolve_safe(); + + m_sh_dly_timer = timer_alloc(SH_DELAY_TMR_ID); + m_c_dly_timer = timer_alloc(C_DELAY_TMR_ID); +} + +void phi_device::device_reset() +{ + m_dio = 0; + for (auto& s : m_signals) { + s = false; + } + for (auto& s : m_ext_signals) { + s = false; + } + m_no_recursion = false; + // The following variables are set "true" because m_reg_address is set to 0 + m_sys_controller = true; + m_loopback = true; + m_id_enabled = false; + m_reg_status = 0; + m_reg_int_cond = 0; + m_reg_int_mask = 0; + m_reg_1st_id = 0; + m_reg_2nd_id = 0; + m_reg_control = 0; + m_reg_address = 0; + m_fifo_in.clear(); + m_fifo_out.clear(); + + pon_msg(); + update_488(); +} + +void phi_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + LOG(("tmr %d enabled %d\n" , id , timer.enabled())); + update_fsm(); +} + +void phi_device::int_reg_w(offs_t offset , uint16_t data) +{ + if (BIT(m_reg_control , REG_CTRL_8BIT_PROC_BIT)) { + // In 8-bit mode, D0/D1 come from status register + data = (data & REG_D08D15_MASK) | ((m_reg_status << (REG_D0D1_SHIFT - REG_STATUS_D0D1_BIT)) & REG_D0D1_MASK); + } + + //LOG(("W %u=%04x\n" , offset , data)); + + switch (offset) { + case REG_W_INT_COND: + // Bits D1/D8/D9/D15 only are clearable when written to 1 + m_reg_int_cond &= ~(data & REG_INT_CLEARABLE_MASK); + update_fsm(); + break; + + case REG_W_INT_MASK: + m_reg_int_mask = data; + update_fsm(); + break; + + case REG_W_OUTBOUND_FIFO: + if (m_fifo_out.full()) { + BIT_SET(m_reg_int_cond, REG_INT_PROC_ABORT_BIT); + } else { + m_fifo_out.enqueue(data); + } + update_fsm(); + break; + + case REG_W_STATUS: + // Copy D0/D1 access bits into status register + m_reg_status = (m_reg_status & ~(3U << REG_STATUS_D0D1_BIT)) | + (data & (3U << REG_STATUS_D0D1_BIT)); + if (BIT(data , REG_STATUS_DATA_FREEZE_BIT) && m_fifo_in.empty()) { + BIT_CLR(m_reg_status, REG_STATUS_DATA_FREEZE_BIT); + } + update_fsm(); + break; + + case REG_W_CONTROL: + // D0/D1/D15 are not mapped into register + m_reg_control = data & 0xfe; + if (BIT(data , REG_CTRL_INIT_OFIFO_BIT)) { + m_fifo_out.clear(); + } + if (m_loopback) { + // TODO: better? + m_id_enabled = BIT(m_reg_control , REG_CTRL_PP_RESPONSE_BIT); + } + update_fsm(); + break; + + case REG_W_ADDRESS: + { + // No D0/D1 in register + data &= REG_D08D15_MASK; + bool prev_ol = BIT(m_reg_address , REG_ADDR_ONLINE_BIT); + m_reg_address = data; + bool current_ol = BIT(m_reg_address , REG_ADDR_ONLINE_BIT); + // TODO: consider SYS CNTRL input, too + m_sys_controller = !current_ol; + m_loopback = !current_ol; + if (!current_ol) { + // IDENTIFY is enabled by PP_RESPONSE bit in control register + m_id_enabled = BIT(m_reg_control , REG_CTRL_PP_RESPONSE_BIT); + } else if (!prev_ol) { + // Going on-line + pon_msg(); + m_id_enabled = BIT(m_reg_control , REG_CTRL_PP_RESPONSE_BIT); + } + if (prev_ol != current_ol) { + update_488(); + } + update_fsm(); + } + break; + + case REG_W_1ST_ID: + // No D0/D1 in register + m_reg_1st_id = data & REG_D08D15_MASK; + update_fsm(); + break; + + case REG_W_2ND_ID: + // No D0/D1 in register + m_reg_2nd_id = data & REG_D08D15_MASK; + update_fsm(); + break; + + default: + LOG(("Writing to unmapped address (%u)\n", offset)); + break; + } +} + +uint8_t phi_device::get_dio(void) +{ + if (m_loopback) { + return m_dio; + } else { + return ~m_dio_read_func(); + } +} + +void phi_device::set_dio(uint8_t data) +{ + if (data != m_dio) { + LOG(("DIO=%02x\n" , data)); + m_dio = data; + if (!m_loopback) { + m_dio_write_func(~data); + } + } +} + +bool phi_device::get_signal(phi_488_signal_t signal) +{ + if (m_loopback) { + return m_signals[ signal ]; + } else { + return m_ext_signals[ signal ]; + } +} + +void phi_device::set_signal(phi_488_signal_t signal , bool state) +{ + if (state != m_signals[ signal ]) { + m_signals[ signal ] = state; + LOG(("INT EOI %d DAV %d NRFD %d NDAC %d IFC %d SRQ %d ATN %d REN %d\n" , + m_signals[ PHI_488_EOI ] , + m_signals[ PHI_488_DAV ] , + m_signals[ PHI_488_NRFD ] , + m_signals[ PHI_488_NDAC ] , + m_signals[ PHI_488_IFC ] , + m_signals[ PHI_488_SRQ ] , + m_signals[ PHI_488_ATN ] , + m_signals[ PHI_488_REN ])); + if (!m_loopback) { + m_signal_wr_fns[ signal ](!state); + } + } +} + +void phi_device::pon_msg(void) +{ + m_sh_state = PHI_SH_SIDS; + m_ah_state = PHI_AH_AIDS; + m_t_state = PHI_T_TIDS; + m_t_spms = false; + m_l_state = PHI_L_LIDS; + m_sr_state = PHI_SR_NPRS; + m_pp_state = PHI_PP_PPIS; + m_pp_pacs = false; + m_ppr_msg = my_address(); + m_s_sense = true; + m_c_state = PHI_C_CIDS; + m_be_counter = 0; + m_nba_origin = NBA_NONE; +} + +void phi_device::update_488(void) +{ + if (m_loopback) { + m_dio_write_func(~0); + for (auto& f : m_signal_wr_fns) { + f(1); + } + } else { + m_dio_write_func(~m_dio); + for (unsigned i = 0; i < PHI_488_SIGNAL_COUNT; i++) { + m_signal_wr_fns[ i ](!m_signals[ i ]); + } + } +} + +void phi_device::update_fsm(void) +{ + if (m_no_recursion) { + // Prevent recursion into this function whenever a signal change propagates through + // set_ext_signal + return; + } + m_no_recursion = true; + + set_signal(PHI_488_IFC , m_sys_controller && BIT(m_reg_control , REG_CTRL_IFC_BIT)); + set_signal(PHI_488_REN , m_sys_controller && BIT(m_reg_control , REG_CTRL_REN_BIT)); + + bool changed = true; + int prev_state; + uint8_t new_byte = 0; + bool new_eoi = false; + bool prev_cic = controller_in_charge(); + // TODO: SR FSM + // TODO: RL FSM + // Loop until all changes settle + while (changed) { + LOG(("SH %d AH %d T %d SPMS %d L %d SR %d PP %d PACS %d PPR %u S %d C %d\n" , + m_sh_state , m_ah_state , m_t_state , m_t_spms , m_l_state , m_sr_state , + m_pp_state , m_pp_pacs , m_ppr_msg , m_s_sense , m_c_state)); + LOG(("O E/F=%d/%d I E/F=%d/%d\n" , m_fifo_out.empty() , m_fifo_out.full() , m_fifo_in.empty() , m_fifo_in.full())); + changed = false; + + // SH FSM + prev_state = m_sh_state; + bool sh_reset = + (get_signal(PHI_488_ATN) && !(m_c_state == PHI_C_CACS || m_c_state == PHI_C_CTRS)) || + (!get_signal(PHI_488_ATN) && !(m_t_state == PHI_T_TACS || m_t_state == PHI_T_SPAS || m_t_state == PHI_T_ID3 || m_t_state == PHI_T_ID5)); + + if (sh_reset) { + m_sh_state = PHI_SH_SIDS; + m_sh_dly_timer->reset(); + } else { + switch (m_sh_state) { + case PHI_SH_SIDS: + if (m_t_state == PHI_T_TACS || + m_t_state == PHI_T_SPAS || + m_t_state == PHI_T_ID3 || + m_t_state == PHI_T_ID5 || + m_c_state == PHI_C_CACS) { + m_sh_state = PHI_SH_SGNS; + } + break; + + case PHI_SH_SGNS: + if ((m_nba_origin = nba_msg(new_byte , new_eoi)) != NBA_NONE) { + m_sh_state = PHI_SH_SDYS; + m_sh_dly_timer->adjust(attotime::from_nsec(DELAY_T1)); + LOG(("SH DLY enabled %d\n" , m_sh_dly_timer->enabled())); + } + break; + + case PHI_SH_SDYS: + if (!get_signal(PHI_488_NRFD) && !m_sh_dly_timer->enabled()) { + m_sh_state = PHI_SH_STRS; + } + break; + + case PHI_SH_STRS: + if (!get_signal(PHI_488_NDAC)) { + m_sh_state = PHI_SH_SGNS; + clear_nba((nba_origin_t)m_nba_origin); + } + break; + + default: + logerror("Invalid SH state %d\n" , m_sh_state); + m_sh_state = PHI_SH_SIDS; + } + } + if (m_sh_state != prev_state) { + changed = true; + } + + // SH outputs + // EOI is controlled by SH & C FSMs + bool eoi_signal; + set_signal(PHI_488_DAV , m_sh_state == PHI_SH_STRS); + if (m_sh_state == PHI_SH_SDYS || m_sh_state == PHI_SH_STRS) { + nba_msg(new_byte , new_eoi); + set_dio(new_byte); + eoi_signal = new_eoi; + } else { + set_dio(0); + eoi_signal = false; + } + + // AH FSM + prev_state = m_ah_state; + bool ah_reset = !(get_signal(PHI_488_ATN) || m_l_state == PHI_L_LADS || m_l_state == PHI_L_LACS || m_c_state == PHI_C_CSBS); + if (ah_reset) { + m_ah_state = PHI_AH_AIDS; + } else { + switch (m_ah_state) { + case PHI_AH_AIDS: + m_ah_state = PHI_AH_ANRS; + break; + + case PHI_AH_ANRS: + //if (!tcs_msg() && (get_signal(PHI_488_ATN) || rdy_msg())) { + // According to standard either ATN or rdy should also be true, but rdy is always true in PHI + if (!tcs_msg()) { + m_ah_state = PHI_AH_ACRS; + } + break; + + case PHI_AH_ACRS: + if (get_signal(PHI_488_DAV)) { + m_ah_state = PHI_AH_ACDS; + } + // rdy is always true + // } else if (!get_signal(PHI_488_ATN) && !rdy_msg()) { + // m_ah_state = PHI_AH_ANRS; + // } + break; + + case PHI_AH_ACDS: + // FSM stays in this state until the acceptor has + // accepted the data byte or the interface command. + if (get_signal(PHI_488_ATN)) { + uint8_t if_cmd = get_dio(); + bool parity_ok = odd_parity(if_cmd); + if (!parity_ok) { + BIT_SET(m_reg_int_cond , REG_INT_PARITY_ERR_BIT); + } + if (BIT(m_reg_control , REG_CTRL_PAR_FREEZE_BIT) && !parity_ok) { + // With even parity and PARITY FREEZE set, command is ignored and + // AH FSM freezes in ACDS + m_ah_state = PHI_AH_ACDS_FROZEN; + } else { + // Clear parity bit & process command + if_cmd &= IFCMD_MASK; + if (if_cmd_received(if_cmd)) { + m_ah_state = PHI_AH_AWNS; + } + } + } else if (byte_received(get_dio() , get_signal(PHI_488_EOI))) { + m_ah_state = PHI_AH_AWNS; + } + break; + + case PHI_AH_ACDS_FROZEN: + case PHI_AH_AWNS: + if (!get_signal(PHI_488_DAV)) { + m_ah_state = PHI_AH_ANRS; + } + break; + + default: + logerror("Invalid AH state %d\n" , m_ah_state); + m_ah_state = PHI_AH_AIDS; + } + } + if (m_ah_state != prev_state) { + changed = true; + } + // AH outputs + set_signal(PHI_488_NRFD , m_ah_state == PHI_AH_ANRS || m_ah_state == PHI_AH_ACDS || m_ah_state == PHI_AH_ACDS_FROZEN || m_ah_state == PHI_AH_AWNS); + set_signal(PHI_488_NDAC , m_ah_state == PHI_AH_ANRS || m_ah_state == PHI_AH_ACRS || m_ah_state == PHI_AH_ACDS || m_ah_state == PHI_AH_ACDS_FROZEN); + + // T FSM + prev_state = m_t_state; + if (get_signal(PHI_488_IFC)) { + m_t_state = PHI_T_TIDS; + m_t_spms = false; + } else { + switch (m_t_state) { + case PHI_T_TIDS: + if (ton_msg()) { + m_t_state = PHI_T_TADS; + } + break; + + case PHI_T_TADS: + if (!get_signal(PHI_488_ATN)) { + if (m_t_spms) { + m_t_state = PHI_T_SPAS; + } else { + m_t_state = PHI_T_TACS; + } + } + break; + + case PHI_T_SPAS: + case PHI_T_TACS: + if (get_signal(PHI_488_ATN)) { + m_t_state = PHI_T_TADS; + } + break; + + case PHI_T_ID1: + break; + + case PHI_T_ID2: + if (!get_signal(PHI_488_ATN)) { + m_t_state = PHI_T_ID3; + } + break; + + case PHI_T_ID3: + if (get_signal(PHI_488_ATN)) { + m_t_state = PHI_T_ID2; + } + break; + + case PHI_T_ID4: + if (!get_signal(PHI_488_ATN)) { + m_t_state = PHI_T_ID5; + } + break; + + case PHI_T_ID5: + if (get_signal(PHI_488_ATN)) { + m_t_state = PHI_T_ID4; + } + break; + + case PHI_T_ID6: + break; + + default: + logerror("Invalid T state %d\n" , m_t_state); + m_t_state = PHI_T_TIDS; + } + } + if (m_t_state != prev_state) { + changed = true; + } + // No direct T outputs + + // L FSM + prev_state = m_l_state; + if (get_signal(PHI_488_IFC)) { + m_l_state = PHI_L_LIDS; + } else { + switch (m_l_state) { + case PHI_L_LIDS: + if (lon_msg()) { + m_l_state = PHI_L_LADS; + } + break; + + case PHI_L_LADS: + if (!get_signal(PHI_488_ATN)) { + m_l_state = PHI_L_LACS; + } + break; + + case PHI_L_LACS: + if (get_signal(PHI_488_ATN)) { + m_l_state = PHI_L_LADS; + } + break; + + default: + logerror("Invalid L state %d\n" , m_l_state); + m_l_state = PHI_L_LIDS; + } + } + if (m_l_state != prev_state) { + changed = true; + } + // No direct L outputs + + // PP FSM + prev_state = m_pp_state; + switch (m_pp_state) { + case PHI_PP_PPIS: + break; + + case PHI_PP_PPSS: + if (get_signal(PHI_488_ATN) && get_signal(PHI_488_EOI)) { + m_pp_state = PHI_PP_PPAS; + } + break; + + case PHI_PP_PPAS: + if (!get_signal(PHI_488_ATN) || !get_signal(PHI_488_EOI)) { + m_pp_state = PHI_PP_PPSS; + } + break; + + default: + logerror("Invalid PP state %d\n" , m_pp_state); + m_pp_state = PHI_PP_PPIS; + } + if (m_pp_state != prev_state) { + changed = true; + } + // PP outputs + if (m_pp_state == PHI_PP_PPAS && m_s_sense == !!BIT(m_reg_control , REG_CTRL_PP_RESPONSE_BIT) && m_ppr_msg <= 7) { + set_dio(1 << m_ppr_msg); + } + + // C FSM + prev_state = m_c_state; + if (!m_sys_controller && get_signal(PHI_488_IFC)) { + m_c_state = PHI_C_CIDS; + m_c_dly_timer->reset(); + } else { + switch (m_c_state) { + case PHI_C_CIDS: + if (m_sys_controller && get_signal(PHI_488_IFC)) { + m_c_state = PHI_C_CADS; + } + break; + + case PHI_C_CADS: + if (!get_signal(PHI_488_ATN)) { + m_c_state = PHI_C_CACS; + } + break; + + case PHI_C_CACS: + // If there are ifcmds to send, just stay in CACS + // else wait for SH to finish its current transfer then decide what to do + if (nba_msg(new_byte , new_eoi) != NBA_CMD_FROM_OFIFO && + m_sh_state != PHI_SH_STRS && m_sh_state != PHI_SH_SDYS) { + if (!m_fifo_out.empty()) { + // Possible cases + // D0/D1 Meaning of 1st word of OFIFO + // ===================================== + // x0 Counted transfer enable or byte to be sent + // 11 Uncounted transfer enable + // 01 Send interface command (already caught by nba_msg) + m_c_state = PHI_C_CSBS; + m_be_counter = 0; + } else if (rpp_msg()) { + // Start parallel polling + m_c_state = PHI_C_CPWS; + } + // It's not clear what PHI does when it's controller-in-charge + // and it's nothing to do. Here we park it in CACS. + } + break; + + case PHI_C_CPWS: + // TODO: + break; + + case PHI_C_CPPS: + // TODO: + break; + + case PHI_C_CSBS: + if (tcs_msg() && m_ah_state == PHI_AH_ANRS) { + m_c_state = PHI_C_CSHS; + m_c_dly_timer->adjust(attotime::from_nsec(DELAY_T10)); + } + break; + + case PHI_C_CSHS: + // tcs_msg cannot go false here + if (!m_c_dly_timer->enabled()) { + m_c_state = PHI_C_CSWS; + m_c_dly_timer->adjust(attotime::from_nsec(DELAY_T7)); + } + break; + + case PHI_C_CAWS: + if (rpp_msg()) { + m_c_state = PHI_C_CPWS; + m_c_dly_timer->reset(); + } else if (!m_c_dly_timer->enabled()) { + m_c_state = PHI_C_CACS; + } + break; + + case PHI_C_CTRS: + if (m_sh_state != PHI_SH_STRS) { + m_c_state = PHI_C_CIDS; + } + break; + + case PHI_C_CSWS: + if (m_t_state == PHI_T_TADS || !m_c_dly_timer->enabled()) { + m_c_state = PHI_C_CAWS; + m_c_dly_timer->adjust(attotime::from_nsec(DELAY_T9)); + } + break; + + default: + logerror("Invalid C state %d\n" , m_c_state); + m_c_state = PHI_C_CIDS; + } + } + if (m_c_state != prev_state) { + changed = true; + } + // C outputs + set_signal(PHI_488_ATN , m_c_state == PHI_C_CACS || m_c_state == PHI_C_CPWS || + m_c_state == PHI_C_CPPS || m_c_state == PHI_C_CSWS || + m_c_state == PHI_C_CAWS || m_c_state == PHI_C_CTRS); + eoi_signal = eoi_signal || m_c_state == PHI_C_CPWS || m_c_state == PHI_C_CPPS; + set_signal(PHI_488_EOI , eoi_signal); + } + + // Update status register + m_reg_status &= ~REG_STATUS_STATE_MASK; + if (m_l_state != PHI_L_LIDS) { + BIT_SET(m_reg_status, REG_STATUS_LISTEN_BIT); + } + if (m_t_state != PHI_T_TIDS && m_t_state != PHI_T_ID1) { + BIT_SET(m_reg_status, REG_STATUS_TALK_BIT); + } + if (m_sys_controller) { + BIT_SET(m_reg_status, REG_STATUS_SYS_CTRL_BIT); + } + if (controller_in_charge()) { + BIT_SET(m_reg_status, REG_STATUS_CONTROLLER_BIT); + } + // TODO: REG_STATUS_REMOTE_BIT + + // Update interrupting condition register and INT signal + if (prev_cic != controller_in_charge()) { + BIT_SET(m_reg_int_cond, REG_INT_STATUS_CH_BIT); + } + m_reg_int_cond &= ~REG_INT_STATE_MASK; + if (m_fifo_out.empty()) { + BIT_SET(m_reg_int_cond , REG_INT_FIFO_IDLE_BIT); + } + if (!m_fifo_in.empty()) { + BIT_SET(m_reg_int_cond, REG_INT_FIFO_AV_BIT); + } + if (!m_fifo_out.full()) { + BIT_SET(m_reg_int_cond, REG_INT_FIFO_ROOM_BIT); + } + if (controller_in_charge() && get_signal(PHI_488_SRQ)) { + BIT_SET(m_reg_int_cond, REG_INT_SRQ_BIT); + } + // TODO: REG_INT_PP_RESPONSE_BIT + bool int_pending = (m_reg_int_cond & m_reg_int_mask) != 0; + bool int_line = false; + if (int_pending) { + BIT_SET(m_reg_int_cond, REG_INT_PENDING_BIT); + if (BIT(m_reg_int_mask , REG_INT_PENDING_BIT)) { + int_line = true; + } + } + if (int_line != m_int_line) { + m_int_line = int_line; + m_int_write_func(m_int_line); + } + + // TODO: update DMARQ + m_no_recursion = false; +} + +phi_device::nba_origin_t phi_device::nba_msg(uint8_t& new_byte , bool& new_eoi) const +{ + // TODO: consider CIC + if (!m_fifo_out.empty()) { + uint16_t word = m_fifo_out.peek(); + if ((word & REG_D0D1_MASK) == REG_OFIFO_IFCMD_MASK) { + // Controller sends an interface command + new_byte = (uint8_t)(word & IFCMD_MASK); + if (!odd_parity(new_byte)) { + BIT_SET(new_byte, 7); + } + new_eoi = false; + return NBA_CMD_FROM_OFIFO; + } + } + + switch (m_t_state) { + case PHI_T_TACS: + if (!BIT(m_reg_status , REG_STATUS_DATA_FREEZE_BIT) && + !BIT(m_reg_int_cond , REG_INT_DEV_CLEAR_BIT) && + !m_fifo_out.empty()) { + uint16_t word = m_fifo_out.peek(); + if (!BIT(word , REG_OFIFO_SPECIAL_BIT)) { + // Talker sends a data byte + new_byte = (uint8_t)word; + new_eoi = BIT(word , REG_OFIFO_END_BIT); + return NBA_BYTE_FROM_OFIFO; + } + } + break; + + case PHI_T_SPAS: + // Reply to serial poll: STB & RQS + new_byte = m_sr_state == PHI_SR_NPRS ? 0x80 : 0x40; + new_eoi = false; + return NBA_FROM_SPAS; + + case PHI_T_ID3: + // 1st byte of ID + new_byte = (uint8_t)m_reg_1st_id; + new_eoi = false; + return NBA_FROM_ID3; + + case PHI_T_ID5: + // 2nd byte of ID + new_byte = (uint8_t)m_reg_2nd_id; + new_eoi = true; + return NBA_FROM_ID5; + + default: + break; + } + return NBA_NONE; +} + +void phi_device::clear_nba(nba_origin_t origin) +{ + switch (origin) { + case NBA_CMD_FROM_OFIFO: + case NBA_BYTE_FROM_OFIFO: + m_fifo_out.dequeue(); + break; + + case NBA_FROM_ID3: + m_t_state = PHI_T_ID4; + break; + + case NBA_FROM_ID5: + m_t_state = PHI_T_ID6; + break; + + default: + break; + } +} + +bool phi_device::if_cmd_received(uint8_t byte) +{ + LOG(("RX cmd: %02x\n" , byte)); + + bool accepted = true; + + if ((byte & IFCMD_AG_MASK) != IFCMD_SCG_VALUE && byte != IFCMD_PPC) { + // PACS -> PUCS when receiving a PCG command that is not PPC + m_pp_pacs = false; + } + + // TODO: IFC & non-reflection into controller + switch (byte) { + case IFCMD_GTL: + // Go to local + // TODO: + break; + + case IFCMD_SDC: + // Selected device clear + if (m_l_state == PHI_L_LADS && !controller_in_charge()) { + BIT_SET(m_reg_int_cond, REG_INT_DEV_CLEAR_BIT); + } + break; + + case IFCMD_PPC: + // Parallel poll configure + if (m_l_state == PHI_L_LADS) { + m_pp_pacs = true; + } + break; + + case IFCMD_GET: + // Group execute trigger + // TODO: + break; + + case IFCMD_TCT: + // Take control + if (m_c_state == PHI_C_CIDS && m_t_state == PHI_T_TADS) { + // Take control + m_c_state = PHI_C_CADS; + } + if (m_c_state == PHI_C_CACS && m_t_state != PHI_T_TADS) { + // Give control to someone else + m_c_state = PHI_C_CTRS; + } + break; + + case IFCMD_LLO: + // Local lock-out + // TODO: + break; + + case IFCMD_DCL: + // Device clear + if (!controller_in_charge()) { + BIT_SET(m_reg_int_cond, REG_INT_DEV_CLEAR_BIT); + } + break; + + case IFCMD_PPU: + // Parallel poll unconfigure + if (m_pp_state == PHI_PP_PPSS) { + m_pp_state = PHI_PP_PPIS; + } + break; + + case IFCMD_SPE: + // Serial poll enable + m_t_spms = true; + break; + + case IFCMD_SPD: + // Serial poll disable + m_t_spms = false; + break; + + case IFCMD_UNL: + // Unlisten + if (m_l_state == PHI_L_LADS && !lon_msg()) { + m_l_state = PHI_L_LIDS; + } + break; + + case IFCMD_UNT: + // Untalk + if (!ton_msg()) { + if (m_id_enabled) { + m_t_state = PHI_T_ID1; + } else { + m_t_state = PHI_T_TIDS; + } + } + break; + + default: + { + uint8_t address = byte & IFCMD_ADDR_MASK; + uint8_t ag = byte & IFCMD_AG_MASK; + bool my_addr = address == my_address(); + + if (ag == IFCMD_LAG_VALUE) { + // LAG + if (!lon_msg() && m_l_state == PHI_L_LIDS && my_addr) { + m_l_state = PHI_L_LADS; + } + } else if (ag == IFCMD_TAG_VALUE) { + // TAG + if (!ton_msg()) { + if (my_addr) { + // MTA + m_t_state = PHI_T_TADS; + } else { + // OTA + m_t_state = PHI_T_TIDS; + } + } + } else if (ag == IFCMD_SCG_VALUE) { + // SCG + if (m_pp_pacs) { + if ((byte & IFCMD_PPX_MASK) == IFCMD_PPE_VALUE && m_pp_state == PHI_PP_PPIS) { + // PPE + m_s_sense = BIT(byte , IFCMD_PPE_S_BIT); + m_ppr_msg = byte & IFCMD_PPE_PPR_MASK; + m_pp_state = PHI_PP_PPSS; + } else if ((byte & IFCMD_PPX_MASK) == IFCMD_PPD_VALUE && m_pp_state == PHI_PP_PPSS) { + // PPD + m_pp_state = PHI_PP_PPIS; + } + } else { + // command is a secondary address + if (m_t_state == PHI_T_ID1 && my_addr) { + // Start IDENTIFY sequence + m_t_state = PHI_T_ID2; + } else if (m_t_state >= PHI_T_ID2 && m_t_state <= PHI_T_ID6 && !my_addr) { + // Unaddressed by OSA + m_t_state = PHI_T_ID1; + } else if (m_t_state == PHI_T_TADS || m_l_state == PHI_L_LADS) { + if (m_fifo_in.full() || BIT(m_reg_int_cond , REG_INT_DEV_CLEAR_BIT)) { + // No room for secondary address in FIFO, stall handshake + accepted = false; + } else { + uint16_t word = REG_IFIFO_2_ADDR_MASK | address; + if (m_t_state == PHI_T_TADS) { + BIT_SET(word, REG_IFIFO_TALK_BIT); + } + m_fifo_in.enqueue(word); + } + } + } + } + } + } + return accepted; +} + +bool phi_device::byte_received(uint8_t byte , bool eoi) +{ + // Start with D0/D1 = 00 + uint16_t word = byte; + + if (eoi) { + // EOI -> D0/D1 = 11 + word |= REG_IFIFO_LAST_MASK; + } + + bool end_of_transfer = false; + + if (!m_fifo_out.empty() && m_c_state == PHI_C_CSBS && m_t_state != PHI_T_TACS) { + uint16_t be_word = m_fifo_out.peek(); + // Monitoring bytes being transferred on the bus + if (eoi) { + end_of_transfer = true; + } else if (!BIT(be_word , REG_OFIFO_LF_INH_BIT) && byte == 0x0a) { + // LF received -> D0/D1 = 11 + word |= REG_IFIFO_LAST_MASK; + end_of_transfer = true; + } else if (be_word != REG_OFIFO_UNCNT_MASK && ((m_be_counter + 1) & 0xff) == (be_word & 0xff)) { + // Byte count expired -> D0/D1 = 10 + word |= REG_IFIFO_CNT_EXP_MASK; + end_of_transfer = true; + } + } + + LOG(("RX word:%04x\n" , word)); + + if (m_l_state == PHI_L_LACS) { + if (m_fifo_in.full() || BIT(m_reg_int_cond , REG_INT_DEV_CLEAR_BIT)) { + // No room for received byte, stall handshake + LOG(("..stalled\n")); + return false; + } else { + m_fifo_in.enqueue(word); + LOG(("..OK\n")); + if (m_t_state != PHI_T_TACS && m_t_state != PHI_T_ID3 && + m_t_state != PHI_T_ID5 && m_t_state != PHI_T_SPAS) { + // If PHI didn't send this byte to itself, set data freeze + BIT_SET(m_reg_status, REG_STATUS_DATA_FREEZE_BIT); + } + } + } + if (end_of_transfer) { + LOG(("End of byte transfer enable\n")); + m_fifo_out.dequeue(); + m_be_counter = 0; + } else { + m_be_counter++; + } + + return true; +} + +bool phi_device::ton_msg(void) const +{ + return BIT(m_reg_address , REG_ADDR_TA_BIT); +} + +bool phi_device::lon_msg(void) const +{ + return BIT(m_reg_address , REG_ADDR_LA_BIT); +} + +bool phi_device::odd_parity(uint8_t byte) const +{ + byte = (byte >> 4) ^ byte; + byte = (byte >> 2) ^ byte; + byte = (byte >> 1) ^ byte; + return (byte & 1) != 0; +} + +uint8_t phi_device::my_address(void) const +{ + if (m_sys_controller) { + return CONTROLLER_ADDR; + } else { + return (m_reg_address >> REG_ADDR_HPIB_ADDR_BIT) & 0x1f; + } +} + +bool phi_device::tcs_msg(void) const +{ + uint8_t new_byte; + bool new_eoi; + + // When the CIC takes back control synchronously: + // * Nothing in OFIFO + // * Request to start parallel poll is pending + // * There's an interface command to be sent at head of OFIFO + return (m_c_state == PHI_C_CSBS || m_c_state == PHI_C_CSHS || m_c_state == PHI_C_CSWS) && + (m_fifo_out.empty() || + rpp_msg() || + nba_msg(new_byte , new_eoi) == NBA_CMD_FROM_OFIFO); +} + +bool phi_device::rpp_msg(void) const +{ + // TODO: + return false; +} + +bool phi_device::controller_in_charge(void) const +{ + return m_c_state != PHI_C_CIDS; +} diff --git a/src/devices/machine/phi.h b/src/devices/machine/phi.h new file mode 100644 index 00000000000..84955d290af --- /dev/null +++ b/src/devices/machine/phi.h @@ -0,0 +1,291 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************* + + phi.h + + HP PHI (Processor-to-Hpib-Interface) (1AA6-6x04) + +*********************************************************************/ + +#ifndef _PHI_H_ +#define _PHI_H_ + +// Set read and write callbacks to access DIO bus on IEEE-488 +#define MCFG_PHI_DIO_READWRITE_CB(_read , _write) \ + phi_device::set_dio_read_cb(*device , DEVCB_##_read); \ + phi_device::set_dio_write_cb(*device , DEVCB_##_write); + +// Set write callbacks to access uniline signals on IEEE-488 +#define MCFG_PHI_EOI_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_EOI , DEVCB_##_write); + +#define MCFG_PHI_DAV_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_DAV , DEVCB_##_write); + +#define MCFG_PHI_NRFD_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_NRFD , DEVCB_##_write); + +#define MCFG_PHI_NDAC_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_NDAC , DEVCB_##_write); + +#define MCFG_PHI_IFC_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_IFC , DEVCB_##_write); + +#define MCFG_PHI_SRQ_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_SRQ , DEVCB_##_write); + +#define MCFG_PHI_ATN_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_ATN , DEVCB_##_write); + +#define MCFG_PHI_REN_WRITE_CB(_write) \ + phi_device::set_488_signal_write_cb(*device , phi_device::PHI_488_REN , DEVCB_##_write); + +// Set write callback for INT signal +#define MCFG_PHI_INT_WRITE_CB(_write) \ + phi_device::set_int_write_cb(*device , DEVCB_##_write); + +// Set write callback for DMARQ signal +#define MCFG_PHI_DMARQ_WRITE_CB(_write) \ + phi_device::set_dmarq_write_cb(*device , DEVCB_##_write); + +// Depth of inbound/outbound FIFOs +#define PHI_FIFO_SIZE 8 + +class phi_device : public device_t +{ +public: + // construction/destruction + phi_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname); + phi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // See ieee488.h + typedef enum { + PHI_488_EOI, + PHI_488_DAV, + PHI_488_NRFD, + PHI_488_NDAC, + PHI_488_IFC, + PHI_488_SRQ, + PHI_488_ATN, + PHI_488_REN, + PHI_488_SIGNAL_COUNT + } phi_488_signal_t; + + template static devcb_base& set_dio_read_cb(device_t &device , _Object object) + { return downcast(device).m_dio_read_func.set_callback(object); } + + template static devcb_base& set_dio_write_cb(device_t &device , _Object object) + { return downcast(device).m_dio_write_func.set_callback(object); } + + template static devcb_base& set_488_signal_write_cb(device_t &device , phi_488_signal_t signal , _Object object) + { return downcast(device).m_signal_wr_fns[ signal ].set_callback(object); } + + template static devcb_base& set_int_write_cb(device_t &device , _Object object) + { return downcast(device).m_int_write_func.set_callback(object); } + + template static devcb_base& set_dmarq_write_cb(device_t &device , _Object object) + { return downcast(device).m_dmarq_write_func.set_callback(object); } + + DECLARE_WRITE_LINE_MEMBER(eoi_w); + DECLARE_WRITE_LINE_MEMBER(dav_w); + DECLARE_WRITE_LINE_MEMBER(nrfd_w); + DECLARE_WRITE_LINE_MEMBER(ndac_w); + DECLARE_WRITE_LINE_MEMBER(ifc_w); + DECLARE_WRITE_LINE_MEMBER(srq_w); + DECLARE_WRITE_LINE_MEMBER(atn_w); + DECLARE_WRITE_LINE_MEMBER(ren_w); + + void set_ext_signal(phi_488_signal_t signal , int state); + + // Register read/write + // Mapping of PHI register bits: + // Reg. bit PHI bit + // ================= + // 15 0 + // 14 1 + // 13 =0= + // 12 =0= + // 11 =0= + // 10 =0= + // 9 =0= + // 8 =0= + // 7 8 + // 6 9 + // 5 10 + // 4 11 + // 3 12 + // 2 13 + // 1 14 + // 0 15 + DECLARE_WRITE16_MEMBER(reg16_w); + DECLARE_READ16_MEMBER(reg16_r); + DECLARE_WRITE8_MEMBER(reg8_w); + DECLARE_READ8_MEMBER(reg8_r); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + +private: + devcb_read8 m_dio_read_func; + devcb_write8 m_dio_write_func; + devcb_write_line m_signal_wr_fns[ PHI_488_SIGNAL_COUNT ]; + devcb_write_line m_int_write_func; + devcb_write_line m_dmarq_write_func; + + bool m_int_line; + bool m_dmarq_line; + + // Internal copy of bus signals + // These signals have the "right" polarity (i.e. the opposite of bus signals, 1=L) + uint8_t m_dio; + bool m_signals[ PHI_488_SIGNAL_COUNT ]; + bool m_ext_signals[ PHI_488_SIGNAL_COUNT ]; + + bool m_no_recursion; + + bool m_sys_controller; + bool m_loopback; + bool m_id_enabled; + + // SH (Source Handshake) states + enum { + PHI_SH_SIDS, // & SIWS + PHI_SH_SGNS, // & SWNS + PHI_SH_SDYS, + PHI_SH_STRS + }; + + int m_sh_state; + + // AH (Acceptor Handshake) states + enum { + PHI_AH_AIDS, + PHI_AH_ANRS, + PHI_AH_ACRS, + PHI_AH_ACDS, + PHI_AH_ACDS_FROZEN, // Non-standard state: IF CMD rejected because of even parity + PHI_AH_AWNS + }; + + int m_ah_state; + + // T (Talker) states + enum { + PHI_T_TIDS, + PHI_T_TADS, + PHI_T_SPAS, + PHI_T_TACS, + // The following are non-standard states for IDENTIFY sequencing + PHI_T_ID1, // Untalked + PHI_T_ID2, // Addressed by secondary address + PHI_T_ID3, // Sending 1st byte + PHI_T_ID4, // Waiting to send 2nd byte + PHI_T_ID5, // Sending 2nd byte + PHI_T_ID6 // 2nd byte sent, end of sequence + }; + + int m_t_state; + bool m_t_spms; // False: SPIS, true: SPMS + + // L (Listener) states + enum { + PHI_L_LIDS, + PHI_L_LADS, + PHI_L_LACS + }; + + int m_l_state; + + // SR (Service Request) states + enum { + PHI_SR_NPRS, + PHI_SR_SRQS, + PHI_SR_APRS + }; + + int m_sr_state; + + // PP (Parallel poll) states + enum { + PHI_PP_PPIS, + PHI_PP_PPSS, + PHI_PP_PPAS + }; + + int m_pp_state; + bool m_pp_pacs; + uint8_t m_ppr_msg; + bool m_s_sense; + + // C (Controller) states + enum { + PHI_C_CIDS, + PHI_C_CADS, + PHI_C_CACS, + PHI_C_CPWS, + PHI_C_CPPS, + PHI_C_CSBS, + PHI_C_CSHS, + PHI_C_CAWS, + PHI_C_CTRS, + PHI_C_CSWS + }; + + int m_c_state; + uint8_t m_be_counter; + uint16_t m_reg_status; + uint16_t m_reg_int_cond; + uint16_t m_reg_int_mask; + uint16_t m_reg_1st_id; + uint16_t m_reg_2nd_id; + uint16_t m_reg_control; + uint16_t m_reg_address; + util::fifo m_fifo_in; + util::fifo m_fifo_out; + + typedef enum { + NBA_NONE, + NBA_CMD_FROM_OFIFO, + NBA_BYTE_FROM_OFIFO, + NBA_FROM_SPAS, + NBA_FROM_ID3, + NBA_FROM_ID5 + } nba_origin_t; + + int m_nba_origin; + + // Timers + emu_timer *m_sh_dly_timer; + emu_timer *m_c_dly_timer; + + void int_reg_w(offs_t offset , uint16_t data); + + uint8_t get_dio(void); + void set_dio(uint8_t data); + bool get_signal(phi_488_signal_t signal); + void set_signal(phi_488_signal_t signal , bool state); + + void pon_msg(void); + void update_488(void); + void update_fsm(void); + nba_origin_t nba_msg(uint8_t& new_byte , bool& new_eoi) const; + void clear_nba(nba_origin_t origin); + bool if_cmd_received(uint8_t byte); + bool byte_received(uint8_t byte , bool eoi); + bool ton_msg(void) const; + bool lon_msg(void) const; + bool odd_parity(uint8_t byte) const; + uint8_t my_address(void) const; + bool tcs_msg(void) const; + bool rpp_msg(void) const; + bool controller_in_charge(void) const; +}; + +// device type definition +extern const device_type PHI; + +#endif /* _PHI_H_ */