summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author arbee <rb6502@users.noreply.github.com>2017-11-25 13:42:22 -0500
committer arbee <rb6502@users.noreply.github.com>2017-11-25 13:42:22 -0500
commit2bb61a25538285924ad3ebceba2e81bbf39b666f (patch)
tree8ab0f184fee3eb0826ae4ee4565b68783f9360db
parent2d1ca63a21ab2940132166f1a5121682ab60c26e (diff)
nds: Added timers, interrupt management, partial DMA, and ARM7 halt-until-IRQ. [R. Belmont]
-rw-r--r--src/mame/drivers/nds.cpp744
-rw-r--r--src/mame/includes/nds.h54
2 files changed, 777 insertions, 21 deletions
diff --git a/src/mame/drivers/nds.cpp b/src/mame/drivers/nds.cpp
index 7cc1461767b..9480aa0ff34 100644
--- a/src/mame/drivers/nds.cpp
+++ b/src/mame/drivers/nds.cpp
@@ -1,10 +1,16 @@
// license:BSD-3-Clause
-// copyright-holders:Ryan Holtz
+// copyright-holders:Ryan Holtz, R. Belmont
/***************************************************************************
nds.cpp
- Skeleton driver for first-generation Nintendo DS.
+ Preliminary driver for first-generation Nintendo DS.
+
+ Tech info: http://problemkaputt.de/gbatek.htm
+
+ Notes:
+ Timers and DMAs 0-3 are ARM9's, 4-7 are ARM7's.
+ Interrupt registers [0] is ARM9, [1] is ARM7.
***************************************************************************/
@@ -13,6 +19,44 @@
#define VERBOSE_LEVEL (0)
+// Measured value from GBATEK. Actual crystal unknown.
+#define MASTER_CLOCK (33513982)
+
+#define INT_VBL 0x00000001
+#define INT_HBL 0x00000002
+#define INT_VCNT 0x00000004
+#define INT_TM0_OVERFLOW 0x00000008
+#define INT_TM1_OVERFLOW 0x00000010
+#define INT_TM2_OVERFLOW 0x00000020
+#define INT_TM3_OVERFLOW 0x00000040
+#define INT_SIO 0x00000080 // also RCNT/RTC (arm7 only)
+#define INT_DMA0 0x00000100
+#define INT_DMA1 0x00000200
+#define INT_DMA2 0x00000400
+#define INT_DMA3 0x00000800
+#define INT_KEYPAD 0x00001000
+#define INT_GAMEPAK 0x00002000 // GBA slot IRQ line (never used?)
+#define INT_NA1 0x00004000 // unused
+#define INT_NA2 0x00008000 // unused
+#define INT_IPCSYNC 0x00010000
+#define INT_IPCSENDEMPTY 0x00020000
+#define INT_IPCRECVNOTEMPTY 0x00040000
+#define INT_CARDXFERCOMPLETE 0x00080000
+#define INT_CARDIREQ 0x00100000
+#define INT_GEOCMDFIFO 0x00200000 // arm9 only
+#define INT_SCREENUNFOLD 0x00400000 // arm7 only
+#define INT_SPIBUS 0x00800000 // arm7 only
+#define INT_WIFI 0x01000000 // arm7 only - also DSP on DSi
+#define INT_CAMERA 0x02000000 // DSi only
+#define INT_NA3 0x04000000
+#define INT_NA4 0x08000000
+#define INT_NEWDMA0 0x10000000 // DSi only
+#define INT_NEWDMA1 0x20000000 // DSi only
+#define INT_NEWDMA2 0x40000000 // DSi only
+#define INT_NEWDMA3 0x80000000 // DSi only
+
+static const uint32_t timer_clks[4] = { MASTER_CLOCK, MASTER_CLOCK / 64, MASTER_CLOCK / 256, MASTER_CLOCK / 1024 };
+
static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, int n_level, const char *s_fmt, ...)
{
if( VERBOSE_LEVEL >= n_level )
@@ -31,11 +75,106 @@ READ32_MEMBER(nds_state::arm7_io_r)
uint8_t temp1, temp2;
switch(offset)
{
+ case TIMER_OFFSET:
+ case TIMER_OFFSET+1:
+ case TIMER_OFFSET+2:
+ case TIMER_OFFSET+3:
+ {
+ uint32_t elapsed;
+ double time, ticks;
+ int timer = (offset - TIMER_OFFSET) + 4;
+
+ printf("Read timer reg %x (PC=%x)\n", timer, space.device().safe_pc());
+
+ // update times for
+ if (m_timer_regs[timer] & 0x800000)
+ {
+ if (m_timer_regs[timer] & 0x00040000)
+ {
+ elapsed = m_timer_regs[timer] & 0xffff;
+ }
+ else
+ {
+ time = 0.1; //m_tmr_timer[timer]->elapsed().as_double();
+
+ ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));
+
+ // printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);
+
+ time *= ticks;
+ time /= (1.0 / m_timer_hz[timer]);
+
+ elapsed = (uint32_t)time;
+ }
+
+// printf("elapsed = %x\n", elapsed);
+ }
+ else
+ {
+// printf("Reading inactive timer!\n");
+ elapsed = 0;
+ }
+
+ return (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
+ }
+ break;
+
+ case IME_OFFSET:
+ return m_ime[1];
+
+ case IE_OFFSET:
+ return m_ie[1];
+
+ case IF_OFFSET:
+ return m_if[1];
+
case IPCSYNC_OFFSET:
return m_arm7_ipcsync;
+ case AUX_SPI_CNT_OFFSET:
+ printf("arm7: read AUX_SPI_CNT mask %08x\n", mem_mask);
+ return 0;
+ break;
+
case GAMECARD_BUS_CTRL_OFFSET:
+ //printf("arm7: read GAMECARD_BUS_CTRL (%08x) mask %08x\n", m_gamecard_ctrl, mem_mask);
+ return m_gamecard_ctrl;
+ break;
+
+ case GAMECARD_DATA_OFFSET:
+ printf("arm7: read to GAMECARD_DATA mask %08x\n", mem_mask);
+ return 0xffffffff;
+ break;
+
+ case GAMECARD_DATA_2_OFFSET:
+ printf("arm7: read to GAMECARD_DATA2 mask %08x\n", mem_mask);
+ return 0xffffffff;
+ break;
+
+ case GAMECARD_DATA_IN_OFFSET:
+ //printf("arm7: read to GAMECARD_DATA_IN mask %08x (len = %x)\n", mem_mask, m_cartdata_len);
+ if (m_cartdata_len >= 4)
+ {
+ m_cartdata_len -= 4;
+ }
+ else
+ {
+ m_cartdata_len = 0;
+ }
+
+ if (m_cartdata_len == 0)
+ {
+ printf("NDS: xfer over\n");
+ m_gamecard_ctrl &= ~GAMECARD_DATA_READY;
+ m_gamecard_ctrl &= ~GAMECARD_BLOCK_BUSY;
+ }
return 0xffffffff;
+ break;
+
+ case SPI_CTRL_OFFSET:
+ //printf("arm7: read SPI_CTRL mask %08x\n", mem_mask);
+ return 0;
+ break;
case POSTFLG_OFFSET:
/* Bit Use
@@ -60,14 +199,139 @@ WRITE32_MEMBER(nds_state::arm7_io_w)
{
switch(offset)
{
+ case TIMER_OFFSET:
+ case TIMER_OFFSET+1:
+ case TIMER_OFFSET+2:
+ case TIMER_OFFSET+3:
+ {
+ double rate, clocksel;
+ uint32_t old_timer_regs;
+
+ int timer = (offset - TIMER_OFFSET)+4;
+
+ old_timer_regs = m_timer_regs[timer];
+
+ m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));
+
+ printf("%08x to timer %d (mask %08x PC %x)\n", data, timer, ~mem_mask, space.device().safe_pc());
+
+ if (ACCESSING_BITS_0_15)
+ {
+ m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
+ m_timer_recalc[timer] = 1;
+ }
+
+ // enabling this timer?
+ if ((ACCESSING_BITS_16_31) && (data & 0x800000))
+ {
+ double final;
+
+ if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
+ {
+ m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
+ }
+
+ rate = 0x10000 - (m_timer_regs[timer] & 0xffff);
+
+ clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3];
+
+ final = clocksel / rate;
+
+ m_timer_hz[timer] = final;
+
+ m_timer_recalc[timer] = 0;
+
+ printf("Enabling timer %d @ %f Hz regs %08x\n", timer, final, m_timer_regs[timer]);
+
+ // enable the timer
+ if( !(data & 0x40000) ) // if we're not in Count-Up mode
+ {
+ attotime time = attotime::from_hz(final);
+ m_tmr_timer[timer]->adjust(time, timer, time);
+ }
+ }
+ }
+ break;
+
+ case IME_OFFSET:
+ printf("ARM7: %08x to IME\n", data);
+ COMBINE_DATA(&m_ime[1]);
+ break;
+
+ case IE_OFFSET:
+ printf("ARM7: %08x to IE\n", data);
+ COMBINE_DATA(&m_ie[1]);
+ break;
+
+ case IF_OFFSET:
+ COMBINE_DATA(&m_if[1]);
+ break;
+
case IPCSYNC_OFFSET:
- printf("ARM7: %x to IPCSYNC\n", data);
+ //printf("ARM7: %x to IPCSYNC\n", data);
m_arm9_ipcsync &= ~0xf;
m_arm9_ipcsync |= ((data >> 8) & 0xf);
m_arm7_ipcsync &= 0xf;
m_arm7_ipcsync |= (data & ~0xf);
break;
+ case AUX_SPI_CNT_OFFSET:
+ //printf("arm7: %08x to AUX_SPI_CNT mask %08x\n", data, mem_mask);
+ m_spicnt &= 0x0080;
+ m_spicnt |= (data & 0xe043);
+
+ break;
+
+ case GAMECARD_BUS_CTRL_OFFSET:
+ //printf("arm7: %08x to GAMECARD_BUS_CTRL mask %08x\n", data, mem_mask);
+ m_gamecard_ctrl &= GAMECARD_DATA_READY;
+ m_gamecard_ctrl |= (data & ~GAMECARD_DATA_READY);
+
+ if (!(m_spicnt & (1<<15)))
+ {
+ return;
+ }
+
+ if (!(m_gamecard_ctrl & GAMECARD_BLOCK_BUSY))
+ {
+ return;
+ }
+
+ m_cartdata_len = (m_gamecard_ctrl >> 24) & 7;
+ if (m_cartdata_len == 7)
+ {
+ m_cartdata_len = 4;
+ }
+ else if (m_cartdata_len != 0)
+ {
+ m_cartdata_len = 256 << m_cartdata_len;
+ }
+ printf("nds: cartdata for transfer = %x\n", m_cartdata_len);
+
+ if (m_cartdata_len > 0)
+ {
+ m_gamecard_ctrl |= GAMECARD_DATA_READY;
+ }
+ else
+ {
+ printf("NDS: xfer over\n");
+ m_gamecard_ctrl &= ~GAMECARD_DATA_READY;
+ m_gamecard_ctrl &= ~GAMECARD_BLOCK_BUSY;
+ }
+ break;
+
+ case GAMECARD_DATA_OFFSET:
+ //printf("arm7: %08x to GAMECARD_DATA mask %08x\n", data, mem_mask);
+ break;
+
+ case GAMECARD_DATA_2_OFFSET:
+ //printf("arm7: %08x to GAMECARD_DATA2 mask %08x\n", data, mem_mask);
+ break;
+
+ case SPI_CTRL_OFFSET:
+ //printf("arm7: %08x to SPI_CTRL mask %08x\n", data, mem_mask);
+ break;
+
case POSTFLG_OFFSET:
/* Bit Use
* 0 0=Booting, 1=Booted (set by BIOS/firmware)
@@ -77,6 +341,16 @@ WRITE32_MEMBER(nds_state::arm7_io_w)
m_arm7_postflg &= ~POSTFLG_PBF_MASK;
m_arm7_postflg |= data & POSTFLG_PBF_MASK;
}
+
+ if (ACCESSING_BITS_8_15)
+ {
+ if ((data>>8) & 0x80)
+ {
+ printf("arm7: HALT\n"); // halts the arm7 until an interrupt occurs
+ m_arm7->suspend(SUSPEND_REASON_HALT, 1);
+ m_arm7halted = true;
+ }
+ }
break;
default:
verboselog(*this, 0, "[ARM7] [IO] Unknown write: %08x = %08x (%08x)\n", offset*4, data, mem_mask);
@@ -88,6 +362,59 @@ READ32_MEMBER(nds_state::arm9_io_r)
{
switch(offset)
{
+ case TIMER_OFFSET:
+ case TIMER_OFFSET+1:
+ case TIMER_OFFSET+2:
+ case TIMER_OFFSET+3:
+ {
+ uint32_t elapsed;
+ double time, ticks;
+ int timer = (offset - TIMER_OFFSET);
+
+ //printf("Read timer reg %x (PC=%x)\n", timer, space.device().safe_pc());
+
+ // update times for
+ if (m_timer_regs[timer] & 0x800000)
+ {
+ if (m_timer_regs[timer] & 0x00040000)
+ {
+ elapsed = m_timer_regs[timer] & 0xffff;
+ }
+ else
+ {
+ time = 0.1; //m_tmr_timer[timer]->elapsed().as_double();
+
+ ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));
+
+ // printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);
+
+ time *= ticks;
+ time /= (1.0 / m_timer_hz[timer]);
+
+ elapsed = (uint32_t)time;
+ }
+
+// printf("elapsed = %x\n", elapsed);
+ }
+ else
+ {
+// printf("Reading inactive timer!\n");
+ elapsed = 0;
+ }
+
+ return (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
+ }
+ break;
+
+ case IME_OFFSET:
+ return m_ime[0];
+
+ case IE_OFFSET:
+ return m_ie[0];
+
+ case IF_OFFSET:
+ return m_if[0];
+
case IPCSYNC_OFFSET:
return m_arm9_ipcsync;
@@ -109,6 +436,74 @@ WRITE32_MEMBER(nds_state::arm9_io_w)
{
switch(offset)
{
+ case TIMER_OFFSET:
+ case TIMER_OFFSET+1:
+ case TIMER_OFFSET+2:
+ case TIMER_OFFSET+3:
+ {
+ double rate, clocksel;
+ uint32_t old_timer_regs;
+
+ int timer = (offset - TIMER_OFFSET)+4;
+
+ old_timer_regs = m_timer_regs[timer];
+
+ m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));
+
+ printf("%x to timer %d (mask %x PC %x)\n", data, timer, ~mem_mask, space.device().safe_pc());
+
+ if (ACCESSING_BITS_0_15)
+ {
+ m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
+ m_timer_recalc[timer] = 1;
+ }
+
+ // enabling this timer?
+ if ((ACCESSING_BITS_16_31) && (data & 0x800000))
+ {
+ double final;
+
+ if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
+ {
+ m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
+ }
+
+ rate = 0x10000 - (m_timer_regs[timer] & 0xffff);
+
+ clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3];
+
+ final = clocksel / rate;
+
+ m_timer_hz[timer] = final;
+
+ m_timer_recalc[timer] = 0;
+
+ printf("Enabling timer %d @ %f Hz\n", timer, final);
+
+ // enable the timer
+ if( !(data & 0x40000) ) // if we're not in Count-Up mode
+ {
+ attotime time = attotime::from_hz(final);
+ m_tmr_timer[timer]->adjust(time, timer, time);
+ }
+ }
+ }
+ break;
+
+ case IME_OFFSET:
+ printf("ARM9: %08x to IME\n", data);
+ COMBINE_DATA(&m_ime[0]);
+ break;
+
+ case IE_OFFSET:
+ printf("ARM9: %08x to IE\n", data);
+ COMBINE_DATA(&m_ie[0]);
+ break;
+
+ case IF_OFFSET:
+ COMBINE_DATA(&m_if[0]);
+ break;
+
case IPCSYNC_OFFSET:
printf("ARM9: %x to IPCSYNC\n", data);
m_arm7_ipcsync &= ~0xf;
@@ -190,15 +585,15 @@ WRITE32_MEMBER(nds_state::arm9_io_w)
static ADDRESS_MAP_START( nds_arm7_map, AS_PROGRAM, 32, nds_state )
AM_RANGE(0x00000000, 0x00003fff) AM_ROM AM_REGION("arm7", 0)
AM_RANGE(0x02000000, 0x023fffff) AM_RAM AM_MIRROR(0x00400000) AM_SHARE("mainram")
- AM_RANGE(0x03000000, 0x03007fff) AM_DEVICE("nds7wram", address_map_bank_device, amap32) AM_MIRROR(0x007f0000)
+ AM_RANGE(0x03000000, 0x03007fff) AM_MIRROR(0x007f8000) AM_DEVICE("nds7wram", address_map_bank_device, amap32)
AM_RANGE(0x03800000, 0x0380ffff) AM_RAM AM_MIRROR(0x007f0000) AM_SHARE("arm7ram")
- AM_RANGE(0x04000000, 0x0400ffff) AM_READWRITE(arm7_io_r, arm7_io_w)
+ AM_RANGE(0x04000000, 0x0410ffff) AM_READWRITE(arm7_io_r, arm7_io_w)
ADDRESS_MAP_END
static ADDRESS_MAP_START( nds_arm9_map, AS_PROGRAM, 32, nds_state )
AM_RANGE(0x02000000, 0x023fffff) AM_RAM AM_MIRROR(0x00400000) AM_SHARE("mainram")
- AM_RANGE(0x03000000, 0x03007fff) AM_DEVICE("nds9wram", address_map_bank_device, amap32) AM_MIRROR(0x00ff0000)
- AM_RANGE(0x04000000, 0x0400ffff) AM_READWRITE(arm9_io_r, arm9_io_w)
+ AM_RANGE(0x03000000, 0x03007fff) AM_MIRROR(0x00ff8000) AM_DEVICE("nds9wram", address_map_bank_device, amap32)
+ AM_RANGE(0x04000000, 0x0410ffff) AM_READWRITE(arm9_io_r, arm9_io_w)
AM_RANGE(0xffff0000, 0xffff0fff) AM_ROM AM_MIRROR(0x1000) AM_REGION("arm9", 0)
ADDRESS_MAP_END
@@ -232,7 +627,6 @@ WRITE32_MEMBER(nds_state::wram_arm7mirror_w) { COMBINE_DATA(&m_arm7ram[offset]);
static INPUT_PORTS_START( nds )
INPUT_PORTS_END
-
void nds_state::machine_reset()
{
m_arm7_postflg = 0;
@@ -240,17 +634,343 @@ void nds_state::machine_reset()
m_wramcnt = 0;
m_arm7wrambnk->set_bank(0);
m_arm9wrambnk->set_bank(0);
+ m_arm7halted = false;
}
void nds_state::machine_start()
{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ m_dma_timer[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(nds_state::dma_complete),this));
+ m_dma_timer[i]->adjust(attotime::never, i);
+
+ m_tmr_timer[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(nds_state::timer_expire),this));
+ m_tmr_timer[i]->adjust(attotime::never, i);
+ }
+
+ m_irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(nds_state::handle_irq),this));
+ m_irq_timer->adjust(attotime::never);
+}
+
+TIMER_CALLBACK_MEMBER(nds_state::dma_complete)
+{
+ #if 0
+ static const uint32_t ch_int[8] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3, INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };
+
+ uintptr_t ch = param;
+
+// printf("dma complete: ch %d\n", ch);
+
+ m_dma_timer[ch]->adjust(attotime::never);
+
+ int ctrl = DMACNT_H(ch);
+
+ // IRQ
+ if (ctrl & 0x4000)
+ {
+ request_irq(ch_int[ch]);
+ }
+
+ // if we're supposed to repeat, don't clear "active" and then the next vbl/hbl will retrigger us
+ // always clear active for immediate DMAs though
+ if (!((ctrl>>9) & 1) || ((ctrl & 0x3000) == 0))
+ {
+ DMACNT_H_RESET(ch, 0x8000); // clear "active" bit
+ }
+ else
+ {
+ // if repeat, reload the count
+ if ((ctrl>>9) & 1)
+ {
+ m_dma_cnt[ch] = DMACNT_L(ch);
+
+ // if increment & reload mode, reload the destination
+ if (((ctrl>>5)&3) == 3)
+ {
+ m_dma_dst[ch] = DMADAD(ch);
+ }
+ }
+ }
+ #endif
+}
+
+void nds_state::dma_exec(int ch)
+{
+ #if 0
+ address_space &space;
+ uint32_t src = m_dma_src[ch];
+ uint32_t dst = m_dma_dst[ch];
+ uint16_t ctrl = DMACNT_H(ch);
+ int srcadd = (ctrl >> 7) & 3;
+ int dstadd = (ctrl >> 5) & 3;
+
+ if (ch > 4)
+ {
+ space = m_arm7->space(AS_PROGRAM);
+ }
+ else
+ {
+ space = m_arm9->space(AS_PROGRAM);
+ }
+
+ int cnt = m_dma_cnt[ch];
+ if (cnt == 0)
+ {
+ if (ch == 3)
+ cnt = 0x10000;
+ else
+ cnt = 0x4000;
+ }
+
+// if (dst >= 0x6000000 && dst <= 0x6017fff)
+// printf("DMA exec: ch %d from %08x to %08x, mode %04x, count %04x (%s)\n", (int)ch, src, dst, ctrl, cnt, ((ctrl>>10) & 1) ? "32" : "16");
+
+ for (int i = 0; i < cnt; i++)
+ {
+ if ((ctrl>>10) & 1)
+ {
+ src &= 0xfffffffc;
+ dst &= 0xfffffffc;
+
+ // 32-bit
+ space.write_dword(dst, space.read_dword(src));
+ switch (dstadd)
+ {
+ case 0: // increment
+ dst += 4;
+ break;
+ case 1: // decrement
+ dst -= 4;
+ break;
+ case 2: // don't move
+ break;
+ case 3: // increment and reload
+ dst += 4;
+ break;
+ }
+ switch (srcadd)
+ {
+ case 0: // increment
+ src += 4;
+ break;
+ case 1: // decrement
+ src -= 4;
+ break;
+ case 2: // don't move
+ break;
+ case 3: // not used ("Metal Max 2 Kai" expects no increment/decrement)
+ break;
+ }
+ }
+ else
+ {
+ src &= 0xfffffffe;
+ dst &= 0xfffffffe;
+
+ // 16-bit
+ space.write_word(dst, space.read_word(src));
+ switch (dstadd)
+ {
+ case 0: // increment
+ dst += 2;
+ break;
+ case 1: // decrement
+ dst -= 2;
+ break;
+ case 2: // don't move
+ break;
+ case 3: // increment and reload
+ dst += 2;
+ break;
+ }
+ switch (srcadd)
+ {
+ case 0: // increment
+ src += 2;
+ break;
+ case 1: // decrement
+ src -= 2;
+ break;
+ case 2: // don't move
+ break;
+ case 3: // not used (see note in 32-bit version above)
+ break;
+ }
+ }
+ }
+
+ m_dma_src[ch] = src;
+ m_dma_dst[ch] = dst;
+#endif
+// printf("settng DMA timer %d for %d cycs (tmr %x)\n", ch, cnt, (uint32_t)m_dma_timer[ch]);
+// m_dma_timer[ch]->adjust(ATTOTIME_IN_CYCLES(0, cnt), ch);
+ dma_complete(nullptr, ch);
+}
+
+TIMER_CALLBACK_MEMBER(nds_state::handle_irq)
+{
+ request_irq(0, m_if[0]);
+ request_irq(1, m_if[1]);
+
+ m_irq_timer->adjust(attotime::never);
+}
+
+void nds_state::request_irq(int cpu, uint32_t int_type)
+{
+ // set flag for later recovery
+ m_if[cpu] |= int_type;
+
+ printf("request IRQ %08x on CPU %d\n", int_type, cpu);
+
+ // is this specific interrupt enabled?
+ int_type &= m_ie[cpu];
+ if (int_type != 0)
+ {
+ // master enable?
+ if (m_ime[cpu] & 1)
+ {
+ if (cpu == 0)
+ {
+ m_arm9->set_input_line(ARM7_IRQ_LINE, ASSERT_LINE);
+ m_arm9->set_input_line(ARM7_IRQ_LINE, CLEAR_LINE);
+ }
+ else
+ {
+ if (m_arm7halted)
+ {
+ printf("ARM7 unhalting\n");
+ m_arm7->resume(SUSPEND_REASON_HALT);
+ m_arm7halted = false;
+ }
+
+ m_arm7->set_input_line(ARM7_IRQ_LINE, ASSERT_LINE);
+ m_arm7->set_input_line(ARM7_IRQ_LINE, CLEAR_LINE);
+ }
+ }
+ }
+}
+
+TIMER_CALLBACK_MEMBER(nds_state::timer_expire)
+{
+ static const uint32_t tmr_ints[8] = { INT_TM0_OVERFLOW, INT_TM1_OVERFLOW, INT_TM2_OVERFLOW, INT_TM3_OVERFLOW };
+ uintptr_t tmr = (uintptr_t) param;
+ int cpu = (tmr > 4) ? 1 : 0;
+
+ verboselog(*this, 1, "Timer %d expired\n", (int)tmr);
+
+ // "The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows,"
+ // "or when the timer start bit becomes changed from 0 to 1."
+ if (m_timer_recalc[tmr] != 0)
+ {
+ double rate, clocksel, final;
+ attotime time;
+ m_timer_recalc[tmr] = 0;
+ m_timer_regs[tmr] = (m_timer_regs[tmr] & 0xFFFF0000) | (m_timer_reload[tmr] & 0x0000FFFF);
+ rate = 0x10000 - (m_timer_regs[tmr] & 0xffff);
+ clocksel = timer_clks[(m_timer_regs[tmr] >> 16) & 3];
+ final = clocksel / rate;
+ m_timer_hz[tmr] = final;
+ time = attotime::from_hz(final);
+ m_tmr_timer[tmr]->adjust(time, tmr, time);
+ }
+
+ // Handle count-up timing
+ switch (tmr)
+ {
+ case 0:
+ if (m_timer_regs[1] & 0x40000)
+ {
+ m_timer_regs[1] = (( ( m_timer_regs[1] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[1] & 0xffff0000);
+ if( ( m_timer_regs[1] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[1] |= m_timer_reload[1];
+ if( ( m_timer_regs[1] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[1]);
+ }
+ if( ( m_timer_regs[2] & 0x40000 ) )
+ {
+ m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
+ if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[2] |= m_timer_reload[2];
+ if( ( m_timer_regs[2] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[2]);
+ }
+ if( ( m_timer_regs[3] & 0x40000 ) )
+ {
+ m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
+ if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[3] |= m_timer_reload[3];
+ if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[3]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 1:
+ if (m_timer_regs[2] & 0x40000)
+ {
+ m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
+ if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[2] |= m_timer_reload[2];
+ if( ( m_timer_regs[2] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[2]);
+ }
+ if( ( m_timer_regs[3] & 0x40000 ) )
+ {
+ m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
+ if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[3] |= m_timer_reload[3];
+ if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[3]);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 2:
+ if (m_timer_regs[3] & 0x40000)
+ {
+ m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
+ if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
+ {
+ m_timer_regs[3] |= m_timer_reload[3];
+ if( ( m_timer_regs[3] & 0x400000 ) && ( m_ime[cpu] != 0 ) )
+ {
+ request_irq(cpu, tmr_ints[3]);
+ }
+ }
+ }
+ break;
+ }
+
+ // are we supposed to IRQ?
+ if ((m_timer_regs[tmr] & 0x400000) && (m_ime[cpu] != 0))
+ {
+ request_irq(cpu, tmr_ints[tmr & 3]);
+ }
}
static MACHINE_CONFIG_START( nds )
- MCFG_CPU_ADD("arm7", ARM7, XTAL_33_333MHz)
+ MCFG_CPU_ADD("arm7", ARM7, MASTER_CLOCK)
MCFG_CPU_PROGRAM_MAP(nds_arm7_map)
- MCFG_CPU_ADD("arm9", ARM946ES, XTAL_66_6667MHz)
+ MCFG_CPU_ADD("arm9", ARM946ES, MASTER_CLOCK*2)
MCFG_ARM_HIGH_VECTORS()
MCFG_CPU_PROGRAM_MAP(nds_arm9_map)
@@ -280,5 +1000,5 @@ ROM_START( nds )
ROM_LOAD( "firmware.bin", 0x0000, 0x40000, CRC(945f9dc9) SHA1(cfe072921ee3fb93f688743f8beef89043c3e9ad) )
ROM_END
-// YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME FLAGS
-CONS(2004, nds, 0, 0, nds, nds, nds_state, 0, "Nintendo", "DS", MACHINE_IS_SKELETON)
+// YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME FLAGS
+CONS(2004, nds, 0, 0, nds, nds, nds_state, 0, "Nintendo", "DS", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)
diff --git a/src/mame/includes/nds.h b/src/mame/includes/nds.h
index 7152a150b03..c7b3037ed3e 100644
--- a/src/mame/includes/nds.h
+++ b/src/mame/includes/nds.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Ryan Holtz
+// copyright-holders:Ryan Holtz, R. Belmont
#pragma once
#ifndef INCLUDES_NDS_H
#define INCLUDES_NDS_H
@@ -48,25 +48,61 @@ protected:
required_shared_ptr<uint32_t> m_arm7ram;
enum {
- IPCSYNC_OFFSET = 0x180/4,
- GAMECARD_BUS_CTRL_OFFSET = 0x1a4/4,
- WRAMSTAT_OFFSET = 0x241/4,
- VRAMCNT_A_OFFSET = 0x240/4,
- WRAMCNT_OFFSET = 0x244/4,
- VRAMCNT_H_OFFSET = 0x248/4,
- POSTFLG_OFFSET = 0x300/4,
+ TIMER_OFFSET = (0x100/4),
+ RTC_OFFSET = (0x138/4),
+ IPCSYNC_OFFSET = (0x180/4),
+ AUX_SPI_CNT_OFFSET = (0x1a0/4),
+ GAMECARD_BUS_CTRL_OFFSET = (0x1a4/4),
+ GAMECARD_DATA_OFFSET = (0x1a8/4),
+ GAMECARD_DATA_2_OFFSET = (0x1ac/4),
+ SPI_CTRL_OFFSET = (0x1c0/4),
+ IME_OFFSET = (0x208/4),
+ IE_OFFSET = (0x210/4),
+ IF_OFFSET = (0x214/4),
+ WRAMSTAT_OFFSET = (0x241/4),
+ VRAMCNT_A_OFFSET = (0x240/4),
+ WRAMCNT_OFFSET = (0x244/4),
+ VRAMCNT_H_OFFSET = (0x248/4),
+ POSTFLG_OFFSET = (0x300/4),
+ GAMECARD_DATA_IN_OFFSET = (0x100010/4),
POSTFLG_PBF_SHIFT = 0,
POSTFLG_RAM_SHIFT = 1,
POSTFLG_PBF_MASK = (1 << POSTFLG_PBF_SHIFT),
POSTFLG_RAM_MASK = (1 << POSTFLG_RAM_SHIFT),
+ GAMECARD_DATA_READY = (1 << 23),
+ GAMECARD_BLOCK_BUSY = (1 << 31)
};
uint32_t m_arm7_postflg;
uint32_t m_arm9_postflg;
- uint16_t m_arm7_ipcsync, m_arm9_ipcsync;
+ uint32_t m_gamecard_ctrl, m_cartdata_len;
+ uint32_t m_ime[2], m_ie[2], m_if[2];
+ uint16_t m_arm7_ipcsync, m_arm9_ipcsync, m_spicnt;
uint8_t m_WRAM[0x8000];
uint8_t m_wramcnt;
uint8_t m_vramcnta, m_vramcntb, m_vramcntc, m_vramcntd, m_vramcnte, m_vramcntf, m_vramcntg, m_vramcnth, m_vramcnti;
+ bool m_arm7halted;
+
+ // DMA
+ emu_timer *m_dma_timer[8];
+ uint32_t m_dma_src[8];
+ uint32_t m_dma_dst[8];
+ uint16_t m_dma_cnt[8];
+
+ // Timers
+ uint32_t m_timer_regs[8];
+ uint16_t m_timer_reload[8];
+ int m_timer_recalc[8];
+ double m_timer_hz[8];
+
+ emu_timer *m_tmr_timer[8], *m_irq_timer;
+
+ TIMER_CALLBACK_MEMBER(dma_complete);
+ TIMER_CALLBACK_MEMBER(timer_expire);
+ TIMER_CALLBACK_MEMBER(handle_irq);
+
+ void request_irq(int which_cpu, uint32_t int_type);
+ void dma_exec(int ch);
};
#endif // INCLUDES_NDS_H