// license: GPL-2.0+ // copyright-holders: Dirk Best /*************************************************************************** GAYLE Gate array used in the Amiga 600 and Amiga 1200 computers for the following functions: - Address decoding and timing for ROM, RAM, CIA, RTC (and unused: Flash ROM, ArcNet) - Data buffers for chip RAM - IDE interface - Credit card (PCMCIA) interface - System reset - Floppy helper - E clock generation TODO: - Generate bus errors (REG_CHANGE, bit 0) - Disabling the PCMCIA interface should remove access to PCMCIA memory - Move more functionality here ***************************************************************************/ #include "emu.h" #include "gayle.h" #define LOG_REG (1U << 1) #define LOG_IDE (1U << 2) #define LOG_CC (1U << 3) #define LOG_ID (1U << 4) //#define VERBOSE (LOG_GENERAL | LOG_REG | LOG_CC) #include "logmacro.h" //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(GAYLE, gayle_device, "gayle", "Amiga GAYLE") //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // gayle_device - constructor //------------------------------------------------- gayle_device::gayle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, GAYLE, tag, owner, clock), m_int2_w(*this), m_int6_w(*this), m_rst_w(*this), m_ide_cs_r_cb(*this, 0xffff), m_ide_cs_w_cb(*this), m_gayle_id(0xff), m_gayle_id_count(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void gayle_device::device_start() { save_item(NAME(m_gayle_id_count)); save_item(NAME(m_gayle_reg)); save_item(NAME(m_line_state)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void gayle_device::device_reset() { // internal registers are cleared to 0 on reset m_gayle_reg[0] = 0; m_gayle_reg[1] = 0; m_gayle_reg[2] = 0; m_gayle_reg[3] = 0; // lower interrupts m_int2_w(0); m_int6_w(0); } //************************************************************************** // ADDRESS MAP //************************************************************************** void gayle_device::register_map(address_map &map) { // base address 0xdaXXXX map(0x0000, 0x001f).mirror(0x0fe0).rw(FUNC(gayle_device::ide_cs_r<1>), FUNC(gayle_device::ide_cs_w<1>)); map(0x2000, 0x201f).mirror(0x0fe0).rw(FUNC(gayle_device::ide_cs_r<0>), FUNC(gayle_device::ide_cs_w<0>)); map(0x8000, 0x8001).rw(FUNC(gayle_device::status_r), FUNC(gayle_device::status_w)); map(0x9000, 0x9001).rw(FUNC(gayle_device::change_r), FUNC(gayle_device::change_w)); map(0xa000, 0xa001).rw(FUNC(gayle_device::int_r), FUNC(gayle_device::int_w)); map(0xb000, 0xb001).rw(FUNC(gayle_device::control_r), FUNC(gayle_device::control_w)); } //************************************************************************** // INTERNAL //************************************************************************** void gayle_device::dump_register() { LOGMASKED(LOG_REG, "status = %02x, change = %02x, int = %02x, control = %02x\n", m_gayle_reg[REG_STATUS], m_gayle_reg[REG_CHANGE], m_gayle_reg[REG_INT], m_gayle_reg[REG_CONTROL]); } void gayle_device::line_change(int line, int state, int level) { LOGMASKED(line == LINE_IDE ? LOG_IDE : LOG_CC, "line_change: %d %d %d\n", line, state, level); m_line_state &= ~(1 << line); m_line_state |= (state << line); // ignore pcmcia line changes if the interface is disabled if (line != LINE_IDE && BIT(m_gayle_reg[REG_STATUS], 0)) return; // did we change state? if (BIT(m_gayle_reg[REG_STATUS], line) != state) { // indicate a change to the line m_gayle_reg[REG_CHANGE] |= 1 << line; // special handling for line 6 (credit card detect) if (line == LINE_CC_DET && BIT(m_gayle_reg[REG_CHANGE], 1)) { LOG("resetting due to credit card detection change\n"); m_rst_w(0); m_rst_w(1); reset(); return; } if (state) m_gayle_reg[REG_STATUS] |= 1 << line; else m_gayle_reg[REG_STATUS] &= ~(1 << line); // generate interrupt (if enabled) if (BIT(m_gayle_reg[REG_INT], line)) { LOGMASKED(line == LINE_IDE ? LOG_IDE : LOG_CC, "line %d int assert\n", line); if (level == 2) m_int2_w(1); else m_int6_w(1); } } } //************************************************************************** // REGISTER //************************************************************************** uint16_t gayle_device::status_r() { // 7------- ide interrupt status // -6------ credit card detect // --5----- battery voltage 1/status change // ---4---- battery voltage 2/digital audio // ----3--- credit card write enable // -----2-- credit card busy/interrupt request // ------1- enable pcmcia digital audio // -------0 disable pcmcia if (!machine().side_effects_disabled()) LOGMASKED(LOG_REG, "status_r: %02x\n", m_gayle_reg[REG_STATUS]); return (m_gayle_reg[REG_STATUS] << 8) | m_gayle_reg[REG_STATUS]; } void gayle_device::status_w(uint16_t data) { uint8_t previous = m_gayle_reg[REG_STATUS]; data >>= 8; LOGMASKED(LOG_REG, "status_w: %02x\n", data); // pcmcia interface disable? if (BIT(data, 0) == 1) m_gayle_reg[REG_STATUS] &= 0x03; // lowest two bits can be set freely m_gayle_reg[REG_STATUS] &= ~0x03; m_gayle_reg[REG_STATUS] |= data & 0x03; // status bits can only be set m_gayle_reg[REG_STATUS] |= data & 0xfc; // pcmcia interface re-enabled? if (BIT(previous, 0) == 1 && BIT(data, 0) == 0) { cc_cd_w(BIT(m_line_state, 6)); cc_bvd1_w(BIT(m_line_state, 5)); cc_bvd2_w(BIT(m_line_state, 4)); cc_wp_w(BIT(m_line_state, 3)); } } uint16_t gayle_device::change_r() { // 7------- ide interrupt changed // -6------ credit card detect changed // --5----- battery voltage 1/status change changed // ---4---- battery voltage 2/digital audio changed // ----3--- credit card write enable changed // -----2-- credit card busy/interrupt request changed // ------1- generate reset after cc detect change // -------0 generate bus error after cc detect change if (!machine().side_effects_disabled()) LOGMASKED(LOG_REG, "change_r: %02x\n", m_gayle_reg[REG_CHANGE]); return (m_gayle_reg[REG_CHANGE] << 8) | m_gayle_reg[REG_CHANGE]; } void gayle_device::change_w(uint16_t data) { data >>= 8; LOGMASKED(LOG_REG, "change_w: %02x\n", data); // clear ide interrupt? if (BIT(data, 7) == 0) { LOGMASKED(LOG_REG, "ide int cleared\n", data); m_int2_w(0); } // clear cc detect interrupt? if (BIT(data, 6) == 0) { LOGMASKED(LOG_REG, "cc detect int cleared\n", data); m_int6_w(0); } // clear bvd1/sc interrupt? if (BIT(data, 5) == 0) { LOGMASKED(LOG_REG, "bvd1/sc int cleared\n", data); if (BIT(m_gayle_reg[REG_INT], 1)) m_int6_w(0); else m_int2_w(0); } // clear bvd2/da interrupt? if (BIT(data, 4) == 0) { LOGMASKED(LOG_REG, "bvd2/da int cleared\n", data); if (BIT(m_gayle_reg[REG_INT], 1)) m_int6_w(0); else m_int2_w(0); } // clear write protect interrupt? if (BIT(data, 3) == 0) { LOGMASKED(LOG_REG, "write protect int cleared\n", data); m_int2_w(0); } // enabling reset on cc detect change can immediately generate a reset if (BIT(data, 1) && BIT(m_gayle_reg[REG_CHANGE], 6)) { LOGMASKED(LOG_REG, "resetting\n", data); m_rst_w(0); m_rst_w(1); reset(); return; } m_gayle_reg[REG_CHANGE] = (m_gayle_reg[REG_CHANGE] & data) | (data & 0x03); } uint16_t gayle_device::int_r() { // 7------- ide interrupt enable // -6------ credit card detect interrupt enable // --5----- battery voltage 1/status change interrupt enable // ---4---- battery voltage 2/digital audio interrupt enable // ----3--- write enable interrupt enable // -----2-- busy/interrupt request interrupt enable // ------1- set interrupt level for battery voltage 1/2 // -------0 set interrupt level for busy/interrupt request if (!machine().side_effects_disabled()) LOGMASKED(LOG_REG, "int_r: %02x\n", m_gayle_reg[REG_INT]); return (m_gayle_reg[REG_INT] << 8) | m_gayle_reg[REG_INT]; } void gayle_device::int_w(uint16_t data) { data >>= 8; LOGMASKED(LOG_REG, "int_w: %02x\n", data); m_gayle_reg[REG_INT] = data; } uint16_t gayle_device::control_r() { // 7654---- not implemented in hardware // ----32--- slow mem (250 ns, 150 ns, 100 ns, 720 ns) // ------1- program 12v // -------0 program 5v if (!machine().side_effects_disabled()) LOGMASKED(LOG_REG, "control_r: %02x\n", m_gayle_reg[REG_CONTROL]); return (m_gayle_reg[REG_CONTROL] << 8) | m_gayle_reg[REG_CONTROL]; } void gayle_device::control_w(uint16_t data) { data >>= 8; LOGMASKED(LOG_REG, "control_w: %02x\n", data); m_gayle_reg[REG_CONTROL] = data; } //************************************************************************** // IDE //************************************************************************** template uint16_t gayle_device::ide_cs_r(offs_t offset, uint16_t mem_mask) { return m_ide_cs_r_cb[N]((offset >> 1) & 0x07, mem_mask); } template void gayle_device::ide_cs_w(offs_t offset, uint16_t data, uint16_t mem_mask) { m_ide_cs_w_cb[N]((offset >> 1) & 0x07, data, mem_mask); } void gayle_device::ide_interrupt_w(int state) { LOGMASKED(LOG_IDE, "ide_interrupt_w: %d\n", state); line_change(7, state, 2); } //************************************************************************** // CREDIT CARD //************************************************************************** void gayle_device::cc_cd_w(int state) { LOGMASKED(LOG_CC, "cc_cd_w: %d\n", state); line_change(LINE_CC_DET, state, 6); } void gayle_device::cc_bvd1_w(int state) { LOGMASKED(LOG_CC, "cc_bvd1_w: %d\n", state); line_change(LINE_CC_BVD1_SC, state, BIT(m_gayle_reg[REG_INT], 1) ? 6 : 2); } void gayle_device::cc_bvd2_w(int state) { LOGMASKED(LOG_CC, "cc_bvd2_w: %d\n", state); line_change(LINE_CC_BVD2_DA, state, BIT(m_gayle_reg[REG_INT], 1) ? 6 : 2); } void gayle_device::cc_wp_w(int state) { LOGMASKED(LOG_CC, "cc_wp_w: %d\n", state); line_change(LINE_CC_WP, state, 2); } //************************************************************************** // ID //************************************************************************** uint16_t gayle_device::gayle_id_r(offs_t offset, uint16_t mem_mask) { uint16_t data; if (ACCESSING_BITS_8_15) data = ((m_gayle_id << m_gayle_id_count++) & 0x80) << 8; else data = 0xffff; LOGMASKED(LOG_ID, "gayle_id_r(%06x): %04x & %04x (id=%02x)\n", offset, data, mem_mask, m_gayle_id); return data; } void gayle_device::gayle_id_w(offs_t offset, uint16_t data, uint16_t mem_mask) { LOGMASKED(LOG_ID, "gayle_id_w(%06x): %04x & %04x (id=%02x)\n", offset, data, mem_mask, m_gayle_id); m_gayle_id_count = 0; }