diff options
-rw-r--r-- | source/common.h | 10 | ||||
-rw-r--r-- | source/dstwo/port.h | 2 | ||||
-rw-r--r-- | source/gcw0/draw.c | 458 | ||||
-rw-r--r-- | source/gcw0/gui.c | 1441 | ||||
-rw-r--r-- | source/gcw0/main.c | 609 | ||||
-rw-r--r-- | source/gcw0/main.h | 123 | ||||
-rw-r--r-- | source/gcw0/port.h | 49 | ||||
-rw-r--r-- | source/psp/gui.c | 293 | ||||
-rw-r--r-- | source/psp/main.c | 778 | ||||
-rw-r--r-- | source/psp/port.h | 53 | ||||
-rw-r--r-- | source/zip.h | 2 |
11 files changed, 3806 insertions, 12 deletions
diff --git a/source/common.h b/source/common.h index d61ddf2..54cd5c9 100644 --- a/source/common.h +++ b/source/common.h @@ -39,6 +39,16 @@ #include <string.h> #include <math.h> +#ifdef __GNUC__ +# define likely(x) __builtin_expect((x),1) +# define unlikely(x) __builtin_expect((x),0) +# define prefetch(x, y) __builtin_prefetch((x),(y)) +#else +# define likely(x) (x) +# define unlikely(x) (x) +# define prefetch(x, y) +#endif + #define SYS_CLOCK (16777216.0) #define ROR(dest, value, shift) \ diff --git a/source/dstwo/port.h b/source/dstwo/port.h index 420ee60..fbf1bf1 100644 --- a/source/dstwo/port.h +++ b/source/dstwo/port.h @@ -53,8 +53,6 @@ typedef u32 FIXED16_16; // Q16.16 fixed-point * STMDB R0!, {R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14,R15} */ #define TRANSLATION_CACHE_LIMIT_THRESHOLD (1024) -#define NO_VOLATILE_SOUND - #define FILE_OPEN_APPEND ("a+") #define FILE_OPEN_READ ("rb") diff --git a/source/gcw0/draw.c b/source/gcw0/draw.c new file mode 100644 index 0000000..6eadbff --- /dev/null +++ b/source/gcw0/draw.c @@ -0,0 +1,458 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" + +SDL_Surface *screen = NULL; +SDL_Surface *display = NULL; + +const u32 video_scale = 1; + +void init_video() +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) + { + printf("Failed to initialize SDL !!\n"); + return; // for debug + // exit(1); + } + + display = SDL_SetVideoMode(320, 240, 16, SDL_HWSURFACE | SDL_DOUBLEBUF); + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, 16, + display->format->Rmask, + display->format->Gmask, + display->format->Bmask, + display->format->Amask); + + SDL_ShowCursor(0); +} + +void video_resolution_large() +{ + if(current_scale != unscaled) + { + resolution_width = 320; + resolution_height = 240; + } +} + +void video_resolution_small() +{ + if(current_scale != screen_scale) + { + resolution_width = small_resolution_width; + resolution_height = small_resolution_height; + } +} + +void set_gba_resolution(video_scale_type scale) +{ + if(screen_scale != scale) + { + screen_scale = scale; + switch(scale) + { + case unscaled: + case scaled_aspect: + case fullscreen: + small_resolution_width = 240 * video_scale; + small_resolution_height = 160 * video_scale; + break; + } + } +} + +void clear_screen(u16 color) +{ + u16 *dest_ptr = get_screen_pixels(); + u32 line_skip = get_screen_pitch() - screen->w; + u32 x, y; + + for(y = 0; y < screen->h; y++) + { + for(x = 0; x < screen->w; x++, dest_ptr++) + { + *dest_ptr = color; + } + dest_ptr += line_skip; + } +} + +#define integer_scale_copy_2() \ + current_scanline_ptr[x2] = current_pixel; \ + current_scanline_ptr[x2 - 1] = current_pixel; \ + x2 -= 2 \ + +#define integer_scale_copy_3() \ + current_scanline_ptr[x2] = current_pixel; \ + current_scanline_ptr[x2 - 1] = current_pixel; \ + current_scanline_ptr[x2 - 2] = current_pixel; \ + x2 -= 3 \ + +#define integer_scale_copy_4() \ + current_scanline_ptr[x2] = current_pixel; \ + current_scanline_ptr[x2 - 1] = current_pixel; \ + current_scanline_ptr[x2 - 2] = current_pixel; \ + current_scanline_ptr[x2 - 3] = current_pixel; \ + x2 -= 4 \ + +#define integer_scale_horizontal(scale_factor) \ + for(y = 0; y < 160; y++) \ + { \ + for(x = 239, x2 = (240 * video_scale) - 1; x >= 0; x--) \ + { \ + current_pixel = current_scanline_ptr[x]; \ + integer_scale_copy_##scale_factor(); \ + current_scanline_ptr[x2] = current_scanline_ptr[x]; \ + current_scanline_ptr[x2 - 1] = current_scanline_ptr[x]; \ + current_scanline_ptr[x2 - 2] = current_scanline_ptr[x]; \ + } \ + current_scanline_ptr += pitch; \ + } \ + + +// GPL software scaler, courtesy of Ayla (paul@crapouillou.net) +// Upscale from 240x160 to 320x240 +void gba_upscale(uint32_t *to, uint32_t *from, uint32_t src_x, uint32_t src_y, uint32_t pitch) +{ + /* Before: + * a b c d e f + * g h i j k l + * + * After (parenthesis = average): + * a (a,b) (b,c) c d (d,e) (e,f) f + * (a,g) (a,b,g,h) (b,c,h,i) (c,i) (d,j) (d,e,j,k) (e,f,k,l) (f,l) + * g (g,h) (h,i) i j (j,k) (k,l) l + */ + + const uint32_t dst_x = src_x * 4/3; + + uint32_t reg1, reg2, reg3, reg4, reg5; + size_t x, y; + + + for (y=0; y<src_y/2; y++) { + for (x=0; x<src_x/6; x++) { + prefetch(to+4, 1); + + /* Read b-a */ + reg1 = *from; + reg2 = ((reg1 & 0xf7de0000) >> 1) + (reg1 & 0x08210000); + + /* Read h-g */ + reg3 = *(from++ + src_x/2 + pitch/2); + reg4 = ((reg3 & 0xf7de0000) >> 1) + (reg3 & 0x08210000); + + reg1 &= 0xffff; + reg1 |= reg2 + ((reg1 & 0xf7de) << 15); + + /* Write (a,b)-a */ + *to = reg1; + + reg3 &= 0xffff; + reg3 |= reg4 + ((reg3 & 0xf7de) << 15); + + /* Write (g,h)-g */ + *(to + 2*dst_x/2) = reg3; + + if (unlikely(reg1 != reg3)) + reg1 = (reg1 & 0x08210821) + + ((reg1 & 0xf7def7de) >> 1) + + ((reg3 & 0xf7def7de) >> 1); + + /* Write (a,b,g,h)-(a,g) */ + *(to++ + dst_x/2) = reg1; + + /* Read d-c */ + reg1 = *from; + reg2 = ((reg2 + ((reg1 & 0xf7de) << 15)) >> 16) | ((reg1 & 0xffff) << 16); + + /* Write c-(b,c) */ + *to = reg2; + + /* Read j-i */ + reg3 = *(from++ + src_x/2 + pitch/2); + reg4 = ((reg4 + ((reg3 & 0xf7de) << 15)) >> 16) | ((reg3 & 0xffff) << 16); + + /* Write i-(h,i) */ + *(to + 2*dst_x/2) = reg4; + + if (unlikely(reg2 != reg4)) + reg2 = (reg2 & 0x08210821) + + ((reg2 & 0xf7def7de) >> 1) + + ((reg4 & 0xf7def7de) >> 1); + + /* Write (c,i)-(b,c,h,i) */ + *(to++ + dst_x/2) = reg2; + + /* Read f-e */ + reg2 = *from; + reg4 = (reg2 & 0xf7def7de) >> 1; + + /* Write (d,e)-d */ + reg1 >>= 16; + reg4 = reg1 | ((reg4 + ((reg1 & 0xf7de) >> 1) + (reg1 & 0x0821)) << 16); + *to = reg4; + + /* Read l-k */ + reg1 = *(from++ + src_x/2 + pitch/2); + reg5 = (reg1 & 0xf7def7de) >> 1; + + /* Write (j,k)-j */ + reg3 >>= 16; + reg5 = reg3 | ((reg5 + ((reg3 & 0xf7de) >> 1) + (reg3 & 0x0821)) << 16); + *(to + 2*dst_x/2) = reg5; + + if (unlikely(reg4 != reg5)) + reg4 = (reg4 & 0x08210821) + + ((reg4 & 0xf7def7de) >> 1) + + ((reg5 & 0xf7def7de) >> 1); + + /* Write (d,e,j,k)-(d,j) */ + *(to++ + dst_x/2) = reg4; + + /* Write f-(e,f) */ + reg3 = ((reg2 & 0xf7def7de) >> 1); + reg2 = (reg2 & 0xffff0000) | ((reg3 + (reg3 >> 16) + (reg2 & 0x0821)) & 0xffff); + *to = reg2; + + /* Write l-(k,l) */ + reg3 = ((reg1 & 0xf7def7de) >> 1); + reg1 = (reg1 & 0xffff0000) | ((reg3 + (reg3 >> 16) + (reg1 & 0x0821)) & 0xffff); + *(to + 2*dst_x/2) = reg1; + + if (unlikely(reg1 != reg2)) + reg1 = (reg1 & 0x08210821) + + ((reg1 & 0xf7def7de) >> 1) + + ((reg2 & 0xf7def7de) >> 1); + + /* Write (f,l)-(e,f,k,l) */ + *(to++ + dst_x/2) = reg1; + } + + to += 2*dst_x/2; + from += src_x/2 + 2*pitch/2; + } +} + +void update_normal(void) +{ + SDL_BlitSurface(screen,NULL,display,NULL); + SDL_Flip(display); +} + +void update_display(void) +{ + if (!screen_scale) + SDL_BlitSurface(screen,NULL,display,NULL); + else + { + uint32_t *src = (uint32_t *)screen->pixels + 20 + 80 * (320 - 240); + gba_upscale((uint32_t*)display->pixels, src, 240, 160, 320 - 240); + } + + SDL_Flip(display); +} + +void flip_screen() +{ + if (!screen) + return; + + if((video_scale != 1) && (current_scale != unscaled)) + { + s32 x, y; + s32 x2, y2; + u16 *screen_ptr = get_screen_pixels(); + u16 *current_scanline_ptr = screen_ptr; + u32 pitch = get_screen_pitch(); + u16 current_pixel; + u32 i; + + switch(video_scale) + { + case 2: + integer_scale_horizontal(2); + break; + + case 3: + integer_scale_horizontal(3); + break; + + default: + case 4: + integer_scale_horizontal(4); + break; + + } + + for(y = 159, y2 = (160 * video_scale) - 1; y >= 0; y--) + { + for(i = 0; i < video_scale; i++) + { + memcpy(screen_ptr + (y2 * pitch), + screen_ptr + (y * pitch), 480 * video_scale); + y2--; + } + } + } + update_normal(); +} + +u32 frame_to_render; + +void update_screen() +{ +#ifdef ZAURUS + if (!screen) + return; + if(!skip_next_frame) + update_display(); + +#else + if(!skip_next_frame) + flip_screen(); +#endif +} + +video_scale_type screen_scale = fullscreen; +video_scale_type current_scale = fullscreen; +video_filter_type screen_filter = filter_bilinear; + +u16 *copy_screen() +{ +#ifdef ZAURUS + u32 pitch = get_screen_pitch(); + u16 *copy = malloc(240 * 160 * 2); + u16 *dest_ptr = copy; + u16 *src_ptr = get_screen_pixels() + 40 + (40 * pitch); + u32 line_skip = pitch - 240; + u32 x, y; + + for(y = 0; y < 160; y++) + { + for(x = 0; x < 240; x++, src_ptr++, dest_ptr++) + { + *dest_ptr = *src_ptr; + } + src_ptr += line_skip; + } +#else + u16 *copy = malloc(240 * 160 * 2); + memcpy(copy, get_screen_pixels(), 240 * 160 * 2); +#endif + return copy; +} + +void blit_to_screen(u16 *src, u32 w, u32 h, u32 dest_x, u32 dest_y) +{ + u32 pitch = get_screen_pitch(); +#ifdef ZAURUS + u16 *dest_ptr = get_screen_pixels(); +#else + u16 *dest_ptr = get_screen_pixels() + dest_x + (dest_y * pitch); +#endif + u16 *src_ptr = src; + u32 line_skip = pitch - w; + u32 x, y; + + for(y = 0; y < h; y++) + { + for(x = 0; x < w; x++, src_ptr++, dest_ptr++) + { + *dest_ptr = *src_ptr; + } + dest_ptr += line_skip; + } +} + +void print_string_ext(const char *str, u16 fg_color, u16 bg_color, + u32 x, u32 y, void *_dest_ptr, u32 pitch, u32 pad) +{ + u16 *dest_ptr = (u16 *)_dest_ptr + (y * pitch) + x; + u8 current_char = str[0]; + u32 current_row; + u32 glyph_offset; + u32 i = 0, i2, i3; + u32 str_index = 1; + u32 current_x = x; + + while(current_char) + { + if(current_char == '\n') + { + y += FONT_HEIGHT; + current_x = x; + dest_ptr = get_screen_pixels() + (y * pitch) + x; + } + else + { + glyph_offset = _font_offset[current_char]; + current_x += FONT_WIDTH; + for(i2 = 0; i2 < FONT_HEIGHT; i2++, glyph_offset++) + { + current_row = _font_bits[glyph_offset]; + for(i3 = 0; i3 < FONT_WIDTH; i3++) + { + if((current_row >> (15 - i3)) & 0x01) + *dest_ptr = fg_color; + else + *dest_ptr = bg_color; + dest_ptr++; + } + dest_ptr += (pitch - FONT_WIDTH); + } + dest_ptr = dest_ptr - (pitch * FONT_HEIGHT) + FONT_WIDTH; + } + + i++; + + current_char = str[str_index]; + + if((i < pad) && (current_char == 0)) + { + current_char = ' '; + } + else + { + str_index++; + } + +#ifdef ZAURUS + if(current_x >= 320) +#else + if(current_x >= 480) +#endif + break; + } +} + +void print_string(const char *str, u16 fg_color, u16 bg_color, + u32 x, u32 y) +{ + print_string_ext(str, fg_color, bg_color, x, y, get_screen_pixels(), + get_screen_pitch(), 0); +} + +void print_string_pad(const char *str, u16 fg_color, u16 bg_color, + u32 x, u32 y, u32 pad) +{ + print_string_ext(str, fg_color, bg_color, x, y, get_screen_pixels(), + get_screen_pitch(), pad); +} diff --git a/source/gcw0/gui.c b/source/gcw0/gui.c new file mode 100644 index 0000000..c0f8789 --- /dev/null +++ b/source/gcw0/gui.c @@ -0,0 +1,1441 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licens e as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <dirent.h> +#include "common.h" + +#define MAX_PATH 1024 + +// Blatantly stolen and trimmed from MZX (megazeux.sourceforge.net) +#ifdef ZAURUS +#define FILE_LIST_ROWS 20 +#define FILE_LIST_POSITION 1 +#define DIR_LIST_POSITION 240 +#else +#define FILE_LIST_ROWS 25 +#define FILE_LIST_POSITION 5 +#define DIR_LIST_POSITION 360 +#endif + +#ifdef PSP_BUILD + +#define color16(red, green, blue) \ + (blue << 11) | (green << 5) | red \ + +#else + +#define color16(red, green, blue) \ + (red << 11) | (green << 5) | blue \ + +#endif + +#define COLOR_BG color16(20, 10, 10) +#define COLOR_ROM_INFO color16(22, 36, 26) +#define COLOR_ACTIVE_ITEM color16(31, 63, 31) +#define COLOR_INACTIVE_ITEM color16(13, 40, 18) +#define COLOR_FRAMESKIP_BAR color16(15, 31, 31) +#define COLOR_HELP_TEXT color16(16, 40, 24) + +int sort_function(const void *dest_str_ptr, const void *src_str_ptr) +{ + char *dest_str = *((char **)dest_str_ptr); + char *src_str = *((char **)src_str_ptr); + + if(src_str[0] == '.') + return 1; + + if(dest_str[0] == '.') + return -1; + + return strcasecmp(dest_str, src_str); +} + +s32 load_file(u8 **wildcards, u8 *result) +{ + DIR *current_dir; + struct dirent *current_file; + struct stat file_info; + u8 current_dir_name[MAX_PATH]; + u8 current_dir_short[81]; + u32 current_dir_length; + u32 total_filenames_allocated; + u32 total_dirnames_allocated; + u8 **file_list; + u8 **dir_list; + u32 num_files; + u32 num_dirs; + u8 *file_name; + u32 file_name_length; + u32 ext_pos = -1; + u32 chosen_file, chosen_dir; + u32 dialog_result = 1; + s32 return_value = 1; + u32 current_file_selection; + u32 current_file_scroll_value; + u32 current_dir_selection; + u32 current_dir_scroll_value; + u32 current_file_in_scroll; + u32 current_dir_in_scroll; + u32 current_file_number, current_dir_number; + u32 current_column = 0; + u32 repeat; + u32 i; + gui_action_type gui_action; + + while(return_value == 1) + { + current_file_selection = 0; + current_file_scroll_value = 0; + current_dir_selection = 0; + current_dir_scroll_value = 0; + current_file_in_scroll = 0; + current_dir_in_scroll = 0; + + total_filenames_allocated = 32; + total_dirnames_allocated = 32; + file_list = (u8 **)malloc(sizeof(u8 *) * 32); + dir_list = (u8 **)malloc(sizeof(u8 *) * 32); + memset(file_list, 0, sizeof(u8 *) * 32); + memset(dir_list, 0, sizeof(u8 *) * 32); + + num_files = 0; + num_dirs = 0; + chosen_file = 0; + chosen_dir = 0; + + sprintf(current_dir_name, "%s/.gpsp", getenv("HOME")); + mkdir(current_dir_name, 0755); + current_dir = opendir(current_dir_name); + + do + { + if(current_dir) + current_file = readdir(current_dir); + else + current_file = NULL; + + if(current_file) + { + file_name = current_file->d_name; + file_name_length = strlen(file_name); + + if((stat(file_name, &file_info) >= 0) && + ((file_name[0] != '.') || (file_name[1] == '.'))) + { + if(S_ISDIR(file_info.st_mode)) + { + dir_list[num_dirs] = + (u8 *)malloc(file_name_length + 1); + strcpy(dir_list[num_dirs], file_name); + + num_dirs++; + } + else + { + // Must match one of the wildcards, also ignore the . + if(file_name_length >= 4) + { + if(file_name[file_name_length - 4] == '.') + ext_pos = file_name_length - 4; + else + + if(file_name[file_name_length - 3] == '.') + ext_pos = file_name_length - 3; + + else + ext_pos = 0; + + for(i = 0; wildcards[i] != NULL; i++) + { + if(!strcasecmp((file_name + ext_pos), + wildcards[i])) + { + file_list[num_files] = + (u8 *)malloc(file_name_length + 1); + + strcpy(file_list[num_files], file_name); + + num_files++; + break; + } + } + } + } + } + + if(num_files == total_filenames_allocated) + { + file_list = (u8 **)realloc(file_list, sizeof(u8 *) * + total_filenames_allocated * 2); + memset(file_list + total_filenames_allocated, 0, + sizeof(u8 *) * total_filenames_allocated); + total_filenames_allocated *= 2; + } + + if(num_dirs == total_dirnames_allocated) + { + dir_list = (u8 **)realloc(dir_list, sizeof(u8 *) * + total_dirnames_allocated * 2); + memset(dir_list + total_dirnames_allocated, 0, + sizeof(u8 *) * total_dirnames_allocated); + total_dirnames_allocated *= 2; + } + } + } while(current_file); + + qsort((void *)file_list, num_files, sizeof(u8 *), sort_function); + qsort((void *)dir_list, num_dirs, sizeof(u8 *), sort_function); + + closedir(current_dir); + + current_dir_length = strlen(current_dir_name); + + if(current_dir_length > 80) + { + memcpy(current_dir_short, "...", 3); + memcpy(current_dir_short + 3, + current_dir_name + current_dir_length - 77, 77); + current_dir_short[80] = 0; + } + else + { + memcpy(current_dir_short, current_dir_name, + current_dir_length + 1); + } + + repeat = 1; + + if(num_files == 0) + current_column = 1; + + clear_screen(COLOR_BG); + u8 print_buffer[81]; + + while(repeat) + { + flip_screen(); + + print_string(current_dir_short, COLOR_ACTIVE_ITEM, COLOR_BG, 0, 0); + + print_string("Press Cancel to return to the main menu.", + COLOR_HELP_TEXT, COLOR_BG, 20, 220); + for(i = 0, current_file_number = i + current_file_scroll_value; + i < FILE_LIST_ROWS; i++, current_file_number++) + { + if(current_file_number < num_files) + { + strncpy(print_buffer,file_list[current_file_number],38); + print_buffer[38] = 0; + if((current_file_number == current_file_selection) && + (current_column == 0)) + { + print_string(print_buffer, COLOR_ACTIVE_ITEM, + COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10)); + } + else + { + print_string(print_buffer, COLOR_INACTIVE_ITEM, + COLOR_BG, FILE_LIST_POSITION, ((i + 1) * 10)); + } + } + } + + for(i = 0, current_dir_number = i + current_dir_scroll_value; + i < FILE_LIST_ROWS; i++, current_dir_number++) + { + if(current_dir_number < num_dirs) + { + strncpy(print_buffer,dir_list[current_dir_number], 13); + print_buffer[14] = 0; + if((current_dir_number == current_dir_selection) && + (current_column == 1)) + { + print_string(print_buffer, COLOR_ACTIVE_ITEM, + COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10)); + } + else + { + print_string(print_buffer, COLOR_INACTIVE_ITEM, + COLOR_BG, DIR_LIST_POSITION, ((i + 1) * 10)); + } + } + } + + gui_action = get_gui_input(); + + switch(gui_action) + { + case CURSOR_DOWN: + if(current_column == 0) + { + if(current_file_selection < (num_files - 1)) + { + current_file_selection++; + if(current_file_in_scroll == (FILE_LIST_ROWS - 1)) + { + clear_screen(COLOR_BG); + current_file_scroll_value++; + } + else + { + current_file_in_scroll++; + } + } + } + else + { + if(current_dir_selection < (num_dirs - 1)) + { + current_dir_selection++; + if(current_dir_in_scroll == (FILE_LIST_ROWS - 1)) + { + clear_screen(COLOR_BG); + current_dir_scroll_value++; + } + else + { + current_dir_in_scroll++; + } + } + } + + break; + + case CURSOR_UP: + if(current_column == 0) + { + if(current_file_selection) + { + current_file_selection--; + if(current_file_in_scroll == 0) + { + clear_screen(COLOR_BG); + current_file_scroll_value--; + } + else + { + current_file_in_scroll--; + } + } + } + else + { + if(current_dir_selection) + { + current_dir_selection--; + if(current_dir_in_scroll == 0) + { + clear_screen(COLOR_BG); + current_dir_scroll_value--; + } + else + { + current_dir_in_scroll--; + } + } + } + break; + + case CURSOR_RIGHT: + if(current_column == 0) + { + if(num_dirs != 0) + current_column = 1; + } + break; + + case CURSOR_LEFT: + if(current_column == 1) + { + if(num_files != 0) + current_column = 0; + } + break; + + case CURSOR_SELECT: + if(current_column == 1) + { + repeat = 0; + chdir(dir_list[current_dir_selection]); + } + else + { + if(num_files != 0) + { + repeat = 0; + return_value = 0; + strcpy(result, file_list[current_file_selection]); + } + } + break; + + case CURSOR_BACK: + repeat = 0; + chdir(".."); + break; + + case CURSOR_EXIT: + return_value = -1; + repeat = 0; + break; + } + } + + for(i = 0; i < num_files; i++) + { + free(file_list[i]); + } + free(file_list); + + for(i = 0; i < num_dirs; i++) + { + free(dir_list[i]); + } + free(dir_list); + } + + clear_screen(COLOR_BG); + return return_value; +} + +typedef enum +{ + NUMBER_SELECTION_OPTION = 0x01, + STRING_SELECTION_OPTION = 0x02, + SUBMENU_OPTION = 0x04, + ACTION_OPTION = 0x08 +} menu_option_type_enum; + +struct _menu_type +{ + void (* init_function)(); + void (* passive_function)(); + struct _menu_option_type *options; + u32 num_options; +}; + +struct _menu_option_type +{ + void (* action_function)(); + void (* passive_function)(); + struct _menu_type *sub_menu; + char *display_string; + void *options; + u32 *current_option; + u32 num_options; + char *help_string; + u32 line_number; + menu_option_type_enum option_type; +}; + +typedef struct _menu_option_type menu_option_type; +typedef struct _menu_type menu_type; + +#define make_menu(name, init_function, passive_function) \ + menu_type name##_menu = \ + { \ + init_function, \ + passive_function, \ + name##_options, \ + sizeof(name##_options) / sizeof(menu_option_type) \ + } \ + +#define gamepad_config_option(display_string, number) \ +{ \ + NULL, \ + menu_fix_gamepad_help, \ + NULL, \ + display_string ": %s", \ + gamepad_config_buttons, \ + gamepad_config_map + gamepad_config_line_to_psp_button[number], \ + sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]), \ + gamepad_help[gamepad_config_map[ \ + gamepad_config_line_to_psp_button[number]]], \ + number, \ + STRING_SELECTION_OPTION \ +} \ + +#define analog_config_option(display_string, number) \ +{ \ + NULL, \ + menu_fix_gamepad_help, \ + NULL, \ + display_string ": %s", \ + gamepad_config_buttons, \ + gamepad_config_map + number + 12, \ + sizeof(gamepad_config_buttons) / sizeof(gamepad_config_buttons[0]), \ + gamepad_help[gamepad_config_map[number + 12]], \ + number + 2, \ + STRING_SELECTION_OPTION \ +} \ + +#define cheat_option(number) \ +{ \ + NULL, \ + NULL, \ + NULL, \ + cheat_format_str[number], \ + enable_disable_options, \ + &(cheats[number].cheat_active), \ + 2, \ + "Activate/deactivate this cheat code.", \ + number, \ + STRING_SELECTION_OPTION \ +} \ + +#define action_option(action_function, passive_function, display_string, \ + help_string, line_number) \ +{ \ + action_function, \ + passive_function, \ + NULL, \ + display_string, \ + NULL, \ + NULL, \ + 0, \ + help_string, \ + line_number, \ + ACTION_OPTION \ +} \ + +#define submenu_option(sub_menu, display_string, help_string, line_number) \ +{ \ + NULL, \ + NULL, \ + sub_menu, \ + display_string, \ + NULL, \ + NULL, \ + sizeof(sub_menu) / sizeof(menu_option_type), \ + help_string, \ + line_number, \ + SUBMENU_OPTION \ +} \ + +#define selection_option(passive_function, display_string, options, \ + option_ptr, num_options, help_string, line_number, type) \ +{ \ + NULL, \ + passive_function, \ + NULL, \ + display_string, \ + options, \ + option_ptr, \ + num_options, \ + help_string, \ + line_number, \ + type \ +} \ + +#define action_selection_option(action_function, passive_function, \ + display_string, options, option_ptr, num_options, help_string, line_number, \ + type) \ +{ \ + action_function, \ + passive_function, \ + NULL, \ + display_string, \ + options, \ + option_ptr, \ + num_options, \ + help_string, \ + line_number, \ + type | ACTION_OPTION \ +} \ + + +#define string_selection_option(passive_function, display_string, options, \ + option_ptr, num_options, help_string, line_number) \ + selection_option(passive_function, display_string ": %s", options, \ + option_ptr, num_options, help_string, line_number, STRING_SELECTION_OPTION)\ + +#define numeric_selection_option(passive_function, display_string, \ + option_ptr, num_options, help_string, line_number) \ + selection_option(passive_function, display_string ": %d", NULL, option_ptr, \ + num_options, help_string, line_number, NUMBER_SELECTION_OPTION) \ + +#define string_selection_action_option(action_function, passive_function, \ + display_string, options, option_ptr, num_options, help_string, line_number) \ + action_selection_option(action_function, passive_function, \ + display_string ": %s", options, option_ptr, num_options, help_string, \ + line_number, STRING_SELECTION_OPTION) \ + +#define numeric_selection_action_option(action_function, passive_function, \ + display_string, option_ptr, num_options, help_string, line_number) \ + action_selection_option(action_function, passive_function, \ + display_string ": %d", NULL, option_ptr, num_options, help_string, \ + line_number, NUMBER_SELECTION_OPTION) \ + +#define numeric_selection_action_hide_option(action_function, \ + passive_function, display_string, option_ptr, num_options, help_string, \ + line_number) \ + action_selection_option(action_function, passive_function, \ + display_string, NULL, option_ptr, num_options, help_string, \ + line_number, NUMBER_SELECTION_OPTION) \ + + +#define GAMEPAD_MENU_WIDTH 15 + +u32 gamepad_config_line_to_psp_button[] = + { 8, 6, 7, 9, 1, 2, 3, 0, 4, 5, 11, 10 }; + +s32 load_game_config_file() +{ + u8 game_config_filename[512]; + u32 file_loaded = 0; + u32 i; + change_ext(gamepak_filename, game_config_filename, ".cfg"); + + FILE_OPEN(game_config_file, game_config_filename, read); + + if(file_check_valid(game_config_file)) + { + u32 file_size = FILE_LENGTH(game_config_file); + + // Sanity check: File size must be the right size + if(file_size == 56) + { + u32 file_options[file_size / 4]; + + FILE_READ_ARRAY(game_config_file, file_options); + current_frameskip_type = file_options[0] % 3; + frameskip_value = file_options[1]; + random_skip = file_options[2] & 1; + clock_speed = global_cycles_per_instruction = file_options[3]; + + if(frameskip_value < 0) + frameskip_value = 0; + + if(frameskip_value > 99) + frameskip_value = 99; + + + file_loaded = 1; + } + FILE_CLOSE(game_config_file); + } + + if(file_loaded) + return 0; + + current_frameskip_type = auto_frameskip; + frameskip_value = 4; + random_skip = 0; + clock_speed = 4; + + for(i = 0; i < 10; i++) + { + cheats[i].cheat_active = 0; + } + + return -1; +} + +s32 load_config_file() +{ + u8 config_path[512]; + sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME); + + FILE_OPEN(config_file, config_path, read); + + if(FILE_CHECK_VALID(config_file)) + { + u32 file_size = FILE_LENGTH(config_file); + + // Sanity check: File size must be the right size + if(file_size == 92) + { + u32 file_options[file_size / 4]; + u32 i; + s32 menu_button = -1; + file_read_array(config_file, file_options); + + screen_scale = file_options[0] % 2; + screen_filter = file_options[1] % 2; + global_enable_audio = file_options[2] % 2; +#ifdef ZAURUS + audio_buffer_size_number = file_options[3] % 4; +#else + audio_buffer_size_number = file_options[3] % 11; +#endif + update_backup_flag = file_options[4] % 2; + global_enable_analog = file_options[5] % 2; + analog_sensitivity_level = file_options[6] % 8; + + // Sanity check: Make sure there's a MENU or FRAMESKIP + // key, if not assign to triangle + + for(i = 0; i < 16; i++) + { + gamepad_config_map[i] = file_options[7 + i] % + (BUTTON_ID_NONE + 1); + + if(gamepad_config_map[i] == BUTTON_ID_MENU) + { + menu_button = i; + } + } + + if(menu_button == -1) + { + gamepad_config_map[0] = BUTTON_ID_MENU; + } + } + + FILE_CLOSE(config_file); + + return 0; + } + + return -1; +} + +s32 save_game_config_file() +{ + u8 game_config_filename[512]; + u32 i; + + change_ext(gamepak_filename, game_config_filename, ".cfg"); + + FILE_OPEN(game_config_file, game_config_filename, write); + + if(FILE_CHECK_VALID(game_config_file)) + { + u32 file_options[14]; + + file_options[0] = current_frameskip_type; + file_options[1] = frameskip_value; + file_options[2] = random_skip; + file_options[3] = clock_speed; + + for(i = 0; i < 10; i++) + { + file_options[4 + i] = cheats[i].cheat_active; + } + + FILE_WRITE_ARRAY(game_config_file, file_options); + FILE_CLOSE(game_config_file); + + return 0; + } + + return -1; +} + +s32 save_config_file() +{ + u8 config_path[512]; + sprintf(config_path, "%s/%s", main_path, GPSP_CONFIG_FILENAME); + + FILE_OPEN(config_file, config_path, write); + + save_game_config_file(); + + if(FILE_CHECK_VALID(config_file)) + { + u32 file_options[23]; + u32 i; + + file_options[0] = screen_scale; + file_options[1] = screen_filter; + file_options[2] = global_enable_audio; + file_options[3] = audio_buffer_size_number; + file_options[4] = update_backup_flag; + file_options[5] = global_enable_analog; + file_options[6] = analog_sensitivity_level; + + for(i = 0; i < 16; i++) + { + file_options[7 + i] = gamepad_config_map[i]; + } + + FILE_WRITE_ARRAY(config_file, file_options); + FILE_CLOSE(config_file); + + return 0; + } + + return -1; +} + +typedef enum +{ + MAIN_MENU, + GAMEPAD_MENU, + SAVESTATE_MENU, + FRAMESKIP_MENU, + CHEAT_MENU +} menu_enum; + +u32 savestate_slot = 0; +u8 ssmsg[8]; +u32 status_display = 0; + +void get_savestate_snapshot(u8 *savestate_filename) +{ + /* Unimplemented. Need to figure out a way to show this screenshot on a + * 320x240 screen first. - Neb, 2013-08-19 */ +#if 0 + u16 snapshot_buffer[240 * 160]; + u8 savestate_timestamp_string[80]; + + FILE_OPEN(savestate_file, savestate_filename, read); + + if(FILE_CHECK_VALID(savestate_file)) + { + u8 weekday_strings[7][11] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; + time_t savestate_time_flat; + struct tm *current_time; + file_read_array(savestate_file, snapshot_buffer); + file_read_variable(savestate_file, savestate_time_flat); + + file_close(savestate_file); + + current_time = localtime(&savestate_time_flat); + sprintf(savestate_timestamp_string, + "%s %02d/%02d/%04d %02d:%02d:%02d ", + weekday_strings[current_time->tm_wday], current_time->tm_mon + 1, + current_time->tm_mday, current_time->tm_year + 1900, + current_time->tm_hour, current_time->tm_min, current_time->tm_sec); + + savestate_timestamp_string[40] = 0; + print_string(savestate_timestamp_string, COLOR_HELP_TEXT, COLOR_BG, + 10, 40); + } + else + { + memset(snapshot_buffer, 0, 240 * 160 * 2); + print_string_ext("No savestate exists for this slot.", + 0xFFFF, 0x0000, 15, 75, snapshot_buffer, 240, 0); + print_string("---------- --/--/---- --:--:-- ", COLOR_HELP_TEXT, + COLOR_BG, 10, 40); + } +#endif +} + +void get_savestate_filename(u32 slot, u8 *name_buffer) +{ + u8 savestate_ext[16]; + + sprintf(savestate_ext, "%d.svs", slot); + change_ext(gamepak_filename, name_buffer, savestate_ext); + + get_savestate_snapshot(name_buffer); +} + +void get_savestate_filename_noshot(u32 slot, u8 *name_buffer) +{ + u8 savestate_ext[16]; + + sprintf(savestate_ext, "%d.svs", slot); + change_ext(gamepak_filename, name_buffer, savestate_ext); +} + +u32 ReGBA_Menu(enum ReGBA_MenuEntryReason EntryReason) +{ + u32 clock_speed_number = clock_speed - 1; + u8 print_buffer[81]; + u32 _current_option = 0; + gui_action_type gui_action; + menu_enum _current_menu = MAIN_MENU; + u32 i; + u32 repeat = 1; + u32 return_value = 0; + u32 first_load = 0; + u8 savestate_ext[16]; + u8 current_savestate_filename[512]; + u8 line_buffer[80]; + u8 cheat_format_str[10][41]; + + menu_type *current_menu; + menu_option_type *current_option; + menu_option_type *display_option; + u32 current_option_num; + + auto void choose_menu(); + auto void clear_help(); + + u8 *gamepad_help[] = + { + "Up button on GBA d-pad.", + "Down button on GBA d-pad.", + "Left button on GBA d-pad.", + "Right button on GBA d-pad.", + "A button on GBA.", + "B button on GBA.", + "Left shoulder button on GBA.", + "Right shoulder button on GBA.", + "Start button on GBA.", + "Select button on GBA.", + "Brings up frameskip adjust bar and menu access.", + "Jumps directly to the menu.", + "Toggles fastforward on/off (don't expect it to do much or anything)", + "Loads the game state from the current slot.", + "Saves the game state to the current slot.", + "Rapidly press/release the A button on GBA.", + "Rapidly press/release the B button on GBA.", + "Rapidly press/release the Left shoulder button on GBA.", + "Rapidly press/release the Right shoulder button on GBA.", + "Does nothing." + }; + + void menu_exit() + { + if(!first_load) + repeat = 0; + } + + void menu_quit() + { + clock_speed = clock_speed_number + 1; + save_config_file(); + quit(); + } + + void menu_load() + { + u8 *file_ext[] = { ".gba", ".bin", ".zip", NULL }; + u8 load_filename[512]; + save_game_config_file(); + if(load_file(file_ext, load_filename) != -1) + { + if(load_gamepak(load_filename) == -1) + { + quit(); + } + reset_gba(); + return_value = 1; + repeat = 0; + reg[CHANGED_PC_STATUS] = 1; + } + else + { + choose_menu(current_menu); + } + } + + void menu_restart() + { + if(!first_load) + { + reset_gba(); + reg[CHANGED_PC_STATUS] = 1; + return_value = 1; + repeat = 0; + } + } + + void menu_change_state() + { + get_savestate_filename(savestate_slot, current_savestate_filename); + } + + void menu_save_state() + { + if(!first_load) + { + get_savestate_filename_noshot(savestate_slot, + current_savestate_filename); + save_state(current_savestate_filename, original_screen); + } + menu_change_state(); + } + + void menu_load_state() + { + if(!first_load) + { + load_state(current_savestate_filename); + return_value = 1; + repeat = 0; + } + } + + void menu_load_state_file() + { + u8 *file_ext[] = { ".svs", NULL }; + u8 load_filename[512]; + if(load_file(file_ext, load_filename) != -1) + { + load_state(load_filename); + return_value = 1; + repeat = 0; + } + else + { + choose_menu(current_menu); + } + } + + void menu_fix_gamepad_help() + { + clear_help(); + current_option->help_string = + gamepad_help[gamepad_config_map[ + gamepad_config_line_to_psp_button[current_option_num]]]; + } + + void submenu_graphics_sound() + { + + } + + void submenu_cheats_misc() + { + + } + + void submenu_gamepad() + { + + } + + void submenu_analog() + { + + } + + void submenu_savestate() + { + print_string("Savestate options:", COLOR_ACTIVE_ITEM, COLOR_BG, 10, 70); + menu_change_state(); + } + + void submenu_main() + { + get_savestate_filename_noshot(savestate_slot, + current_savestate_filename); + } + + u8 *yes_no_options[] = { "no", "yes" }; + u8 *enable_disable_options[] = { "disabled", "enabled" }; + + u8 *scale_options[] = + { + "normal", "fullscreen" + }; + + u8 *frameskip_options[] = { "automatic", "manual", "off" }; + u8 *frameskip_variation_options[] = { "uniform", "random" }; + +#if 0 + u8 *audio_buffer_options[] = + { + "3072 bytes", "4096 bytes", "5120 bystes", "6144 bytes", "7168 bytes", + "8192 bytes", "9216 bytes", "10240 bytes", "11264 bytes", "12288 bytes" + }; + +#endif + u8 *update_backup_options[] = { "Exit only", "Automatic" }; + +#ifndef ZAURUS + u8 *gamepad_config_buttons[] = + { + "UP", + "DOWN", + "LEFT", + "RIGHT", + "A", + "B", + "L", + "R", + "START", + "SELECT", + "MENU", + "FASTFORWARD", + "LOAD STATE", + "SAVE STATE", + "RAPIDFIRE A", + "RAPIDFIRE B", + "RAPIDFIRE L", + "RAPIDFIRE R", + "NOTHING" + }; +#endif + + // Marker for help information, don't go past this mark (except \n)------* + menu_option_type graphics_sound_options[] = + { + string_selection_option(NULL, "Display scaling", scale_options, + (u32 *)(&screen_scale), 2, + "Determines how the GBA screen is resized in relation to the entire\n" + "screen. Select unscaled 3:2 for GBA resolution, scaled 3:2 for GBA\n" + "aspect ratio scaled to fill the height of the PSP screen, and\n" + "fullscreen to fill the entire PSP screen.", 2), +#ifndef ZAURUS + string_selection_option(NULL, "Screen filtering", yes_no_options, + (u32 *)(&screen_filter), 2, + "Determines whether or not bilinear filtering should be used when\n" + "scaling the screen. Selecting this will produce a more even and\n" + "smooth image, at the cost of being blurry and having less vibrant\n" + "colors.", 3), +#endif + string_selection_option(NULL, "Frameskip type", frameskip_options, + (u32 *)(¤t_frameskip_type), 3, + "Determines what kind of frameskipping should be employed.\n" + "Frameskipping may improve emulation speed of many games.\n" + "Off: Do not skip any frames.\n" + "Auto: Skip up to N frames (see next option) as needed.\n" + "Manual: Always render only 1 out of N + 1 frames.", 5), + numeric_selection_option(NULL, "Frameskip value", &frameskip_value, 100, + "For auto frameskip, determines the maximum number of frames that\n" + "are allowed to be skipped consecutively.\n" + "For manual frameskip, determines the number of frames that will\n" + "always be skipped.", 6), + string_selection_option(NULL, "Framskip variation", + frameskip_variation_options, &random_skip, 2, + "If objects in the game flicker at a regular rate certain manual\n" + "frameskip values may cause them to normally disappear. Change this\n" + "value to 'random' to avoid this. Do not use otherwise, as it tends to\n" + "make the image quality worse, especially in high motion games.", 7), + string_selection_option(NULL, "Audio output", yes_no_options, + &global_enable_audio, 2, + "Select 'no' to turn off all audio output. This will not result in a\n" + "significant change in performance.", 9), +#if 0 + string_selection_option(NULL, "Audio buffer", audio_buffer_options, + &audio_buffer_size_number, 10, + "Set the size (in bytes) of the audio buffer. Larger values may result\n" + "in slightly better performance at the cost of latency; the lowest\n" + "value will give the most responsive audio.\n" + "This option requires gpSP to be restarted before it will take effect.", + 10), +#endif + string_selection_option(NULL, "Status Display", enable_disable_options, + &status_display, 2, + "Display fps and some infomation.",12), + + submenu_option(NULL, "Back", "Return to the main menu.", 14) + }; + + make_menu(graphics_sound, submenu_graphics_sound, NULL); + + menu_option_type cheats_misc_options[] = + { + cheat_option(0), + cheat_option(1), + cheat_option(2), + cheat_option(3), + cheat_option(4), + cheat_option(5), + cheat_option(6), + cheat_option(7), + cheat_option(8), + cheat_option(9), + string_selection_option(NULL, "Update backup", + update_backup_options, &update_backup_flag, 2, + "Determines when in-game save files should be written back to\n" + "memstick. If set to 'automatic' writebacks will occur shortly after\n" + "the game's backup is altered. On 'exit only' it will only be written\n" + "back when you exit from this menu (NOT from using the home button).\n" + "Use the latter with extreme care.", 12), + submenu_option(NULL, "Back", "Return to the main menu.", 14) + }; + + make_menu(cheats_misc, submenu_cheats_misc, NULL); + + menu_option_type savestate_options[] = + { + numeric_selection_action_hide_option(menu_load_state, menu_change_state, + "Load savestate from current slot", &savestate_slot, 10, + "Select to load the game state from the current slot for this game.\n" + "Press left + right to change the current slot.", 6), + numeric_selection_action_hide_option(menu_save_state, menu_change_state, + "Save savestate to current slot", &savestate_slot, 10, + "Select to save the game state to the current slot for this game.\n" + "Press left + right to change the current slot.", 7), + numeric_selection_action_hide_option(menu_load_state_file, + menu_change_state, + "Load savestate from file", &savestate_slot, 10, + "Restore gameplay from a savestate file.\n" + "Note: The same file used to save the state must be present.\n", 9), + numeric_selection_option(menu_change_state, + "Current savestate slot", &savestate_slot, 10, + "Change the current savestate slot.\n", 11), + submenu_option(NULL, "Back", "Return to the main menu.", 13) + }; + + make_menu(savestate, submenu_savestate, NULL); + +#ifndef ZAURUS + menu_option_type gamepad_config_options[] = + { + gamepad_config_option("D-pad up ", 0), + gamepad_config_option("D-pad down ", 1), + gamepad_config_option("D-pad left ", 2), + gamepad_config_option("D-pad right ", 3), + gamepad_config_option("Circle ", 4), + gamepad_config_option("Cross ", 5), + gamepad_config_option("Square ", 6), + gamepad_config_option("Triangle ", 7), + gamepad_config_option("Left Trigger ", 8), + gamepad_config_option("Right Trigger", 9), + gamepad_config_option("Start ", 10), + gamepad_config_option("Select ", 11), + submenu_option(NULL, "Back", "Return to the main menu.", 13) + }; + + make_menu(gamepad_config, submenu_gamepad, NULL); + + menu_option_type analog_config_options[] = + { + analog_config_option("Analog up ", 0), + analog_config_option("Analog down ", 1), + analog_config_option("Analog left ", 2), + analog_config_option("Analog right", 3), + string_selection_option(NULL, "Enable analog", yes_no_options, + &global_enable_analog, 2, + "Select 'no' to block analog input entirely.", 7), + numeric_selection_option(NULL, "Analog sensitivity", + &analog_sensitivity_level, 10, + "Determine sensitivity/responsiveness of the analog input.\n" + "Lower numbers are less sensitive.", 8), + submenu_option(NULL, "Back", "Return to the main menu.", 11) + }; + + make_menu(analog_config, submenu_analog, NULL); +#endif + + menu_option_type main_options[] = + { + submenu_option(&graphics_sound_menu, "Graphics and Sound options", + "Select to set display parameters and frameskip behavior,\n" + "audio on/off, audio buffer size, and audio filtering.", 0), + numeric_selection_action_option(menu_load_state, NULL, + "Load state from slot", &savestate_slot, 10, + "Select to load the game state from the current slot for this game,\n" + "if it exists (see the extended menu for more information)\n" + "Press left + right to change the current slot.", 2), + numeric_selection_action_option(menu_save_state, NULL, + "Save state to slot", &savestate_slot, 10, + "Select to save the game state to the current slot for this game.\n" + "See the extended menu for more information.\n" + "Press left + right to change the current slot.", 3), + submenu_option(&savestate_menu, "Savestate options", + "Select to enter a menu for loading, saving, and viewing the\n" + "currently active savestate for this game (or to load a savestate\n" + "file from another game)", 4), +#ifndef ZAURUS + submenu_option(&gamepad_config_menu, "Configure gamepad input", + "Select to change the in-game behavior of the PSP buttons and d-pad.", + 6), + submenu_option(&analog_config_menu, "Configure analog input", + "Select to change the in-game behavior of the PSP analog nub.", 7), +#endif + submenu_option(&cheats_misc_menu, "Cheats and Miscellaneous options", + "Select to manage cheats, set backup behavior, and set device clock\n" + "speed.", 9), + action_option(menu_restart, NULL, "Restart game", + "Select to reset the GBA with the current game loaded.", 11), + action_option(menu_exit, NULL, "Return to game", + "Select to exit this menu and resume gameplay.", 12), + action_option(menu_quit, NULL, "Exit gpSP", + "Select to exit gpSP and return to the PSP XMB/loader.", 14) + }; + + make_menu(main, submenu_main, NULL); + + void choose_menu(menu_type *new_menu) + { + if(new_menu == NULL) + new_menu = &main_menu; + + clear_screen(COLOR_BG); + + blit_to_screen(original_screen, 240, 160, 230, 40); + + current_menu = new_menu; + current_option = new_menu->options; + current_option_num = 0; + if(current_menu->init_function) + current_menu->init_function(); + } + + void clear_help() + { +#ifndef ZAURUS + for(i = 0; i < 6; i++) + { + print_string_pad(" ", COLOR_BG, COLOR_BG, 30, 210 + (i * 10), 70); + } +#endif + } + + video_resolution_large(); + + SDL_LockMutex(sound_mutex); + SDL_PauseAudio(1); + SDL_UnlockMutex(sound_mutex); + + if(gamepak_filename[0] == 0) + { + first_load = 1; + memset(original_screen, 0x00, 240 * 160 * 2); + print_string_ext("No game loaded yet.", 0xFFFF, 0x0000, + 60, 75,original_screen, 240, 0); + } + + choose_menu(&main_menu); + + for(i = 0; i < 10; i++) + { + if(i >= num_cheats) + { + sprintf(cheat_format_str[i], "cheat %d (none loaded)", i); + } + else + { + sprintf(cheat_format_str[i], "cheat %d (%s): %%s", i, + cheats[i].cheat_name); + } + } + + current_menu->init_function(); + + while(repeat) + { + display_option = current_menu->options; + + for(i = 0; i < current_menu->num_options; i++, display_option++) + { + if(display_option->option_type & NUMBER_SELECTION_OPTION) + { + sprintf(line_buffer, display_option->display_string, + *(display_option->current_option)); + } + else + + if(display_option->option_type & STRING_SELECTION_OPTION && strchr(display_option->display_string, '%')) + { + sprintf(line_buffer, display_option->display_string, + ((u32 *)display_option->options)[*(display_option->current_option)]); + } + else + { + strcpy(line_buffer, display_option->display_string); + } + + if(display_option == current_option) + { + print_string_pad(line_buffer, COLOR_ACTIVE_ITEM, COLOR_BG, 10, + (display_option->line_number * 10) + 40, 36); + } + else + { + print_string_pad(line_buffer, COLOR_INACTIVE_ITEM, COLOR_BG, 10, + (display_option->line_number * 10) + 40, 36); + } + } +#ifndef ZAURUS + print_string(current_option->help_string, COLOR_HELP_TEXT, + COLOR_BG, 30, 210); +#endif + flip_screen(); + + gui_action = get_gui_input(); + + switch(gui_action) + { + case CURSOR_DOWN: + current_option_num = (current_option_num + 1) % + current_menu->num_options; + + current_option = current_menu->options + current_option_num; + clear_help(); + break; + + case CURSOR_UP: + if(current_option_num) + current_option_num--; + else + current_option_num = current_menu->num_options - 1; + + current_option = current_menu->options + current_option_num; + clear_help(); + break; + + case CURSOR_RIGHT: + if(current_option->option_type & (NUMBER_SELECTION_OPTION | + STRING_SELECTION_OPTION)) + { + *(current_option->current_option) = + (*current_option->current_option + 1) % + current_option->num_options; + + if(current_option->passive_function) + current_option->passive_function(); + } + break; + + case CURSOR_LEFT: + if(current_option->option_type & (NUMBER_SELECTION_OPTION | + STRING_SELECTION_OPTION)) + { + u32 current_option_val = *(current_option->current_option); + + if(current_option_val) + current_option_val--; + else + current_option_val = current_option->num_options - 1; + + *(current_option->current_option) = current_option_val; + + if(current_option->passive_function) + current_option->passive_function(); + } + break; + + case CURSOR_EXIT: + if(current_menu == &main_menu) + menu_exit(); + + choose_menu(&main_menu); + break; + + case CURSOR_SELECT: + if(current_option->option_type & ACTION_OPTION) + current_option->action_function(); + + if(current_option->option_type & SUBMENU_OPTION) + choose_menu(current_option->sub_menu); + break; + } + } + + set_gba_resolution(screen_scale); + video_resolution_small(); + + global_cycles_per_instruction = clock_speed_number + 1; + + SDL_PauseAudio(0); + + clear_screen(0); + flip_screen(); + flip_screen(); + + return return_value; +} diff --git a/source/gcw0/main.c b/source/gcw0/main.c new file mode 100644 index 0000000..8bfea80 --- /dev/null +++ b/source/gcw0/main.c @@ -0,0 +1,609 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" +#include <sys/time.h> +#include <stdlib.h> + +extern SDL_Surface *screen; + +timer_type timer[4]; + +frameskip_type current_frameskip_type = auto_frameskip; +u32 frameskip_value = 4; +u32 random_skip = 0; +u32 global_cycles_per_instruction = 3; +u32 skip_next_frame = 0; + +u32 frameskip_counter = 0; + +u32 cpu_ticks = 0; +u32 frame_ticks = 0; + +u32 execute_cycles = 960; +s32 video_count = 960; +u32 ticks; + +u32 arm_frame = 0; +u32 thumb_frame = 0; +u32 last_frame = 0; + +u32 cycle_memory_access = 0; +u32 cycle_pc_relative_access = 0; +u32 cycle_sp_relative_access = 0; +u32 cycle_block_memory_access = 0; +u32 cycle_block_memory_sp_access = 0; +u32 cycle_block_memory_words = 0; +u32 cycle_dma16_words = 0; +u32 cycle_dma32_words = 0; +u32 flush_ram_count = 0; +u32 gbc_update_count = 0; +u32 oam_update_count = 0; + +u32 synchronize_flag = 1; + +u32 update_backup_flag = 1; +u32 clock_speed = 333; +u8 main_path[512]; + +#define check_count(count_var) \ + if(count_var < execute_cycles) \ + execute_cycles = count_var; \ + +#define check_timer(timer_number) \ + if(timer[timer_number].status == TIMER_PRESCALE) \ + check_count(timer[timer_number].count); \ + +#define update_timer(timer_number) \ + if(timer[timer_number].status != TIMER_INACTIVE) \ + { \ + if(timer[timer_number].status != TIMER_CASCADE) \ + { \ + timer[timer_number].count -= execute_cycles; \ + io_registers[REG_TM##timer_number##D] = \ + -(timer[timer_number].count >> timer[timer_number].prescale); \ + } \ + \ + if(timer[timer_number].count <= 0) \ + { \ + if(timer[timer_number].irq == TIMER_TRIGGER_IRQ) \ + irq_raised |= IRQ_TIMER##timer_number; \ + \ + if((timer_number != 3) && \ + (timer[timer_number + 1].status == TIMER_CASCADE)) \ + { \ + timer[timer_number + 1].count--; \ + io_registers[REG_TM0D + (timer_number + 1) * 2] = \ + -(timer[timer_number + 1].count); \ + } \ + \ + if(timer_number < 2) \ + { \ + if(timer[timer_number].direct_sound_channels & 0x01) \ + sound_timer(timer[timer_number].frequency_step, 0); \ + \ + if(timer[timer_number].direct_sound_channels & 0x02) \ + sound_timer(timer[timer_number].frequency_step, 1); \ + } \ + \ + timer[timer_number].count += \ + (timer[timer_number].reload << timer[timer_number].prescale); \ + } \ + } \ + +u8 *file_ext[] = { ".gba", ".bin", ".zip", NULL }; + +static u8 caches_inited = 0; + +void init_main() +{ + u32 i; + + skip_next_frame = 0; + + for(i = 0; i < 4; i++) + { + dma[i].start_type = DMA_INACTIVE; + dma[i].direct_sound_channel = DMA_NO_DIRECT_SOUND; + timer[i].status = TIMER_INACTIVE; + timer[i].reload = 0x10000; + timer[i].stop_cpu_ticks = 0; + } + + timer[0].direct_sound_channels = TIMER_DS_CHANNEL_BOTH; + timer[1].direct_sound_channels = TIMER_DS_CHANNEL_NONE; + + cpu_ticks = 0; + frame_ticks = 0; + + execute_cycles = 960; + video_count = 960; + + if (!caches_inited) + { + flush_translation_cache(TRANSLATION_REGION_READONLY, FLUSH_REASON_INITIALIZING); + flush_translation_cache(TRANSLATION_REGION_WRITABLE, FLUSH_REASON_INITIALIZING); + } + else + { + flush_translation_cache(TRANSLATION_REGION_READONLY, FLUSH_REASON_LOADING_ROM); + clear_metadata_area(METADATA_AREA_EWRAM, CLEAR_REASON_LOADING_ROM); + clear_metadata_area(METADATA_AREA_IWRAM, CLEAR_REASON_LOADING_ROM); + clear_metadata_area(METADATA_AREA_VRAM, CLEAR_REASON_LOADING_ROM); + } + + caches_inited = 1; + + StatsInitGame(); +} + +int main(int argc, char *argv[]) +{ + u32 i; + u32 vcount = 0; + u32 ticks; + u32 dispstat; + u8 load_filename[512]; + u8 bios_file[512]; + +#ifdef PSP_BUILD + sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, + vblank_interrupt_handler, NULL); + sceKernelEnableSubIntr(PSP_VBLANK_INT, 0); +#else +#ifndef ZAURUS + freopen("CON", "wb", stdout); +#endif +#endif + + init_gamepak_buffer(); + + // Copy the directory path of the executable into main_path + sprintf(main_path, "%s/.gpsp", getenv("HOME")); + mkdir(main_path, 0755); + load_config_file(); + + gamepak_filename[0] = 0; + + sprintf(bios_file, "%s/gba_bios.bin", main_path); + if(load_bios(bios_file) == -1) + { + printf("Sorry, but ReGBA requires a Gameboy Advance BIOS image to run\n"); + printf("correctly. Make sure to get an authentic one (search the web,\n"); + printf("beg other people if you want, but don't hold me accountable\n"); + printf("if you get hated or banned for it), it'll be exactly 16384\n"); + printf("bytes large and should have the following md5sum value:\n\n"); + printf("a860e8c0b6d573d191e4ec7db1b1e4f6\n\n"); + printf("Other BIOS files might work either partially completely, I\n"); + printf("really don't know.\n\n"); + printf("When you do get it name it gba_bios.bin and put it in\n"); + printf("/boot/local/home/.gpsp.\n\n"); + printf("Good luck.\n"); + + quit(); + } + + init_main(); + + video_resolution_large(); + + if(argc > 1) + { + if(load_gamepak(argv[1]) == -1) + { + printf("Failed to load gamepak %s, exiting.\n", load_filename); + quit(); + } + + init_video(); + init_sound(); + init_input(); + + set_gba_resolution(screen_scale); + video_resolution_small(); + + init_cpu(); + init_memory(); + } + else + { + init_video(); + init_sound(); + init_input(); + + if(load_file(file_ext, load_filename) == -1) + { + ReGBA_Menu(REGBA_MENU_ENTRY_REASON_NO_ROM); + } + else + { + if(load_gamepak(load_filename) == -1) + { + printf("Failed to load gamepak %s, exiting.\n", load_filename); + delay_us(5000000); + quit(); + } + + set_gba_resolution(screen_scale); + video_resolution_small(); + clear_screen(0); + flip_screen(); + init_cpu(); + init_memory(); + } + } + + last_frame = 0; + + // We'll never actually return from here. + + execute_arm_translate(execute_cycles); + return 0; +} + +u32 update_gba() +{ + irq_type irq_raised = IRQ_NONE; + do + { + cpu_ticks += execute_cycles; + reg[CHANGED_PC_STATUS] = 0; + + if(gbc_sound_update) + { + gbc_update_count++; + update_gbc_sound(cpu_ticks); + gbc_sound_update = 0; + } + + update_timer(0); + update_timer(1); + update_timer(2); + update_timer(3); + + video_count -= execute_cycles; + + if(video_count <= 0) + { + u32 vcount = io_registers[REG_VCOUNT]; + u32 dispstat = io_registers[REG_DISPSTAT]; + + if((dispstat & 0x02) == 0) + { + // Transition from hrefresh to hblank + video_count += (272); + dispstat |= 0x02; + + if((dispstat & 0x01) == 0) + { + u32 i; + if(oam_update) + oam_update_count++; + + if(no_alpha) + io_registers[REG_BLDCNT] = 0; + + update_scanline(); + + // If in visible area also fire HDMA + for(i = 0; i < 4; i++) + { + if(dma[i].start_type == DMA_START_HBLANK) + dma_transfer(dma + i); + } + } + + if(dispstat & 0x10) + irq_raised |= IRQ_HBLANK; + } + else + { + // Transition from hblank to next line + video_count += 960; + dispstat &= ~0x02; + + vcount++; + + if(vcount == 160) + { + // Transition from vrefresh to vblank + u32 i; + + dispstat |= 0x01; + if(dispstat & 0x8) + { + irq_raised |= IRQ_VBLANK; + } + + affine_reference_x[0] = + (s32)(address32(io_registers, 0x28) << 4) >> 4; + affine_reference_y[0] = + (s32)(address32(io_registers, 0x2C) << 4) >> 4; + affine_reference_x[1] = + (s32)(address32(io_registers, 0x38) << 4) >> 4; + affine_reference_y[1] = + (s32)(address32(io_registers, 0x3C) << 4) >> 4; + + for(i = 0; i < 4; i++) + { + if(dma[i].start_type == DMA_START_VBLANK) + dma_transfer(dma + i); + } + } + else + + if(vcount == 228) + { + // Transition from vblank to next screen + dispstat &= ~0x01; + frame_ticks++; + + #if 0 + printf("frame update (%x), %d instructions total, %d RAM flushes\n", + reg[REG_PC], instruction_count - last_frame, flush_ram_count); + last_frame = instruction_count; + +/* printf("%d gbc audio updates\n", gbc_update_count); + printf("%d oam updates\n", oam_update_count); */ + gbc_update_count = 0; + oam_update_count = 0; + flush_ram_count = 0; + #endif + + if(update_input()) + continue; + + update_gbc_sound(cpu_ticks); + synchronize(); + + update_screen(); + + if(update_backup_flag) + update_backup(); + + process_cheats(); + + vcount = 0; + } + + if(vcount == (dispstat >> 8)) + { + // vcount trigger + dispstat |= 0x04; + if(dispstat & 0x20) + { + irq_raised |= IRQ_VCOUNT; + } + } + else + { + dispstat &= ~0x04; + } + + io_registers[REG_VCOUNT] = vcount; + } + io_registers[REG_DISPSTAT] = dispstat; + } + + if(irq_raised) + raise_interrupt(irq_raised); + + execute_cycles = video_count; + + check_timer(0); + check_timer(1); + check_timer(2); + check_timer(3); + } while(reg[CPU_HALT_STATE] != CPU_ACTIVE); + return execute_cycles; +} + +u64 last_screen_timestamp = 0; +u32 frame_speed = 15000; + +u32 ticks_needed_total = 0; +float us_needed = 0.0; +u32 frames = 0; +u32 skipped_num_frame = 60; +const u32 frame_interval = 60; +u32 skipped_num = 0; + +void synchronize() +{ + u64 new_ticks; + char char_buffer[64]; + u64 time_delta = 16667; + u32 fpsw = 60; + + get_ticks_us(&new_ticks); + time_delta = new_ticks - last_screen_timestamp; + last_screen_timestamp = new_ticks; + ticks_needed_total += time_delta; + + skip_next_frame = 0; + if((time_delta < frame_speed) && synchronize_flag) + { + delay_us(frame_speed - time_delta); + } + frames++; + + if(frames == 60) + { + if(status_display) { + us_needed = (float)ticks_needed_total / frame_interval; + fpsw = (u32)(1000000.0 / us_needed); + ticks_needed_total = 0; + if(current_frameskip_type == manual_frameskip) { + sprintf(char_buffer, "%s%3dfps %s:%d slot:%d ", synchronize_flag?" ":">>", fpsw, + current_frameskip_type==auto_frameskip?"Auto":current_frameskip_type==manual_frameskip?"Manu":"Off ", + frameskip_value, savestate_slot); + } else { + sprintf(char_buffer, "%s%3dfps %s:%d slot:%d ", synchronize_flag?" ":">>", + (skipped_num_frame==60&&fpsw>60)?fpsw:skipped_num_frame, + current_frameskip_type==auto_frameskip?"Auto":current_frameskip_type==manual_frameskip?"Manu":"Off ", + frameskip_value, savestate_slot); + } + } else + strcpy(char_buffer, " "); + print_string(char_buffer, 0xFFFF, 0x000, 40, 30); + print_string(ssmsg, 0xF000, 0x000, 180, 30); + strcpy(ssmsg, " "); + frames = 0; + skipped_num_frame = 60; + } + + if(current_frameskip_type == manual_frameskip) + { + frameskip_counter = (frameskip_counter + 1) % + (frameskip_value + 1); + if(random_skip) + { + if(frameskip_counter != (rand() % (frameskip_value + 1))) + skip_next_frame = 1; + } + else + { + if(frameskip_counter) + skip_next_frame = 1; + } + } else if(current_frameskip_type == auto_frameskip) { + static struct timeval next1 = {0, 0}; + static struct timeval now; + + gettimeofday(&now, NULL); + if(next1.tv_sec == 0) { + next1 = now; + next1.tv_usec++; + } + if(timercmp(&next1, &now, >)) { + //struct timeval tdiff; + if(synchronize_flag) + do { + synchronize_sound(); + gettimeofday(&now, NULL); + } while (timercmp(&next1, &now, >)); + else + gettimeofday(&now, NULL); + //timersub(&next1, &now, &tdiff); + //usleep(tdiff.tv_usec/2); + //gettimeofday(&now, NULL); + skipped_num = 0; + next1 = now; + } else { + if(skipped_num < frameskip_value) { + skipped_num++; + skipped_num_frame--; + skip_next_frame = 1; + } else { + //synchronize_sound(); + skipped_num = 0; + next1 = now; + } + } + next1.tv_usec += 16667; + if(next1.tv_usec >= 1000000) { + next1.tv_sec++; + next1.tv_usec -= 1000000; + } + } +} + +void quit() +{ + if(!update_backup_flag) + update_backup_force(); + + sound_exit(); + + SDL_Quit(); + exit(0); +} + +void reset_gba() +{ + init_main(); + init_memory(); + init_cpu(); + reset_sound(); +} + +u32 FILE_LENGTH(FILE_TAG_TYPE fp) +{ + u32 length; + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + return length; +} + +void delay_us(u32 us_count) +{ + SDL_Delay(us_count / 1000); // for dingux + // sleep(0); +} + +void get_ticks_us(u64 *ticks_return) +{ + *ticks_return = (SDL_GetTicks() * 1000); +} + +void change_ext(u8 *src, u8 *buffer, u8 *extension) +{ + u8 *position; + + strcpy(buffer, main_path); + strcat(buffer, "/"); + + position = strrchr(src, '/'); + if (position) + src = position+1; + + strcat(buffer, src); + position = strrchr(buffer, '.'); + + if(position) + strcpy(position, extension); +} + +// type = READ / WRITE_MEM +#define MAIN_SAVESTATE_BODY(type) \ +{ \ + FILE_##type##_VARIABLE(g_state_buffer_ptr, cpu_ticks); \ + FILE_##type##_VARIABLE(g_state_buffer_ptr, execute_cycles); \ + FILE_##type##_VARIABLE(g_state_buffer_ptr, video_count); \ + FILE_##type##_ARRAY(g_state_buffer_ptr, timer); \ +} \ + +void main_read_mem_savestate() +MAIN_SAVESTATE_BODY(READ_MEM); + +void main_write_mem_savestate() +MAIN_SAVESTATE_BODY(WRITE_MEM); + +void print_out(u32 address, u32 pc) +{ + char buffer[256]; + sprintf(buffer, "patching from gp8 %x", address); + print_string(buffer, 0xFFFF, 0x0000, 0, 0); + update_screen(); + delay_us(5000000); +} + diff --git a/source/gcw0/main.h b/source/gcw0/main.h new file mode 100644 index 0000000..aad69c3 --- /dev/null +++ b/source/gcw0/main.h @@ -0,0 +1,123 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_H +#define MAIN_H + +typedef enum +{ + TIMER_INACTIVE, + TIMER_PRESCALE, + TIMER_CASCADE +} timer_status_type; + +typedef enum +{ + TIMER_NO_IRQ, + TIMER_TRIGGER_IRQ +} timer_irq_type; + + +typedef enum +{ + TIMER_DS_CHANNEL_NONE, + TIMER_DS_CHANNEL_A, + TIMER_DS_CHANNEL_B, + TIMER_DS_CHANNEL_BOTH +} timer_ds_channel_type; + +typedef struct +{ + s32 count; + u32 reload; + u32 prescale; + u32 stop_cpu_ticks; + FIXED16_16 frequency_step; + timer_ds_channel_type direct_sound_channels; + timer_irq_type irq; + timer_status_type status; +} timer_type; + +typedef enum +{ + auto_frameskip, + manual_frameskip, + no_frameskip +} frameskip_type; + +extern u32 cpu_ticks; +extern u32 frame_ticks; +extern u32 execute_cycles; +extern frameskip_type current_frameskip_type; +extern u32 frameskip_value; +extern u32 random_skip; +extern u32 global_cycles_per_instruction; +extern u32 synchronize_flag; +extern u32 skip_next_frame; + +extern timer_type timer[4]; +static u32 prescale_table[] = { 0, 6, 8, 10 }; + +extern u32 cycle_memory_access; +extern u32 cycle_pc_relative_access; +extern u32 cycle_sp_relative_access; +extern u32 cycle_block_memory_access; +extern u32 cycle_block_memory_sp_access; +extern u32 cycle_block_memory_words; +extern u32 cycle_dma16_words; +extern u32 cycle_dma32_words; +extern u32 flush_ram_count; + +extern u64 base_timestamp; + +extern u8 main_path[512]; + +extern u32 update_backup_flag; +extern u32 clock_speed; + +u32 update_gba(); +void reset_gba(); +void synchronize(); +void quit(); +void delay_us(u32 us_count); +void get_ticks_us(u64 *tick_return); +void game_name_ext(u8 *src, u8 *buffer, u8 *extension); +void main_write_mem_savestate(FILE_TAG_TYPE savestate_file); +void main_read_mem_savestate(FILE_TAG_TYPE savestate_file); + +#define count_timer(timer_number) \ + timer[timer_number].reload = 0x10000 - value; \ + if(timer_number < 2) \ + { \ + u32 timer_reload = \ + timer[timer_number].reload << timer[timer_number].prescale; \ + sound_update_frequency_step(timer_number); \ + } \ + +#define adjust_sound_buffer(timer_number, channel) \ + if(timer[timer_number].direct_sound_channels & (0x01 << channel)) \ + { \ + direct_sound_channel[channel].buffer_index = \ + (direct_sound_channel[channel].buffer_index + buffer_adjust) % \ + BUFFER_SIZE; \ + } \ + +#endif + + diff --git a/source/gcw0/port.h b/source/gcw0/port.h new file mode 100644 index 0000000..5bb04b0 --- /dev/null +++ b/source/gcw0/port.h @@ -0,0 +1,49 @@ +#ifndef _PORT_H_ +#define _PORT_H_ + +#include <SDL/SDL.h> + +/* Tuning parameters for the GCW Zero version of gpSP */ +/* Its processor is an Ingenic JZ4770 at 1000 MHz with 256..512 MiB of RAM */ +#define READONLY_CODE_CACHE_SIZE (4 * 1024 * 1024) +#define WRITABLE_CODE_CACHE_SIZE (4 * 1024 * 1024) +/* The following parameter needs to be at least enough bytes to hold + * the generated code for the largest instruction on your platform. + * In most cases, that will be the ARM instruction + * STMDB R0!, {R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14,R15} */ +#define TRANSLATION_CACHE_LIMIT_THRESHOLD (1024) + +#define FILE_OPEN_APPEND ("a+") + +#define FILE_OPEN_READ ("rb") + +#define FILE_OPEN_WRITE ("wb") + +#define FILE_OPEN(filename_tag, filename, mode) \ + filename_tag = fopen(filename, FILE_OPEN_##mode) \ + +#define FILE_CHECK_VALID(filename_tag) \ + (filename_tag != FILE_TAG_INVALID) \ + +#define FILE_TAG_INVALID \ + (NULL) \ + +#define FILE_CLOSE(filename_tag) \ + fclose(filename_tag) \ + +#define FILE_DELETE(filename) \ + unlink(filename) \ + +#define FILE_READ(filename_tag, buffer, size) \ + fread(buffer, 1, size, filename_tag) \ + +#define FILE_WRITE(filename_tag, buffer, size) \ + fwrite(buffer, 1, size, filename_tag) \ + +#define FILE_SEEK(filename_tag, offset, type) \ + fseek(filename_tag, offset, type) \ + +#define FILE_TELL(filename_tag) \ + ftell(filename_tag) \ + +#endif
\ No newline at end of file diff --git a/source/psp/gui.c b/source/psp/gui.c new file mode 100644 index 0000000..c135cb4 --- /dev/null +++ b/source/psp/gui.c @@ -0,0 +1,293 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" +#include "font.h" + +#include <pspctrl.h> + +#include <pspkernel.h> +#include <pspdebug.h> +#include <pspdisplay.h> + +#include <pspgu.h> +#include <psppower.h> +#include <psprtc.h> + +static float *screen_vertex = (float *)0x441FC100; +static u32 *ge_cmd = (u32 *)0x441FC000; +static u16 *psp_gu_vram_base = (u16 *)(0x44000000); +static u32 *ge_cmd_ptr = (u32 *)0x441FC000; +static u32 gecbid; +static u32 video_direct = 0; + +static u32 __attribute__((aligned(16))) display_list[32]; + +#define GBA_SCREEN_WIDTH 240 +#define GBA_SCREEN_HEIGHT 160 + +#define PSP_SCREEN_WIDTH 480 +#define PSP_SCREEN_HEIGHT 272 +#define PSP_LINE_SIZE 512 + +#define PSP_ALL_BUTTON_MASK 0xFFFF + +#define GE_CMD_FBP 0x9C +#define GE_CMD_FBW 0x9D +#define GE_CMD_TBP0 0xA0 +#define GE_CMD_TBW0 0xA8 +#define GE_CMD_TSIZE0 0xB8 +#define GE_CMD_TFLUSH 0xCB +#define GE_CMD_CLEAR 0xD3 +#define GE_CMD_VTYPE 0x12 +#define GE_CMD_BASE 0x10 +#define GE_CMD_VADDR 0x01 +#define GE_CMD_IADDR 0x02 +#define GE_CMD_PRIM 0x04 +#define GE_CMD_FINISH 0x0F +#define GE_CMD_SIGNAL 0x0C +#define GE_CMD_NOP 0x00 + +#define GE_CMD(cmd, operand) \ + *ge_cmd_ptr = (((GE_CMD_##cmd) << 24) | (operand)); \ + ge_cmd_ptr++ \ + +static u16 *screen_texture = (u16 *)(0x4000000 + (512 * 272 * 2)); +static u16 *current_screen_texture = (u16 *)(0x4000000 + (512 * 272 * 2)); +static u16 *GBAScreen = (u16 *)(0x4000000 + (512 * 272 * 2)); +static u32 GBAScreenPitch = 240; + +static void Ge_Finish_Callback(int id, void *arg) +{ +} + +void init_video() +{ + sceDisplaySetMode(0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); + + sceDisplayWaitVblankStart(); + sceDisplaySetFrameBuf((void*)psp_gu_vram_base, PSP_LINE_SIZE, + PSP_DISPLAY_PIXEL_FORMAT_565, PSP_DISPLAY_SETBUF_NEXTFRAME); + + sceGuInit(); + + sceGuStart(GU_DIRECT, display_list); + sceGuDrawBuffer(GU_PSM_5650, (void*)0, PSP_LINE_SIZE); + sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, + (void*)0, PSP_LINE_SIZE); + sceGuClear(GU_COLOR_BUFFER_BIT); + + sceGuOffset(2048 - (PSP_SCREEN_WIDTH / 2), 2048 - (PSP_SCREEN_HEIGHT / 2)); + sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); + + sceGuScissor(0, 0, PSP_SCREEN_WIDTH + 1, PSP_SCREEN_HEIGHT + 1); + sceGuEnable(GU_SCISSOR_TEST); + sceGuTexMode(GU_PSM_5650, 0, 0, GU_FALSE); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + sceGuEnable(GU_TEXTURE_2D); + + sceGuFrontFace(GU_CW); + sceGuDisable(GU_BLEND); + + sceGuFinish(); + sceGuSync(0, 0); + + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); + + PspGeCallbackData gecb; + gecb.signal_func = NULL; + gecb.signal_arg = NULL; + gecb.finish_func = Ge_Finish_Callback; + gecb.finish_arg = NULL; + gecbid = sceGeSetCallback(&gecb); + + screen_vertex[0] = 0 + 0.5; + screen_vertex[1] = 0 + 0.5; + screen_vertex[2] = 0 + 0.5; + screen_vertex[3] = 0 + 0.5; + screen_vertex[4] = 0; + screen_vertex[5] = GBA_SCREEN_WIDTH - 0.5; + screen_vertex[6] = GBA_SCREEN_HEIGHT - 0.5; + screen_vertex[7] = PSP_SCREEN_WIDTH - 0.5; + screen_vertex[8] = PSP_SCREEN_HEIGHT - 0.5; + screen_vertex[9] = 0; + + // Set framebuffer to PSP VRAM + GE_CMD(FBP, ((u32)psp_gu_vram_base & 0x00FFFFFF)); + GE_CMD(FBW, (((u32)psp_gu_vram_base & 0xFF000000) >> 8) | PSP_LINE_SIZE); + // Set texture 0 to the screen texture + GE_CMD(TBP0, ((u32)screen_texture & 0x00FFFFFF)); + GE_CMD(TBW0, (((u32)screen_texture & 0xFF000000) >> 8) | GBA_SCREEN_WIDTH); + // Set the texture size to 256 by 256 (2^8 by 2^8) + GE_CMD(TSIZE0, (8 << 8) | 8); + // Flush the texture cache + GE_CMD(TFLUSH, 0); + // Use 2D coordinates, no indeces, no weights, 32bit float positions, + // 32bit float texture coordinates + GE_CMD(VTYPE, (1 << 23) | (0 << 11) | (0 << 9) | + (3 << 7) | (0 << 5) | (0 << 2) | 3); + // Set the base of the index list pointer to 0 + GE_CMD(BASE, 0); + // Set the rest of index list pointer to 0 (not being used) + GE_CMD(IADDR, 0); + // Set the base of the screen vertex list pointer + GE_CMD(BASE, ((u32)screen_vertex & 0xFF000000) >> 8); + // Set the rest of the screen vertex list pointer + GE_CMD(VADDR, ((u32)screen_vertex & 0x00FFFFFF)); + // Primitive kick: render sprite (primitive 6), 2 vertices + GE_CMD(PRIM, (6 << 16) | 2); + // Done with commands + GE_CMD(FINISH, 0); + // Raise signal interrupt + GE_CMD(SIGNAL, 0); + GE_CMD(NOP, 0); + GE_CMD(NOP, 0); +} + +u32 screen_flip = 0; + +void ReGBA_RenderScreen() +{ + if(video_direct == 0) + { + u32 *old_ge_cmd_ptr = ge_cmd_ptr; + sceKernelDcacheWritebackAll(); + + // Render the current screen + ge_cmd_ptr = ge_cmd + 2; + GE_CMD(TBP0, ((u32) GBAScreen & 0x00FFFFFF)); + GE_CMD(TBW0, (((u32) GBAScreen & 0xFF000000) >> 8) | + GBA_SCREEN_WIDTH); + ge_cmd_ptr = old_ge_cmd_ptr; + + sceGeListEnQueue(ge_cmd, ge_cmd_ptr, gecbid, NULL); + + // Flip to the next screen + screen_flip ^= 1; + + if(screen_flip) + GBAScreen = screen_texture + (240 * 160 * 2); + else + GBAScreen = screen_texture; + } +} + +void video_resolution_large() +{ + if(video_direct != 1) + { + video_direct = 1; + screen_pixels = psp_gu_vram_base; + screen_pitch = 512; + sceGuStart(GU_DIRECT, display_list); + sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, + (void*)0, PSP_LINE_SIZE); + sceGuFinish(); + } +} + +void set_gba_resolution(video_scale_type scale) +{ + u32 filter_linear = 0; + screen_scale = scale; + switch(scale) + { + case unscaled: + screen_vertex[2] = 120 + 0.5; + screen_vertex[3] = 56 + 0.5; + screen_vertex[7] = GBA_SCREEN_WIDTH + 120 - 0.5; + screen_vertex[8] = GBA_SCREEN_HEIGHT + 56 - 0.5; + break; + + case scaled_aspect: + screen_vertex[2] = 36 + 0.5; + screen_vertex[3] = 0 + 0.5; + screen_vertex[7] = 408 + 36 - 0.5; + screen_vertex[8] = PSP_SCREEN_HEIGHT - 0.5; + break; + + case fullscreen: + screen_vertex[2] = 0; + screen_vertex[3] = 0; + screen_vertex[7] = PSP_SCREEN_WIDTH; + screen_vertex[8] = PSP_SCREEN_HEIGHT; + break; + } + + sceGuStart(GU_DIRECT, display_list); + if(screen_filter == filter_bilinear) + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + else + sceGuTexFilter(GU_NEAREST, GU_NEAREST); + + sceGuFinish(); + sceGuSync(0, 0); + + clear_screen(0x0000); +} + +void video_resolution_small() +{ + if(video_direct != 0) + { + set_gba_resolution(screen_scale); + video_direct = 0; + screen_pixels = screen_texture; + screen_flip = 0; + screen_pitch = 240; + sceGuStart(GU_DIRECT, display_list); + sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, + (void*)0, PSP_LINE_SIZE); + sceGuFinish(); + } +} + +void clear_screen(u16 color) +{ + u32 i; + u16 *src_ptr = get_screen_pixels(); + + sceGuSync(0, 0); + + for(i = 0; i < (512 * 272); i++, src_ptr++) + { + *src_ptr = color; + } + + // I don't know why this doesn't work. +/* color = (((color & 0x1F) * 255 / 31) << 0) | + ((((color >> 5) & 0x3F) * 255 / 63) << 8) | + ((((color >> 11) & 0x1F) * 255 / 31) << 16) | (0xFF << 24); + + sceGuStart(GU_DIRECT, display_list); + sceGuDrawBuffer(GU_PSM_5650, (void*)0, PSP_LINE_SIZE); + //sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, + // (void*)0, PSP_LINE_SIZE); + sceGuClearColor(color); + sceGuClear(GU_COLOR_BUFFER_BIT); + sceGuFinish(); + sceGuSync(0, 0); */ +} + +void _flush_cache() +{ + sceKernelDcacheWritebackAll(); +} diff --git a/source/psp/main.c b/source/psp/main.c new file mode 100644 index 0000000..b820653 --- /dev/null +++ b/source/psp/main.c @@ -0,0 +1,778 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +//PSP_MODULE_INFO("gpSP", 0x1000, 0, 6); +//PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); + +#include "common.h" +#include <sys/time.h> +#include <stdlib.h> + +void vblank_interrupt_handler(u32 sub, u32 *parg); + +extern SDL_Surface *screen; + +timer_type timer[4]; + +frameskip_type current_frameskip_type = auto_frameskip; +u32 frameskip_value = 4; +u32 random_skip = 0; +u32 global_cycles_per_instruction = 3; +u32 skip_next_frame = 0; + +u32 frameskip_counter = 0; + +u32 cpu_ticks = 0; +u32 frame_ticks = 0; + +u32 execute_cycles = 960; +s32 video_count = 960; +u32 ticks; + +u32 arm_frame = 0; +u32 thumb_frame = 0; +u32 last_frame = 0; + +u32 cycle_memory_access = 0; +u32 cycle_pc_relative_access = 0; +u32 cycle_sp_relative_access = 0; +u32 cycle_block_memory_access = 0; +u32 cycle_block_memory_sp_access = 0; +u32 cycle_block_memory_words = 0; +u32 cycle_dma16_words = 0; +u32 cycle_dma32_words = 0; +u32 flush_ram_count = 0; +u32 gbc_update_count = 0; +u32 oam_update_count = 0; + +u32 synchronize_flag = 1; + +u32 update_backup_flag = 1; +u32 clock_speed = 333; +u8 main_path[512]; + +#define check_count(count_var) \ + if(count_var < execute_cycles) \ + execute_cycles = count_var; \ + +#define check_timer(timer_number) \ + if(timer[timer_number].status == TIMER_PRESCALE) \ + check_count(timer[timer_number].count); \ + +#define update_timer(timer_number) \ + if(timer[timer_number].status != TIMER_INACTIVE) \ + { \ + if(timer[timer_number].status != TIMER_CASCADE) \ + { \ + timer[timer_number].count -= execute_cycles; \ + io_registers[REG_TM##timer_number##D] = \ + -(timer[timer_number].count >> timer[timer_number].prescale); \ + } \ + \ + if(timer[timer_number].count <= 0) \ + { \ + if(timer[timer_number].irq == TIMER_TRIGGER_IRQ) \ + irq_raised |= IRQ_TIMER##timer_number; \ + \ + if((timer_number != 3) && \ + (timer[timer_number + 1].status == TIMER_CASCADE)) \ + { \ + timer[timer_number + 1].count--; \ + io_registers[REG_TM0D + (timer_number + 1) * 2] = \ + -(timer[timer_number + 1].count); \ + } \ + \ + if(timer_number < 2) \ + { \ + if(timer[timer_number].direct_sound_channels & 0x01) \ + sound_timer(timer[timer_number].frequency_step, 0); \ + \ + if(timer[timer_number].direct_sound_channels & 0x02) \ + sound_timer(timer[timer_number].frequency_step, 1); \ + } \ + \ + timer[timer_number].count += \ + (timer[timer_number].reload << timer[timer_number].prescale); \ + } \ + } \ + +u8 *file_ext[] = { ".gba", ".bin", ".zip", NULL }; + +void init_main() +{ + u32 i; + + skip_next_frame = 0; + + for(i = 0; i < 4; i++) + { + dma[i].start_type = DMA_INACTIVE; + dma[i].direct_sound_channel = DMA_NO_DIRECT_SOUND; + timer[i].status = TIMER_INACTIVE; + timer[i].reload = 0x10000; + timer[i].stop_cpu_ticks = 0; + } + + timer[0].direct_sound_channels = TIMER_DS_CHANNEL_BOTH; + timer[1].direct_sound_channels = TIMER_DS_CHANNEL_NONE; + + cpu_ticks = 0; + frame_ticks = 0; + + execute_cycles = 960; + video_count = 960; + + flush_translation_cache_rom(); + flush_translation_cache_ram(); + flush_translation_cache_bios(); +} + +int main(int argc, char *argv[]) +{ + u32 i; + u32 vcount = 0; + u32 ticks; + u32 dispstat; + u8 load_filename[512]; + u8 bios_file[512]; + +#ifdef PSP_BUILD + sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, + vblank_interrupt_handler, NULL); + sceKernelEnableSubIntr(PSP_VBLANK_INT, 0); +#else +#ifndef ZAURUS + freopen("CON", "wb", stdout); +#endif +#endif + + init_gamepak_buffer(); + + // Copy the directory path of the executable into main_path + sprintf(main_path, "%s/.gpsp", getenv("HOME")); + mkdir(main_path, 0755); + load_config_file(); + + gamepak_filename[0] = 0; + + sprintf(bios_file, "%s/gba_bios.bin", main_path); + if(load_bios(bios_file) == -1) + { +#ifdef PSP_BUILD + gui_action_type gui_action = CURSOR_NONE; + + printf("Sorry, but gpSP requires a Gameboy Advance BIOS image to run\n"); + printf("correctly. Make sure to get an authentic one (search the web,\n"); + printf("beg other people if you want, but don't hold me accountable\n"); + printf("if you get hated or banned for it), it'll be exactly 16384\n"); + printf("bytes large and should have the following md5sum value:\n\n"); + printf("a860e8c0b6d573d191e4ec7db1b1e4f6\n\n"); + printf("Other BIOS files might work either partially completely, I\n"); + printf("really don't know.\n\n"); + printf("When you do get it name it gba_bios.bin and put it in the\n"); + printf("same directory as this EBOOT.\n\n"); + printf("Good luck. Press any button to exit.\n"); + + while(gui_action == CURSOR_NONE) + { + gui_action = get_gui_input(); + delay_us(15000); + } + + quit(); +#endif +#ifdef ZAURUS + printf("Failed to load bios.\n"); + quit(); +#endif + } + +#ifdef PSP_BUILD + delay_us(2500000); +#endif + + init_main(); + + video_resolution_large(); + + if(argc > 1) + { + if(load_gamepak(argv[1]) == -1) + { + printf("Failed to load gamepak %s, exiting.\n", load_filename); + quit(); + } + + init_video(); + init_sound(); + init_input(); + + set_gba_resolution(screen_scale); + video_resolution_small(); + + init_cpu(); + init_memory(); + } + else + { + init_video(); + init_sound(); + init_input(); + + if(load_file(file_ext, load_filename) == -1) + { + menu(copy_screen()); + } + else + { + if(load_gamepak(load_filename) == -1) + { + printf("Failed to load gamepak %s, exiting.\n", load_filename); + delay_us(5000000); + quit(); + } + + set_gba_resolution(screen_scale); + video_resolution_small(); +#ifdef ZAURUS + clear_screen(0); + flip_screen(); +#endif + init_cpu(); + init_memory(); + } + } + + last_frame = 0; + + // We'll never actually return from here. + + execute_arm_translate(execute_cycles); + return 0; +} + +void print_memory_stats(u32 *counter, u32 *region_stats, u8 *stats_str) +{ + u32 other_region_counter = region_stats[0x1] + region_stats[0xE] + region_stats[0xF]; + u32 rom_region_counter = region_stats[0x8] + region_stats[0x9] + region_stats[0xA] + + region_stats[0xB] + region_stats[0xC] + region_stats[0xD]; + u32 _counter = *counter; + + printf("memory access stats: %s (out of %d)\n", stats_str, _counter); + printf("bios: %f%%\tiwram: %f%%\tewram: %f%%\tvram: %f\n", + region_stats[0x0] * 100.0 / _counter, region_stats[0x3] * 100.0 / _counter, + region_stats[0x2] * 100.0 / _counter, region_stats[0x6] * 100.0 / _counter); + + printf("oam: %f%%\tpalette: %f%%\trom: %f%%\tother: %f%%\n", + region_stats[0x7] * 100.0 / _counter, region_stats[0x5] * 100.0 / _counter, + rom_region_counter * 100.0 / _counter, other_region_counter * 100.0 / _counter); + + *counter = 0; + memset(region_stats, 0, sizeof(u32) * 16); +} + +u32 update_gba() +{ + irq_type irq_raised = IRQ_NONE; + do + { + cpu_ticks += execute_cycles; + reg[CHANGED_PC_STATUS] = 0; + + if(gbc_sound_update) + { + gbc_update_count++; + update_gbc_sound(cpu_ticks); + gbc_sound_update = 0; + } + + update_timer(0); + update_timer(1); + update_timer(2); + update_timer(3); + + video_count -= execute_cycles; + + if(video_count <= 0) + { + u32 vcount = io_registers[REG_VCOUNT]; + u32 dispstat = io_registers[REG_DISPSTAT]; + + if((dispstat & 0x02) == 0) + { + // Transition from hrefresh to hblank + video_count += (272); + dispstat |= 0x02; + + if((dispstat & 0x01) == 0) + { + u32 i; + if(oam_update) + oam_update_count++; + + if(no_alpha) + io_registers[REG_BLDCNT] = 0; + + update_scanline(); + + // If in visible area also fire HDMA + for(i = 0; i < 4; i++) + { + if(dma[i].start_type == DMA_START_HBLANK) + dma_transfer(dma + i); + } + } + + if(dispstat & 0x10) + irq_raised |= IRQ_HBLANK; + } + else + { + // Transition from hblank to next line + video_count += 960; + dispstat &= ~0x02; + + vcount++; + + if(vcount == 160) + { + // Transition from vrefresh to vblank + u32 i; + + dispstat |= 0x01; + if(dispstat & 0x8) + { + irq_raised |= IRQ_VBLANK; + } + + affine_reference_x[0] = + (s32)(address32(io_registers, 0x28) << 4) >> 4; + affine_reference_y[0] = + (s32)(address32(io_registers, 0x2C) << 4) >> 4; + affine_reference_x[1] = + (s32)(address32(io_registers, 0x38) << 4) >> 4; + affine_reference_y[1] = + (s32)(address32(io_registers, 0x3C) << 4) >> 4; + + for(i = 0; i < 4; i++) + { + if(dma[i].start_type == DMA_START_VBLANK) + dma_transfer(dma + i); + } + } + else + + if(vcount == 228) + { + // Transition from vblank to next screen + dispstat &= ~0x01; + frame_ticks++; + + #if 0 + printf("frame update (%x), %d instructions total, %d RAM flushes\n", + reg[REG_PC], instruction_count - last_frame, flush_ram_count); + last_frame = instruction_count; + +/* printf("%d gbc audio updates\n", gbc_update_count); + printf("%d oam updates\n", oam_update_count); */ + gbc_update_count = 0; + oam_update_count = 0; + flush_ram_count = 0; + #endif + + if(update_input()) + continue; + + update_gbc_sound(cpu_ticks); + synchronize(); + + update_screen(); + + if(update_backup_flag) + update_backup(); + + process_cheats(); + + vcount = 0; + } + + if(vcount == (dispstat >> 8)) + { + // vcount trigger + dispstat |= 0x04; + if(dispstat & 0x20) + { + irq_raised |= IRQ_VCOUNT; + } + } + else + { + dispstat &= ~0x04; + } + + io_registers[REG_VCOUNT] = vcount; + } + io_registers[REG_DISPSTAT] = dispstat; + } + + if(irq_raised) + raise_interrupt(irq_raised); + + execute_cycles = video_count; + + check_timer(0); + check_timer(1); + check_timer(2); + check_timer(3); + } while(reg[CPU_HALT_STATE] != CPU_ACTIVE); + return execute_cycles; +} + +u64 last_screen_timestamp = 0; +u32 frame_speed = 15000; + +#ifdef PSP_BUILD + +u32 real_frame_count = 0; +u32 virtual_frame_count = 0; +u32 num_skipped_frames = 0; + +void vblank_interrupt_handler(u32 sub, u32 *parg) +{ + real_frame_count++; +} + +void synchronize() +{ + char char_buffer[64]; + u64 new_ticks, time_delta; + s32 used_frameskip = frameskip_value; + + if(!synchronize_flag) + { + print_string("--FF--", 0xFFFF, 0x000, 0, 0); + used_frameskip = 4; + virtual_frame_count = real_frame_count - 1; + } + + skip_next_frame = 0; + + virtual_frame_count++; + + if(real_frame_count >= virtual_frame_count) + { + if((real_frame_count > virtual_frame_count) && + (current_frameskip_type == auto_frameskip) && + (num_skipped_frames < frameskip_value)) + { + skip_next_frame = 1; + num_skipped_frames++; + } + else + { + virtual_frame_count = real_frame_count; + num_skipped_frames = 0; + } + + // Here so that the home button return will eventually work. + // If it's not running fullspeed anyway this won't really hurt + // it much more. + + delay_us(1); + } + else + { + if(synchronize_flag) + sceDisplayWaitVblankStart(); + } + + if(current_frameskip_type == manual_frameskip) + { + frameskip_counter = (frameskip_counter + 1) % + (used_frameskip + 1); + if(random_skip) + { + if(frameskip_counter != (rand() % (used_frameskip + 1))) + skip_next_frame = 1; + } + else + { + if(frameskip_counter) + skip_next_frame = 1; + } + } + +/* sprintf(char_buffer, "%08d %08d %d %d %d\n", + real_frame_count, virtual_frame_count, num_skipped_frames, + real_frame_count - virtual_frame_count, skip_next_frame); + print_string(char_buffer, 0xFFFF, 0x0000, 0, 10); */ + +/* + sprintf(char_buffer, "%02d %02d %06d %07d", frameskip, (u32)ms_needed, + ram_translation_ptr - ram_translation_cache, rom_translation_ptr - + rom_translation_cache); + print_string(char_buffer, 0xFFFF, 0x0000, 0, 0); +*/ +} + +#else + +u32 ticks_needed_total = 0; +float us_needed = 0.0; +u32 frames = 0; +u32 skipped_num_frame = 60; +const u32 frame_interval = 60; +u32 skipped_num = 0; + +void synchronize() +{ + u64 new_ticks; + char char_buffer[64]; + u64 time_delta = 16667; + u32 fpsw = 60; + + get_ticks_us(&new_ticks); + time_delta = new_ticks - last_screen_timestamp; + last_screen_timestamp = new_ticks; + ticks_needed_total += time_delta; + + skip_next_frame = 0; +#ifndef ZAURUS + if((time_delta < frame_speed) && synchronize_flag) + { + delay_us(frame_speed - time_delta); + } +#endif + frames++; + + if(frames == 60) + { + if(status_display) { + us_needed = (float)ticks_needed_total / frame_interval; + fpsw = (u32)(1000000.0 / us_needed); + ticks_needed_total = 0; + if(current_frameskip_type == manual_frameskip) { + sprintf(char_buffer, "%s%3dfps %s:%d slot:%d ", synchronize_flag?" ":">>", fpsw, + current_frameskip_type==auto_frameskip?"Auto":current_frameskip_type==manual_frameskip?"Manu":"Off ", + frameskip_value, savestate_slot); + } else { + sprintf(char_buffer, "%s%3dfps %s:%d slot:%d ", synchronize_flag?" ":">>", + (skipped_num_frame==60&&fpsw>60)?fpsw:skipped_num_frame, + current_frameskip_type==auto_frameskip?"Auto":current_frameskip_type==manual_frameskip?"Manu":"Off ", + frameskip_value, savestate_slot); + } + } else + strcpy(char_buffer, " "); + print_string(char_buffer, 0xFFFF, 0x000, 40, 30); + print_string(ssmsg, 0xF000, 0x000, 180, 30); + strcpy(ssmsg, " "); + frames = 0; + skipped_num_frame = 60; + } + + if(current_frameskip_type == manual_frameskip) + { + frameskip_counter = (frameskip_counter + 1) % + (frameskip_value + 1); + if(random_skip) + { + if(frameskip_counter != (rand() % (frameskip_value + 1))) + skip_next_frame = 1; + } + else + { + if(frameskip_counter) + skip_next_frame = 1; + } +#ifndef ZAURUS + } +#else + } else if(current_frameskip_type == auto_frameskip) { + static struct timeval next1 = {0, 0}; + static struct timeval now; + + gettimeofday(&now, NULL); + if(next1.tv_sec == 0) { + next1 = now; + next1.tv_usec++; + } + if(timercmp(&next1, &now, >)) { + //struct timeval tdiff; + if(synchronize_flag) + do { + synchronize_sound(); + gettimeofday(&now, NULL); + } while (timercmp(&next1, &now, >)); + else + gettimeofday(&now, NULL); + //timersub(&next1, &now, &tdiff); + //usleep(tdiff.tv_usec/2); + //gettimeofday(&now, NULL); + skipped_num = 0; + next1 = now; + } else { + if(skipped_num < frameskip_value) { + skipped_num++; + skipped_num_frame--; + skip_next_frame = 1; + } else { + //synchronize_sound(); + skipped_num = 0; + next1 = now; + } + } + next1.tv_usec += 16667; + if(next1.tv_usec >= 1000000) { + next1.tv_sec++; + next1.tv_usec -= 1000000; + } + } +#endif + +// if(synchronize_flag == 0) +// print_string("--FF--", 0xFFFF, 0x000, 0, 0); +#ifdef ZAURUS + //sprintf(char_buffer, "%.1ffps", 1000000.0 / us_needed); + //print_string(" ", 0xFFFF, 0x000, 40, 30); + //print_string(char_buffer, 0xFFFF, 0x000, 40, 30); +#else + sprintf(char_buffer, "gpSP: %.1fms %.1ffps", us_needed / 1000.0, + 1000000.0 / us_needed); + SDL_WM_SetCaption(char_buffer, "gpSP"); +#endif + +/* + sprintf(char_buffer, "%02d %02d %06d %07d", frameskip, (u32)ms_needed, + ram_translation_ptr - ram_translation_cache, rom_translation_ptr - + rom_translation_cache); + print_string(char_buffer, 0xFFFF, 0x0000, 0, 0); +*/ +} + +#endif + +void quit() +{ + if(!update_backup_flag) + update_backup_force(); + + sound_exit(); + +#ifdef PSP_BUILD + sceKernelExitGame(); +#else + SDL_Quit(); + exit(0); +#endif +} + +void reset_gba() +{ + init_main(); + init_memory(); + init_cpu(); + reset_sound(); +} + +#ifdef PSP_BUILD + +u32 file_length(u8 *filename, s32 dummy) +{ + SceIoStat stats; + sceIoGetstat(filename, &stats); + return stats.st_size; +} + +void delay_us(u32 us_count) +{ + sceKernelDelayThread(us_count); +} + +void get_ticks_us(u64 *tick_return) +{ + u64 ticks; + sceRtcGetCurrentTick(&ticks); + + *tick_return = (ticks * 1000000) / sceRtcGetTickResolution(); +} + +#else + +u32 file_length(u8 *dummy, FILE *fp) +{ + u32 length; + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + return length; +} + +void delay_us(u32 us_count) +{ + SDL_Delay(us_count / 1000); // for dingux + // sleep(0); +} + +void get_ticks_us(u64 *ticks_return) +{ + *ticks_return = (SDL_GetTicks() * 1000); +} + +#endif + +void change_ext(u8 *src, u8 *buffer, u8 *extension) +{ + u8 *position; + + strcpy(buffer, main_path); + strcat(buffer, "/"); + + position = strrchr(src, '/'); + if (position) + src = position+1; + + strcat(buffer, src); + position = strrchr(buffer, '.'); + + if(position) + strcpy(position, extension); +} + +#define main_savestate_builder(type) \ +void main_##type##_savestate(file_tag_type savestate_file) \ +{ \ + file_##type##_variable(savestate_file, cpu_ticks); \ + file_##type##_variable(savestate_file, execute_cycles); \ + file_##type##_variable(savestate_file, video_count); \ + file_##type##_array(savestate_file, timer); \ +} \ + +main_savestate_builder(read); +main_savestate_builder(write_mem); + +void print_out(u32 address, u32 pc) +{ + char buffer[256]; + sprintf(buffer, "patching from gp8 %x", address); + print_string(buffer, 0xFFFF, 0x0000, 0, 0); + update_screen(); + delay_us(5000000); +} + diff --git a/source/psp/port.h b/source/psp/port.h index aa74fdd..a9ea25e 100644 --- a/source/psp/port.h +++ b/source/psp/port.h @@ -50,6 +50,47 @@ #include "homehook.h" #include "dvemgr.h" +#include <pspkernel.h> +#include <pspdebug.h> +#include <pspctrl.h> +#include <pspgu.h> +#include <pspaudio.h> +#include <pspaudiolib.h> +#include <psprtc.h> + +#define printf pspDebugScreenPrintf + +#define convert_palette(value) \ + value = ((value & 0x7FE0) << 1) | (value & 0x1F) \ + + +typedef u32 FIXED16_16; // Q16.16 fixed-point + +#define FILE_OPEN_APPEND (PSP_O_CREAT | PSP_O_APPEND | PSP_O_TRUNC) +#define FILE_OPEN_READ PSP_O_RDONLY +#define FILE_OPEN_WRITE (PSP_O_CREAT | PSP_O_WRONLY | PSP_O_TRUNC) + +#define FILE_OPEN(filename_tag, filename, mode) \ + s32 filename_tag = sceIoOpen(filename, FILE_OPEN_##mode, 0777) \ + +#define FILE_CHECK_VALID(filename_tag) \ + (filename_tag >= 0) \ + +#define FILE_TAG_INVALID \ + (-1) \ + +#define FILE_CLOSE(filename_tag) \ + sceIoClose(filename_tag) \ + +#define FILE_READ(filename_tag, buffer, size) \ + sceIoRead(filename_tag, buffer, size) \ + +#define FILE_WRITE(filename_tag, buffer, size) \ + sceIoWrite(filename_tag, buffer, size) \ + +#define FILE_SEEK(filename_tag, offset, type) \ + sceIoLseek(filename_tag, offset, PSP_##type) \ + /* Tuning parameters for the PSP version of gpSP */ /* Its processors are: * a) a Sony CXD2962GG at 222 MHz based on the MIPS R4000 @@ -58,8 +99,8 @@ * b) an embedded graphics core at 111 MHz; * c) a Sony CXD1876 Media Engine chip based on the MIPS R4000 * 64-bit core with 2 MiB of RAM (2.6 Gbit/s). */ -#define READONLY_CODE_CACHE_SIZE (2 * 1024 * 1024) -#define WRITABLE_CODE_CACHE_SIZE (6 * 1024 * 1024) +#define READONLY_CODE_CACHE_SIZE (4 * 1024 * 1024) +#define WRITABLE_CODE_CACHE_SIZE (4 * 1024 * 1024) /* The following parameter needs to be at least enough bytes to hold * the generated code for the largest instruction on your platform. * In most cases, that will be the ARM instruction @@ -68,12 +109,6 @@ #define GET_DISK_FREE_SPACE FS_CMD_GET_DISKFREE -typedef SceUID FILE_TAG_TYPE; - -#define FILE_OPEN_APPEND (PSP_O_CREAT | PSP_O_APPEND | PSP_O_TRUNC) - -#define FILE_OPEN_READ PSP_O_RDONLY - -#define FILE_OPEN_WRITE (PSP_O_CREAT | PSP_O_WRONLY | PSP_O_TRUNC) +typedef s32 FILE_TAG_TYPE; #endif diff --git a/source/zip.h b/source/zip.h index a9fc1f3..2d72e69 100644 --- a/source/zip.h +++ b/source/zip.h @@ -22,7 +22,7 @@ #ifndef ZIP_H
#define ZIP_H
-#include "zlib.h"
+#include <zlib.h>
#define ZIP_TMP "ZIPTMP.GBA" /* Acceptable on all platforms */
|