diff src/xsf/desmume/NDSSystem.c @ 2961:70b0973e7b70

Merge xsf plugin.
author William Pitcock <nenolod@atheme.org>
date Thu, 16 Oct 2008 14:45:41 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/xsf/desmume/NDSSystem.c	Thu Oct 16 14:45:41 2008 -0500
@@ -0,0 +1,748 @@
+/*  Copyright (C) 2006 yopyop
+    yopyop156@ifrance.com
+    yopyop156.ifrance.com
+
+    This file is part of DeSmuME
+
+    DeSmuME is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DeSmuME is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DeSmuME; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "NDSSystem.h"
+#include "MMU.h"
+//#include "cflash.h"
+
+//#include "ROMReader.h"
+
+/* the count of bytes copied from the firmware into memory */
+#define NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT 0x70
+
+NDSSystem nds;
+
+static u32
+calc_CRC16( u32 start, const u8 *data, int count) {
+  int i,j;
+  u32 crc = start & 0xffff;
+  static u16 val[] = { 0xC0C1,0xC181,0xC301,0xC601,0xCC01,0xD801,0xF001,0xA001 };
+  for(i = 0; i < count; i++)
+  {
+    crc = crc ^ data[i];
+
+    for(j = 0; j < 8; j++) {
+      int do_bit = 0;
+
+      if ( crc & 0x1)
+        do_bit = 1;
+
+      crc = crc >> 1;
+
+      if ( do_bit) {
+        crc = crc ^ (val[j] << (7-j));
+      }
+    }
+  }
+  return crc;
+}
+
+static int
+copy_firmware_user_data( u8 *dest_buffer, const u8 *fw_data) {
+  /*
+   * Determine which of the two user settings in the firmware is the current
+   * and valid one and then copy this into the destination buffer.
+   *
+   * The current setting will have a greater count.
+   * Settings are only valid if its CRC16 is correct.
+   */
+  int user1_valid = 0;
+  int user2_valid = 0;
+  u32 user_settings_offset;
+  u32 fw_crc;
+  u32 crc;
+  int copy_good = 0;
+
+  user_settings_offset = fw_data[0x20];
+  user_settings_offset |= fw_data[0x21] << 8;
+  user_settings_offset <<= 3;
+
+  if ( user_settings_offset <= 0x3FE00) {
+    s32 copy_settings_offset = -1;
+
+    crc = calc_CRC16( 0xffff, &fw_data[user_settings_offset],
+                      NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT);
+    fw_crc = fw_data[user_settings_offset + 0x72];
+    fw_crc |= fw_data[user_settings_offset + 0x73] << 8;
+    if ( crc == fw_crc) {
+      user1_valid = 1;
+    }
+
+    crc = calc_CRC16( 0xffff, &fw_data[user_settings_offset + 0x100],
+                      NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT);
+    fw_crc = fw_data[user_settings_offset + 0x100 + 0x72];
+    fw_crc |= fw_data[user_settings_offset + 0x100 + 0x73] << 8;
+    if ( crc == fw_crc) {
+      user2_valid = 1;
+    }
+
+    if ( user1_valid) {
+      if ( user2_valid) {
+        u16 count1, count2;
+
+        count1 = fw_data[user_settings_offset + 0x70];
+        count1 |= fw_data[user_settings_offset + 0x71] << 8;
+
+        count2 = fw_data[user_settings_offset + 0x100 + 0x70];
+        count2 |= fw_data[user_settings_offset + 0x100 + 0x71] << 8;
+
+        if ( count2 > count1) {
+          copy_settings_offset = user_settings_offset + 0x100;
+        }
+        else {
+          copy_settings_offset = user_settings_offset;
+        }
+      }
+      else {
+        copy_settings_offset = user_settings_offset;
+      }
+    }
+    else if ( user2_valid) {
+      /* copy the second user settings */
+      copy_settings_offset = user_settings_offset + 0x100;
+    }
+
+    if ( copy_settings_offset > 0) {
+      memcpy( dest_buffer, &fw_data[copy_settings_offset],
+              NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT);
+      copy_good = 1;
+    }
+  }
+
+  return copy_good;
+}
+
+
+#ifdef GDB_STUB
+int NDS_Init( struct armcpu_memory_iface *arm9_mem_if,
+              struct armcpu_ctrl_iface **arm9_ctrl_iface,
+              struct armcpu_memory_iface *arm7_mem_if,
+              struct armcpu_ctrl_iface **arm7_ctrl_iface) {
+#else
+int NDS_Init( void) {
+#endif
+     nds.ARM9Cycle = 0;
+     nds.ARM7Cycle = 0;
+     nds.cycles = 0;
+     MMU_Init();
+     nds.nextHBlank = 3168;
+     nds.VCount = 0;
+     nds.lignerendu = FALSE;
+
+     if (Screen_Init(GFXCORE_DUMMY) != 0)
+        return -1;
+     
+ #ifdef GDB_STUB
+     armcpu_new(&NDS_ARM7,1, arm7_mem_if, arm7_ctrl_iface);
+     armcpu_new(&NDS_ARM9,0, arm9_mem_if, arm9_ctrl_iface);
+#else
+	 armcpu_new(&NDS_ARM7,1);
+     armcpu_new(&NDS_ARM9,0);
+#endif
+
+     if (SPU_Init(SNDCORE_DUMMY, 735) != 0)
+        return -1;
+
+#ifdef EXPERIMENTAL_WIFI
+	 WIFI_Init(&wifiMac) ;
+#endif
+
+     return 0;
+}
+
+static void armcpu_deinit(armcpu_t *armcpu)
+{
+	if(armcpu->coproc[15])
+	{
+		free(armcpu->coproc[15]);
+		armcpu->coproc[15] = 0;
+	}
+}
+
+void NDS_DeInit(void) {
+     if(MMU.CART_ROM != MMU.UNUSED_RAM)
+        NDS_FreeROM();
+
+	armcpu_deinit(&NDS_ARM7);
+	armcpu_deinit(&NDS_ARM9);
+
+     nds.nextHBlank = 3168;
+     SPU_DeInit();
+     Screen_DeInit();
+     MMU_DeInit();
+}
+
+BOOL NDS_SetROM(u8 * rom, u32 mask)
+{
+     MMU_setRom(rom, mask);
+
+     return TRUE;
+} 
+
+NDS_header * NDS_getROMHeader(void)
+{
+	NDS_header * header = malloc(sizeof(NDS_header));
+
+	memcpy(header->gameTile, MMU.CART_ROM, 12);
+	memcpy(header->gameCode, MMU.CART_ROM + 12, 4);
+	header->makerCode = T1ReadWord(MMU.CART_ROM, 16);
+	header->unitCode = MMU.CART_ROM[18];
+	header->deviceCode = MMU.CART_ROM[19];
+	header->cardSize = MMU.CART_ROM[20];
+	memcpy(header->cardInfo, MMU.CART_ROM + 21, 8);
+	header->flags = MMU.CART_ROM[29];
+	header->ARM9src = T1ReadLong(MMU.CART_ROM, 32);
+	header->ARM9exe = T1ReadLong(MMU.CART_ROM, 36);
+	header->ARM9cpy = T1ReadLong(MMU.CART_ROM, 40);
+	header->ARM9binSize = T1ReadLong(MMU.CART_ROM, 44);
+	header->ARM7src = T1ReadLong(MMU.CART_ROM, 48);
+	header->ARM7exe = T1ReadLong(MMU.CART_ROM, 52);
+	header->ARM7cpy = T1ReadLong(MMU.CART_ROM, 56);
+	header->ARM7binSize = T1ReadLong(MMU.CART_ROM, 60);
+	header->FNameTblOff = T1ReadLong(MMU.CART_ROM, 64);
+	header->FNameTblSize = T1ReadLong(MMU.CART_ROM, 68);
+	header->FATOff = T1ReadLong(MMU.CART_ROM, 72);
+	header->FATSize = T1ReadLong(MMU.CART_ROM, 76);
+	header->ARM9OverlayOff = T1ReadLong(MMU.CART_ROM, 80);
+	header->ARM9OverlaySize = T1ReadLong(MMU.CART_ROM, 84);
+	header->ARM7OverlayOff = T1ReadLong(MMU.CART_ROM, 88);
+	header->ARM7OverlaySize = T1ReadLong(MMU.CART_ROM, 92);
+	header->unknown2a = T1ReadLong(MMU.CART_ROM, 96);
+	header->unknown2b = T1ReadLong(MMU.CART_ROM, 100);
+	header->IconOff = T1ReadLong(MMU.CART_ROM, 104);
+	header->CRC16 = T1ReadWord(MMU.CART_ROM, 108);
+	header->ROMtimeout = T1ReadWord(MMU.CART_ROM, 110);
+	header->ARM9unk = T1ReadLong(MMU.CART_ROM, 112);
+	header->ARM7unk = T1ReadLong(MMU.CART_ROM, 116);
+	memcpy(header->unknown3c, MMU.CART_ROM + 120, 8);
+	header->ROMSize = T1ReadLong(MMU.CART_ROM, 128);
+	header->HeaderSize = T1ReadLong(MMU.CART_ROM, 132);
+	memcpy(header->unknown5, MMU.CART_ROM + 136, 56);
+	memcpy(header->logo, MMU.CART_ROM + 192, 156);
+	header->logoCRC16 = T1ReadWord(MMU.CART_ROM, 348);
+	header->headerCRC16 = T1ReadWord(MMU.CART_ROM, 350);
+	memcpy(header->reserved, MMU.CART_ROM + 352, 160);
+
+	return header;
+
+     //return (NDS_header *)MMU.CART_ROM;
+} 
+
+
+
+void NDS_FreeROM(void)
+{
+   if (MMU.CART_ROM != MMU.UNUSED_RAM)
+      free(MMU.CART_ROM);
+   MMU_unsetRom();
+//   if (MMU.bupmem.fp)
+//      fclose(MMU.bupmem.fp);
+//   MMU.bupmem.fp = NULL;
+}
+
+                         
+
+void NDS_Reset( void)
+{
+   BOOL oldexecute=execute;
+   int i;
+   u32 src;
+   u32 dst;
+   NDS_header * header = NDS_getROMHeader();
+
+	if (!header) return ;
+
+   execute = FALSE;
+
+   MMU_clearMem();
+
+   src = header->ARM9src;
+   dst = header->ARM9cpy;
+
+   for(i = 0; i < (header->ARM9binSize>>2); ++i)
+   {
+      MMU_write32(0, dst, T1ReadLong(MMU.CART_ROM, src));
+      dst += 4;
+      src += 4;
+   }
+
+   src = header->ARM7src;
+   dst = header->ARM7cpy;
+     
+   for(i = 0; i < (header->ARM7binSize>>2); ++i)
+   {
+      MMU_write32(1, dst, T1ReadLong(MMU.CART_ROM, src));
+      dst += 4;
+      src += 4;
+   }
+
+   armcpu_init(&NDS_ARM7, header->ARM7exe);
+   armcpu_init(&NDS_ARM9, header->ARM9exe);
+     
+   nds.ARM9Cycle = 0;
+   nds.ARM7Cycle = 0;
+   nds.cycles = 0;
+   memset(nds.timerCycle, 0, sizeof(s32) * 2 * 4);
+   memset(nds.timerOver, 0, sizeof(BOOL) * 2 * 4);
+   nds.nextHBlank = 3168;
+   nds.VCount = 0;
+   nds.old = 0;
+   nds.diff = 0;
+   nds.lignerendu = FALSE;
+   nds.touchX = nds.touchY = 0;
+
+   MMU_write16(0, 0x04000130, 0x3FF);
+   MMU_write16(1, 0x04000130, 0x3FF);
+   MMU_write8(1, 0x04000136, 0x43);
+
+   /*
+    * Setup a copy of the firmware user settings in memory.
+    * (this is what the DS firmware would do).
+    */
+   {
+     u8 temp_buffer[NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT];
+     int fw_index;
+
+     if ( copy_firmware_user_data( temp_buffer, MMU.fw.data)) {
+       for ( fw_index = 0; fw_index < NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT; fw_index++) {
+         MMU_write8( 0, 0x027FFC80 + fw_index, temp_buffer[fw_index]);
+       }
+     }
+   }
+
+	// Copy the whole header to Main RAM 0x27FFE00 on startup.
+	//  Reference: http://nocash.emubase.de/gbatek.htm#dscartridgeheader
+	for (i = 0; i < ((0x170+0x90)/4); i++)
+	{
+		MMU_write32 (0, 0x027FFE00+i*4, LE_TO_LOCAL_32(((u32*)MMU.CART_ROM)[i]));
+	}
+     
+   MainScreen.offset = 0;
+   SubScreen.offset = 192;
+     
+   //MMU_write32(0, 0x02007FFC, 0xE92D4030);
+
+     //ARM7 BIOS IRQ HANDLER
+     MMU_write32(1, 0x00, 0xE25EF002);
+     MMU_write32(1, 0x04, 0xEAFFFFFE);
+     MMU_write32(1, 0x18, 0xEA000000);
+     MMU_write32(1, 0x20, 0xE92D500F);
+     MMU_write32(1, 0x24, 0xE3A00301);
+     MMU_write32(1, 0x28, 0xE28FE000);
+     MMU_write32(1, 0x2C, 0xE510F004);
+     MMU_write32(1, 0x30, 0xE8BD500F);
+     MMU_write32(1, 0x34, 0xE25EF004);
+    
+     //ARM9 BIOS IRQ HANDLER
+     MMU_write32(0, 0xFFFF0018, 0xEA000000);
+     MMU_write32(0, 0xFFFF0020, 0xE92D500F);
+     MMU_write32(0, 0xFFFF0024, 0xEE190F11);
+     MMU_write32(0, 0xFFFF0028, 0xE1A00620);
+     MMU_write32(0, 0xFFFF002C, 0xE1A00600);
+     MMU_write32(0, 0xFFFF0030, 0xE2800C40);
+     MMU_write32(0, 0xFFFF0034, 0xE28FE000);
+     MMU_write32(0, 0xFFFF0038, 0xE510F004);
+     MMU_write32(0, 0xFFFF003C, 0xE8BD500F);
+     MMU_write32(0, 0xFFFF0040, 0xE25EF004);
+        
+     MMU_write32(0, 0x0000004, 0xE3A0010E);
+     MMU_write32(0, 0x0000008, 0xE3A01020);
+//     MMU_write32(0, 0x000000C, 0xE1B02110);
+     MMU_write32(0, 0x000000C, 0xE1B02040);
+     MMU_write32(0, 0x0000010, 0xE3B02020);
+//     MMU_write32(0, 0x0000010, 0xE2100202);
+
+   free(header);
+
+   GPU_Reset(MainScreen.gpu, 0);
+   GPU_Reset(SubScreen.gpu, 1);
+   SPU_Reset();
+
+   execute = oldexecute;
+}
+
+static void dma_check(void)
+{
+	if((MMU.DMAing[0][0])&&(MMU.DMACycle[0][0]<=nds.cycles))
+	{
+		T1WriteLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*0), T1ReadLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*0)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[0][0])&(1<<30)) NDS_makeARM9Int(8);
+		MMU.DMAing[0][0] = FALSE;
+	}
+
+	if((MMU.DMAing[0][1])&&(MMU.DMACycle[0][1]<=nds.cycles))
+	{
+		T1WriteLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*1), T1ReadLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*1)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[0][1])&(1<<30)) NDS_makeARM9Int(9);
+		MMU.DMAing[0][1] = FALSE;
+	}
+
+	if((MMU.DMAing[0][2])&&(MMU.DMACycle[0][2]<=nds.cycles))
+	{
+		T1WriteLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*2), T1ReadLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*2)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[0][2])&(1<<30)) NDS_makeARM9Int(10);
+		MMU.DMAing[0][2] = FALSE;
+	}
+
+	if((MMU.DMAing[0][3])&&(MMU.DMACycle[0][3]<=nds.cycles))
+	{
+		T1WriteLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*3), T1ReadLong(ARM9Mem.ARM9_REG, 0xB8 + (0xC*3)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[0][3])&(1<<30)) NDS_makeARM9Int(11);
+		MMU.DMAing[0][3] = FALSE;
+	}
+
+	if((MMU.DMAing[1][0])&&(MMU.DMACycle[1][0]<=nds.cycles))
+	{
+		T1WriteLong(MMU.ARM7_REG, 0xB8 + (0xC*0), T1ReadLong(MMU.ARM7_REG, 0xB8 + (0xC*0)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[1][0])&(1<<30)) NDS_makeARM7Int(8);
+		MMU.DMAing[1][0] = FALSE;
+	}
+
+	if((MMU.DMAing[1][1])&&(MMU.DMACycle[1][1]<=nds.cycles))
+	{
+		T1WriteLong(MMU.ARM7_REG, 0xB8 + (0xC*1), T1ReadLong(MMU.ARM7_REG, 0xB8 + (0xC*1)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[1][1])&(1<<30)) NDS_makeARM7Int(9);
+		MMU.DMAing[1][1] = FALSE;
+	}
+
+	if((MMU.DMAing[1][2])&&(MMU.DMACycle[1][2]<=nds.cycles))
+	{
+		T1WriteLong(MMU.ARM7_REG, 0xB8 + (0xC*2), T1ReadLong(MMU.ARM7_REG, 0xB8 + (0xC*2)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[1][2])&(1<<30)) NDS_makeARM7Int(10);
+		MMU.DMAing[1][2] = FALSE;
+	}
+
+	if((MMU.DMAing[1][3])&&(MMU.DMACycle[1][3]<=nds.cycles))
+	{
+		T1WriteLong(MMU.ARM7_REG, 0xB8 + (0xC*3), T1ReadLong(MMU.ARM7_REG, 0xB8 + (0xC*3)) & 0x7FFFFFFF);
+		if((MMU.DMACrt[1][3])&(1<<30)) NDS_makeARM7Int(11);
+		MMU.DMAing[1][3] = FALSE;
+	}
+
+	if((MMU.reg_IF[0]&MMU.reg_IE[0]) && (MMU.reg_IME[0]))
+	{
+#ifdef GDB_STUB
+		if ( armcpu_flagIrq( &NDS_ARM9)) 
+#else
+		if ( armcpu_irqExeption(&NDS_ARM9))
+#endif
+		{
+			nds.ARM9Cycle = nds.cycles;
+		}
+	}
+
+	if((MMU.reg_IF[1]&MMU.reg_IE[1]) && (MMU.reg_IME[1]))
+	{
+#ifdef GDB_STUB
+		if ( armcpu_flagIrq( &NDS_ARM7)) 
+#else
+		if ( armcpu_irqExeption(&NDS_ARM7))
+#endif
+		{
+			nds.ARM7Cycle = nds.cycles;
+		}
+	}
+
+}
+
+static void timer_check(void)
+{
+	int p, t;
+	for (p = 0; p < 2; p++)
+	{
+		for (t = 0; t < 4; t++)
+		{
+			nds.timerOver[p][t] = 0;
+			if(MMU.timerON[p][t])
+			{
+				if(MMU.timerRUN[p][t])
+				{
+					switch(MMU.timerMODE[p][t])
+					{
+					case 0xFFFF :
+						if(t > 0 && nds.timerOver[p][t - 1])
+						{
+							++(MMU.timer[p][t]);
+							nds.timerOver[p][t] = !MMU.timer[p][t];
+							if (nds.timerOver[p][t])
+							{
+								if (p == 0)
+								{
+									if(T1ReadWord(ARM9Mem.ARM9_REG, 0x102 + (t << 2)) & 0x40)
+										NDS_makeARM9Int(3 + t);
+								}
+								else
+								{
+									if(T1ReadWord(MMU.ARM7_REG, 0x102 + (t << 2)) & 0x40)
+										NDS_makeARM7Int(3 + t);
+								}
+								MMU.timer[p][t] = MMU.timerReload[p][t];
+							}
+						}
+						break;
+					default :
+						{
+							nds.diff = (nds.cycles >> MMU.timerMODE[p][t]) - (nds.timerCycle[p][t] >> MMU.timerMODE[p][t]);
+							nds.old = MMU.timer[p][t];
+							MMU.timer[p][t] += nds.diff;
+							nds.timerCycle[p][t] += nds.diff << MMU.timerMODE[p][t];
+							nds.timerOver[p][t] = nds.old >= MMU.timer[p][t];
+							if(nds.timerOver[p][t])
+							{
+								if (p == 0)
+								{
+									if(T1ReadWord(ARM9Mem.ARM9_REG, 0x102 + (t << 2)) & 0x40)
+										NDS_makeARM9Int(3 + t);
+								}
+								else
+								{
+									if(T1ReadWord(MMU.ARM7_REG, 0x102 + (t << 2)) & 0x40)
+										NDS_makeARM7Int(3 + t);
+								}
+								MMU.timer[p][t] = MMU.timerReload[p][t] + MMU.timer[p][t] - nds.old;
+							}
+						}
+						break;
+					}
+				}
+				else
+				{
+					MMU.timerRUN[p][t] = TRUE;
+					nds.timerCycle[p][t] = nds.cycles;
+				}
+			}
+		}
+	}
+}
+
+void NDS_exec_hframe(int cpu_clockdown_level_arm9, int cpu_clockdown_level_arm7)
+{
+	int h;
+	for (h = 0; h < 2; h++)
+	{
+		s32 nb = nds.cycles + (h ? (99 * 12) : (256 * 12));
+
+		while (nb > nds.ARM9Cycle && !NDS_ARM9.waitIRQ)
+			nds.ARM9Cycle += armcpu_exec(&NDS_ARM9) << (cpu_clockdown_level_arm9);
+		if (NDS_ARM9.waitIRQ) nds.ARM9Cycle = nb;
+		while (nb > nds.ARM7Cycle && !NDS_ARM7.waitIRQ)
+			nds.ARM7Cycle += armcpu_exec(&NDS_ARM7) << (1 + (cpu_clockdown_level_arm7));
+		if (NDS_ARM7.waitIRQ) nds.ARM7Cycle = nb;
+		nds.cycles = (nds.ARM9Cycle<nds.ARM7Cycle)?nds.ARM9Cycle : nds.ARM7Cycle;
+
+		/* HBLANK */
+		if (h)
+		{
+			T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) | 2);
+			T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 2);
+			NDS_ARM9HBlankInt();
+			NDS_ARM7HBlankInt();
+
+			if(nds.VCount<192)
+			{
+				if(MMU.DMAStartTime[0][0] == 2)
+					MMU_doDMA(0, 0);
+				if(MMU.DMAStartTime[0][1] == 2)
+					MMU_doDMA(0, 1);
+				if(MMU.DMAStartTime[0][2] == 2)
+					MMU_doDMA(0, 2);
+				if(MMU.DMAStartTime[0][3] == 2)
+					MMU_doDMA(0, 3);
+			}
+		}
+		else
+		{
+			/* HDISP */
+			u32 vmatch;
+
+			nds.nextHBlank += 4260;
+			++nds.VCount;
+			T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) & 0xFFFD);
+			T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFD);
+
+			if(MMU.DMAStartTime[0][0] == 3)
+				MMU_doDMA(0, 0);
+			if(MMU.DMAStartTime[0][1] == 3)
+				MMU_doDMA(0, 1);
+			if(MMU.DMAStartTime[0][2] == 3)
+				MMU_doDMA(0, 2);
+			if(MMU.DMAStartTime[0][3] == 3)
+				MMU_doDMA(0, 3);
+
+			// Main memory display
+			if(MMU.DMAStartTime[0][0] == 4)
+			{
+				MMU_doDMA(0, 0);
+				MMU.DMAStartTime[0][0] = 0;
+			}
+			if(MMU.DMAStartTime[0][1] == 4)
+			{
+				MMU_doDMA(0, 1);
+				MMU.DMAStartTime[0][1] = 0;
+			}
+			if(MMU.DMAStartTime[0][2] == 4)
+			{
+				MMU_doDMA(0, 2);
+				MMU.DMAStartTime[0][2] = 0;
+			}
+			if(MMU.DMAStartTime[0][3] == 4)
+			{
+				MMU_doDMA(0, 3);
+				MMU.DMAStartTime[0][3] = 0;
+			}
+
+			if(MMU.DMAStartTime[1][0] == 4)
+			{
+				MMU_doDMA(1, 0);
+				MMU.DMAStartTime[1][0] = 0;
+			}
+			if(MMU.DMAStartTime[1][1] == 4)
+			{
+				MMU_doDMA(1, 1);
+				MMU.DMAStartTime[0][1] = 0;
+			}
+			if(MMU.DMAStartTime[1][2] == 4)
+			{
+				MMU_doDMA(1, 2);
+				MMU.DMAStartTime[1][2] = 0;
+			}
+			if(MMU.DMAStartTime[1][3] == 4)
+			{
+				MMU_doDMA(1, 3);
+				MMU.DMAStartTime[1][3] = 0;
+			}
+                            
+			if(nds.VCount == 192)
+			{
+				/* VBLANK */
+				T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) | 1);
+				T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 1);
+				NDS_ARM9VBlankInt();
+				NDS_ARM7VBlankInt();
+
+				if(MMU.DMAStartTime[0][0] == 1)
+					MMU_doDMA(0, 0);
+				if(MMU.DMAStartTime[0][1] == 1)
+					MMU_doDMA(0, 1);
+				if(MMU.DMAStartTime[0][2] == 1)
+					MMU_doDMA(0, 2);
+				if(MMU.DMAStartTime[0][3] == 1)
+					MMU_doDMA(0, 3);
+
+				if(MMU.DMAStartTime[1][0] == 1)
+					MMU_doDMA(1, 0);
+				if(MMU.DMAStartTime[1][1] == 1)
+					MMU_doDMA(1, 1);
+				if(MMU.DMAStartTime[1][2] == 1)
+					MMU_doDMA(1, 2);
+				if(MMU.DMAStartTime[1][3] == 1)
+					MMU_doDMA(1, 3);
+			}
+			else if(nds.VCount == 263)
+			{
+				const int cycles_per_frame = (263 * (99 * 12 + 256 * 12));
+				/* VDISP */
+				nds.nextHBlank = 3168;
+				nds.VCount = 0;
+				T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) & 0xFFFE);
+				T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFE);
+
+				nds.cycles -= cycles_per_frame;
+				nds.ARM9Cycle -= cycles_per_frame;
+				nds.ARM7Cycle -= cycles_per_frame;
+				nb -= cycles_per_frame;
+				if(MMU.timerON[0][0])
+					nds.timerCycle[0][0] -= cycles_per_frame;
+				if(MMU.timerON[0][1])
+					nds.timerCycle[0][1] -= cycles_per_frame;
+				if(MMU.timerON[0][2])
+					nds.timerCycle[0][2] -= cycles_per_frame;
+				if(MMU.timerON[0][3])
+					nds.timerCycle[0][3] -= cycles_per_frame;
+
+				if(MMU.timerON[1][0])
+					nds.timerCycle[1][0] -= cycles_per_frame;
+				if(MMU.timerON[1][1])
+					nds.timerCycle[1][1] -= cycles_per_frame;
+				if(MMU.timerON[1][2])
+					nds.timerCycle[1][2] -= cycles_per_frame;
+				if(MMU.timerON[1][3])
+					nds.timerCycle[1][3] -= cycles_per_frame;
+				if(MMU.DMAing[0][0])
+					MMU.DMACycle[0][0] -= cycles_per_frame;
+				if(MMU.DMAing[0][1])
+					MMU.DMACycle[0][1] -= cycles_per_frame;
+				if(MMU.DMAing[0][2])
+					MMU.DMACycle[0][2] -= cycles_per_frame;
+				if(MMU.DMAing[0][3])
+					MMU.DMACycle[0][3] -= cycles_per_frame;
+				if(MMU.DMAing[1][0])
+					MMU.DMACycle[1][0] -= cycles_per_frame;
+				if(MMU.DMAing[1][1])
+					MMU.DMACycle[1][1] -= cycles_per_frame;
+				if(MMU.DMAing[1][2])
+					MMU.DMACycle[1][2] -= cycles_per_frame;
+				if(MMU.DMAing[1][3])
+					MMU.DMACycle[1][3] -= cycles_per_frame;
+
+			}
+
+			T1WriteWord(ARM9Mem.ARM9_REG, 6, nds.VCount);
+			T1WriteWord(MMU.ARM7_REG, 6, nds.VCount);
+
+			vmatch = T1ReadWord(ARM9Mem.ARM9_REG, 4);
+			if((nds.VCount==(vmatch>>8)|((vmatch<<1)&(1<<8))))
+			{
+				T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) | 4);
+				if(T1ReadWord(ARM9Mem.ARM9_REG, 4) & 32)
+					NDS_makeARM9Int(2);
+			}
+			else
+				T1WriteWord(ARM9Mem.ARM9_REG, 4, T1ReadWord(ARM9Mem.ARM9_REG, 4) & 0xFFFB);
+
+			vmatch = T1ReadWord(MMU.ARM7_REG, 4);
+			if((nds.VCount==(vmatch>>8)|((vmatch<<1)&(1<<8))))
+			{
+				T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 4);
+				if(T1ReadWord(MMU.ARM7_REG, 4) & 32)
+					NDS_makeARM7Int(2);
+			}
+			else
+				T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFB);
+
+			timer_check();
+			dma_check();
+		}
+	}
+}
+
+void NDS_exec_frame(int cpu_clockdown_level_arm9, int cpu_clockdown_level_arm7)
+{
+	int v;
+	for (v = 0; v < 263; v++)
+	{
+		NDS_exec_hframe(cpu_clockdown_level_arm9, cpu_clockdown_level_arm7);
+	}
+}
+