diff options
Diffstat (limited to 'src/mame/machine/interpro_ioga.cpp')
-rw-r--r-- | src/mame/machine/interpro_ioga.cpp | 1610 |
1 files changed, 943 insertions, 667 deletions
diff --git a/src/mame/machine/interpro_ioga.cpp b/src/mame/machine/interpro_ioga.cpp index 8c5d03131f2..9a58de26336 100644 --- a/src/mame/machine/interpro_ioga.cpp +++ b/src/mame/machine/interpro_ioga.cpp @@ -2,601 +2,518 @@ // copyright-holders:Patrick Mackinlay /* - * An implementation of the IOGA device found on Intergraph InterPro family workstations. There is no - * public documentation on this device, so the implementation is being built to follow the logic of the - * system boot ROM and its diagnostic tests. + * An implementation of the IOGA (I/O Gate Array) devices found in Intergraph + * InterPro family systems. There is no public documentation for these devices, + * so the implementation is being built to follow the logic of the system boot + * ROM and diagnostic tests. * - * The device handles most of the I/O for the system, including timers, interrupts, DMA and target device - * interfacing. There remains a significant amount of work to be completed before the boot diagnostics will - * pass without errors, let alone successfully booting CLIX. - * - * Please be aware that code in here is not only broken, it's likely wrong in many cases. + * The device handles most of the I/O for the system, including timers, DMA, + * interrupts, and target device interfacing. There is a significant amount of + * work to be completed before the boot diagnostics will pass without errors, + * let alone successfully booting CLIX. * * TODO - * - too long to list + * - edge/level triggered interrupts + * - make mouse a slot device + * - improve dma and timers */ + #include "emu.h" #include "interpro_ioga.h" -// the following enables some hacks which will allow all iogadiag tests to complete successfully, but also breaks scsi dma +// enables hacks which allow iogadiag tests to complete but break scsi dma #define IOGA_DMA_DIAG_HACK 0 -#define LOG_GENERAL (1 << 31) -#define LOG_HWINT_ENABLE 0 -#define LOG_DMA_ENABLE 0 -#define LOG_TIMER_ENABLE 0 -#define LOG_DMA_SERIAL_ENABLE 0 - -#define VERBOSE 0 - -#if VERBOSE -#define LOG_INTERRUPT(...) logerror(__VA_ARGS__) -#define LOG_TIMER(timer, ...) if (LOG_TIMER_ENABLE & (1 << timer)) logerror(__VA_ARGS__) -#define LOG_HWINT(interrupt, ...) if (LOG_HWINT_ENABLE & (1 << interrupt)) logerror(__VA_ARGS__) -#define LOG_DMA(channel, ...) if (LOG_DMA_ENABLE & (1 << channel)) logerror(__VA_ARGS__) -#define LOG_DMA_SERIAL(channel, ...) if (LOG_DMA_SERIAL_ENABLE & (1 << channel)) logerror(__VA_ARGS__) -#define LOG_ETH(...) logerror(__VA_ARGS__) -#else -#define LOG_INTERRUPT(...) -#define LOG_TIMER(timer, ...) -#define LOG_HWINT(interrupt, ...) -#define LOG_DMA(channel, ...) do {} while(0) -#define LOG_DMA_SERIAL(channel, ...) -#define LOG_ETH(...) -#endif +#define LOG_GENERAL (1U << 0) +#define LOG_NMI (1U << 1) +#define LOG_INT (1U << 2) +#define LOG_DMA (1U << 3) + +#define LOG_FLOPPY (1U << 4) +#define LOG_SCSI (1U << 5) +#define LOG_NETWORK (1U << 6) +#define LOG_MOUSE (1U << 7) +#define LOG_SERIALDMA (1U << 8) + +#define LOG_TIMER0 (1U << 0x10) +#define LOG_TIMER1 (1U << 0x11) +#define LOG_TIMER2 (1U << 0x12) +#define LOG_TIMER3 (1U << 0x13) +#define LOG_TIMERRD (1U << 0x14) +#define LOG_TIMERS (LOG_TIMER0 | LOG_TIMER1 | LOG_TIMER2 | LOG_TIMER3) + +//#define VERBOSE (LOG_GENERAL | LOG_INT | LOG_NMI | LOG_DMA | LOG_SERIALDMA | LOG_NETWORK) +//#define VERBOSE_IRQ ((1 << IRQ_SCSI) | (1 << IRQ_FLOPPY) | (1 << IRQ_MOUSE) | (1 << IRQ_SERDMA) | (1 << IRQ_SERIAL) | (1 << IRQ_ETHERNET)) +//#define VERBOSE_DMA ((1 << DMA_PLOTTER) | (1 << DMA_SCSI) | (1 << DMA_FLOPPY)) + +#define VERBOSE 0 +#define VERBOSE_IRQ 0 +#define VERBOSE_DMA 0 +#include "logmacro.h" + +#define LOGIRQ(irq, ...) do { if (VERBOSE_IRQ & (1U << irq)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false) +#define LOGDMA(dma, ...) do { if (VERBOSE_DMA & (1U << dma)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false) + +const interpro_ioga_device::interrupt_data_t interpro_ioga_device::m_interrupt_data[] = +{ + { INT_HARD_IN, IRQ_TIMER2, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 3", "timer 2" }, // either external or internal? + { INT_HARD_IN, IRQ_TIMER3, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 4", "timer 3" }, // either external or internal? + { INT_HARD_EX, IRQ_SCSI, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 0", "scsi" }, // IRQ_ENABLE_INTERNAL + { INT_HARD_EX, IRQ_FLOPPY, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 1", "floppy" }, // IRQ_ENABLE_INTERNAL + { INT_HARD_EX, IRQ_PLOTTER, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 2", "plotter" }, // IRQ_ENABLE_INTERNAL + + { INT_HARD_EX, IRQ_SRXCBUS0, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 3", "srx/cbus 0" }, + { INT_HARD_EX, IRQ_SRXCBUS1, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 4", "srx/cbus 1" }, + { INT_HARD_EX, IRQ_SRXCBUS2, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 5", "srx/cbus 2" }, + + { INT_HARD_EX, IRQ_VB, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 6", "vb" }, + { INT_HARD_EX, IRQ_9, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 7", "?" }, + { INT_HARD_EX, IRQ_CBUS3, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 8", "cbus 3" }, + { INT_HARD_EX, IRQ_RTC, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 9", "clock/calendar" }, + { INT_HARD_EX, IRQ_12, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 10", "sga?" }, + + { INT_HARD_IN, IRQ_MOUSE, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 0", "mouse" }, + { INT_HARD_IN, IRQ_TIMER0, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 1", "timer 0" }, + { INT_HARD_IN, IRQ_TIMER1, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 2", "timer 1" }, + { INT_HARD_IN, IRQ_SERDMA, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "internal int 5", "serial dma" }, // IRQ_ENABLE_EXTERNAL << 0 | IRQ_ENABLE_EXTERNAL << 1 | IRQ_ENABLE_EXTERNAL << 2 + + { INT_HARD_EX, IRQ_SERIAL, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 11", "serial" }, + { INT_HARD_EX, IRQ_ETHERNET, IRQ_PENDING | IRQ_ENABLE_EXTERNAL, "external int 12", "ethernet" }, // IRQ_ENABLE_INTERNAL + + { INT_SOFT_LO, IRQ_SOFT0, 0x0100, "soft int 0", "soft" }, + { INT_SOFT_LO, IRQ_SOFT1, 0x0200, "soft int 1", "soft" }, + { INT_SOFT_LO, IRQ_SOFT2, 0x0400, "soft int 2", "soft" }, + { INT_SOFT_LO, IRQ_SOFT3, 0x0800, "soft int 3", "soft" }, + { INT_SOFT_LO, IRQ_SOFT4, 0x1000, "soft int 4", "soft" }, + { INT_SOFT_LO, IRQ_SOFT5, 0x2000, "soft int 5", "soft" }, + { INT_SOFT_LO, IRQ_SOFT6, 0x4000, "soft int 6", "soft" }, + { INT_SOFT_LO, IRQ_SOFT7, 0x8000, "soft int 7", "soft" }, + + { INT_SOFT_HI, IRQ_SOFT8, IRQ_PENDING, "soft int 8", "soft" }, + { INT_SOFT_HI, IRQ_SOFT9, IRQ_PENDING, "soft int 9", "soft" }, + { INT_SOFT_HI, IRQ_SOFT10, IRQ_PENDING, "soft int 10", "soft" }, + { INT_SOFT_HI, IRQ_SOFT11, IRQ_PENDING, "soft int 11", "soft" }, + { INT_SOFT_HI, IRQ_SOFT12, IRQ_PENDING, "soft int 12", "soft" }, + { INT_SOFT_HI, IRQ_SOFT13, IRQ_PENDING, "soft int 13", "soft" }, + { INT_SOFT_HI, IRQ_SOFT14, IRQ_PENDING, "soft int 14", "soft" }, + { INT_SOFT_HI, IRQ_SOFT15, IRQ_PENDING, "soft int 15", "soft" }, +}; DEVICE_ADDRESS_MAP_START(map, 32, interpro_ioga_device) - AM_RANGE(0x00, 0x03) AM_READWRITE(eth_remap_r, eth_remap_w) - AM_RANGE(0x04, 0x07) AM_READWRITE(eth_map_page_r, eth_map_page_w) - AM_RANGE(0x08, 0x0b) AM_READWRITE(eth_control_r, eth_control_w) AM_RANGE(0x0c, 0x1b) AM_READWRITE(dma_plotter_r, dma_plotter_w) AM_RANGE(0x1c, 0x1f) AM_READWRITE(dma_plotter_eosl_r, dma_plotter_eosl_w) AM_RANGE(0x20, 0x2f) AM_READWRITE(dma_scsi_r, dma_scsi_w) AM_RANGE(0x30, 0x3f) AM_READWRITE(dma_floppy_r, dma_floppy_w) - - AM_RANGE(0x40, 0x43) AM_READWRITE(dma_serial0_addr_r, dma_serial0_addr_w) - AM_RANGE(0x44, 0x47) AM_READWRITE16(dma_serial0_count_r, dma_serial0_count_w, 0x0000ffff) - AM_RANGE(0x44, 0x47) AM_READWRITE16(dma_serial0_ctrl_r, dma_serial0_ctrl_w, 0xffff0000) - - AM_RANGE(0x48, 0x4b) AM_READWRITE(dma_serial1_addr_r, dma_serial1_addr_w) - AM_RANGE(0x4c, 0x4f) AM_READWRITE16(dma_serial1_count_r, dma_serial1_count_w, 0x0000ffff) - AM_RANGE(0x4c, 0x4f) AM_READWRITE16(dma_serial1_ctrl_r, dma_serial1_ctrl_w, 0xffff0000) - - AM_RANGE(0x50, 0x53) AM_READWRITE(dma_serial2_addr_r, dma_serial2_addr_w) - AM_RANGE(0x54, 0x57) AM_READWRITE16(dma_serial2_count_r, dma_serial2_count_w, 0x0000ffff) - AM_RANGE(0x54, 0x57) AM_READWRITE16(dma_serial2_ctrl_r, dma_serial2_ctrl_w, 0xffff0000) - + AM_RANGE(0x40, 0x43) AM_READWRITE(serial_dma0_addr_r, serial_dma0_addr_w) + AM_RANGE(0x44, 0x47) AM_READWRITE(serial_dma0_ctrl_r, serial_dma0_ctrl_w) + AM_RANGE(0x48, 0x4b) AM_READWRITE(serial_dma1_addr_r, serial_dma1_addr_w) + AM_RANGE(0x4c, 0x4f) AM_READWRITE(serial_dma1_ctrl_r, serial_dma1_ctrl_w) + AM_RANGE(0x50, 0x53) AM_READWRITE(serial_dma2_addr_r, serial_dma2_addr_w) + AM_RANGE(0x54, 0x57) AM_READWRITE(serial_dma2_ctrl_r, serial_dma2_ctrl_w) + + // 5a - sib control? AM_RANGE(0x5c, 0x7f) AM_READWRITE16(icr_r, icr_w, 0xffffffff) AM_RANGE(0x80, 0x83) AM_READWRITE16(icr18_r, icr18_w, 0x0000ffff) AM_RANGE(0x80, 0x83) AM_READWRITE8(softint_r, softint_w, 0x00ff0000) AM_RANGE(0x80, 0x83) AM_READWRITE8(nmictrl_r, nmictrl_w, 0xff000000) - - AM_RANGE(0x88, 0x8b) AM_READWRITE(timer_prescaler_r, timer_prescaler_w) + AM_RANGE(0x84, 0x87) AM_READWRITE(mouse_status_r, mouse_status_w) + AM_RANGE(0x88, 0x8b) AM_READWRITE(prescaler_r, prescaler_w) AM_RANGE(0x8c, 0x8f) AM_READWRITE(timer0_r, timer0_w) AM_RANGE(0x90, 0x93) AM_READWRITE(timer1_r, timer1_w) AM_RANGE(0x94, 0x97) AM_READ(error_address_r) AM_RANGE(0x98, 0x9b) AM_READ(error_businfo_r) AM_RANGE(0x9c, 0x9f) AM_READWRITE16(arbctl_r, arbctl_w, 0x0000ffff) - + AM_RANGE(0xa0, 0xa3) AM_READWRITE(timer2_count_r, timer2_count_w) + AM_RANGE(0xa4, 0xa7) AM_READWRITE(timer2_value_r, timer2_value_w) AM_RANGE(0xa8, 0xab) AM_READWRITE(timer3_r, timer3_w) - AM_RANGE(0xac, 0xaf) AM_READWRITE(bus_timeout_r, bus_timeout_w) // boot code writes 0x64 - + AM_RANGE(0xac, 0xaf) AM_READWRITE(bus_timeout_r, bus_timeout_w) // boot writes 0x64 AM_RANGE(0xb0, 0xbf) AM_READWRITE16(softint_vector_r, softint_vector_w, 0xffffffff) + + //c0, c4, c8 -ethernet address a,b,c? ADDRESS_MAP_END -DEFINE_DEVICE_TYPE(INTERPRO_IOGA, interpro_ioga_device, "ioga", "InterPro IOGA") - -interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, INTERPRO_IOGA, tag, owner, clock), - m_out_nmi_func(*this), - m_out_irq_func(*this), - m_memory_space(nullptr), - m_dma_channel{ - { 0,0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_PLOT, IOGA_DMA_PLOTTER, "plotter" }, - { 0,0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_SCSI, IOGA_DMA_SCSI, "scsi" }, - { 0,0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_FDC, IOGA_DMA_FLOPPY, "floppy" } }, - m_dma_serial_channel { - { 0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_SER0, IOGA_DMA_SERIAL0, "serial0" }, - { 0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_SER1, IOGA_DMA_SERIAL1, "serial1" }, - { 0,0,0,0,IDLE, {*this}, {*this}, ARBCTL_BGR_SER2, IOGA_DMA_SERIAL2, "serial2" } }, - m_fdc_tc_func(*this), - m_eth_ca_func(*this) +DEVICE_ADDRESS_MAP_START(map, 32, turquoise_ioga_device) + AM_INHERIT_FROM(interpro_ioga_device::map) + + AM_RANGE(0x00, 0x03) AM_READWRITE(eth_base_r, eth_base_w) + AM_RANGE(0x04, 0x07) AM_READWRITE16(eth_control_r, eth_control_w, 0xffff) +ADDRESS_MAP_END + +DEVICE_ADDRESS_MAP_START(map, 32, sapphire_ioga_device) + AM_INHERIT_FROM(interpro_ioga_device::map) + + AM_RANGE(0x00, 0x03) AM_READWRITE(eth_remap_r, eth_remap_w) + AM_RANGE(0x04, 0x07) AM_READWRITE(eth_mappg_r, eth_mappg_w) + AM_RANGE(0x08, 0x0b) AM_READWRITE(eth_control_r, eth_control_w) +ADDRESS_MAP_END + +static INPUT_PORTS_START(interpro_mouse) + PORT_START("mouse_buttons") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Left Button") PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, interpro_ioga_device, mouse_button, nullptr) + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Mouse Middle Button") PORT_CODE(MOUSECODE_BUTTON3) PORT_CHANGED_MEMBER(DEVICE_SELF, interpro_ioga_device, mouse_button, nullptr) + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Mouse Right Button") PORT_CODE(MOUSECODE_BUTTON2) PORT_CHANGED_MEMBER(DEVICE_SELF, interpro_ioga_device, mouse_button, nullptr) + + PORT_START("mouse_x") + PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, interpro_ioga_device, mouse_x, nullptr) + PORT_START("mouse_y") + PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_CHANGED_MEMBER(DEVICE_SELF, interpro_ioga_device, mouse_y, nullptr) +INPUT_PORTS_END + +DEFINE_DEVICE_TYPE(TURQUOISE_IOGA, turquoise_ioga_device, "ioga_t", "I/O Gate Array (Turquoise)") +DEFINE_DEVICE_TYPE(SAPPHIRE_IOGA, sapphire_ioga_device, "ioga_s", "I/O Gate Array (Sapphire)") + +interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , m_memory_space(nullptr) + , m_out_nmi_func(*this) + , m_out_irq_func(*this) + , m_out_ivec_func(*this) + , m_fdc_tc_func(*this) + , m_eth_ca_func(*this) + , m_dma_channel{ + { 0,0,0,0,CLEAR_LINE,IDLE, {*this}, {*this}, ARBCTL_BGR_PLOT, DMA_PLOTTER, "plotter" }, + { 0,0,0,0,CLEAR_LINE,IDLE, {*this}, {*this}, ARBCTL_BGR_SCSI, DMA_SCSI, "scsi" }, + { 0,0,0,0,CLEAR_LINE,IDLE, {*this}, {*this}, ARBCTL_BGR_FDC, DMA_FLOPPY, "floppy" } } + , m_serial_dma_channel{ + { 0,0,CLEAR_LINE, {*this}, {*this}, ARBCTL_BGR_SER0, 0, "serial0" }, + { 0,0,CLEAR_LINE, {*this}, {*this}, ARBCTL_BGR_SER1, 1, "serial1" }, + { 0,0,CLEAR_LINE, {*this}, {*this}, ARBCTL_BGR_SER2, 2, "serial2" } } { } -#if LOG_HWINT_ENABLE -static const char *interrupt_source[IOGA_INTERRUPT_COUNT] = { - // internal - "timer 2", - "timer 3", - // external - "SCSI", - "floppy", - "plotter", - "SRX / CBUS 0", - "SRX / CBUS 1", - "SRX / CBUS 2", - "VB", - "", - "CBUS 3", - "clock / calendar", - "clock / SGA", - // internal - "mouse", - "timer 0", - "timer 1", - "serial DMA", - // external - "serial", - "Ethernet", -}; -#endif +turquoise_ioga_device::turquoise_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : interpro_ioga_device(mconfig, TURQUOISE_IOGA, tag, owner, clock) +{ +} -void interpro_ioga_device::device_start() +sapphire_ioga_device::sapphire_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : interpro_ioga_device(mconfig, SAPPHIRE_IOGA, tag, owner, clock) { - // resolve callbacks - m_out_nmi_func.resolve(); - m_out_irq_func.resolve(); +} +void interpro_ioga_device::device_start() +{ // TODO: parameterise the cammu name and space number // grab the main memory space from the mmu so we can do DMA to/from it device_memory_interface *mmu; siblingdevice("mmu")->interface(mmu); m_memory_space = &mmu->space(0); - for (auto &dma_channel : m_dma_channel) + // resolve callbacks + m_out_nmi_func.resolve(); + m_out_irq_func.resolve(); + m_out_ivec_func.resolve(); + m_fdc_tc_func.resolve(); + m_eth_ca_func.resolve(); + + for (dma_channel_t &dma_channel : m_dma_channel) { - dma_channel.device_r.resolve_safe(0xff); + dma_channel.device_r.resolve(); dma_channel.device_w.resolve(); } - for (auto &dma_channel : m_dma_serial_channel) + for (serial_dma_channel_t &dma_channel : m_serial_dma_channel) { - dma_channel.device_r.resolve_safe(0xff); + dma_channel.device_r.resolve(); dma_channel.device_w.resolve(); } - m_fdc_tc_func.resolve(); - m_eth_ca_func.resolve(); - - // allocate ioga timers - m_timer[0] = timer_alloc(IOGA_TIMER_0); - m_timer[1] = timer_alloc(IOGA_TIMER_1); - m_timer[2] = timer_alloc(IOGA_TIMER_2); - m_timer[3] = timer_alloc(IOGA_TIMER_3); + // allocate timers + m_interrupt_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::interrupt_check), this)); + m_dma_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::dma), this)); + m_serial_dma_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::serial_dma), this)); - for (auto & timer : m_timer) - timer->enable(false); + m_timer0 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::timer0), this)); + m_timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::timer1), this)); + m_timer2 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::timer2), this)); + m_timer3 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::timer3), this)); - // allocate ioga interrupt and dma timer - m_ioga_clock = timer_alloc(IOGA_CLOCK); - m_ioga_clock->adjust(attotime::never); + m_mouse_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::mouse_timer), this)); + m_eth_reset_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(interpro_ioga_device::eth_reset), this)); } void interpro_ioga_device::device_reset() { // initialise interrupt state - m_active_interrupt_type = IOGA_INTERRUPT_NONE; - m_hwint_forced = 0; - m_nmi_pending = false; + m_active_interrupt = nullptr; m_nmi_state = CLEAR_LINE; m_irq_state = CLEAR_LINE; - m_int_line = 0; + m_ivec = 0; + m_hwint_forced = 0; - // configure timer 0 at 60Hz - m_timer_reg[0] = 0; - //m_timer[0]->adjust(attotime::zero, IOGA_TIMER_0, attotime::from_hz(60)); + // initialise dma state + for (dma_channel_t &dma_channel : m_dma_channel) + { + dma_channel.drq_state = CLEAR_LINE; + dma_channel.state = IDLE; + } - // configure ioga interrupt and dma timer - m_ioga_clock->adjust(attotime::zero, IOGA_CLOCK, attotime::from_hz(clock())); -} + for (serial_dma_channel_t &dma_channel : m_serial_dma_channel) + dma_channel.drq_state = CLEAR_LINE; -/****************************************************************************** - Timers -******************************************************************************/ -READ32_MEMBER(interpro_ioga_device::timer1_r) -{ - uint32_t result = m_timer1_count & IOGA_TIMER1_VMASK; + // disable timers + m_interrupt_timer->enable(false); + m_dma_timer->enable(false); + m_serial_dma_timer->enable(false); - // set the start bit if the timer is currently enabled - if (m_timer[1]->enabled()) - result |= IOGA_TIMER1_START; - else if (m_timer[1]->param()) - result |= IOGA_TIMER1_EXPIRED; + m_timer0->enable(false); + m_timer1->enable(false); + m_timer2->enable(false); + m_timer3->enable(false); - return result; -} + m_mouse_timer->enable(false); + m_eth_reset_timer->enable(false); -READ32_MEMBER(interpro_ioga_device::timer3_r) -{ - uint32_t result = m_timer3_count & IOGA_TIMER3_VMASK; + // configure timer 0 for 60Hz + m_timer0_count = 0; + m_timer0_limit = 0; + m_timer0->adjust(attotime::zero, 0, attotime::from_hz(60)); - if (m_timer[3]->enabled()) - result |= IOGA_TIMER3_START; - else if (m_timer[3]->param()) - result |= IOGA_TIMER3_EXPIRED; + m_timer1_count = 0; + m_timer2_count = 0; + m_timer2_value = 0; + m_timer3_count = 0; - return result; + m_prescaler = 0; } -void interpro_ioga_device::write_timer(int timer, u32 value, device_timer_id id) +ioport_constructor interpro_ioga_device::device_input_ports() const { - switch (id) - { - case IOGA_TIMER_1: - // disable the timer - m_timer[timer]->enable(false); + return INPUT_PORTS_NAME(interpro_mouse); +} - // store the timer count value - m_timer1_count = value; +void interpro_ioga_device::set_nmi_line(int state) +{ + LOGMASKED(LOG_NMI, "nmi: %s (%s)\n", state ? "asserted" : "cleared", machine().describe_context()); - // start the timer if necessary - if (value & IOGA_TIMER1_START) + if (state == ASSERT_LINE) + { + // check if nmi is enabled + if (((m_nmictrl & NMI_IE) == NMI_IE) || ((m_nmictrl & (NMI_ALL | NMI_ENABLE1)) == (NMI_ALL | NMI_ENABLE1))) { - LOG_TIMER(1, "timer 1: started prescaler %d value %d\n", m_prescaler & 0x7fff, value & IOGA_TIMER1_VMASK); - - // FIXME: this division by 50 is sufficient to pass iogadiag timer 1 tests - m_timer[timer]->adjust(attotime::zero, false, attotime::from_usec((m_prescaler & 0x7fff) / 50)); - } - break; - - case IOGA_TIMER_3: - // stop the timer so it won't trigger while we're fiddling with it - m_timer[timer]->enable(false); - - // write the new value to the timer register - m_timer3_count = value & IOGA_TIMER3_VMASK; + LOGMASKED(LOG_NMI, "nmi: asserting output nmi line\n"); - // start the timer if necessary - if (value & IOGA_TIMER3_START) - { - LOG_TIMER(3, "timer 3: started value %d\n", value & IOGA_TIMER3_VMASK); + // if level triggered, disable input from pin + if ((m_nmictrl & NMI_EDGE) == 0) + m_nmictrl &= ~NMI_ENABLE2; - m_timer[timer]->adjust(attotime::zero, false, attotime::from_hz(XTAL_25MHz)); + nmi(ASSERT_LINE); } - break; - - default: - // save the value - m_timer_reg[timer] = value; - - // timer_set(attotime::from_usec(500), id); - - LOG_TIMER(0xf, "timer %d: set to 0x%x (%d)\n", timer, m_timer_reg[timer], m_timer_reg[timer]); - break; } + else + nmi(CLEAR_LINE); } -void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +void interpro_ioga_device::set_int_line(interrupt_type type, int number, int state) { - switch (id) + LOGIRQ(number, "irq: set_int_line(%d, %d, %d)\n", type, number, state); +#if 0 + // preliminary edge/level triggered interrupt handling + if (type == INT_HARD_IN || type == INT_HARD_EX) { - case IOGA_TIMER_0: - m_timer_reg[0]++; - m_hwicr[IOGA_TIMER0_IRQ] |= IOGA_INTERRUPT_PENDING; - break; - - case IOGA_TIMER_1: - // decrement timer count value - m_timer1_count--; - - // check if timer has expired - if (m_timer1_count == 0) + /* + * existing new type result + * clear clear edge nothing + * clear assert edge pending if not masked + * assert clear edge nothing + * assert assert edge nothing + + * clear clear level nothing + * clear assert level pending + * assert clear level !pending + * assert assert level nothing + */ + + u16 icr = get_icr(type, number); + u32 mask = 1 << number; + + if (icr & IRQ_EDGE) { - LOG_TIMER(1, "timer 1: stopped\n"); - - // disable timer and set the zero flag - timer.enable(false); - timer.set_param(true); - - // throw an interrupt - m_hwicr[IOGA_TIMER1_IRQ] |= IOGA_INTERRUPT_PENDING; + if ((m_line_state & mask) == 0 && state) + set_pending(type, number, true); } - break; - - case IOGA_TIMER_3: - // decrement timer count value - m_timer3_count--; - - // check for expiry - if (m_timer3_count == 0) + else { - LOG_TIMER(3, "timer 3: stopped\n"); - - // disable timer and set the zero flag - timer.enable(false); - timer.set_param(true); - - // throw an interrupt - m_hwicr[IOGA_TIMER3_IRQ] |= IOGA_INTERRUPT_PENDING; + if (((m_line_state & mask) == 0) ^ (state != ASSERT_LINE)) + set_pending(type, number, state == ASSERT_LINE); } - break; - case IOGA_CLOCK: - interrupt_clock(); - dma_clock(); - dma_serial_clock(); - break; - } -} - -/****************************************************************************** - Interrupts -******************************************************************************/ -static const u16 irq_enable_mask[IOGA_INTERRUPT_COUNT] = -{ - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 0: SCSI - IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 1: floppy - IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL, // external interrupt 2: plotter - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - - // internal interrupt 5: serial DMA - one interrupt enable per DMA channel - IOGA_INTERRUPT_ENABLE_EXTERNAL << 0 | IOGA_INTERRUPT_ENABLE_EXTERNAL << 1 | IOGA_INTERRUPT_ENABLE_EXTERNAL << 2, - IOGA_INTERRUPT_ENABLE_EXTERNAL, - IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL // external interrupt 12: Ethernet -}; - -bool interpro_ioga_device::nmi(int state) -{ - if (m_nmi_state != state) - { - m_nmi_state = state; - m_out_nmi_func(m_nmi_state); - - return true; + m_line_state = (state == ASSERT_LINE) ? (m_line_state | mask) : (m_line_state & ~mask); } else - return false; +#endif + set_pending(type, number, state == ASSERT_LINE); + + m_interrupt_timer->adjust(attotime::zero); } -bool interpro_ioga_device::irq(int state) +/* + * Interrupts + */ +IRQ_CALLBACK_MEMBER(interpro_ioga_device::acknowledge_interrupt) { - if (m_irq_state != state) + switch (irqline) { - m_irq_state = state; - m_out_irq_func(m_irq_state); + case INPUT_LINE_IRQ0: + // clear pending flag + set_pending(m_active_interrupt->type, m_active_interrupt->number, false); - return true; - } - else - return false; -} + // clear irq/ivec + irq(CLEAR_LINE, 0); -void interpro_ioga_device::set_nmi_line(int state) -{ - LOG_INTERRUPT("nmi: set_nmi_line(%d)\n", state); - switch (state) - { - case ASSERT_LINE: - m_nmi_pending = true; + m_active_interrupt = nullptr; break; - case CLEAR_LINE: - m_nmi_pending = false; + case INPUT_LINE_NMI: + nmi(CLEAR_LINE); break; } + + m_interrupt_timer->adjust(attotime::zero); + + return 0; } -void interpro_ioga_device::set_irq_line(int irq, int state) +TIMER_CALLBACK_MEMBER(interpro_ioga_device::interrupt_check) { - LOG_HWINT(irq, "irq: set_irq_line(%d, %d)\n", irq, state); - switch (state) + // find highest priority unmasked pending interrupt + u16 ivec = m_ivec; + for (const interrupt_data_t &interrupt : m_interrupt_data) { - case ASSERT_LINE: - // set pending bit - m_int_line |= (1 << irq); - m_hwicr[irq] |= IOGA_INTERRUPT_PENDING; - break; + u16 icr = get_icr(interrupt.type, interrupt.number); - case CLEAR_LINE: - // clear pending bit - m_int_line &= ~(1 << irq); - m_hwicr[irq] &= ~IOGA_INTERRUPT_PENDING; - break; + // test for pending and not masked + if ((icr & interrupt.mask) == interrupt.mask) + { + // check priority + if (m_active_interrupt == nullptr || (icr & IRQ_VECTOR) < ivec) + { + m_active_interrupt = &interrupt; + ivec = icr & IRQ_VECTOR; + } + } } + + // assert irq and ivec + if (m_active_interrupt != nullptr) + irq(ASSERT_LINE, ivec); } -void interpro_ioga_device::set_irq_soft(int irq, int state) +void interpro_ioga_device::set_pending(interrupt_type type, int number, bool pending) { - LOG_INTERRUPT("irq: set_irq_soft(%d, %d)\n", irq, state); - switch (state) + switch (type) { - case ASSERT_LINE: - // set pending bit - if (irq < 8) - m_softint |= 1 << irq; + case INT_HARD_IN: + case INT_HARD_EX: + if (pending) + m_hwicr[number] |= IRQ_PENDING; else - m_swicr[irq - 8] |= IOGA_INTERRUPT_PENDING; + m_hwicr[number] &= ~IRQ_PENDING; break; - case CLEAR_LINE: - // clear pending bit - if (irq < 8) - m_softint &= ~(1 << irq); + case INT_SOFT_LO: + if (pending) + m_softint |= (1 << number); else - m_swicr[irq - 8] &= ~IOGA_INTERRUPT_PENDING; + m_softint &= ~(1 << number); + break; + + case INT_SOFT_HI: + if (pending) + m_swicr[number] |= IRQ_PENDING; + else + m_swicr[number] &= ~IRQ_PENDING; break; } } -IRQ_CALLBACK_MEMBER(interpro_ioga_device::inta_cb) +bool interpro_ioga_device::nmi(int state) { - int vector = 0; - - switch (irqline) + if (m_nmi_state != state) { - case INPUT_LINE_IRQ0: - // FIXME: clear pending bit - can't rely on device callbacks - switch (m_active_interrupt_type) - { - case IOGA_INTERRUPT_INTERNAL: - case IOGA_INTERRUPT_EXTERNAL: - m_hwicr[m_active_interrupt_number] &= ~IOGA_INTERRUPT_PENDING; - break; - - case IOGA_INTERRUPT_SOFT_LO: - m_softint &= ~(1 << m_active_interrupt_number); - break; - - case IOGA_INTERRUPT_SOFT_HI: - m_swicr[m_active_interrupt_number] &= ~IOGA_INTERRUPT_PENDING; - break; - } - - // fall through to return interrupt vector - case -1: - // return vector for current interrupt without clearing pending flag - switch (m_active_interrupt_type) - { - case IOGA_INTERRUPT_INTERNAL: - case IOGA_INTERRUPT_EXTERNAL: - vector = m_hwicr[m_active_interrupt_number] & 0xff; - break; - - case IOGA_INTERRUPT_SOFT_LO: - vector = 0x8f + m_active_interrupt_number * 0x10; - break; - - case IOGA_INTERRUPT_SOFT_HI: - vector = m_swicr[m_active_interrupt_number] & 0xff; - break; - } - - // interrupt is acknowledged - if (irqline == INPUT_LINE_IRQ0) - m_active_interrupt_type = IOGA_INTERRUPT_NONE; - break; + m_nmi_state = state; + m_out_nmi_func(state); - case INPUT_LINE_NMI: - m_active_interrupt_type = IOGA_INTERRUPT_NONE; - m_nmi_pending = false; - break; + return true; } - - return vector; + else + return false; } -void interpro_ioga_device::interrupt_clock() +bool interpro_ioga_device::irq(int state, u8 ivec) { - // called on every ioga clock cycle - // if there are no active interrupts, raise the next pending one - - // don't do anything if any interrupts are currently being serviced - if (m_active_interrupt_type != IOGA_INTERRUPT_NONE) - return; - - // if nmi line is asserted, clear it - if (nmi(CLEAR_LINE)) - return; - - // if irq line is asserted, clear it - if (irq(CLEAR_LINE)) - return; - - // check for pending nmi - if (m_nmi_pending) + if (ivec != m_ivec) { - // check if nmi is enabled - if (((m_nmictrl & NMI_IE) == NMI_IE) - || ((m_nmictrl & (NMI_ALL | NMI_ENABLE1)) == (NMI_ALL | NMI_ENABLE1))) - { - LOG_INTERRUPT("nmi: accepting nmi\n"); + LOGIRQ(m_active_interrupt->number, "setting ivec 0x%02x\n", ivec); - // if level triggered, disable input from pin - if ((m_nmictrl & NMI_EDGE) == 0) - m_nmictrl &= ~NMI_ENABLE2; - - m_active_interrupt_type = IOGA_INTERRUPT_NMI; - nmi(ASSERT_LINE); - return; - } + m_ivec = ivec; + machine().scheduler().synchronize(timer_expired_delegate(FUNC(interpro_ioga_device::set_ivec), this)); } - // scan all hardware interrupts - for (int i = 0; i < IOGA_INTERRUPT_COUNT; i++) + if (m_irq_state != state) { - // check if there is a pending interrupt - if (m_hwicr[i] & IOGA_INTERRUPT_PENDING) - { - // check if from an external device or internal to ioga - bool external = m_int_line & (1 << i); - - // check if masked - if (m_hwicr[i] & irq_enable_mask[i]) //(external ? IRQ_ENABLE_EXTERNAL : IRQ_ENABLE_INTERNAL)) - { - LOG_HWINT(i, "irq: accepting interrupt %d - %s (%s)\n", i, interrupt_source[i], external ? "external" : "internal"); + LOGIRQ(m_active_interrupt->number, "%s %s number %d source %s (%s)\n", + state ? "asserting" : "clearing", m_active_interrupt->name, m_active_interrupt->number, m_active_interrupt->source, machine().describe_context()); - m_active_interrupt_type = external ? IOGA_INTERRUPT_EXTERNAL : IOGA_INTERRUPT_INTERNAL; - m_active_interrupt_number = i; + m_irq_state = state; + m_out_irq_func(state); - irq(ASSERT_LINE); - return; - } - } + return true; } + else + return false; +} - // check for any pending soft interrupts (low type) - for (int i = 0; i < 8; i++) - { - if (m_softint & (1 << i)) - { - m_active_interrupt_type = IOGA_INTERRUPT_SOFT_LO; - m_active_interrupt_number = i; - - irq(ASSERT_LINE); - return; - } - } +TIMER_CALLBACK_MEMBER(interpro_ioga_device::set_ivec) +{ + m_out_ivec_func(m_ivec); +} - // check for any pending soft interrupts (high type) - for (int i = 0; i < 8; i++) +u16 interpro_ioga_device::get_icr(interrupt_type type, int number) const +{ + switch (type) { - if (m_swicr[i] & IOGA_INTERRUPT_PENDING) - { - m_active_interrupt_type = IOGA_INTERRUPT_SOFT_HI; - m_active_interrupt_number = i; + case INT_HARD_IN: + case INT_HARD_EX: + return m_hwicr[number]; - irq(ASSERT_LINE); - return; - } + case INT_SOFT_LO: + return (m_softint << 8) | (0x8f + number * 0x10); + + case INT_SOFT_HI: + return m_swicr[number]; } + + return 0; } WRITE16_MEMBER(interpro_ioga_device::icr_w) { /* - * It appears that writing the pending flag high and then low again is intended to - * "force" an interrupt to be generated. We record the initial write in m_hwint_forced, - * and when a subsequent write occurrs, turn the pending bit on to trigger the interrupt. - * - * FIXME: should we only flag a forced interrupt if pending is written high from low? - */ - - LOG_HWINT(offset, "irq: interrupt vector %d = 0x%04x (%s)\n", offset, data, machine().describe_context()); - - if (data & IOGA_INTERRUPT_PENDING) + * It appears that writing the pending flag high and then low again is + * intended to force an interrupt to be generated. We record the initial + * write in m_hwint_forced, and when a subsequent write occurrs, turn the + * pending bit on to trigger the interrupt. + * + * FIXME: should we only flag a forced interrupt if pending is written high + * from low? Maybe all of this can be eliminated by the edge/level trigger + * logic in the future. + */ + + LOGIRQ(offset, "irq: interrupt vector %d = 0x%04x (%s)\n", offset, data, machine().describe_context()); + + if (data & IRQ_PENDING) { // record interrupt pending forced m_hwint_forced |= 1 << offset; // store all bits except pending - m_hwicr[offset] = (m_hwicr[offset] & IOGA_INTERRUPT_PENDING) | (data & ~IOGA_INTERRUPT_PENDING); + m_hwicr[offset] = (m_hwicr[offset] & IRQ_PENDING) | (data & ~IRQ_PENDING); + + // FIXME: is it possible trigger a pending interrupt by unmasking it here? } - else if (m_hwint_forced & 1 << offset) + else if (m_hwint_forced & (1 << offset)) { // interrupt is being forced m_hwicr[offset] = data; @@ -604,8 +521,9 @@ WRITE16_MEMBER(interpro_ioga_device::icr_w) // clear forced flag m_hwint_forced &= ~(1 << offset); - // force an interrupt - m_hwicr[offset] |= IOGA_INTERRUPT_PENDING; + // FIXME: in/ex + LOGIRQ(offset, "irq: forcing interrupt vector %d\n", offset); + set_int_line(INT_HARD_IN, offset, ASSERT_LINE); } else // otherwise just store the value @@ -614,121 +532,129 @@ WRITE16_MEMBER(interpro_ioga_device::icr_w) WRITE8_MEMBER(interpro_ioga_device::softint_w) { - // save the existing value - u8 previous = m_softint; + // check for forced interrupts + const u8 forced = m_softint & ~data; // store the written value m_softint = data; - // force soft interrupt for any bit written from 1 to 0 - for (int i = 0; i < 8; i++) + // force interrupts if needed + if (forced != 0) { - u8 mask = 1 << i; - - // check for transition from 1 to 0 and force a soft interrupt - if (previous & mask && !(data & mask)) - set_irq_soft(i, ASSERT_LINE); + for (int i = 0; i < 8; i++) + if (forced & (1 << i)) + set_int_line(INT_SOFT_LO, i, ASSERT_LINE); } } WRITE8_MEMBER(interpro_ioga_device::nmictrl_w) { - LOG_INTERRUPT("nmi: nmictrl = 0x%02x, nmi_pending = %d (%s)\n", data, m_nmi_pending, machine().describe_context()); - // check for a forced nmi (NMI_NEGPOL written from 1 to 0 with NMI_IE set) - if ((m_nmictrl & NMI_NEGPOL) && (data & (NMI_NEGPOL | NMI_IE)) == NMI_IE) - { - LOG_INTERRUPT("nmi: forced nmi\n"); - m_nmi_pending = true; - } + const bool forced = (m_nmictrl & NMI_NEGPOL) && ((data & (NMI_NEGPOL | NMI_IE)) == NMI_IE); + LOGMASKED(LOG_NMI, "nmi: nmictrl = 0x%02x (%s)\n", data, machine().describe_context()); m_nmictrl = data; + + if (forced) + { + LOGMASKED(LOG_NMI, "nmi: forcing nmi\n"); + + set_nmi_line(ASSERT_LINE); + } } WRITE16_MEMBER(interpro_ioga_device::softint_vector_w) { - // save the existing value - u16 previous = m_swicr[offset]; + // check for forced interrupt + const bool forced = (m_swicr[offset] & IRQ_PENDING) && !(data & IRQ_PENDING); // store the written value m_swicr[offset] = data; - // check for transition from 1 to 0 and force a soft interrupt - if (previous & IOGA_INTERRUPT_PENDING && !(data & IOGA_INTERRUPT_PENDING)) - set_irq_soft(offset + 8, ASSERT_LINE); + // force interrupt if needed + if (forced) + set_int_line(INT_SOFT_HI, offset, ASSERT_LINE); } -/****************************************************************************** - DMA -******************************************************************************/ -void interpro_ioga_device::dma_clock() +/* + * DMA + */ +TIMER_CALLBACK_MEMBER(interpro_ioga_device::dma) { - for (auto &dma_channel : m_dma_channel) + for (dma_channel_t &dma_channel : m_dma_channel) { - switch (dma_channel.state) + // check if there's something to transfer + if (dma_channel.transfer_count == 0 || (dma_channel.control & DMA_CTRL_BGR) == 0) + continue; + + // check if the device is requesting a transfer + if (dma_channel.drq_state == CLEAR_LINE) + continue; + + // check if the bus is available + if ((m_arbctl & dma_channel.arb_mask) == 0) { - case IDLE: - break; + // set bus wait flag and abort + dma_channel.control |= DMA_CTRL_WAIT; - case COMMAND: - // start a command - if (dma_channel.control & DMA_CTRL_BGR) - dma_channel.state = WAIT; - else - dma_channel.state = FINAL; - break; + continue; + } + else + // clear bus wait flag + dma_channel.control &= ~DMA_CTRL_WAIT; - case WAIT: - // if bus grant is enabled, clear wait flag and execute transfer - if (m_arbctl & dma_channel.arb_mask) - { - // clear bus wait flag - dma_channel.control &= ~DMA_CTRL_WAIT; + //LOGMASKED(LOG_DMA, "dma: transfer %s device begun, channel = %d, control 0x%08x, real address 0x%08x, virtual address 0x%08x, count 0x%08x\n", + // (dma_channel.control & DMA_CTRL_WRITE) ? "to" : "from", dma_channel.channel, dma_channel.control, dma_channel.real_address, dma_channel.virtual_address, dma_channel.transfer_count); - LOG_DMA(dma_channel.channel, "dma: transfer %s device begun, channel = %d, control 0x%08x, real address 0x%08x, virtual address 0x%08x, count 0x%08x\n", - (dma_channel.control & DMA_CTRL_WRITE) ? "to" : "from", - dma_channel.channel, dma_channel.control, dma_channel.real_address, dma_channel.virtual_address, dma_channel.transfer_count); + // transfer from the memory to device or device to memory + while (dma_channel.transfer_count && dma_channel.drq_state) + { + // transfer from the memory to device or device to memory + // TODO: implement virtual addressing when DMA_CTRL_VIRTUAL is set - dma_channel.state = TRANSFER; - } + if (dma_channel.control & DMA_CTRL_WRITE) + dma_channel.device_w(m_memory_space->read_byte(dma_channel.real_address)); else - // (7.0265) set the bus grant wait flag - dma_channel.control |= DMA_CTRL_WAIT; - break; + m_memory_space->write_byte(dma_channel.real_address, dma_channel.device_r()); + + // increment addresses and decrement count + dma_channel.real_address++; + dma_channel.virtual_address++; + dma_channel.transfer_count--; + } - case TRANSFER: - if (dma_channel.transfer_count) + // check if the transfer is complete + if (dma_channel.transfer_count == 0) + { + LOGMASKED(LOG_DMA, "dma: transfer %s device ended, channel = %d, control 0x%08x, real address 0x%08x, virtual address 0x%08x, count 0x%08x\n", + (dma_channel.control & DMA_CTRL_WRITE) ? "to" : "from", dma_channel.channel, dma_channel.control, dma_channel.real_address, dma_channel.virtual_address, dma_channel.transfer_count); + + if (dma_channel.channel == DMA_FLOPPY) { - if (dma_channel.drq_state) - { - // transfer from the memory to device or device to memory - // TODO: implement virtual addressing when DMA_CTRL_VIRTUAL is set - - if (dma_channel.control & DMA_CTRL_WRITE) - dma_channel.device_w(m_memory_space->read_byte(dma_channel.real_address)); - else - m_memory_space->write_byte(dma_channel.real_address, dma_channel.device_r()); - - // increment addresses and decrement count - dma_channel.real_address++; - dma_channel.virtual_address++; - dma_channel.transfer_count--; - } + LOGMASKED(LOG_DMA | LOG_FLOPPY, "dma: asserting fdc terminal count line\n"); + + m_fdc_tc_func(ASSERT_LINE); + m_fdc_tc_func(CLEAR_LINE); } - else - dma_channel.state = COMPLETE; + + // clear bus grant required + dma_channel.control &= ~DMA_CTRL_BGR; + + // set transfer count zero flag + dma_channel.control |= DMA_CTRL_TCZERO; + } + #if IOGA_DMA_DIAG_HACK - else #define TAG ((dma_channel.control & DMA_CTRL_TAG) >> 3) // hacks for forced dma bus error diagnostic tests if ((dma_channel.control & 0xfe000000 && dma_channel.control & 0xe00) || ((dma_channel.control & DMA_CTRL_WMASK) == 0x41000000)) if (dma_channel.real_address & 0xff000000 || dma_channel.real_address == 0) { - LOG_DMA(dma_channel.channel, "dma: forced bus error hack, control 0x%08x\n", dma_channel.control); + LOGDMA(dma_channel.channel, "dma: forced bus error hack, control 0x%08x\n", dma_channel.control); // (7.0267) trigger an interrupt - m_hwicr[dma_channel.channel + 1] |= IOGA_INTERRUPT_PENDING; + m_hwicr[dma_channel.channel + 1] |= IRQ_PENDING; // (7.0268) set bus error bit dma_channel.control |= DMA_CTRL_BERR; @@ -784,121 +710,28 @@ void interpro_ioga_device::dma_clock() dma_channel.state = COMPLETE; } #endif - break; - - case COMPLETE: - LOG_DMA(dma_channel.channel, "dma: transfer %s device ended, channel = %d, control 0x%08x, real address 0x%08x, virtual address 0x%08x, count 0x%08x\n", - (dma_channel.control & DMA_CTRL_WRITE) ? "to" : "from", - dma_channel.channel, dma_channel.control, dma_channel.real_address, dma_channel.virtual_address, dma_channel.transfer_count); - - if (dma_channel.channel == IOGA_DMA_FLOPPY) - { - LOG_DMA(dma_channel.channel, "dma: asserting fdc terminal count line\n"); - - m_fdc_tc_func(ASSERT_LINE); - m_fdc_tc_func(CLEAR_LINE); - } - - // clear bus grant required - dma_channel.control &= ~DMA_CTRL_BGR; - - // set transfer count zero flag - dma_channel.control |= DMA_CTRL_TCZERO; - - dma_channel.state = FINAL; - break; - - case FINAL: - dma_channel.state = IDLE; - break; - } - } -} - -void interpro_ioga_device::dma_serial_clock() -{ - for (auto &dma_channel : m_dma_serial_channel) - { - switch (dma_channel.state) - { - case IDLE: - if (dma_channel.drq_state && dma_channel.count) - dma_channel.state = WAIT; - break; - - case COMMAND: - case WAIT: - if (m_arbctl & dma_channel.arb_mask) - dma_channel.state = TRANSFER; - break; - - case TRANSFER: - if (dma_channel.count) - { - if (dma_channel.drq_state) - { - // transfer from the memory to device or device to memory - // TODO: work out which control register bits indicate read from device - if (true) - { - u8 data = m_memory_space->read_byte(dma_channel.address); - - LOG_DMA_SERIAL(dma_channel.channel, "dma: transferring byte 0x%02x to serial channel %d\n", data, dma_channel.channel); - - dma_channel.device_w(data); - } - else - m_memory_space->write_byte(dma_channel.address, dma_channel.device_r()); - - // increment address and decrement count - dma_channel.address++; - dma_channel.count--; - } - } - else - dma_channel.state = COMPLETE; - break; - - case COMPLETE: - // transfer count zero - dma_channel.control |= 0x20; - - dma_channel.state = FINAL; - break; - - case FINAL: - // raise an interrupt - m_hwicr[16] |= IOGA_INTERRUPT_PENDING; - - dma_channel.state = IDLE; - break; - } } } void interpro_ioga_device::drq(int state, int channel) { - struct dma &dma_channel = m_dma_channel[channel]; + dma_channel_t &dma_channel = m_dma_channel[channel]; dma_channel.drq_state = state; - // only log every 256 bytes + // log every 256 bytes if ((dma_channel.transfer_count & 0xff) == 0) - LOG_DMA(channel, "dma: drq for channel %d %s, transfer_count 0x%08x\n", channel, state ? "asserted" : "deasserted", dma_channel.transfer_count); -} - -void interpro_ioga_device::drq_serial(int state, int channel) -{ - struct dma_serial &dma_channel = m_dma_serial_channel[channel]; - - dma_channel.drq_state = state; + { + LOGMASKED(LOG_DMA, "dma: drq for channel %d %s transfer_count 0x%08x\n", channel, state ? "asserted" : "deasserted", dma_channel.transfer_count); + } - LOG_DMA_SERIAL(channel, "dma: drq for serial channel %d %s, counter 0x%04x\n", channel, state ? "asserted" : "deasserted", dma_channel.count); + if (state) + m_dma_timer->adjust(attotime::zero); } -u32 interpro_ioga_device::dma_r(address_space &space, offs_t offset, u32 mem_mask, int channel) +u32 interpro_ioga_device::dma_r(address_space &space, offs_t offset, u32 mem_mask, dma_channel channel) const { - struct dma &dma_channel = m_dma_channel[channel]; + const dma_channel_t &dma_channel = m_dma_channel[channel]; switch (offset) { @@ -920,29 +753,29 @@ u32 interpro_ioga_device::dma_r(address_space &space, offs_t offset, u32 mem_mas } } -void interpro_ioga_device::dma_w(address_space &space, offs_t offset, u32 data, u32 mem_mask, int channel) +void interpro_ioga_device::dma_w(address_space &space, offs_t offset, u32 data, u32 mem_mask, dma_channel channel) { - struct dma &dma_channel = m_dma_channel[channel]; + dma_channel_t &dma_channel = m_dma_channel[channel]; switch (offset) { case 0: - LOG_DMA(channel, "dma: channel %d real address = 0x%08x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_DMA, "dma: channel %d real address = 0x%08x (%s)\n", channel, data, machine().describe_context()); dma_channel.real_address = data; break; case 1: - LOG_DMA(channel, "dma: channel %d virtual address = 0x%08x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_DMA, "dma: channel %d virtual address = 0x%08x (%s)\n", channel, data, machine().describe_context()); dma_channel.virtual_address = data & ~0x3; break; case 2: - LOG_DMA(channel, "dma: channel %d transfer count = 0x%08x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_DMA, "dma: channel %d transfer count = 0x%08x (%s)\n", channel, data, machine().describe_context()); dma_channel.transfer_count = data; break; case 3: - LOG_DMA(channel, "dma: channel %d control = 0x%08x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_DMA, "dma: channel %d control = 0x%08x (%s)\n", channel, data, machine().describe_context()); // (7.0272) if bus error flag is set, clear existing bus error (otherwise retain existing state) if (data & DMA_CTRL_BERR) @@ -955,78 +788,521 @@ void interpro_ioga_device::dma_w(address_space &space, offs_t offset, u32 data, } } -void interpro_ioga_device::dma_serial_addr_w(address_space &space, offs_t offset, u32 data, u32 mem_mask, int channel) +TIMER_CALLBACK_MEMBER(interpro_ioga_device::serial_dma) +{ + LOGMASKED(LOG_SERIALDMA, "dma: serial_dma()\n"); + + for (serial_dma_channel_t &dma_channel : m_serial_dma_channel) + { + // check if there's something to transfer + if ((dma_channel.control & SDMA_COUNT) == 0) + continue; + + // check if the device is requesting a transfer + if (dma_channel.drq_state == CLEAR_LINE) + continue; + + // check if the bus is available + if ((m_arbctl & dma_channel.arb_mask) == 0) + continue; + + // transfer from the memory to device or device to memory + while ((dma_channel.control & SDMA_COUNT) && dma_channel.drq_state) + { + // TODO: work out which control register bits indicate read from device + if (dma_channel.control & SDMA_SEND) + { + u8 data = m_memory_space->read_byte(dma_channel.address); + + LOGMASKED(LOG_SERIALDMA, "dma: transmitting byte 0x%02x to serial channel %d\n", data, dma_channel.channel); + + dma_channel.device_w(data); + } + else + { + u8 data = dma_channel.device_r(); + + LOGMASKED(LOG_SERIALDMA, "dma: receiving byte 0x%02x from serial channel %d\n", data, dma_channel.channel); + + m_memory_space->write_byte(dma_channel.address, data); + } + + // increment address and decrement count + dma_channel.address++; + dma_channel.control = (dma_channel.control & SDMA_CONTROL) | ((dma_channel.control & SDMA_COUNT) - 1); + } + + if ((dma_channel.control & SDMA_COUNT) == 0) + { + // transfer count zero + dma_channel.control |= SDMA_TCZERO; + + // raise an interrupt + set_int_line(INT_HARD_IN, IRQ_SERDMA, ASSERT_LINE); + } + } +} + +void interpro_ioga_device::serial_drq(int state, int channel) +{ + serial_dma_channel_t &dma_channel = m_serial_dma_channel[channel]; + + dma_channel.drq_state = state; + + LOGMASKED(LOG_SERIALDMA, "dma: drq for serial channel %d %s count 0x%04x\n", channel, state ? "asserted" : "deasserted", dma_channel.control & SDMA_COUNT); + + if (state && (dma_channel.control & SDMA_COUNT)) + m_serial_dma_timer->adjust(attotime::zero); +} + +void interpro_ioga_device::serial_dma_addr_w(address_space &space, offs_t offset, u32 data, u32 mem_mask, int channel) { - LOG_DMA_SERIAL(channel, "dma: serial channel %d address = 0x%08x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_SERIALDMA, "dma: serial channel %d address 0x%08x mask 0x%08x (%s)\n", channel, data, mem_mask, machine().describe_context()); - m_dma_serial_channel[channel].address = data; + COMBINE_DATA(&m_serial_dma_channel[channel].address); } -void interpro_ioga_device::dma_serial_count_w(address_space &space, offs_t offset, u16 data, u16 mem_mask, int channel) +void interpro_ioga_device::serial_dma_ctrl_w(address_space &space, offs_t offset, u32 data, u32 mem_mask, int channel) { - LOG_DMA_SERIAL(channel, "dma: serial channel %d count = 0x%04x (%s)\n", channel, data, machine().describe_context()); + serial_dma_channel_t &dma_channel = m_serial_dma_channel[channel]; + + LOGMASKED(LOG_SERIALDMA, "dma: serial channel %d control 0x%08x mask 0x%08x (%s)\n", channel, data, mem_mask, machine().describe_context()); + + COMBINE_DATA(&dma_channel.control); - m_dma_serial_channel[channel].count = data; + if (dma_channel.control & SDMA_COUNT) + m_serial_dma_timer->adjust(attotime::zero); } -void interpro_ioga_device::dma_serial_ctrl_w(address_space &space, offs_t offset, u16 data, u16 mem_mask, int channel) +/* + * Bus arbitration and control + */ +WRITE16_MEMBER(interpro_ioga_device::arbctl_w) { - LOG_DMA_SERIAL(channel, "dma: serial channel %d control = 0x%04x (%s)\n", channel, data, machine().describe_context()); + LOGMASKED(LOG_DMA | LOG_SERIALDMA, "dma: arbctl = 0x%04x (%s)\n", data, machine().describe_context()); + + m_arbctl = data; - m_dma_serial_channel[channel].control = data; + // trigger serial dma waiting for bus access + if (m_arbctl & (ARBCTL_BGR_SER0 | ARBCTL_BGR_SER1 | ARBCTL_BGR_SER2)) + m_serial_dma_timer->adjust(attotime::zero); } READ32_MEMBER(interpro_ioga_device::error_businfo_r) { - u32 result = m_error_businfo; + const u32 result = m_error_businfo; // clear register after reading - m_error_businfo = 0; + if (!machine().side_effect_disabled()) + m_error_businfo = 0; return result; } -WRITE16_MEMBER(interpro_ioga_device::arbctl_w) +/* + * Timers + */ +READ32_MEMBER(interpro_ioga_device::timer0_r) const { - LOG_DMA(31, "dma: arbctl = 0x%04x (%s)\n", data, machine().describe_context()); + LOGMASKED(LOG_TIMERRD, "timer0_r data 0x%08x mask 0x%08x (%s)\n", m_timer0_count, mem_mask, machine().describe_context()); - m_arbctl = data; + return m_timer0_count; } -WRITE32_MEMBER(interpro_ioga_device::eth_remap_w) +WRITE32_MEMBER(interpro_ioga_device::timer0_w) { - LOG_ETH("eth: remap = 0x%08x (%s)\n", data, machine().describe_context()); + //m_timer0_limit = data; + m_timer0_count = data; - m_eth_remap = data & ~0xf; + m_timer0->adjust(attotime::zero, 0, attotime::from_hz(60)); + + LOGMASKED(LOG_TIMER0, "timer0_w data 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); } -WRITE32_MEMBER(interpro_ioga_device::eth_map_page_w) +TIMER_CALLBACK_MEMBER(interpro_ioga_device::timer0) { - LOG_ETH("eth: remap page = 0x%08x (%s)\n", data, machine().describe_context()); + if (m_timer0_count == 0) + set_int_line(INT_HARD_IN, IRQ_TIMER0, ASSERT_LINE); + else + m_timer0_count++; - m_eth_map_page = data & ~0xf; + //m_hwicr[IRQ_VB] |= IRQ_PENDING; // this makes mouse diag countdown work (at half correct speed) + set_int_line(INT_HARD_EX, IRQ_12, ASSERT_LINE); // FIXME: 60Hz timer on InterPro 2000 } -WRITE32_MEMBER(interpro_ioga_device::eth_control_w) +READ32_MEMBER(interpro_ioga_device::timer1_r) const { - LOG_ETH("eth: control = 0x%08x (%s)\n", data, machine().describe_context()); + u32 result = m_timer1_count & TIMER1_COUNT; - /* (7.0202) eth ctrl register input test patterns and expected outputs are: + // set the start bit if the timer is currently enabled + if (m_timer1->enabled()) + result |= TIMER1_START; + else if (m_timer1->param()) + result |= TIMER1_EXPIRED; + + LOGMASKED(LOG_TIMERRD, "timer1_r data 0x%08x mask 0x%08x (%s)\n", result, mem_mask, machine().describe_context()); + + return result; +} + +WRITE32_MEMBER(interpro_ioga_device::timer1_w) +{ + // disable the timer + m_timer1->enable(false); + + // store the timer count value + m_timer1_count = data & TIMER1_COUNT; + + // start the timer if necessary + if (data & TIMER1_START) + { + LOGMASKED(LOG_TIMER1, "timer1_w data 0x%08x mask 0x%08x prescaler 0x%04x (%s)\n", data, mem_mask, m_prescaler & 0x7fff, machine().describe_context()); + + // theory: timer 1 is 10MHz (typical prescaler value of 1000 and timer value of 100 giving a delay of 100ms) + // FIXME: division by 50 is sufficient to pass iogadiag timer 1 tests + m_timer1->adjust(attotime::zero, false, attotime::from_usec((m_prescaler & 0x7fff) / 50)); + } +} + +TIMER_CALLBACK_MEMBER(interpro_ioga_device::timer1) +{ + // decrement timer count value + m_timer1_count--; - 7809 -> 4000 + // check if timer has expired + if (m_timer1_count == 0) + { + LOGMASKED(LOG_TIMER1, "timer1 expired\n"); + + // disable timer and set the zero flag + m_timer1->enable(false); + m_timer1->set_param(true); + + // throw an interrupt + set_int_line(INT_HARD_IN, IRQ_TIMER1, ASSERT_LINE); + } +} + +READ32_MEMBER(interpro_ioga_device::timer2_count_r) const +{ + LOGMASKED(LOG_TIMERRD, "timer2_count_r data 0x%08x mask 0x%08x (%s)\n", m_timer2_count, mem_mask, machine().describe_context()); + + return m_timer2_count; +} + +WRITE32_MEMBER(interpro_ioga_device::timer2_count_w) +{ + m_timer2_count = data; + + LOGMASKED(LOG_TIMER2, "timer2_count_w data 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); +} + +READ32_MEMBER(interpro_ioga_device::timer2_value_r) const +{ + LOGMASKED(LOG_TIMERRD, "timer2_value_r data 0x%08x mask 0x%08x (%s)\n", m_timer2_value, mem_mask, machine().describe_context()); + + return m_timer2_value; +} + +WRITE32_MEMBER(interpro_ioga_device::timer2_value_w) +{ + m_timer2_value = data; + m_timer2_count = data; // ? + + LOGMASKED(LOG_TIMER2, "timer2_value_w data 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); +} + +READ32_MEMBER(interpro_ioga_device::timer3_r) const +{ + u32 result = m_timer3_count & TIMER3_COUNT; + + if (m_timer3->enabled()) + result |= TIMER3_START; + else if (m_timer3->param()) + result |= TIMER3_EXPIRED; + + LOGMASKED(LOG_TIMERRD, "timer3_r data 0x%08x mask 0x%08x (%s)\n", result, mem_mask, machine().describe_context()); + + return result; +} + +WRITE32_MEMBER(interpro_ioga_device::timer3_w) +{ + // stop the timer so it won't trigger while we're fiddling with it + m_timer3->enable(false); - 3809 -> 0800 - 3409 -> 0400 - 3209 -> 0200 - 3109 -> 0100 + // write the new value to the timer register + m_timer3_count = data & TIMER3_COUNT; - 3089 -> 80 - 3049 -> 40 - 3019 -> 10 - 300b -> 02 - */ - if (data & ETH_RESET) - m_eth_control = ETH_RESET; + // start the timer if necessary + if (data & TIMER3_START) + { + LOGMASKED(LOG_TIMER3, "timer3_w data 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + // theory: timer 3 is 12.5MHz (typical value of 12500 giving a delay of 1ms) + m_timer3->adjust(attotime::zero, false, attotime::from_hz(XTAL_12_5MHz)); + } +} + +TIMER_CALLBACK_MEMBER(interpro_ioga_device::timer3) +{ + // decrement timer count value + m_timer3_count--; + + // check for expiry + if (m_timer3_count == 0) + { + LOGMASKED(LOG_TIMER3, "timer3 expired\n"); + + // disable timer and set the zero flag + m_timer3->enable(false); + m_timer3->set_param(true); + + // throw an interrupt + set_int_line(INT_HARD_IN, IRQ_TIMER3, ASSERT_LINE); + } +} + +READ32_MEMBER(interpro_ioga_device::prescaler_r) const +{ + // FIXME: prescaler only used with timer 1? + LOGMASKED(LOG_TIMERRD, "prescaler_r data 0x%08x mask 0x%08x (%s)\n", m_prescaler, mem_mask, machine().describe_context()); + + return m_prescaler; +} + +WRITE32_MEMBER(interpro_ioga_device::prescaler_w) +{ + LOGMASKED(LOG_TIMER1, "prescaler_w data 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + // this logic satisfies prescaler tests, but fails timer prescaler tests + if ((data & 0x7fff) < 0x100 && (data & 0x7fff) != 0) + m_prescaler = (data ^ 0xffff0000); else - m_eth_control = data & 0x0ff2; + m_prescaler = (data ^ 0xffff0000) - 0x10000; +} + +/* + * Mouse + */ +READ32_MEMBER(interpro_ioga_device::mouse_status_r) +{ + const u32 result = m_mouse_status; + + LOGMASKED(LOG_MOUSE, "mouse_status_r status 0x%08x mask 0x%08x (%s)\n", result, mem_mask, machine().describe_context()); + + // clear xpos and ypos fields + if (!machine().side_effect_disabled()) + m_mouse_status &= ~(MOUSE_XPOS | MOUSE_YPOS); + + return result; +} + +WRITE32_MEMBER(interpro_ioga_device::mouse_status_w) +{ + LOGMASKED(LOG_MOUSE, "mouse_status_w status 0x%08x mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + m_mouse_status |= (data & MOUSE_COUNTER); + + // FIXME: diagnostic test fails with value >= 0x1000 + if (data & MOUSE_COUNTER) + m_mouse_timer->adjust(attotime::from_nsec(data & MOUSE_COUNTER)); +} + +INPUT_CHANGED_MEMBER(interpro_ioga_device::mouse_button) +{ + if (newval) + m_mouse_status |= (field.mask() << 16); + else + m_mouse_status &= ~(field.mask() << 16); + + set_int_line(INT_HARD_IN, IRQ_MOUSE, ASSERT_LINE); +} + +INPUT_CHANGED_MEMBER(interpro_ioga_device::mouse_x) +{ + // compute x delta + int delta = newval - oldval; + if (delta > 0x80) + delta -= 0x100; + else if (delta < -0x80) + delta += 0x100; + + // set new x delta + m_mouse_status &= ~MOUSE_XPOS; + m_mouse_status |= ((delta << 8) & MOUSE_XPOS); + + set_int_line(INT_HARD_IN, IRQ_MOUSE, ASSERT_LINE); +} + +INPUT_CHANGED_MEMBER(interpro_ioga_device::mouse_y) +{ + // compute y delta + int delta = newval - oldval; + if (delta > 0x80) + delta -= 0x100; + else if (delta < -0x80) + delta += 0x100; + + // set new x delta + m_mouse_status &= ~MOUSE_YPOS; + m_mouse_status |= ((delta << 0) & MOUSE_YPOS); + + set_int_line(INT_HARD_IN, IRQ_MOUSE, ASSERT_LINE); +} + +TIMER_CALLBACK_MEMBER(interpro_ioga_device::mouse_timer) +{ + set_int_line(INT_HARD_IN, IRQ_MOUSE, ASSERT_LINE); +} + +/* + * Ethernet + */ +WRITE32_MEMBER(turquoise_ioga_device::eth_base_w) +{ + LOGMASKED(LOG_NETWORK, "eth: base_w 0x%08x mem_mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + m_eth_base = ((m_eth_base & ~mem_mask) | (data & mem_mask)) & ETH_BASE_MASK; +} + +WRITE16_MEMBER(turquoise_ioga_device::eth_control_w) +{ + LOGMASKED(LOG_NETWORK, "eth: control_w 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context()); + + m_eth_control = data; + + // ethernet device reset (active low) + if ((data & ETH_RESET) == 0) + { + // reset the ethernet device + siblingdevice("eth")->reset(); + + // clear the reset flag (the 250ns delay is long enough to pass diagnostic tests) + m_eth_reset_timer->adjust(attotime::from_nsec(250)); + } + + // ethernet channel attention + if (data & ETH_CA) + { + m_eth_ca_func(ASSERT_LINE); + m_eth_ca_func(CLEAR_LINE); + } +} + +WRITE16_MEMBER(turquoise_ioga_device::eth_w) +{ + const u32 address = m_eth_base | ((offset << 1) & ~ETH_BASE_MASK); + + LOGMASKED(LOG_NETWORK, "eth_w address 0x%08x mask 0x%04x data 0x%04x\n", address, mem_mask, data); + m_memory_space->write_word(address, data, mem_mask); +} + +READ16_MEMBER(turquoise_ioga_device::eth_r) const +{ + const u32 address = m_eth_base | ((offset << 1) & ~ETH_BASE_MASK); + + const u16 data = m_memory_space->read_word(address, mem_mask); + LOGMASKED(LOG_NETWORK, "eth_r 0x%08x mask 0x%04x data 0x%04x\n", address, mem_mask, data); + + return data; +} + +TIMER_CALLBACK_MEMBER(turquoise_ioga_device::eth_reset) +{ + LOGMASKED(LOG_NETWORK, "eth: reset flag cleared\n"); + + // clear ethernet reset flag + m_eth_control &= ~ETH_RESET; +} + +WRITE32_MEMBER(sapphire_ioga_device::eth_remap_w) +{ + LOGMASKED(LOG_NETWORK, "eth: remap = 0x%08x (%s)\n", data, machine().describe_context()); + + m_eth_remap = data & ~0xf; +} + +WRITE32_MEMBER(sapphire_ioga_device::eth_mappg_w) +{ + LOGMASKED(LOG_NETWORK, "eth: map page = 0x%08x (%s)\n", data, machine().describe_context()); + + m_eth_mappg = data & ~0xf; +} + +READ32_MEMBER(sapphire_ioga_device::eth_control_r) const +{ + LOGMASKED(LOG_NETWORK, "eth: control_r 0x%08x (%s)\n", m_eth_control, machine().describe_context()); + + return m_eth_control; +} + +WRITE32_MEMBER(sapphire_ioga_device::eth_control_w) +{ + LOGMASKED(LOG_NETWORK, "eth: control_w 0x%08x mem_mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + m_eth_control = data & ETH_MASK; + + // ethernet device reset + if ((data & ETH_RESET) == 0) + { + // reset the ethernet device + siblingdevice("eth")->reset(); + + // clear the reset flag (the 250ns delay is long enough to pass diagnostic tests) + m_eth_reset_timer->adjust(attotime::from_nsec(250)); + } + + // ethernet channel attention + if (data & ETH_CA) + { + m_eth_ca_func(ASSERT_LINE); + m_eth_ca_func(CLEAR_LINE); + } +} + +WRITE32_MEMBER(sapphire_ioga_device::eth_w) +{ + // top two bits give channel (0=A, 4=B, 8=C, f=?) + const int channel = offset >> 28; + u32 address = (offset << 2) & 0x3fffffff; + + if ((m_eth_control & ETH_MAPEN) && (address & ETH_MAPPG) == (m_eth_mappg & ETH_MAPPG)) + { + address &= ~(m_eth_mappg & ETH_MAPPG); + address |= (m_eth_remap & ETH_REMAP_ADDR); + + LOGMASKED(LOG_NETWORK, "eth_w address 0x%08x remapped 0x%08x\n", offset << 2, address); + } + + LOGMASKED(LOG_NETWORK, "eth_w channel %c address 0x%08x mask 0x%08x data 0x%08x\n", channel + 'A', address, mem_mask, data); + m_memory_space->write_dword(address, data, mem_mask); +} + +READ32_MEMBER(sapphire_ioga_device::eth_r) const +{ + // top two bits give channel (0=A, 4=B, 8=C, f=?) + const int channel = offset >> 28; + u32 address = (offset << 2) & 0x3fffffff; + + if ((m_eth_control & ETH_MAPEN) && (address & ETH_MAPPG) == (m_eth_mappg & ETH_MAPPG)) + { + address &= ~(m_eth_mappg & ETH_MAPPG); + address |= (m_eth_remap & ETH_REMAP_ADDR); + address &= 0x3fffffff; + + LOGMASKED(LOG_NETWORK, "eth_r address 0x%08x remapped 0x%08x\n", offset << 2, address); + } + + u32 data = m_memory_space->read_dword(address, mem_mask); + LOGMASKED(LOG_NETWORK, "eth_r channel %c address 0x%08x mask 0x%08x data 0x%08x\n", channel + 'A', address, mem_mask, data); + return data; +} + +TIMER_CALLBACK_MEMBER(sapphire_ioga_device::eth_reset) +{ + LOGMASKED(LOG_NETWORK, "eth: reset flag cleared\n"); + + // clear ethernet reset flag + m_eth_control &= ~ETH_RESET; } |