diff options
author | 2021-11-14 13:05:28 +0000 | |
---|---|---|
committer | 2021-11-14 19:08:39 +0000 | |
commit | 8acb4d846de5738ff26da7f4a535be7b4bd03e62 (patch) | |
tree | 23b8521e10b85c605d10ab3603e420e8d7771273 /src/devices/machine/upc82c711.cpp | |
parent | 7481bdef865a14b78b27d4b8111e33efe0c3b167 (diff) |
New devices for Archimedes machines:
- Acorn Battery Management Unit
- Acorn LC ASIC (preliminary)
- Universal Peripheral Controller 82C710/82C711
Diffstat (limited to 'src/devices/machine/upc82c711.cpp')
-rw-r--r-- | src/devices/machine/upc82c711.cpp | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/src/devices/machine/upc82c711.cpp b/src/devices/machine/upc82c711.cpp new file mode 100644 index 00000000000..34d58cd6001 --- /dev/null +++ b/src/devices/machine/upc82c711.cpp @@ -0,0 +1,478 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/********************************************************************** + + Universal Peripheral Controller 82C711 emulation + +**********************************************************************/ + +#include "emu.h" +#include "machine/upc82c711.h" + +#define LOG_CFG (1 << 0) +#define LOG_FDC (1 << 1) +#define LOG_IDE (1 << 2) +#define LOG_LPT (1 << 3) +#define LOG_SER (1 << 4) + +#define VERBOSE (0) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(UPC82C711, upc82c711_device, "upc82c711", "Universal Peripheral Controller 82C711") + + +upc82c711_device::upc82c711_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, UPC82C711, tag, owner, clock) + , m_ide(*this, "ide") + , m_fdc(*this, "fdc") + , m_lpt(*this, "parallel") + , m_serial(*this, "serial%u", 1U) + , m_fintr_callback(*this) + , m_fdrq_callback(*this) + , m_pintr_callback(*this) + , m_irq3_callback(*this) + , m_irq4_callback(*this) + , m_txd1_callback(*this) + , m_dtr1_callback(*this) + , m_rts1_callback(*this) + , m_txd2_callback(*this) + , m_dtr2_callback(*this) + , m_rts2_callback(*this) + , m_cfg_mode(0) +{ +} + + +void upc82c711_device::device_add_mconfig(machine_config &config) +{ + // ide interface + ATA_INTERFACE(config, m_ide); + + // floppy disc controller + UPD765A(config, m_fdc, clock() / 3, false, false); // uPD72065B + m_fdc->intrq_wr_callback().set(FUNC(upc82c711_device::fdc_irq_w)); + m_fdc->drq_wr_callback().set(FUNC(upc82c711_device::fdc_drq_w)); + + // parallel port + PC_LPT(config, m_lpt); + m_lpt->irq_handler().set([this](int state) { m_pintr_callback(state); }); + + // serial ports + NS16450(config, m_serial[0], clock() / 13); + m_serial[0]->out_int_callback().set([this](int state) { m_irq4_callback(state); }); + m_serial[0]->out_tx_callback().set([this](int state) { m_txd1_callback(state); }); + m_serial[0]->out_dtr_callback().set([this](int state) { m_dtr1_callback(state); }); + m_serial[0]->out_rts_callback().set([this](int state) { m_rts1_callback(state); }); + + NS16450(config, m_serial[1], clock() / 13); + m_serial[1]->out_int_callback().set([this](int state) { m_irq3_callback(state); }); + m_serial[1]->out_tx_callback().set([this](int state) { m_txd2_callback(state); }); + m_serial[1]->out_dtr_callback().set([this](int state) { m_dtr2_callback(state); }); + m_serial[1]->out_rts_callback().set([this](int state) { m_rts2_callback(state); }); +} + + +void upc82c711_device::device_start() +{ + for (int i=0; i<4; i++) + { + char name[2] = {static_cast<char>('0'+i), 0}; + floppy_connector *conn = m_fdc->subdevice<floppy_connector>(name); + floppy[i] = conn ? conn->get_device() : nullptr; + } + + irq = drq = false; + fdc_irq = fdc_drq = false; + + m_fintr_callback.resolve_safe(); + m_fdrq_callback.resolve_safe(); + m_pintr_callback.resolve_safe(); + m_irq3_callback.resolve_safe(); + m_irq4_callback.resolve_safe(); + m_txd1_callback.resolve_safe(); + m_dtr1_callback.resolve_safe(); + m_rts1_callback.resolve_safe(); + m_txd2_callback.resolve_safe(); + m_dtr2_callback.resolve_safe(); + m_rts2_callback.resolve_safe(); + + // default addresses + com_address[0] = 0x3f8; + com_address[1] = 0x2f8; + com_address[2] = 0x338; + com_address[3] = 0x238; + + device_address[LogicalDevice::IDE] = 0x1f0; + device_address[LogicalDevice::FDC] = 0x3f0; + device_address[LogicalDevice::LPT] = 0x278; + device_address[LogicalDevice::SER1] = com_address[0]; + device_address[LogicalDevice::SER2] = com_address[1]; + + const u8 cfg_regs_defaults[] = { 0x3f, 0x9f, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // Set the value first and then use write_config because some flags + // rely on other flags being initialized properly first + std::copy(std::begin(cfg_regs_defaults), std::end(cfg_regs_defaults), std::begin(m_cfg_regs)); + for (int i = 0; i < std::size(cfg_regs_defaults); i++) + { + write_cfg(i, cfg_regs_defaults[i]); + } + + save_item(NAME(m_cfg_regs)); + save_item(NAME(m_cfg_indx)); + save_item(NAME(m_cfg_mode)); +} + +void upc82c711_device::device_reset() +{ + dor_w(0x00); +} + + +u16 upc82c711_device::io_r(offs_t offset, u16 mem_mask) +{ + u16 data = 0xffff; + + // configuration + if (offset == 0x3f1 && m_cfg_mode == 2) + { + data = m_cfg_regs[m_cfg_indx]; + LOGMASKED(LOG_CFG, "CFG read %04x -> %02x\n", offset, data); + } + + // ide + if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0) + { + data = m_ide->cs0_r(offset & 7); + LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data); + } + if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6) + { + data = m_ide->cs1_r(offset & 7); + LOGMASKED(LOG_IDE, "IDE read %04x -> %04x\n", offset, data); + } + + // fdc + if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6) + { + switch (offset & 7) + { + case 3: + data = 0x20; + break; + case 4: + data = m_fdc->msr_r(); + break; + case 5: + data = m_fdc->fifo_r(); + break; + case 7: + data = m_fdc->dir_r() | (m_ide->cs1_r(7) & 0x7f); + break; + } + LOGMASKED(LOG_FDC, "FDC read %04x -> %02x\n", offset, data); + } + + // parallel + if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT]) + { + data = m_lpt->read(offset & 3); + LOGMASKED(LOG_LPT, "LPT read %04x -> %02x\n", offset, data); + } + + // serial 1 + if (device_enabled[LogicalDevice::SER1] && (offset & ~7) == device_address[LogicalDevice::SER1]) + { + data = m_serial[0]->ins8250_r(offset & 7); + LOGMASKED(LOG_SER, "SER1 read %04x -> %02x\n", offset, data); + } + + // serial 2 + if (device_enabled[LogicalDevice::SER2] && (offset & ~7) == device_address[LogicalDevice::SER2]) + { + data = m_serial[1]->ins8250_r(offset & 7); + LOGMASKED(LOG_SER, "SER2 read %04x -> %02x\n", offset, data); + } + + return data; +} + +void upc82c711_device::io_w(offs_t offset, u16 data, u16 mem_mask) +{ + // configuration + if (offset == 0x3f0) + { + LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data); + if (m_cfg_mode == 2) + { + if (data == 0xaa) // escape configuration + { + m_cfg_mode = 0; + LOGMASKED(LOG_CFG, "CFG -- mode off --\n"); + } + else + { + m_cfg_indx = data & 0x0f; + } + } + else + { + if (data == 0x55) // enter configuration + { + m_cfg_mode++; + if (m_cfg_mode == 2) + LOGMASKED(LOG_CFG, "CFG -- mode on --\n"); + } + else + { + m_cfg_mode = 0; + } + } + } + else if (offset == 0x3f1) + { + LOGMASKED(LOG_CFG, "CFG write %04x <- %02x\n", offset, data); + if (m_cfg_mode == 2) + { + write_cfg(m_cfg_indx, data); + } + } + + // ide + if (device_enabled[LogicalDevice::IDE] && (offset & ~7) == 0x1f0) + { + m_ide->cs0_w(offset & 7, data); + LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data); + } + if (device_enabled[LogicalDevice::IDE] && offset == 0x3f6) + { + m_ide->cs1_w(offset & 7, data); + LOGMASKED(LOG_IDE, "IDE write %04x <- %04x\n", offset, data); + } + + // fdc + if (device_enabled[LogicalDevice::FDC] && (offset & ~7) == 0x3f0 && offset != 0x3f0 && offset != 0x3f1 && offset != 0x3f6) + { + switch (offset & 7) + { + case 2: + dor_w(data); + break; + case 5: + m_fdc->fifo_w(data); + break; + case 7: + m_fdc->ccr_w(data); + break; + } + LOGMASKED(LOG_FDC, "FDC write %04x <- %02x\n", offset, data); + } + + // parallel + if (device_enabled[LogicalDevice::LPT] && (offset & ~3) == device_address[LogicalDevice::LPT]) + { + m_lpt->write(offset & 3, data); + LOGMASKED(LOG_LPT, "LPT write %04x <- %02x\n", offset, data); + } + + // serial 1 + if (device_enabled[LogicalDevice::SER1] && (offset & ~7) == device_address[LogicalDevice::SER1]) + { + m_serial[0]->ins8250_w(offset & 7, data); + LOGMASKED(LOG_SER, "SER1 write %04x <- %02x\n", offset, data); + } + + // serial 2 + if (device_enabled[LogicalDevice::SER2] && (offset & ~7) == device_address[LogicalDevice::SER2]) + { + m_serial[1]->ins8250_w(offset & 7, data); + LOGMASKED(LOG_SER, "SER2 write %04x <- %02x\n", offset, data); + } +} + + +u8 upc82c711_device::dack_r() +{ + return m_fdc->dma_r(); +} + +void upc82c711_device::dack_w(u8 data) +{ + m_fdc->dma_w(data); +} + +void upc82c711_device::tc_w(bool state) +{ + m_fdc->tc_w(state); +} + + +void upc82c711_device::dor_w(uint8_t data) +{ + dor = data; + + for (int i=0; i<4; i++) + if (floppy[i]) + floppy[i]->mon_w(!(dor & (0x10 << i))); + + int fid = dor & 3; + if (dor & (0x10 << fid)) + m_fdc->set_floppy(floppy[fid]); + else + m_fdc->set_floppy(nullptr); + + check_irq(); + check_drq(); + m_fdc->reset_w(!BIT(dor, 2)); +} + +WRITE_LINE_MEMBER(upc82c711_device::fdc_irq_w) +{ + fdc_irq = state; + check_irq(); +} + +WRITE_LINE_MEMBER(upc82c711_device::fdc_drq_w) +{ + fdc_drq = state; + check_drq(); +} + +void upc82c711_device::check_irq() +{ + bool pirq = irq; + irq = fdc_irq && (dor & 4) && (dor & 8); + if (irq != pirq) + m_fintr_callback(irq); +} + +void upc82c711_device::check_drq() +{ + bool pdrq = drq; + drq = fdc_drq && (dor & 4) && (dor & 8); + if (drq != pdrq) + m_fdrq_callback(drq); +} + + +void upc82c711_device::write_cfg(int index, u8 data) +{ + m_cfg_regs[index] = data; + LOGMASKED(LOG_CFG, "CR[%02x] = %02x\n", index, data); + + switch (index) + { + case 0x00: + device_enabled[LogicalDevice::IDE] = BIT(m_cfg_regs[index], 0); + device_enabled[LogicalDevice::FDC] = BIT(m_cfg_regs[index], 3) && BIT(m_cfg_regs[index], 4); + LOGMASKED(LOG_CFG, "IDE %s\n", device_enabled[LogicalDevice::IDE] ? "enabled" : "disabled"); + LOGMASKED(LOG_CFG, "FDC %s\n", device_enabled[LogicalDevice::FDC] ? "enabled" : "disabled"); + break; + + case 0x01: + { + device_enabled[LogicalDevice::LPT] = BIT(m_cfg_regs[index], 2); + + auto lpt_port = BIT(m_cfg_regs[index], 0, 2); + switch (lpt_port) + { + case 0: // Disabled + device_enabled[LogicalDevice::LPT] = false; + break; + case 1: + device_address[LogicalDevice::LPT] = 0x3bc; + break; + case 2: + device_address[LogicalDevice::LPT] = 0x378; + break; + case 3: // Default + device_address[LogicalDevice::LPT] = 0x278; + break; + } + LOGMASKED(LOG_CFG, "LPT %04x %s\n", device_address[LogicalDevice::LPT], device_enabled[LogicalDevice::LPT] ? "enabled" : "disabled"); + auto com34 = BIT(m_cfg_regs[index], 5, 2); + switch (com34) + { + case 0: // Default + com_address[2] = 0x338; + com_address[3] = 0x238; + break; + case 1: + com_address[2] = 0x3e8; + com_address[3] = 0x2e8; + break; + case 2: + com_address[2] = 0x2e8; + com_address[3] = 0x2e0; + break; + case 3: + com_address[2] = 0x220; + com_address[3] = 0x228; + break; + } + break; + } + case 0x02: + device_enabled[LogicalDevice::SER1] = BIT(m_cfg_regs[index], 2) && BIT(m_cfg_regs[index], 3); + device_address[LogicalDevice::SER1] = com_address[BIT(m_cfg_regs[index], 0, 2)]; + + device_enabled[LogicalDevice::SER2] = BIT(m_cfg_regs[index], 6) && BIT(m_cfg_regs[index], 7); + device_address[LogicalDevice::SER2] = com_address[BIT(m_cfg_regs[index], 4, 2)]; + + LOGMASKED(LOG_CFG, "SER1 %04x %s\n", device_address[LogicalDevice::SER1], device_enabled[LogicalDevice::SER1] ? "enabled" : "disabled"); + LOGMASKED(LOG_CFG, "SER2 %04x %s\n", device_address[LogicalDevice::SER2], device_enabled[LogicalDevice::SER2] ? "enabled" : "disabled"); + break; + } +} + + +WRITE_LINE_MEMBER(upc82c711_device::rxd1_w) +{ + m_serial[0]->rx_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::dcd1_w) +{ + m_serial[0]->dcd_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::dsr1_w) +{ + m_serial[0]->dsr_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::ri1_w) +{ + m_serial[0]->ri_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::cts1_w) +{ + m_serial[0]->cts_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::rxd2_w) +{ + m_serial[1]->rx_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::dcd2_w) +{ + m_serial[1]->dcd_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::dsr2_w) +{ + m_serial[1]->dsr_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::ri2_w) +{ + m_serial[1]->ri_w(state); +} + +WRITE_LINE_MEMBER(upc82c711_device::cts2_w) +{ + m_serial[1]->cts_w(state); +} |