diff --git a/src/mame/machine/interpro_ioga.cpp b/src/mame/machine/interpro_ioga.cpp index 0b1282fcb56..a7772ee2007 100644 --- a/src/mame/machine/interpro_ioga.cpp +++ b/src/mame/machine/interpro_ioga.cpp @@ -14,13 +14,28 @@ #define LOG_IOGA(...) #endif +DEVICE_ADDRESS_MAP_START(map, 32, interpro_ioga_device) + AM_RANGE(0x30, 0x3f) AM_READWRITE(fdc_dma_r, fdc_dma_w) + + 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(0x8c, 0x8f) AM_READWRITE(timer0_r, timer0_w) + AM_RANGE(0x90, 0x93) AM_READWRITE(timer1_r, timer1_w) + + AM_RANGE(0xa8, 0xab) AM_READWRITE(timer3_r, timer3_w) +ADDRESS_MAP_END + // InterPro IOGA const device_type INTERPRO_IOGA = &device_creator; interpro_ioga_device::interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, INTERPRO_IOGA, "InterPro IOGA", tag, owner, clock, "ioga", __FILE__), - m_out_int_func(*this), - m_irq_lines(0) + m_out_nmi_func(*this), + m_out_int_func(*this) { } @@ -32,6 +47,15 @@ void interpro_ioga_device::device_start() m_cpu = machine().device("cpu"); m_fdc = machine().device("fdc"); + // 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); + + for (auto & elem : m_timer) + elem->enable(false); + // allocate timer for DMA controller m_dma_timer = timer_alloc(IOGA_TIMER_DMA); m_dma_timer->adjust(attotime::never); @@ -39,9 +63,12 @@ void interpro_ioga_device::device_start() void interpro_ioga_device::device_reset() { - m_irq_lines = 0; m_interrupt = 0; m_state_drq = 0; + + // configure timer 0 at 60Hz + //m_timer_reg[0] = 0; + //m_timer[0]->adjust(attotime::from_hz(60), IOGA_TIMER_0, attotime::from_hz(60)); } void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) @@ -51,16 +78,40 @@ void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, in switch (id) { case IOGA_TIMER_0: - m_timer[0] = 0x80000000; - set_irq_line(14, 1); + m_timer_reg[0]++; + set_irq_line(IOGA_TIMER0_IRQ, ASSERT_LINE); break; + case IOGA_TIMER_1: - m_timer[1] = 0x80000000; - set_irq_line(15, 1); + m_timer_reg[1]--; + + // check if timer has expired + if (m_timer_reg[1] == 0) + { + // disable timer + m_timer[3]->enable(false); + + // set expired flag + m_timer_reg[1] |= IOGA_TIMER1_EXPIRED; + + // throw an interrupt + set_irq_line(IOGA_TIMER1_IRQ, ASSERT_LINE); + } break; + case IOGA_TIMER_3: - m_timer[3] = 0x80000000; - set_irq_line(1, 1); + m_timer_reg[3]--; + if (m_timer_reg[3] == 0) + { + // disable timer + m_timer[3]->enable(false); + + // set expired flag + m_timer_reg[3] |= IOGA_TIMER3_EXPIRED; + + // throw an interrupt + set_irq_line(IOGA_TIMER3_IRQ, ASSERT_LINE); + } break; case IOGA_TIMER_DMA: @@ -68,80 +119,72 @@ void interpro_ioga_device::device_timer(emu_timer &timer, device_timer_id id, in // TODO: vice-versa // TODO: get the dma transfer address and count // TODO: implement multiple dma channels - { - address_space &space = m_cpu->space(AS_PROGRAM); + { + address_space &space = m_cpu->space(AS_PROGRAM); - space.write_byte(m_fdc_dma[0]++, m_fdc->dma_r()); - if (--m_fdc_dma[2]) - m_dma_timer->adjust(attotime::from_usec(10)); - else - m_dma_timer->adjust(attotime::never); + space.write_byte(m_fdc_dma[0]++, m_fdc->dma_r()); + if (--m_fdc_dma[2]) + m_dma_timer->adjust(attotime::from_usec(10)); + else + m_dma_timer->adjust(attotime::never); } - break; + break; } } -DEVICE_ADDRESS_MAP_START(map, 32, interpro_ioga_device) - - AM_RANGE(0x30, 0x3f) AM_READWRITE(fdc_dma_r, fdc_dma_w) - - AM_RANGE(0x5C, 0x81) AM_READWRITE16(icr_r, icr_w, 0xffffffff) - - AM_RANGE(0x8C, 0x8F) AM_READWRITE(timer0_r, timer0_w) - AM_RANGE(0x90, 0x93) AM_READWRITE(timer1_r, timer1_w) - - AM_RANGE(0xA8, 0xAB) AM_READWRITE(timer3_r, timer3_w) -ADDRESS_MAP_END - - /* IOGA 00: ethernet remap 003e0480 // ET_82586_BASE_ADDR or IOGA_ETH_REMAP 04 : ethernet map page 00fff4b0 // ET_82586_CNTL_REG or IOGA_ETH_MAPPG 08 : ethernet control 000004b2 // IOGA_ETH_CTL 0C : plotter real address 00000fff + 10 : plotter virtual address fffffffc 14 : plotter transfer count 003fffff 18 : plotter control ec000001 1C : plotter end - of - scanline counter ffffffff + 20 : SCSI real address 00000000 24 : SCSI virtual address 007e96b8 28 : SCSI transfer count 2C : SCSI control + 30 : floppy real address 34 : floppy virtual address 38 : floppy transfer count 3C : floppy control + 40 : serial address 0 (003ba298) 44 : serial control 0 (01000000) 48 : serial address 1 (ffffffff) 4C : serial control 1 (01200000) + 50 : serial address 2 (ffffffff) 54 : serial control 2 (01200000) -- 16 bit 5A : SIB control(00ff) -5C : internal int 3 (timer 2) 00ff -5E : internal int 4 (timer 3) 00ff +5C : internal int 3 (timer 2) 00ff irq 0 +5E : internal int 4 (timer 3) 00ff irq 1 -60 : external int 0 (SCSI)0a20 -62 : external int 1 (floppy)0621 -64 : external int 2 (plotter)1622 -66 : external int 3 (SRX / CBUS 0) 0a02 -68 : external int 4 (SRX / CBUS 1) 0e24 -6A : external int 5 (SRX / CBUS 2) 0e25 -6C : external int 6 (VB)0c26 -6E : external int 7 0cff -70 : external int 8 (CBUS 3) 0cff -72 : external int 9 (clock / calendar) 0e29 -74 : external int 10 (clock/SGA) 04fe +60 : external int 0 (SCSI)0a20 irq 2 +62 : external int 1 (floppy)0621 irq 3 +64 : external int 2 (plotter)1622 irq 4 +66 : external int 3 (SRX / CBUS 0) 0a02 irq 5 +68 : external int 4 (SRX / CBUS 1) 0e24 irq 6 +6A : external int 5 (SRX / CBUS 2) 0e25 irq 7 +6C : external int 6 (VB)0c26 irq 8 +6E : external int 7 0cff irq 9 +70 : external int 8 (CBUS 3) 0cff irq 10 +72 : external int 9 (clock / calendar) 0e29 irq 11 +74 : external int 10 (clock/SGA) 04fe irq 12 -76 : internal int 0 (mouse)0010 -78 : internal int 1 (timer 0) 0011 - 60Hz -7A : internal int 2 (timer 1) 0212 -7C : internal int 5 (serial DMA) 0e13 -7E : external int 11 (serial) 0a01 -80 : external int 12 (Ethernet)162c // IOGA_EXTINT12 +76 : internal int 0 (mouse)0010 irq 13 +78 : internal int 1 (timer 0) 0011 - 60Hz irq 14 +7A : internal int 2 (timer 1) 0212 irq 15 +7C : internal int 5 (serial DMA) 0e13 irq 16 +7E : external int 11 (serial) 0a01 irq 17 +80 : external int 12 (Ethernet)162c irq 18 // IOGA_EXTINT12 -- 8 bit 82 : soft int 00 @@ -204,36 +247,50 @@ C8 : ethernet address C 4039f088 // IOGA_ETHADDR_C void interpro_ioga_device::set_irq_line(int irq, int state) { - uint32_t mask = (1 << irq); -#define E_INTRC_INTPEND 0x0100 -#define E_INTRC_EXT_IE 0x0200 -#define E_INTRC_EDGE 0x0400 -#define E_INTRC_NEGPOL 0x0800 -#define E_INTRC_INT_IE 0x1000 - - if (m_vectors[irq] & (E_INTRC_EXT_IE | E_INTRC_INT_IE)) + LOG_INTERRUPT("set_irq_line(%d, %d)\n", irq, state); + switch (state) { - if (state) + case ASSERT_LINE: + if (m_vectors[irq] & (IOGA_INTERRUPT_ENABLE_EXTERNAL | IOGA_INTERRUPT_ENABLE_INTERNAL)) { - LOG_INTERRUPT("interpro_ioga_device::set_irq_line(%d, %d)\n", irq, state); + // set interrupt pending bit + m_vectors[irq] |= IOGA_INTERRUPT_PENDING; - m_irq_lines |= mask; m_interrupt = irq; - m_out_int_func(1); + m_out_int_func(ASSERT_LINE); } else - { - m_irq_lines &= ~mask; - m_out_int_func(0); - } + LOG_INTERRUPT("received disabled interrupt irq %d vector 0x%04x\n", irq, m_vectors[irq]); + break; + + case CLEAR_LINE: + // clear interrupt pending bit + m_vectors[irq] &= ~IOGA_INTERRUPT_PENDING; + + m_out_int_func(CLEAR_LINE); + break; } - else - LOG_INTERRUPT("ignoring irq %d vector 0x%04x\n", irq, m_vectors[irq]); } IRQ_CALLBACK_MEMBER(interpro_ioga_device::inta_cb) { - return m_vectors[m_interrupt]; + switch (irqline) + { + case -1: + // return vector for current interrupt without acknowledgement + return m_vectors[m_interrupt] & 0xff; + + case INPUT_LINE_IRQ0: + // acknowledge interrupt + // FIXME: clear IRQ + return m_vectors[m_interrupt] & 0xff; + + case INPUT_LINE_NMI: + //return m_nmictrl; + + default: + return 0; + } } WRITE_LINE_MEMBER(interpro_ioga_device::drq) @@ -247,54 +304,61 @@ WRITE_LINE_MEMBER(interpro_ioga_device::drq) m_state_drq = state; } - -READ32_MEMBER(interpro_ioga_device::read) +void interpro_ioga_device::write_timer(int timer, uint32_t value, device_timer_id id) { - switch (offset) + switch (id) { -#if 0 - case 0x3C: - // 3C: floppy control + case IOGA_TIMER_1: + // disable the timer + m_timer[timer]->enable(false); + + // store the value + m_timer_reg[timer] = value & IOGA_TIMER1_VMASK; + + // start the timer if necessary + if (value & IOGA_TIMER1_START) + m_timer[timer]->adjust(attotime::from_usec(m_prescaler), id, attotime::from_usec(m_prescaler)); break; - case 0x9C: - // 9C: arbiter control 000a + case IOGA_TIMER_3: + // write the value without the top two bits to the register + m_timer_reg[timer] = value & IOGA_TIMER3_VMASK; + + // start the timer if necessary + if (value & IOGA_TIMER3_START) + m_timer[timer]->adjust(attotime::from_hz(IOGA_TIMER3_CLOCK), id, attotime::from_hz(IOGA_TIMER3_CLOCK)); + else + m_timer[timer]->enable(false); break; -#endif default: - LOG_IOGA("ioga read from offset = %08x, mask = %08x, pc = %08x\n", offset, mem_mask, space.device().safe_pc()); - return 0xffffffff; - } -} + // save the value + m_timer_reg[timer] = value; -void interpro_ioga_device::set_timer(int timer, uint32_t value, device_timer_id id) -{ - m_timer[timer] = value; - if (value & 0x40000000) - //timer_set(attotime::from_usec(value & 0x3fffff), id); - timer_set(attotime::from_usec(500), id); + // timer_set(attotime::from_usec(500), id); -} - -WRITE32_MEMBER(interpro_ioga_device::write) -{ - switch (offset) - { - default: - LOG_IOGA("ioga write to offset = 0x%08x, mask = 0x%08x) = 0x%08x, pc = %08x\n", offset, mem_mask, data, space.device().safe_pc()); + logerror("timer %d set to 0x%x (%d)\n", timer, m_timer_reg[timer], m_timer_reg[timer]); break; } } READ16_MEMBER(interpro_ioga_device::icr_r) { - return m_vectors[offset]; + return m_vectors[offset] & ~IOGA_INTERRUPT_FORCED; } WRITE16_MEMBER(interpro_ioga_device::icr_w) { LOG_INTERRUPT("interrupt vector %d set to 0x%04x at pc 0x%08x\n", offset, data, space.device().safe_pc()); - m_vectors[offset] = data; -} \ No newline at end of file + if (data & IOGA_INTERRUPT_PENDING) + m_vectors[offset] = (data | IOGA_INTERRUPT_FORCED) & ~IOGA_INTERRUPT_PENDING; + else if (m_vectors[offset] & IOGA_INTERRUPT_FORCED) + { + m_vectors[offset] = data; + + set_irq_line(offset, ASSERT_LINE); + } + else + m_vectors[offset] = data; +} diff --git a/src/mame/machine/interpro_ioga.h b/src/mame/machine/interpro_ioga.h index 076d1a4ebd0..8b8931412fa 100644 --- a/src/mame/machine/interpro_ioga.h +++ b/src/mame/machine/interpro_ioga.h @@ -9,9 +9,38 @@ #include "emu.h" #include "machine/upd765.h" -#define MCFG_INTERPRO_IOGA_ADD(_tag, _out_int) \ - MCFG_DEVICE_ADD(_tag, INTERPRO_IOGA, 0) \ - devcb = &interpro_ioga_device::static_set_out_int_callback( *device, DEVCB_##_out_int ); +#define MCFG_INTERPRO_IOGA_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, INTERPRO_IOGA, 0) + +#define MCFG_INTERPRO_IOGA_NMI_CB(_out_nmi) \ + devcb = &interpro_ioga_device::static_set_out_int_callback(*device, DEVCB_##_out_nmi); + +#define MCFG_INTERPRO_IOGA_IRQ_CB(_out_int) \ + devcb = &interpro_ioga_device::static_set_out_int_callback(*device, DEVCB_##_out_int); + +// timer 0 seem to be a 60Hz cycle +#define IOGA_TIMER0_IRQ 14 + +// best guess for timer 1 is 10MHz based on typical prescaler value of 1000 and timer value of 100 for a delay of 100ms +#define IOGA_TIMER1_IRQ 15 +#define IOGA_TIMER1_VMASK 0xffff +#define IOGA_TIMER1_START 0x10000 +#define IOGA_TIMER1_EXPIRED 0x20000 + +// best guess for timer 3 is 12.5MHz based on typical value of 12500 for a delay of 1ms +#define IOGA_TIMER3_CLOCK XTAL_12_5MHz +#define IOGA_TIMER3_IRQ 1 +#define IOGA_TIMER3_VMASK 0x3fffffff +#define IOGA_TIMER3_START 0x40000000 +#define IOGA_TIMER3_EXPIRED 0x80000000 + +#define IOGA_INTERRUPT_PENDING 0x0100 +#define IOGA_INTERRUPT_ENABLE_EXTERNAL 0x0200 +#define IOGA_INTERRUPT_EDGE 0x0400 +#define IOGA_INTERRUPT_NEGPOL 0x0800 +#define IOGA_INTERRUPT_ENABLE_INTERNAL 0x1000 +// FIXME: hack for forced interrupts +#define IOGA_INTERRUPT_FORCED 0x8000 class interpro_ioga_device : public device_t { @@ -19,6 +48,7 @@ public: // construction/destruction interpro_ioga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + template static devcb_base &static_set_out_nmi_callback(device_t &device, _Object object) { return downcast(device).m_out_nmi_func.set_callback(object); } template static devcb_base &static_set_out_int_callback(device_t &device, _Object object) { return downcast(device).m_out_int_func.set_callback(object); } virtual DECLARE_ADDRESS_MAP(map, 8); @@ -43,19 +73,27 @@ public: DECLARE_WRITE_LINE_MEMBER(drq); - DECLARE_READ32_MEMBER(read); - DECLARE_WRITE32_MEMBER(write); + DECLARE_READ32_MEMBER(timer_prescaler_r) { return m_prescaler; }; + DECLARE_READ32_MEMBER(timer0_r) { return m_timer_reg[0]; }; + DECLARE_READ32_MEMBER(timer1_r) { return m_timer_reg[1]; }; + DECLARE_READ32_MEMBER(timer2_r) { return m_timer_reg[2]; }; + DECLARE_READ32_MEMBER(timer3_r) { return m_timer_reg[3]; }; - DECLARE_READ32_MEMBER(timer0_r) { return m_timer[0]; }; - DECLARE_READ32_MEMBER(timer1_r) { return m_timer[1]; }; - DECLARE_READ32_MEMBER(timer3_r) { return m_timer[3]; }; - - DECLARE_WRITE32_MEMBER(timer0_w) { set_timer(0, data, IOGA_TIMER_0); } - DECLARE_WRITE32_MEMBER(timer1_w) { set_timer(1, data, IOGA_TIMER_1); } - DECLARE_WRITE32_MEMBER(timer3_w) { set_timer(3, data, IOGA_TIMER_3); } + DECLARE_WRITE32_MEMBER(timer_prescaler_w) { m_prescaler = data; } + DECLARE_WRITE32_MEMBER(timer0_w) { write_timer(0, data, IOGA_TIMER_0); } + DECLARE_WRITE32_MEMBER(timer1_w) { write_timer(1, data, IOGA_TIMER_1); } + DECLARE_WRITE32_MEMBER(timer2_w) { write_timer(2, data, IOGA_TIMER_2); } + DECLARE_WRITE32_MEMBER(timer3_w) { write_timer(3, data, IOGA_TIMER_3); } DECLARE_READ16_MEMBER(icr_r); DECLARE_WRITE16_MEMBER(icr_w); + DECLARE_READ16_MEMBER(icr18_r) { return icr_r(space, 18, mem_mask); }; + DECLARE_WRITE16_MEMBER(icr18_w) { icr_w(space, 18, data, mem_mask); }; + + DECLARE_READ8_MEMBER(softint_r) { return m_softint; } + DECLARE_WRITE8_MEMBER(softint_w) { m_softint = data; } + DECLARE_READ8_MEMBER(nmictrl_r) { return m_nmictrl; } + DECLARE_WRITE8_MEMBER(nmictrl_w) { m_nmictrl = data; } DECLARE_READ32_MEMBER(fdc_dma_r) { return m_fdc_dma[offset]; }; DECLARE_WRITE32_MEMBER(fdc_dma_w) { m_fdc_dma[offset] = data; }; @@ -77,18 +115,21 @@ private: static const device_timer_id IOGA_TIMER_DMA = 4; void set_irq_line(int irq, int state); - void set_timer(int timer, uint32_t value, device_timer_id id); + void write_timer(int timer, uint32_t value, device_timer_id id); + devcb_write_line m_out_nmi_func; devcb_write_line m_out_int_func; // a hack to get hold of the dma devices upd765_family_device *m_fdc; - uint32_t m_irq_lines; uint8_t m_interrupt; uint16_t m_vectors[19]; + uint8_t m_softint, m_nmictrl; - uint32_t m_timer[4]; + uint32_t m_prescaler; + uint32_t m_timer_reg[4]; + emu_timer *m_timer[4]; emu_timer *m_dma_timer; uint32_t m_state_drq;