summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/applepic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/applepic.cpp')
-rw-r--r--src/devices/machine/applepic.cpp104
1 files changed, 88 insertions, 16 deletions
diff --git a/src/devices/machine/applepic.cpp b/src/devices/machine/applepic.cpp
index feb122140d4..a523b690e34 100644
--- a/src/devices/machine/applepic.cpp
+++ b/src/devices/machine/applepic.cpp
@@ -19,6 +19,18 @@ DEFINE_DEVICE_TYPE(APPLEPIC, applepic_device, "applepic", "Apple 343S1021 PIC")
const std::string_view applepic_device::s_interrupt_names[8] = { "0", "DMA 1", "DMA 2", "peripheral", "host", "timer", "6", "7" };
+static constexpr int IRQ_DMA1 = 1;
+static constexpr int IRQ_DMA2 = 2;
+static constexpr int IRQ_PERIPHERAL = 3;
+static constexpr int IRQ_HOST = 4;
+static constexpr int IRQ_TIMER = 5;
+
+static constexpr u8 DMAEN = 0x01; // 1 = channel enabled
+static constexpr u8 DREQ = 0x02; // 1 = DREQ line for this channel is active
+static constexpr u8 DMADIR = 0x04; // 1 = I/O to RAM, 0 = RAM to I/O
+static constexpr u8 DENxONx = 0x08; // 1 = enable this channel when the other channel completes a transfer
+static constexpr int DIOASHIFT = 0x04; // right shift amount for I/O offset bits
+
applepic_device::applepic_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, APPLEPIC, tag, owner, clock)
, m_iopcpu(*this, "iopcpu")
@@ -28,6 +40,7 @@ applepic_device::applepic_device(const machine_config &mconfig, const char *tag,
, m_gpin_callback(*this, 0)
, m_gpout_callback(*this)
, m_timer1(nullptr)
+ , m_dma_timer(nullptr)
, m_timer_last_expired(attotime::zero)
, m_ram_address(0)
, m_status_reg(0x80)
@@ -43,6 +56,7 @@ applepic_device::applepic_device(const machine_config &mconfig, const char *tag,
channel.control = 0;
channel.map = 0;
channel.tc = 0;
+ channel.req = false;
}
}
@@ -73,6 +87,9 @@ void applepic_device::device_start()
// Initialize timer
m_timer1 = timer_alloc(FUNC(applepic_device::timer1_callback), this);
+ // get a timer for the DMA
+ m_dma_timer = timer_alloc(FUNC(applepic_device::dma_timer_callback), this);
+
// Save internal state
save_item(NAME(m_timer_last_expired));
save_item(NAME(m_ram_address));
@@ -96,6 +113,13 @@ void applepic_device::device_reset()
m_iopcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
m_iopcpu->set_input_line(r65c02_device::IRQ_LINE, CLEAR_LINE);
m_hint_callback(CLEAR_LINE);
+
+ for (dma_channel &channel : m_dma_channel)
+ {
+ channel.control = 0;
+ }
+
+ m_dma_timer->adjust(attotime::zero, 0, clocks_to_attotime(8));
}
u8 applepic_device::host_r(offs_t offset)
@@ -154,7 +178,7 @@ void applepic_device::host_w(offs_t offset, u8 data)
m_iopcpu->set_input_line(INPUT_LINE_RESET, BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE);
}
if (BIT(data, 3))
- set_interrupt(4);
+ set_interrupt(IRQ_HOST);
if ((m_status_reg & data & 0x30) != 0)
{
m_status_reg &= ~(data & 0x30);
@@ -178,30 +202,40 @@ void applepic_device::pint_w(int state)
{
m_status_reg |= 0x40;
if (!BIT(m_scc_control, 0))
- set_interrupt(3);
+ set_interrupt(IRQ_PERIPHERAL);
}
else
{
m_status_reg &= 0xbf;
if (!BIT(m_scc_control, 0))
- reset_interrupt(3);
+ reset_interrupt(IRQ_PERIPHERAL);
}
}
void applepic_device::reqa_w(int state)
{
- if (state == ASSERT_LINE)
- m_dma_channel[0].control |= 0x02;
+ m_dma_channel[0].req = state;
+ if (state)
+ {
+ m_dma_channel[0].control |= DREQ;
+ }
else
- m_dma_channel[0].control &= 0xfd;
+ {
+ m_dma_channel[0].control &= ~DREQ;
+ }
}
void applepic_device::reqb_w(int state)
{
- if (state == ASSERT_LINE)
- m_dma_channel[1].control |= 0x02;
+ m_dma_channel[1].req = state;
+ if (state)
+ {
+ m_dma_channel[1].control |= DREQ;
+ }
else
- m_dma_channel[1].control &= 0xfd;
+ {
+ m_dma_channel[1].control &= ~DREQ;
+ }
}
u8 applepic_device::timer_r(offs_t offset)
@@ -212,7 +246,7 @@ u8 applepic_device::timer_r(offs_t offset)
else
{
if (offset == 0 && !machine().side_effects_disabled())
- reset_interrupt(5);
+ reset_interrupt(IRQ_TIMER);
return reg & 0x00ff;
}
}
@@ -224,7 +258,7 @@ void applepic_device::timer_w(offs_t offset, u8 data)
m_timer_latch = u16(data) << 8 | (m_timer_latch & 0x00ff);
if (offset == 1)
{
- reset_interrupt(5);
+ reset_interrupt(IRQ_TIMER);
m_timer1->adjust(clocks_to_attotime(m_timer_latch * 8 + 12));
}
}
@@ -242,7 +276,7 @@ u16 applepic_device::get_timer_count() const
TIMER_CALLBACK_MEMBER(applepic_device::timer1_callback)
{
- set_interrupt(5);
+ set_interrupt(IRQ_TIMER);
if (BIT(m_timer_dpll_control, 0))
m_timer1->adjust(clocks_to_attotime((m_timer_latch + 2) * 8));
else
@@ -284,7 +318,7 @@ void applepic_device::dma_channel_w(offs_t offset, u8 data)
switch (offset & 7)
{
case 0:
- channel.control = (data & 0xfd) | (channel.control & 0x02);
+ channel.control = ((data & ~DREQ) | (channel.control & DREQ));
break;
case 1:
@@ -300,7 +334,7 @@ void applepic_device::dma_channel_w(offs_t offset, u8 data)
break;
case 4:
- channel.tc = (data & 0x07) | (channel.tc & 0x0ff);
+ channel.tc = ((data & 0x07) << 8) | (channel.tc & 0x0ff);
break;
default:
@@ -309,6 +343,44 @@ void applepic_device::dma_channel_w(offs_t offset, u8 data)
}
}
+TIMER_CALLBACK_MEMBER(applepic_device::dma_timer_callback)
+{
+ for (int ch = 0; ch < 2; ch++)
+ {
+ auto &channel = m_dma_channel[ch];
+ auto other_channel = m_dma_channel[ch ^ 1];
+
+ if ((channel.control & DMAEN) && (channel.req) && (channel.tc > 0))
+ {
+ if (channel.control & DMADIR)
+ {
+ const u8 xfer = m_prd_callback(channel.control >> DIOASHIFT);
+ m_iopcpu->space(AS_PROGRAM).write_byte(channel.map, xfer);
+ }
+ else
+ {
+ const u8 xfer = m_iopcpu->space(AS_PROGRAM).read_byte(channel.map);
+ m_pwr_callback(channel.control >> DIOASHIFT, xfer);
+ }
+
+ channel.map++;
+ channel.tc--;
+
+ // if this channel completed, handle the DEN1ON2/DEN2ON1 alternating transfer bits and IRQ
+ if (channel.tc == 0)
+ {
+ channel.control &= ~DMAEN;
+ if (other_channel.control & DENxONx)
+ {
+ other_channel.control |= DMAEN;
+ }
+
+ set_interrupt(IRQ_DMA1 + ch);
+ }
+ }
+ }
+}
+
u8 applepic_device::scc_control_r()
{
return m_scc_control;
@@ -318,9 +390,9 @@ void applepic_device::scc_control_w(u8 data)
{
m_scc_control = data;
if (!BIT(data, 0) && BIT(m_status_reg, 6))
- set_interrupt(3);
+ set_interrupt(IRQ_PERIPHERAL);
else
- reset_interrupt(3);
+ reset_interrupt(IRQ_PERIPHERAL);
m_gpout_callback[1](BIT(data, 7));
}