/* Copyright (C) 2006 yopyop Copyright (C) 2008-2012 DeSmuME team This file 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. This file 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 the this software. If not, see . */ #include #include #include #include #include #include "common.h" #include "NDSSystem.h" //#include "render3D.h" #include "MMU.h" //#include "ROMReader.h" //#include "gfx3d.h" //#include "utils/decrypt/decrypt.h" //#include "utils/decrypt/crc.h" #include "bios.h" //#include "debug.h" //#include "cheatSystem.h" //#include "movie.h" //#include "Disassembler.h" #include "readwrite.h" //#include "debug.h" #include "firmware.h" #include "version.h" #include "slot1.h" #include "path.h" //#define LOG_ARM9 //#define LOG_ARM7 //bool dolog = false; //#define LOG_TO_FILE //#define LOG_TO_FILE_REGS //=============================================================== //FILE *fp_dis7 = NULL; //FILE *fp_dis9 = NULL; PathInfo path; TCommonSettings CommonSettings; //static BaseDriver _stub_driver; //BaseDriver* driver = &_stub_driver; //std::string InputDisplayString; //static bool LidClosed = false; //static uint8_t countLid = 0; GameInfo gameInfo; NDSSystem nds; CFIRMWARE *firmware = NULL; //ADVANsCEne advsc; bool singleStep; //bool nds_debug_continuing[2]; //int lagframecounter; //int LagFrameFlag; //int lastLag; //int TotalLagFrames; //TSCalInfo TSCal; namespace DLDI { bool tryPatch(void* data, size_t size); } /*void Desmume_InitOnce() { static bool initOnce = false; if(initOnce) return; initOnce = true;*/ /*#ifdef HAVE_LIBAGG extern void Agg_init(); //no need to include just for this Agg_init(); #endif*/ //} #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() { #endif //nds.idleFrameCounter = 0; //memset(nds.runCycleCollector,0,sizeof(nds.runCycleCollector)); MMU_Init(); nds.VCount = 0; //got to print this somewhere.. printf("%s\n", EMU_DESMUME_NAME_AND_VERSION()); /*if (Screen_Init(GFXCORE_DUMMY) != 0) return -1;*/ //gfx3d_init(); #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, 740) != 0) return -1; //WIFI_Init() ; // Init calibration info //TSCal.adc.x1 = 0x0200; //TSCal.adc.y1 = 0x0200; //TSCal.scr.x1 = 0x20 + 1; // calibration screen coords are 1-based, //TSCal.scr.y1 = 0x20 + 1; // either that or NDS_getADCTouchPosX/Y are wrong. //TSCal.adc.x2 = 0x0E00; //TSCal.adc.y2 = 0x0800; //TSCal.scr.x2 = 0xE0 + 1; //TSCal.scr.y2 = 0x80 + 1; //TSCal.adc.width = (TSCal.adc.x2 - TSCal.adc.x1); //TSCal.adc.height = (TSCal.adc.y2 - TSCal.adc.y1); //TSCal.scr.width = (TSCal.scr.x2 - TSCal.scr.x1); //TSCal.scr.height = (TSCal.scr.y2 - TSCal.scr.y1); //cheats = new CHEATS(); //cheatSearch = new CHEATSEARCH(); return 0; } void NDS_DeInit() { if(MMU.CART_ROM != MMU.UNUSED_RAM) NDS_FreeROM(); SPU_DeInit(); //Screen_DeInit(); MMU_DeInit(); //gpu3D->NDS_3D_Close(); //WIFI_DeInit(); /*if (cheats) delete cheats;*/ /*if (cheatSearch) delete cheatSearch;*/ #ifdef LOG_ARM7 if (fp_dis7 != NULL) { fclose(fp_dis7); fp_dis7 = NULL; } #endif #ifdef LOG_ARM9 if (fp_dis9 != NULL) { fclose(fp_dis9); fp_dis9 = NULL; } #endif } bool NDS_SetROM(uint8_t * rom, uint32_t mask) { MMU_setRom(rom, mask); return true; } NDS_header * NDS_getROMHeader() { if(MMU.CART_ROM == MMU.UNUSED_RAM) return NULL; NDS_header * header = new 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->romversion = MMU.CART_ROM[30]; 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, std::min(160, (int)gameInfo.romsize - 352)); return header; } /*void debug() { //if(NDS_ARM9.R[15]==0x020520DC) emu_halt(); //DSLinux //if(NDS_ARM9.CPSR.bits.mode == 0) emu_halt(); //if((NDS_ARM9.R[15]&0xFFFFF000)==0) emu_halt(); //if((NDS_ARM9.R[15]==0x0201B4F4)&&(NDS_ARM9.R[1]==0x0)) emu_halt(); //AOE //if((NDS_ARM9.R[15]==0x01FFE194)&&(NDS_ARM9.R[0]==0)) emu_halt(); //if((NDS_ARM9.R[15]==0x01FFE134)&&(NDS_ARM9.R[0]==0)) emu_halt(); //BBMAN //if(NDS_ARM9.R[15]==0x02098B4C) emu_halt(); //if(NDS_ARM9.R[15]==0x02004924) emu_halt(); //if(NDS_ARM9.R[15]==0x02004890) emu_halt(); //if(NDS_ARM9.R[15]==0x0202B800) emu_halt(); //if(NDS_ARM9.R[15]==0x0202B3DC) emu_halt(); //if((NDS_ARM9.R[1]==0x9AC29AC1)&&(!fait)) {emu_halt();fait = true;} //if(NDS_ARM9.R[1]==0x0400004A) {emu_halt();fait = true;} if(NDS_ARM9.R[4]==0x2E33373C) emu_halt(); if(NDS_ARM9.R[15]==0x02036668) //emu_halt(); { nds.logcount++; sprintf(logbuf, "%d %08X", nds.logcount, NDS_ARM9.R[13]); log::ajouter(logbuf); if(nds.logcount==89) execute=false; } //if(NDS_ARM9.instruction==0) emu_halt(); //if((NDS_ARM9.R[15]>>28)) emu_halt(); }*/ //#define DSGBA_LOADER_SIZE 512 /*enum { ROM_NDS = 0, ROM_DSGBA };*/ #if 0 /* not used */ //http://www.aggregate.org/MAGIC/#Population%20Count%20(Ones%20Count) static uint32_t ones32(uint32_t x) { /* 32-bit recursive reduction using SWAR... but first step is mapping 2-bit values into sum of 2 1-bit values in sneaky way */ x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); return x & 0x0000003f; } #endif RomBanner::RomBanner(bool defaultInit) { if(!defaultInit) return; version = 1; //Version (0001h) crc16 = 0; //CRC16 across entries 020h..83Fh memset(reserved,0,sizeof(reserved)); memset(bitmap,0,sizeof(bitmap)); memset(palette,0,sizeof(palette)); memset(titles,0,sizeof(titles)); memset(end0xFF,0,sizeof(end0xFF)); } /*bool GameInfo::hasRomBanner() { if(header.IconOff + sizeof(RomBanner) > romsize) return false; else return true; }*/ /*const RomBanner& GameInfo::getRomBanner() { //we may not have a valid banner. return a default one if(!hasRomBanner()) { static RomBanner defaultBanner(true); return defaultBanner; } return *(RomBanner*)(romdata+header.IconOff); }*/ /*void GameInfo::populate() { const char *regions[] = { "JPFSEDIRKH", "JPN", "EUR", "FRA", "ESP", "USA", "NOE", "ITA", "RUS", "KOR", "HOL", }; NDS_header * _header = NDS_getROMHeader(); header = *_header; delete _header; memset(ROMserial, 0, sizeof(ROMserial)); memset(ROMname, 0, sizeof(ROMname)); if ( //Option 1. - look for this instruction in the game title //(did this ever work?) //(header->gameTile[0] == 0x2E) && //(header->gameTile[1] == 0x00) && //(header->gameTile[2] == 0x00) && //(header->gameTile[3] == 0xEA) //) && //option 2. - look for gamecode #### (default for ndstool) //or an invalid gamecode ( ((header.gameCode[0] == 0x23) && (header.gameCode[1] == 0x23) && (header.gameCode[2] == 0x23) && (header.gameCode[3] == 0x23) ) || (header.gameCode[0] == 0x00) ) && header.makerCode == 0x0 ) { //we can't really make a serial for a homebrew game that hasnt set a game code strcpy(ROMserial, "Homebrew"); } else { strcpy(ROMserial,"NTR- -"); memcpy(ROMserial+4, header.gameCode, 4); uint32_t region = (uint32_t)(std::max(strchr(regions[0],header.gameCode[3]) - regions[0] + 1, 0)); if (region != 0) strcat(ROMserial, regions[region]); else strcat(ROMserial, "Unknown"); } //rom name is probably set even in homebrew memset(ROMname, 0, sizeof(ROMname)); memcpy(ROMname, header.gameTile, 12); trim(ROMname,20); if(header.IconOff < romsize) { uint8_t num = (T1ReadByte((uint8_t*)romdata, header.IconOff) == 1)?6:7; for (int i = 0; i < num; i++) { wcstombs(ROMfullName[i], (wchar_t *)(romdata+header.IconOff+0x240+(i*0x100)), 0x100); trim(ROMfullName[i]); } } //this may look like a poor heuristic for detecting homebrew, but it is actually pretty good. //setting your own game code is stupid, so homebrew should just leave it. //however, non-devkitARM-default makefiles may not have set this. isHomebrew = !memcmp(header.gameCode,"####",4); }*/ /*#ifdef _WINDOWS //static std::vector buffer; //static std::vector v; static void loadrom(std::string fname) { FILE* inf = fopen(fname.c_str(),"rb"); if(!inf) return; fseek(inf,0,SEEK_END); int size = ftell(inf); fseek(inf,0,SEEK_SET); gameInfo.resize(size); fread(gameInfo.romdata,1,size,inf); gameInfo.fillGap(); fclose(inf); } static int rom_init_path(const char *filename, const char *logicalFilename) { int type = ROM_NDS; path.init(logicalFilename); if ( path.isdsgba(path.path)) { type = ROM_DSGBA; loadrom(path.path); } else if ( !strcasecmp(path.extension().c_str(), "nds")) { type = ROM_NDS; loadrom(path.path); //n.b. this does nothing if the file can't be found (i.e. if it was an extracted tempfile)... //...but since the data was extracted to gameInfo then it is ok } //ds.gba in archives, it's already been loaded into memory at this point else if (path.isdsgba(std::string(logicalFilename))) { type = ROM_DSGBA; } else { //well, try to load it as an nds rom anyway type = ROM_NDS; loadrom(path.path); } if(type == ROM_DSGBA) { std::vector v(gameInfo.romdata + DSGBA_LOADER_SIZE, gameInfo.romdata + gameInfo.romsize); gameInfo.loadData(&v[0],gameInfo.romsize - DSGBA_LOADER_SIZE); } //check that size is at least the size of the header if (gameInfo.romsize < 352) { return -1; } return 1; } #else static int rom_init_path(const char *filename, const char *logicalFilename) { int ret; int type; ROMReader_struct *reader; void *file; uint32_t size; char *noext; noext = strdup(filename); reader = ROMReaderInit(&noext); free(noext); if (logicalFilename) path.init(logicalFilename); else path.init(filename); if (!strcasecmp(path.extension().c_str(), "zip")) type = ROM_NDS; else if (!strcasecmp(path.extension().c_str(), "nds")) type = ROM_NDS; else if (path.isdsgba(path.path)) type = ROM_DSGBA; else type = ROM_NDS; file = reader->Init(filename); if (!file) { reader->DeInit(file); return -1; } size = reader->Size(file); if(type == ROM_DSGBA) { reader->Seek(file, DSGBA_LOADER_SIZE, SEEK_SET); size -= DSGBA_LOADER_SIZE; } //check that size is at least the size of the header if (size < 352) { reader->DeInit(file); return -1; } // Make sure old ROM is freed first(at least this way we won't be eating // up a ton of ram before the old ROM is freed) if(MMU.CART_ROM != MMU.UNUSED_RAM) NDS_FreeROM(); gameInfo.resize(size); ret = reader->Read(file, gameInfo.romdata, size); gameInfo.fillGap(); reader->DeInit(file); return ret; } #endif*/ /*int NDS_LoadROM(const char *filename, const char *logicalFilename) { int ret; char buf[MAX_PATH]; if (filename == NULL) return -1; ret = rom_init_path(filename, logicalFilename); if (ret < 1) return ret; //decrypt if necessary.. //but this is untested and suspected to fail on big endian, so lets not support this on big endian #ifndef WORDS_BIGENDIAN bool okRom = DecryptSecureArea((uint8_t*)gameInfo.romdata,gameInfo.romsize); if(!okRom) { printf("Specified file is not a valid rom\n"); return -1; } #endif if (cheatSearch) cheatSearch->close(); FCEUI_StopMovie(); MMU_unsetRom(); NDS_SetROM((uint8_t*)gameInfo.romdata, gameInfo.mask); gameInfo.populate(); gameInfo.crc = crc32(0,(uint8_t*)gameInfo.romdata,gameInfo.romsize); //INFO("\nROM game code: %c%c%c%c\n", gameInfo.header.gameCode[0], gameInfo.header.gameCode[1], gameInfo.header.gameCode[2], gameInfo.header.gameCode[3]); //INFO("ROM crc: %08X\n", gameInfo.crc); //INFO("ROM serial: %s\n", gameInfo.ROMserial); //INFO("ROM internal name: %s\n", gameInfo.ROMname); memset(buf, 0, MAX_PATH); strcpy(buf, path.pathToModule); strcat(buf, "desmume.ddb"); // DeSmuME database :) advsc.setDatabase(buf); buf[0] = gameInfo.header.gameCode[0]; buf[1] = gameInfo.header.gameCode[1]; buf[2] = gameInfo.header.gameCode[2]; buf[3] = gameInfo.header.gameCode[3]; buf[4] = 0; if (advsc.checkDB(buf)) { u8 sv = advsc.getSaveType(); printf("ADVANsCEne database:\n"); printf("\t* ROM save type: "); if (sv == 0xFF) printf("Unknown"); else if (sv == 0xFE) printf("None"); else { printf("%s", save_names[sv]); if (CommonSettings.autodetectBackupMethod == 1) backup_setManualBackupType(sv+1); } printf("\n\t* ROM crc: %08X\n", advsc.getCRC32()); } printf("\n"); //for homebrew, try auto-patching DLDI. should be benign if there is no DLDI or if it fails if(gameInfo.isHomebrew) DLDI::tryPatch((void*)gameInfo.romdata, gameInfo.romsize); memset(buf, 0, MAX_PATH); path.getpathnoext(path.BATTERY, buf); strcat(buf, ".dsv"); // DeSmuME memory card :) MMU_new.backupDevice.load_rom(buf); memset(buf, 0, MAX_PATH); path.getpathnoext(path.CHEATS, buf); strcat(buf, ".dct"); // DeSmuME cheat :) cheats->init(buf); NDS_Reset(); return ret; }*/ void NDS_FreeROM() { //FCEUI_StopMovie(); if ((uint8_t*)MMU.CART_ROM == (uint8_t*)gameInfo.romdata) gameInfo.romdata = NULL; if (MMU.CART_ROM != MMU.UNUSED_RAM) delete [] MMU.CART_ROM; MMU_unsetRom(); } /*int NDS_ImportSave(const char *filename) { if (strlen(filename) < 4) return 0; if (memcmp(filename+strlen(filename)-4, ".duc", 4) == 0) return MMU_new.backupDevice.load_duc(filename); else if (MMU_new.backupDevice.load_no_gba(filename)) return 1; else return MMU_new.backupDevice.load_raw(filename); //return 0; }*/ /*bool NDS_ExportSave(const char *filename) { if (strlen(filename) < 4) return false; if (memcmp(filename+strlen(filename)-5, ".sav*", 5) == 0) { char tmp[MAX_PATH]; memset(tmp, 0, MAX_PATH); strcpy(tmp, filename); tmp[strlen(tmp)-1] = 0; return MMU_new.backupDevice.save_no_gba(tmp); } if (memcmp(filename+strlen(filename)-4, ".sav", 4) == 0) return MMU_new.backupDevice.save_raw(filename); return false; }*/ /*static int WritePNGChunk(FILE *fp, uint32 size, const char *type, const uint8 *data) { uint32 crc; uint8 tempo[4]; tempo[0]=size>>24; tempo[1]=size>>16; tempo[2]=size>>8; tempo[3]=size; if(fwrite(tempo,4,1,fp)!=1) return 0; if(fwrite(type,4,1,fp)!=1) return 0; if(size) if(fwrite(data,1,size,fp)!=size) return 0; crc = crc32(0,(uint8 *)type,4); if(size) crc = crc32(crc,data,size); tempo[0]=crc>>24; tempo[1]=crc>>16; tempo[2]=crc>>8; tempo[3]=crc; if(fwrite(tempo,4,1,fp)!=1) return 0; return 1; }*/ /*int NDS_WritePNG(const char *fname) { int x, y; int width=256; int height=192*2; uint16_t * bmp = (uint16_t *)GPU_screen; FILE *pp=NULL; uint8 *compmem = NULL; uLongf compmemsize = (uLongf)( (height * (width + 1) * 3 * 1.001 + 1) + 12 ); if(!(compmem=(uint8 *)malloc(compmemsize))) return 0; if(!(pp=fopen(fname, "wb"))) { goto PNGerr; } { const uint8 header[8]={137,80,78,71,13,10,26,10}; if(fwrite(header,8,1,pp)!=1) goto PNGerr; } { uint8 chunko[13]; chunko[0] = width >> 24; // Width chunko[1] = width >> 16; chunko[2] = width >> 8; chunko[3] = width; chunko[4] = height >> 24; // Height chunko[5] = height >> 16; chunko[6] = height >> 8; chunko[7] = height; chunko[8]=8; // 8 bits per sample(24 bits per pixel) chunko[9]=2; // Color type; RGB triplet chunko[10]=0; // compression: deflate chunko[11]=0; // Basic adapative filter set(though none are used). chunko[12]=0; // No interlace. if(!WritePNGChunk(pp,13,"IHDR",chunko)) goto PNGerr; } { uint8 *tmp_buffer; uint8 *tmp_inc; tmp_inc = tmp_buffer = (uint8 *)malloc((width * 3 + 1) * height); for(y=0;y>10; pixel-=r<<10; g = pixel>>5; pixel-=g<<5; b = pixel; r*=255/31; g*=255/31; b*=255/31; tmp_inc[0] = b; tmp_inc[1] = g; tmp_inc[2] = r; tmp_inc += 3; } } if(compress(compmem, &compmemsize, tmp_buffer, height * (width * 3 + 1))!=Z_OK) { if(tmp_buffer) free(tmp_buffer); goto PNGerr; } if(tmp_buffer) free(tmp_buffer); if(!WritePNGChunk(pp,compmemsize,"IDAT",compmem)) goto PNGerr; } if(!WritePNGChunk(pp,0,"IEND",0)) goto PNGerr; free(compmem); fclose(pp); return 1; PNGerr: if(compmem) free(compmem); if(pp) fclose(pp); return 0; }*/ /*typedef struct { uint32_t size; int32_t width; int32_t height; uint16_t planes; uint16_t bpp; uint32_t cmptype; uint32_t imgsize; int32_t hppm; int32_t vppm; uint32_t numcol; uint32_t numimpcol; } bmpimgheader_struct; #include "PACKED.h" typedef struct { uint16_t id __PACKED; uint32_t size __PACKED; uint16_t reserved1 __PACKED; uint16_t reserved2 __PACKED; uint32_t imgoffset __PACKED; } bmpfileheader_struct; #include "PACKED_END.h"*/ /*int NDS_WriteBMP(const char *filename) { bmpfileheader_struct fileheader; bmpimgheader_struct imageheader; FILE *file; int i,j; uint16_t * bmp = (uint16_t *)GPU_screen; size_t elems_written = 0; memset(&fileheader, 0, sizeof(fileheader)); fileheader.size = sizeof(fileheader); fileheader.id = 'B' | ('M' << 8); fileheader.imgoffset = sizeof(fileheader)+sizeof(imageheader); memset(&imageheader, 0, sizeof(imageheader)); imageheader.size = sizeof(imageheader); imageheader.width = 256; imageheader.height = 192*2; imageheader.planes = 1; imageheader.bpp = 24; imageheader.cmptype = 0; // None imageheader.imgsize = imageheader.width * imageheader.height * 3; if ((file = fopen(filename,"wb")) == NULL) return 0; elems_written += fwrite(&fileheader, 1, sizeof(fileheader), file); elems_written += fwrite(&imageheader, 1, sizeof(imageheader), file); for(j=0;j<192*2;j++) { for(i=0;i<256;i++) { uint8_t r,g,b; uint16_t pixel = bmp[(192*2-j-1)*256+i]; r = pixel>>10; pixel-=r<<10; g = pixel>>5; pixel-=g<<5; b = (uint8_t)pixel; r*=255/31; g*=255/31; b*=255/31; elems_written += fwrite(&r, 1, sizeof(uint8_t), file); elems_written += fwrite(&g, 1, sizeof(uint8_t), file); elems_written += fwrite(&b, 1, sizeof(uint8_t), file); } } fclose(file); return 1; }*/ /*int NDS_WriteBMP_32bppBuffer(int width, int height, const void* buf, const char *filename) { bmpfileheader_struct fileheader; bmpimgheader_struct imageheader; FILE *file; size_t elems_written = 0; memset(&fileheader, 0, sizeof(fileheader)); fileheader.size = sizeof(fileheader); fileheader.id = 'B' | ('M' << 8); fileheader.imgoffset = sizeof(fileheader)+sizeof(imageheader); memset(&imageheader, 0, sizeof(imageheader)); imageheader.size = sizeof(imageheader); imageheader.width = width; imageheader.height = height; imageheader.planes = 1; imageheader.bpp = 32; imageheader.cmptype = 0; // None imageheader.imgsize = imageheader.width * imageheader.height * 4; if ((file = fopen(filename,"wb")) == NULL) return 0; elems_written += fwrite(&fileheader, 1, sizeof(fileheader), file); elems_written += fwrite(&imageheader, 1, sizeof(imageheader), file); for(int i=0;i 30) || forceEvenIfCapturing) { SkipCur2DFrame = false; SkipCur3DFrame = false; SkipNext2DFrame = false; if(forceEvenIfCapturing) consecutiveNonCaptures = 0; } } void Advance() { bool capturing = false*//*(MainScreen.gpu->dispCapCnt.enabled || (MainScreen.gpu->dispCapCnt.val & 0x80000000))*//*; if(capturing && consecutiveNonCaptures > 30) { // the worst-looking graphics corruption problems from frameskip // are the result of skipping the capture on first frame it turns on. // so we do this to handle the capture immediately, // despite the risk of 1 frame of 2d/3d mismatch or wrong screen display. SkipNext2DFrame = false; nextSkip = false; } else if(*//*lastOffset != MainScreen.offset && *//*lastSkip && !skipped) { // if we're switching from not skipping to skipping // and the screens are also switching around this frame, // go for 1 extra frame without skipping. // this avoids the scenario where we only draw one of the two screens // when a game is switching screens every frame. nextSkip = false; } if(capturing) consecutiveNonCaptures = 0; else if(!(consecutiveNonCaptures > 9000)) // arbitrary cap to avoid eventual wrap consecutiveNonCaptures++; lastLastOffset = lastOffset; //lastOffset = MainScreen.offset; lastSkip = skipped; skipped = nextSkip; nextSkip = false; SkipCur2DFrame = SkipNext2DFrame; SkipCur3DFrame = skipped; SkipNext2DFrame = skipped; } inline bool ShouldSkip2D() { return SkipCur2DFrame; } inline bool ShouldSkip3D() { return SkipCur3DFrame; } FrameSkipper() { nextSkip = false; skipped = false; lastSkip = false; lastOffset = 0; SkipCur2DFrame = false; SkipCur3DFrame = false; SkipNext2DFrame = false; consecutiveNonCaptures = 0; } private: bool nextSkip; bool skipped; bool lastSkip; int lastOffset; int lastLastOffset; int consecutiveNonCaptures; bool SkipCur2DFrame; bool SkipCur3DFrame; bool SkipNext2DFrame; };*/ //static FrameSkipper frameSkipper; /*void NDS_SkipNextFrame() { //if (!driver->AVI_IsRecording()) { //frameSkipper.RequestSkip(); //} }*/ /*void NDS_OmitFrameSkip(int force) { //frameSkipper.OmitSkip(force > 0, force > 1); }*/ //#define INDEX(i) ((((i)>>16)&0xFF0)|(((i)>>4)&0xF)) enum ESI_DISPCNT { ESI_DISPCNT_HStart, ESI_DISPCNT_HStartIRQ, ESI_DISPCNT_HDraw, ESI_DISPCNT_HBlank }; uint64_t nds_timer; uint64_t nds_arm9_timer, nds_arm7_timer; static const uint64_t kNever = 0xFFFFFFFFFFFFFFFFULL; struct TSequenceItem { uint64_t timestamp; uint32_t param; bool enabled; /*virtual void save(EMUFILE* os) { write64le(timestamp,os); write32le(param,os); writebool(enabled,os); }*/ virtual bool load(EMUFILE* is) { if(read64le(×tamp,is) != 1) return false; if(read32le(¶m,is) != 1) return false; if(readbool(&enabled,is) != 1) return false; return true; } inline bool isTriggered() { return enabled && nds_timer >= timestamp; } inline uint64_t next() { return timestamp; } }; /*struct TSequenceItem_GXFIFO : public TSequenceItem { inline bool isTriggered() { return enabled && nds_timer >= MMU.gfx3dCycles; } inline void exec() { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[4]++); while(isTriggered()) { enabled = false; //gfx3d_execute3D(); } } inline uint64_t next() { if(enabled) return MMU.gfx3dCycles; else return kNever; } };*/ template struct TSequenceItem_Timer : public TSequenceItem { inline bool isTriggered() { return enabled && nds_timer >= nds.timerCycle[procnum][num]; } inline void schedule() { enabled = MMU.timerON[procnum][num] && MMU.timerMODE[procnum][num] != 0xFFFF; } inline uint64_t next() { return nds.timerCycle[procnum][num]; } inline void exec() { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[13+procnum*4+num]++); uint8_t* regs = procnum==0?MMU.ARM9_REG:MMU.ARM7_REG; bool first = true, over; //we'll need to check chained timers.. for(int i=num;i<4;i++) { //maybe too many checks if this is here, but we need it here for now if(!MMU.timerON[procnum][i]) return; if(MMU.timerMODE[procnum][i] == 0xFFFF) { ++(MMU.timer[procnum][i]); over = !MMU.timer[procnum][i]; } else { if(!first) break; //this timer isn't chained. break the chain first = false; over = true; int remain = 65536 - MMU.timerReload[procnum][i]; int ctr=0; while(nds.timerCycle[procnum][i] <= nds_timer) { nds.timerCycle[procnum][i] += (remain << MMU.timerMODE[procnum][i]); ctr++; } #ifndef NDEBUG if(ctr>1) { printf("yikes!!!!! please report!\n"); } #endif } if(over) { MMU.timer[procnum][i] = MMU.timerReload[procnum][i]; if(T1ReadWord(regs, 0x102 + i*4) & 0x40) { NDS_makeIrq(procnum, IRQ_BIT_TIMER_0 + i); } } else break; //no more chained timers to trigger. we're done here } } }; template struct TSequenceItem_DMA : public TSequenceItem { DmaController* controller; inline bool isTriggered() { return controller->dmaCheck && nds_timer>= controller->nextEvent; } inline bool isEnabled() { return controller->dmaCheck?true:false; } inline uint64_t next() { return controller->nextEvent; } inline void exec() { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[5+procnum*4+chan]++); //if (nds.freezeBus) return; //printf("exec from TSequenceItem_DMA: %d %d\n",procnum,chan); controller->exec(); // //give gxfifo dmas a chance to re-trigger // if(MMU.DMAStartTime[procnum][chan] == EDMAMode_GXFifo) { // MMU.DMAing[procnum][chan] = false; // if (gxFIFO.size <= 127) // { // execHardware_doDma(procnum,chan,EDMAMode_GXFifo); // if (MMU.DMACompleted[procnum][chan]) // goto docomplete; // else return; // } // } // //docomplete: // if (MMU.DMACompleted[procnum][chan]) // { // uint8_t* regs = procnum==0?MMU.ARM9_REG:MMU.ARM7_REG; // // //disable the channel // if(MMU.DMAStartTime[procnum][chan] != EDMAMode_GXFifo) { // T1WriteLong(regs, 0xB8 + (0xC*chan), T1ReadLong(regs, 0xB8 + (0xC*chan)) & 0x7FFFFFFF); // MMU.DMACrt[procnum][chan] &= 0x7FFFFFFF; //blehhh i hate this shit being mirrored in memory // } // // if((MMU.DMACrt[procnum][chan])&(1<<30)) { // if(procnum==0) NDS_makeARM9Int(8+chan); // else NDS_makeARM7Int(8+chan); // } // // MMU.DMAing[procnum][chan] = false; // } } }; struct TSequenceItem_divider : public TSequenceItem { inline bool isTriggered() { return MMU.divRunning && nds_timer >= MMU.divCycles; } bool isEnabled() { return MMU.divRunning!=0; } inline uint64_t next() { return MMU.divCycles; } void exec() { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[2]++); MMU_new.div.busy = 0; #ifdef _WIN64 T1WriteQuad(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A0, MMU.divResult); T1WriteQuad(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A8, MMU.divMod); #else T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A0, (uint32_t)MMU.divResult); T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A4, (uint32_t)(MMU.divResult >> 32)); T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A8, (uint32_t)MMU.divMod); T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2AC, (uint32_t)(MMU.divMod >> 32)); #endif MMU.divRunning = false; } }; struct TSequenceItem_sqrtunit : public TSequenceItem { inline bool isTriggered() { return MMU.sqrtRunning && nds_timer >= MMU.sqrtCycles; } bool isEnabled() { return MMU.sqrtRunning!=0; } inline uint64_t next() { return MMU.sqrtCycles; } inline void exec() { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[3]++); MMU_new.sqrt.busy = 0; T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2B4, MMU.sqrtResult); MMU.sqrtRunning = false; } }; struct Sequencer { bool nds_vblankEnded; bool reschedule; TSequenceItem dispcnt; TSequenceItem wifi; TSequenceItem_divider divider; TSequenceItem_sqrtunit sqrtunit; TSequenceItem/*_GXFIFO*/ gxfifo; TSequenceItem_DMA<0,0> dma_0_0; TSequenceItem_DMA<0,1> dma_0_1; TSequenceItem_DMA<0,2> dma_0_2; TSequenceItem_DMA<0,3> dma_0_3; TSequenceItem_DMA<1,0> dma_1_0; TSequenceItem_DMA<1,1> dma_1_1; TSequenceItem_DMA<1,2> dma_1_2; TSequenceItem_DMA<1,3> dma_1_3; TSequenceItem_Timer<0,0> timer_0_0; TSequenceItem_Timer<0,1> timer_0_1; TSequenceItem_Timer<0,2> timer_0_2; TSequenceItem_Timer<0,3> timer_0_3; TSequenceItem_Timer<1,0> timer_1_0; TSequenceItem_Timer<1,1> timer_1_1; TSequenceItem_Timer<1,2> timer_1_2; TSequenceItem_Timer<1,3> timer_1_3; void init(); void execHardware(); uint64_t findNext(); /*void save(EMUFILE* os) { write64le(nds_timer,os); write64le(nds_arm9_timer,os); write64le(nds_arm7_timer,os); dispcnt.save(os); divider.save(os); sqrtunit.save(os); gxfifo.save(os); wifi.save(os); #define SAVE(I,X,Y) I##_##X##_##Y .save(os); SAVE(timer,0,0); SAVE(timer,0,1); SAVE(timer,0,2); SAVE(timer,0,3); SAVE(timer,1,0); SAVE(timer,1,1); SAVE(timer,1,2); SAVE(timer,1,3); SAVE(dma,0,0); SAVE(dma,0,1); SAVE(dma,0,2); SAVE(dma,0,3); SAVE(dma,1,0); SAVE(dma,1,1); SAVE(dma,1,2); SAVE(dma,1,3); #undef SAVE }*/ bool load(EMUFILE* is, int version) { if(read64le(&nds_timer,is) != 1) return false; if(read64le(&nds_arm9_timer,is) != 1) return false; if(read64le(&nds_arm7_timer,is) != 1) return false; if(!dispcnt.load(is)) return false; if(!divider.load(is)) return false; if(!sqrtunit.load(is)) return false; if(!gxfifo.load(is)) return false; if(version >= 1) if(!wifi.load(is)) return false; #define LOAD(I,X,Y) if(!I##_##X##_##Y .load(is)) return false; LOAD(timer,0,0); LOAD(timer,0,1); LOAD(timer,0,2); LOAD(timer,0,3); LOAD(timer,1,0); LOAD(timer,1,1); LOAD(timer,1,2); LOAD(timer,1,3); LOAD(dma,0,0); LOAD(dma,0,1); LOAD(dma,0,2); LOAD(dma,0,3); LOAD(dma,1,0); LOAD(dma,1,1); LOAD(dma,1,2); LOAD(dma,1,3); #undef LOAD return true; } } sequencer; /*void NDS_RescheduleGXFIFO(uint32_t cost) { //if(!sequencer.gxfifo.enabled) { //MMU.gfx3dCycles = nds_timer; //sequencer.gxfifo.enabled = true; //} //MMU.gfx3dCycles += cost; NDS_Reschedule(); }*/ void NDS_RescheduleTimers() { #define check(X,Y) sequencer.timer_##X##_##Y .schedule(); check(0,0); check(0,1); check(0,2); check(0,3); check(1,0); check(1,1); check(1,2); check(1,3); #undef check NDS_Reschedule(); } void NDS_RescheduleDMA() { //TBD NDS_Reschedule(); } static void initSchedule() { sequencer.init(); //begin at the very end of the last scanline //so that at t=0 we can increment to scanline=0 nds.VCount = 262; sequencer.nds_vblankEnded = false; } // 2196372 ~= (ARM7_CLOCK << 16) / 1000000 // This value makes more sense to me, because: // ARM7_CLOCK = 33.51 mhz // = 33513982 cycles per second // = 33.513982 cycles per microsecond //const uint64_t kWifiCycles = 34*2; //(this isn't very precise. I don't think it needs to be) void Sequencer::init() { NDS_RescheduleTimers(); NDS_RescheduleDMA(); reschedule = false; nds_timer = 0; nds_arm9_timer = 0; nds_arm7_timer = 0; dispcnt.enabled = true; dispcnt.param = ESI_DISPCNT_HStart; dispcnt.timestamp = 0; //gxfifo.enabled = false; dma_0_0.controller = &MMU_new.dma[0][0]; dma_0_1.controller = &MMU_new.dma[0][1]; dma_0_2.controller = &MMU_new.dma[0][2]; dma_0_3.controller = &MMU_new.dma[0][3]; dma_1_0.controller = &MMU_new.dma[1][0]; dma_1_1.controller = &MMU_new.dma[1][1]; dma_1_2.controller = &MMU_new.dma[1][2]; dma_1_3.controller = &MMU_new.dma[1][3]; /*#ifdef EXPERIMENTAL_WIFI_COMM wifi.enabled = true; wifi.timestamp = kWifiCycles; #else wifi.enabled = false; #endif*/ } //this isnt helping much right now. work on it later //#include "utils/task.h" //Task taskSubGpu(true); //void* renderSubScreen(void*) //{ // GPU_RenderLine(&SubScreen, nds.VCount, SkipCur2DFrame); // return NULL; //} static void execHardware_hblank() { //this logic keeps moving around. //now, we try and give the game as much time as possible to finish doing its work for the scanline, //by drawing scanline N at the end of drawing time (but before subsequent interrupt or hdma-driven events happen) //don't try to do this at the end of the scanline, because some games (sonic classics) may use hblank IRQ to set //scroll regs for the next scanline if(nds.VCount<192) { //taskSubGpu.execute(renderSubScreen,NULL); //GPU_RenderLine(&MainScreen, nds.VCount, frameSkipper.ShouldSkip2D()); //GPU_RenderLine(&SubScreen, nds.VCount, frameSkipper.ShouldSkip2D()); //taskSubGpu.finish(); //trigger hblank dmas //but notice, we do that just after we finished drawing the line //(values copied by this hdma should not be used until the next scanline) triggerDma(EDMAMode_HBlank); } if(nds.VCount==262) { //we need to trigger one last hblank dma since //a. we're sort of lagged behind by one scanline //b. i think that 193 hblanks actually fire (one for the hblank in scanline 262) //this is demonstrated by NSMB splot-parallaxing clouds //for some reason the game will setup two hdma scroll register buffers //to be run consecutively, and unless we do this, the second buffer will be offset by one scanline //causing a glitch in the 0th scanline //triggerDma(EDMAMode_HBlank); //BUT! this was removed in order to make glitches in megaman zero collection (mmz 4 1st level) work. //and, it seems that it is no longer necessary in nsmb. perhaps something else fixed it } //turn on hblank status bit T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) | 2); T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 2); //fire hblank interrupts if necessary if(T1ReadWord(MMU.ARM9_REG, 4) & 0x10) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_LCD_HBLANK); if(T1ReadWord(MMU.ARM7_REG, 4) & 0x10) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_LCD_HBLANK); //emulation housekeeping. for some reason we always do this at hblank, //even though it sounds more reasonable to do it at hstart SPU_Emulate_core(); //driver->AVI_SoundUpdate(SPU_core->outbuf,spu_core_samples); //WAV_WavSoundUpdate(SPU_core->outbuf,spu_core_samples); } static void execHardware_hstart_vblankEnd() { sequencer.nds_vblankEnded = true; sequencer.reschedule = true; //turn off vblank status bit T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & ~1); T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & ~1); //some emulation housekeeping //frameSkipper.Advance(); } static void execHardware_hstart_vblankStart() { //printf("--------VBLANK!!!--------\n"); //fire vblank interrupts if necessary for(int i=0;i<2;i++) if(MMU.reg_IF_pending[i] & (1<>8)|((vmatch<<1)&(1<<8))); return vmatch; } static void execHardware_hstart_vcount_irq() { //trigger pending VMATCH irqs if(MMU.reg_IF_pending[ARMCPU_ARM9] & (1<>8)|((vmatch<<1)&(1<<8))); if(nds.VCount==vmatch) { //arm7 vmatch T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 4); if(T1ReadWord(MMU.ARM7_REG, 4) & 32) MMU.reg_IF_pending[ARMCPU_ARM7] |= (1<(nds.VCount)); T1WriteWord(MMU.ARM9_REG, 0x1006, static_cast(nds.VCount)); T1WriteWord(MMU.ARM7_REG, 6, static_cast(nds.VCount)); T1WriteWord(MMU.ARM7_REG, 0x1006, static_cast(nds.VCount)); //turn off hblank status bit T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & 0xFFFD); T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFD); //handle vcount status execHardware_hstart_vcount(); //trigger hstart dmas triggerDma(EDMAMode_HStart); if(nds.VCount<192) { //this is hacky. //there is a corresponding hack in doDMA. //it should be driven by a fifo (and generate just in time as the scanline is displayed) //but that isnt even possible until we have some sort of sub-scanline timing. //it may not be necessary. triggerDma(EDMAMode_MemDisplay); } } void NDS_Reschedule() { IF_DEVELOPER(if(!sequencer.reschedule) DEBUG_statistics.sequencerExecutionCounters[0]++;); sequencer.reschedule = true; } /*inline uint32_t _fast_min32(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { return (( ((int32_t)(a-b)) >> (32-1)) & (c^d)) ^ d; }*/ inline uint64_t _fast_min(uint64_t a, uint64_t b) { //you might find that this is faster on a 64bit system; someone should try it //http://aggregate.org/MAGIC/#Integer%20Selection //uint64_t ret = (((((s64)(a-b)) >> (64-1)) & (a^b)) ^ b); //assert(ret==min(a,b)); //return ret; //but this ends up being the fastest on 32bits return a>= 63; //no 64bit shra in sse2, what a disappointment //temp = _mm_and_si128(temp,xorval); //temp = _mm_xor_si128(temp,__b); //return temp.m128i_u64[0]; } uint64_t Sequencer::findNext() { //this one is always enabled so dont bother to check it uint64_t next = dispcnt.next(); if(divider.isEnabled()) next = _fast_min(next,divider.next()); if(sqrtunit.isEnabled()) next = _fast_min(next,sqrtunit.next()); //if(gxfifo.enabled) next = _fast_min(next,gxfifo.next()); #ifdef EXPERIMENTAL_WIFI_COMM next = _fast_min(next,wifi.next()); #endif #define test(X,Y) if(dma_##X##_##Y .isEnabled()) next = _fast_min(next,dma_##X##_##Y .next()); test(0,0); test(0,1); test(0,2); test(0,3); test(1,0); test(1,1); test(1,2); test(1,3); #undef test #define test(X,Y) if(timer_##X##_##Y .enabled) next = _fast_min(next,timer_##X##_##Y .next()); test(0,0); test(0,1); test(0,2); test(0,3); test(1,0); test(1,1); test(1,2); test(1,3); #undef test return next; } void Sequencer::execHardware() { if(dispcnt.isTriggered()) { IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[1]++); switch(dispcnt.param) { case ESI_DISPCNT_HStart: execHardware_hstart(); //(used to be 3168) //hstart is actually 8 dots before the visible drawing begins //we're going to run 1 here and then run 7 in the next case dispcnt.timestamp += 1*6*2; dispcnt.param = ESI_DISPCNT_HStartIRQ; break; case ESI_DISPCNT_HStartIRQ: execHardware_hstart_irq(); dispcnt.timestamp += 7*6*2; dispcnt.param = ESI_DISPCNT_HDraw; break; case ESI_DISPCNT_HDraw: execHardware_hdraw(); //duration of non-blanking period is ~1606 clocks (gbatek agrees) [but says its different on arm7] //im gonna call this 267 dots = 267*6=1602 //so, this event lasts 267 dots minus the 8 dot preroll dispcnt.timestamp += (267-8)*6*2; dispcnt.param = ESI_DISPCNT_HBlank; break; case ESI_DISPCNT_HBlank: execHardware_hblank(); //(once this was 1092 or 1092/12=91 dots.) //there are surely 355 dots per scanline, less 267 for non-blanking period. the rest is hblank and then after that is hstart dispcnt.timestamp += (355-267)*6*2; dispcnt.param = ESI_DISPCNT_HStart; break; } } #ifdef EXPERIMENTAL_WIFI_COMM if(wifi.isTriggered()) { WIFI_usTrigger(); wifi.timestamp += kWifiCycles; } #endif if(divider.isTriggered()) divider.exec(); if(sqrtunit.isTriggered()) sqrtunit.exec(); //if(gxfifo.isTriggered()) gxfifo.exec(); #define test(X,Y) if(dma_##X##_##Y .isTriggered()) dma_##X##_##Y .exec(); test(0,0); test(0,1); test(0,2); test(0,3); test(1,0); test(1,1); test(1,2); test(1,3); #undef test #define test(X,Y) if(timer_##X##_##Y .enabled) if(timer_##X##_##Y .isTriggered()) timer_##X##_##Y .exec(); test(0,0); test(0,1); test(0,2); test(0,3); test(1,0); test(1,1); test(1,2); test(1,3); #undef test } void execHardware_interrupts(); //static void saveUserInput(EMUFILE* os); static bool loadUserInput(EMUFILE* is, int version); /*void nds_savestate(EMUFILE* os) { //version write32le(3,os); //sequencer.save(os); saveUserInput(os); write32le(LidClosed,os); write8le(countLid,os); }*/ bool nds_loadstate(EMUFILE* is, int) { // this isn't part of the savestate loading logic, but // don't skip the next frame after loading a savestate //frameSkipper.OmitSkip(true, true); //read version uint32_t version; if(read32le(&version,is) != 1) return false; if(version > 3) return false; bool temp = true; temp &= sequencer.load(is, version); if(version <= 1 || !temp) return temp; temp &= loadUserInput(is, version); if(version < 3) return temp; uint32_t tmpu32; read32le(&tmpu32,is); uint8_t tmpu8; read8le(&tmpu8,is); return temp; } /*inline void arm9log() { #ifdef LOG_ARM9 if(dolog) { char dasmbuf[4096]; if(NDS_ARM9.CPSR.bits.T) des_thumb_instructions_set[((NDS_ARM9.instruction)>>6)&1023](NDS_ARM9.instruct_adr, NDS_ARM9.instruction, dasmbuf); else des_arm_instructions_set[INDEX(NDS_ARM9.instruction)](NDS_ARM9.instruct_adr, NDS_ARM9.instruction, dasmbuf); #ifdef LOG_TO_FILE if (!fp_dis9) return; #ifdef LOG_TO_FILE_REGS fprintf(fp_dis9, "\t\t;R0:%08X R1:%08X R2:%08X R3:%08X R4:%08X R5:%08X R6:%08X R7:%08X R8:%08X R9:%08X\n\t\t;R10:%08X R11:%08X R12:%08X R13:%08X R14:%08X R15:%08X| next %08X, N:%i Z:%i C:%i V:%i\n", NDS_ARM9.R[0], NDS_ARM9.R[1], NDS_ARM9.R[2], NDS_ARM9.R[3], NDS_ARM9.R[4], NDS_ARM9.R[5], NDS_ARM9.R[6], NDS_ARM9.R[7], NDS_ARM9.R[8], NDS_ARM9.R[9], NDS_ARM9.R[10], NDS_ARM9.R[11], NDS_ARM9.R[12], NDS_ARM9.R[13], NDS_ARM9.R[14], NDS_ARM9.R[15], NDS_ARM9.next_instruction, NDS_ARM9.CPSR.bits.N, NDS_ARM9.CPSR.bits.Z, NDS_ARM9.CPSR.bits.C, NDS_ARM9.CPSR.bits.V); #endif fprintf(fp_dis9, "%s %08X\t%08X \t%s\n", NDS_ARM9.CPSR.bits.T?"THUMB":"ARM", NDS_ARM9.instruct_adr, NDS_ARM9.instruction, dasmbuf);*/ /*if (NDS_ARM9.instruction == 0) { dolog = false; INFO("Disassembler is stopped\n"); }*/ /*#else printf("%05d:%03d %12lld 9:%08X %08X %-30s R00:%08X R01:%08X R02:%08X R03:%08X R04:%08X R05:%08X R06:%08X R07:%08X R08:%08X R09:%08X R10:%08X R11:%08X R12:%08X R13:%08X R14:%08X R15:%08X\n", currFrameCounter, nds.VCount, nds_timer, NDS_ARM9.instruct_adr,NDS_ARM9.instruction, dasmbuf, NDS_ARM9.R[0], NDS_ARM9.R[1], NDS_ARM9.R[2], NDS_ARM9.R[3], NDS_ARM9.R[4], NDS_ARM9.R[5], NDS_ARM9.R[6], NDS_ARM9.R[7], NDS_ARM9.R[8], NDS_ARM9.R[9], NDS_ARM9.R[10], NDS_ARM9.R[11], NDS_ARM9.R[12], NDS_ARM9.R[13], NDS_ARM9.R[14], NDS_ARM9.R[15]); #endif } #endif }*/ /*inline void arm7log() { #ifdef LOG_ARM7 if(dolog) { char dasmbuf[4096]; if(NDS_ARM7.CPSR.bits.T) des_thumb_instructions_set[((NDS_ARM7.instruction)>>6)&1023](NDS_ARM7.instruct_adr, NDS_ARM7.instruction, dasmbuf); else des_arm_instructions_set[INDEX(NDS_ARM7.instruction)](NDS_ARM7.instruct_adr, NDS_ARM7.instruction, dasmbuf); #ifdef LOG_TO_FILE if (!fp_dis7) return; #ifdef LOG_TO_FILE_REGS fprintf(fp_dis7, "\t\t;R0:%08X R1:%08X R2:%08X R3:%08X R4:%08X R5:%08X R6:%08X R7:%08X R8:%08X R9:%08X\n\t\t;R10:%08X R11:%08X R12:%08X R13:%08X R14:%08X R15:%08X| next %08X, N:%i Z:%i C:%i V:%i\n", NDS_ARM7.R[0], NDS_ARM7.R[1], NDS_ARM7.R[2], NDS_ARM7.R[3], NDS_ARM7.R[4], NDS_ARM7.R[5], NDS_ARM7.R[6], NDS_ARM7.R[7], NDS_ARM7.R[8], NDS_ARM7.R[9], NDS_ARM7.R[10], NDS_ARM7.R[11], NDS_ARM7.R[12], NDS_ARM7.R[13], NDS_ARM7.R[14], NDS_ARM7.R[15], NDS_ARM7.next_instruction, NDS_ARM7.CPSR.bits.N, NDS_ARM7.CPSR.bits.Z, NDS_ARM7.CPSR.bits.C, NDS_ARM7.CPSR.bits.V); #endif fprintf(fp_dis7, "%s %08X\t%08X \t%s\n", NDS_ARM7.CPSR.bits.T?"THUMB":"ARM", NDS_ARM7.instruct_adr, NDS_ARM7.instruction, dasmbuf);*/ /*if (NDS_ARM7.instruction == 0) { dolog = false; INFO("Disassembler is stopped\n"); }*/ /*#else printf("%05d:%03d %12lld 7:%08X %08X %-30s R00:%08X R01:%08X R02:%08X R03:%08X R04:%08X R05:%08X R06:%08X R07:%08X R08:%08X R09:%08X R10:%08X R11:%08X R12:%08X R13:%08X R14:%08X R15:%08X\n", currFrameCounter, nds.VCount, nds_timer, NDS_ARM7.instruct_adr,NDS_ARM7.instruction, dasmbuf, NDS_ARM7.R[0], NDS_ARM7.R[1], NDS_ARM7.R[2], NDS_ARM7.R[3], NDS_ARM7.R[4], NDS_ARM7.R[5], NDS_ARM7.R[6], NDS_ARM7.R[7], NDS_ARM7.R[8], NDS_ARM7.R[9], NDS_ARM7.R[10], NDS_ARM7.R[11], NDS_ARM7.R[12], NDS_ARM7.R[13], NDS_ARM7.R[14], NDS_ARM7.R[15]); #endif } #endif }*/ //these have not been tuned very well yet. static const int kMaxWork = 4000; static const int kIrqWait = 4000; template static inline int32_t minarmtime(int32_t arm9, int32_t arm7) { if(doarm9) { if(doarm7) return std::min(arm9,arm7); else return arm9; } else return arm7; } template static /*donotinline*/ std::pair armInnerLoop( const uint64_t nds_timer_base, const int32_t s32next, int32_t arm9, int32_t arm7) { int32_t timer = minarmtime(arm9,arm7); while(timer < s32next && !sequencer.reschedule && execute) { if(doarm9 && (!doarm7 || arm9 <= timer)) { if(!NDS_ARM9.waitIRQ&&!nds.freezeBus) { //arm9log(); arm9 += armcpu_exec(); #ifdef DEVELOPER nds_debug_continuing[0] = false; #endif } else { //int32_t temp = arm9; arm9 = std::min(s32next, arm9 + kIrqWait); //nds.idleCycles[0] += arm9-temp; //if (gxFIFO.size < 255) nds.freezeBus = false; } } if(doarm7 && (!doarm9 || arm7 <= timer)) { if(!NDS_ARM7.waitIRQ&&!nds.freezeBus) { //arm7log(); arm7 += (armcpu_exec()<<1); #ifdef DEVELOPER nds_debug_continuing[1] = false; #endif } else { //int32_t temp = arm7; arm7 = std::min(s32next, arm7 + kIrqWait); //nds.idleCycles[1] += arm7-temp; if(arm7 == s32next) { nds_timer = nds_timer_base + minarmtime(arm9,arm7); return armInnerLoop(nds_timer_base, s32next, arm9, arm7); } } } timer = minarmtime(arm9,arm7); nds_timer = nds_timer_base + timer; } return std::make_pair(arm9, arm7); } /*void NDS_debug_break() { NDS_ARM9.stalled = NDS_ARM7.stalled = 1; //triggers an immediate exit from the cpu loop NDS_Reschedule(); }*/ /*void NDS_debug_continue() { NDS_ARM9.stalled = NDS_ARM7.stalled = 0; }*/ /*void NDS_debug_step() { NDS_debug_continue(); singleStep = true; }*/ template void NDS_exec(int32_t) { //LagFrameFlag=1; /*if((currFrameCounter&63) == 0) MMU_new.backupDevice.lazy_flush();*/ sequencer.nds_vblankEnded = false; //nds.cpuloopIterationCount = 0; IF_DEVELOPER(for(int i=0;i<32;i++) DEBUG_statistics.sequencerExecutionCounters[i] = 0); if(nds.sleeping) { //speculative code: if ANY irq happens, wake up the arm7. //I think the arm7 program analyzes the system and may decide not to wake up //if it is dissatisfied with the conditions if((MMU.reg_IE[1] & MMU.gen_IF<1>())) { nds.sleeping = false; } } else { for(;;) { //trap the debug-stalled condition #ifdef DEVELOPER singleStep = false; //(gdb stub doesnt yet know how to trigger these immediately by calling reschedule) while((NDS_ARM9.stalled || NDS_ARM7.stalled) && execute) { driver->EMU_DebugIdleUpdate(); nds_debug_continuing[0] = nds_debug_continuing[1] = true; } #endif //nds.cpuloopIterationCount++; sequencer.execHardware(); //break out once per frame if(sequencer.nds_vblankEnded) break; //it should be benign to execute execHardware in the next frame, //since there won't be anything for it to do (everything should be scheduled in the future) //bail in case the system halted if(!execute) break; execHardware_interrupts(); //find next work unit: uint64_t next = sequencer.findNext(); next = std::min(next,nds_timer+kMaxWork); //lets set an upper limit for now //printf("%d\n",(next-nds_timer)); sequencer.reschedule = false; //cast these down to 32bits so that things run faster on 32bit procs uint64_t nds_timer_base = nds_timer; int32_t arm9 = (int32_t)(nds_arm9_timer-nds_timer); int32_t arm7 = (int32_t)(nds_arm7_timer-nds_timer); int32_t s32next = (int32_t)(next-nds_timer); #ifdef DEVELOPER if(singleStep) { s32next = 1; } #endif std::pair arm9arm7 = armInnerLoop(nds_timer_base,s32next,arm9,arm7); #ifdef DEVELOPER if(singleStep) { NDS_ARM9.stalled = NDS_ARM7.stalled = 1; } #endif arm9 = arm9arm7.first; arm7 = arm9arm7.second; nds_arm7_timer = nds_timer_base+arm7; nds_arm9_timer = nds_timer_base+arm9; #ifndef NDEBUG //what we find here is dependent on the timing constants above //if(nds_timer>next && (nds_timer-next)>22) // printf("curious. please report: over by %d\n",(int)(nds_timer-next)); #endif //if we were waiting for an irq, don't wait too long: //let's re-analyze it after this hardware event (this rolls back a big burst of irq waiting which may have been interrupted by a resynch) if(NDS_ARM9.waitIRQ) { //nds.idleCycles[0] -= (int32_t)(nds_arm9_timer-nds_timer); nds_arm9_timer = nds_timer; } if(NDS_ARM7.waitIRQ) { //nds.idleCycles[1] -= (int32_t)(nds_arm7_timer-nds_timer); nds_arm7_timer = nds_timer; } } } //DEBUG_statistics.printSequencerExecutionCounters(); //DEBUG_statistics.print(); //end of frame emulation housekeeping /*if(LagFrameFlag) { lagframecounter++; TotalLagFrames++; } else { lastLag = lagframecounter; lagframecounter = 0; }*/ //currFrameCounter++; //DEBUG_Notify.NextFrame(); /*if (cheats) cheats->process();*/ } template static void execHardware_interrupts_core() { uint32_t IF = MMU.gen_IF(); uint32_t IE = MMU.reg_IE[PROCNUM]; uint32_t masked = IF & IE; if(ARMPROC.halt_IE_and_IF && masked) { ARMPROC.halt_IE_and_IF = false; ARMPROC.waitIRQ = false; } if(masked && MMU.reg_IME[PROCNUM] && !ARMPROC.CPSR.bits.I) { //printf("Executing IRQ on procnum %d with IF = %08X and IE = %08X\n",PROCNUM,IF,IE); armcpu_irqException(&ARMPROC); } } void execHardware_interrupts() { execHardware_interrupts_core(); execHardware_interrupts_core(); } //static void resetUserInput(); //bool _HACK_DONT_STOPMOVIE = false; void NDS_Reset() { singleStep = false; //nds_debug_continuing[0] = nds_debug_continuing[1] = false; uint32_t src = 0; uint32_t dst = 0; bool fw_success = false; FILE* inf = NULL; NDS_header * header = NDS_getROMHeader(); //DEBUG_reset(); if (!header) return ; nds.sleeping = false; nds.cardEjected = false; nds.freezeBus = 0; //nds.power1.lcd = nds.power1.gpuMain = nds.power1.gfx3d_render = nds.power1.gfx3d_geometry = nds.power1.gpuSub = nds.power1.dispswap = 1; //nds.power2.speakers = 1; //nds.power2.wifi = 0; nds_timer = 0; nds_arm9_timer = 0; nds_arm7_timer = 0; /*if(movieMode != MOVIEMODE_INACTIVE && !_HACK_DONT_STOPMOVIE) movie_reset_command = true;*/ /*if(movieMode == MOVIEMODE_INACTIVE) { //currFrameCounter = 0; lagframecounter = 0; LagFrameFlag = 0; lastLag = 0; TotalLagFrames = 0; }*/ SPU_DeInit(); MMU_Reset(); //put random garbage in vram for homebrew games, to help mimic the situation where libnds does not clear out junk //which the card's launcher may or may not have left behind //1. retail games dont clear TCM, so why should we jumble it and expect homebrew to clear it? //2. some retail games _dont boot_ if main memory is jumbled. wha...? //3. clearing this is not as useful as tracking uninitialized reads in dev+ builds //4. the vram clearing causes lots of graphical corruptions in badly coded homebrews. this reduces compatibility substantially //conclusion: disable it for now and bring it back as an option //if(gameInfo.isHomebrew) //{ // uint32_t w=100000,x=99,y=117,z=19382173; // CTASSERT(sizeof(MMU.ARM9_LCD) < sizeof(MMU.MAIN_MEM)); // CTASSERT(sizeof(MMU.ARM9_VMEM) < sizeof(MMU.MAIN_MEM)); // CTASSERT(sizeof(MMU.ARM9_ITCM) < sizeof(MMU.MAIN_MEM)); // CTASSERT(sizeof(MMU.ARM9_DTCM) < sizeof(MMU.MAIN_MEM)); // for(int i=0;i>19))^(t^(t>>8))); // //MMU.MAIN_MEM[i] = t; // if (i(0x00002F08, 0x4770); //INFO("ARM7 BIOS is %s.\n", NDS_ARM7.BIOS_loaded?"loaded":"failed"); } else { NDS_ARM7.swi_tab = ARM7_swi_tab; #if 0 // TODO T1WriteLong(MMU.ARM7_BIOS, 0x0000, 0xEAFFFFFE); // loop for Reset !!! T1WriteLong(MMU.ARM7_BIOS, 0x0004, 0xEAFFFFFE); // loop for Undef instr expection T1WriteLong(MMU.ARM7_BIOS, 0x0008, 0xEA00009C); // SWI T1WriteLong(MMU.ARM7_BIOS, 0x000C, 0xEAFFFFFE); // loop for Prefetch Abort T1WriteLong(MMU.ARM7_BIOS, 0x0010, 0xEAFFFFFE); // loop for Data Abort T1WriteLong(MMU.ARM7_BIOS, 0x0014, 0x00000000); // Reserved T1WriteLong(MMU.ARM7_BIOS, 0x001C, 0x00000000); // Fast IRQ #endif T1WriteLong(MMU.ARM7_BIOS, 0x0000, 0xE25EF002); T1WriteLong(MMU.ARM7_BIOS, 0x0018, 0xEA000000); T1WriteLong(MMU.ARM7_BIOS, 0x0020, 0xE92D500F); T1WriteLong(MMU.ARM7_BIOS, 0x0024, 0xE3A00301); T1WriteLong(MMU.ARM7_BIOS, 0x0028, 0xE28FE000); T1WriteLong(MMU.ARM7_BIOS, 0x002C, 0xE510F004); T1WriteLong(MMU.ARM7_BIOS, 0x0030, 0xE8BD500F); T1WriteLong(MMU.ARM7_BIOS, 0x0034, 0xE25EF004); } //ARM9 BIOS IRQ HANDLER if(CommonSettings.UseExtBIOS == true) inf = fopen(CommonSettings.ARM9BIOS,"rb"); else inf = NULL; if(inf) { if (fread(MMU.ARM9_BIOS,1,4096,inf) == 4096) NDS_ARM9.BIOS_loaded = true; fclose(inf); if((CommonSettings.SWIFromBIOS) && (NDS_ARM9.BIOS_loaded)) NDS_ARM9.swi_tab = 0; else NDS_ARM9.swi_tab = ARM9_swi_tab; if (CommonSettings.PatchSWI3) _MMU_write16(0xFFFF07CC, 0x4770); //INFO("ARM9 BIOS is %s.\n", NDS_ARM9.BIOS_loaded?"loaded":"failed"); } else { NDS_ARM9.swi_tab = ARM9_swi_tab; //bios chains data abort to fast irq //exception vectors: T1WriteLong(MMU.ARM9_BIOS, 0x0000, 0xEAFFFFFE); // (infinite loop for) Reset !!! //T1WriteLong(MMU.ARM9_BIOS, 0x0004, 0xEAFFFFFE); // (infinite loop for) Undefined instruction T1WriteLong(MMU.ARM9_BIOS, 0x0004, 0xEA000004); // Undefined instruction -> Fast IRQ (just guessing) T1WriteLong(MMU.ARM9_BIOS, 0x0008, 0xEA00009C); // SWI -> ????? T1WriteLong(MMU.ARM9_BIOS, 0x000C, 0xEAFFFFFE); // (infinite loop for) Prefetch Abort T1WriteLong(MMU.ARM9_BIOS, 0x0010, 0xEA000001); // Data Abort -> Fast IRQ T1WriteLong(MMU.ARM9_BIOS, 0x0014, 0x00000000); // Reserved T1WriteLong(MMU.ARM9_BIOS, 0x0018, 0xEA000095); // Normal IRQ -> 0x0274 T1WriteLong(MMU.ARM9_BIOS, 0x001C, 0xEA00009D); // Fast IRQ -> 0x0298 // logo (do some games fail to boot without this? example?) for (int t = 0; t < 0x9C; t++) MMU.ARM9_BIOS[t + 0x20] = logo_data[t]; //...0xBC: //(now what goes in this gap??) //IRQ handler: get dtcm address and jump to a vector in it T1WriteLong(MMU.ARM9_BIOS, 0x0274, 0xE92D500F); //STMDB SP!, {R0-R3,R12,LR} T1WriteLong(MMU.ARM9_BIOS, 0x0278, 0xEE190F11); //MRC CP15, 0, R0, CR9, CR1, 0 T1WriteLong(MMU.ARM9_BIOS, 0x027C, 0xE1A00620); //MOV R0, R0, LSR #C T1WriteLong(MMU.ARM9_BIOS, 0x0280, 0xE1A00600); //MOV R0, R0, LSL #C T1WriteLong(MMU.ARM9_BIOS, 0x0284, 0xE2800C40); //ADD R0, R0, #4000 T1WriteLong(MMU.ARM9_BIOS, 0x0288, 0xE28FE000); //ADD LR, PC, #0 T1WriteLong(MMU.ARM9_BIOS, 0x028C, 0xE510F004); //LDR PC, [R0, -#4] //???? T1WriteLong(MMU.ARM9_BIOS, 0x0290, 0xE8BD500F); //LDMIA SP!, {R0-R3,R12,LR} T1WriteLong(MMU.ARM9_BIOS, 0x0294, 0xE25EF004); //SUBS PC, LR, #4 //------- //FIQ and abort exception handler //TODO - this code is copied from the bios. refactor it //friendly reminder: to calculate an immediate offset: encoded = (desired_address-cur_address-8) T1WriteLong(MMU.ARM9_BIOS, 0x0298, 0xE10FD000); //MRS SP, CPSR T1WriteLong(MMU.ARM9_BIOS, 0x029C, 0xE38DD0C0); //ORR SP, SP, #C0 T1WriteLong(MMU.ARM9_BIOS, 0x02A0, 0xE12FF00D); //MSR CPSR_fsxc, SP T1WriteLong(MMU.ARM9_BIOS, 0x02A4, 0xE59FD000 | (0x2D4-0x2A4-8)); //LDR SP, [FFFF02D4] T1WriteLong(MMU.ARM9_BIOS, 0x02A8, 0xE28DD001); //ADD SP, SP, #1 T1WriteLong(MMU.ARM9_BIOS, 0x02AC, 0xE92D5000); //STMDB SP!, {R12,LR} T1WriteLong(MMU.ARM9_BIOS, 0x02B0, 0xE14FE000); //MRS LR, SPSR T1WriteLong(MMU.ARM9_BIOS, 0x02B4, 0xEE11CF10); //MRC CP15, 0, R12, CR1, CR0, 0 T1WriteLong(MMU.ARM9_BIOS, 0x02B8, 0xE92D5000); //STMDB SP!, {R12,LR} T1WriteLong(MMU.ARM9_BIOS, 0x02BC, 0xE3CCC001); //BIC R12, R12, #1 T1WriteLong(MMU.ARM9_BIOS, 0x02C0, 0xEE01CF10); //MCR CP15, 0, R12, CR1, CR0, 0 T1WriteLong(MMU.ARM9_BIOS, 0x02C4, 0xE3CDC001); //BIC R12, SP, #1 T1WriteLong(MMU.ARM9_BIOS, 0x02C8, 0xE59CC010); //LDR R12, [R12, #10] T1WriteLong(MMU.ARM9_BIOS, 0x02CC, 0xE35C0000); //CMP R12, #0 T1WriteLong(MMU.ARM9_BIOS, 0x02D0, 0x112FFF3C); //BLXNE R12 T1WriteLong(MMU.ARM9_BIOS, 0x02D4, 0x027FFD9C); //0x027FFD9C //--------- } #ifdef LOG_ARM7 if (fp_dis7 != NULL) { fclose(fp_dis7); fp_dis7 = NULL; } fp_dis7 = fopen("D:\\desmume_dis7.asm", "w"); #endif #ifdef LOG_ARM9 if (fp_dis9 != NULL) { fclose(fp_dis9); fp_dis9 = NULL; } fp_dis9 = fopen("D:\\desmume_dis9.asm", "w"); #endif if (firmware) { delete firmware; firmware = NULL; } firmware = new CFIRMWARE(); fw_success = firmware->load(); if (NDS_ARM7.BIOS_loaded && NDS_ARM9.BIOS_loaded && CommonSettings.BootFromFirmware && fw_success) { // Copy secure area to memory if needed if ((header->ARM9src >= 0x4000) && (header->ARM9src < 0x8000)) { src = header->ARM9src; dst = header->ARM9cpy; uint32_t size = (0x8000 - src) >> 2; //INFO("Copy secure area from 0x%08X to 0x%08X (size %i/0x%08X)\n", src, dst, size, size); for (uint32_t i = 0; i < size; i++) { _MMU_write32(dst, T1ReadLong(MMU.CART_ROM, src)); src += 4; dst += 4; } } if (firmware->patched) { armcpu_init(&NDS_ARM7, 0x00000008); armcpu_init(&NDS_ARM9, 0xFFFF0008); } else { //INFO("Booting at ARM9: 0x%08X, ARM7: 0x%08X\n", firmware->ARM9bootAddr, firmware->ARM7bootAddr); // need for firmware //armcpu_init(&NDS_ARM7, 0x00000008); //armcpu_init(&NDS_ARM9, 0xFFFF0008); armcpu_init(&NDS_ARM7, firmware->ARM7bootAddr); armcpu_init(&NDS_ARM9, firmware->ARM9bootAddr); } _MMU_write08(0x04000300, 0); _MMU_write08(0x04000300, 0); } else { src = header->ARM9src; dst = header->ARM9cpy; for(uint32_t i = 0; i < (header->ARM9binSize>>2); ++i) { _MMU_write32(dst, T1ReadLong(MMU.CART_ROM, src)); dst += 4; src += 4; } src = header->ARM7src; dst = header->ARM7cpy; for(uint32_t i = 0; i < (header->ARM7binSize>>2); ++i) { _MMU_write32(dst, T1ReadLong(MMU.CART_ROM, src)); dst += 4; src += 4; } armcpu_init(&NDS_ARM7, header->ARM7exe); armcpu_init(&NDS_ARM9, header->ARM9exe); _MMU_write08(REG_POSTFLG, 1); _MMU_write08(REG_POSTFLG, 1); } //bitbox 4k demo is so stripped down it relies on default stack values //otherwise the arm7 will crash before making a sound //(these according to gbatek softreset bios docs) NDS_ARM7.R13_svc = 0x0380FFDC; NDS_ARM7.R13_irq = 0x0380FFB0; NDS_ARM7.R13_usr = 0x0380FF00; NDS_ARM7.R[13] = NDS_ARM7.R13_usr; //and let's set these for the arm9 while we're at it, though we have no proof NDS_ARM9.R13_svc = 0x00803FC0; NDS_ARM9.R13_irq = 0x00803FA0; NDS_ARM9.R13_usr = 0x00803EC0; NDS_ARM9.R13_abt = NDS_ARM9.R13_usr; //????? //I think it is wrong to take gbatek's "SYS" and put it in USR--maybe USR doesnt matter. //i think SYS is all the misc modes. please verify by setting nonsensical stack values for USR here NDS_ARM9.R[13] = NDS_ARM9.R13_usr; //n.b.: im not sure about all these, I dont know enough about arm9 svc/irq/etc modes //and how theyre named in desmume to match them up correctly. i just guessed. //nds.wifiCycle = 0; memset(nds.timerCycle, 0, sizeof(uint64_t) * 2 * 4); nds.old = 0; //nds.touchX = nds.touchY = 0; //nds.isTouch = 0; //nds.debugConsole = CommonSettings.DebugConsole; //nds.ensataEmulation = CommonSettings.EnsataEmulation; //nds.ensataHandshake = ENSATA_HANDSHAKE_none; //nds.ensataIpcSyncCounter = 0; SetupMMU(/*nds.Is_DebugConsole()*/false,nds.Is_DSI()); _MMU_write16(REG_KEYINPUT, 0x3FF); _MMU_write16(REG_KEYINPUT, 0x3FF); _MMU_write08(REG_EXTKEYIN, 0x43); //LidClosed = false; //countLid = 0; //resetUserInput(); //Setup a copy of the firmware user settings in memory. //(this is what the DS firmware would do). { uint8_t temp_buffer[NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT]; int fw_index; if ( copy_firmware_user_data( temp_buffer, &MMU.fw.data[0])) { for ( fw_index = 0; fw_index < NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT; fw_index++) _MMU_write08(0x027FFC80 + fw_index, temp_buffer[fw_index]); } } // Copy the whole header to Main RAM 0x27FFE00 on startup. (http://nocash.emubase.de/gbatek.htm#dscartridgeheader) //once upon a time this copied 0x90 more. this was thought to be wrong, and changed. if(nds.Is_DSI()) { //dsi needs this copied later in memory. there are probably a number of things that get copied to a later location in memory.. thats where the NDS consoles tend to stash stuff. for (int i = 0; i < ((0x170)/4); i++) _MMU_write32(0x02FFFE00+i*4, LE_TO_LOCAL_32(((uint32_t*)MMU.CART_ROM)[i])); } else { for (int i = 0; i < ((0x170)/4); i++) _MMU_write32(0x027FFE00+i*4, LE_TO_LOCAL_32(((uint32_t*)MMU.CART_ROM)[i])); } // Write the header checksum to memory (the firmware needs it to see the cart) _MMU_write16(0x027FF808, T1ReadWord(MMU.CART_ROM, 0x15E)); //-------------------------------- //setup the homebrew argv //this is useful for nitrofs apps which are emulating themselves via cflash //struct __argv { // int argvMagic; //!< argv magic number, set to 0x5f617267 ('_arg') if valid // char *commandLine; //!< base address of command line, set of null terminated strings // int length; //!< total length of command line // int argc; //!< internal use, number of arguments // char **argv; //!< internal use, argv pointer //}; std::string rompath = "fat:/" + path.RomName; const uint32_t kCommandline = 0x027E0000; //const uint32_t kCommandline = 0x027FFF84; // _MMU_write32(0x02FFFE70, 0x5f617267); _MMU_write32(0x02FFFE74, kCommandline); //(commandline starts here) _MMU_write32(0x02FFFE78, rompath.size()+1); //0x027FFF7C (argc) //0x027FFF80 (argv) for(size_t i=0;i(kCommandline+i, rompath[i]); _MMU_write08(kCommandline+rompath.size(), 0); //-------------------------------- if ((firmware->patched) && (CommonSettings.UseExtBIOS == true) && (CommonSettings.BootFromFirmware == true) && (fw_success == true)) { // HACK! for flashme _MMU_write32(0x27FFE24, firmware->ARM9bootAddr); _MMU_write32(0x27FFE34, firmware->ARM7bootAddr); } // make system think it's booted from card -- EXTREMELY IMPORTANT!!! Thanks to cReDiAr _MMU_write08(0x02FFFC40,0x1); _MMU_write08(0x02FFFC40,0x1); // Save touchscreen calibration info in a structure // so we can easily access it at any time //TSCal.adc.x1 = _MMU_read16(0x027FFC80 + 0x58); //TSCal.adc.y1 = _MMU_read16(0x027FFC80 + 0x5A); //TSCal.scr.x1 = _MMU_read08(0x027FFC80 + 0x5C); //TSCal.scr.y1 = _MMU_read08(0x027FFC80 + 0x5D); //TSCal.adc.x2 = _MMU_read16(0x027FFC80 + 0x5E); //TSCal.adc.y2 = _MMU_read16(0x027FFC80 + 0x60); //TSCal.scr.x2 = _MMU_read08(0x027FFC80 + 0x62); //TSCal.scr.y2 = _MMU_read08(0x027FFC80 + 0x63); //TSCal.adc.width = (TSCal.adc.x2 - TSCal.adc.x1); //TSCal.adc.height = (TSCal.adc.y2 - TSCal.adc.y1); //TSCal.scr.width = (TSCal.scr.x2 - TSCal.scr.x1); //TSCal.scr.height = (TSCal.scr.y2 - TSCal.scr.y1); //MainScreen.offset = 0; //SubScreen.offset = 192; //_MMU_write32[ARMCPU_ARM9](0x02007FFC, 0xE92D4030); delete header; //Screen_Reset(); //gfx3d_reset(); //gpu3D->NDS_3D_Reset(); //slot1Reset(); //WIFI_Reset(); //memcpy(FW_Mac, (MMU.fw.data + 0x36), 6); initSchedule(); SPU_ReInit(); } /*static std::string MakeInputDisplayString(uint16_t pad, const std::string* Buttons, int count) { std::string s; for (int x = 0; x < count; x++) { if (pad & (1 << x)) s.append(Buttons[x].size(), ' '); else s += Buttons[x]; } return s; }*/ /*static std::string MakeInputDisplayString(uint16_t pad, uint16_t padExt) { const std::string Buttons[] = {"A", "B", "Sl", "St", "R", "L", "U", "D", "Rs", "Ls"}; const std::string Ext[] = {"X", "Y"}; std::string s = MakeInputDisplayString(pad, Ext, ARRAY_SIZE(Ext)); s += MakeInputDisplayString(padExt, Buttons, ARRAY_SIZE(Buttons)); return s; }*/ //buttonstruct Turbo; buttonstruct TurboTime; //buttonstruct AutoHold; /*void ClearAutoHold() { for (uint32_t i=0; i < ARRAY_SIZE(AutoHold.array); i++) { AutoHold.array[i]=false; } }*/ /*inline uint16_t NDS_getADCTouchPosX(uint16_t scrX) { // this is a little iffy, // we're basically adjusting the ADC results to // compensate for how they will be interpreted. // the actual system doesn't do this transformation. int rv = (scrX - TSCal.scr.x1 + 1) * TSCal.adc.width / TSCal.scr.width + TSCal.adc.x1; rv = min(0xFFF, max(0, rv)); return (uint16_t)rv; }*/ /*inline uint16_t NDS_getADCTouchPosY(uint16_t scrY) { int rv = (scrY - TSCal.scr.y1 + 1) * TSCal.adc.height / TSCal.scr.height + TSCal.adc.y1; rv = min(0xFFF, max(0, rv)); return (uint16_t)rv; }*/ //static UserInput rawUserInput = {}; // requested input, generally what the user is physically pressing static UserInput intermediateUserInput; // intermediate buffer for modifications (seperated from finalUserInput for safety reasons) static UserInput finalUserInput; // what gets sent to the game and possibly recorded bool validToProcessInput = false; /*const UserInput& NDS_getRawUserInput() { return rawUserInput; }*/ /*UserInput& NDS_getProcessingUserInput() { assert(validToProcessInput); return intermediateUserInput; }*/ /*bool NDS_isProcessingUserInput() { return validToProcessInput; }*/ /*const UserInput& NDS_getFinalUserInput() { return finalUserInput; }*/ /*static void saveUserInput(EMUFILE* os, UserInput& input) { os->fwrite((const char*)input.buttons.array, 14); writebool(input.touch.isTouch, os); write16le(input.touch.touchX, os); write16le(input.touch.touchY, os); write32le(input.mic.micButtonPressed, os); }*/ static bool loadUserInput(EMUFILE* is, UserInput& input, int) { is->fread((char*)input.buttons.array, 14); readbool(&input.touch.isTouch, is); read16le(&input.touch.touchX, is); read16le(&input.touch.touchY, is); read32le(&input.mic.micButtonPressed, is); return true; } /*static void resetUserInput(UserInput& input) { memset(&input, 0, sizeof(UserInput)); }*/ // (userinput is kind of a misnomer, e.g. finalUserInput has to mirror nds.pad, nds.touchX, etc.) /*static void saveUserInput(EMUFILE* os) { saveUserInput(os, finalUserInput); saveUserInput(os, intermediateUserInput); // saved in case a savestate is made during input processing (which Lua could do if nothing else) writebool(validToProcessInput, os); for(int i = 0; i < 14; i++) write32le(TurboTime.array[i], os); // saved to make autofire more tolerable to use with re-recording }*/ static bool loadUserInput(EMUFILE* is, int version) { bool rv = true; rv &= loadUserInput(is, finalUserInput, version); rv &= loadUserInput(is, intermediateUserInput, version); readbool(&validToProcessInput, is); for(int i = 0; i < 14; i++) read32le((uint32_t*)&TurboTime.array[i], is); return rv; } /*static void resetUserInput() { resetUserInput(finalUserInput); resetUserInput(intermediateUserInput); }*/ /*static inline void gotInputRequest() { // nobody should set the raw input while we're processing the input. // it might not screw anything up but it would be completely useless. assert(!validToProcessInput); }*/ /*void NDS_setPad(bool R,bool L,bool D,bool U,bool T,bool S,bool B,bool A,bool Y,bool X,bool W,bool E,bool G, bool F) { gotInputRequest(); UserButtons& rawButtons = rawUserInput.buttons; rawButtons.R = R; rawButtons.L = L; rawButtons.D = D; rawButtons.U = U; rawButtons.T = T; rawButtons.S = S; rawButtons.B = B; rawButtons.A = A; rawButtons.Y = Y; rawButtons.X = X; rawButtons.W = W; rawButtons.E = E; rawButtons.G = G; rawButtons.F = F; }*/ /*void NDS_setTouchPos(uint16_t x, uint16_t y) { gotInputRequest(); rawUserInput.touch.touchX = NDS_getADCTouchPosX(x); rawUserInput.touch.touchY = NDS_getADCTouchPosY(y); rawUserInput.touch.isTouch = true;*/ /*if(movieMode != MOVIEMODE_INACTIVE && movieMode != MOVIEMODE_FINISHED) { // just in case, since the movie only stores 8 bits per touch coord rawUserInput.touch.touchX &= 0x0FF0; rawUserInput.touch.touchY &= 0x0FF0; }*/ /* #ifndef WIN32 // FIXME: this code should be deleted from here, // other platforms should call NDS_beginProcessingInput,NDS_endProcessingInput once per frame instead // (see the function called "StepRunLoop_Core" in src/windows/main.cpp), // but I'm leaving this here for now since I can't test those other platforms myself. nds.touchX = rawUserInput.touch.touchX; nds.touchY = rawUserInput.touch.touchY; nds.isTouch = 1; MMU.ARM7_REG[0x136] &= 0xBF; #endif }*/ /*void NDS_releaseTouch() { gotInputRequest(); rawUserInput.touch.touchX = 0; rawUserInput.touch.touchY = 0; rawUserInput.touch.isTouch = false; #ifndef WIN32 // FIXME: this code should be deleted from here, // other platforms should call NDS_beginProcessingInput,NDS_endProcessingInput once per frame instead // (see the function called "StepRunLoop_Core" in src/windows/main.cpp), // but I'm leaving this here for now since I can't test those other platforms myself. nds.touchX = 0; nds.touchY = 0; nds.isTouch = 0; MMU.ARM7_REG[0x136] |= 0x40; #endif }*/ /*void NDS_setMic(bool pressed) { gotInputRequest(); rawUserInput.mic.micButtonPressed = (pressed ? true : false); }*/ //static void NDS_applyFinalInput(); /*void NDS_beginProcessingInput() { // start off from the raw input intermediateUserInput = rawUserInput; // processing is valid now validToProcessInput = true; }*/ /*void NDS_endProcessingInput() { // transfer the processed input finalUserInput = intermediateUserInput; // processing is invalid now validToProcessInput = false; // use the final input for a few things right away NDS_applyFinalInput(); }*/ /*static void NDS_applyFinalInput() { const UserInput& input = NDS_getFinalUserInput(); uint16_t pad = (0 | ((input.buttons.A ? 0 : 0x80) >> 7) | ((input.buttons.B ? 0 : 0x80) >> 6) | ((input.buttons.T ? 0 : 0x80) >> 5) | ((input.buttons.S ? 0 : 0x80) >> 4) | ((input.buttons.R ? 0 : 0x80) >> 3) | ((input.buttons.L ? 0 : 0x80) >> 2) | ((input.buttons.U ? 0 : 0x80) >> 1) | ((input.buttons.D ? 0 : 0x80) ) | ((input.buttons.E ? 0 : 0x80) << 1) | ((input.buttons.W ? 0 : 0x80) << 2)) ; ((uint16_t *)MMU.ARM9_REG)[0x130>>1] = (uint16_t)pad; ((uint16_t *)MMU.ARM7_REG)[0x130>>1] = (uint16_t)pad; uint16_t k_cnt = ((uint16_t *)MMU.ARM9_REG)[0x132>>1]; if ( k_cnt & (1<<14)) { //INFO("ARM9: KeyPad IRQ (pad 0x%04X, cnt 0x%04X (condition %s))\n", pad, k_cnt, k_cnt&(1<<15)?"AND":"OR"); uint16_t k_cnt_selected = (k_cnt & 0x3F); if (k_cnt&(1<<15)) // AND { if ((~pad & k_cnt_selected) == k_cnt_selected) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_KEYPAD); } else // OR { if (~pad & k_cnt_selected) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_KEYPAD); } } k_cnt = ((uint16_t *)MMU.ARM7_REG)[0x132>>1]; if ( k_cnt & (1<<14)) { //INFO("ARM7: KeyPad IRQ (pad 0x%04X, cnt 0x%04X (condition %s))\n", pad, k_cnt, k_cnt&(1<<15)?"AND":"OR"); uint16_t k_cnt_selected = (k_cnt & 0x3F); if (k_cnt&(1<<15)) // AND { if ((~pad & k_cnt_selected) == k_cnt_selected) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_KEYPAD); } else // OR { if (~pad & k_cnt_selected) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_KEYPAD); } } if(input.touch.isTouch) { nds.touchX = input.touch.touchX; nds.touchY = input.touch.touchY; nds.isTouch = 1; MMU.ARM7_REG[0x136] &= 0xBF; } else { nds.touchX = 0; nds.touchY = 0; nds.isTouch = 0; MMU.ARM7_REG[0x136] |= 0x40; } if (input.buttons.F && !countLid) { LidClosed = (!LidClosed) & 0x01; if (!LidClosed) { // SPU_Pause(false); NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_ARM7_FOLD); } //else //SPU_Pause(true); countLid = 30; } else { if (countLid > 0) countLid--; } uint16_t padExt = (((uint16_t *)MMU.ARM7_REG)[0x136>>1] & 0x0070) | ((input.buttons.X ? 0 : 0x80) >> 7) | ((input.buttons.Y ? 0 : 0x80) >> 6) | ((input.buttons.G ? 0 : 0x80) >> 4) | ((LidClosed) << 7) | 0x0034; ((uint16_t *)MMU.ARM7_REG)[0x136>>1] = (uint16_t)padExt; InputDisplayString=MakeInputDisplayString(padExt, pad); //put into the format we want for the movie system //fRLDUTSBAYXWEg //we don't really need nds.pad anymore, but removing it would be a pain nds.pad = ((input.buttons.R ? 1 : 0) << 12)| ((input.buttons.L ? 1 : 0) << 11)| ((input.buttons.D ? 1 : 0) << 10)| ((input.buttons.U ? 1 : 0) << 9)| ((input.buttons.T ? 1 : 0) << 8)| ((input.buttons.S ? 1 : 0) << 7)| ((input.buttons.B ? 1 : 0) << 6)| ((input.buttons.A ? 1 : 0) << 5)| ((input.buttons.Y ? 1 : 0) << 4)| ((input.buttons.X ? 1 : 0) << 3)| ((input.buttons.W ? 1 : 0) << 2)| ((input.buttons.E ? 1 : 0) << 1); // TODO: low power IRQ }*/ /*void NDS_suspendProcessingInput(bool suspend) { static int suspendCount = 0; if(suspend) { // enter non-processing block assert(validToProcessInput); validToProcessInput = false; suspendCount++; } else if(suspendCount) { // exit non-processing block validToProcessInput = true; suspendCount--; } else { // unwound past first time -> not processing validToProcessInput = false; } }*/ /*void emu_halt() { //printf("halting emu: ARM9 PC=%08X/%08X, ARM7 PC=%08X/%08X\n", NDS_ARM9.R[15], NDS_ARM9.instruct_adr, NDS_ARM7.R[15], NDS_ARM7.instruct_adr); execute = false; #ifdef LOG_ARM9 if (fp_dis9) { char buf[256] = { 0 }; sprintf(buf, "halting emu: ARM9 PC=%08X/%08X\n", NDS_ARM9.R[15], NDS_ARM9.instruct_adr); fwrite(buf, 1, strlen(buf), fp_dis9); INFO("ARM9 halted\n"); } #endif #ifdef LOG_ARM7 if (fp_dis7) { char buf[256] = { 0 }; sprintf(buf, "halting emu: ARM7 PC=%08X/%08X\n", NDS_ARM7.R[15], NDS_ARM7.instruct_adr); fwrite(buf, 1, strlen(buf), fp_dis7); INFO("ARM7 halted\n"); } #endif }*/ //returns true if exmemcnt specifies satisfactory parameters for the device, which calls this function /*bool ValidateSlot2Access(uint32_t procnum, uint32_t demandSRAMSpeed, uint32_t demand1stROMSpeed, uint32_t demand2ndROMSpeed, int clockbits) { static const uint32_t _sramSpeeds[] = {10,8,6,18}; static const uint32_t _rom1Speeds[] = {10,8,6,18}; static const uint32_t _rom2Speeds[] = {6,4}; uint16_t exmemcnt = T1ReadWord(MMU.MMU_MEM[procnum][0x40], 0x204); uint16_t exmemcnt9 = T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x204); uint32_t arm7access = (exmemcnt9 & EXMEMCNT_MASK_SLOT2_ARM7); uint32_t sramSpeed = _sramSpeeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_SRAM_TIME)]; uint32_t romSpeed1 = _rom1Speeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_ROM_1ST_TIME)>>2]; uint32_t romSpeed2 = _rom2Speeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_ROM_2ND_TIME)>>4]; uint32_t curclockbits = (exmemcnt & EXMEMCNT_MASK_SLOT2_CLOCKRATE)>>5; if(procnum==ARMCPU_ARM9 && arm7access) return false; if(procnum==ARMCPU_ARM7 && !arm7access) return false; //what we're interested in here is whether the rom/ram are too low -> too fast. then accesses won't have enough time to work. //i'm not sure if this gives us enough flexibility, but it is good enough for now. //should make the arguments to this function bitmasks later if we need better. if(sramSpeed < demandSRAMSpeed) return false; if(romSpeed1 < demand1stROMSpeed) return false; if(romSpeed2 < demand2ndROMSpeed) return false; if(clockbits != -1 && clockbits != (int)curclockbits) return false; return true; }*/ //these templates needed to be instantiated manually template void NDS_exec(int32_t nb); template void NDS_exec(int32_t nb);