sun4: Simplified DMA implementation. [Ryan Holtz]

This commit is contained in:
therealmogminer@gmail.com 2016-11-08 13:01:33 +01:00
parent 114aa294cd
commit 2ac8863d14

View File

@ -554,8 +554,7 @@ public:
static const device_timer_id TIMER_0 = 0; static const device_timer_id TIMER_0 = 0;
static const device_timer_id TIMER_1 = 1; static const device_timer_id TIMER_1 = 1;
static const device_timer_id TIMER_DMA = 2; static const device_timer_id TIMER_RESET = 2;
static const device_timer_id TIMER_RESET = 3;
DECLARE_READ32_MEMBER( sun4_mmu_r ); DECLARE_READ32_MEMBER( sun4_mmu_r );
DECLARE_WRITE32_MEMBER( sun4_mmu_w ); DECLARE_WRITE32_MEMBER( sun4_mmu_w );
@ -611,8 +610,9 @@ protected:
uint32_t m_buserr[4]; uint32_t m_buserr[4];
uint32_t m_counter[4]; uint32_t m_counter[4];
uint32_t m_dma[4]; uint32_t m_dma[4];
bool m_dma_tc_read;
uint32_t m_dma_pack_register;
int m_scsi_irq; int m_scsi_irq;
int m_scsi_drq;
private: private:
uint32_t *m_ram_ptr; uint32_t *m_ram_ptr;
@ -629,7 +629,6 @@ private:
int m_arch; int m_arch;
emu_timer *m_c0_timer, *m_c1_timer; emu_timer *m_c0_timer, *m_c1_timer;
emu_timer *m_dma_timer;
emu_timer *m_reset_timer; emu_timer *m_reset_timer;
uint32_t read_insn_data(uint8_t asi, address_space &space, uint32_t offset, uint32_t mem_mask); uint32_t read_insn_data(uint8_t asi, address_space &space, uint32_t offset, uint32_t mem_mask);
@ -637,10 +636,10 @@ private:
uint32_t read_insn_data_4c(uint8_t asi, address_space &space, uint32_t offset, uint32_t mem_mask); uint32_t read_insn_data_4c(uint8_t asi, address_space &space, uint32_t offset, uint32_t mem_mask);
void write_insn_data_4c(uint8_t asi, address_space &space, uint32_t offset, uint32_t data, uint32_t mem_mask); void write_insn_data_4c(uint8_t asi, address_space &space, uint32_t offset, uint32_t data, uint32_t mem_mask);
void dma_set_int_pend(int state); void dma_check_interrupts();
void dma_update_irq(); void dma_transfer();
void dma_tick(); void dma_transfer_write();
void dma_setup_timer(bool continuing); void dma_transfer_read();
void start_timer(int num); void start_timer(int num);
@ -1340,7 +1339,8 @@ void sun4_state::machine_reset()
m_irq_reg = 0; m_irq_reg = 0;
m_scc1_int = m_scc2_int = 0; m_scc1_int = m_scc2_int = 0;
m_scsi_irq = 0; m_scsi_irq = 0;
m_scsi_drq = 0; m_dma_tc_read = false;
m_dma_pack_register = 0;
memset(m_counter, 0, sizeof(m_counter)); memset(m_counter, 0, sizeof(m_counter));
memset(m_dma, 0, sizeof(m_dma)); memset(m_dma, 0, sizeof(m_dma));
@ -1371,10 +1371,6 @@ void sun4_state::machine_start()
// allocate timer for system reset // allocate timer for system reset
m_reset_timer = timer_alloc(TIMER_RESET); m_reset_timer = timer_alloc(TIMER_RESET);
m_reset_timer->adjust(attotime::never); m_reset_timer->adjust(attotime::never);
// allocate timer for DMA controller
m_dma_timer = timer_alloc(TIMER_DMA);
m_dma_timer->adjust(attotime::never);
} }
READ32_MEMBER( sun4_state::ram_r ) READ32_MEMBER( sun4_state::ram_r )
@ -1575,10 +1571,6 @@ void sun4_state::device_timer(emu_timer &timer, device_timer_id id, int param, v
} }
break; break;
case TIMER_DMA:
dma_tick();
break;
case TIMER_RESET: case TIMER_RESET:
m_reset_timer->adjust(attotime::never); m_reset_timer->adjust(attotime::never);
m_maincpu->set_input_line(SPARC_RESET, CLEAR_LINE); m_maincpu->set_input_line(SPARC_RESET, CLEAR_LINE);
@ -1665,22 +1657,15 @@ WRITE32_MEMBER( sun4_state::timer_w )
} }
} }
void sun4_state::dma_set_int_pend(int state) void sun4_state::dma_check_interrupts()
{ {
if (!state) bool tc_interrupt = (m_dma[DMA_CTRL] & DMA_TC) != 0 && !m_dma_tc_read;
{ bool scsi_interrupt = m_scsi_irq != 0;
m_dma[DMA_CTRL] &= ~DMA_INT_PEND;
} m_dma[DMA_CTRL] &= ~DMA_INT_PEND;
else if (tc_interrupt || scsi_interrupt)
{
m_dma[DMA_CTRL] |= DMA_INT_PEND; m_dma[DMA_CTRL] |= DMA_INT_PEND;
}
dma_update_irq();
}
void sun4_state:: dma_update_irq()
{
int irq_or_err_pending = (m_dma[DMA_CTRL] & (DMA_INT_PEND | DMA_ERR_PEND)) ? 1 : 0; int irq_or_err_pending = (m_dma[DMA_CTRL] & (DMA_INT_PEND | DMA_ERR_PEND)) ? 1 : 0;
int irq_enabled = (m_dma[DMA_CTRL] & DMA_INT_EN) ? 1 : 0; int irq_enabled = (m_dma[DMA_CTRL] & DMA_INT_EN) ? 1 : 0;
if (irq_or_err_pending && irq_enabled) if (irq_or_err_pending && irq_enabled)
@ -1693,152 +1678,108 @@ void sun4_state:: dma_update_irq()
} }
} }
void sun4_state::dma_tick() void sun4_state::dma_transfer_write()
{ {
uint32_t transfer_size = (m_dma[DMA_BYTE_COUNT] > PAGE_SIZE ? PAGE_SIZE : m_dma[DMA_BYTE_COUNT]); logerror("DMAing from device to RAM\n");
address_space &space = m_maincpu->space(AS_PROGRAM); address_space &space = m_maincpu->space(AS_PROGRAM);
if (m_dma[DMA_CTRL] & DMA_WRITE) uint8_t pack_cnt = (m_dma[DMA_CTRL] & DMA_PACK_CNT) >> DMA_PACK_CNT_SHIFT;
while (m_dma[DMA_CTRL] & DMA_REQ_PEND)
{ {
uint32_t data = 0; int bit_index = (3 - pack_cnt) * 8;
uint32_t byte_cnt = 3; uint8_t dma_value = m_scsi->dma_r();
uint32_t mem_mask = 0; logerror("Read from device: %02x\n", dma_value);
while (transfer_size > 0) m_dma_pack_register |= dma_value << bit_index;
if ((m_dma[DMA_CTRL] & DMA_EN_CNT) != 0)
m_dma[DMA_BYTE_COUNT]--;
pack_cnt++;
if (pack_cnt == 4)
{ {
uint8_t value = m_scsi->dma_r(); logerror("Writing pack register %08x\n", m_dma_pack_register);
data |= value << (byte_cnt * 8); if (m_arch == ARCH_SUN4C)
mem_mask |= 0xff << (byte_cnt * 8);
transfer_size--;
if (byte_cnt == 0)
{ {
if (m_arch == ARCH_SUN4C) write_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, m_dma_pack_register, ~0);
{
write_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, data, mem_mask);
}
else
{
write_insn_data(11, space, m_dma[DMA_ADDR] >> 2, data, mem_mask);
}
m_dma[DMA_ADDR] += 4;
m_dma[DMA_BYTE_COUNT] -= 4;
byte_cnt = 3;
data = 0;
mem_mask = 0;
} }
else else
{ {
byte_cnt--; write_insn_data(11, space, m_dma[DMA_ADDR] >> 2, m_dma_pack_register, ~0);
} }
}
if (byte_cnt != 0) pack_cnt = 0;
m_dma_pack_register = 0;
}
}
m_dma[DMA_CTRL] &= ~DMA_PACK_CNT;
m_dma[DMA_CTRL] |= pack_cnt << DMA_PACK_CNT_SHIFT;
}
void sun4_state::dma_transfer_read()
{
logerror("DMAing from RAM to device\n");
address_space &space = m_maincpu->space(AS_PROGRAM);
bool word_cached = false;
uint32_t current_word = 0;
while (m_dma[DMA_CTRL] & DMA_REQ_PEND)
{
if (!word_cached)
{ {
if (m_arch == ARCH_SUN4C) if (m_arch == ARCH_SUN4C)
{ {
write_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, data, mem_mask); current_word = read_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, ~0);
} }
else else
{ {
write_insn_data(11, space, m_dma[DMA_ADDR] >> 2, data, mem_mask); current_word = read_insn_data(11, space, m_dma[DMA_ADDR] >> 2, ~0);
}
m_dma[DMA_ADDR] += byte_cnt;
m_dma[DMA_BYTE_COUNT] -= byte_cnt;
}
}
else
{
uint32_t byte_cnt = 3;
uint32_t mem_mask = 0;
while (transfer_size > 0)
{
mem_mask |= 0xff << (byte_cnt * 8);
transfer_size--;
if (byte_cnt == 0)
{
uint32_t data = 0;
if (m_arch == ARCH_SUN4C)
{
data = read_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, mem_mask);
}
else
{
data = read_insn_data(11, space, m_dma[DMA_ADDR] >> 2, mem_mask);
}
for (int i = 24; i >= 0; i -= 8)
{
m_scsi->dma_w((data >> i) & 0xff);
m_dma[DMA_BYTE_COUNT]--;
}
byte_cnt = 3;
mem_mask = 0;
}
else
{
byte_cnt--;
} }
logerror("Current word: %08x\n", current_word);
} }
if (byte_cnt != 0) int bit_index = (3 - (m_dma[DMA_ADDR] & 3)) * 8;
{ uint8_t dma_value(current_word >> bit_index);
byte_cnt++; logerror("Write to device: %02x\n", dma_value);
uint32_t data = 0; m_scsi->dma_w(dma_value);
if (m_arch == ARCH_SUN4C)
{
data = read_insn_data_4c(11, space, m_dma[DMA_ADDR] >> 2, mem_mask);
}
else
{
data = read_insn_data(11, space, m_dma[DMA_ADDR] >> 2, mem_mask);
}
uint32_t initial_cnt = byte_cnt; m_dma[DMA_ADDR]++;
for (int i = 0; i < 4 - initial_cnt; i++) if ((m_dma[DMA_CTRL] & DMA_EN_CNT) != 0)
{ m_dma[DMA_BYTE_COUNT]--;
uint8_t byte_val = (data >> (byte_cnt * 8)) & 0xff;
m_scsi->dma_w(byte_val);
m_dma[DMA_BYTE_COUNT]--; if ((m_dma[DMA_ADDR] & 3) == 3)
byte_cnt--; word_cached = false;
}
}
}
if (m_dma[DMA_BYTE_COUNT] == 0)
{
m_dma[DMA_CTRL] |= DMA_TC;
dma_set_int_pend(1);
m_dma_timer->adjust(attotime::never);
}
else
{
dma_setup_timer(true);
} }
} }
void sun4_state::dma_setup_timer(bool continuing) void sun4_state::dma_transfer()
{ {
const uint32_t remaining_bytes = m_dma[DMA_BYTE_COUNT]; if (m_dma[DMA_CTRL] & DMA_WRITE)
const uint64_t latency = (continuing ? 0 : 40);
const uint64_t transfer_size = (remaining_bytes < PAGE_SIZE ? remaining_bytes : PAGE_SIZE);
const uint64_t transfer_duration = ((transfer_size + 15) / 16) * 8; // 16 byte groupings, 8 clock cycles per group
const uint64_t total_ticks = latency + transfer_duration;
m_dma_timer->adjust(attotime::from_ticks(total_ticks, DMA_XTAL));
if (!continuing)
{ {
m_dma[DMA_CTRL] &= DMA_TC; dma_transfer_write();
}
else
{
dma_transfer_read();
}
if (m_dma[DMA_BYTE_COUNT] == 0 && (m_dma[DMA_CTRL] & DMA_EN_CNT) != 0)
{
m_dma[DMA_CTRL] |= DMA_TC;
m_dma_tc_read = false;
dma_check_interrupts();
} }
} }
READ32_MEMBER( sun4_state::dma_r ) READ32_MEMBER( sun4_state::dma_r )
{ {
if (offset == DMA_CTRL && (m_dma[DMA_CTRL] & DMA_TC) != 0)
{
m_dma_tc_read = true;
dma_check_interrupts();
}
return m_dma[offset]; return m_dma[offset];
} }
@ -1849,33 +1790,40 @@ WRITE32_MEMBER( sun4_state::dma_w )
case DMA_CTRL: case DMA_CTRL:
{ {
// clear write-only bits // clear write-only bits
logerror("dma_w: ctrl: %08x\n", data);
uint32_t old_ctrl = m_dma[DMA_CTRL]; uint32_t old_ctrl = m_dma[DMA_CTRL];
m_dma[DMA_CTRL] &= (DMA_READ_ONLY | DMA_READ_WRITE); m_dma[DMA_CTRL] &= (DMA_WRITE_ONLY | DMA_READ_WRITE);
m_dma[DMA_CTRL] |= (data & (DMA_WRITE_ONLY | DMA_READ_WRITE)); m_dma[DMA_CTRL] |= (data & (DMA_WRITE_ONLY | DMA_READ_WRITE));
if (data & DMA_FLUSH) uint32_t diff = old_ctrl ^ m_dma[DMA_CTRL];
if (diff & DMA_FLUSH)
{ {
m_dma[DMA_CTRL] &= ~DMA_PACK_CNT; m_dma[DMA_CTRL] &= ~DMA_PACK_CNT;
m_dma[DMA_CTRL] &= ~DMA_ERR_PEND; m_dma[DMA_CTRL] &= ~DMA_ERR_PEND;
m_dma[DMA_CTRL] &= ~DMA_TC; m_dma[DMA_CTRL] &= ~DMA_TC;
if (!m_scsi_irq) dma_check_interrupts();
{
dma_set_int_pend(0);
}
} }
if (data & DMA_EN_DMA && !(old_ctrl & DMA_EN_DMA)) if (diff & DMA_EN_DMA)
{ {
dma_setup_timer(false); if ((data & DMA_EN_DMA) != 0 && (m_dma[DMA_CTRL] & DMA_REQ_PEND) != 0)
{
dma_transfer();
}
} }
break; break;
} }
case DMA_ADDR: case DMA_ADDR:
logerror("dma_w: addr: %08x\n", data);
m_dma[offset] = data;
break;
case DMA_BYTE_COUNT: case DMA_BYTE_COUNT:
logerror("dma_w: byte_count: %08x\n", data);
m_dma[offset] = data; m_dma[offset] = data;
break; break;
@ -1886,23 +1834,24 @@ WRITE32_MEMBER( sun4_state::dma_w )
WRITE_LINE_MEMBER( sun4_state::scsi_irq ) WRITE_LINE_MEMBER( sun4_state::scsi_irq )
{ {
if (!(m_dma[DMA_CTRL] & DMA_TC))
{
dma_set_int_pend(state);
}
m_scsi_irq = state; m_scsi_irq = state;
dma_check_interrupts();
} }
WRITE_LINE_MEMBER( sun4_state::scsi_drq ) WRITE_LINE_MEMBER( sun4_state::scsi_drq )
{ {
logerror("scsi_drq %d\n", state);
m_dma[DMA_CTRL] &= ~DMA_REQ_PEND;
if (state) if (state)
{ {
logerror("scsi_drq, DMA pending\n");
m_dma[DMA_CTRL] |= DMA_REQ_PEND;
if (m_dma[DMA_CTRL] & DMA_EN_DMA) if (m_dma[DMA_CTRL] & DMA_EN_DMA)
{ {
dma_setup_timer(false); logerror("DMA enabled, starting dma\n");
dma_transfer();
} }
} }
m_scsi_drq = state;
} }
// indicate 4/60 SCSI/DMA/Ethernet card exists // indicate 4/60 SCSI/DMA/Ethernet card exists