improved timer and interrupt handling

This commit is contained in:
Patrick Mackinlay 2017-01-04 10:48:54 +07:00
parent 4ebd7f9bd8
commit 8f49f5c2ba
2 changed files with 218 additions and 113 deletions

View File

@ -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::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_device>("cpu");
m_fdc = machine().device<upd765_family_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;
}
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;
}

View File

@ -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<class _Object> static devcb_base &static_set_out_nmi_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(device).m_out_nmi_func.set_callback(object); }
template<class _Object> static devcb_base &static_set_out_int_callback(device_t &device, _Object object) { return downcast<interpro_ioga_device &>(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;