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