340b0be4fd383ff00c32687d889fd8f76b891463
[gpsp.git] / main.c
1 /* gameplaySP
2  *
3  * Copyright (C) 2006 Exophase <exophase@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program 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 GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19
20 #include "common.h"
21
22 #ifdef PSP_BUILD
23
24 //PSP_MODULE_INFO("gpSP", 0x1000, 0, 6);
25 //PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER);
26
27 void vblank_interrupt_handler(u32 sub, u32 *parg);
28
29 #endif
30
31 timer_type timer[4];
32
33 //debug_state current_debug_state = COUNTDOWN_BREAKPOINT;
34 //debug_state current_debug_state = PC_BREAKPOINT;
35 u32 breakpoint_value = 0x7c5000;
36 debug_state current_debug_state = RUN;
37 //debug_state current_debug_state = STEP_RUN;
38
39 //u32 breakpoint_value = 0;
40
41 frameskip_type current_frameskip_type = auto_frameskip;
42 u32 global_cycles_per_instruction = 1;
43 u32 random_skip = 0;
44 u32 fps_debug = 0;
45
46 u32 frameskip_value = 2;
47
48 u64 last_frame_interval_timestamp;
49
50 u32 skip_next_frame = 0;
51
52 u32 frameskip_counter = 0;
53
54 u32 cpu_ticks = 0;
55 u32 frame_ticks = 0;
56
57 u32 execute_cycles = 960;
58 s32 video_count = 960;
59 u32 ticks;
60
61 u32 arm_frame = 0;
62 u32 thumb_frame = 0;
63 u32 last_frame = 0;
64
65 u32 cycle_memory_access = 0;
66 u32 cycle_pc_relative_access = 0;
67 u32 cycle_sp_relative_access = 0;
68 u32 cycle_block_memory_access = 0;
69 u32 cycle_block_memory_sp_access = 0;
70 u32 cycle_block_memory_words = 0;
71 u32 cycle_dma16_words = 0;
72 u32 cycle_dma32_words = 0;
73 u32 flush_ram_count = 0;
74 u32 gbc_update_count = 0;
75 u32 oam_update_count = 0;
76
77 u32 synchronize_flag = 1;
78
79 u32 update_backup_flag = 1;
80 #ifdef GP2X_BUILD
81 u32 clock_speed = 200;
82 #else
83 u32 clock_speed = 333;
84 #endif
85 char main_path[512];
86
87 void trigger_ext_event();
88
89 #define check_count(count_var)                                                \
90   if(count_var < execute_cycles)                                              \
91     execute_cycles = count_var;                                               \
92
93 #define check_timer(timer_number)                                             \
94   if(timer[timer_number].status == TIMER_PRESCALE)                            \
95     check_count(timer[timer_number].count);                                   \
96
97 #define update_timer(timer_number)                                            \
98   if(timer[timer_number].status != TIMER_INACTIVE)                            \
99   {                                                                           \
100     if(timer[timer_number].status != TIMER_CASCADE)                           \
101     {                                                                         \
102       timer[timer_number].count -= execute_cycles;                            \
103       io_registers[REG_TM##timer_number##D] =                                 \
104        -(timer[timer_number].count >> timer[timer_number].prescale);          \
105     }                                                                         \
106                                                                               \
107     if(timer[timer_number].count <= 0)                                        \
108     {                                                                         \
109       if(timer[timer_number].irq == TIMER_TRIGGER_IRQ)                        \
110         irq_raised |= IRQ_TIMER##timer_number;                                \
111                                                                               \
112       if((timer_number != 3) &&                                               \
113        (timer[timer_number + 1].status == TIMER_CASCADE))                     \
114       {                                                                       \
115         timer[timer_number + 1].count--;                                      \
116         io_registers[REG_TM0D + (timer_number + 1) * 2] =                     \
117          -(timer[timer_number + 1].count);                                    \
118       }                                                                       \
119                                                                               \
120       if(timer_number < 2)                                                    \
121       {                                                                       \
122         if(timer[timer_number].direct_sound_channels & 0x01)                  \
123           sound_timer(timer[timer_number].frequency_step, 0);                 \
124                                                                               \
125         if(timer[timer_number].direct_sound_channels & 0x02)                  \
126           sound_timer(timer[timer_number].frequency_step, 1);                 \
127       }                                                                       \
128                                                                               \
129       timer[timer_number].count +=                                            \
130        (timer[timer_number].reload << timer[timer_number].prescale);          \
131     }                                                                         \
132   }                                                                           \
133
134 static const char *file_ext[] = { ".gba", ".bin", ".zip", NULL };
135
136 #ifdef ARM_ARCH
137 void ChangeWorkingDirectory(char *exe)
138 {
139 #ifndef _WIN32_WCE
140   char *s = strrchr(exe, '/');
141   if (s != NULL) {
142     *s = '\0';
143     chdir(exe);
144     *s = '/';
145   }
146 #endif
147 }
148 #endif
149
150 void init_main()
151 {
152   u32 i;
153
154   skip_next_frame = 0;
155
156   for(i = 0; i < 4; i++)
157   {
158     dma[i].start_type = DMA_INACTIVE;
159     dma[i].direct_sound_channel = DMA_NO_DIRECT_SOUND;
160     timer[i].status = TIMER_INACTIVE;
161     timer[i].reload = 0x10000;
162     timer[i].stop_cpu_ticks = 0;
163   }
164
165   timer[0].direct_sound_channels = TIMER_DS_CHANNEL_BOTH;
166   timer[1].direct_sound_channels = TIMER_DS_CHANNEL_NONE;
167
168   cpu_ticks = 0;
169   frame_ticks = 0;
170
171   execute_cycles = 960;
172   video_count = 960;
173
174   flush_translation_cache_rom();
175   flush_translation_cache_ram();
176   flush_translation_cache_bios();
177 }
178
179 int main(int argc, char *argv[])
180 {
181 #ifdef PSP_BUILD
182   sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0,
183    vblank_interrupt_handler, NULL);
184   sceKernelEnableSubIntr(PSP_VBLANK_INT, 0);
185 #endif
186
187   init_gamepak_buffer();
188
189   // Copy the directory path of the executable into main_path
190
191 #ifdef ARM_ARCH
192   // ChangeWorkingDirectory will null out the filename out of the path
193   ChangeWorkingDirectory(argv[0]);
194 #endif
195
196   getcwd(main_path, 512);
197
198 #ifdef PSP_BUILD
199   delay_us(2500000);
200 #endif
201
202 #ifndef PC_BUILD
203   gpsp_plat_init();
204 #endif
205   load_config_file();
206
207   gamepak_filename[0] = 0;
208
209   init_video();
210
211 #ifdef GP2X_BUILD
212   char bios_filename[512];
213   sprintf(bios_filename, "%s/%s", main_path, "gba_bios.bin");
214   if(load_bios(bios_filename) == -1)
215 #else
216   if(load_bios("gba_bios.bin") == -1)
217 #endif
218   {
219     gui_action_type gui_action = CURSOR_NONE;
220
221     debug_screen_start();
222     debug_screen_printl("Sorry, but gpSP requires a Gameboy Advance BIOS   ");
223     debug_screen_printl("image to run correctly. Make sure to get an       ");
224     debug_screen_printl("authentic one, it'll be exactly 16384 bytes large ");
225     debug_screen_printl("and should have the following md5sum value:       ");
226     debug_screen_printl("                                                  ");
227     debug_screen_printl("a860e8c0b6d573d191e4ec7db1b1e4f6                  ");
228     debug_screen_printl("                                                  ");
229     debug_screen_printl("When you do get it name it gba_bios.bin and put it");
230 #ifdef PND_BUILD
231     debug_screen_printl("in <CD card>/pandora/appdata/gpsp/ .              ");
232 #else
233     debug_screen_printl("in the same directory as gpSP.                    ");
234 #endif
235     debug_screen_printl("                                                  ");
236     debug_screen_printl("Press any button to exit.                         ");
237
238     debug_screen_update();
239
240     while(gui_action == CURSOR_NONE)
241     {
242       gui_action = get_gui_input();
243       delay_us(15000);
244     }
245
246     debug_screen_end();
247
248     quit();
249   }
250
251   if(bios_rom[0] != 0x18)
252   {
253     gui_action_type gui_action = CURSOR_NONE;
254
255     debug_screen_start();
256     debug_screen_printl("You have an incorrect BIOS image.                 ");
257     debug_screen_printl("While many games will work fine, some will not. It");
258     debug_screen_printl("is strongly recommended that you obtain the       ");
259     debug_screen_printl("correct BIOS file. Do NOT report any bugs if you  ");
260     debug_screen_printl("are seeing this message.                          ");
261     debug_screen_printl("                                                  ");
262     debug_screen_printl("Press any button to resume, at your own risk.     ");
263
264     debug_screen_update();
265
266     while(gui_action == CURSOR_NONE)
267     {
268       gui_action = get_gui_input();
269       delay_us(15000);
270     }
271
272     debug_screen_end();
273   }
274
275   init_main();
276   init_sound();
277
278   init_input();
279
280   video_resolution_large();
281
282   if(argc > 1)
283   {
284     if(load_gamepak(argv[1]) == -1)
285     {
286 #ifndef PSP_BUILD
287       printf("Failed to load gamepak %s, exiting.\n", argv[1]);
288 #endif
289       exit(-1);
290     }
291
292     set_gba_resolution(screen_scale);
293     video_resolution_small();
294
295     init_cpu();
296     init_memory();
297   }
298   else
299   {
300     char load_filename[512];
301     if(load_file(file_ext, load_filename) == -1)
302     {
303       menu(copy_screen());
304     }
305     else
306     {
307       if(load_gamepak(load_filename) == -1)
308       {
309 #ifndef PSP_BUILD
310         printf("Failed to load gamepak %s, exiting.\n", load_filename);
311 #endif
312         exit(-1);
313       }
314
315       set_clock_speed();
316       set_gba_resolution(screen_scale);
317       video_resolution_small();
318
319       init_cpu();
320       init_memory();
321     }
322   }
323
324   last_frame = 0;
325
326   // We'll never actually return from here.
327
328 #ifdef PSP_BUILD
329   execute_arm_translate(execute_cycles);
330 #else
331
332 /*  u8 current_savestate_filename[512];
333   get_savestate_filename_noshot(savestate_slot,
334    current_savestate_filename);
335   load_state(current_savestate_filename); */
336
337 //  debug_on();
338
339   if(argc > 2)
340   {
341     current_debug_state = COUNTDOWN_BREAKPOINT;
342     breakpoint_value = strtol(argv[2], NULL, 16);
343   }
344
345   trigger_ext_event();
346
347   execute_arm_translate(execute_cycles);
348   execute_arm(execute_cycles);
349 #endif
350   return 0;
351 }
352
353 void print_memory_stats(u32 *counter, u32 *region_stats, char *stats_str)
354 {
355   u32 other_region_counter = region_stats[0x1] + region_stats[0xE] +
356    region_stats[0xF];
357   u32 rom_region_counter = region_stats[0x8] + region_stats[0x9] +
358    region_stats[0xA] + region_stats[0xB] + region_stats[0xC] +
359    region_stats[0xD];
360   u32 _counter = *counter;
361
362   printf("memory access stats: %s (out of %d)\n", stats_str, _counter);
363   printf("bios: %f%%\tiwram: %f%%\tewram: %f%%\tvram: %f\n",
364    region_stats[0x0] * 100.0 / _counter, region_stats[0x3] * 100.0 /
365    _counter,
366    region_stats[0x2] * 100.0 / _counter, region_stats[0x6] * 100.0 /
367    _counter);
368
369   printf("oam: %f%%\tpalette: %f%%\trom: %f%%\tother: %f%%\n",
370    region_stats[0x7] * 100.0 / _counter, region_stats[0x5] * 100.0 /
371    _counter,
372    rom_region_counter * 100.0 / _counter, other_region_counter * 100.0 /
373    _counter);
374
375   *counter = 0;
376   memset(region_stats, 0, sizeof(u32) * 16);
377 }
378
379 u32 event_cycles = 0;
380 const u32 event_cycles_trigger = 60 * 5;
381 u32 no_alpha = 0;
382
383 void trigger_ext_event()
384 {
385   static u32 event_number = 0;
386   static u64 benchmark_ticks[16];
387   u64 new_ticks;
388   char current_savestate_filename[512];
389
390   return;
391
392   if(event_number)
393   {
394     get_ticks_us(&new_ticks);
395     benchmark_ticks[event_number - 1] =
396      new_ticks - benchmark_ticks[event_number - 1];
397   }
398
399   current_frameskip_type = no_frameskip;
400   no_alpha = 0;
401   synchronize_flag = 0;
402
403   get_savestate_filename_noshot(savestate_slot,
404    current_savestate_filename);
405   load_state(current_savestate_filename);
406
407   switch(event_number)
408   {
409     case 0:
410       // Full benchmark, run normally
411       break;
412
413     case 1:
414       // No alpha blending
415       no_alpha = 1;
416       break;
417
418     case 2:
419       // No video benchmark
420       // Set frameskip really high + manual
421       current_frameskip_type = manual_frameskip;
422       frameskip_value = 1000000;
423       break;
424
425     case 3:
426       // No CPU benchmark
427       // Put CPU in halt mode, put it in IRQ mode with interrupts off
428       reg[CPU_HALT_STATE] = CPU_HALT;
429       reg[REG_CPSR] = 0xD2;
430       break;
431
432     case 4:
433       // No CPU or video benchmark
434       reg[CPU_HALT_STATE] = CPU_HALT;
435       reg[REG_CPSR] = 0xD2;
436       current_frameskip_type = manual_frameskip;
437       frameskip_value = 1000000;
438       break;
439
440     case 5:
441     {
442       // Done
443       char *print_strings[] =
444       {
445         "Full test   ",
446         "No blending ",
447         "No video    ",
448         "No CPU      ",
449         "No CPU/video",
450         "CPU speed   ",
451         "Video speed ",
452         "Alpha cost  "
453       };
454       u32 i;
455
456       benchmark_ticks[6] = benchmark_ticks[0] - benchmark_ticks[2];
457       benchmark_ticks[5] = benchmark_ticks[0] - benchmark_ticks[4] -
458        benchmark_ticks[6];
459       benchmark_ticks[7] = benchmark_ticks[0] - benchmark_ticks[1];
460
461       printf("Benchmark results (%d frames): \n", event_cycles_trigger);
462       for(i = 0; i < 8; i++)
463       {
464         printf("   %s: %d ms (%f ms per frame)\n",
465          print_strings[i], (u32)benchmark_ticks[i] / 1000,
466          (float)(benchmark_ticks[i] / (1000.0 * event_cycles_trigger)));
467         if(i == 4)
468           printf("\n");
469       }
470       quit();
471     }
472   }
473
474   event_cycles = 0;
475
476   get_ticks_us(benchmark_ticks + event_number);
477   event_number++;
478 }
479
480 static u32 fps = 60;
481 static u32 frames_drawn = 60;
482
483 u32 update_gba()
484 {
485   irq_type irq_raised = IRQ_NONE;
486
487   do
488   {
489     cpu_ticks += execute_cycles;
490
491     reg[CHANGED_PC_STATUS] = 0;
492
493     if(gbc_sound_update)
494     {
495       gbc_update_count++;
496       update_gbc_sound(cpu_ticks);
497       gbc_sound_update = 0;
498     }
499
500     update_timer(0);
501     update_timer(1);
502     update_timer(2);
503     update_timer(3);
504
505     video_count -= execute_cycles;
506
507     if(video_count <= 0)
508     {
509       u32 vcount = io_registers[REG_VCOUNT];
510       u32 dispstat = io_registers[REG_DISPSTAT];
511
512       if((dispstat & 0x02) == 0)
513       {
514         // Transition from hrefresh to hblank
515         video_count += (272);
516         dispstat |= 0x02;
517
518         if((dispstat & 0x01) == 0)
519         {
520           u32 i;
521           if(oam_update)
522             oam_update_count++;
523
524           if(no_alpha)
525             io_registers[REG_BLDCNT] = 0;
526           update_scanline();
527
528           // If in visible area also fire HDMA
529           for(i = 0; i < 4; i++)
530           {
531             if(dma[i].start_type == DMA_START_HBLANK)
532               dma_transfer(dma + i);
533           }
534         }
535
536         if(dispstat & 0x10)
537           irq_raised |= IRQ_HBLANK;
538       }
539       else
540       {
541         // Transition from hblank to next line
542         video_count += 960;
543         dispstat &= ~0x02;
544
545         vcount++;
546
547         if(vcount == 160)
548         {
549           // Transition from vrefresh to vblank
550           u32 i;
551
552           dispstat |= 0x01;
553           if(dispstat & 0x8)
554           {
555             irq_raised |= IRQ_VBLANK;
556           }
557
558           affine_reference_x[0] =
559            (s32)(address32(io_registers, 0x28) << 4) >> 4;
560           affine_reference_y[0] =
561            (s32)(address32(io_registers, 0x2C) << 4) >> 4;
562           affine_reference_x[1] =
563            (s32)(address32(io_registers, 0x38) << 4) >> 4;
564           affine_reference_y[1] =
565            (s32)(address32(io_registers, 0x3C) << 4) >> 4;
566
567           for(i = 0; i < 4; i++)
568           {
569             if(dma[i].start_type == DMA_START_VBLANK)
570               dma_transfer(dma + i);
571           }
572         }
573         else
574
575         if(vcount == 228)
576         {
577           // Transition from vblank to next screen
578           dispstat &= ~0x01;
579           frame_ticks++;
580
581   #ifdef PC_BUILD
582         printf("frame update (%x), %d instructions total, %d RAM flushes\n",
583            reg[REG_PC], instruction_count - last_frame, flush_ram_count);
584           last_frame = instruction_count;
585
586 /*          printf("%d gbc audio updates\n", gbc_update_count);
587           printf("%d oam updates\n", oam_update_count); */
588           gbc_update_count = 0;
589           oam_update_count = 0;
590           flush_ram_count = 0;
591   #endif
592
593           if(update_input())
594             continue;
595
596           update_gbc_sound(cpu_ticks);
597
598           if(fps_debug)
599           {
600             char print_buffer[32];
601             sprintf(print_buffer, "%2d (%2d)", fps, frames_drawn);
602             print_string(print_buffer, 0xFFFF, 0x000, 0, 0);
603           }
604           if(!synchronize_flag)
605             print_string("-FF-", 0xFFFF, 0x000, 216, 0);
606
607           update_screen();
608
609           synchronize();
610
611           if(update_backup_flag)
612             update_backup();
613
614           process_cheats();
615
616           event_cycles++;
617           if(event_cycles == event_cycles_trigger)
618           {
619             trigger_ext_event();
620             continue;
621           }
622
623           vcount = 0;
624         }
625
626         if(vcount == (dispstat >> 8))
627         {
628           // vcount trigger
629           dispstat |= 0x04;
630           if(dispstat & 0x20)
631           {
632             irq_raised |= IRQ_VCOUNT;
633           }
634         }
635         else
636         {
637           dispstat &= ~0x04;
638         }
639
640         io_registers[REG_VCOUNT] = vcount;
641       }
642       io_registers[REG_DISPSTAT] = dispstat;
643     }
644
645     if(irq_raised)
646       raise_interrupt(irq_raised);
647
648     execute_cycles = video_count;
649
650     check_timer(0);
651     check_timer(1);
652     check_timer(2);
653     check_timer(3);
654   } while(reg[CPU_HALT_STATE] != CPU_ACTIVE);
655
656   return execute_cycles;
657 }
658
659 #ifdef PSP_BUILD
660
661 u32 real_frame_count = 0;
662 u32 virtual_frame_count = 0;
663 u32 num_skipped_frames = 0;
664
665 void vblank_interrupt_handler(u32 sub, u32 *parg)
666 {
667   real_frame_count++;
668 }
669
670 void synchronize()
671 {
672   char char_buffer[64];
673   u64 new_ticks, time_delta;
674   s32 used_frameskip = frameskip_value;
675
676   if(!synchronize_flag)
677   {
678     used_frameskip = 4;
679     virtual_frame_count = real_frame_count - 1;
680   }
681
682   skip_next_frame = 0;
683
684   virtual_frame_count++;
685
686   if(real_frame_count >= virtual_frame_count)
687   {
688     if((real_frame_count > virtual_frame_count) &&
689      (current_frameskip_type == auto_frameskip) &&
690      (num_skipped_frames < frameskip_value))
691     {
692       skip_next_frame = 1;
693       num_skipped_frames++;
694     }
695     else
696     {
697       virtual_frame_count = real_frame_count;
698       num_skipped_frames = 0;
699     }
700
701     // Here so that the home button return will eventually work.
702     // If it's not running fullspeed anyway this won't really hurt
703     // it much more.
704
705     delay_us(1);
706   }
707   else
708   {
709     if(synchronize_flag)
710       sceDisplayWaitVblankStart();
711   }
712
713   if(current_frameskip_type == manual_frameskip)
714   {
715     frameskip_counter = (frameskip_counter + 1) %
716      (used_frameskip + 1);
717     if(random_skip)
718     {
719       if(frameskip_counter != (rand() % (used_frameskip + 1)))
720         skip_next_frame = 1;
721     }
722     else
723     {
724       if(frameskip_counter)
725         skip_next_frame = 1;
726     }
727   }
728
729 /*  sprintf(char_buffer, "%08d %08d %d %d %d\n",
730    real_frame_count, virtual_frame_count, num_skipped_frames,
731    real_frame_count - virtual_frame_count, skip_next_frame);
732   print_string(char_buffer, 0xFFFF, 0x0000, 0, 10); */
733
734 /*
735     sprintf(char_buffer, "%02d %02d %06d %07d", frameskip, (u32)ms_needed,
736      ram_translation_ptr - ram_translation_cache, rom_translation_ptr -
737      rom_translation_cache);
738     print_string(char_buffer, 0xFFFF, 0x0000, 0, 0);
739 */
740 }
741
742 #else
743
744 u32 real_frame_count = 0;
745 u32 virtual_frame_count = 0;
746 u32 num_skipped_frames = 0;
747 u32 interval_skipped_frames;
748 u32 frames;
749
750 const u32 frame_interval = 60;
751
752 void synchronize()
753 {
754   u64 new_ticks;
755   u64 time_delta;
756
757   get_ticks_us(&new_ticks);
758
759   skip_next_frame = 0;
760   virtual_frame_count++;
761
762   real_frame_count = (new_ticks * 3) / 50000;
763
764   if(real_frame_count >= virtual_frame_count)
765   {
766     if((real_frame_count > virtual_frame_count) &&
767      (current_frameskip_type == auto_frameskip) &&
768      (num_skipped_frames < frameskip_value))
769     {
770       skip_next_frame = 1;
771       num_skipped_frames++;
772     }
773     else
774     {
775       virtual_frame_count = real_frame_count;
776       num_skipped_frames = 0;
777     }
778   }
779   else if (synchronize_flag)
780   {
781 #if defined(PND_BUILD)
782     fb_wait_vsync();
783 #elif !defined(GP2X_BUILD) // sleeping on GP2X is a bad idea
784     delay_us((u64)virtual_frame_count * 50000 / 3 - new_ticks + 2);
785 #endif
786   }
787
788   frames++;
789
790   if(frames == frame_interval)
791   {
792     u32 new_fps;
793     u32 new_frames_drawn;
794
795     time_delta = new_ticks - last_frame_interval_timestamp;
796     new_fps = (u64)((u64)1000000 * (u64)frame_interval) / time_delta;
797     new_frames_drawn =
798      (frame_interval - interval_skipped_frames) * (60 / frame_interval);
799
800     // Left open for rolling averages
801     fps = new_fps;
802     frames_drawn = new_frames_drawn;
803
804     last_frame_interval_timestamp = new_ticks;
805     interval_skipped_frames = 0;
806     frames = 0;
807   }
808
809   if(current_frameskip_type == manual_frameskip)
810   {
811     frameskip_counter = (frameskip_counter + 1) %
812      (frameskip_value + 1);
813     if(random_skip)
814     {
815       if(frameskip_counter != (rand() % (frameskip_value + 1)))
816         skip_next_frame = 1;
817     }
818     else
819     {
820       if(frameskip_counter)
821         skip_next_frame = 1;
822     }
823   }
824
825   interval_skipped_frames += skip_next_frame;
826
827 #if !defined(GP2X_BUILD) && !defined(PND_BUILD)
828   char char_buffer[64];
829   sprintf(char_buffer, "gpSP: %2d (%2d) fps", fps, frames_drawn);
830   SDL_WM_SetCaption(char_buffer, "gpSP");
831 #endif
832 }
833
834 #endif
835
836 void quit()
837 {
838   if(!update_backup_flag)
839     update_backup_force();
840
841   sound_exit();
842
843 #ifdef REGISTER_USAGE_ANALYZE
844   print_register_usage();
845 #endif
846
847 #ifdef PSP_BUILD
848   sceKernelExitGame();
849 #else
850   SDL_Quit();
851
852 #ifndef PC_BUILD
853   gpsp_plat_quit();
854 #endif
855
856   exit(0);
857 #endif
858 }
859
860 void reset_gba()
861 {
862   init_main();
863   init_memory();
864   init_cpu();
865   reset_sound();
866 }
867
868 #ifdef PSP_BUILD
869
870 u32 file_length(char *filename, s32 dummy)
871 {
872   SceIoStat stats;
873   sceIoGetstat(filename, &stats);
874   return stats.st_size;
875 }
876
877 void delay_us(u32 us_count)
878 {
879   sceKernelDelayThread(us_count);
880 }
881
882 void get_ticks_us(u64 *tick_return)
883 {
884   u64 ticks;
885   sceRtcGetCurrentTick(&ticks);
886
887   *tick_return = (ticks * 1000000) / sceRtcGetTickResolution();
888 }
889
890 #else
891
892 u32 file_length(char *dummy, FILE *fp)
893 {
894   u32 length;
895
896   fseek(fp, 0, SEEK_END);
897   length = ftell(fp);
898   fseek(fp, 0, SEEK_SET);
899
900   return length;
901 }
902
903 #ifdef PC_BUILD
904
905 void delay_us(u32 us_count)
906 {
907   SDL_Delay(us_count / 1000);
908 }
909
910 void get_ticks_us(u64 *ticks_return)
911 {
912   *ticks_return = (u64)SDL_GetTicks() * 1000;
913 }
914
915 #else
916
917 void delay_us(u32 us_count)
918 {
919   //usleep(us_count);
920   SDL_Delay(us_count / 1000);
921 }
922
923 void get_ticks_us(u64 *ticks_return)
924 {
925   struct timeval current_time;
926   gettimeofday(&current_time, NULL);
927
928   *ticks_return =
929    (u64)current_time.tv_sec * 1000000 + current_time.tv_usec;
930 }
931
932 #endif
933
934 #endif
935
936 void change_ext(const char *src, char *buffer, const char *extension)
937 {
938   char *dot_position;
939   strcpy(buffer, src);
940   dot_position = strrchr(buffer, '.');
941
942   if(dot_position)
943     strcpy(dot_position, extension);
944 }
945
946 #define main_savestate_builder(type)                                          \
947 void main_##type##_savestate(file_tag_type savestate_file)                    \
948 {                                                                             \
949   file_##type##_variable(savestate_file, cpu_ticks);                          \
950   file_##type##_variable(savestate_file, execute_cycles);                     \
951   file_##type##_variable(savestate_file, video_count);                        \
952   file_##type##_array(savestate_file, timer);                                 \
953 }                                                                             \
954
955 main_savestate_builder(read);
956 main_savestate_builder(write_mem);
957
958
959 void printout(void *str, u32 val)
960 {
961   printf(str, val);
962 }
963
964 void set_clock_speed()
965 {
966   static u32 clock_speed_old = default_clock_speed;
967   if (clock_speed != clock_speed_old)
968   {
969     printf("about to set CPU clock to %iMHz\n", clock_speed);
970   #ifdef PSP_BUILD
971     scePowerSetClockFrequency(clock_speed, clock_speed, clock_speed / 2);
972   #elif defined(GP2X_BUILD)
973     set_FCLK(clock_speed);
974   #endif
975     clock_speed_old = clock_speed;
976   }
977 }
978