1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 //////////////////////////////////////////////////////////////////////
24 // Simple ARM7 stub (sends RTC, TSC, and X/Y data to the ARM 9)
25 // -- joat
26 // -- modified by Darkain and others
27 //////////////////////////////////////////////////////////////////////
28 
29 // #define USE_LIBCARTRESET
30 
31 #include <nds.h>
32 
33 #include <bios.h>
34 #include <arm7/touch.h>
35 #include <arm7/clock.h>
36 #include <arm7/audio.h>
37 #include <system.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <registers_alt.h> // Needed for SOUND_CR
41 #include <NDS/scummvm_ipc.h>
42 //////////////////////////////////////////////////////////////////////
43 #ifdef USE_DEBUGGER
44 #include <dswifi7.h>
45 #endif
46 
47 #include "cartreset_nolibfat.h"
48 
49 #define TOUCH_CAL_X1 (*(vs16 *)0x027FFCD8)
50 #define TOUCH_CAL_Y1 (*(vs16 *)0x027FFCDA)
51 #define TOUCH_CAL_X2 (*(vs16 *)0x027FFCDE)
52 #define TOUCH_CAL_Y2 (*(vs16 *)0x027FFCE0)
53 #define SCREEN_WIDTH    256
54 #define SCREEN_HEIGHT   192
55 s32 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1;
56 s32 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
57 s32 TOUCH_OFFSET_X = ( ((SCREEN_WIDTH  - 60) * TOUCH_CAL_X1) / TOUCH_WIDTH  ) - 28;
58 s32 TOUCH_OFFSET_Y = ( ((SCREEN_HEIGHT - 60) * TOUCH_CAL_Y1) / TOUCH_HEIGHT ) - 28;
59 
60 vu8 *soundData;
61 
62 vu8 *soundBuffer;
63 vu8 *arm9Buffer;
64 bool soundFilled[4];
65 
66 int playingSection;
67 
68 bool needSleep = false;
69 int temp;
70 
71 int adpcmBufferNum = 0;
72 
73 // those are pixel positions of the two points you click when calibrating
74 #define TOUCH_CNTRL_X1 (*(vu8 *)0x027FFCDC)
75 #define TOUCH_CNTRL_Y1 (*(vu8 *)0x027FFCDD)
76 #define TOUCH_CNTRL_X2 (*(vu8 *)0x027FFCE2)
77 #define TOUCH_CNTRL_Y2 (*(vu8 *)0x027FFCE3)
78 
79 /*
80 void startSound(int sampleRate, const void *data, uint32 bytes, u8 channel = 0, u8 vol = 0x7F, u8 pan = 63, u8 format = 0) {
81 	SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate);
82 	SCHANNEL_SOURCE(channel) = (uint32)data;
83 	SCHANNEL_LENGTH(channel) = bytes;
84 	SCHANNEL_CR(channel)     = SOUND_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(vol) | SOUND_PAN(pan) | (format==1?SOUND_8BIT:SOUND_16BIT);
85 }
86 
87 s8 getFreeSoundChannel() {
88 	for (int i = 0; i < 16; i++) {
89 		if ( (SCHANNEL_CR(i) & SOUND_ENABLE) == 0 )
90 			return i;
91 	}
92 	return -1;
93 }
94 */
95 
getFreeSoundChannel()96 s8 getFreeSoundChannel() {
97 	// return 0;
98 	for (int i = 0; i < 16; i++) {
99 		if ( (SCHANNEL_CR(i) & SCHANNEL_ENABLE) == 0 )
100 			return i;
101 	}
102 	return -1;
103 }
104 
startSound(int sampleRate,const void * data,uint32 bytes,u8 channel=0,u8 vol=0x7F,u8 pan=63,u8 format=0)105 void startSound(int sampleRate, const void *data, uint32 bytes, u8 channel = 0, u8 vol = 0x7F, u8 pan = 63, u8 format = 0) {
106 	// REG_IME = IME_DISABLE;
107 
108 	channel = getFreeSoundChannel();
109 	/*
110 	if (format == 2) {
111 		channel = 1;
112 	} else {
113 		channel = 0;
114 	}
115 	*/
116 
117 	if (channel > 1)
118 		channel = 1;
119 
120 	bytes &= ~7; // Multiple of 4 bytes!
121 	// bytes += 4;
122 
123 	SCHANNEL_CR(channel) = 0;
124 	SCHANNEL_TIMER(channel)  = SOUND_FREQ(sampleRate);
125 	SCHANNEL_SOURCE(channel) = (uint32)data;
126 	SCHANNEL_LENGTH(channel) = (bytes & 0x7FFFFFFF) >> 2;
127 	SCHANNEL_REPEAT_POINT(channel) = 0;
128 
129 	SCHANNEL_CR(channel + 2) = 0;
130 	SCHANNEL_TIMER(channel + 2)  = SOUND_FREQ(sampleRate);
131 	SCHANNEL_SOURCE(channel + 2) = (uint32)data;
132 	SCHANNEL_LENGTH(channel + 2) = (bytes & 0x7FFFFFFF) >> 2;
133 	SCHANNEL_REPEAT_POINT(channel + 2) = 0;
134 
135 	uint32 flags = SCHANNEL_ENABLE | SOUND_VOL(vol) | SOUND_PAN(pan);
136 
137 	switch (format) {
138 	case 1: {
139 		flags |= SOUND_FORMAT_8BIT;
140 		flags |= SOUND_REPEAT; // | (1 << 15);
141 		break;
142 	}
143 
144 	case 0: {
145 		flags |= SOUND_FORMAT_16BIT;
146 		flags |= SOUND_REPEAT; // | (1 << 15);
147 		break;
148 	}
149 
150 	case 2: {
151 		flags |= SOUND_FORMAT_ADPCM;
152 		flags |= SOUND_ONE_SHOT; // | (1 << 15);
153 
154 		SCHANNEL_SOURCE(channel) = (unsigned int)IPC->adpcm.buffer[0];
155 		// bytes += 32;
156 		SCHANNEL_LENGTH(channel) = ((bytes + 4) & 0x7FFFFFFF) >> 2;
157 
158 		SCHANNEL_CR(channel + 1) = 0;
159 		SCHANNEL_SOURCE(channel + 1) = (unsigned int)IPC->adpcm.buffer[0];
160 		SCHANNEL_LENGTH(channel + 1) = ((bytes + 4) & 0x7FFFFFFF) >> 2;
161 		SCHANNEL_TIMER(channel + 1)  = SOUND_FREQ(sampleRate);
162 		SCHANNEL_REPEAT_POINT(channel + 1) = 0;
163 		SCHANNEL_CR(channel + 1) = flags;
164 		temp = bytes;
165 		adpcmBufferNum = 0;
166 		break;
167 	}
168 	}
169 
170 	/*
171 	if (bytes & 0x80000000) {
172 		flags |= SOUND_REPEAT;
173 	} else {
174 	}
175 	*/
176 
177 	soundData = (vu8 *)data;
178 
179 	SCHANNEL_CR(channel)     = flags;
180 	SCHANNEL_CR(channel + 2) = flags;
181 
182 	if (channel == 0) {
183 		for (volatile int i = 0; i < 16384 * 2; i++) {
184 			// Delay loop - this makes everything stay in sync!
185 		}
186 
187 		TIMER0_CR = 0;
188 		TIMER0_DATA = SOUND_FREQ(sampleRate) * 2;
189 		TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1;
190 
191 		TIMER1_CR = 0;
192 		TIMER1_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
193 		TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
194 
195 		playingSection = 0;
196 	} else {
197 		for (volatile int i = 0; i < 16384 * 2; i++) {
198 			// Delay loop - this makes everything stay in sync!
199 		}
200 
201 		TIMER2_CR = 0;
202 		TIMER2_DATA = SOUND_FREQ(sampleRate) * 2;
203 		TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1;
204 
205 		TIMER3_CR = 0;
206 		TIMER3_DATA = 65536 - ((bytes & 0x7FFFFFFF) >> 3); // Trigger four times during the length of the buffer
207 		TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
208 
209 		for (int r = 0; r < 4; r++) {
210 			// IPC->streamFillNeeded[r] = true;
211 		}
212 
213 		IPC->streamPlayingSection = 0;
214 	}
215 
216 	// IPC->fillSoundFirstHalf = true;
217 	// IPC->fillSoundSecondHalf = true;
218 	// soundFirstHalf = true;
219 
220 	// REG_IME = IME_ENABLE;
221 }
222 
stopSound(int chan)223 void stopSound(int chan) {
224 	SCHANNEL_CR(chan) = 0;
225 }
226 
DummyHandler()227 void DummyHandler() {
228 	REG_IF = REG_IF;
229 }
230 
powerManagerWrite(uint32 command,u32 data,bool enable)231 void powerManagerWrite(uint32 command, u32 data, bool enable) {
232 	uint16 result;
233 	SerialWaitBusy();
234 
235 	// Write the command and wait for it to complete
236 	REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
237 	REG_SPIDATA = command | 0x80;
238 	SerialWaitBusy();
239 
240 	// Write the second command and clock in the data
241 	REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
242 	REG_SPIDATA = 0;
243 	SerialWaitBusy();
244 
245 	result = REG_SPIDATA & 0xFF;
246 
247 	// Write the command and wait for it to complete
248 	REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz | (1 << 11);
249 	REG_SPIDATA = command;
250 	SerialWaitBusy();
251 
252 	// Write the second command and clock in the data
253 	REG_SPICNT = SPI_ENABLE | SPI_BAUD_1MHz;
254 	REG_SPIDATA = enable ? (result | data) : (result & ~data);
255 	SerialWaitBusy();
256 }
257 
258 /*
259 void performSleep() {
260 	powerManagerWrite(0, 0x30, true);
261 
262 	// Here, I set up a dummy interrupt handler, then trigger all interrupts.
263 	// These are just aknowledged by the handler without doing anything else.
264 	// Why?  Because without it the sleep mode will only happen once, and then
265 	// never again.  I got the idea from reading the MoonShell source.
266 	IME = 0;
267 	u32 irq = (u32)IRQ_HANDLER;
268 	IRQ_HANDLER = DummyHandler;
269 	IF = ~0;
270 	IME = 1;
271 
272 	// Now save which interrupts are enabled, then set only the screens unfolding
273 	// interrupt to be enabled, so that the first interrupt that happens is the
274 	// one I want.
275 	int saveInts = IE;
276 
277 	IE = IRQ_TIMER0; // Screens unfolding interrupt
278 
279 	// Now call the sleep function in the bios
280 	bool b;
281 	do {
282 		TIMER0_CR = 0;
283 		TIMER0_DATA = TIMER_FREQ(20);
284 		TIMER0_CR = TIMER_ENABLE | TIMER_DIV_64;
285 
286 		swiDelay(100);
287 
288 		swiSleep();
289 
290 		swiDelay(100);
291 
292 		powerManagerWrite(0, 0x30, b = !b);
293 	} while (!(TIMER0_CR & TIMER_ENABLE));
294 
295 	TIMER0_CR = 0;
296 
297 	// We're back from sleep, now restore the interrupt state and IRQ handler
298 	IRQ_HANDLER = (void (*)())irq;
299 	IE = saveInts;
300 	IF = ~0;
301 	IME = 1;
302 
303 	powerManagerWrite(0, 0x30, false);
304 }
305 */
306 
performSleep()307 void performSleep() {
308 	powerManagerWrite(0, 0x30, true);
309 
310 	IPC->performArm9SleepMode = true; // Tell ARM9 to sleep
311 
312 	// u32 irq = (u32)IRQ_HANDLER;
313 	// IRQ_HANDLER = DummyHandler;
314 	// POWER_CR &= ~POWER_SOUND;
315 
316 	// int saveInts = REG_IE;
317 	// REG_IE = (1 << 22) | IRQ_VBLANK; // Lid open
318 	// *((u32 *)(0x0380FFF8)) = *((u32 *)(0x0380FFF8)) | (REG_IE & REG_IF);
319 	// VBLANK_INTR_WAIT_FLAGS = IRQ_VBLANK;
320 
321 	int r = 0;
322 	while ((REG_KEYXY & (1 << 7))) { // Wait for lid to open
323 		swiDelay(1000000);
324 		r++;
325 	}
326 
327 	// IRQ_HANDLER = (void (*)())irq;
328 	IPC->performArm9SleepMode = false; // Tell ARM9 to wake up
329 	// REG_IE = saveInts;
330 
331 	// POWER_CR |= POWER_SOUND;
332 
333 	powerManagerWrite(0, 0x30, false);
334 }
335 
powerOff()336 void powerOff() {
337 	powerManagerWrite(0, 0x40, true);
338 }
339 
InterruptTimer1()340 void InterruptTimer1() {
341 	IPC->fillNeeded[playingSection] = true;
342 	soundFilled[playingSection] = false;
343 
344 	if (playingSection == 3) {
345 		// IME = IME_DISABLED;
346 
347 		// while (SCHANNEL_CR(0) & SCHANNEL_ENABLE) {
348 		// }
349 		// SCHANNEL_CR(0) &= ~SCHANNEL_ENABLE;
350 
351 		// SCHANNEL_CR(0) |= SCHANNEL_ENABLE;
352 		// TIMER1_CR = 0;
353 		// TIMER1_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;
354 
355 		playingSection = 0;
356 
357 		// IME = IME_ENABLED;
358 	} else {
359 		playingSection++;
360 	}
361 
362 	IPC->playingSection = playingSection;
363 
364 /*	for (int r = 0; r < 4; r++) {
365 		//if ((!soundFilled[r]) && (!IPC->fillNeeded[playingSection])) {
366 			memcpy((void *) (soundBuffer + (r * 1024)), (void *) (arm9Buffer + (r * 1024)), 1024);
367 
368 			vu16 *p = (vu16 *) (soundBuffer);
369 			//for (int t = 0; t < 2048; t++) {
370 		//		*(p + t) = (t & 1)? 0xF000: 0x0000;
371 			//}
372 			soundFilled[r] = true;
373 		//}
374 	}*/
375 }
376 
InterruptTimer3()377 void InterruptTimer3() {
378 	while (IPC->adpcm.semaphore); // Wait for buffer to become free if needed
379 	IPC->adpcm.semaphore = true; // Lock the buffer structure to prevent clashing with the ARM7
380 
381 	IPC->streamFillNeeded[IPC->streamPlayingSection] = true;
382 
383 	if (IPC->streamPlayingSection == 3) {
384 		IPC->streamPlayingSection = 0;
385 	} else {
386 		IPC->streamPlayingSection++;
387 	}
388 
389 	IPC->adpcm.semaphore = false;
390 }
391 
392 // IPC->performArm9SleepMode = false;
393 
394 // precalculate some values
395 // static int16 TOUCH_WIDTH  = TOUCH_CAL_X2 - TOUCH_CAL_X1;
396 // static int16 TOUCH_HEIGHT = TOUCH_CAL_Y2 - TOUCH_CAL_Y1;
397 // static int16 CNTRL_WIDTH  = TOUCH_CNTRL_X2 - (TOUCH_CNTRL_X1 - 8);
398 // static int16 CNTRL_HEIGHT = TOUCH_CNTRL_Y2 - (TOUCH_CNTRL_Y1 - 8);
399 
InterruptVBlank()400  void InterruptVBlank() {
401 	uint16 but = 0, x = 0, y = 0, xpx = 0, ypx = 0, z1 = 0, z2 = 0, batt = 0, aux = 0;
402 	int t1 = 0, t2 = 0;
403 	uint32 temp = 0;
404 	uint8 ct[sizeof(IPC->curtime)];
405 	static int heartbeat = 0;
406 	// Update the heartbeat
407 	heartbeat++;
408 
409 	// Read the X/Y buttons and the /PENIRQ line
410 	but = REG_KEYXY;
411 	if (!(but & 0x40)) {
412 		// Read the touch screen
413 		touchPosition p;
414 		touchReadXY(&p);
415 
416 		// x = touchRead(TSC_MEASURE_X);
417 		// y = touchRead(TSC_MEASURE_Y);
418 
419 		x = p.rawx;
420 		y = p.rawy;
421 
422 		// xpx = p.px;
423 		// ypx = p.py;
424 
425 		xpx = ( ((SCREEN_WIDTH  - 60) * x) / TOUCH_WIDTH  ) - TOUCH_OFFSET_X;
426 		ypx = ( ((SCREEN_HEIGHT - 60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y;
427 
428 		// xpx = (IPC->touchX - (int16) TOUCH_CAL_X1) * CNTRL_WIDTH  / TOUCH_WIDTH  + (int16) (TOUCH_CNTRL_X1 - 8);
429 		// ypx = (IPC->touchY - (int16) TOUCH_CAL_Y1) * CNTRL_HEIGHT / TOUCH_HEIGHT + (int16) (TOUCH_CNTRL_Y1 - 8);
430 
431 		z1 = touchRead(TSC_MEASURE_Z1);
432 		z2 = touchRead(TSC_MEASURE_Z2);
433 	}
434 
435 	// Check if screen is folded
436 	if (but & (1 << 7)) {
437 		needSleep = true;
438 	}
439 
440 	batt = touchRead(TSC_MEASURE_BATTERY);
441 	aux  = touchRead(TSC_MEASURE_AUX);
442 
443 	// Read the time
444 	rtcGetTime((uint8 *)ct);
445 	BCDToInteger((uint8 *)&(ct[1]), 7);
446 
447 	// Read the temperature
448 	temp = touchReadTemperature(&t1, &t2);
449 
450 	// Update the IPC struct
451 	IPC->heartbeat = heartbeat;
452 	IPC->buttons   = but;
453 	IPC->touchX    = x;
454 	IPC->touchY    = y;
455 	IPC->touchXpx  = xpx;
456 	IPC->touchYpx  = ypx;
457 	IPC->touchZ1   = z1;
458 	IPC->touchZ2   = z2;
459 	IPC->battery   = batt;
460 	IPC->aux       = aux;
461 
462 	for (u32 i = 0; i < sizeof(ct); i++) {
463 		IPC->curtime[i] = ct[i];
464 	}
465 
466 	IPC->temperature = temp;
467 	IPC->tdiode1 = t1;
468 	IPC->tdiode2 = t2;
469 
470 	// sound code  :)
471 	TransferSound *snd = IPC->soundData;
472 	IPC->soundData = 0;
473 	if (snd) {
474 		for (int i = 0; i < snd->count; i++) {
475 			s8 chan = getFreeSoundChannel();
476 			if (snd->data[i].rate > 0) {
477 				if (chan >= 0) {
478 					startSound(snd->data[i].rate, snd->data[i].data, snd->data[i].len, chan, snd->data[i].vol, snd->data[i].pan, snd->data[i].format);
479 				}
480 			} else {
481 				stopSound(-snd->data[i].rate);
482 			}
483 		}
484 	}
485 
486 #ifdef USE_DEBUGGER
487 	Wifi_Update(); // update wireless in vblank
488 #endif
489 }
490 
491 #ifdef USE_DEBUGGER
492 // callback to allow wifi library to notify arm9
arm7_synctoarm9()493 void arm7_synctoarm9() { // send fifo message
494 	REG_IPC_FIFO_TX = 0x87654321;
495 }
496 
497 // interrupt handler to allow incoming notifications from arm9
arm7_fifo()498 void arm7_fifo() { // check incoming fifo messages
499 	u32 msg = REG_IPC_FIFO_RX;
500 	if (msg == 0x87654321)
501 		Wifi_Sync();
502 }
503 
initDebugger()504 void initDebugger() {
505 	// set up the wifi irq
506 	irqSet(IRQ_WIFI, Wifi_Interrupt); // set up wifi interrupt
507 	irqEnable(IRQ_WIFI);
508 
509 	// get them talking together
510 
511 	// sync with arm9 and init wifi
512 	u32 fifo_temp;
513 
514 	while (1) { // wait for magic number
515 		while (REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY)
516 			swiWaitForVBlank();
517 
518 		fifo_temp = REG_IPC_FIFO_RX;
519 
520 		if (fifo_temp == 0x12345678)
521 			break;
522 	}
523 
524 	while (REG_IPC_FIFO_CR & IPC_FIFO_RECV_EMPTY)
525 		swiWaitForVBlank();
526 
527 	fifo_temp = REG_IPC_FIFO_RX; // give next value to wifi_init
528 	Wifi_Init(fifo_temp);
529 
530 	irqSet(IRQ_FIFO_NOT_EMPTY,arm7_fifo); // set up fifo irq
531 	irqEnable(IRQ_FIFO_NOT_EMPTY);
532 	REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_RECV_IRQ;
533 
534 	Wifi_SetSyncHandler(arm7_synctoarm9); // allow wifi lib to notify arm9
535 	// arm7 wifi init complete
536 }
537 #endif
538 
539 #ifdef USE_LIBCARTRESET
reboot()540 void reboot() {
541 	cartExecute();
542 }
543 #endif
544 
main(int argc,char ** argv)545 int main(int argc, char ** argv) {
546 #ifdef USE_DEBUGGER
547 	REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR;
548 #endif
549 
550 	// Reset the clock if needed
551 	rtcReset();
552 
553 	// enable sound
554 	// powerOn(POWER_SOUND);
555 	SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F);
556 	IPC->soundData = 0;
557 	IPC->reset = false;
558 
559 	// fifoInit();
560 
561 	for (int r = 0; r < 8; r++) {
562 		IPC->adpcm.arm7Buffer[r] = (u8 *)malloc(512);
563 	}
564 
565 	for (int r = 0; r < 4; r++) {
566 		soundFilled[r] = false;
567 	}
568 
569 	// Set up the interrupt handler
570 
571 	irqInit();
572 
573 	irqSet(IRQ_VBLANK, InterruptVBlank);
574 	irqEnable(IRQ_VBLANK);
575 
576 	irqSet(IRQ_TIMER1, InterruptTimer1);
577 	irqEnable(IRQ_TIMER1);
578 
579 	irqSet(IRQ_TIMER3, InterruptTimer3);
580 	irqEnable(IRQ_TIMER3);
581 
582 	/*
583 	REG_IME = 0;
584 	IRQ_HANDLER = &InterruptHandler;
585 	REG_IE = IRQ_VBLANK | IRQ_TIMER1 | IRQ_TIMER3;
586 	REG_IF = ~0;
587 	DISP_SR = DISP_VBLANK_IRQ;
588 	REG_IME = 1;
589 	*/
590 
591 #ifdef USE_DEBUGGER
592 	initDebugger();
593 #endif
594 
595 	// Keep the ARM7 out of main RAM
596 	while ((1)) {
597 		if (needSleep) {
598 			performSleep();
599 			needSleep = false;
600 		}
601 
602 #ifdef USE_LIBCARTRESET
603 		if (passmeloopQuery()) {
604 			reboot();
605 		}
606 #endif
607 
608 		if (IPC->reset) {
609 			powerOff();
610 		}
611 
612 		swiWaitForVBlank();
613 	}
614 
615 	return 0;
616 }
617