From 2bb61a25538285924ad3ebceba2e81bbf39b666f Mon Sep 17 00:00:00 2001 From: arbee Date: Sat, 25 Nov 2017 13:42:22 -0500 Subject: nds: Added timers, interrupt management, partial DMA, and ARM7 halt-until-IRQ. [R. Belmont] --- src/mame/drivers/nds.cpp | 744 ++++++++++++++++++++++++++++++++++++++++++++++- src/mame/includes/nds.h | 54 +++- 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 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 -- cgit v1.2.3