1 /*
2 Copyright (C) 2006 yopyop
3 Copyright (C) 2008-2015 DeSmuME team
4
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (at your option) any later version.
9
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with the this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "NDSSystem.h"
20
21 #include <string.h>
22 #include <stdlib.h>
23 #include <algorithm>
24 #include <math.h>
25
26 #include <string/stdstring.h>
27 extern unsigned long crc32(unsigned long, const unsigned char*,unsigned int);
28
29 #include "utils/dlditool.h"
30 #include "utils/decrypt/decrypt.h"
31 #include "utils/decrypt/crc.h"
32 #include "utils/advanscene.h"
33 #include "utils/task.h"
34 #include "utils/bits.h"
35
36 #include "common.h"
37 #include "armcpu.h"
38 #include "render3D.h"
39 #include "MMU.h"
40 #include "gfx3d.h"
41 #include "GPU.h"
42 #include "cp15.h"
43 #include "bios.h"
44 #include "debug.h"
45 #include "cheatSystem.h"
46 #include "movie.h"
47 #include "emufile.h"
48 #ifdef DEBUG
49 #include "Disassembler.h"
50 #endif
51 #include "FIFO.h"
52 #include "readwrite.h"
53 #include "registers.h"
54 #ifdef DEBUG
55 #include "debug.h"
56 #endif
57 #include "driver.h"
58 #include "firmware.h"
59 #include "version.h"
60 #include "path.h"
61 #include "slot1.h"
62 #include "slot2.h"
63 #include "SPU.h"
64 #include "wifi.h"
65
66
67 //int xxctr=0;
68 //#define LOG_ARM9
69 //#define LOG_ARM7
70 //#define dolog (currFrameCounter>30)
71 bool dolog = false;
72 //#define LOG_TO_FILE
73 //#define LOG_TO_FILE_REGS
74
75 //===============================================================
76 FILE *fp_dis7 = NULL;
77 FILE *fp_dis9 = NULL;
78
79 PathInfo path;
80
81 TCommonSettings CommonSettings;
82 static BaseDriver _stub_driver;
83 BaseDriver* driver = &_stub_driver;
84
85 static BOOL LidClosed = FALSE;
86 static u8 countLid = 0;
87
88 GameInfo gameInfo;
89 NDSSystem nds;
90 CFIRMWARE *firmware = NULL;
91
92 using std::min;
93 using std::max;
94
95 bool singleStep;
96 bool nds_debug_continuing[2];
97
98 TSCalInfo TSCal;
99
100 extern "C" int DLDI_tryPatch(void* data, size_t size, unsigned int device);
101
Desmume_InitOnce()102 void Desmume_InitOnce()
103 {
104 static bool initOnce = false;
105 if(initOnce) return;
106 initOnce = true;
107 }
108
NDS_GetCPUCoreCount(void)109 int NDS_GetCPUCoreCount(void)
110 {
111 int amount = cpu_features_get_core_amount();
112 return amount;
113 }
114
NDS_SetupDefaultFirmware()115 void NDS_SetupDefaultFirmware()
116 {
117 NDS_FillDefaultFirmwareConfigData(&CommonSettings.fw_config);
118 }
119
NDS_RunAdvansceneAutoImport()120 void NDS_RunAdvansceneAutoImport()
121 {
122 if(CommonSettings.run_advanscene_import != "")
123 {
124 std::string fname = CommonSettings.run_advanscene_import;
125 std::string fname_out = fname + ".ddb";
126 EMUFILE_FILE outf(fname_out,"wb");
127 u32 ret = advsc.convertDB(fname.c_str(),&outf);
128 if(ret == 0)
129 exit(0);
130 else exit(1);
131 }
132 }
133
NDS_Init()134 int NDS_Init()
135 {
136 nds.idleFrameCounter = 0;
137 memset(nds.runCycleCollector,0,sizeof(nds.runCycleCollector));
138 MMU_Init();
139
140 //got to print this somewhere..
141 printf("%s\n", EMU_DESMUME_NAME_AND_VERSION());
142
143 {
144 char buf[PATH_MAX_LENGTH];
145 memset(buf, 0, sizeof(buf));
146 strcpy(buf, path.pathToModule);
147 strcat(buf, "desmume.ddb"); // DeSmuME database :)
148 advsc.setDatabase(buf);
149
150 //why is this done here? shitty engineering. not intended.
151 NDS_RunAdvansceneAutoImport();
152 }
153
154 armcpu_new(&NDS_ARM9,0);
155 NDS_ARM9.SetBaseMemoryInterface(&arm9_base_memory_iface);
156 NDS_ARM9.SetBaseMemoryInterfaceData(NULL);
157 NDS_ARM9.ResetMemoryInterfaceToBase();
158
159 armcpu_new(&NDS_ARM7,1);
160 NDS_ARM7.SetBaseMemoryInterface(&arm7_base_memory_iface);
161 NDS_ARM7.SetBaseMemoryInterfaceData(NULL);
162 NDS_ARM7.ResetMemoryInterfaceToBase();
163
164 if (GPU != NULL)
165 delete GPU;
166
167 GPU = new GPUSubsystem;
168
169 if (SPU_Init(SNDCORE_DUMMY, 740) != 0)
170 return -1;
171
172 WIFI_Init() ;
173
174 cheats = new CHEATS();
175
176 return 0;
177 }
178
NDS_DeInit(void)179 void NDS_DeInit(void)
180 {
181 gameInfo.closeROM();
182 SPU_DeInit();
183
184 delete GPU;
185 GPU = NULL;
186
187 MMU_DeInit();
188
189 WIFI_DeInit();
190
191 delete cheats;
192 cheats = NULL;
193
194 #ifdef HAVE_JIT
195 arm_jit_close();
196 #endif
197
198 #ifdef LOG_ARM7
199 if (fp_dis7 != NULL)
200 {
201 fclose(fp_dis7);
202 fp_dis7 = NULL;
203 }
204 #endif
205
206 #ifdef LOG_ARM9
207 if (fp_dis9 != NULL)
208 {
209 fclose(fp_dis9);
210 fp_dis9 = NULL;
211 }
212 #endif
213 }
214
NDS_getROMHeader(void)215 NDS_header* NDS_getROMHeader(void)
216 {
217 NDS_header *newHeader = new NDS_header;
218 memcpy(newHeader, &gameInfo.header, sizeof(NDS_header));
219
220 return newHeader;
221 }
222
223 #ifdef DEBUG
debug()224 void debug()
225 {
226 //if(NDS_ARM9.R[15]==0x020520DC) emu_halt();
227 //DSLinux
228 //if(NDS_ARM9.CPSR.bits.mode == 0) emu_halt();
229 //if((NDS_ARM9.R[15]&0xFFFFF000)==0) emu_halt();
230 //if((NDS_ARM9.R[15]==0x0201B4F4)/*&&(NDS_ARM9.R[1]==0x0)*/) emu_halt();
231 //AOE
232 //if((NDS_ARM9.R[15]==0x01FFE194)&&(NDS_ARM9.R[0]==0)) emu_halt();
233 //if((NDS_ARM9.R[15]==0x01FFE134)&&(NDS_ARM9.R[0]==0)) emu_halt();
234
235 //BBMAN
236 //if(NDS_ARM9.R[15]==0x02098B4C) emu_halt();
237 //if(NDS_ARM9.R[15]==0x02004924) emu_halt();
238 //if(NDS_ARM9.R[15]==0x02004890) emu_halt();
239
240 //if(NDS_ARM9.R[15]==0x0202B800) emu_halt();
241 //if(NDS_ARM9.R[15]==0x0202B3DC) emu_halt();
242 //if((NDS_ARM9.R[1]==0x9AC29AC1)&&(!fait)) {emu_halt();fait = TRUE;}
243 //if(NDS_ARM9.R[1]==0x0400004A) {emu_halt();fait = TRUE;}
244 /*if(NDS_ARM9.R[4]==0x2E33373C) emu_halt();
245 if(NDS_ARM9.R[15]==0x02036668) //emu_halt();
246 {
247 nds.logcount++;
248 sprintf(logbuf, "%d %08X", nds.logcount, NDS_ARM9.R[13]);
249 log::ajouter(logbuf);
250 if(nds.logcount==89) execute=FALSE;
251 }*/
252 //if(NDS_ARM9.instruction==0) emu_halt();
253 //if((NDS_ARM9.R[15]>>28)) emu_halt();
254 }
255 #endif
256
257
RomBanner(bool defaultInit)258 RomBanner::RomBanner(bool defaultInit)
259 {
260 if(!defaultInit) return;
261 version = 1; //Version (0001h)
262 crc16 = 0; //CRC16 across entries 020h..83Fh
263 memset(reserved,0,sizeof(reserved));
264 memset(bitmap,0,sizeof(bitmap));
265 memset(palette,0,sizeof(palette));
266 memset(titles,0,sizeof(titles));
267 memset(end0xFF,0,sizeof(end0xFF));
268 }
269
hasRomBanner()270 bool GameInfo::hasRomBanner()
271 {
272 if(header.IconOff + sizeof(RomBanner) > romsize)
273 return false;
274 else return true;
275 }
276
getRomBanner()277 const RomBanner& GameInfo::getRomBanner()
278 {
279 return banner;
280 }
281
populate()282 void GameInfo::populate()
283 {
284 const char regions_index[] = "JPFSEODIRKHXVWUC";
285 const char *regions[] = {
286 "???",
287 "JPN", // J
288 "EUR", // P
289 "FRA", // F
290 "ESP", // S
291 "USA", // E
292 "INT", // O
293 "NOE", // D
294 "ITA", // I
295 "RUS", // R
296 "KOR", // K
297 "HOL", // H
298 "EUU", // X
299 "EUU", // V
300 "EUU", // W
301 "AUS", // U
302 "CHN", // C
303
304 };
305
306 memset(ROMserial, 0, sizeof(ROMserial));
307 memset(ROMname, 0, sizeof(ROMname));
308
309 if(isHomebrew())
310 {
311 //we can't really make a serial for a homebrew game that hasnt set a game code
312 strcpy(ROMserial, "Homebrew");
313 }
314 else
315 {
316 if (isDSiEnhanced())
317 strcpy(ROMserial,"TWL- -");
318 else
319 strcpy(ROMserial,"NTR- -");
320 memcpy(ROMserial+4, header.gameCode, 4);
321
322 u32 regions_num = ARRAY_SIZE(regions);
323 u32 region = (u32)(std::max<s32>(strchr(regions_index,header.gameCode[3]) - regions_index + 1, 0));
324
325 if (region < regions_num)
326 strcat(ROMserial, regions[region]);
327 else
328 strcat(ROMserial, "???");
329 }
330
331 //rom name is probably set even in homebrew, so do it regardless
332 memset(ROMname, 0, sizeof(ROMname));
333 memcpy(ROMname, header.gameTile, 12);
334 string_trim_whitespace(ROMname);
335
336 /*if(header.IconOff < romsize)
337 {
338 u8 num = (T1ReadByte((u8*)romdata, header.IconOff) == 1)?6:7;
339 for (int i = 0; i < num; i++)
340 {
341 wcstombs(ROMfullName[i], (wchar_t *)(romdata+header.IconOff+0x240+(i*0x100)), 0x100);
342 trim(ROMfullName[i]);
343 }
344 }*/
345 }
346
loadROM(std::string fname,u32 type)347 bool GameInfo::loadROM(std::string fname, u32 type)
348 {
349 //printf("ROM %s\n", CommonSettings.loadToMemory?"loaded to RAM":"stream from disk");
350
351 closeROM();
352
353 fROM = fopen(fname.c_str(), "rb");
354 if (!fROM) return false;
355
356 headerOffset = (type == ROM_DSGBA)?DSGBA_LOADER_SIZE:0;
357 fseek(fROM, 0, SEEK_END);
358 romsize = ftell(fROM) - headerOffset;
359 fseek(fROM, headerOffset, SEEK_SET);
360
361 bool res = (fread(&header, 1, sizeof(header), fROM) == sizeof(header));
362
363 if (res)
364 {
365 #ifdef MSB_FIRST
366 //endian swap necessary fields. It would be better if we made accessors for these. I wonder if you could make a macro for a field accessor that would take the bitsize and do the swap on the fly
367 struct FieldSwap {
368 const size_t offset;
369 const size_t bytes;
370 };
371
372 static const FieldSwap fieldSwaps[] = {
373 { offsetof(NDS_header,makerCode), 2},
374
375 { offsetof(NDS_header,ARM9src), 4},
376 { offsetof(NDS_header,ARM9exe), 4},
377 { offsetof(NDS_header,ARM9cpy), 4},
378 { offsetof(NDS_header,ARM9binSize), 4},
379 { offsetof(NDS_header,ARM7src), 4},
380 { offsetof(NDS_header,ARM7exe), 4},
381 { offsetof(NDS_header,ARM7cpy), 4},
382 { offsetof(NDS_header,ARM7binSize), 4},
383 { offsetof(NDS_header,FNameTblOff), 4},
384 { offsetof(NDS_header,FNameTblSize), 4},
385 { offsetof(NDS_header,FATOff), 4},
386 { offsetof(NDS_header,FATSize), 4},
387 { offsetof(NDS_header,ARM9OverlayOff), 4},
388 { offsetof(NDS_header,ARM9OverlaySize), 4},
389 { offsetof(NDS_header,ARM7OverlayOff), 4},
390 { offsetof(NDS_header,ARM7OverlaySize), 4},
391 { offsetof(NDS_header,normalCmd), 4},
392 { offsetof(NDS_header,Key1Cmd), 4},
393 { offsetof(NDS_header,IconOff), 4},
394
395 { offsetof(NDS_header,CRC16), 2},
396 { offsetof(NDS_header,ROMtimeout), 2},
397
398 { offsetof(NDS_header,ARM9autoload), 4},
399 { offsetof(NDS_header,ARM7autoload), 4},
400 { offsetof(NDS_header,endROMoffset), 4},
401 { offsetof(NDS_header,HeaderSize), 4},
402
403 { offsetof(NDS_header, ARM9module), 4},
404 { offsetof(NDS_header, ARM7module), 4},
405
406 { offsetof(NDS_header,logoCRC16), 2},
407 { offsetof(NDS_header,headerCRC16), 2},
408 };
409
410 for(size_t i = 0; i < ARRAY_SIZE(fieldSwaps); i++)
411 {
412 const u8 *fieldAddr = (u8 *)&header + fieldSwaps[i].offset;
413
414 switch(fieldSwaps[i].bytes)
415 {
416 case 2:
417 *(u16 *)fieldAddr = LE_TO_LOCAL_16(*(u16 *)fieldAddr);
418 break;
419
420 case 4:
421 *(u32 *)fieldAddr = LE_TO_LOCAL_32(*(u32 *)fieldAddr);
422 break;
423 }
424 }
425 #endif
426 cardSize = (128 * 1024) << header.cardSize;
427
428 if (cardSize < romsize)
429 {
430 msgbox->warn("The ROM header is invalid.\nThe device size has been increased to allow for the provided file size.\n");
431
432 for (u32 i = header.cardSize; i < 0xF; i++)
433 {
434 if (((128 * 1024) << i) >= romsize)
435 {
436 header.cardSize = i;
437 cardSize = (128 * 1024) << i;
438 break;
439 }
440 }
441 }
442
443 mask = (cardSize - 1);
444 mask |= (mask >>1);
445 mask |= (mask >>2);
446 mask |= (mask >>4);
447 mask |= (mask >>8);
448 mask |= (mask >>16);
449
450 if (type == ROM_NDS)
451 {
452 size_t elements_read;
453
454 fseek(fROM, 0x4000 + headerOffset, SEEK_SET);
455 elements_read = fread(&secureArea[0], 1, 0x4000, fROM);
456 if (elements_read != 0x4000)
457 printf("Unexpectedly short post-header bit.\n");
458 }
459
460 if (CommonSettings.loadToMemory)
461 {
462 fseek(fROM, headerOffset, SEEK_SET);
463
464 romdata = new u8[romsize + 4];
465 if (fread(romdata, 1, romsize, fROM) != romsize)
466 {
467 delete [] romdata; romdata = NULL;
468 romsize = 0;
469
470 return false;
471 }
472
473 if(hasRomBanner())
474 {
475 memcpy(&banner, romdata + header.IconOff, sizeof(RomBanner));
476
477 banner.version = LE_TO_LOCAL_16(banner.version);
478 banner.crc16 = LE_TO_LOCAL_16(banner.crc16);
479
480 for(size_t i = 0; i < ARRAY_SIZE(banner.palette); i++)
481 {
482 banner.palette[i] = LE_TO_LOCAL_16(banner.palette[i]);
483 }
484 }
485
486 _isDSiEnhanced = (LE_TO_LOCAL_32(*(u32*)(romdata + 0x180) == 0x8D898581U) && LE_TO_LOCAL_32(*(u32*)(romdata + 0x184) == 0x8C888480U));
487 fclose(fROM); fROM = NULL;
488 return true;
489 }
490 _isDSiEnhanced = ((readROM(0x180) == 0x8D898581U) && (readROM(0x184) == 0x8C888480U));
491 if (hasRomBanner())
492 {
493 size_t elements_read;
494 const size_t elements_to_read = sizeof(RomBanner);
495
496 fseek(fROM, header.IconOff + headerOffset, SEEK_SET);
497 elements_read = fread(&banner, 1, elements_to_read, fROM);
498 if (elements_read != elements_to_read)
499 printf("Unexpectedly short post-header bit.\n");
500
501 banner.version = LE_TO_LOCAL_16(banner.version);
502 banner.crc16 = LE_TO_LOCAL_16(banner.crc16);
503
504 for(size_t i = 0; i < ARRAY_SIZE(banner.palette); i++)
505 {
506 banner.palette[i] = LE_TO_LOCAL_16(banner.palette[i]);
507 }
508 }
509 fseek(fROM, headerOffset, SEEK_SET);
510 lastReadPos = 0;
511 return true;
512 }
513
514 romsize = 0;
515 fclose(fROM); fROM = NULL;
516 return false;
517 }
518
closeROM()519 void GameInfo::closeROM()
520 {
521 if (fROM)
522 fclose(fROM);
523
524 if (romdata)
525 delete [] romdata;
526
527 fROM = NULL;
528 romdata = NULL;
529 romsize = 0;
530 lastReadPos = 0xFFFFFFFF;
531 }
532
readROM(u32 pos)533 u32 GameInfo::readROM(u32 pos)
534 {
535 if (!romdata)
536 {
537 u32 data;
538 if (lastReadPos != pos)
539 fseek(fROM, pos + headerOffset, SEEK_SET);
540 u32 num = fread(&data, 1, 4, fROM);
541 lastReadPos = (pos + num);
542 return LE_TO_LOCAL_32(data);
543 }
544 else
545 {
546 if(pos + 4 > romsize)
547 {
548 printf("Panic! GameInfo reading out of buffer!\n");
549 exit(-1);
550 }
551 return LE_TO_LOCAL_32(*(u32*)(romdata + pos));
552 }
553 }
554
isDSiEnhanced()555 bool GameInfo::isDSiEnhanced()
556 {
557 return _isDSiEnhanced;
558 }
559
isHomebrew()560 bool GameInfo::isHomebrew()
561 {
562 return ((header.ARM9src < 0x4000) && (T1ReadLong(header.logo, 0) != 0x51AEFF24) && (T1ReadLong(header.logo, 4) != 0x699AA221));
563 }
564
rom_init_path(const char * filename,const char * physicalName,const char * logicalFilename)565 static int rom_init_path(const char *filename, const char *physicalName, const char *logicalFilename)
566 {
567 u32 type = ROM_NDS;
568
569 path.init(logicalFilename? logicalFilename : filename);
570
571 if ( path.isdsgba(path.path)) {
572 type = ROM_DSGBA;
573 gameInfo.loadROM(path.path, type);
574 }
575 else if ( !strcasecmp(path.extension().c_str(), "nds")) {
576 type = ROM_NDS;
577 gameInfo.loadROM(physicalName ? physicalName : path.path, type); //n.b. this does nothing if the file can't be found (i.e. if it was an extracted tempfile)...
578 //...but since the data was extracted to gameInfo then it is ok
579 }
580 //ds.gba in archives, it's already been loaded into memory at this point
581 else if (logicalFilename && path.isdsgba(std::string(logicalFilename))) {
582 type = ROM_DSGBA;
583 } else {
584 //well, try to load it as an nds rom anyway
585 type = ROM_NDS;
586 gameInfo.loadROM(physicalName ? physicalName : path.path, type);
587 }
588
589 //check that size is at least the size of the header
590 if (gameInfo.romsize < 352) {
591 return -1;
592 }
593
594 gameInfo.romType = type;
595
596 return 1;
597 }
598
NDS_LoadROM(const char * filename,const char * physicalName,const char * logicalFilename)599 int NDS_LoadROM(const char *filename, const char *physicalName, const char *logicalFilename)
600 {
601 int ret;
602 char buf[PATH_MAX_LENGTH];
603
604 if (filename == NULL)
605 return -1;
606
607 ret = rom_init_path(filename, physicalName, logicalFilename);
608 if (ret < 1)
609 return ret;
610
611 //check whether this rom is any kind of valid
612 if(!CheckValidRom((u8*)&gameInfo.header, gameInfo.secureArea))
613 {
614 printf("Specified file is not a valid rom\n");
615 return -1;
616 }
617
618 gameInfo.populate();
619
620
621 if (CommonSettings.loadToMemory)
622 gameInfo.crc = crc32(0, (u8*)gameInfo.romdata, gameInfo.romsize);
623 else
624 gameInfo.crc = 0;
625
626 gameInfo.chipID = 0xC2; // The Manufacturer ID is defined by JEDEC (C2h = Macronix)
627 if (!gameInfo.isHomebrew())
628 {
629 gameInfo.chipID |= ((((128 << gameInfo.header.cardSize) / 1024) - 1) << 8); // Chip size in megabytes minus 1
630 // (07h = 8MB, 0Fh = 16MB, 1Fh = 32MB, 3Fh = 64MB, 7Fh = 128MB)
631
632 // flags
633 // 0: Unknown
634 // 1: Unknown
635 // 2: Unknown
636 // 3: Unknown
637 // 4: Unknown
638 // 5: DSi? (if set to 1 then DSi Enhanced games send command D6h to Slot1)
639 // 6: Unknown
640 // 7: ROM speed (Secure Area Block transfer mode (trasfer 8x200h or 1000h bytes)
641 // TODO:
642 //if (gameInfo.isDSiEnhanced())
643 // gameInfo.chipID |= (0x40 << 24);
644 gameInfo.chipID |= (0x00 << 24);
645 }
646
647 #ifdef DEBUG
648 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]);
649 if (gameInfo.crc)
650 INFO("ROM crc: %08X\n", gameInfo.crc);
651 if (!gameInfo.isHomebrew())
652 {
653 INFO("ROM serial: %s\n", gameInfo.ROMserial);
654 INFO("ROM chipID: %08X\n", gameInfo.chipID);
655 INFO("ROM internal name: %s\n", gameInfo.ROMname);
656 if (gameInfo.isDSiEnhanced()) INFO("ROM DSi Enhanced\n");
657 }
658 INFO("ROM developer: %s\n", ((gameInfo.header.makerCode == 0) && gameInfo.isHomebrew())
659 ? "Homebrew" :
660 getDeveloperNameByID(gameInfo.header.makerCode));
661 #endif
662
663 buf[0] = gameInfo.header.gameCode[0];
664 buf[1] = gameInfo.header.gameCode[1];
665 buf[2] = gameInfo.header.gameCode[2];
666 buf[3] = gameInfo.header.gameCode[3];
667 buf[4] = 0;
668 if (advsc.checkDB(buf, gameInfo.crc))
669 {
670 u8 sv = advsc.getSaveType();
671 printf("Found in game database by %s:\n", advsc.getIdMethod());
672 printf("\t* ROM serial:\t\t%s\n", advsc.getSerial());
673 printf("\t* ROM save type:\t");
674 if (sv == 0xFF)
675 printf("Unknown");
676 else
677 if (sv == 0xFE)
678 printf("None");
679 else
680 {
681 printf("%s", save_types[sv + 1].descr);
682 if (CommonSettings.autodetectBackupMethod == 1)
683 backup_setManualBackupType(sv + 1);
684 }
685 printf("\n\t* ROM crc:\t\t%08X\n", advsc.getCRC32());
686 }
687 printf("\n");
688
689 //for homebrew, try auto-patching DLDI. should be benign if there is no DLDI or if it fails
690 if(gameInfo.isHomebrew())
691 {
692 if(!CommonSettings.loadToMemory)
693 msgbox->warn("Sorry.. right now, you can't use the default (stream rom from disk) with homebrew due to a bug with DLDI-autopatching");
694 if (slot1_GetCurrentType() == NDS_SLOT1_R4)
695 DLDI_tryPatch((void*)gameInfo.romdata, gameInfo.romsize, 1);
696 else
697 if (slot2_GetCurrentType() == NDS_SLOT2_CFLASH)
698 DLDI_tryPatch((void*)gameInfo.romdata, gameInfo.romsize, 0);
699
700 }
701
702 if (cheats != NULL)
703 {
704 memset(buf, 0, sizeof(buf));
705 path.getpathnoext(path.CHEATS, buf);
706 strcat(buf, ".dct"); // DeSmuME cheat :)
707 cheats->init(buf);
708 }
709
710 NDS_Reset();
711
712 return ret;
713 }
714
NDS_FreeROM(void)715 void NDS_FreeROM(void)
716 {
717 gameInfo.closeROM();
718 }
719
NDS_Sleep()720 void NDS_Sleep() { nds.sleeping = TRUE; }
721
NDS_TriggerCardEjectIRQ()722 void NDS_TriggerCardEjectIRQ()
723 {
724 NDS_makeIrq(ARMCPU_ARM7, IRQ_BIT_GC_IREQ_MC);
725 NDS_makeIrq(ARMCPU_ARM9, IRQ_BIT_GC_IREQ_MC); //zero added on 11-aug-2013 with no proof of accuracy
726 }
727
728
729 class FrameSkipper
730 {
731 public:
RequestSkip()732 void RequestSkip()
733 {
734 nextSkip = true;
735 }
OmitSkip(bool force,bool forceEvenIfCapturing=false)736 void OmitSkip(bool force, bool forceEvenIfCapturing=false)
737 {
738 nextSkip = false;
739 if((force && consecutiveNonCaptures > 30) || forceEvenIfCapturing)
740 {
741 SkipCur2DFrame = false;
742 SkipCur3DFrame = false;
743 SkipNext2DFrame = false;
744 if(forceEvenIfCapturing)
745 consecutiveNonCaptures = 0;
746 }
747 }
Advance()748 void Advance()
749 {
750 const GPUEngineA *mainEngine = GPU->GetEngineMain();
751 bool capturing = (mainEngine->dispCapCnt.enabled || (mainEngine->dispCapCnt.val & 0x80000000));
752
753
754 if(capturing && consecutiveNonCaptures > 30)
755 {
756 // the worst-looking graphics corruption problems from frameskip
757 // are the result of skipping the capture on first frame it turns on.
758 // so we do this to handle the capture immediately,
759 // despite the risk of 1 frame of 2d/3d mismatch or wrong screen display.
760 SkipNext2DFrame = false;
761 nextSkip = false;
762 }
763 else if((lastDisplayTarget != mainEngine->GetDisplayByID()) && lastSkip && !skipped)
764 {
765 // if we're switching from not skipping to skipping
766 // and the screens are also switching around this frame,
767 // go for 1 extra frame without skipping.
768 // this avoids the scenario where we only draw one of the two screens
769 // when a game is switching screens every frame.
770 nextSkip = false;
771 }
772
773 if(capturing)
774 consecutiveNonCaptures = 0;
775 else if(!(consecutiveNonCaptures > 9000)) // arbitrary cap to avoid eventual wrap
776 consecutiveNonCaptures++;
777
778 lastDisplayTarget = mainEngine->GetDisplayByID();
779 lastSkip = skipped;
780 skipped = nextSkip;
781 nextSkip = false;
782
783 SkipCur2DFrame = SkipNext2DFrame;
784 SkipCur3DFrame = skipped;
785 SkipNext2DFrame = skipped;
786 }
ShouldSkip2D()787 FORCEINLINE bool ShouldSkip2D()
788 {
789 return SkipCur2DFrame;
790 }
ShouldSkip3D()791 FORCEINLINE bool ShouldSkip3D()
792 {
793 return SkipCur3DFrame;
794 }
FrameSkipper()795 FrameSkipper()
796 {
797 nextSkip = false;
798 skipped = false;
799 lastSkip = false;
800 lastDisplayTarget = NDSDisplayID_Main;
801 SkipCur2DFrame = false;
802 SkipCur3DFrame = false;
803 SkipNext2DFrame = false;
804 consecutiveNonCaptures = 0;
805 }
806 private:
807 bool nextSkip;
808 bool skipped;
809 bool lastSkip;
810 NDSDisplayID lastDisplayTarget;
811 int consecutiveNonCaptures;
812 bool SkipCur2DFrame;
813 bool SkipCur3DFrame;
814 bool SkipNext2DFrame;
815 };
816 static FrameSkipper frameSkipper;
817
818
NDS_SkipNextFrame()819 void NDS_SkipNextFrame() {
820 frameSkipper.RequestSkip();
821 }
NDS_OmitFrameSkip(int force)822 void NDS_OmitFrameSkip(int force) {
823 frameSkipper.OmitSkip(force > 0, force > 1);
824 }
825
826 #define INDEX(i) ((((i)>>16)&0xFF0)|(((i)>>4)&0xF))
827
828
829 enum ESI_DISPCNT
830 {
831 ESI_DISPCNT_HStart, ESI_DISPCNT_HStartIRQ, ESI_DISPCNT_HDraw, ESI_DISPCNT_HBlank
832 };
833
834 u64 nds_timer;
835 u64 nds_arm9_timer, nds_arm7_timer;
836
837 static const u64 kNever = 0xFFFFFFFFFFFFFFFFULL;
838
839 struct TSequenceItem
840 {
841 u64 timestamp;
842 u32 param;
843 bool enabled;
844
saveTSequenceItem845 virtual void save(EMUFILE* os)
846 {
847 write64le(timestamp,os);
848 write32le(param,os);
849 writebool(enabled,os);
850 }
851
loadTSequenceItem852 virtual bool load(EMUFILE* is)
853 {
854 if(read64le(×tamp,is) != 1) return false;
855 if(read32le(¶m,is) != 1) return false;
856 if(readbool(&enabled,is) != 1) return false;
857 return true;
858 }
859
isTriggeredTSequenceItem860 FORCEINLINE bool isTriggered()
861 {
862 return enabled && nds_timer >= timestamp;
863 }
864
nextTSequenceItem865 FORCEINLINE u64 next()
866 {
867 return timestamp;
868 }
869 };
870
871 struct TSequenceItem_GXFIFO : public TSequenceItem
872 {
isTriggeredTSequenceItem_GXFIFO873 FORCEINLINE bool isTriggered()
874 {
875 return enabled && nds_timer >= MMU.gfx3dCycles;
876 }
877
execTSequenceItem_GXFIFO878 FORCEINLINE void exec()
879 {
880 #ifndef NDEBUG
881 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[4]++);
882 #endif
883 while(isTriggered()) {
884 enabled = false;
885 gfx3d_execute3D();
886 }
887 }
888
nextTSequenceItem_GXFIFO889 FORCEINLINE u64 next()
890 {
891 if(enabled) return MMU.gfx3dCycles;
892 else return kNever;
893 }
894 };
895
896 template<int procnum, int num> struct TSequenceItem_Timer : public TSequenceItem
897 {
isTriggeredTSequenceItem_Timer898 FORCEINLINE bool isTriggered()
899 {
900 return enabled && nds_timer >= nds.timerCycle[procnum][num];
901 }
902
scheduleTSequenceItem_Timer903 FORCEINLINE void schedule()
904 {
905 enabled = MMU.timerON[procnum][num] && MMU.timerMODE[procnum][num] != 0xFFFF;
906 }
907
nextTSequenceItem_Timer908 FORCEINLINE u64 next()
909 {
910 return nds.timerCycle[procnum][num];
911 }
912
execTSequenceItem_Timer913 FORCEINLINE void exec()
914 {
915 #ifndef NDEBUG
916 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[13+procnum*4+num]++);
917 #endif
918 u8* regs = procnum==0?MMU.ARM9_REG:MMU.ARM7_REG;
919 bool first = true;
920 //we'll need to check chained timers..
921 for(int i=num;i<4;i++)
922 {
923 bool over = false;
924 //maybe too many checks if this is here, but we need it here for now
925 if(!MMU.timerON[procnum][i]) return;
926
927 if(MMU.timerMODE[procnum][i] == 0xFFFF)
928 {
929 ++(MMU.timer[procnum][i]);
930 over = !MMU.timer[procnum][i];
931 }
932 else
933 {
934 if(!first) break; //this timer isn't chained. break the chain
935 first = false;
936
937 over = true;
938 int remain = 65536 - MMU.timerReload[procnum][i];
939 int ctr=0;
940 while(nds.timerCycle[procnum][i] <= nds_timer) {
941 nds.timerCycle[procnum][i] += (remain << MMU.timerMODE[procnum][i]);
942 ctr++;
943 }
944 #ifndef NDEBUG
945 if(ctr>1) {
946 printf("yikes!!!!! please report!\n");
947 }
948 #endif
949 }
950
951 if(over)
952 {
953 MMU.timer[procnum][i] = MMU.timerReload[procnum][i];
954 if(T1ReadWord(regs, 0x102 + i*4) & 0x40)
955 {
956 NDS_makeIrq(procnum, IRQ_BIT_TIMER_0 + i);
957 }
958 }
959 else
960 break; //no more chained timers to trigger. we're done here
961 }
962 }
963 };
964
965 template<int procnum, int chan> struct TSequenceItem_DMA : public TSequenceItem
966 {
967 DmaController* controller;
968
isTriggeredTSequenceItem_DMA969 FORCEINLINE bool isTriggered()
970 {
971 return (controller->dmaCheck && nds_timer>= controller->nextEvent);
972 }
973
isEnabledTSequenceItem_DMA974 FORCEINLINE bool isEnabled() {
975 return controller->dmaCheck?TRUE:FALSE;
976 }
977
nextTSequenceItem_DMA978 FORCEINLINE u64 next()
979 {
980 return controller->nextEvent;
981 }
982
execTSequenceItem_DMA983 FORCEINLINE void exec()
984 {
985 #ifndef NDEBUG
986 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[5+procnum*4+chan]++);
987 #endif
988
989 //if (nds.freezeBus) return;
990
991 //printf("exec from TSequenceItem_DMA: %d %d\n",procnum,chan);
992 controller->exec();
993 // //give gxfifo dmas a chance to re-trigger
994 // if(MMU.DMAStartTime[procnum][chan] == EDMAMode_GXFifo) {
995 // MMU.DMAing[procnum][chan] = FALSE;
996 // if (gxFIFO.size <= 127)
997 // {
998 // execHardware_doDma(procnum,chan,EDMAMode_GXFifo);
999 // if (MMU.DMACompleted[procnum][chan])
1000 // goto docomplete;
1001 // else return;
1002 // }
1003 // }
1004 //
1005 //docomplete:
1006 // if (MMU.DMACompleted[procnum][chan])
1007 // {
1008 // u8* regs = procnum==0?MMU.ARM9_REG:MMU.ARM7_REG;
1009 //
1010 // //disable the channel
1011 // if(MMU.DMAStartTime[procnum][chan] != EDMAMode_GXFifo) {
1012 // T1WriteLong(regs, 0xB8 + (0xC*chan), T1ReadLong(regs, 0xB8 + (0xC*chan)) & 0x7FFFFFFF);
1013 // MMU.DMACrt[procnum][chan] &= 0x7FFFFFFF; //blehhh i hate this shit being mirrored in memory
1014 // }
1015 //
1016 // if((MMU.DMACrt[procnum][chan])&(1<<30)) {
1017 // if(procnum==0) NDS_makeARM9Int(8+chan);
1018 // else NDS_makeARM7Int(8+chan);
1019 // }
1020 //
1021 // MMU.DMAing[procnum][chan] = FALSE;
1022 // }
1023
1024 }
1025 };
1026
1027 struct TSequenceItem_divider : public TSequenceItem
1028 {
isTriggeredTSequenceItem_divider1029 FORCEINLINE bool isTriggered()
1030 {
1031 return MMU.divRunning && nds_timer >= MMU.divCycles;
1032 }
1033
isEnabledTSequenceItem_divider1034 bool isEnabled() { return MMU.divRunning!=0; }
1035
nextTSequenceItem_divider1036 FORCEINLINE u64 next()
1037 {
1038 return MMU.divCycles;
1039 }
1040
execTSequenceItem_divider1041 void exec()
1042 {
1043 #ifndef NDEBUG
1044 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[2]++);
1045 #endif
1046 MMU_new.div.busy = 0;
1047 #ifdef HOST_64
1048 T1WriteQuad(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A0, MMU.divResult);
1049 T1WriteQuad(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A8, MMU.divMod);
1050 #else
1051 T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A0, (u32)MMU.divResult);
1052 T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A4, (u32)(MMU.divResult >> 32));
1053 T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2A8, (u32)MMU.divMod);
1054 T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2AC, (u32)(MMU.divMod >> 32));
1055 #endif
1056 MMU.divRunning = FALSE;
1057 }
1058
1059 };
1060
1061 struct TSequenceItem_sqrtunit : public TSequenceItem
1062 {
isTriggeredTSequenceItem_sqrtunit1063 FORCEINLINE bool isTriggered()
1064 {
1065 return MMU.sqrtRunning && nds_timer >= MMU.sqrtCycles;
1066 }
1067
isEnabledTSequenceItem_sqrtunit1068 bool isEnabled() { return MMU.sqrtRunning!=0; }
1069
nextTSequenceItem_sqrtunit1070 FORCEINLINE u64 next()
1071 {
1072 return MMU.sqrtCycles;
1073 }
1074
execTSequenceItem_sqrtunit1075 FORCEINLINE void exec()
1076 {
1077 #ifndef NDEBUG
1078 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[3]++);
1079 #endif
1080 MMU_new.sqrt.busy = 0;
1081 T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x2B4, MMU.sqrtResult);
1082 MMU.sqrtRunning = FALSE;
1083 }
1084
1085 };
1086
1087 struct Sequencer
1088 {
1089 bool nds_vblankEnded;
1090 bool reschedule;
1091 TSequenceItem dispcnt;
1092 TSequenceItem wifi;
1093 TSequenceItem_divider divider;
1094 TSequenceItem_sqrtunit sqrtunit;
1095 TSequenceItem_GXFIFO gxfifo;
1096 TSequenceItem_DMA<0,0> dma_0_0; TSequenceItem_DMA<0,1> dma_0_1;
1097 TSequenceItem_DMA<0,2> dma_0_2; TSequenceItem_DMA<0,3> dma_0_3;
1098 TSequenceItem_DMA<1,0> dma_1_0; TSequenceItem_DMA<1,1> dma_1_1;
1099 TSequenceItem_DMA<1,2> dma_1_2; TSequenceItem_DMA<1,3> dma_1_3;
1100 TSequenceItem_Timer<0,0> timer_0_0; TSequenceItem_Timer<0,1> timer_0_1;
1101 TSequenceItem_Timer<0,2> timer_0_2; TSequenceItem_Timer<0,3> timer_0_3;
1102 TSequenceItem_Timer<1,0> timer_1_0; TSequenceItem_Timer<1,1> timer_1_1;
1103 TSequenceItem_Timer<1,2> timer_1_2; TSequenceItem_Timer<1,3> timer_1_3;
1104
1105 void init();
1106
1107 void execHardware();
1108 u64 findNext();
1109
saveSequencer1110 void save(EMUFILE* os)
1111 {
1112 write64le(nds_timer,os);
1113 write64le(nds_arm9_timer,os);
1114 write64le(nds_arm7_timer,os);
1115 dispcnt.save(os);
1116 divider.save(os);
1117 sqrtunit.save(os);
1118 gxfifo.save(os);
1119 wifi.save(os);
1120 #define SAVE(I,X,Y) I##_##X##_##Y .save(os);
1121 SAVE(timer,0,0); SAVE(timer,0,1); SAVE(timer,0,2); SAVE(timer,0,3);
1122 SAVE(timer,1,0); SAVE(timer,1,1); SAVE(timer,1,2); SAVE(timer,1,3);
1123 SAVE(dma,0,0); SAVE(dma,0,1); SAVE(dma,0,2); SAVE(dma,0,3);
1124 SAVE(dma,1,0); SAVE(dma,1,1); SAVE(dma,1,2); SAVE(dma,1,3);
1125 #undef SAVE
1126 }
1127
loadSequencer1128 bool load(EMUFILE* is, int version)
1129 {
1130 if(read64le(&nds_timer,is) != 1) return false;
1131 if(read64le(&nds_arm9_timer,is) != 1) return false;
1132 if(read64le(&nds_arm7_timer,is) != 1) return false;
1133 if(!dispcnt.load(is)) return false;
1134 if(!divider.load(is)) return false;
1135 if(!sqrtunit.load(is)) return false;
1136 if(!gxfifo.load(is)) return false;
1137 if(version >= 1) if(!wifi.load(is)) return false;
1138 #define LOAD(I,X,Y) if(!I##_##X##_##Y .load(is)) return false;
1139 LOAD(timer,0,0); LOAD(timer,0,1); LOAD(timer,0,2); LOAD(timer,0,3);
1140 LOAD(timer,1,0); LOAD(timer,1,1); LOAD(timer,1,2); LOAD(timer,1,3);
1141 LOAD(dma,0,0); LOAD(dma,0,1); LOAD(dma,0,2); LOAD(dma,0,3);
1142 LOAD(dma,1,0); LOAD(dma,1,1); LOAD(dma,1,2); LOAD(dma,1,3);
1143 #undef LOAD
1144
1145 return true;
1146 }
1147
1148 } sequencer;
1149
NDS_RescheduleGXFIFO(u32 cost)1150 void NDS_RescheduleGXFIFO(u32 cost)
1151 {
1152 if(!sequencer.gxfifo.enabled) {
1153 MMU.gfx3dCycles = nds_timer;
1154 sequencer.gxfifo.enabled = true;
1155 }
1156 MMU.gfx3dCycles += cost;
1157 NDS_Reschedule();
1158 }
1159
NDS_RescheduleTimers()1160 void NDS_RescheduleTimers()
1161 {
1162 #define check(X,Y) sequencer.timer_##X##_##Y .schedule();
1163 check(0,0); check(0,1); check(0,2); check(0,3);
1164 check(1,0); check(1,1); check(1,2); check(1,3);
1165 #undef check
1166
1167 NDS_Reschedule();
1168 }
1169
NDS_RescheduleDMA()1170 void NDS_RescheduleDMA()
1171 {
1172 //TBD
1173 NDS_Reschedule();
1174
1175 }
1176
initSchedule()1177 static void initSchedule()
1178 {
1179 sequencer.init();
1180
1181 //begin at the very end of the last scanline
1182 //so that at t=0 we can increment to scanline=0
1183 nds.VCount = 262;
1184
1185 sequencer.nds_vblankEnded = false;
1186 }
1187
1188
1189 // 2196372 ~= (ARM7_CLOCK << 16) / 1000000
1190 // This value makes more sense to me, because:
1191 // ARM7_CLOCK = 33.51 mhz
1192 // = 33513982 cycles per second
1193 // = 33.513982 cycles per microsecond
1194 const u64 kWifiCycles = 67;//34*2;
1195 //(this isn't very precise. I don't think it needs to be)
1196
init()1197 void Sequencer::init()
1198 {
1199 NDS_RescheduleTimers();
1200 NDS_RescheduleDMA();
1201
1202 reschedule = false;
1203 nds_timer = 0;
1204 nds_arm9_timer = 0;
1205 nds_arm7_timer = 0;
1206
1207 dispcnt.enabled = true;
1208 dispcnt.param = ESI_DISPCNT_HStart;
1209 dispcnt.timestamp = 0;
1210
1211 gxfifo.enabled = false;
1212
1213 dma_0_0.controller = &MMU_new.dma[0][0];
1214 dma_0_1.controller = &MMU_new.dma[0][1];
1215 dma_0_2.controller = &MMU_new.dma[0][2];
1216 dma_0_3.controller = &MMU_new.dma[0][3];
1217 dma_1_0.controller = &MMU_new.dma[1][0];
1218 dma_1_1.controller = &MMU_new.dma[1][1];
1219 dma_1_2.controller = &MMU_new.dma[1][2];
1220 dma_1_3.controller = &MMU_new.dma[1][3];
1221
1222
1223 #ifdef EXPERIMENTAL_WIFI_COMM
1224 wifi.enabled = true;
1225 wifi.timestamp = kWifiCycles;
1226 #else
1227 wifi.enabled = false;
1228 #endif
1229 }
1230
execHardware_hblank()1231 static void execHardware_hblank()
1232 {
1233 //this logic keeps moving around.
1234 //now, we try and give the game as much time as possible to finish doing its work for the scanline,
1235 //by drawing scanline N at the end of drawing time (but before subsequent interrupt or hdma-driven events happen)
1236 //don't try to do this at the end of the scanline, because some games (sonic classics) may use hblank IRQ to set
1237 //scroll regs for the next scanline
1238 if(nds.VCount<192)
1239 {
1240 GPU->RenderLine(nds.VCount, frameSkipper.ShouldSkip2D());
1241
1242 //trigger hblank dmas
1243 //but notice, we do that just after we finished drawing the line
1244 //(values copied by this hdma should not be used until the next scanline)
1245 triggerDma(EDMAMode_HBlank);
1246 }
1247
1248 #if 0
1249 if(nds.VCount==262)
1250 {
1251 //we need to trigger one last hblank dma since
1252 //a. we're sort of lagged behind by one scanline
1253 //b. i think that 193 hblanks actually fire (one for the hblank in scanline 262)
1254 //this is demonstrated by NSMB splot-parallaxing clouds
1255 //for some reason the game will setup two hdma scroll register buffers
1256 //to be run consecutively, and unless we do this, the second buffer will be offset by one scanline
1257 //causing a glitch in the 0th scanline
1258 //triggerDma(EDMAMode_HBlank);
1259
1260 //BUT! this was removed in order to make glitches in megaman zero collection (mmz 4 1st level) work.
1261 //and, it seems that it is no longer necessary in nsmb. perhaps something else fixed it
1262 }
1263 #endif
1264
1265
1266 //turn on hblank status bit
1267 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) | 2);
1268 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 2);
1269
1270 //fire hblank interrupts if necessary
1271 if(T1ReadWord(MMU.ARM9_REG, 4) & 0x10) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_LCD_HBLANK);
1272 if(T1ReadWord(MMU.ARM7_REG, 4) & 0x10) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_LCD_HBLANK);
1273
1274 //emulation housekeeping. for some reason we always do this at hblank,
1275 //even though it sounds more reasonable to do it at hstart
1276 SPU_Emulate_core();
1277 }
1278
execHardware_hstart_vblankEnd()1279 static void execHardware_hstart_vblankEnd()
1280 {
1281 sequencer.nds_vblankEnded = true;
1282 sequencer.reschedule = true;
1283
1284 //turn off vblank status bit
1285 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & ~1);
1286 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & ~1);
1287
1288 //some emulation housekeeping
1289 frameSkipper.Advance();
1290
1291 if (GPU->GetWillAutoBlitNativeToCustomBuffer())
1292 {
1293 GPU->GetEngineMain()->BlitNativeToCustomFramebuffer();
1294 GPU->GetEngineSub()->BlitNativeToCustomFramebuffer();
1295 }
1296 }
1297
execHardware_hstart_vblankStart()1298 static void execHardware_hstart_vblankStart()
1299 {
1300 //printf("--------VBLANK!!!--------\n");
1301
1302 //fire vblank interrupts if necessary
1303 for(int i=0;i<2;i++)
1304 if(MMU.reg_IF_pending[i] & (1<<IRQ_BIT_LCD_VBLANK))
1305 {
1306 MMU.reg_IF_pending[i] &= ~(1<<IRQ_BIT_LCD_VBLANK);
1307 NDS_makeIrq(i,IRQ_BIT_LCD_VBLANK);
1308 }
1309
1310 //trigger vblank dmas
1311 triggerDma(EDMAMode_VBlank);
1312
1313 //tracking for arm9 load average
1314 nds.runCycleCollector[0][nds.idleFrameCounter] = 1120380-nds.idleCycles[0];
1315 nds.runCycleCollector[1][nds.idleFrameCounter] = 1120380-nds.idleCycles[1];
1316 nds.idleFrameCounter++;
1317 nds.idleFrameCounter &= 15;
1318 nds.idleCycles[0] = 0;
1319 nds.idleCycles[1] = 0;
1320 }
1321
execHardware_gen_vmatch_goal()1322 static u16 execHardware_gen_vmatch_goal()
1323 {
1324 u16 vmatch = T1ReadWord(MMU.ARM9_REG, 4);
1325 vmatch = ((vmatch>>8)|((vmatch<<1)&(1<<8)));
1326 return vmatch;
1327 }
1328
execHardware_hstart_vcount_irq()1329 static void execHardware_hstart_vcount_irq()
1330 {
1331 //trigger pending VMATCH irqs
1332 if(MMU.reg_IF_pending[ARMCPU_ARM9] & (1<<IRQ_BIT_LCD_VMATCH))
1333 {
1334 MMU.reg_IF_pending[ARMCPU_ARM9] &= ~(1<<IRQ_BIT_LCD_VMATCH);
1335 NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_LCD_VMATCH);
1336 }
1337 if(MMU.reg_IF_pending[ARMCPU_ARM7] & (1<<IRQ_BIT_LCD_VMATCH))
1338 {
1339 MMU.reg_IF_pending[ARMCPU_ARM7] &= ~(1<<IRQ_BIT_LCD_VMATCH);
1340 NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_LCD_VMATCH);
1341 }
1342 }
1343
execHardware_hstart_vcount()1344 static void execHardware_hstart_vcount()
1345 {
1346 u16 vmatch = execHardware_gen_vmatch_goal();
1347 if(nds.VCount==vmatch)
1348 {
1349 //arm9 vmatch
1350 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) | 4);
1351 if(T1ReadWord(MMU.ARM9_REG, 4) & 32) {
1352 MMU.reg_IF_pending[ARMCPU_ARM9] |= (1<<IRQ_BIT_LCD_VMATCH);
1353 }
1354 }
1355 else
1356 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & 0xFFFB);
1357
1358 vmatch = T1ReadWord(MMU.ARM7_REG, 4);
1359 vmatch = ((vmatch>>8)|((vmatch<<1)&(1<<8)));
1360 if(nds.VCount==vmatch)
1361 {
1362 //arm7 vmatch
1363 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 4);
1364 if(T1ReadWord(MMU.ARM7_REG, 4) & 32)
1365 MMU.reg_IF_pending[ARMCPU_ARM7] |= (1<<IRQ_BIT_LCD_VMATCH);
1366 }
1367 else
1368 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFB);
1369 }
1370
execHardware_hdraw()1371 static void execHardware_hdraw()
1372 {
1373 //due to hacks in our selection of rendering time, we do not actually render here as intended.
1374 //consider changing this if there is some problem with raster fx timing but check the documentation near the gpu rendering calls
1375 //to make sure you check for regressions (nsmb, sonic classics, et al)
1376 }
1377
execHardware_hstart_irq()1378 static void execHardware_hstart_irq()
1379 {
1380 //this function very soon after the registers get updated to trigger IRQs
1381 //this is necessary to fix "egokoro kyoushitsu" which idles waiting for vcount=192, which never happens due to a long vblank irq
1382 //100% accurate emulation would require the read of VCOUNT to be in the pipeline already with the irq coming in behind it, thus
1383 //allowing the vcount to register as 192 occasionally (maybe about 1 out of 28 frames)
1384 //the actual length of the delay is in execHardware() where the events are scheduled
1385 sequencer.reschedule = true;
1386
1387 //when the vcount hits 192, vblank begins
1388 if(nds.VCount==192)
1389 execHardware_hstart_vblankStart();
1390
1391 execHardware_hstart_vcount_irq();
1392 }
1393
execHardware_hstart()1394 static void execHardware_hstart()
1395 {
1396 nds.VCount++;
1397
1398 switch (nds.VCount)
1399 {
1400 case 214:
1401 if (CommonSettings.rigorous_timing)
1402 gfx3d_VBlankEndSignal(frameSkipper.ShouldSkip3D());
1403 break;
1404 case 263:
1405 //when the vcount hits 263 it rolls over to 0
1406 nds.VCount=0;
1407 break;
1408 case 262:
1409 //this should be 214, but we are going to be generous for games with tight timing
1410 //they shouldnt be changing any textures at 262 but they might accidentally still be at 214
1411 //so..
1412 if (!CommonSettings.rigorous_timing)
1413 gfx3d_VBlankEndSignal(frameSkipper.ShouldSkip3D());
1414
1415 //when the vcount hits 262, vblank ends (oam pre-renders by one scanline)
1416 execHardware_hstart_vblankEnd();
1417 break;
1418 case 192:
1419 //turn on vblank status bit
1420 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) | 1);
1421 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) | 1);
1422
1423 //check whether we'll need to fire vblank irqs
1424 if(T1ReadWord(MMU.ARM9_REG, 4) & 0x8) MMU.reg_IF_pending[ARMCPU_ARM9] |= (1<<IRQ_BIT_LCD_VBLANK);
1425 if(T1ReadWord(MMU.ARM7_REG, 4) & 0x8) MMU.reg_IF_pending[ARMCPU_ARM7] |= (1<<IRQ_BIT_LCD_VBLANK);
1426
1427 //this is important for the character select in Dragon Ball Kai - Ultimate Butouden
1428 //it seems if you allow the 3d to begin before the vblank, then it will get interrupted and not complete.
1429 //the game needs to pick up the gxstat reg busy as clear after it finishes processing vblank.
1430 //therefore, this can't happen until sometime after vblank.
1431 //devil survivor 2 will have screens get stuck if this is on any other scanline.
1432 //obviously 192 is the right choice.
1433 gfx3d_VBlankSignal();
1434 //this isnt important for any known game, but it would be nice to prove it.
1435 NDS_RescheduleGXFIFO(392*2);
1436 break;
1437 }
1438
1439 //write the new vcount
1440 T1WriteWord(MMU.ARM9_REG, 6, nds.VCount);
1441 T1WriteWord(MMU.ARM9_REG, 0x1006, nds.VCount);
1442 T1WriteWord(MMU.ARM7_REG, 6, nds.VCount);
1443 T1WriteWord(MMU.ARM7_REG, 0x1006, nds.VCount);
1444
1445 //turn off hblank status bit
1446 T1WriteWord(MMU.ARM9_REG, 4, T1ReadWord(MMU.ARM9_REG, 4) & 0xFFFD);
1447 T1WriteWord(MMU.ARM7_REG, 4, T1ReadWord(MMU.ARM7_REG, 4) & 0xFFFD);
1448
1449 //handle vcount status
1450 execHardware_hstart_vcount();
1451
1452 //trigger hstart dmas
1453 triggerDma(EDMAMode_HStart);
1454
1455 if(nds.VCount<192)
1456 {
1457 //this is hacky.
1458 //there is a corresponding hack in doDMA.
1459 //it should be driven by a fifo (and generate just in time as the scanline is displayed)
1460 //but that isnt even possible until we have some sort of sub-scanline timing.
1461 //it may not be necessary.
1462 triggerDma(EDMAMode_MemDisplay);
1463 }
1464 }
1465
NDS_Reschedule()1466 void NDS_Reschedule()
1467 {
1468 #ifndef NDEBUG
1469 IF_DEVELOPER(if(!sequencer.reschedule) DEBUG_statistics.sequencerExecutionCounters[0]++;);
1470 #endif
1471 sequencer.reschedule = true;
1472 }
1473
_fast_min32(u32 a,u32 b,u32 c,u32 d)1474 FORCEINLINE u32 _fast_min32(u32 a, u32 b, u32 c, u32 d)
1475 {
1476 return ((( ((s32)(a-b)) >> (32-1)) & (c^d)) ^ d);
1477 }
1478
_fast_min(u64 a,u64 b)1479 FORCEINLINE u64 _fast_min(u64 a, u64 b)
1480 {
1481 //you might find that this is faster on a 64bit system; someone should try it
1482 //http://aggregate.org/MAGIC/#Integer%20Selection
1483 //u64 ret = (((((s64)(a-b)) >> (64-1)) & (a^b)) ^ b);
1484 //assert(ret==min(a,b));
1485 //return ret;
1486
1487 //but this ends up being the fastest on 32bits
1488 return a<b?a:b;
1489
1490 //just for the record, I tried to do the 64bit math on a 32bit proc
1491 //using sse2 and it was really slow
1492 //__m128i __a; __a.m128i_u64[0] = a;
1493 //__m128i __b; __b.m128i_u64[0] = b;
1494 //__m128i xorval = _mm_xor_si128(__a,__b);
1495 //__m128i temp = _mm_sub_epi64(__a,__b);
1496 //temp.m128i_i64[0] >>= 63; //no 64bit shra in sse2, what a disappointment
1497 //temp = _mm_and_si128(temp,xorval);
1498 //temp = _mm_xor_si128(temp,__b);
1499 //return temp.m128i_u64[0];
1500 }
1501
1502
1503
findNext()1504 u64 Sequencer::findNext()
1505 {
1506 //this one is always enabled so dont bother to check it
1507 u64 next = dispcnt.next();
1508
1509 if(divider.isEnabled()) next = _fast_min(next,divider.next());
1510 if(sqrtunit.isEnabled()) next = _fast_min(next,sqrtunit.next());
1511 if(gxfifo.enabled) next = _fast_min(next,gxfifo.next());
1512
1513 #ifdef EXPERIMENTAL_WIFI_COMM
1514 next = _fast_min(next,wifi.next());
1515 #endif
1516
1517 #define test(X,Y) if(dma_##X##_##Y .isEnabled()) next = _fast_min(next,dma_##X##_##Y .next());
1518 test(0,0); test(0,1); test(0,2); test(0,3);
1519 test(1,0); test(1,1); test(1,2); test(1,3);
1520 #undef test
1521 #define test(X,Y) if(timer_##X##_##Y .enabled) next = _fast_min(next,timer_##X##_##Y .next());
1522 test(0,0); test(0,1); test(0,2); test(0,3);
1523 test(1,0); test(1,1); test(1,2); test(1,3);
1524 #undef test
1525
1526 return next;
1527 }
1528
execHardware()1529 void Sequencer::execHardware()
1530 {
1531 if(dispcnt.isTriggered())
1532 {
1533 #ifndef NDEBUG
1534 IF_DEVELOPER(DEBUG_statistics.sequencerExecutionCounters[1]++);
1535 #endif
1536
1537 switch(dispcnt.param)
1538 {
1539 case ESI_DISPCNT_HStart:
1540 execHardware_hstart();
1541 //(used to be 3168)
1542 //hstart is actually 8 dots before the visible drawing begins
1543 //we're going to run 1 here and then run 7 in the next case
1544 dispcnt.timestamp += 1*6*2;
1545 dispcnt.param = ESI_DISPCNT_HStartIRQ;
1546 break;
1547 case ESI_DISPCNT_HStartIRQ:
1548 execHardware_hstart_irq();
1549 dispcnt.timestamp += 7*6*2;
1550 dispcnt.param = ESI_DISPCNT_HDraw;
1551 break;
1552
1553 case ESI_DISPCNT_HDraw:
1554 //execHardware_hdraw();
1555 //duration of non-blanking period is ~1606 clocks (gbatek agrees) [but says its different on arm7]
1556 //im gonna call this 267 dots = 267*6=1602
1557 //so, this event lasts 267 dots minus the 8 dot preroll
1558 dispcnt.timestamp += (267-8)*6*2;
1559 dispcnt.param = ESI_DISPCNT_HBlank;
1560 break;
1561
1562 case ESI_DISPCNT_HBlank:
1563 execHardware_hblank();
1564 //(once this was 1092 or 1092/12=91 dots.)
1565 //there are surely 355 dots per scanline, less 267 for non-blanking period. the rest is hblank and then after that is hstart
1566 dispcnt.timestamp += (355-267)*6*2;
1567 dispcnt.param = ESI_DISPCNT_HStart;
1568 break;
1569 }
1570 }
1571
1572 #ifdef EXPERIMENTAL_WIFI_COMM
1573 if(wifi.isTriggered())
1574 {
1575 WIFI_usTrigger();
1576 wifi.timestamp += kWifiCycles;
1577 }
1578 #endif
1579
1580 if(divider.isTriggered()) divider.exec();
1581 if(sqrtunit.isTriggered()) sqrtunit.exec();
1582 if(gxfifo.isTriggered()) gxfifo.exec();
1583
1584
1585 #define test(X,Y) if(dma_##X##_##Y .isTriggered()) dma_##X##_##Y .exec();
1586 test(0,0); test(0,1); test(0,2); test(0,3);
1587 test(1,0); test(1,1); test(1,2); test(1,3);
1588 #undef test
1589 #define test(X,Y) if(timer_##X##_##Y .enabled) if(timer_##X##_##Y .isTriggered()) timer_##X##_##Y .exec();
1590 test(0,0); test(0,1); test(0,2); test(0,3);
1591 test(1,0); test(1,1); test(1,2); test(1,3);
1592 #undef test
1593 }
1594
1595 void execHardware_interrupts();
1596
1597 static void saveUserInput(EMUFILE* os);
1598 static bool loadUserInput(EMUFILE* is, int version);
1599
nds_savestate(EMUFILE * os)1600 void nds_savestate(EMUFILE* os)
1601 {
1602 //version
1603 write32le(3,os);
1604
1605 sequencer.save(os);
1606
1607 saveUserInput(os);
1608
1609 write32le(LidClosed,os);
1610 write8le(countLid,os);
1611 }
1612
nds_loadstate(EMUFILE * is,int size)1613 bool nds_loadstate(EMUFILE* is, int size)
1614 {
1615 // this isn't part of the savestate loading logic, but
1616 // don't skip the next frame after loading a savestate
1617 frameSkipper.OmitSkip(true, true);
1618
1619 //read version
1620 u32 version;
1621 if(read32le(&version,is) != 1) return false;
1622
1623 if(version > 3) return false;
1624
1625 bool temp = true;
1626 temp &= sequencer.load(is, version);
1627 if(version <= 1 || !temp) return temp;
1628 temp &= loadUserInput(is, version);
1629
1630 if(version < 3) return temp;
1631
1632 read32le(&LidClosed,is);
1633 read8le(&countLid,is);
1634
1635 return temp;
1636 }
1637
1638 #ifdef LOG_ARM9
arm9log()1639 FORCEINLINE void arm9log()
1640 {
1641 if(dolog)
1642 {
1643 char dasmbuf[4096];
1644 if(NDS_ARM9.CPSR.bits.T)
1645 des_thumb_instructions_set[((NDS_ARM9.instruction)>>6)&1023](NDS_ARM9.instruct_adr, NDS_ARM9.instruction, dasmbuf);
1646 else
1647 des_arm_instructions_set[INDEX(NDS_ARM9.instruction)](NDS_ARM9.instruct_adr, NDS_ARM9.instruction, dasmbuf);
1648
1649 #ifdef LOG_TO_FILE
1650 if (!fp_dis9) return;
1651 #ifdef LOG_TO_FILE_REGS
1652 fprintf(fp_dis9, "\t\t;%05d:%03d %12lld\n\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",
1653 currFrameCounter, nds.VCount, nds_timer,
1654 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],
1655 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],
1656 NDS_ARM9.next_instruction, NDS_ARM9.CPSR.bits.N, NDS_ARM9.CPSR.bits.Z, NDS_ARM9.CPSR.bits.C, NDS_ARM9.CPSR.bits.V);
1657 #endif
1658 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);
1659 /*if (NDS_ARM9.instruction == 0)
1660 {
1661 dolog = false;
1662 INFO("Disassembler is stopped\n");
1663 }*/
1664 #else
1665 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",
1666 currFrameCounter, nds.VCount, nds_timer,
1667 NDS_ARM9.instruct_adr,NDS_ARM9.instruction, dasmbuf,
1668 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],
1669 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]);
1670 #endif
1671 }
1672 }
1673 #endif
1674
1675 #ifdef LOG_ARM7
arm7log()1676 FORCEINLINE void arm7log()
1677 {
1678 if(dolog)
1679 {
1680 char dasmbuf[4096];
1681 if(NDS_ARM7.CPSR.bits.T)
1682 des_thumb_instructions_set[((NDS_ARM7.instruction)>>6)&1023](NDS_ARM7.instruct_adr, NDS_ARM7.instruction, dasmbuf);
1683 else
1684 des_arm_instructions_set[INDEX(NDS_ARM7.instruction)](NDS_ARM7.instruct_adr, NDS_ARM7.instruction, dasmbuf);
1685 #ifdef LOG_TO_FILE
1686 if (!fp_dis7) return;
1687 #ifdef LOG_TO_FILE_REGS
1688 fprintf(fp_dis7, "\t\t;%05d:%03d %12lld\n\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",
1689 currFrameCounter, nds.VCount, nds_timer,
1690 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],
1691 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],
1692 NDS_ARM7.next_instruction, NDS_ARM7.CPSR.bits.N, NDS_ARM7.CPSR.bits.Z, NDS_ARM7.CPSR.bits.C, NDS_ARM7.CPSR.bits.V);
1693 #endif
1694 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);
1695 /*if (NDS_ARM7.instruction == 0)
1696 {
1697 dolog = false;
1698 INFO("Disassembler is stopped\n");
1699 }*/
1700 #else
1701 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",
1702 currFrameCounter, nds.VCount, nds_timer,
1703 NDS_ARM7.instruct_adr,NDS_ARM7.instruction, dasmbuf,
1704 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],
1705 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]);
1706 #endif
1707 }
1708 }
1709 #endif
1710
1711 //these have not been tuned very well yet.
1712 static const int kMaxWork = 4000;
1713 static const int kIrqWait = 4000;
1714
1715
1716 template<bool doarm9, bool doarm7>
minarmtime(s32 arm9,s32 arm7)1717 static FORCEINLINE s32 minarmtime(s32 arm9, s32 arm7)
1718 {
1719 if(doarm9)
1720 {
1721 if(doarm7)
1722 return min(arm9,arm7);
1723 return arm9;
1724 }
1725 return arm7;
1726 }
1727
1728 #ifdef HAVE_JIT
1729 template<bool doarm9, bool doarm7, bool jit>
1730 #else
1731 template<bool doarm9, bool doarm7>
1732 #endif
armInnerLoop(const u64 nds_timer_base,const s32 s32next,s32 arm9,s32 arm7)1733 static /*donotinline*/ std::pair<s32,s32> armInnerLoop(
1734 const u64 nds_timer_base, const s32 s32next, s32 arm9, s32 arm7)
1735 {
1736 s32 timer = minarmtime<doarm9,doarm7>(arm9,arm7);
1737 while(timer < s32next && !sequencer.reschedule && execute)
1738 {
1739 if(doarm9 && (!doarm7 || arm9 <= timer))
1740 {
1741 if(!NDS_ARM9.waitIRQ&&!nds.freezeBus)
1742 {
1743 #ifdef LOG_ARM9
1744 arm9log();
1745 #endif
1746 #ifdef DEBUG
1747 debug();
1748 #endif
1749 #ifdef HAVE_JIT
1750 arm9 += armcpu_exec<ARMCPU_ARM9,jit>();
1751 #else
1752 arm9 += armcpu_exec<ARMCPU_ARM9>();
1753 #endif
1754 #ifdef DEVELOPER
1755 nds_debug_continuing[0] = false;
1756 #endif
1757 }
1758 else
1759 {
1760 s32 temp = arm9;
1761 arm9 = min(s32next, arm9 + kIrqWait);
1762 nds.idleCycles[0] += arm9-temp;
1763 if (gxFIFO.size < 255) nds.freezeBus &= ~1;
1764 }
1765 }
1766 if(doarm7 && (!doarm9 || arm7 <= timer))
1767 {
1768 if(!NDS_ARM7.waitIRQ&&!nds.freezeBus)
1769 {
1770 #ifdef LOG_ARM7
1771 arm7log();
1772 #endif
1773 #ifdef HAVE_JIT
1774 arm7 += (armcpu_exec<ARMCPU_ARM7,jit>()<<1);
1775 #else
1776 arm7 += (armcpu_exec<ARMCPU_ARM7>()<<1);
1777 #endif
1778 #ifdef DEVELOPER
1779 nds_debug_continuing[1] = false;
1780 #endif
1781 }
1782 else
1783 {
1784 s32 temp = arm7;
1785 arm7 = min(s32next, arm7 + kIrqWait);
1786 nds.idleCycles[1] += arm7-temp;
1787 if(arm7 == s32next)
1788 {
1789 nds_timer = nds_timer_base + minarmtime<doarm9,false>(arm9,arm7);
1790 #ifdef HAVE_JIT
1791 return armInnerLoop<doarm9,false,jit>(nds_timer_base, s32next, arm9, arm7);
1792 #else
1793 return armInnerLoop<doarm9,false>(nds_timer_base, s32next, arm9, arm7);
1794 #endif
1795 }
1796 }
1797 }
1798
1799 timer = minarmtime<doarm9,doarm7>(arm9,arm7);
1800 nds_timer = nds_timer_base + timer;
1801 }
1802
1803 return std::make_pair(arm9, arm7);
1804 }
1805
NDS_debug_break()1806 void NDS_debug_break()
1807 {
1808 NDS_ARM9.stalled = NDS_ARM7.stalled = 1;
1809
1810 //triggers an immediate exit from the cpu loop
1811 NDS_Reschedule();
1812 }
1813
NDS_debug_continue()1814 void NDS_debug_continue()
1815 {
1816 NDS_ARM9.stalled = NDS_ARM7.stalled = 0;
1817 }
1818
NDS_debug_step()1819 void NDS_debug_step()
1820 {
1821 NDS_debug_continue();
1822 singleStep = true;
1823 }
1824
NDS_exec(s32 nb)1825 void NDS_exec(s32 nb)
1826 {
1827 sequencer.nds_vblankEnded = false;
1828
1829 nds.cpuloopIterationCount = 0;
1830
1831 #ifndef NDEBUG
1832 IF_DEVELOPER(for(int i=0;i<32;i++) DEBUG_statistics.sequencerExecutionCounters[i] = 0);
1833 #endif
1834
1835 if(nds.sleeping)
1836 {
1837 //speculative code: if ANY irq happens, wake up the arm7.
1838 //I think the arm7 program analyzes the system and may decide not to wake up
1839 //if it is dissatisfied with the conditions
1840 if((MMU.reg_IE[1] & MMU.gen_IF<1>()))
1841 nds.sleeping = FALSE;
1842 }
1843 else
1844 {
1845 for(;;)
1846 {
1847 //trap the debug-stalled condition
1848 #ifdef DEVELOPER
1849 singleStep = false;
1850 //(gdb stub doesnt yet know how to trigger these immediately by calling reschedule)
1851 if ((NDS_ARM9.stalled || NDS_ARM7.stalled) && execute)
1852 {
1853 driver->EMU_DebugIdleEnter();
1854
1855 while((NDS_ARM9.stalled || NDS_ARM7.stalled) && execute)
1856 {
1857 driver->EMU_DebugIdleUpdate();
1858 nds_debug_continuing[0] = nds_debug_continuing[1] = true;
1859 }
1860
1861 driver->EMU_DebugIdleWakeUp();
1862 }
1863 #endif
1864
1865 nds.cpuloopIterationCount++;
1866 sequencer.execHardware();
1867
1868 //break out once per frame
1869 if(sequencer.nds_vblankEnded) break;
1870 //it should be benign to execute execHardware in the next frame,
1871 //since there won't be anything for it to do (everything should be scheduled in the future)
1872
1873 //bail in case the system halted
1874 if(!execute) break;
1875
1876 execHardware_interrupts();
1877
1878 //find next work unit:
1879 u64 next = sequencer.findNext();
1880 next = min(next,nds_timer+kMaxWork); //lets set an upper limit for now
1881
1882 //printf("%d\n",(next-nds_timer));
1883
1884 sequencer.reschedule = false;
1885
1886 //cast these down to 32bits so that things run faster on 32bit procs
1887 u64 nds_timer_base = nds_timer;
1888 s32 arm9 = (s32)(nds_arm9_timer-nds_timer);
1889 s32 arm7 = (s32)(nds_arm7_timer-nds_timer);
1890 s32 s32next = (s32)(next-nds_timer);
1891
1892 #ifdef DEVELOPER
1893 if(singleStep)
1894 {
1895 s32next = 1;
1896 }
1897 #endif
1898
1899 #ifdef HAVE_JIT
1900 std::pair<s32,s32> arm9arm7 = CommonSettings.use_jit
1901 ? armInnerLoop<true,true,true>(nds_timer_base,s32next,arm9,arm7)
1902 : armInnerLoop<true,true,false>(nds_timer_base,s32next,arm9,arm7);
1903 #else
1904 std::pair<s32,s32> arm9arm7 = armInnerLoop<true,true>(nds_timer_base,s32next,arm9,arm7);
1905 #endif
1906
1907 #ifdef DEVELOPER
1908 if(singleStep)
1909 {
1910 NDS_ARM9.stalled = NDS_ARM7.stalled = 1;
1911 }
1912 #endif
1913
1914 arm9 = arm9arm7.first;
1915 arm7 = arm9arm7.second;
1916 nds_arm7_timer = nds_timer_base+arm7;
1917 nds_arm9_timer = nds_timer_base+arm9;
1918
1919 #ifndef NDEBUG
1920 //what we find here is dependent on the timing constants above
1921 //if(nds_timer>next && (nds_timer-next)>22)
1922 // printf("curious. please report: over by %d\n",(int)(nds_timer-next));
1923 #endif
1924
1925 //if we were waiting for an irq, don't wait too long:
1926 //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)
1927 if(NDS_ARM9.waitIRQ)
1928 {
1929 nds.idleCycles[0] -= (s32)(nds_arm9_timer-nds_timer);
1930 nds_arm9_timer = nds_timer;
1931 }
1932 if(NDS_ARM7.waitIRQ)
1933 {
1934 nds.idleCycles[1] -= (s32)(nds_arm7_timer-nds_timer);
1935 nds_arm7_timer = nds_timer;
1936 }
1937 }
1938 }
1939
1940 currFrameCounter++;
1941 #ifndef NDEBUG
1942 DEBUG_Notify.NextFrame();
1943 #endif
1944 if (cheats)
1945 cheats->process();
1946 }
1947
execHardware_interrupts_core()1948 template<int PROCNUM> static void execHardware_interrupts_core()
1949 {
1950 u32 IF = MMU.gen_IF<PROCNUM>();
1951 u32 IE = MMU.reg_IE[PROCNUM];
1952 u32 masked = IF & IE;
1953 if(ARMPROC.halt_IE_and_IF && masked)
1954 {
1955 ARMPROC.halt_IE_and_IF = FALSE;
1956 ARMPROC.waitIRQ = FALSE;
1957 }
1958
1959 if(masked && MMU.reg_IME[PROCNUM] && !ARMPROC.CPSR.bits.I)
1960 {
1961 //printf("Executing IRQ on procnum %d with IF = %08X and IE = %08X\n",PROCNUM,IF,IE);
1962 armcpu_irqException(&ARMPROC);
1963 }
1964 }
1965
execHardware_interrupts()1966 void execHardware_interrupts()
1967 {
1968 execHardware_interrupts_core<ARMCPU_ARM9>();
1969 execHardware_interrupts_core<ARMCPU_ARM7>();
1970 }
1971
1972 static void resetUserInput();
1973
PrepareBiosARM7()1974 static void PrepareBiosARM7()
1975 {
1976 //begin with the bios unloaded
1977 NDS_ARM7.BIOS_loaded = false;
1978 memset(MMU.ARM7_BIOS, 0, sizeof(MMU.ARM7_BIOS));
1979
1980 if(CommonSettings.UseExtBIOS == true)
1981 {
1982 //read arm7 bios from inputfile and flag it if it succeeds
1983 FILE *arm7inf = fopen(CommonSettings.ARM7BIOS,"rb");
1984 if (arm7inf)
1985 {
1986 if (fread(MMU.ARM7_BIOS,1,16384,arm7inf) == 16384)
1987 NDS_ARM7.BIOS_loaded = true;
1988 fclose(arm7inf);
1989 }
1990 }
1991
1992 //choose to use SWI emulation or routines from bios
1993 if((CommonSettings.SWIFromBIOS) && (NDS_ARM7.BIOS_loaded))
1994 {
1995 NDS_ARM7.swi_tab = 0;
1996
1997 //if we used routines from bios, apply patches
1998 if (CommonSettings.PatchSWI3)
1999 {
2000 //[3801] SUB R0, #1 -> [4770] BX LR
2001 T1WriteWord(MMU.ARM7_BIOS,0x2F08, 0x4770);
2002 }
2003 }
2004 else
2005 NDS_ARM7.swi_tab = ARM_swi_tab[ARMCPU_ARM7];
2006
2007 if(NDS_ARM7.BIOS_loaded)
2008 {
2009 #ifdef DEBUG
2010 INFO("ARM7 BIOS load: %s.\n", NDS_ARM7.BIOS_loaded?"OK":"FAILED");
2011 #endif
2012 }
2013 else
2014 {
2015 //fake bios content, critical to normal operations, since we dont have a real bios.
2016
2017 T1WriteLong(MMU.ARM7_BIOS, 0x0000, 0xEAFFFFFE); //B 00000000 (reset: infinite loop) (originally: 0xE25EF002 - SUBS PC, LR, #2
2018 T1WriteLong(MMU.ARM7_BIOS, 0x0004, 0xEAFFFFFE); //B 00000004 (undefined instruction: infinite loop)
2019 T1WriteLong(MMU.ARM7_BIOS, 0x0008, 0xEAFFFFFE); //B 00000280 (SWI: infinite loop [since we will be HLEing the SWI routines])
2020 T1WriteLong(MMU.ARM7_BIOS, 0x000C, 0xEAFFFFFE); //B 0000000C (prefetch abort: infinite loop)
2021 T1WriteLong(MMU.ARM7_BIOS, 0x0010, 0xEAFFFFFE); //B 00000010 (data abort: infinite loop)
2022 T1WriteLong(MMU.ARM7_BIOS, 0x0018, 0xEA000000); //B 00000020 (IRQ: branch to handler)
2023 T1WriteLong(MMU.ARM7_BIOS, 0x001C, 0xEAFFFFFE); //B 0000001C (FIQ vector: infinite loop)
2024 //IRQ handler
2025 T1WriteLong(MMU.ARM7_BIOS, 0x0020, 0xE92D500F); //STMDB SP!, {R0-R3,R12,LR}
2026 T1WriteLong(MMU.ARM7_BIOS, 0x0024, 0xE3A00301); //MOV R0, #4000000
2027 T1WriteLong(MMU.ARM7_BIOS, 0x0028, 0xE28FE000); //ADD LR, PC, #0
2028 T1WriteLong(MMU.ARM7_BIOS, 0x002C, 0xE510F004); //LDR PC, [R0, -#4]
2029 T1WriteLong(MMU.ARM7_BIOS, 0x0030, 0xE8BD500F); //LDMIA SP!, {R0-R3,R12,LR}
2030 T1WriteLong(MMU.ARM7_BIOS, 0x0034, 0xE25EF004); //SUBS PC, LR, #4
2031 }
2032 }
2033
PrepareBiosARM9()2034 static void PrepareBiosARM9()
2035 {
2036 //begin with the bios unloaded
2037 memset(MMU.ARM9_BIOS, 0, sizeof(MMU.ARM9_BIOS));
2038 NDS_ARM9.BIOS_loaded = false;
2039
2040 if(CommonSettings.UseExtBIOS == true)
2041 {
2042 //read arm9 bios from inputfile and flag it if it succeeds
2043 FILE* arm9inf = fopen(CommonSettings.ARM9BIOS,"rb");
2044 if (arm9inf)
2045 {
2046 if (fread(MMU.ARM9_BIOS,1,4096,arm9inf) == 4096)
2047 NDS_ARM9.BIOS_loaded = true;
2048 fclose(arm9inf);
2049 }
2050 }
2051
2052 //choose to use SWI emulation or routines from bios
2053 if((CommonSettings.SWIFromBIOS) && (NDS_ARM9.BIOS_loaded))
2054 {
2055 NDS_ARM9.swi_tab = 0;
2056
2057 //if we used routines from bios, apply patches
2058 //[3801] SUB R0, #1 -> [4770] BX LR
2059 if (CommonSettings.PatchSWI3)
2060 T1WriteWord(MMU.ARM9_BIOS, 0x07CC, 0x4770);
2061 }
2062 else NDS_ARM9.swi_tab = ARM_swi_tab[ARMCPU_ARM9];
2063
2064 if(NDS_ARM9.BIOS_loaded)
2065 {
2066 #ifdef DEBUG
2067 INFO("ARM9 BIOS load: %s.\n", NDS_ARM9.BIOS_loaded?"OK":"FAILED");
2068 #endif
2069 }
2070 else
2071 {
2072 //fake bios content, critical to normal operations, since we dont have a real bios.
2073 //it'd be cool if we could write this in some kind of assembly language, inline or otherwise, without some bulky dependencies
2074 //perhaps we could build it with devkitarm? but thats bulky (offline) dependencies, to be sure..
2075
2076 //reminder: bios chains data abort to fast irq
2077
2078 //exception vectors:
2079 T1WriteLong(MMU.ARM9_BIOS, 0x0000, 0xEAFFFFFE); // (infinite loop for) Reset !!!
2080 //T1WriteLong(MMU.ARM9_BIOS, 0x0004, 0xEAFFFFFE); // (infinite loop for) Undefined instruction
2081 T1WriteLong(MMU.ARM9_BIOS, 0x0004, 0xEA000004); // Undefined instruction -> Fast IRQ (just guessing)
2082 T1WriteLong(MMU.ARM9_BIOS, 0x0008, 0xEA00009C); // SWI -> ?????
2083 T1WriteLong(MMU.ARM9_BIOS, 0x000C, 0xEAFFFFFE); // (infinite loop for) Prefetch Abort
2084 T1WriteLong(MMU.ARM9_BIOS, 0x0010, 0xEA000001); // Data Abort -> Fast IRQ
2085 T1WriteLong(MMU.ARM9_BIOS, 0x0014, 0x00000000); // Reserved
2086 T1WriteLong(MMU.ARM9_BIOS, 0x0018, 0xEA000095); // Normal IRQ -> 0x0274
2087 T1WriteLong(MMU.ARM9_BIOS, 0x001C, 0xEA00009D); // Fast IRQ -> 0x0298
2088
2089 //copy the logo content into the bios - Pokemon Platinum uses this in Pal Park trade
2090 //it compares the logo from the arm9 bios to the logo in the GBA header.
2091 //NOTE: in the unlikely event that the valid header is missing from the gameInfo, we'd be doing wrong here.
2092 // however, its nice not to have the logo embedded here.
2093 // TODO - take a CRC of the logo, check vs logoCRC16, and a hardcoded value, to make sure all is in order--report error if not
2094 memcpy(&MMU.ARM9_BIOS[0x20], &gameInfo.header.logo[0], 0x9C);
2095 T1WriteWord(MMU.ARM9_BIOS, 0x20 + 0x9C, gameInfo.header.logoCRC16);
2096 //... and with that we are at 0xBC:
2097
2098 //(now what goes in this gap?? nothing we need, i guess)
2099
2100 //IRQ handler: get dtcm address and jump to a vector in it
2101 T1WriteLong(MMU.ARM9_BIOS, 0x0274, 0xE92D500F); //STMDB SP!, {R0-R3,R12,LR}
2102 T1WriteLong(MMU.ARM9_BIOS, 0x0278, 0xEE190F11); //MRC CP15, 0, R0, CR9, CR1, 0
2103 T1WriteLong(MMU.ARM9_BIOS, 0x027C, 0xE1A00620); //MOV R0, R0, LSR #C
2104 T1WriteLong(MMU.ARM9_BIOS, 0x0280, 0xE1A00600); //MOV R0, R0, LSL #C
2105 T1WriteLong(MMU.ARM9_BIOS, 0x0284, 0xE2800C40); //ADD R0, R0, #4000
2106 T1WriteLong(MMU.ARM9_BIOS, 0x0288, 0xE28FE000); //ADD LR, PC, #0
2107 T1WriteLong(MMU.ARM9_BIOS, 0x028C, 0xE510F004); //LDR PC, [R0, -#4]
2108
2109 //????
2110 T1WriteLong(MMU.ARM9_BIOS, 0x0290, 0xE8BD500F); //LDMIA SP!, {R0-R3,R12,LR}
2111 T1WriteLong(MMU.ARM9_BIOS, 0x0294, 0xE25EF004); //SUBS PC, LR, #4
2112
2113 //-------
2114 //FIQ and abort exception handler
2115 //TODO - this code is copied from the bios. refactor it
2116 //friendly reminder: to calculate an immediate offset: encoded = (desired_address-cur_address-8)
2117
2118 T1WriteLong(MMU.ARM9_BIOS, 0x0298, 0xE10FD000); //MRS SP, CPSR
2119 T1WriteLong(MMU.ARM9_BIOS, 0x029C, 0xE38DD0C0); //ORR SP, SP, #C0
2120
2121 T1WriteLong(MMU.ARM9_BIOS, 0x02A0, 0xE12FF00D); //MSR CPSR_fsxc, SP
2122 T1WriteLong(MMU.ARM9_BIOS, 0x02A4, 0xE59FD000 | (0x2D4-0x2A4-8)); //LDR SP, [FFFF02D4]
2123 T1WriteLong(MMU.ARM9_BIOS, 0x02A8, 0xE28DD001); //ADD SP, SP, #1
2124 T1WriteLong(MMU.ARM9_BIOS, 0x02AC, 0xE92D5000); //STMDB SP!, {R12,LR}
2125
2126 T1WriteLong(MMU.ARM9_BIOS, 0x02B0, 0xE14FE000); //MRS LR, SPSR
2127 T1WriteLong(MMU.ARM9_BIOS, 0x02B4, 0xEE11CF10); //MRC CP15, 0, R12, CR1, CR0, 0
2128 T1WriteLong(MMU.ARM9_BIOS, 0x02B8, 0xE92D5000); //STMDB SP!, {R12,LR}
2129 T1WriteLong(MMU.ARM9_BIOS, 0x02BC, 0xE3CCC001); //BIC R12, R12, #1
2130
2131 T1WriteLong(MMU.ARM9_BIOS, 0x02C0, 0xEE01CF10); //MCR CP15, 0, R12, CR1, CR0, 0
2132 T1WriteLong(MMU.ARM9_BIOS, 0x02C4, 0xE3CDC001); //BIC R12, SP, #1
2133 T1WriteLong(MMU.ARM9_BIOS, 0x02C8, 0xE59CC010); //LDR R12, [R12, #10]
2134 T1WriteLong(MMU.ARM9_BIOS, 0x02CC, 0xE35C0000); //CMP R12, #0
2135
2136 T1WriteLong(MMU.ARM9_BIOS, 0x02D0, 0x112FFF3C); //BLXNE R12
2137 T1WriteLong(MMU.ARM9_BIOS, 0x02D4, 0x027FFD9C); //0x027FFD9C
2138 //---------
2139 }
2140 }
2141
JumbleMemory()2142 static void JumbleMemory() { }
2143
PrepareLogfiles()2144 static void PrepareLogfiles()
2145 {
2146 #ifdef LOG_ARM7
2147 if (fp_dis7 != NULL)
2148 {
2149 fclose(fp_dis7);
2150 fp_dis7 = NULL;
2151 }
2152 fp_dis7 = fopen("D:\\desmume_dis7.asm", "w");
2153 #endif
2154
2155 #ifdef LOG_ARM9
2156 if (fp_dis9 != NULL)
2157 {
2158 fclose(fp_dis9);
2159 fp_dis9 = NULL;
2160 }
2161 fp_dis9 = fopen("D:\\desmume_dis9.asm", "w");
2162 #endif
2163 }
2164
NDS_LegitBoot()2165 bool NDS_LegitBoot()
2166 {
2167 #ifdef HAVE_JIT
2168 //hack for firmware boot in JIT mode.
2169 //we know that it takes certain jit parameters to successfully boot the firmware.
2170 //CRAZYMAX: is it safe to accept anything smaller than 12?
2171 CommonSettings.jit_max_block_size = std::min(CommonSettings.jit_max_block_size,12U);
2172 #endif
2173
2174 //partially clobber the loaded firmware with the user settings from DFC
2175 firmware->loadSettings();
2176
2177 //since firmware only boots encrypted roms, we have to make sure it's encrypted first
2178 //this has not been validated on big endian systems. it almost positively doesn't work.
2179 if (gameInfo.header.CRC16 != 0)
2180 EncryptSecureArea((u8*)&gameInfo.header, (u8*)gameInfo.secureArea);
2181
2182 //boot processors from their bios entrypoints
2183 armcpu_init(&NDS_ARM7, 0x00000000);
2184 armcpu_init(&NDS_ARM9, 0xFFFF0000);
2185
2186 return true;
2187 }
2188
2189 //the fake firmware boot-up process
NDS_FakeBoot()2190 bool NDS_FakeBoot()
2191 {
2192 NDS_header * header = NDS_getROMHeader();
2193
2194 #ifndef NDEBUG
2195 DEBUG_reset();
2196 #endif
2197
2198 if (!header) return false;
2199
2200 nds.isFakeBooted = true;
2201
2202 //since we're bypassing the code to decrypt the secure area, we need to make sure its decrypted first
2203 //this has not been validated on big endian systems. it almost positively doesn't work.
2204 if (gameInfo.header.CRC16 != 0)
2205 {
2206 bool okRom = DecryptSecureArea((u8*)&gameInfo.header, (u8*)gameInfo.secureArea);
2207
2208 if(!okRom) {
2209 printf("Specified file is not a valid rom\n");
2210 return false;
2211 }
2212 }
2213
2214 //bios (or firmware) sets this default, which is generally not important for retail games but some homebrews are depending on
2215 _MMU_write08<ARMCPU_ARM9>(REG_WRAMCNT,3);
2216
2217 //EDIT - whats this firmware and how is it relating to the dummy firmware below
2218 //how do these even get used? what is the purpose of unpack and why is it not used by the firmware boot process?
2219 if (CommonSettings.UseExtFirmware && firmware->loaded())
2220 {
2221 firmware->unpack();
2222 firmware->loadSettings();
2223 }
2224
2225 // Create the dummy firmware
2226 //EDIT - whats dummy firmware and how is relating to the above?
2227 //it seems to be emplacing basic firmware data into MMU.fw.data
2228 NDS_CreateDummyFirmware(&CommonSettings.fw_config);
2229
2230 //firmware loads the game card arm9 and arm7 programs as specified in rom header
2231 {
2232 bool hasSecureArea = ((gameInfo.romType == ROM_NDS) && (gameInfo.header.CRC16 != 0));
2233 //copy the arm9 program to the address specified by rom header
2234 u32 src = header->ARM9src;
2235 u32 dst = header->ARM9cpy;
2236 for(u32 i = 0; i < header->ARM9binSize; i+=4)
2237 {
2238 u32 tmp = (hasSecureArea && ((src >= 0x4000) && (src < 0x8000)))?LE_TO_LOCAL_32(*(u32*)(gameInfo.secureArea + (src - 0x4000))):gameInfo.readROM(src);
2239
2240 _MMU_write32<ARMCPU_ARM9>(dst, tmp);
2241
2242 dst += 4;
2243 src += 4;
2244 }
2245
2246 //copy the arm7 program to the address specified by rom header
2247 src = header->ARM7src;
2248 dst = header->ARM7cpy;
2249 for(u32 i = 0; i < header->ARM7binSize; i+=4)
2250 {
2251 _MMU_write32<ARMCPU_ARM7>(dst, gameInfo.readROM(src));
2252
2253 dst += 4;
2254 src += 4;
2255 }
2256 }
2257
2258 //bios does this (thats weird, though. shouldnt it get changed when the card is swapped in the firmware menu?
2259 //right now our firmware menu isnt detecting any change to the card.
2260 //are some games depending on it being written here? please document.
2261 //_MMU_write16<ARMCPU_ARM9>(0x027FF808, T1ReadWord(MMU.CART_ROM, 0x15E));
2262
2263 //firmware sets up a copy of the firmware user settings in memory.
2264 //TBD - this code is really clunky
2265 //it seems to be copying the MMU.fw.data data into RAM in the user memory stash locations
2266 u8 temp_buffer[NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT];
2267 if ( copy_firmware_user_data( temp_buffer, MMU.fw.data)) {
2268 for ( int fw_index = 0; fw_index < NDS_FW_USER_SETTINGS_MEM_BYTE_COUNT; fw_index++)
2269 _MMU_write08<ARMCPU_ARM9>(0x027FFC80 + fw_index, temp_buffer[fw_index]);
2270 }
2271
2272 //something copies the whole header to Main RAM 0x27FFE00 on startup. (http://nocash.emubase.de/gbatek.htm#dscartridgeheader)
2273 //once upon a time this copied 0x90 more. this was thought to be wrong, and changed.
2274 if(nds.Is_DSI())
2275 {
2276 //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.
2277 for (int i = 0; i < (0x170); i+=4)
2278 _MMU_write32<ARMCPU_ARM9>(0x027FFE00 + i, gameInfo.readROM(i));
2279 }
2280 else
2281 {
2282 for (int i = 0; i < (0x170); i+=4)
2283 _MMU_write32<ARMCPU_ARM9>(0x027FFE00 + i, gameInfo.readROM(i));
2284 }
2285
2286 //the firmware will be booting to these entrypoint addresses via BX (well, the arm9 at least; is unverified for the arm7)
2287 armcpu_init(&NDS_ARM7, header->ARM7exe);
2288 armcpu_init(&NDS_ARM9, header->ARM9exe);
2289
2290 //firmware sets REG_POSTFLG to the value indicating post-firmware status
2291 MMU.ARM9_REG[0x300] = 1;
2292 MMU.ARM7_REG[0x300] = 1;
2293
2294 //firmware makes system think it's booted from card -- EXTREMELY IMPORTANT!!! This is actually checked by some things. (which things?) Thanks to cReDiAr
2295 _MMU_write08<ARMCPU_ARM7>(0x02FFFC40,0x1); //<zero> removed redundant write to ARM9, this is going to shared main memory. But one has to wonder why r3478 was made which corrected a typo resulting in only ARMCPU_ARM7 getting used.
2296
2297 //the chipId is read several times
2298 //for some reason, each of those reads get stored here.
2299 _MMU_write32<ARMCPU_ARM7>(0x027FF800, gameInfo.chipID); //1st chipId
2300 _MMU_write32<ARMCPU_ARM7>(0x027FF804, gameInfo.chipID); //2nd (secure) chipId
2301 _MMU_write32<ARMCPU_ARM7>(0x027FFC00, gameInfo.chipID); //3rd (secure) chipId
2302
2303 // Write the header checksum to memory
2304 _MMU_write16<ARMCPU_ARM9>(0x027FF808, gameInfo.header.headerCRC16);
2305
2306 //bitbox 4k demo is so stripped down it relies on default stack values
2307 //otherwise the arm7 will crash before making a sound
2308 //(these according to gbatek softreset bios docs)
2309 NDS_ARM7.R13_svc = 0x0380FFDC;
2310 NDS_ARM7.R13_irq = 0x0380FFB0;
2311 NDS_ARM7.R13_usr = 0x0380FF00;
2312 NDS_ARM7.R[13] = NDS_ARM7.R13_usr;
2313 //and let's set these for the arm9 while we're at it, though we have no proof
2314 NDS_ARM9.R13_svc = 0x00803FC0;
2315 NDS_ARM9.R13_irq = 0x00803FA0;
2316 NDS_ARM9.R13_usr = 0x00803EC0;
2317 NDS_ARM9.R13_abt = NDS_ARM9.R13_usr; //?????
2318 //I think it is wrong to take gbatek's "SYS" and put it in USR--maybe USR doesnt matter.
2319 //i think SYS is all the misc modes. please verify by setting nonsensical stack values for USR here
2320 NDS_ARM9.R[13] = NDS_ARM9.R13_usr;
2321 //n.b.: im not sure about all these, I dont know enough about arm9 svc/irq/etc modes
2322 //and how theyre named in desmume to match them up correctly. i just guessed.
2323
2324 //--------------------------------
2325 //setup the homebrew argv
2326 //this is useful for nitrofs apps which are emulating themselves via cflash
2327 //struct __argv {
2328 // int argvMagic; //!< argv magic number, set to 0x5f617267 ('_arg') if valid
2329 // char *commandLine; //!< base address of command line, set of null terminated strings
2330 // int length; //!< total length of command line
2331 // int argc; //!< internal use, number of arguments
2332 // char **argv; //!< internal use, argv pointer
2333 //};
2334 std::string rompath = "fat:/" + path.RomName;
2335 const u32 kCommandline = 0x027E0000;
2336 //const u32 kCommandline = 0x027FFF84;
2337
2338 //homebrew-related stuff.
2339 //its safe to put things in this position.. apparently nothing important is here.
2340 //however, some games could be checking them as an anti-desmume measure, so we might have to control it with slot-1 settings to suggest booting a homebrew app
2341 //perhaps we could automatically boot homebrew to an R4-like device.
2342 _MMU_write32<ARMCPU_ARM9>(0x02FFFE70, 0x5f617267);
2343 _MMU_write32<ARMCPU_ARM9>(0x02FFFE74, kCommandline); //(commandline starts here)
2344 _MMU_write32<ARMCPU_ARM9>(0x02FFFE78, rompath.size()+1);
2345 //0x027FFF7C (argc)
2346 //0x027FFF80 (argv)
2347 for(size_t i=0;i<rompath.size();i++)
2348 _MMU_write08<ARMCPU_ARM9>(kCommandline+i, rompath[i]);
2349 _MMU_write08<ARMCPU_ARM9>(kCommandline+rompath.size(), 0);
2350 //--------------------------------
2351
2352 //Call the card post_fakeboot hook to perform additional initialization
2353 slot1_device->post_fakeboot(ARMCPU_ARM9);
2354 slot1_device->post_fakeboot(ARMCPU_ARM7);
2355
2356 delete header;
2357 return true;
2358 }
2359
NDS_Reset()2360 void NDS_Reset()
2361 {
2362 PrepareLogfiles();
2363
2364 currFrameCounter = 0;
2365
2366 resetUserInput();
2367
2368
2369 singleStep = false;
2370 nds_debug_continuing[0] = nds_debug_continuing[1] = false;
2371 nds.sleeping = FALSE;
2372 nds.cardEjected = FALSE;
2373 nds.freezeBus = 0;
2374 nds.power1.lcd = nds.power1.gpuMain = nds.power1.gfx3d_render = nds.power1.gfx3d_geometry = nds.power1.gpuSub = nds.power1.dispswap = 1;
2375 nds.power2.speakers = 1;
2376 nds.power2.wifi = 0;
2377 nds.wifiCycle = 0;
2378 memset(nds.timerCycle, 0, sizeof(u64) * 2 * 4);
2379 nds.old = 0;
2380 nds.scr_touchX = nds.scr_touchY = nds.adc_touchX = nds.adc_touchY = 0;
2381 nds.isTouch = 0;
2382 nds.isFakeBooted = false;
2383 nds.paddle = 0;
2384 nds.ConsoleType = CommonSettings.ConsoleType;
2385 nds._DebugConsole = CommonSettings.DebugConsole;
2386 nds.ensataEmulation = CommonSettings.EnsataEmulation;
2387 nds.stylusJitter = CommonSettings.StylusJitter;
2388 nds.ensataHandshake = ENSATA_HANDSHAKE_none;
2389 nds.ensataIpcSyncCounter = 0;
2390 nds_timer = 0;
2391 nds_arm9_timer = 0;
2392 nds_arm7_timer = 0;
2393 LidClosed = FALSE;
2394 countLid = 0;
2395
2396 MMU_Reset();
2397 SetupMMU(nds.Is_DebugConsole(),nds.Is_DSI());
2398 JumbleMemory();
2399
2400 #ifdef HAVE_JIT
2401 arm_jit_reset(CommonSettings.use_jit);
2402 #endif
2403
2404
2405 //initialize CP15 specially for this platform
2406 //TODO - how much of this is necessary for firmware boot?
2407 //(only ARM9 has CP15)
2408 reconstruct(&cp15);
2409 MMU.ARM9_RW_MODE = BIT7(cp15.ctrl);
2410 NDS_ARM9.intVector = 0xFFFF0000 * (BIT13(cp15.ctrl));
2411 NDS_ARM9.LDTBit = !BIT15(cp15.ctrl); //TBit
2412
2413 PrepareBiosARM7();
2414 PrepareBiosARM9();
2415
2416 if (firmware)
2417 {
2418 delete firmware;
2419 firmware = NULL;
2420 }
2421
2422 firmware = new CFIRMWARE();
2423 firmware->load();
2424
2425 //we will allow a proper firmware boot, if:
2426 //1. we have the ARM7 and ARM9 bioses (its doubtful that our HLE bios implement the necessary functions)
2427 //2. firmware is available
2428 //3. user has requested booting from firmware
2429 bool canBootFromFirmware = (NDS_ARM7.BIOS_loaded && NDS_ARM9.BIOS_loaded && CommonSettings.BootFromFirmware && firmware->loaded());
2430 bool bootResult = false;
2431 if(canBootFromFirmware)
2432 bootResult = NDS_LegitBoot();
2433 else
2434 bootResult = NDS_FakeBoot();
2435
2436 // Init calibration info
2437 memcpy(&TSCal, firmware->getTouchCalibrate(), sizeof(TSCalInfo));
2438
2439 GPU->Reset();
2440
2441 WIFI_Reset();
2442 memcpy(FW_Mac, (MMU.fw.data + 0x36), 6);
2443
2444 SPU_DeInit();
2445 SPU_ReInit(!canBootFromFirmware && bootResult);
2446
2447 //this needs to happen last, pretty much, since it establishes the correct scheduling state based on all of the above initialization
2448 initSchedule();
2449 }
2450
2451 //convert a 12.4 screen coordinate to an ADC value.
2452 //the desmume host system will track the screen coordinate, but the hardware should be receiving the raw ADC values.
2453 //so we'll need to use this to simulate the ADC values corresponding to the desired screen coords, based on the current TSC calibrations
NDS_getADCTouchPosX(int scrX_lsl4)2454 u16 NDS_getADCTouchPosX(int scrX_lsl4)
2455 {
2456 scrX_lsl4 >>= 4;
2457 int rv = ((scrX_lsl4 - TSCal.scr.x1 + 1) * TSCal.adc.width) / TSCal.scr.width + TSCal.adc.x1;
2458 rv = min(0xFFF, max(0, rv));
2459 return (u16)(rv);
2460 }
NDS_getADCTouchPosY(int scrY_lsl4)2461 u16 NDS_getADCTouchPosY(int scrY_lsl4)
2462 {
2463 scrY_lsl4 >>= 4;
2464 int rv = ((scrY_lsl4 - TSCal.scr.y1 + 1) * TSCal.adc.height) / TSCal.scr.height + TSCal.adc.y1;
2465 rv = min(0xFFF, max(0, rv));
2466 return (u16)(rv);
2467 }
2468
2469 static UserInput rawUserInput = {}; // requested input, generally what the user is physically pressing
2470 static UserInput finalUserInput = {}; // what gets sent to the game and possibly recorded
2471
NDS_getRawUserInput()2472 const UserInput& NDS_getRawUserInput()
2473 {
2474 return rawUserInput;
2475 }
2476
NDS_getProcessingUserInput()2477 UserInput& NDS_getProcessingUserInput()
2478 {
2479 return rawUserInput;
2480 }
2481
NDS_getFinalUserInput()2482 const UserInput& NDS_getFinalUserInput()
2483 {
2484 return finalUserInput;
2485 }
2486
2487
saveUserInput(EMUFILE * os,UserInput & input)2488 static void saveUserInput(EMUFILE* os, UserInput& input)
2489 {
2490 os->fwrite((const char*)input.buttons.array, 14);
2491 writebool(input.touch.isTouch, os);
2492 write16le(input.touch.touchX, os);
2493 write16le(input.touch.touchY, os);
2494 write32le(input.mic.micButtonPressed, os);
2495 }
loadUserInput(EMUFILE * is,UserInput & input,int version)2496 static bool loadUserInput(EMUFILE* is, UserInput& input, int version)
2497 {
2498 is->fread((char*)input.buttons.array, 14);
2499 readbool(&input.touch.isTouch, is);
2500 read16le(&input.touch.touchX, is);
2501 read16le(&input.touch.touchY, is);
2502 read32le(&input.mic.micButtonPressed, is);
2503 return true;
2504 }
resetUserInput(UserInput & input)2505 static void resetUserInput(UserInput& input)
2506 {
2507 memset(&input, 0, sizeof(UserInput));
2508 }
2509 // (userinput is kind of a misnomer, e.g. finalUserInput has to mirror nds.pad, nds.touchX, etc.)
saveUserInput(EMUFILE * os)2510 static void saveUserInput(EMUFILE* os)
2511 {
2512 saveUserInput(os, finalUserInput);
2513 saveUserInput(os, rawUserInput); // saved in case a savestate is made during input processing (which Lua could do if nothing else)
2514 bool validToProcessInput = true;
2515 writebool(validToProcessInput, os);
2516 for(int i = 0; i < 14; i++)
2517 write32le(0, os); // saved to make autofire more tolerable to use with re-recording
2518 }
loadUserInput(EMUFILE * is,int version)2519 static bool loadUserInput(EMUFILE* is, int version)
2520 {
2521 bool rv = true;
2522 bool validToProcessInput = true;
2523 rv &= loadUserInput(is, finalUserInput, version);
2524 rv &= loadUserInput(is, rawUserInput, version);
2525 readbool(&validToProcessInput, is);
2526 u32 j = 0;
2527 for(int i = 0; i < 14; i++)
2528 read32le((u32*)&j, is);
2529 return rv;
2530 }
resetUserInput()2531 static void resetUserInput()
2532 {
2533 resetUserInput(finalUserInput);
2534 }
2535
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)2536 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)
2537 {
2538 UserButtons& rawButtons = rawUserInput.buttons;
2539 rawButtons.R = R;
2540 rawButtons.L = L;
2541 rawButtons.D = D;
2542 rawButtons.U = U;
2543 rawButtons.T = T;
2544 rawButtons.S = S;
2545 rawButtons.B = B;
2546 rawButtons.A = A;
2547 rawButtons.Y = Y;
2548 rawButtons.X = X;
2549 rawButtons.W = W;
2550 rawButtons.E = E;
2551 rawButtons.G = G;
2552 rawButtons.F = F;
2553 }
NDS_setTouchPos(u16 x,u16 y,u16 scale)2554 void NDS_setTouchPos(u16 x, u16 y, u16 scale)
2555 {
2556 rawUserInput.touch.touchX = (x/scale)<<4;
2557 rawUserInput.touch.touchY = (y/scale)<<4;
2558 rawUserInput.touch.isTouch = true;
2559
2560 nds.stylusJitter = CommonSettings.StylusJitter;
2561 }
2562
NDS_releaseTouch(void)2563 void NDS_releaseTouch(void)
2564 {
2565 rawUserInput.touch.touchX = 0;
2566 rawUserInput.touch.touchY = 0;
2567 rawUserInput.touch.isTouch = false;
2568 }
NDS_setMic(bool pressed)2569 void NDS_setMic(bool pressed)
2570 {
2571 rawUserInput.mic.micButtonPressed = (pressed ? TRUE : FALSE);
2572 }
2573
2574
NDS_applyFinalInput()2575 static void NDS_applyFinalInput()
2576 {
2577 const UserInput& input = NDS_getFinalUserInput();
2578
2579 u16 pad = (0 |
2580 ((input.buttons.A ? 0 : 0x80) >> 7) |
2581 ((input.buttons.B ? 0 : 0x80) >> 6) |
2582 ((input.buttons.T ? 0 : 0x80) >> 5) |
2583 ((input.buttons.S ? 0 : 0x80) >> 4) |
2584 ((input.buttons.R ? 0 : 0x80) >> 3) |
2585 ((input.buttons.L ? 0 : 0x80) >> 2) |
2586 ((input.buttons.U ? 0 : 0x80) >> 1) |
2587 ((input.buttons.D ? 0 : 0x80) ) |
2588 ((input.buttons.E ? 0 : 0x80) << 1) |
2589 ((input.buttons.W ? 0 : 0x80) << 2)) ;
2590
2591 pad = LOCAL_TO_LE_16(pad);
2592 ((u16 *)MMU.ARM9_REG)[0x130>>1] = (u16)pad;
2593 ((u16 *)MMU.ARM7_REG)[0x130>>1] = (u16)pad;
2594
2595 u16 k_cnt = ((u16 *)MMU.ARM9_REG)[0x132>>1];
2596 if ( k_cnt & (1<<14))
2597 {
2598 //INFO("ARM9: KeyPad IRQ (pad 0x%04X, cnt 0x%04X (condition %s))\n", pad, k_cnt, k_cnt&(1<<15)?"AND":"OR");
2599 u16 k_cnt_selected = (k_cnt & 0x3F);
2600 if (k_cnt&(1<<15)) // AND
2601 {
2602 if ((~pad & k_cnt_selected) == k_cnt_selected) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_KEYPAD);
2603 }
2604 else // OR
2605 {
2606 if (~pad & k_cnt_selected) NDS_makeIrq(ARMCPU_ARM9,IRQ_BIT_KEYPAD);
2607 }
2608 }
2609
2610 k_cnt = ((u16 *)MMU.ARM7_REG)[0x132>>1];
2611 if ( k_cnt & (1<<14))
2612 {
2613 //INFO("ARM7: KeyPad IRQ (pad 0x%04X, cnt 0x%04X (condition %s))\n", pad, k_cnt, k_cnt&(1<<15)?"AND":"OR");
2614 u16 k_cnt_selected = (k_cnt & 0x3F);
2615 if (k_cnt&(1<<15)) // AND
2616 {
2617 if ((~pad & k_cnt_selected) == k_cnt_selected) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_KEYPAD);
2618 }
2619 else // OR
2620 {
2621 if (~pad & k_cnt_selected) NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_KEYPAD);
2622 }
2623 }
2624
2625
2626 if(input.touch.isTouch)
2627 {
2628 u16 adc_x = NDS_getADCTouchPosX(input.touch.touchX);
2629 u16 adc_y = NDS_getADCTouchPosY(input.touch.touchY);
2630 nds.adc_touchX = adc_x;
2631 nds.adc_touchY = adc_y;
2632 nds.adc_jitterctr = 0;
2633
2634 nds.scr_touchX = input.touch.touchX;
2635 nds.scr_touchY = input.touch.touchY;
2636 nds.isTouch = 1;
2637 }
2638 else
2639 {
2640 nds.adc_touchX = 0;
2641 nds.adc_touchY = 0;
2642 nds.scr_touchX = 0;
2643 nds.scr_touchY = 0;
2644 nds.isTouch = 0;
2645 }
2646
2647 if (input.buttons.F && !countLid)
2648 {
2649 LidClosed = (!LidClosed) & 0x01;
2650 if (!LidClosed)
2651 {
2652 NDS_makeIrq(ARMCPU_ARM7,IRQ_BIT_ARM7_FOLD);
2653 }
2654
2655 countLid = 30;
2656 }
2657 else
2658 {
2659 if (countLid > 0)
2660 countLid--;
2661 }
2662
2663 u16 padExt = ((input.buttons.X ? 0 : 0x80) >> 7) |
2664 ((input.buttons.Y ? 0 : 0x80) >> 6) |
2665 ((input.buttons.G ? 0 : 0x80) >> 4) |
2666 ((LidClosed) << 7) |
2667 0x0034;
2668
2669 padExt = LOCAL_TO_LE_16(padExt);
2670 padExt |= (((u16 *)MMU.ARM7_REG)[0x136>>1] & 0x0070);
2671
2672 ((u16 *)MMU.ARM7_REG)[0x136>>1] = (u16)padExt;
2673
2674 //put into the format we want for the movie system
2675 //fRLDUTSBAYXWEg
2676 //we don't really need nds.pad anymore, but removing it would be a pain
2677
2678 nds.pad =
2679 ((input.buttons.R ? 1 : 0) << 12)|
2680 ((input.buttons.L ? 1 : 0) << 11)|
2681 ((input.buttons.D ? 1 : 0) << 10)|
2682 ((input.buttons.U ? 1 : 0) << 9)|
2683 ((input.buttons.T ? 1 : 0) << 8)|
2684 ((input.buttons.S ? 1 : 0) << 7)|
2685 ((input.buttons.B ? 1 : 0) << 6)|
2686 ((input.buttons.A ? 1 : 0) << 5)|
2687 ((input.buttons.Y ? 1 : 0) << 4)|
2688 ((input.buttons.X ? 1 : 0) << 3)|
2689 ((input.buttons.W ? 1 : 0) << 2)|
2690 ((input.buttons.E ? 1 : 0) << 1);
2691 }
2692
2693
NDS_beginProcessingInput()2694 void NDS_beginProcessingInput()
2695 {
2696 }
2697
NDS_endProcessingInput()2698 void NDS_endProcessingInput()
2699 {
2700 // transfer the processed input
2701 finalUserInput = rawUserInput;
2702
2703 // use the final input for a few things right away
2704 NDS_applyFinalInput();
2705 }
2706
NDS_suspendProcessingInput(bool suspend)2707 void NDS_suspendProcessingInput(bool suspend)
2708 {
2709 static int suspendCount = 0;
2710 if(suspend)
2711 suspendCount++;
2712 else if(suspendCount)
2713 suspendCount--;
2714 }
2715
NDS_swapScreen()2716 void NDS_swapScreen()
2717 {
2718 if (GPU->GetDisplayMain()->GetEngineID() == GPUEngineID_Main)
2719 {
2720 GPU->GetDisplayMain()->SetEngineByID(GPUEngineID_Sub);
2721 GPU->GetDisplayTouch()->SetEngineByID(GPUEngineID_Main);
2722 }
2723 else
2724 {
2725 GPU->GetDisplayMain()->SetEngineByID(GPUEngineID_Main);
2726 GPU->GetDisplayTouch()->SetEngineByID(GPUEngineID_Sub);
2727 }
2728 }
2729
2730 #if defined(LOG_ARM9) || defined(LOG_ARM7)
emu_halt()2731 void emu_halt()
2732 {
2733 //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);
2734 execute = false;
2735 #ifdef LOG_ARM9
2736 if (fp_dis9)
2737 {
2738 char buf[256] = { 0 };
2739 sprintf(buf, "halting emu: ARM9 PC=%08X/%08X\n", NDS_ARM9.R[15], NDS_ARM9.instruct_adr);
2740 fwrite(buf, 1, strlen(buf), fp_dis9);
2741 INFO("ARM9 halted\n");
2742 }
2743 #endif
2744
2745 #ifdef LOG_ARM7
2746 if (fp_dis7)
2747 {
2748 char buf[256] = { 0 };
2749 sprintf(buf, "halting emu: ARM7 PC=%08X/%08X\n", NDS_ARM7.R[15], NDS_ARM7.instruct_adr);
2750 fwrite(buf, 1, strlen(buf), fp_dis7);
2751 INFO("ARM7 halted\n");
2752 }
2753 #endif
2754 }
2755 #endif
2756
2757 //returns true if exmemcnt specifies satisfactory parameters for the device, which calls this function
ValidateSlot2Access(u32 procnum,u32 demandSRAMSpeed,u32 demand1stROMSpeed,u32 demand2ndROMSpeed,int clockbits)2758 bool ValidateSlot2Access(u32 procnum, u32 demandSRAMSpeed, u32 demand1stROMSpeed, u32 demand2ndROMSpeed, int clockbits)
2759 {
2760 static const u32 _sramSpeeds[] = {10,8,6,18};
2761 static const u32 _rom1Speeds[] = {10,8,6,18};
2762 static const u32 _rom2Speeds[] = {6,4};
2763 u16 exmemcnt = T1ReadWord(MMU.MMU_MEM[procnum][0x40], 0x204);
2764 u16 exmemcnt9 = T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x204);
2765 u32 arm7access = (exmemcnt9 & EXMEMCNT_MASK_SLOT2_ARM7);
2766 u32 sramSpeed = _sramSpeeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_SRAM_TIME)];
2767 u32 romSpeed1 = _rom1Speeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_ROM_1ST_TIME)>>2];
2768 u32 romSpeed2 = _rom2Speeds[(exmemcnt & EXMEMCNT_MASK_SLOT2_ROM_2ND_TIME)>>4];
2769 u32 curclockbits = (exmemcnt & EXMEMCNT_MASK_SLOT2_CLOCKRATE)>>5;
2770
2771 if(procnum==ARMCPU_ARM9 && arm7access) return false;
2772 if(procnum==ARMCPU_ARM7 && !arm7access) return false;
2773
2774 //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.
2775 //i'm not sure if this gives us enough flexibility, but it is good enough for now.
2776 //should make the arguments to this function bitmasks later if we need better.
2777 if(sramSpeed < demandSRAMSpeed) return false;
2778 if(romSpeed1 < demand1stROMSpeed) return false;
2779 if(romSpeed2 < demand2ndROMSpeed) return false;
2780
2781 if(clockbits != -1 && clockbits != (int)curclockbits) return false;
2782
2783 return true;
2784 }
2785