// license:BSD-3-Clause // copyright-holders:AJR /********************************************************************** Intel 82355 Bus Master Interface Controller (BMIC) Currently very little of this device is emulated besides storing data that the local processor writes to it. Its interface will likely evolve greatly in tandem with EISA bus emulation. **********************************************************************/ #include "emu.h" #include "i82355.h" //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(I82355, i82355_device, "i82355", "Intel 82355 BMIC") //************************************************************************** // DEVICE INITIALIZATION //************************************************************************** //------------------------------------------------- // i82355_device - constructor //------------------------------------------------- i82355_device::i82355_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, I82355, tag, owner, clock) , m_eint_callback(*this) , m_lint_callback(*this) , m_local_index(0x00) , m_local_status(0x00) , m_id{{0}} , m_global_config(0x00) // TBD: revision code in upper 4 bits , m_system_interrupt(0x00) , m_semaphore_flag{false, false} , m_semaphore_etest{false, false} , m_semaphore_ltest{false, false} , m_local_doorbell_status(0x00) , m_local_doorbell_enable(0x00) , m_eisa_doorbell_status(0x00) , m_eisa_doorbell_enable(0x00) , m_peek_poke_data{{0}} , m_peek_poke_address{{0}} , m_peek_poke_status(0x00) , m_io_decode_base{0x00, 0x00} , m_io_decode_control{0x00, 0x00} , m_transfer_config{0x00, 0x00} , m_transfer_status{0x00, 0x00} , m_base_count{ {{0}}, {{0}} } , m_base_address{ {{0}}, {{0}} } , m_current_count{ {{0}}, {{0}} } , m_current_address{ {{0}}, {{0}} } , m_tbi_base{ {{0}}, {{0}} } , m_tbi_current{ {{0}}, {{0}} } { // TODO: 24-byte FIFO for each data transfer channel std::fill(std::begin(m_mailbox), std::end(m_mailbox), 0x00); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void i82355_device::device_start() { // Register save state save_item(NAME(m_local_index)); save_item(NAME(m_local_status)); save_item(NAME(m_id.d)); save_item(NAME(m_global_config)); save_item(NAME(m_system_interrupt)); save_item(NAME(m_semaphore_flag)); save_item(NAME(m_semaphore_etest)); save_item(NAME(m_semaphore_ltest)); save_item(NAME(m_local_doorbell_status)); save_item(NAME(m_local_doorbell_enable)); save_item(NAME(m_eisa_doorbell_enable)); save_item(NAME(m_eisa_doorbell_status)); save_item(NAME(m_mailbox)); save_item(NAME(m_peek_poke_data.d)); save_item(NAME(m_peek_poke_address.d)); save_item(NAME(m_peek_poke_control)); save_item(NAME(m_peek_poke_status)); save_item(NAME(m_io_decode_base)); save_item(NAME(m_io_decode_control)); save_item(NAME(m_transfer_config)); save_item(NAME(m_transfer_status)); for (int i = 0; i < 2; i++) { save_item(NAME(m_base_count[i].d), i); save_item(NAME(m_base_address[i].d), i); save_item(NAME(m_current_count[i].d), i); save_item(NAME(m_current_address[i].d), i); save_item(NAME(m_tbi_base[i].w), i); save_item(NAME(m_tbi_current[i].w), i); } } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void i82355_device::device_reset() { // Reset local index register m_local_index = 0x00; // Reset interrupt/status registers m_local_status = 0x00; m_local_doorbell_status = 0x00; m_local_doorbell_enable = 0x00; m_eisa_doorbell_enable = 0x00; m_eisa_doorbell_status = 0x00; m_system_interrupt = 0x00; m_eint_callback(1); m_lint_callback(1); // Reset global configuration register m_global_config &= 0xf0; // Reset semaphore flags for (int i = 0; i < 2; i++) m_semaphore_flag[i] = m_semaphore_etest[i] = m_semaphore_ltest[i] = false; // ID register byte 0 set to delay value m_id.b.h3 = 0x70 | (m_id.b.h3 & 0x0f); // Reset peek/poke address and control registers m_peek_poke_address.d = 0x00000000; m_peek_poke_control = 0x00; // Reset data transfer configuration and status registers (TODO: 24-byte FIFOs) for (int i = 0; i < 2; i++) { m_transfer_config[i] = 0x00; m_transfer_status[i] = 0x00; } // Reset I/O range decode registers (TODO: differs if local processor not present) m_io_decode_base[0] = 0xe0; m_io_decode_base[1] = 0x00; m_io_decode_control[0] = m_io_decode_control[1] = 0x20; } //************************************************************************** // INTERRUPT REGISTRATION AND CONFIGURATION //************************************************************************** //------------------------------------------------- // set_local_interrupt - set an interrupt flag // in the local register; maybe assert LINT //------------------------------------------------- void i82355_device::set_local_interrupt(u8 flag) { m_local_status |= flag; if ((m_local_status & 0x18) == 0x10) { m_local_status |= 0x08; if (BIT(m_global_config, 2)) m_lint_callback(1); else m_lint_callback(0); } } //------------------------------------------------- // clear_local_interrupt - clear an interrupt // flag in the local register; maybe clear LINT //------------------------------------------------- void i82355_device::clear_local_interrupt(u8 flag) { m_local_status &= ~flag; if ((m_local_status & 0xf8) == 0x18) { m_local_status &= 0xf7; if (BIT(m_global_config, 2)) m_lint_callback(0); else m_lint_callback(1); } } //------------------------------------------------- // set_system_interrupt - set an interrupt flag // in the system register; maybe assert EINT //------------------------------------------------- void i82355_device::set_system_interrupt(u8 flag) { if (m_system_interrupt == 0x01) { if (BIT(m_global_config, 3)) m_eint_callback(1); else m_eint_callback(0); } m_system_interrupt |= flag; } //------------------------------------------------- // clear_system_interrupt - clear an interrupt // flag in the system register; maybe clear EINT //------------------------------------------------- void i82355_device::clear_system_interrupt(u8 flag) { if ((m_system_interrupt & flag) == 0) return; m_system_interrupt &= ~flag; if (m_system_interrupt == 0x01) { if (BIT(m_global_config, 3)) m_eint_callback(0); else m_eint_callback(1); } } //------------------------------------------------- // global_config - handle writes to the global // configuration register //------------------------------------------------- void i82355_device::global_config(u8 data) { // Handle changes in interrupt line polarity if (BIT(data, 3) != BIT(m_global_config, 3) && BIT(m_system_interrupt, 0)) { if (BIT(data, 3)) m_eint_callback((m_system_interrupt & 0xfe) != 0 ? 1 : 0); else m_eint_callback((m_system_interrupt & 0xfe) != 0 ? 0 : 1); } if (BIT(data, 2) != BIT(m_global_config, 2)) { if (BIT(data, 2)) m_lint_callback(BIT(m_local_status, 3) ? 1 : 0); else m_lint_callback(BIT(m_local_status, 3) ? 0 : 1); } // Revision code remains the same m_global_config = (m_global_config & 0xf0) | (data & 0x0f); logerror("%s: Global configuration = %02X\n", machine().describe_context(), m_global_config); } //------------------------------------------------- // identify_board - report board ID //------------------------------------------------- void i82355_device::identify_board() { // Use standard format for naming .CFG and .OVL files logerror("%s: EISA board identified as %c%c%c%04X\n", machine().describe_context(), ((m_id.w.h & 0x7c00) >> 10) + 'A' - 1, ((m_id.w.h & 0x03e0) >> 5) + 'A' - 1, (m_id.w.h & 0x001f) + 'A' - 1, m_id.w.l); } //************************************************************************** // PEEK/POKE INTERFACE //************************************************************************** //------------------------------------------------- // peek_poke_control - handle writes to the // peek/poke control register //------------------------------------------------- void i82355_device::peek_poke_control(u8 data) { const u32 lane_mask = (BIT(data, 3) ? 0xff000000 : 0) | (BIT(data, 2) ? 0x00ff0000 : 0) | (BIT(data, 1) ? 0x0000ff00 : 0) | (BIT(data, 0) ? 0x000000ff : 0); switch (data & 0x60) { case 0x40: logerror("%s: Requesting %s peek (read cycle) at %08X & %08X\n", machine().describe_context(), BIT(data, 4) ? "memory" : "I/O", m_peek_poke_address.d & 0xfffffffc, lane_mask); break; case 0x20: logerror("%s: Requesting %s poke (write cycle) at %08X & %08X\n", machine().describe_context(), BIT(data, 4) ? "memory" : "I/O", m_peek_poke_address.d & 0xfffffffc, lane_mask); break; case 0x60: logerror("%s: Requesting %s peek/poke (locked exchange) at %08X & %08X\n", machine().describe_context(), BIT(data, 4) ? "memory" : "I/O", m_peek_poke_address.d & 0xfffffffc, lane_mask); break; } m_peek_poke_control = data; } //************************************************************************** // DATA TRANSFER CHANNELS //************************************************************************** //------------------------------------------------- // transfer_config - handle writes to each data // transfer channel's configuration register //------------------------------------------------- void i82355_device::transfer_config(int channel, u8 data) { logerror("%s: Data channel %d transfer configuration register = %02X\n", machine().describe_context(), channel, data); m_transfer_config[channel] = data; } //------------------------------------------------- // transfer_strobe - programmed strobe for either // data transfer channel //------------------------------------------------- void i82355_device::transfer_strobe(int channel) { logerror("%s: Data channel %d transfer strobe\n", machine().describe_context(), channel); } //************************************************************************** // LOCAL REGISTER ACCESS //************************************************************************** //------------------------------------------------- // local_register_read - read data from a given // register from the local interface //------------------------------------------------- u8 i82355_device::local_register_read(u8 reg) { switch (reg) { case 0x00: // ID register byte 0 return m_id.b.h3; case 0x01: // ID register byte 1 return m_id.b.h2; case 0x02: // ID register byte 2 return m_id.b.h; case 0x03: // ID register byte 3 return m_id.b.l; case 0x08: // Global configuration register return m_global_config; case 0x09: // System interrupt enable register return m_system_interrupt; case 0x0a: case 0x0b: // Semaphore ports return m_semaphore_flag[BIT(reg, 0)] | (m_semaphore_ltest[BIT(reg, 0)] << 1); case 0x0c: // Local doorbell enable register return m_local_doorbell_enable; case 0x0d: // Local doorbell status register return m_local_doorbell_status; case 0x0e: // EISA doorbell enable register return m_eisa_doorbell_enable; case 0x0f: // EISA doorbell status register return m_eisa_doorbell_status; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: // Mailbox registers return m_mailbox[reg & 0x0f]; case 0x30: // Peek data register byte 0 return m_peek_poke_data.b.l; case 0x31: // Peek data register byte 1 return m_peek_poke_data.b.h; case 0x32: // Peek data register byte 2 return m_peek_poke_data.b.h2; case 0x33: // Peek data register byte 3 return m_peek_poke_data.b.h3; case 0x34: // Peek/poke address register byte 0 return m_peek_poke_address.b.l; case 0x35: // Peek/poke address register byte 1 return m_peek_poke_address.b.h; case 0x36: // Peek/poke address register byte 2 return m_peek_poke_address.b.h2; case 0x37: // Peek/poke address register byte 3 return m_peek_poke_address.b.h3; case 0x38: // Peek/poke control register return m_peek_poke_control; case 0x39: // Range 0 I/O decode base address return m_io_decode_base[0]; case 0x3a: // Range 0 I/O decode control address return m_io_decode_control[0]; case 0x3b: // Range 1 I/O decode base address return m_io_decode_base[1]; case 0x3c: // Range 1 I/O decode control address return m_io_decode_control[1]; case 0x40: case 0x60: // Data transfer base count register byte 0 return m_base_count[BIT(reg, 5)].b.l; case 0x41: case 0x61: // Data transfer base count register byte 1 return m_base_count[BIT(reg, 5)].b.h; case 0x42: case 0x62: // Data transfer base count register byte 2 return m_base_count[BIT(reg, 5)].b.h2; case 0x43: case 0x63: // Data transfer base address register byte 0 return m_base_address[BIT(reg, 5)].b.l; case 0x44: case 0x64: // Data transfer base address register byte 1 return m_base_address[BIT(reg, 5)].b.h; case 0x45: case 0x65: // Data transfer base address register byte 2 return m_base_address[BIT(reg, 5)].b.h2; case 0x46: case 0x66: // Data transfer base address register byte 3 return m_base_address[BIT(reg, 5)].b.h3; case 0x48: case 0x68: // Data transfer channel configuration register return m_transfer_config[BIT(reg, 5)]; case 0x4a: case 0x6a: // Data transfer channel status register return m_transfer_status[BIT(reg, 5)]; case 0x4b: case 0x6b: // TBI base register byte 0 return m_tbi_base[BIT(reg, 5)].b.l; case 0x4c: case 0x6c: // TBI base register byte 1 return m_tbi_base[BIT(reg, 5)].b.h; case 0x50: case 0x70: // Data transfer current count register byte 0 return m_current_count[BIT(reg, 5)].b.l; case 0x51: case 0x71: // Data transfer current count register byte 1 return m_current_count[BIT(reg, 5)].b.h; case 0x52: case 0x72: // Data transfer current count register byte 2 return m_current_count[BIT(reg, 5)].b.h2; case 0x53: case 0x73: // Data transfer current address register byte 0 return m_current_address[BIT(reg, 5)].b.l; case 0x54: case 0x74: // Data transfer current address register byte 1 return m_current_address[BIT(reg, 5)].b.h; case 0x55: case 0x75: // Data transfer current address register byte 2 return m_current_address[BIT(reg, 5)].b.h2; case 0x56: case 0x76: // Data transfer current address register byte 3 return m_current_address[BIT(reg, 5)].b.h3; case 0x5b: case 0x7b: // TBI current register byte 0 return m_tbi_current[BIT(reg, 5)].b.l; case 0x5c: case 0x7c: // TBI current register byte 1 return m_tbi_current[BIT(reg, 5)].b.h; default: if (!machine().side_effects_disabled()) logerror("%s: Local read from undefined register %02X\n", machine().describe_context(), reg); return 0; } } //------------------------------------------------- // local_register_write - write data to a given // register from the local interface //------------------------------------------------- void i82355_device::local_register_write(u8 reg, u8 data) { switch (reg) { case 0x00: // ID register byte 0 (manufacturer's code, first and second portions // According to Intel, this should be written after the other 3 ID bytes, but manufacturers don't agree if ((std::exchange(m_id.b.h3, data) & 0xf0) == 0x70 && m_id.w.l != 0x0000) identify_board(); break; case 0x01: // ID register byte 1 (manufacturer's code, second and third portions) m_id.b.h2 = data; break; case 0x02: // ID register byte 2 (product number) m_id.b.h = data; break; case 0x03: // ID register byte 3 (product revision) m_id.b.l = data; if ((m_id.b.h3 & 0xf0) != 0x70) identify_board(); break; case 0x08: // Global configuration register global_config(data); break; case 0x0a: case 0x0b: // Semaphore ports m_semaphore_ltest[BIT(reg, 0)] = std::exchange(m_semaphore_flag[BIT(reg, 0)], BIT(data, 0)); break; case 0x0c: // Local doorbell enable register m_local_doorbell_enable = data; if ((m_local_doorbell_status & m_local_doorbell_enable) != 0x00) set_local_interrupt(0x80); else clear_local_interrupt(0x80); break; case 0x0d: // Local doorbell interrupt register (reset bits) m_local_doorbell_status &= ~data; if ((m_local_doorbell_status & m_local_doorbell_enable) == 0x00) clear_local_interrupt(0x80); break; case 0x0f: // EISA doorbell interrupt register (set bits) m_eisa_doorbell_status |= data; if ((m_eisa_doorbell_status & m_eisa_doorbell_enable) != 0x00) set_system_interrupt(0x02); break; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: // Mailbox registers m_mailbox[reg & 0x0f] = data; break; case 0x30: // Poke data register byte 0 m_peek_poke_data.b.l = data; break; case 0x31: // Poke data register byte 1 m_peek_poke_data.b.h = data; break; case 0x32: // Poke data register byte 2 m_peek_poke_data.b.h2 = data; break; case 0x33: // Poke data register byte 3 m_peek_poke_data.b.h3 = data; break; case 0x34: // Peek/poke address register byte 0 (lowest 2 bits unused) m_peek_poke_address.b.l = data; break; case 0x35: // Peek/poke address register byte 1 m_peek_poke_address.b.h = data; break; case 0x36: // Peek/poke address register byte 2 m_peek_poke_address.b.h2 = data; break; case 0x37: // Peek/poke address register byte 3 m_peek_poke_address.b.h3 = data; break; case 0x38: // Peek/poke control register peek_poke_control(data); break; case 0x39: // Range 0 I/O decode base address m_io_decode_base[0] = data; break; case 0x3a: // Range 0 I/O decode control address m_io_decode_control[0] = data; break; case 0x3b: // Range 1 I/O decode base address m_io_decode_base[1] = data; break; case 0x3c: // Range 1 I/O decode control address m_io_decode_control[1] = data; break; case 0x40: case 0x60: // Data transfer base count register byte 0 m_base_count[BIT(reg, 5)].b.l = data; break; case 0x41: case 0x61: // Data transfer base count register byte 1 m_base_count[BIT(reg, 5)].b.h = data; break; case 0x42: case 0x62: // Data transfer base count register byte 2 m_base_count[BIT(reg, 5)].b.h2 = data; break; case 0x43: case 0x63: // Data transfer base address register byte 0 m_base_address[BIT(reg, 5)].b.l = data; break; case 0x44: case 0x64: // Data transfer base address register byte 1 m_base_address[BIT(reg, 5)].b.h = data; break; case 0x45: case 0x65: // Data transfer base address register byte 2 m_base_address[BIT(reg, 5)].b.h2 = data; break; case 0x46: case 0x66: // Data transfer base address register byte 3 m_base_address[BIT(reg, 5)].b.h3 = data; break; case 0x48: case 0x68: // Data transfer channel configuration register transfer_config(BIT(reg, 5), data); break; case 0x49: case 0x69: // Data transfer strobe registers (data written is ignored) transfer_strobe(BIT(reg, 5)); break; case 0x4a: case 0x6a: // Data transfer channel status register (reset bits) m_transfer_status[BIT(reg, 5)] &= ~(data & 0x03); break; default: logerror("%s: Local write to undefined or read-only register %02X (%02X)\n", machine().describe_context(), reg, data); break; } } //------------------------------------------------- // local_r - read data onto the local CPU bus //------------------------------------------------- u8 i82355_device::local_r(offs_t offset) { switch (offset & 3) { case 0: { u8 result = local_register_read(m_local_index & 0x7f); // Autoincrement mode if (BIT(m_local_index, 7) && !machine().side_effects_disabled()) m_local_index = (m_local_index + 1) | 0x80; return result; } case 1: return m_local_index; case 2: return m_local_status; default: if (!machine().side_effects_disabled()) logerror("%s: Local read from reserved register\n", machine().describe_context()); return 0; } } //------------------------------------------------- // local_w - write data from the local CPU bus //------------------------------------------------- void i82355_device::local_w(offs_t offset, u8 data) { switch (offset & 3) { case 0: local_register_write(m_local_index & 0x7f, data); // Autoincrement mode if (BIT(m_local_index, 7) && !machine().side_effects_disabled()) m_local_index = (m_local_index + 1) | 0x80; break; case 1: m_local_index = data; break; case 2: // Local control register (all but one bit is read-only) if (BIT(data, 4)) m_local_status |= 0x10; else { m_local_status &= 0xef; clear_local_interrupt(0xe0); } break; default: logerror("%s: Local write to reserved register (%02X)\n", machine().describe_context(), data); break; } }