diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile.linux | 41 | ||||
-rw-r--r-- | Makefile.trimui | 52 | ||||
-rw-r--r-- | arm/arm_stub.S | 34 | ||||
-rw-r--r-- | common.h | 6 | ||||
-rw-r--r-- | cpu.c | 2 | ||||
-rw-r--r-- | cpu.h | 8 | ||||
-rw-r--r-- | cpu_threaded.c | 77 | ||||
-rw-r--r-- | frontend/config.c | 91 | ||||
-rw-r--r-- | frontend/config.h | 9 | ||||
m--------- | frontend/libpicofe | 0 | ||||
-rw-r--r-- | frontend/main.c | 442 | ||||
-rw-r--r-- | frontend/main.h | 70 | ||||
-rw-r--r-- | frontend/menu.c | 335 | ||||
-rw-r--r-- | frontend/menu.h | 10 | ||||
-rw-r--r-- | frontend/plat.h | 19 | ||||
-rw-r--r-- | frontend/plat_linux.c | 258 | ||||
-rw-r--r-- | frontend/plat_trimui.c | 277 | ||||
-rw-r--r-- | frontend/scale.c | 604 | ||||
-rw-r--r-- | frontend/scale.h | 9 | ||||
-rw-r--r-- | gba_memory.c | 24 | ||||
-rw-r--r-- | gba_memory.h | 6 | ||||
-rw-r--r-- | gpsp_config.h | 2 | ||||
-rw-r--r-- | input.c | 34 | ||||
-rw-r--r-- | libretro.c | 19 | ||||
-rw-r--r-- | psp/mips_stub.S | 3 | ||||
-rw-r--r-- | sound.c | 10 | ||||
-rw-r--r-- | sound.h | 8 | ||||
-rw-r--r-- | x86/x86_stub.S | 2 | ||||
-rw-r--r-- | zip.c | 160 | ||||
-rw-r--r-- | zip.h | 26 |
31 files changed, 2625 insertions, 16 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1f260d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "frontend/libpicofe"] + path = frontend/libpicofe + url = https://github.com/notaz/libpicofe diff --git a/Makefile.linux b/Makefile.linux new file mode 100644 index 0000000..066b5b9 --- /dev/null +++ b/Makefile.linux @@ -0,0 +1,41 @@ +# gpSP makefile +# Gilead Kutnick - Exophase +# pandora port - notaz +# respberry pi - DPR + +# Global definitions + +CC = $(CROSS_COMPILE)gcc +SYSROOT = $(shell $(CC) --print-sysroot) + +OBJS = main.o cpu.o gba_memory.o video.o input.o sound.o cheats.o cpu_threaded.o bios_data.o zip.o x86/x86_stub.o gba_cc_lut.o \ + frontend/libpicofe/input.o frontend/libpicofe/in_sdl.o frontend/libpicofe/linux/in_evdev.o frontend/libpicofe/linux/plat.o frontend/libpicofe/fonts.o frontend/libpicofe/readpng.o frontend/libpicofe/config_file.o \ + frontend/config.o frontend/menu.o frontend/plat_linux.o frontend/main.o frontend/scale.o + +BIN = picogpsp + +# Platform specific definitions + +CFLAGS += -DPC_BUILD -Wall -m32 -DX86_ARCH -DHAVE_DYNAREC -DHAVE_MMAP +CFLAGS += -Ofast -fdata-sections -ffunction-sections -fno-PIC -DPICO_HOME_DIR='"/.picogpsp/"' +CFLAGS += -I./ $(shell $(SYSROOT)/usr/bin/sdl-config --cflags) + +# expecting to have PATH set up to get correct sdl-config first + +LIBS = -m32 -lc -lgcc -lSDL -lasound -lpng -lz -Wl,--as-needed -Wl,--gc-sections -flto -s + +# Compilation: + +all: $(BIN) + +%.o: %.S + $(CC) $(CFLAGS) -c -o $@ $< + + +cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label + +$(BIN): $(OBJS) + $(CC) $(OBJS) $(LIBS) -o $(BIN) + +clean: + rm -f $(OBJS) $(BIN) diff --git a/Makefile.trimui b/Makefile.trimui new file mode 100644 index 0000000..25fe88e --- /dev/null +++ b/Makefile.trimui @@ -0,0 +1,52 @@ +# gpSP makefile +# Gilead Kutnick - Exophase +# pandora port - notaz +# respberry pi - DPR + +# Global definitions + +CC = $(CROSS_COMPILE)gcc +SYSROOT = $(shell $(CC) --print-sysroot) + +OBJS = main.o cpu.o gba_memory.o video.o input.o sound.o gba_cc_lut.o \ + bios_data.o cheats.o zip.o arm/arm_stub.o cpu_threaded.o arm/video_blend.o \ + frontend/libpicofe/input.o frontend/libpicofe/in_sdl.o \ + frontend/libpicofe/linux/in_evdev.o frontend/libpicofe/linux/plat.o \ + frontend/libpicofe/fonts.o frontend/libpicofe/readpng.o frontend/libpicofe/config_file.o \ + frontend/config.o frontend/menu.o frontend/plat_trimui.o frontend/main.o frontend/scale.o + +BIN = picogpsp + +# Platform specific definitions + +VPATH += .. ../arm +CFLAGS += -DARM_ARCH -DARM_ARCH_BLENDING_OPTS -DPC_BUILD -Wall -DHAVE_DYNAREC +CFLAGS += -Ofast -fdata-sections -ffunction-sections -flto -fno-PIC -DPICO_HOME_DIR='"/.picogpsp/"' +CFLAGS += -I./ $(shell $(SYSROOT)/usr/bin/sdl-config --cflags) + +# expecting to have PATH set up to get correct sdl-config first + +LIBS = -lc -lgcc -lSDL -lasound -lpng -lz -Wl,--as-needed -Wl,--gc-sections -flto -s + +ifeq ($(PROFILE), YES) +CFLAGS += -fprofile-generate=./profile +LIBS += -lgcov +else ifeq ($(PROFILE), APPLY) +CFLAGS += -fprofile-use -fprofile-dir=./profile -fbranch-probabilities +endif + +# Compilation: + +all: $(BIN) + +%.o: %.S + $(CC) $(CFLAGS) -c -o $@ $< + + +cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label + +$(BIN): $(OBJS) + $(CC) $(OBJS) $(LIBS) -o $(BIN) + +clean: + rm -f $(OBJS) $(BIN) diff --git a/arm/arm_stub.S b/arm/arm_stub.S index d7203f8..4d7e22b 100644 --- a/arm/arm_stub.S +++ b/arm/arm_stub.S @@ -67,8 +67,14 @@ _##symbol: #define MODE_SUPERVISOR 3 -#define extract_u16(rd, rs) \ - uxth rd, rs +#if __ARM_ARCH >= 6 + #define extract_u16(rd, rs) \ + uxth rd, rs +#else + #define extract_u16(rd, rs) \ + bic rd, rs, #0xff000000 ;\ + bic rd, rd, #0x00ff0000 +#endif @ Will load the register set from memory into the appropriate cached registers. @ See arm_emit.h for listing explanation. @@ -444,6 +450,8 @@ defsymbl(execute_swi_##mode) ;\ orr r0, r0, #0x13 /* set to supervisor mode */;\ str r0, [reg_base, #REG_CPSR] /* update cpsr */;\ ;\ + call_c_function(bios_region_read_allow) ;\ + ;\ mov r0, #MODE_SUPERVISOR ;\ ;\ store_registers_##mode() /* store regs for mode */;\ @@ -770,11 +778,22 @@ lookup_pc: #define sign_extend_u16(reg) #define sign_extend_u32(reg) -#define sign_extend_s8(reg) ;\ - sxtb reg, reg +#if __ARM_ARCH >= 6 + #define sign_extend_s8(reg) ;\ + sxtb reg, reg -#define sign_extend_s16(reg) ;\ - sxth reg, reg + #define sign_extend_s16(reg) ;\ + sxth reg, reg +#else + #define sign_extend_s8(reg) ;\ + mov reg, reg, lsl #24 /* shift reg into upper 8bits */;\ + mov reg, reg, asr #24 /* shift down, sign extending */;\ + + #define sign_extend_s16(reg) ;\ + mov reg, reg, lsl #16 /* shift reg into upper 16bits */;\ + mov reg, reg, asr #16 /* shift down, sign extending */;\ + +#endif #define execute_load_op_u8(load_op) ;\ mov r0, r0, lsl #17 ;\ @@ -877,6 +896,9 @@ defsymbl(rom_translation_cache) defsymbl(ram_translation_cache) .space RAM_TRANSLATION_CACHE_SIZE .size ram_translation_cache, .-ram_translation_cache +defsymbl(bios_translation_cache) + .space BIOS_TRANSLATION_CACHE_SIZE +.size bios_translation_cache, .-bios_translation_cache #endif @@ -31,6 +31,10 @@ #define PATH_SEPARATOR_CHAR '/' #endif +#define prefetch(a,b) __builtin_prefetch(a,b) +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + /* On x86 we pass arguments via registers instead of stack */ #ifdef X86_ARCH #define function_cc __attribute__((regparm(2))) @@ -40,7 +44,7 @@ #ifdef ARM_ARCH -#define _BSD_SOURCE // sync +#define _DEFAULT_SOURCE 1 // sync #include <stdlib.h> #include <string.h> #include <math.h> @@ -1614,6 +1614,8 @@ void raise_interrupt(irq_type irq_raised) reg[REG_CPSR] = 0xD2; reg[REG_PC] = 0x00000018; + bios_region_read_allow(); + set_cpu_mode(MODE_IRQ); reg[CPU_HALT_STATE] = CPU_ACTIVE; reg[CHANGED_PC_STATUS] = 1; @@ -93,6 +93,7 @@ typedef enum { TRANSLATION_REGION_RAM, TRANSLATION_REGION_ROM, + TRANSLATION_REGION_BIOS } translation_region_type; extern u32 instruction_count; @@ -126,22 +127,28 @@ s32 translate_block_thumb(u32 pc, translation_region_type translation_region, #if defined(HAVE_MMAP) extern u8* rom_translation_cache; extern u8* ram_translation_cache; +extern u8* bios_translation_cache; #elif defined(_3DS) #define rom_translation_cache ((u8*)0x02000000 - ROM_TRANSLATION_CACHE_SIZE) #define ram_translation_cache (rom_translation_cache - RAM_TRANSLATION_CACHE_SIZE) +#define bios_translation_cache (ram_translation_cache - BIOS_TRANSLATION_CACHE_SIZE) extern u8* rom_translation_cache_ptr; extern u8* ram_translation_cache_ptr; +extern u8* bios_translation_cache_ptr; #elif defined(VITA) extern u8* rom_translation_cache; extern u8* ram_translation_cache; +extern u8* bios_translation_cache; extern int sceBlock; #else extern u8 rom_translation_cache[ROM_TRANSLATION_CACHE_SIZE]; extern u8 ram_translation_cache[RAM_TRANSLATION_CACHE_SIZE]; +extern u8 bios_translation_cache[BIOS_TRANSLATION_CACHE_SIZE]; #endif extern u32 stub_arena[STUB_ARENA_SIZE / 4]; extern u8 *rom_translation_ptr; extern u8 *ram_translation_ptr; +extern u8 *bios_translation_ptr; #define MAX_TRANSLATION_GATES 8 @@ -156,6 +163,7 @@ extern u32 *rom_branch_hash[ROM_BRANCH_HASH_SIZE]; void flush_translation_cache_rom(void); void flush_translation_cache_ram(void); +void flush_translation_cache_bios(void); void dump_translation_cache(void); void init_caches(void); void init_emitter(void); diff --git a/cpu_threaded.c b/cpu_threaded.c index 2d61b4a..148fd78 100644 --- a/cpu_threaded.c +++ b/cpu_threaded.c @@ -29,26 +29,34 @@ u8 *last_rom_translation_ptr = NULL; u8 *last_ram_translation_ptr = NULL; +u8 *last_bios_translation_ptr = NULL; #if defined(HAVE_MMAP) u8* rom_translation_cache; u8* ram_translation_cache; +u8* bios_translation_cache; u8 *rom_translation_ptr; u8 *ram_translation_ptr; +u8 *bios_translation_ptr; #elif defined(VITA) u8* rom_translation_cache; u8* ram_translation_cache; +u8* bios_translation_cache; u8 *rom_translation_ptr; u8 *ram_translation_ptr; +u8 *bios_translation_ptr; int sceBlock; #elif defined(_3DS) u8* rom_translation_cache_ptr; u8* ram_translation_cache_ptr; +u8* bios_translation_cache_ptr; u8 *rom_translation_ptr = rom_translation_cache; u8 *ram_translation_ptr = ram_translation_cache; +u8 *bios_translation_ptr = bios_translation_cache; #else u8 *rom_translation_ptr = rom_translation_cache; u8 *ram_translation_ptr = ram_translation_cache; +u8 *bios_translation_ptr = bios_translation_cache; #endif /* Note, see stub files for more cache definitions */ @@ -244,6 +252,10 @@ void translate_icache_sync() { platform_cache_sync(last_ram_translation_ptr, ram_translation_ptr); last_ram_translation_ptr = ram_translation_ptr; } + if (last_bios_translation_ptr < bios_translation_ptr) { + platform_cache_sync(last_bios_translation_ptr, bios_translation_ptr); + last_bios_translation_ptr = bios_translation_ptr; + } } /* End of Cache invalidation */ @@ -2443,6 +2455,9 @@ void translate_icache_sync() { u8 *ram_block_ptrs[1024 * 64]; u32 ram_block_tag_top = 0x0101; +u8 *bios_block_ptrs[1024 * 8]; +u32 bios_block_tag_top = 0x0101; + // This function will return a pointer to a translated block of code. If it // doesn't exist it will translate it, if it does it will pass it back. @@ -2472,6 +2487,7 @@ u32 ram_block_tag_top = 0x0101; #define ram_translation_region TRANSLATION_REGION_RAM #define rom_translation_region TRANSLATION_REGION_ROM +#define bios_translation_region TRANSLATION_REGION_BIOS #define block_lookup_translate_arm(mem_type, smc_enable) \ translation_result = translate_block_arm(pc, mem_type##_translation_region, \ @@ -2567,17 +2583,28 @@ u8 function_cc *block_lookup_address_##type(u32 pc) \ \ switch(pc >> 24) \ { \ + case 0x0: \ + bios_region_read_allow(); \ + location = (u16 *)(bios_rom + pc + 0x4000); \ + block_lookup_translate(type, bios, 0); \ + if(translation_recursion_level == 0) \ + bios_region_read_allow(); \ + break; \ + \ case 0x2: \ location = (u16 *)(ewram + (pc & 0x3FFFF) + 0x40000); \ block_lookup_translate(type, ram, 1); \ + if(translation_recursion_level == 0) \ + bios_region_read_protect(); \ break; \ \ case 0x3: \ location = (u16 *)(iwram + (pc & 0x7FFF)); \ block_lookup_translate(type, ram, 1); \ + if(translation_recursion_level == 0) \ + bios_region_read_protect(); \ break; \ \ - case 0x0: \ case 0x8 ... 0xD: \ { \ u32 hash_target = ((pc * 2654435761U) >> 16) & \ @@ -2601,7 +2628,7 @@ u8 function_cc *block_lookup_address_##type(u32 pc) \ \ redo: \ \ - translation_recursion_level++; \ + translation_recursion_level++; \ ((u32 *)rom_translation_ptr)[0] = pc; \ ((u32 **)rom_translation_ptr)[1] = NULL; \ *block_ptr_address = (u32 *)rom_translation_ptr; \ @@ -2623,6 +2650,8 @@ u8 function_cc *block_lookup_address_##type(u32 pc) \ if(translation_recursion_level == 0) \ translate_icache_sync(); \ } \ + if(translation_recursion_level == 0) \ + bios_region_read_protect(); \ break; \ } \ \ @@ -3037,6 +3066,12 @@ s32 translate_block_arm(u32 pc, translation_region_type rom_translation_cache + ROM_TRANSLATION_CACHE_SIZE - TRANSLATION_CACHE_LIMIT_THRESHOLD; break; + + case TRANSLATION_REGION_BIOS: + translation_ptr = bios_translation_ptr; + translation_cache_limit = bios_translation_cache + + BIOS_TRANSLATION_CACHE_SIZE; + break; } generate_block_prologue(); @@ -3104,6 +3139,10 @@ s32 translate_block_arm(u32 pc, translation_region_type case TRANSLATION_REGION_ROM: flush_translation_cache_rom(); break; + + case TRANSLATION_REGION_BIOS: + flush_translation_cache_bios(); + break; } return -1; } @@ -3177,6 +3216,10 @@ s32 translate_block_arm(u32 pc, translation_region_type case TRANSLATION_REGION_ROM: rom_translation_ptr = translation_ptr; break; + + case TRANSLATION_REGION_BIOS: + bios_translation_ptr = translation_ptr; + break; } for(i = 0; i < external_block_exit_position; i++) @@ -3249,6 +3292,12 @@ s32 translate_block_thumb(u32 pc, translation_region_type rom_translation_cache + ROM_TRANSLATION_CACHE_SIZE - TRANSLATION_CACHE_LIMIT_THRESHOLD; break; + + case TRANSLATION_REGION_BIOS: + translation_ptr = bios_translation_ptr; + translation_cache_limit = bios_translation_cache + + BIOS_TRANSLATION_CACHE_SIZE; + break; } generate_block_prologue(); @@ -3314,6 +3363,10 @@ s32 translate_block_thumb(u32 pc, translation_region_type case TRANSLATION_REGION_ROM: flush_translation_cache_rom(); break; + + case TRANSLATION_REGION_BIOS: + flush_translation_cache_bios(); + break; } return -1; } @@ -3381,6 +3434,10 @@ s32 translate_block_thumb(u32 pc, translation_region_type case TRANSLATION_REGION_ROM: rom_translation_ptr = translation_ptr; break; + + case TRANSLATION_REGION_BIOS: + bios_translation_ptr = translation_ptr; + break; } for(i = 0; i < external_block_exit_position; i++) @@ -3437,6 +3494,16 @@ void flush_translation_cache_rom(void) memset(rom_branch_hash, 0, sizeof(rom_branch_hash)); } +void flush_translation_cache_bios(void) +{ + bios_block_tag_top = 0x0101; + + last_bios_translation_ptr = bios_translation_cache; + bios_translation_ptr = bios_translation_cache; + + memset(bios_rom + 0x4000, 0, 0x4000); +} + void init_caches(void) { /* Ensure we wipe everything including the SMC mirrors */ @@ -3446,6 +3513,7 @@ void init_caches(void) iwram_code_min = 0; iwram_code_max = 0x7FFF; flush_translation_cache_ram(); + flush_translation_cache_bios(); /* Ensure 0 and FFFF get zeroed out */ memset(ram_block_ptrs, 0, sizeof(ram_block_ptrs)); } @@ -3463,6 +3531,11 @@ void dump_translation_cache(void) fwrite(rom_translation_cache, 1, rom_translation_ptr - rom_translation_cache, fd); fclose(fd); + + fd = fopen(cache_dump_prefix "bios_cache.bin", "wb"); + fwrite(bios_translation_cache, 1, + bios_translation_ptr - bios_translation_cache, fd); + fclose(fd); } diff --git a/frontend/config.c b/frontend/config.c new file mode 100644 index 0000000..027fb8e --- /dev/null +++ b/frontend/config.c @@ -0,0 +1,91 @@ +#include "common.h" +#include "frontend/main.h" +#include "frontend/config.h" + +typedef enum { + CE_TYPE_STR = 0, + CE_TYPE_NUM = 4, +} config_entry_type; + +#define CE_STR(val) \ + { #val, CE_TYPE_STRING, val } + +#define CE_NUM(val) \ + { #val, CE_TYPE_NUM, &val } + +static const struct { + const char *name; + config_entry_type type; + void *val; +} config_data[] = { + CE_NUM(dynarec_enable), + CE_NUM(frameskip_style), + CE_NUM(max_frameskip), + CE_NUM(scaling_mode), + CE_NUM(color_correct), + CE_NUM(lcd_blend), + CE_NUM(show_fps), +}; + +void config_write(FILE *f) +{ + for (int i = 0; i < array_size(config_data); i++) { + switch (config_data[i].type) + { + case CE_TYPE_STR: + fprintf(f, "%s = %s\n", config_data[i].name, (char *)config_data[i].val); + break; + case CE_TYPE_NUM: + fprintf(f, "%s = %u\n", config_data[i].name, *(uint32_t *)config_data[i].val); + break; + default: + printf("unhandled type %d for %s\n", config_data[i].type, (char *)config_data[i].val); + break; + } + } +} + +static void parse_str_val(char *cval, const char *src) +{ + char *tmp; + strncpy(cval, src, 256); + cval[256 - 1] = 0; + tmp = strchr(cval, '\n'); + if (tmp == NULL) + tmp = strchr(cval, '\r'); + if (tmp != NULL) + *tmp = 0; +} + +static void parse_num_val(uint32_t *cval, const char *src) +{ + char *tmp = NULL; + uint32_t val; + val = strtoul(src, &tmp, 10); + if (tmp == NULL || src == tmp) + return; // parse failed + + *cval = val; +} + +void config_read(const char* cfg) +{ + for (int i = 0; i < array_size(config_data); i++) { + char *tmp; + + tmp = strstr(cfg, config_data[i].name); + if (tmp == NULL) + continue; + tmp += strlen(config_data[i].name); + if (strncmp(tmp, " = ", 3) != 0) + continue; + tmp += 3; + + if (config_data[i].type == CE_TYPE_STR) { + parse_str_val(config_data[i].val, tmp); + continue; + } + + parse_num_val(config_data[i].val, tmp); + } +} diff --git a/frontend/config.h b/frontend/config.h new file mode 100644 index 0000000..d45e435 --- /dev/null +++ b/frontend/config.h @@ -0,0 +1,9 @@ +#ifndef FRONTEND_CONFIG_H +#define FRONTEND_CONFIG_H + +#include <stdio.h> + +void config_write(FILE *f); +void config_read(const char *cfg); + +#endif diff --git a/frontend/libpicofe b/frontend/libpicofe new file mode 160000 +Subproject 76e45c3489a0c32d91744413c198f685b553f6c diff --git a/frontend/main.c b/frontend/main.c new file mode 100644 index 0000000..f303201 --- /dev/null +++ b/frontend/main.c @@ -0,0 +1,442 @@ +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "main.h" +#include "memmap.h" +#include "frontend/menu.h" +#include "frontend/plat.h" +#include "frontend/libpicofe/plat.h" + +/* Percentage of free space allowed in the audio buffer before + * skipping frames. Lower numbers mean more skipping but smoother + * audio, since the buffer will stay closer to filled. */ +#define FRAMESKIP_UNDERRUN_THRESHOLD 0.5 + +int should_quit = 0; + +u32 idle_loop_target_pc = 0xFFFFFFFF; +u32 iwram_stack_optimize = 1; +u32 translation_gate_target_pc[MAX_TRANSLATION_GATES]; +u32 translation_gate_targets = 0; + +uint16_t *gba_screen_pixels_prev = NULL; +uint16_t *gba_processed_pixels = NULL; + +int use_libretro_save_method = 0; +bios_type selected_bios = auto_detect; +boot_mode selected_boot_mode = boot_game; + +u32 skip_next_frame = 0; + +int dynarec_enable; +int state_slot; +frameskip_style_t frameskip_style; +scaling_mode_t scaling_mode; +int max_frameskip; +int color_correct; +int lcd_blend; +int show_fps; +int limit_frames; + +static float vsyncsps = 0.0; +static float rendersps = 0.0; + +void quit(); + +void gamepak_related_name(char *buf, size_t len, char *new_extension) +{ + char root_dir[512]; + char filename[512]; + char *p; + + plat_get_root_dir(root_dir, len); + p = strrchr(gamepak_filename, PATH_SEPARATOR_CHAR); + + if (p) + p++; + else + p = gamepak_filename; + strncpy(filename, p, sizeof(filename)); + filename[sizeof(filename) - 1] = 0; + p = strrchr(filename, '.'); + if (p) + *p = 0; + + snprintf(buf, len, "%s%s%s", root_dir, filename, new_extension); +} + +void toggle_fast_forward(int force_off) +{ + static frameskip_style_t frameskip_style_was; + static int max_frameskip_was; + static int limit_frames_was; + static int global_process_audio_was; + static int fast_forward; + + if (force_off && !fast_forward) + return; + + fast_forward = !fast_forward; + + if (fast_forward) { + frameskip_style_was = frameskip_style; + max_frameskip_was = max_frameskip; + limit_frames_was = limit_frames; + global_process_audio_was = global_process_audio; + + frameskip_style = FRAMESKIP_MANUAL; + max_frameskip = 5; + limit_frames = 0; + global_process_audio = 0; + } else { + frameskip_style = frameskip_style_was; + max_frameskip = max_frameskip_was; + limit_frames = limit_frames_was; + global_process_audio = global_process_audio_was; + } +} + +void state_file_name(char *buf, size_t len, unsigned state_slot) +{ + char ext[20]; + snprintf(ext, sizeof(ext), ".st%d", state_slot); + + gamepak_related_name(buf, len, ext); +} + +void config_file_name(char *buf, size_t len, int is_game) +{ + char root_dir[MAXPATHLEN]; + + if (is_game) { + gamepak_related_name(buf, len, ".cfg"); + } else { + plat_get_root_dir(root_dir, MAXPATHLEN); + snprintf(buf, len, "%s%s", root_dir, "picogpsp.cfg"); + } +} + +void handle_emu_action(emu_action action) +{ + static frameskip_style_t prev_frameskip_style; + static emu_action prev_action = EACTION_NONE; + if (prev_action != EACTION_NONE && prev_action == action) return; + + switch (action) + { + case EACTION_NONE: + break; + case EACTION_QUIT: + should_quit = 1; + break; + case EACTION_TOGGLE_FPS: + show_fps = !show_fps; + /* Force the hud to clear */ + plat_video_set_msg(" "); + break; + case EACTION_SAVE_STATE: + save_state_file(0); + break; + case EACTION_LOAD_STATE: + load_state_file(0); + break; + case EACTION_TOGGLE_FSKIP: + if (prev_frameskip_style == FRAMESKIP_NONE) + prev_frameskip_style = FRAMESKIP_AUTO; + + if (frameskip_style == FRAMESKIP_NONE) { + frameskip_style = prev_frameskip_style; + } else { + prev_frameskip_style = frameskip_style; + frameskip_style = FRAMESKIP_NONE; + } + break; + case EACTION_TOGGLE_FF: + toggle_fast_forward(0); + break; + case EACTION_MENU: + toggle_fast_forward(1); + update_backup(); + menu_loop(); + break; + default: + break; + } + + prev_action = action; +} + +void synchronize(void) +{ + static uint32_t vsyncs = 0; + static uint32_t renders = 0; + static uint32_t nextsec = 0; + static uint32_t skipped_frames = 0; + unsigned int ticks = 0; + + float capacity = plat_sound_capacity(); + + switch (frameskip_style) + { + case FRAMESKIP_AUTO: + skip_next_frame = 0; + + if (capacity > FRAMESKIP_UNDERRUN_THRESHOLD) { + skip_next_frame = 1; + skipped_frames++; + } + break; + case FRAMESKIP_MANUAL: + skip_next_frame = 1; + skipped_frames++; + break; + default: + skip_next_frame = 0; + break; + } + + if (skipped_frames > max_frameskip) { + skip_next_frame = 0; + skipped_frames = 0; + } + + if (show_fps) { + ticks = plat_get_ticks_ms(); + if (ticks > nextsec) { + vsyncsps = vsyncs; + rendersps = renders; + vsyncs = 0; + renders = 0; + nextsec = ticks + 1000; + } + vsyncs++; + if (!skip_next_frame) renders++; + } +} + +void print_hud() +{ + char msg[HUD_LEN]; + if (show_fps) { + snprintf(msg, HUD_LEN, "FPS: %2.0f (%4.1f)", rendersps, vsyncsps); + plat_video_set_msg(msg); + } +} + +int save_state_file(unsigned state_slot) +{ + char state_filename[MAXPATHLEN]; + void *data; + FILE *f; + int ret = 0; + state_file_name(state_filename, MAXPATHLEN, state_slot); + + f = fopen(state_filename, "wb"); + + if (!f) + return -1; + + data = calloc(1, GBA_STATE_MEM_SIZE); + if (!data) { + ret = -1; + goto fail; + } + + gba_save_state(data); + + if (fwrite(data, 1, GBA_STATE_MEM_SIZE, f) != GBA_STATE_MEM_SIZE) { + ret = -1; + goto fail; + } + +fail: + if (data) + free(data); + if (f) + fclose(f); + + return ret; +} + +int load_state_file(unsigned state_slot) +{ + char state_filename[MAXPATHLEN]; + void *data; + FILE *f; + int ret = 0; + state_file_name(state_filename, MAXPATHLEN, state_slot); + + f = fopen(state_filename, "rb"); + + if (!f) + return -1; + + data = calloc(1, GBA_STATE_MEM_SIZE); + if (!data) { + ret = -1; + goto fail; + } + + + if (fread(data, 1, GBA_STATE_MEM_SIZE, f) != GBA_STATE_MEM_SIZE) { + ret = -1; + goto fail; + } + + gba_load_state(data); + +fail: + if (data) + free(data); + if (f) + fclose(f); + + return ret; +} + +int main(int argc, char *argv[]) +{ + bool bios_loaded = false; + char bios_filename[MAXPATHLEN]; + char filename[MAXPATHLEN]; + char path[MAXPATHLEN]; + + if (argc < 2) { + printf("Usage: picogpsp FILE"); + return 0; + }; + + strncpy(filename, argv[1], MAXPATHLEN); + if (filename[0] != '/') { + getcwd(path, MAXPATHLEN); + if (strlen(path) + strlen(filename) + 1 < MAXPATHLEN) { + strcat(path, "/"); + strcat(path, filename); + strcpy(filename, path); + } else + filename[0] = 0; + } + + if (selected_bios == auto_detect || selected_bios == official_bios) { + bios_loaded = true; + getcwd(bios_filename, MAXPATHLEN); + strncat(bios_filename, "/gba_bios.bin", MAXPATHLEN - strlen(bios_filename)); + + if (load_bios(bios_filename)) { + if (selected_bios == official_bios) + printf("Could not load BIOS image file\n"); + bios_loaded = false; + } + + if (bios_loaded && bios_rom[0] != 0x18) { + if (selected_bios == official_bios) + printf("BIOS image seems incorrect\n"); + bios_loaded = false; + } + } + + if (bios_loaded) { + printf("Using official BIOS\n"); + } else { + /* Load the built-in BIOS */ + memcpy(bios_rom, open_gba_bios_rom, sizeof(bios_rom)); + printf("Using built-in BIOS\n"); + } + + getcwd(main_path, 512); + plat_get_root_dir(save_path, 512); + + if (!gamepak_rom) + init_gamepak_buffer(); + + if(!gba_screen_pixels) + gba_screen_pixels = (uint16_t*)calloc(GBA_SCREEN_PITCH * GBA_SCREEN_HEIGHT, sizeof(uint16_t)); + + if (plat_init()) { + return -1; + }; + + if (load_gamepak(filename) != 0) + { + fprintf(stderr, "Could not load the game file.\n"); + return -1; + } + + init_main(); + init_sound(1); + menu_init(); + +#if defined(HAVE_DYNAREC) + if (dynarec_enable) + { +#ifdef HAVE_MMAP + rom_translation_cache = mmap(NULL, ROM_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + ram_translation_cache = mmap(NULL, RAM_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + bios_translation_cache = mmap(NULL, BIOS_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); +#endif + } + else + dynarec_enable = 0; +#else + dynarec_enable = 0; +#endif + + reset_gba(); + + do { + update_input(); + + synchronize(); + +#ifdef HAVE_DYNAREC + if (dynarec_enable) + execute_arm_translate(execute_cycles); + else +#endif + execute_arm(execute_cycles); + + render_audio(); + + print_hud(); + + if (!skip_next_frame) + plat_video_flip(); + } while (!should_quit); + + quit(); + return 0; +} + +void quit() +{ + update_backup(); + + memory_term(); + + if (gba_screen_pixels_prev) { + free(gba_screen_pixels_prev); + gba_screen_pixels_prev = NULL; + } + + if (gba_processed_pixels) { + free(gba_processed_pixels); + gba_processed_pixels = NULL; + } + + free(gba_screen_pixels); + gba_screen_pixels = NULL; + +#if defined(HAVE_MMAP) && defined(HAVE_DYNAREC) + munmap(rom_translation_cache, ROM_TRANSLATION_CACHE_SIZE); + munmap(ram_translation_cache, RAM_TRANSLATION_CACHE_SIZE); + munmap(bios_translation_cache, BIOS_TRANSLATION_CACHE_SIZE); +#endif + + menu_finish(); + plat_finish(); + + exit(0); +} diff --git a/frontend/main.h b/frontend/main.h new file mode 100644 index 0000000..d039c3b --- /dev/null +++ b/frontend/main.h @@ -0,0 +1,70 @@ +#ifndef __FRONTEND_MAIN_H__ +#define __FRONTEND_MAIN_H__ + +#include <stddef.h> + +#define MAXPATHLEN 512 + +typedef enum { + EACTION_NONE = 0, + EACTION_MENU, + EACTION_TOGGLE_FPS, + EACTION_TOGGLE_FSKIP, + EACTION_TOGGLE_FF, + EACTION_SAVE_STATE, + EACTION_LOAD_STATE, + EACTION_QUIT, +} emu_action; + +typedef enum { + KBIT_A = 0, + KBIT_B, + KBIT_SELECT, + KBIT_START, + KBIT_RIGHT, + KBIT_LEFT, + KBIT_UP, + KBIT_DOWN, + KBIT_R, + KBIT_L +} keybit; + +typedef enum { + FRAMESKIP_NONE = 0, + FRAMESKIP_AUTO, + FRAMESKIP_MANUAL, +} frameskip_style_t; + +typedef enum { + SCALING_NONE = 0, + SCALING_ASPECT_SHARP, + SCALING_ASPECT_SMOOTH, + SCALING_FULL_SHARP, + SCALING_FULL_SMOOTH, +} scaling_mode_t; + + +extern int should_quit; + +extern int state_slot; +extern int dynarec_enable; +extern frameskip_style_t frameskip_style; +extern scaling_mode_t scaling_mode; +extern int max_frameskip; +extern int color_correct; +extern int lcd_blend; +extern int show_fps; +extern int limit_frames; + +extern uint16_t *gba_screen_pixels_prev; +extern uint16_t *gba_processed_pixels; + +#define array_size(x) (sizeof(x) / sizeof(x[0])) + +void state_file_name(char *buf, size_t len, unsigned state_slot); +void config_file_name(char *buf, size_t len, int is_game); +void handle_emu_action(emu_action action); +int save_state_file(unsigned state_slot); +int load_state_file(unsigned state_slot); + +#endif /* __FRONTEND_MAIN_H__ */ diff --git a/frontend/menu.c b/frontend/menu.c new file mode 100644 index 0000000..44bcd07 --- /dev/null +++ b/frontend/menu.c @@ -0,0 +1,335 @@ +#include <sys/stat.h> +#include "common.h" + +#include "frontend/config.h" +#include "frontend/main.h" +#include "frontend/menu.h" +#include "frontend/scale.h" +#include "frontend/plat.h" + +#include "frontend/libpicofe/config_file.h" + +#define MENU_ALIGN_LEFT 0 +#define MENU_X2 0 + +typedef enum +{ + MA_NONE = 1, + MA_MAIN_RESUME_GAME, + MA_MAIN_SAVE_STATE, + MA_MAIN_LOAD_STATE, + MA_MAIN_RESET_GAME, + MA_MAIN_CREDITS, + MA_MAIN_EXIT, + MA_OPT_SAVECFG, + MA_OPT_SAVECFG_GAME, + MA_CTRL_PLAYER1, + MA_CTRL_EMU, +} menu_id; + +int menu_save_config(int is_game); +void menu_set_defaults(void); + +me_bind_action me_ctrl_actions[] = +{ + { "UP ", 1 << KBIT_UP}, + { "DOWN ", 1 << KBIT_DOWN }, + { "LEFT ", 1 << KBIT_LEFT }, + { "RIGHT ", 1 << KBIT_RIGHT }, + { "A BUTTON ", 1 << KBIT_A }, + { "B BUTTON ", 1 << KBIT_B }, + { "START ", 1 << KBIT_START }, + { "SELECT ", 1 << KBIT_SELECT }, + { "L BUTTON ", 1 << KBIT_L }, + { "R BUTTON ", 1 << KBIT_R }, + { NULL, 0 } +}; + +me_bind_action emuctrl_actions[] = +{ + { "Save State ", 1 << EACTION_SAVE_STATE }, + { "Load State ", 1 << EACTION_LOAD_STATE }, + { "Toggle Frameskip ", 1 << EACTION_TOGGLE_FSKIP }, + { "Show/Hide FPS ", 1 << EACTION_TOGGLE_FPS }, + { "Toggle FF ", 1 << EACTION_TOGGLE_FF }, + { "Enter Menu ", 1 << EACTION_MENU }, + { NULL, 0 } +}; + +int emu_check_save_file(int slot, int *time) +{ + char fname[MAXPATHLEN]; + struct stat status; + int ret; + + state_file_name(fname, sizeof(fname), slot); + + ret = stat(fname, &status); + if (ret != 0) + return 0; + + return 1; +} + +int emu_save_load_game(int load, int unused) +{ + int ret; + + if (load) + ret = load_state_file(state_slot); + else + ret = save_state_file(state_slot); + + return ret; +} + +#include "frontend/libpicofe/menu.c" + +static const char *mgn_saveloadcfg(int id, int *offs) +{ + return ""; +} + +static int mh_restore_defaults(int id, int keys) +{ + menu_set_defaults(); + menu_update_msg("defaults restored"); + return 1; +} + +static int mh_savecfg(int id, int keys) +{ + if (menu_save_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0) + menu_update_msg("config saved"); + else + menu_update_msg("failed to write config"); + + return 1; +} + +static const char h_restore_def[] = "Switches back to default / recommended\n" + "configuration"; + +static const char h_color_correct[] = "Modifies colors to simulate original display"; +static const char h_lcd_blend[] = "Blends frames to simulate LCD lag"; +static const char h_show_fps[] = "Shows frames and vsyncs per second"; +static const char h_dynarec_enable[] = "Improves performance, but may reduce accuracy"; + + +static const char *men_frameskip[] = { "OFF", "Auto", "Manual", NULL }; + +static const char *men_scaling[] = { "Native", "3:2 Sharp", "3:2 Smooth", "4:3 Sharp", "4:3 Smooth", NULL}; + +static menu_entry e_menu_options[] = +{ + mee_enum ("Frameskip", 0, frameskip_style, men_frameskip), + mee_range ("Max Frameskip", 0, max_frameskip, 1, 5), + mee_enum ("Scaling", 0, scaling_mode, men_scaling), + mee_onoff_h ("Color Correction", 0, color_correct, 1, h_color_correct), + mee_onoff_h ("LCD Ghosting", 0, lcd_blend, 1, h_lcd_blend), + mee_onoff_h ("Dynamic Recompiler", 0, dynarec_enable, 1, h_dynarec_enable), + mee_onoff_h ("Show FPS", 0, show_fps, 1, h_show_fps), + mee_cust_nosave ("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg), + mee_cust_nosave ("Save game config", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg), + mee_handler_h ("Restore defaults", mh_restore_defaults, h_restore_def), + mee_end, +}; + +static int menu_loop_options(int id, int keys) +{ + static int sel = 0; + int prev_dynarec_enable = dynarec_enable; + + me_loop(e_menu_options, &sel); + + if (prev_dynarec_enable != dynarec_enable) + init_caches(); + + return 0; +} + +static int key_config_loop_wrap(int id, int keys) +{ + switch (id) { + case MA_CTRL_PLAYER1: + key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0); + break; + case MA_CTRL_EMU: + key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1); + break; + default: + break; + } + return 0; +} + +static menu_entry e_menu_keyconfig[] = +{ + mee_handler_id ("Player controls", MA_CTRL_PLAYER1, key_config_loop_wrap), + mee_handler_id ("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap), + mee_cust_nosave ("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg), + mee_cust_nosave ("Save game config", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg), + mee_end, +}; + +static int menu_loop_keyconfig(int id, int keys) +{ + static int sel = 0; + me_loop(e_menu_keyconfig, &sel); + return 0; +} + + +static int main_menu_handler(int id, int keys) +{ + switch (id) + { + case MA_MAIN_RESUME_GAME: + return 1; + case MA_MAIN_SAVE_STATE: + return menu_loop_savestate(0); + case MA_MAIN_LOAD_STATE: + return menu_loop_savestate(1); + case MA_MAIN_RESET_GAME: + reset_gba(); + return 1; + case MA_MAIN_EXIT: + should_quit = 1; + return 1; + default: + lprintf("%s: something unknown selected\n", __FUNCTION__); + break; + } + + return 0; +} + +static menu_entry e_menu_main[] = +{ + mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler), + mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler), + mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler), + mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler), + mee_handler ("Options", menu_loop_options), + mee_handler ("Controls", menu_loop_keyconfig), + /* mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler), */ + mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler), + mee_end, +}; + +void draw_savestate_bg(int slot) +{ +} + +void menu_set_defaults(void) +{ + dynarec_enable = 1; + frameskip_style = 1; + scaling_mode = 0; + max_frameskip = 3; + color_correct = 0; + lcd_blend = 0; + show_fps = 0; + limit_frames = 1; +} + +void menu_loop(void) +{ + static int sel = 0; + plat_video_menu_enter(1); + video_scale(g_menubg_ptr, g_menuscreen_h, g_menuscreen_pp); + menu_darken_bg(g_menubg_ptr, g_menubg_ptr, g_menuscreen_h * g_menuscreen_pp, 0); + me_loop_d(e_menu_main, &sel, NULL, NULL); + + /* wait until menu, ok, back is released */ + while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK)) + ; + memset(g_menubg_ptr, 0, g_menuscreen_h * g_menuscreen_pp * sizeof(uint16_t)); + + plat_video_menu_leave(); +} + +int menu_save_config(int is_game) +{ + char config_filename[MAXPATHLEN]; + FILE *config_file; + + config_file_name(config_filename, MAXPATHLEN, is_game); + config_file = fopen(config_filename, "wb"); + if (!config_file) { + fprintf(stderr, "Could not write config to %s\n", config_filename); + return -1; + } + + config_write(config_file); + config_write_keys(config_file); + + fclose(config_file); + return 0; +} + + +void menu_load_config(void) +{ + char config_filename[MAXPATHLEN]; + FILE *config_file; + size_t length; + char *config; + + config_file_name(config_filename, MAXPATHLEN, 1); + config_file = fopen(config_filename, "rb"); + if (!config_file) { + config_file_name(config_filename, MAXPATHLEN, 0); + config_file = fopen(config_filename, "rb"); + } + + if (!config_file) + return; + + fseek(config_file, 0, SEEK_END); + length = ftell(config_file); + fseek(config_file, 0, SEEK_SET); + + config = calloc(1, length); + + if (fread(config, 1, length, config_file)) + fclose(config_file); + + config_read(config); + config_read_keys(config); + + if (config) + free(config); +} + +void menu_init(void) +{ + menu_init_base(); + + menu_set_defaults(); + menu_load_config(); + + g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1); + g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1); + if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) { + fprintf(stderr, "OOM\n"); + exit(1); + } +} + +void menu_finish(void) +{ +} + +static void debug_menu_loop(void) +{ +} + +void menu_update_msg(const char *msg) +{ + strncpy(menu_error_msg, msg, sizeof(menu_error_msg)); + menu_error_msg[sizeof(menu_error_msg) - 1] = 0; + + menu_error_time = plat_get_ticks_ms(); + lprintf("msg: %s\n", menu_error_msg); +} diff --git a/frontend/menu.h b/frontend/menu.h new file mode 100644 index 0000000..e151098 --- /dev/null +++ b/frontend/menu.h @@ -0,0 +1,10 @@ +#ifndef _FRONTEND_MENU_H__ +#define _FRONTEND_MENU_H__ + +#include "frontend/libpicofe/menu.h" + +void menu_init(void); +void menu_loop(void); +void menu_finish(void); + +#endif diff --git a/frontend/plat.h b/frontend/plat.h new file mode 100644 index 0000000..dc4be73 --- /dev/null +++ b/frontend/plat.h @@ -0,0 +1,19 @@ +#ifndef __FRONTEND_PLAT_H__ +#define __FRONTEND_PLAT_H__ + +#define HUD_LEN 39 + +int plat_init(void); +void plat_finish(void); +void plat_minimize(void); +void *plat_prepare_screenshot(int *w, int *h, int *bpp); + +void plat_video_open(void); +void plat_video_set_msg(char *new_msg); +void plat_video_flip(void); +void plat_video_close(void); + +float plat_sound_capacity(void); +void plat_sound_write(void *data, int bytes); + +#endif /* __FRONTEND_PLAT_H__ */ diff --git a/frontend/plat_linux.c b/frontend/plat_linux.c new file mode 100644 index 0000000..09d9a48 --- /dev/null +++ b/frontend/plat_linux.c @@ -0,0 +1,258 @@ +#include <SDL/SDL.h> +#include "common.h" + +#include "frontend/main.h" +#include "frontend/plat.h" +#include "frontend/scale.h" +#include "frontend/libpicofe/menu.h" +#include "frontend/libpicofe/plat.h" +#include "frontend/libpicofe/input.h" +#include "frontend/libpicofe/in_sdl.h" + +static SDL_Surface* screen; + +#define BUF_LEN 8192 +static short buf[BUF_LEN]; +static int buf_w, buf_r; + +static char msg[HUD_LEN]; + +static const struct in_default_bind in_sdl_defbinds[] = { + { SDLK_UP, IN_BINDTYPE_PLAYER12, KBIT_UP }, + { SDLK_DOWN, IN_BINDTYPE_PLAYER12, KBIT_DOWN }, + { SDLK_LEFT, IN_BINDTYPE_PLAYER12, KBIT_LEFT }, + { SDLK_RIGHT, IN_BINDTYPE_PLAYER12, KBIT_RIGHT }, + { SDLK_LCTRL, IN_BINDTYPE_PLAYER12, KBIT_B }, + { SDLK_SPACE, IN_BINDTYPE_PLAYER12, KBIT_A }, + { SDLK_RETURN, IN_BINDTYPE_PLAYER12, KBIT_START }, + { SDLK_RCTRL, IN_BINDTYPE_PLAYER12, KBIT_SELECT }, + { SDLK_TAB, IN_BINDTYPE_PLAYER12, KBIT_L }, + { SDLK_BACKSPACE, IN_BINDTYPE_PLAYER12, KBIT_R }, + { SDLK_ESCAPE, IN_BINDTYPE_EMU, EACTION_MENU }, + { 0, 0, 0 } +}; + +const struct menu_keymap in_sdl_key_map[] = +{ + { SDLK_UP, PBTN_UP }, + { SDLK_DOWN, PBTN_DOWN }, + { SDLK_LEFT, PBTN_LEFT }, + { SDLK_RIGHT, PBTN_RIGHT }, + { SDLK_SPACE, PBTN_MOK }, + { SDLK_LCTRL, PBTN_MBACK }, + { SDLK_LALT, PBTN_MA2 }, + { SDLK_LSHIFT, PBTN_MA3 }, + { SDLK_TAB, PBTN_L }, + { SDLK_BACKSPACE, PBTN_R }, +}; + +const struct menu_keymap in_sdl_joy_map[] = +{ + { SDLK_UP, PBTN_UP }, + { SDLK_DOWN, PBTN_DOWN }, + { SDLK_LEFT, PBTN_LEFT }, + { SDLK_RIGHT, PBTN_RIGHT }, + { SDLK_WORLD_0, PBTN_MOK }, + { SDLK_WORLD_1, PBTN_MBACK }, + { SDLK_WORLD_2, PBTN_MA2 }, + { SDLK_WORLD_3, PBTN_MA3 }, +}; + +static const struct in_pdata in_sdl_platform_data = { + .defbinds = in_sdl_defbinds, + .key_map = in_sdl_key_map, + .kmap_size = array_size(in_sdl_key_map), + .joy_map = in_sdl_joy_map, + .jmap_size = array_size(in_sdl_joy_map), +}; + +static void *fb_flip(void) +{ + SDL_Flip(screen); + return screen->pixels; +} + +void plat_video_menu_enter(int is_rom_loaded) +{ +} + +void plat_video_menu_begin(void) +{ + g_menuscreen_ptr = fb_flip(); +} + +void plat_video_menu_end(void) +{ + g_menuscreen_ptr = fb_flip(); +} + +void plat_video_menu_leave(void) +{ + SDL_LockSurface(screen); + memset(g_menuscreen_ptr, 0, 320 * 240 * sizeof(uint16_t)); + SDL_UnlockSurface(screen); + g_menuscreen_ptr = fb_flip(); + SDL_LockSurface(screen); + memset(g_menuscreen_ptr, 0, 320 * 240 * sizeof(uint16_t)); + SDL_UnlockSurface(screen); +} + +void plat_video_open(void) +{ +} + +void plat_video_set_msg(char *new_msg) +{ + snprintf(msg, HUD_LEN, "%s", new_msg); +} + +void plat_video_flip(void) +{ + video_post_process(); + SDL_LockSurface(screen); + if (msg[0]) + video_clear_msg(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t)); + + video_scale(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t)); + + if (msg[0]) + video_print_msg(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t), msg); + SDL_UnlockSurface(screen); + + g_menuscreen_ptr = fb_flip(); + msg[0] = 0; +} + +void plat_video_close(void) +{ +} + +void plat_sound_callback(void *unused, u8 *stream, int len) +{ + short *p = (short *)stream; + len /= sizeof(short); + + while (buf_r != buf_w && len > 0) { + *p++ = buf[buf_r++]; + buf_r %= BUF_LEN; + --len; + } + + while(len > 0) { + *p++ = 0; + --len; + } +} + +void plat_sound_finish(void) +{ + SDL_PauseAudio(1); + SDL_CloseAudio(); +} + +int plat_sound_init(void) +{ + + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { + return -1; + } + + SDL_AudioSpec spec; + + spec.freq = sound_frequency; + spec.format = AUDIO_S16; + spec.channels = 2; + spec.samples = 512; + spec.callback = plat_sound_callback; + + if (SDL_OpenAudio(&spec, NULL) < 0) { + plat_sound_finish(); + return -1; + } + + SDL_PauseAudio(0); + return 0; +} + +float plat_sound_capacity(void) +{ + int buffered = 0; + if (buf_w != buf_r) { + buffered = buf_w > buf_r ? buf_w - buf_r : (buf_w + BUF_LEN) - buf_r; + } + + return 1.0 - (float)buffered / BUF_LEN; +} + +void plat_sound_write(void *data, int bytes) +{ + short *sound_data = (short *)data; + SDL_LockAudio(); + + while (bytes > 0) { + while (((buf_w + 1) % BUF_LEN) == buf_r) { + SDL_UnlockAudio(); + + if (!limit_frames) + return; + + plat_sleep_ms(1); + SDL_LockAudio(); + } + + buf[buf_w] = *sound_data++; + + ++buf_w; + buf_w %= BUF_LEN; + bytes -= sizeof(short); + } + SDL_UnlockAudio(); +} + +void plat_sdl_event_handler(void *event_) +{ +} + +int plat_init(void) +{ + SDL_Init(SDL_INIT_VIDEO); + screen = SDL_SetVideoMode(320, 240, 16, SDL_SWSURFACE); + + if (screen == NULL) { + printf("%s, failed to set video mode\n", __func__); + return -1; + } + + SDL_ShowCursor(0); + + g_menuscreen_w = 320; + g_menuscreen_h = 240; + g_menuscreen_pp = 320; + g_menuscreen_ptr = fb_flip(); + + if (in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler)) { + fprintf(stderr, "SDL input failed to init: %s\n", SDL_GetError()); + return -1; + } + in_probe(); + + if (plat_sound_init()) { + fprintf(stderr, "SDL sound failed to init: %s\n", SDL_GetError()); + return -1; + } + return 0; +} + +void plat_pre_finish(void) +{ +} + +void plat_finish(void) +{ + plat_sound_finish(); + SDL_Quit(); +} + +void plat_trigger_vibrate(int pad, int low, int high) +{ +} diff --git a/frontend/plat_trimui.c b/frontend/plat_trimui.c new file mode 100644 index 0000000..bdd5a92 --- /dev/null +++ b/frontend/plat_trimui.c @@ -0,0 +1,277 @@ +#include <SDL/SDL.h> +#include "common.h" + +#include "frontend/main.h" +#include "frontend/plat.h" +#include "frontend/scale.h" +#include "frontend/libpicofe/menu.h" +#include "frontend/libpicofe/plat.h" +#include "frontend/libpicofe/input.h" +#include "frontend/libpicofe/in_sdl.h" + +static SDL_Surface* screen; + +#define BUF_LEN 8192 + +static short buf[BUF_LEN]; +static int buf_w, buf_r; + +static char msg[HUD_LEN]; + +static const struct in_default_bind in_sdl_defbinds[] = { + { SDLK_UP, IN_BINDTYPE_PLAYER12, KBIT_UP }, + { SDLK_DOWN, IN_BINDTYPE_PLAYER12, KBIT_DOWN }, + { SDLK_LEFT, IN_BINDTYPE_PLAYER12, KBIT_LEFT }, + { SDLK_RIGHT, IN_BINDTYPE_PLAYER12, KBIT_RIGHT }, + { SDLK_LCTRL, IN_BINDTYPE_PLAYER12, KBIT_B }, + { SDLK_SPACE, IN_BINDTYPE_PLAYER12, KBIT_A }, + { SDLK_RETURN, IN_BINDTYPE_PLAYER12, KBIT_START }, + { SDLK_RCTRL, IN_BINDTYPE_PLAYER12, KBIT_SELECT }, + { SDLK_TAB, IN_BINDTYPE_PLAYER12, KBIT_L }, + { SDLK_BACKSPACE, IN_BINDTYPE_PLAYER12, KBIT_R }, + { SDLK_ESCAPE, IN_BINDTYPE_EMU, EACTION_MENU }, + { 0, 0, 0 } +}; + +const struct menu_keymap in_sdl_key_map[] = +{ + { SDLK_UP, PBTN_UP }, + { SDLK_DOWN, PBTN_DOWN }, + { SDLK_LEFT, PBTN_LEFT }, + { SDLK_RIGHT, PBTN_RIGHT }, + { SDLK_SPACE, PBTN_MOK }, + { SDLK_LCTRL, PBTN_MBACK }, + { SDLK_LALT, PBTN_MA2 }, + { SDLK_LSHIFT, PBTN_MA3 }, + { SDLK_TAB, PBTN_L }, + { SDLK_BACKSPACE, PBTN_R }, +}; + +const struct menu_keymap in_sdl_joy_map[] = +{ + { SDLK_UP, PBTN_UP }, + { SDLK_DOWN, PBTN_DOWN }, + { SDLK_LEFT, PBTN_LEFT }, + { SDLK_RIGHT, PBTN_RIGHT }, + { SDLK_WORLD_0, PBTN_MOK }, + { SDLK_WORLD_1, PBTN_MBACK }, + { SDLK_WORLD_2, PBTN_MA2 }, + { SDLK_WORLD_3, PBTN_MA3 }, +}; + +static const char * const in_sdl_key_names[SDLK_LAST] = { + [SDLK_UP] = "up", + [SDLK_DOWN] = "down", + [SDLK_LEFT] = "left", + [SDLK_RIGHT] = "right", + [SDLK_LSHIFT] = "x", + [SDLK_LCTRL] = "b", + [SDLK_SPACE] = "a", + [SDLK_LALT] = "y", + [SDLK_RETURN] = "start", + [SDLK_RCTRL] = "select", + [SDLK_TAB] = "l", + [SDLK_BACKSPACE] = "r", + [SDLK_ESCAPE] = "menu", +}; + +static const struct in_pdata in_sdl_platform_data = { + .defbinds = in_sdl_defbinds, + .key_map = in_sdl_key_map, + .kmap_size = array_size(in_sdl_key_map), + .joy_map = in_sdl_joy_map, + .jmap_size = array_size(in_sdl_joy_map), + .key_names = in_sdl_key_names, +}; + +static void *fb_flip(void) +{ + SDL_Flip(screen); + return screen->pixels; +} + +void plat_video_menu_enter(int is_rom_loaded) +{ +} + +void plat_video_menu_begin(void) +{ + g_menuscreen_ptr = fb_flip(); +} + +void plat_video_menu_end(void) +{ + g_menuscreen_ptr = fb_flip(); +} + +void plat_video_menu_leave(void) +{ + SDL_LockSurface(screen); + memset(g_menuscreen_ptr, 0, 320 * 240 * sizeof(uint16_t)); + SDL_UnlockSurface(screen); + g_menuscreen_ptr = fb_flip(); + SDL_LockSurface(screen); + memset(g_menuscreen_ptr, 0, 320 * 240 * sizeof(uint16_t)); + SDL_UnlockSurface(screen); +} + +void plat_video_open(void) +{ +} + +void plat_video_set_msg(char *new_msg) +{ + snprintf(msg, HUD_LEN, "%s", new_msg); +} + +void plat_video_flip(void) +{ + video_post_process(); + SDL_LockSurface(screen); + if (msg[0]) + video_clear_msg(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t)); + + video_scale(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t)); + + if (msg[0]) + video_print_msg(screen->pixels, screen->h, screen->pitch / sizeof(uint16_t), msg); + SDL_UnlockSurface(screen); + + g_menuscreen_ptr = fb_flip(); + msg[0] = 0; +} + +void plat_video_close(void) +{ +} + +void plat_sound_callback(void *unused, u8 *stream, int len) +{ + short *p = (short *)stream; + len /= sizeof(short); + + while (buf_r != buf_w && len > 0) { + *p++ = buf[buf_r++]; + buf_r %= BUF_LEN; + --len; + } + + while(len > 0) { + *p++ = 0; + --len; + } +} + +void plat_sound_finish(void) +{ + SDL_PauseAudio(1); + SDL_CloseAudio(); +} + +int plat_sound_init(void) +{ + + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { + return -1; + } + + SDL_AudioSpec spec; + + spec.freq = sound_frequency; + spec.format = AUDIO_S16SYS; + spec.channels = 2; + spec.samples = 512; + spec.callback = plat_sound_callback; + + if (SDL_OpenAudio(&spec, NULL) < 0) { + plat_sound_finish(); + return -1; + } + + SDL_PauseAudio(0); + return 0; +} + +float plat_sound_capacity(void) +{ + int buffered = 0; + if (buf_w != buf_r) { + buffered = buf_w > buf_r ? buf_w - buf_r : (buf_w + BUF_LEN) - buf_r; + } + + return 1.0 - (float)buffered / BUF_LEN; +} + +void plat_sound_write(void *data, int bytes) +{ + short *sound_data = (short *)data; + SDL_LockAudio(); + + while (bytes > 0) { + while (((buf_w + 1) % BUF_LEN) == buf_r) { + SDL_UnlockAudio(); + + if (!limit_frames) + return; + + plat_sleep_ms(1); + SDL_LockAudio(); + } + buf[buf_w] = *sound_data++; + + ++buf_w; + buf_w %= BUF_LEN; + bytes -= sizeof(short); + } + SDL_UnlockAudio(); +} + +void plat_sdl_event_handler(void *event_) +{ +} + +int plat_init(void) +{ + SDL_Init(SDL_INIT_VIDEO); + screen = SDL_SetVideoMode(320, 240, 16, SDL_SWSURFACE); + + if (screen == NULL) { + printf("%s, failed to set video mode\n", __func__); + return -1; + } + + SDL_ShowCursor(0); + + g_menuscreen_w = 320; + g_menuscreen_h = 240; + g_menuscreen_pp = 320; + g_menuscreen_ptr = fb_flip(); + + if (in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler)) { + fprintf(stderr, "SDL input failed to init: %s\n", SDL_GetError()); + return -1; + } + in_probe(); + + sound_frequency = 48000; + + if (plat_sound_init()) { + fprintf(stderr, "SDL sound failed to init: %s\n", SDL_GetError()); + return -1; + } + return 0; +} + +void plat_pre_finish(void) +{ +} + +void plat_finish(void) +{ + plat_sound_finish(); + SDL_Quit(); +} + +void plat_trigger_vibrate(int pad, int low, int high) +{ +} diff --git a/frontend/scale.c b/frontend/scale.c new file mode 100644 index 0000000..3998f9f --- /dev/null +++ b/frontend/scale.c @@ -0,0 +1,604 @@ +#include "common.h" +#include "gba_cc_lut.h" +#include "frontend/main.h" +#include "frontend/libpicofe/fonts.h" + +#define Average(A, B) ((((A) & 0xF7DE) >> 1) + (((B) & 0xF7DE) >> 1) + ((A) & (B) & 0x0821)) + +/* Calculates the average of two pairs of RGB565 pixels. The result is, in + * the lower bits, the average of both lower pixels, and in the upper bits, + * the average of both upper pixels. */ +#define Average32(A, B) ((((A) & 0xF7DEF7DE) >> 1) + (((B) & 0xF7DEF7DE) >> 1) + ((A) & (B) & 0x08210821)) + +/* Raises a pixel from the lower half to the upper half of a pair. */ +#define Raise(N) ((N) << 16) + +/* Extracts the upper pixel of a pair into the lower pixel of a pair. */ +#define Hi(N) ((N) >> 16) + +/* Extracts the lower pixel of a pair. */ +#define Lo(N) ((N) & 0xFFFF) + +/* Calculates the average of two RGB565 pixels. The source of the pixels is + * the lower 16 bits of both parameters. The result is in the lower 16 bits. + * The average is weighted so that the first pixel contributes 3/4 of its + * color and the second pixel contributes 1/4. */ +#define AverageQuarters3_1(A, B) ( (((A) & 0xF7DE) >> 1) + (((A) & 0xE79C) >> 2) + (((B) & 0xE79C) >> 2) + ((( (( ((A) & 0x1863) + ((A) & 0x0821) ) << 1) + ((B) & 0x1863) ) >> 2) & 0x1863) ) + +static inline void gba_upscale(uint16_t *to, uint16_t *from, + uint32_t src_x, uint32_t src_y, uint32_t src_pitch, uint32_t dst_pitch) +{ + /* Before: + * a b c d e f + * g h i j k l + * + * After (multiple letters = average): + * a ab bc c d de ef f + * ag abgh bchi ci dj dejk efkl fl + * g gh hi i j jk kl l + */ + + const uint32_t dst_x = src_x * 4 / 3; + const uint32_t src_skip = src_pitch - src_x * sizeof(uint16_t), + dst_skip = dst_pitch - dst_x * sizeof(uint16_t); + + uint32_t x, y; + + for (y = 0; y < src_y; y += 2) { + for (x = 0; x < src_x / 6; x++) { + // -- Row 1 -- + // Read RGB565 elements in the source grid. + // The notation is high_low (little-endian). + uint32_t b_a = (*(uint32_t*) (from )), + d_c = (*(uint32_t*) (from + 2)), + f_e = (*(uint32_t*) (from + 4)); + + // Generate ab_a from b_a. + *(uint32_t*) (to) = likely(Hi(b_a) == Lo(b_a)) + ? b_a + : Lo(b_a) /* 'a' verbatim to low pixel */ | + Raise(Average(Hi(b_a), Lo(b_a))) /* ba to high pixel */; + + // Generate c_bc from b_a and d_c. + *(uint32_t*) (to + 2) = likely(Hi(b_a) == Lo(d_c)) + ? Lo(d_c) | Raise(Lo(d_c)) + : Raise(Lo(d_c)) /* 'c' verbatim to high pixel */ | + Average(Lo(d_c), Hi(b_a)) /* bc to low pixel */; + + // Generate de_d from d_c and f_e. + *(uint32_t*) (to + 4) = likely(Hi(d_c) == Lo(f_e)) + ? Lo(f_e) | Raise(Lo(f_e)) + : Hi(d_c) /* 'd' verbatim to low pixel */ | + Raise(Average(Lo(f_e), Hi(d_c))) /* de to high pixel */; + + // Generate f_ef from f_e. + *(uint32_t*) (to + 6) = likely(Hi(f_e) == Lo(f_e)) + ? f_e + : Raise(Hi(f_e)) /* 'f' verbatim to high pixel */ | + Average(Hi(f_e), Lo(f_e)) /* ef to low pixel */; + + if (likely(y + 1 < src_y)) // Is there a source row 2? + { + // -- Row 2 -- + uint32_t h_g = (*(uint32_t*) ((uint8_t*) from + src_pitch )), + j_i = (*(uint32_t*) ((uint8_t*) from + src_pitch + 4)), + l_k = (*(uint32_t*) ((uint8_t*) from + src_pitch + 8)); + + // Generate abgh_ag from b_a and h_g. + uint32_t bh_ag = Average32(b_a, h_g); + *(uint32_t*) ((uint8_t*) to + dst_pitch) = likely(Hi(bh_ag) == Lo(bh_ag)) + ? bh_ag + : Lo(bh_ag) /* ag verbatim to low pixel */ | + Raise(Average(Hi(bh_ag), Lo(bh_ag))) /* abgh to high pixel */; + + // Generate ci_bchi from b_a, d_c, h_g and j_i. + uint32_t ci_bh = + Hi(bh_ag) /* bh verbatim to low pixel */ | + Raise(Average(Lo(d_c), Lo(j_i))) /* ci to high pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch + 4) = likely(Hi(ci_bh) == Lo(ci_bh)) + ? ci_bh + : Raise(Hi(ci_bh)) /* ci verbatim to high pixel */ | + Average(Hi(ci_bh), Lo(ci_bh)) /* bchi to low pixel */; + + // Generate fl_efkl from f_e and l_k. + uint32_t fl_ek = Average32(f_e, l_k); + *(uint32_t*) ((uint8_t*) to + dst_pitch + 12) = likely(Hi(fl_ek) == Lo(fl_ek)) + ? fl_ek + : Raise(Hi(fl_ek)) /* fl verbatim to high pixel */ | + Average(Hi(fl_ek), Lo(fl_ek)) /* efkl to low pixel */; + + // Generate dejk_dj from d_c, f_e, j_i and l_k. + uint32_t ek_dj = + Raise(Lo(fl_ek)) /* ek verbatim to high pixel */ | + Average(Hi(d_c), Hi(j_i)) /* dj to low pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch + 8) = likely(Hi(ek_dj) == Lo(ek_dj)) + ? ek_dj + : Lo(ek_dj) /* dj verbatim to low pixel */ | + Raise(Average(Hi(ek_dj), Lo(ek_dj))) /* dejk to high pixel */; + + // -- Row 3 -- + // Generate gh_g from h_g. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2) = likely(Hi(h_g) == Lo(h_g)) + ? h_g + : Lo(h_g) /* 'g' verbatim to low pixel */ | + Raise(Average(Hi(h_g), Lo(h_g))) /* gh to high pixel */; + + // Generate i_hi from g_h and j_i. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 4) = likely(Hi(h_g) == Lo(j_i)) + ? Lo(j_i) | Raise(Lo(j_i)) + : Raise(Lo(j_i)) /* 'i' verbatim to high pixel */ | + Average(Lo(j_i), Hi(h_g)) /* hi to low pixel */; + + // Generate jk_j from j_i and l_k. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 8) = likely(Hi(j_i) == Lo(l_k)) + ? Lo(l_k) | Raise(Lo(l_k)) + : Hi(j_i) /* 'j' verbatim to low pixel */ | + Raise(Average(Hi(j_i), Lo(l_k))) /* jk to high pixel */; + + // Generate l_kl from l_k. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 12) = likely(Hi(l_k) == Lo(l_k)) + ? l_k + : Raise(Hi(l_k)) /* 'l' verbatim to high pixel */ | + Average(Hi(l_k), Lo(l_k)) /* kl to low pixel */; + } + + from += 6; + to += 8; + } + + // Skip past the waste at the end of the first line, if any, + // then past 1 whole lines of source and 2 of destination. + from = (uint16_t*) ((uint8_t*) from + src_skip + src_pitch); + to = (uint16_t*) ((uint8_t*) to + dst_skip + 2 * dst_pitch); + } +} + +static inline void gba_upscale_aspect(uint16_t *to, uint16_t *from, + uint32_t src_x, uint32_t src_y, uint32_t src_pitch, uint32_t dst_pitch) +{ + /* Before: + * a b c d e f + * g h i j k l + * m n o p q r + * + * After (multiple letters = average): + * a ab bc c d de ef f + * ag abgh bchi ci dj dejk efkl fl + * gm ghmn hino io jp jkpq klqr lr + * m mn no o p pq qr r + */ + + const uint32_t dst_x = src_x * 4 / 3; + const uint32_t src_skip = src_pitch - src_x * sizeof(uint16_t), + dst_skip = dst_pitch - dst_x * sizeof(uint16_t); + + uint32_t x, y; + + for (y = 0; y < src_y; y += 3) { + for (x = 0; x < src_x / 6; x++) { + // -- Row 1 -- + // Read RGB565 elements in the source grid. + // The notation is high_low (little-endian). + uint32_t b_a = (*(uint32_t*) (from )), + d_c = (*(uint32_t*) (from + 2)), + f_e = (*(uint32_t*) (from + 4)); + + // Generate ab_a from b_a. + *(uint32_t*) (to) = likely(Hi(b_a) == Lo(b_a)) + ? b_a + : Lo(b_a) /* 'a' verbatim to low pixel */ | + Raise(Average(Hi(b_a), Lo(b_a))) /* ba to high pixel */; + + // Generate c_bc from b_a and d_c. + *(uint32_t*) (to + 2) = likely(Hi(b_a) == Lo(d_c)) + ? Lo(d_c) | Raise(Lo(d_c)) + : Raise(Lo(d_c)) /* 'c' verbatim to high pixel */ | + Average(Lo(d_c), Hi(b_a)) /* bc to low pixel */; + + // Generate de_d from d_c and f_e. + *(uint32_t*) (to + 4) = likely(Hi(d_c) == Lo(f_e)) + ? Lo(f_e) | Raise(Lo(f_e)) + : Hi(d_c) /* 'd' verbatim to low pixel */ | + Raise(Average(Lo(f_e), Hi(d_c))) /* de to high pixel */; + + // Generate f_ef from f_e. + *(uint32_t*) (to + 6) = likely(Hi(f_e) == Lo(f_e)) + ? f_e + : Raise(Hi(f_e)) /* 'f' verbatim to high pixel */ | + Average(Hi(f_e), Lo(f_e)) /* ef to low pixel */; + + if (likely(y + 1 < src_y)) // Is there a source row 2? + { + // -- Row 2 -- + uint32_t h_g = (*(uint32_t*) ((uint8_t*) from + src_pitch )), + j_i = (*(uint32_t*) ((uint8_t*) from + src_pitch + 4)), + l_k = (*(uint32_t*) ((uint8_t*) from + src_pitch + 8)); + + // Generate abgh_ag from b_a and h_g. + uint32_t bh_ag = Average32(b_a, h_g); + *(uint32_t*) ((uint8_t*) to + dst_pitch) = likely(Hi(bh_ag) == Lo(bh_ag)) + ? bh_ag + : Lo(bh_ag) /* ag verbatim to low pixel */ | + Raise(Average(Hi(bh_ag), Lo(bh_ag))) /* abgh to high pixel */; + + // Generate ci_bchi from b_a, d_c, h_g and j_i. + uint32_t ci_bh = + Hi(bh_ag) /* bh verbatim to low pixel */ | + Raise(Average(Lo(d_c), Lo(j_i))) /* ci to high pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch + 4) = likely(Hi(ci_bh) == Lo(ci_bh)) + ? ci_bh + : Raise(Hi(ci_bh)) /* ci verbatim to high pixel */ | + Average(Hi(ci_bh), Lo(ci_bh)) /* bchi to low pixel */; + + // Generate fl_efkl from f_e and l_k. + uint32_t fl_ek = Average32(f_e, l_k); + *(uint32_t*) ((uint8_t*) to + dst_pitch + 12) = likely(Hi(fl_ek) == Lo(fl_ek)) + ? fl_ek + : Raise(Hi(fl_ek)) /* fl verbatim to high pixel */ | + Average(Hi(fl_ek), Lo(fl_ek)) /* efkl to low pixel */; + + // Generate dejk_dj from d_c, f_e, j_i and l_k. + uint32_t ek_dj = + Raise(Lo(fl_ek)) /* ek verbatim to high pixel */ | + Average(Hi(d_c), Hi(j_i)) /* dj to low pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch + 8) = likely(Hi(ek_dj) == Lo(ek_dj)) + ? ek_dj + : Lo(ek_dj) /* dj verbatim to low pixel */ | + Raise(Average(Hi(ek_dj), Lo(ek_dj))) /* dejk to high pixel */; + + if (likely(y + 2 < src_y)) // Is there a source row 3? + { + // -- Row 3 -- + uint32_t n_m = (*(uint32_t*) ((uint8_t*) from + src_pitch * 2 )), + p_o = (*(uint32_t*) ((uint8_t*) from + src_pitch * 2 + 4)), + r_q = (*(uint32_t*) ((uint8_t*) from + src_pitch * 2 + 8)); + + // Generate ghmn_gm from h_g and n_m. + uint32_t hn_gm = Average32(h_g, n_m); + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2) = likely(Hi(hn_gm) == Lo(hn_gm)) + ? hn_gm + : Lo(hn_gm) /* gm verbatim to low pixel */ | + Raise(Average(Hi(hn_gm), Lo(hn_gm))) /* ghmn to high pixel */; + + // Generate io_hino from h_g, j_i, n_m and p_o. + uint32_t io_hn = + Hi(hn_gm) /* hn verbatim to low pixel */ | + Raise(Average(Lo(j_i), Lo(p_o))) /* io to high pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 4) = likely(Hi(io_hn) == Lo(io_hn)) + ? io_hn + : Raise(Hi(io_hn)) /* io verbatim to high pixel */ | + Average(Hi(io_hn), Lo(io_hn)) /* hino to low pixel */; + + // Generate lr_klqr from l_k and r_q. + uint32_t lr_kq = Average32(l_k, r_q); + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 12) = likely(Hi(lr_kq) == Lo(lr_kq)) + ? lr_kq + : Raise(Hi(lr_kq)) /* lr verbatim to high pixel */ | + Average(Hi(lr_kq), Lo(lr_kq)) /* klqr to low pixel */; + + // Generate jkpq_jp from j_i, l_k, p_o and r_q. + uint32_t kq_jp = + Raise(Lo(lr_kq)) /* kq verbatim to high pixel */ | + Average(Hi(j_i), Hi(p_o)) /* jp to low pixel */; + *(uint32_t*) ((uint8_t*) to + dst_pitch * 2 + 8) = likely(Hi(kq_jp) == Lo(kq_jp)) + ? kq_jp + : Lo(kq_jp) /* jp verbatim to low pixel */ | + Raise(Average(Hi(kq_jp), Lo(kq_jp))) /* jkpq to high pixel */; + + // -- Row 4 -- + // Generate mn_m from n_m. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 3) = likely(Hi(n_m) == Lo(n_m)) + ? n_m + : Lo(n_m) /* 'm' verbatim to low pixel */ | + Raise(Average(Hi(n_m), Lo(n_m))) /* mn to high pixel */; + + // Generate o_no from n_m and p_o. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 3 + 4) = likely(Hi(n_m) == Lo(p_o)) + ? Lo(p_o) | Raise(Lo(p_o)) + : Raise(Lo(p_o)) /* 'o' verbatim to high pixel */ | + Average(Lo(p_o), Hi(n_m)) /* no to low pixel */; + + // Generate pq_p from p_o and r_q. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 3 + 8) = likely(Hi(p_o) == Lo(r_q)) + ? Lo(r_q) | Raise(Lo(r_q)) + : Hi(p_o) /* 'p' verbatim to low pixel */ | + Raise(Average(Hi(p_o), Lo(r_q))) /* pq to high pixel */; + + // Generate r_qr from r_q. + *(uint32_t*) ((uint8_t*) to + dst_pitch * 3 + 12) = likely(Hi(r_q) == Lo(r_q)) + ? r_q + : Raise(Hi(r_q)) /* 'r' verbatim to high pixel */ | + Average(Hi(r_q), Lo(r_q)) /* qr to low pixel */; + } + } + + from += 6; + to += 8; + } + + // Skip past the waste at the end of the first line, if any, + // then past 2 whole lines of source and 3 of destination. + from = (uint16_t*) ((uint8_t*) from + src_skip + 2 * src_pitch); + to = (uint16_t*) ((uint8_t*) to + dst_skip + 3 * dst_pitch); + } +} + +static inline void gba_nofilter_noscale(uint16_t *dst, uint32_t dst_h, uint32_t dst_pitch, uint16_t *src) { + int dst_x = ((dst_pitch - GBA_SCREEN_PITCH) / 2); + int dst_y = ((dst_h - GBA_SCREEN_HEIGHT) / 2); + + for (int y = 0; y < GBA_SCREEN_HEIGHT; y++) { + memcpy(dst + (dst_y + y) * dst_pitch + dst_x, + src + y * GBA_SCREEN_PITCH, + GBA_SCREEN_PITCH * sizeof(src[0])); + } +} + +/* drowsnug's nofilter upscaler, edited by eggs for smoothness */ +#define AVERAGE16(c1, c2) (((c1) + (c2) + (((c1) ^ (c2)) & 0x0821))>>1) //More accurate +static inline void gba_smooth_upscale(uint16_t *dst, uint16_t *src, int h) +{ + int Eh = 0; + int dh = 0; + int width = 240; + int vf = 0; + + dst += ((240-h)/2) * 320; // blank upper border. h=240(full) or h=214(aspect) + + int x, y; + for (y = 0; y < h; y++) + { + int source = dh * width; + for (x = 0; x < 320/4; x++) + { + register uint16_t a, b, c; + + a = src[source]; + b = src[source+1]; + c = src[source+2]; + + if(vf == 1){ + a = AVERAGE16(a, src[source+width]); + b = AVERAGE16(b, src[source+width+1]); + c = AVERAGE16(c, src[source+width+2]); + } + + *dst++ = a; + *dst++ = AVERAGE16(AVERAGE16(a,b),b); + *dst++ = AVERAGE16(b,AVERAGE16(b,c)); + *dst++ = c; + source+=3; + + } + Eh += 160; + if(Eh >= h) { + Eh -= h; + dh++; + vf = 0; + } + else + vf = 1; + } +} + +#define EXTRACT(c, mask, offset) ((c >> offset) & mask) +#define BLENDCHANNEL(cl, cm, cr, mask, offset) ((((EXTRACT(cl, mask, offset) + 2 * EXTRACT(cm, mask, offset) + EXTRACT(cr, mask, offset)) >> 2) & mask) << offset) +#define BLENDB(cl, cm, cr) BLENDCHANNEL(cl, cm, cr, 0b0000000000011111, 0) +#define BLENDG(cl, cm, cr) BLENDCHANNEL(cl, cm, cr, 0b0000011111100000, 0) +#define BLENDR(cl, cm, cr) BLENDCHANNEL(cl, cm, cr, 0b0011111000000000, 2) +static inline void gba_smooth_subpx_upscale(uint16_t *dst, uint16_t *src, int h) +{ + int Eh = 0; + int dh = 0; + int width = 240; + int vf = 0; + + dst += ((240-h)/2) * 320; // blank upper border. h=240(full) or h=214(aspect) + + int x, y; + for (y = 0; y < h; y++) + { + int source = dh * width; + for (x = 0; x < 320/4; x++) + { + register uint16_t a, b, c; + + a = src[source]; + b = src[source+1]; + c = src[source+2]; + + if(vf == 1){ + a = AVERAGE16(a, src[source+width]); + b = AVERAGE16(b, src[source+width+1]); + c = AVERAGE16(c, src[source+width+2]); + } + + *dst++ = a; + *dst++ = BLENDB(a, a, b) | BLENDG(a, b, b) | BLENDR(b, b, b); + *dst++ = BLENDB(b, b, b) | BLENDG(b, b, c) | BLENDR(b, c, c); + *dst++ = c; + + source+=3; + } + Eh += 160; + if(Eh >= h) { + Eh -= h; + dh++; + vf = 0; + } + else + vf = 1; + } +} + +void video_clear_msg(uint16_t *dst, uint32_t h, uint32_t pitch) +{ + memset(dst + (h - 10) * pitch, 0, 10 * pitch * sizeof(uint16_t)); +} + +void video_print_msg(uint16_t *dst, uint32_t h, uint32_t pitch, char *msg) +{ + basic_text_out16_nf(dst, pitch, 2, h - 10, msg); +} + +void video_scale(uint16_t *dst, uint32_t h, uint32_t pitch) { + uint16_t *gba_screen_pixels_buf = gba_screen_pixels; + + if (color_correct || lcd_blend) + gba_screen_pixels_buf = gba_processed_pixels; + + switch (scaling_mode) + { + case SCALING_ASPECT_SHARP: + gba_smooth_subpx_upscale(dst, gba_screen_pixels_buf, 214); + break; + case SCALING_ASPECT_SMOOTH: + gba_smooth_upscale(dst, gba_screen_pixels_buf, 214); + break; + case SCALING_FULL_SHARP: + gba_smooth_subpx_upscale(dst, gba_screen_pixels_buf, 240); + break; + case SCALING_FULL_SMOOTH: + gba_smooth_upscale(dst, gba_screen_pixels_buf, 240); + break; + default: + gba_nofilter_noscale(dst, h, pitch, gba_screen_pixels_buf); + break; + } +} + +/* Video post processing START */ + +/* Note: This code is intentionally W.E.T. + * (Write Everything Twice). These functions + * are performance critical, and we cannot + * afford to do unnecessary comparisons/switches + * inside the inner for loops */ + +static void video_post_process_cc(void) +{ + uint16_t *src = gba_screen_pixels; + uint16_t *dst = gba_processed_pixels; + size_t x, y; + + for (y = 0; y < GBA_SCREEN_HEIGHT; y++) + { + for (x = 0; x < GBA_SCREEN_PITCH; x++) + { + u16 src_color = *(src + x); + + /* Convert colour to RGB555 and perform lookup */ + *(dst + x) = *(gba_cc_lut + (((src_color & 0xFFC0) >> 1) | (src_color & 0x1F))); + } + + src += GBA_SCREEN_PITCH; + dst += GBA_SCREEN_PITCH; + } +} + +static void video_post_process_mix(void) +{ + uint16_t *src_curr = gba_screen_pixels; + uint16_t *src_prev = gba_screen_pixels_prev; + uint16_t *dst = gba_processed_pixels; + size_t x, y; + + for (y = 0; y < GBA_SCREEN_HEIGHT; y++) + { + for (x = 0; x < GBA_SCREEN_PITCH; x++) + { + /* Get colours from current + previous frames (RGB565) */ + uint16_t rgb_curr = *(src_curr + x); + uint16_t rgb_prev = *(src_prev + x); + + /* Store colours for next frame */ + *(src_prev + x) = rgb_curr; + + /* Mix colours + * > "Mixing Packed RGB Pixels Efficiently" + * http://blargg.8bitalley.com/info/rgb_mixing.html */ + *(dst + x) = (rgb_curr + rgb_prev + ((rgb_curr ^ rgb_prev) & 0x821)) >> 1; + } + + src_curr += GBA_SCREEN_PITCH; + src_prev += GBA_SCREEN_PITCH; + dst += GBA_SCREEN_PITCH; + } +} + +static void video_post_process_cc_mix(void) +{ + uint16_t *src_curr = gba_screen_pixels; + uint16_t *src_prev = gba_screen_pixels_prev; + uint16_t *dst = gba_processed_pixels; + size_t x, y; + + for (y = 0; y < GBA_SCREEN_HEIGHT; y++) + { + for (x = 0; x < GBA_SCREEN_PITCH; x++) + { + /* Get colours from current + previous frames (RGB565) */ + uint16_t rgb_curr = *(src_curr + x); + uint16_t rgb_prev = *(src_prev + x); + + /* Store colours for next frame */ + *(src_prev + x) = rgb_curr; + + /* Mix colours + * > "Mixing Packed RGB Pixels Efficiently" + * http://blargg.8bitalley.com/info/rgb_mixing.html */ + uint16_t rgb_mix = (rgb_curr + rgb_prev + ((rgb_curr ^ rgb_prev) & 0x821)) >> 1; + + /* Convert colour to RGB555 and perform lookup */ + *(dst + x) = *(gba_cc_lut + (((rgb_mix & 0xFFC0) >> 1) | (rgb_mix & 0x1F))); + } + + src_curr += GBA_SCREEN_PITCH; + src_prev += GBA_SCREEN_PITCH; + dst += GBA_SCREEN_PITCH; + } +} + +void video_post_process(void) +{ + size_t buf_size = GBA_SCREEN_PITCH * GBA_SCREEN_HEIGHT * sizeof(u16); + + /* If post processing is disabled, return + * immediately */ + if (!color_correct && !lcd_blend) + return; + + /* Initialise output buffer, if required */ + if (!gba_processed_pixels && + (color_correct || lcd_blend)) + { + gba_processed_pixels = (u16*)malloc(buf_size); + + if (!gba_processed_pixels) + return; + + memset(gba_processed_pixels, 0xFFFF, buf_size); + } + + /* Initialise 'history' buffer, if required */ + if (!gba_screen_pixels_prev && + lcd_blend) + { + gba_screen_pixels_prev = (u16*)malloc(buf_size); + + if (!gba_screen_pixels_prev) + return; + + memset(gba_screen_pixels_prev, 0xFFFF, buf_size); + } + + /* Assign post processing function */ + if (color_correct && lcd_blend) + video_post_process_cc_mix(); + else if (color_correct) + video_post_process_cc(); + else if (lcd_blend) + video_post_process_mix(); +} + +/* Video post processing END */ diff --git a/frontend/scale.h b/frontend/scale.h new file mode 100644 index 0000000..fd538eb --- /dev/null +++ b/frontend/scale.h @@ -0,0 +1,9 @@ +#ifndef __FRONTEND_SCALE_H__ +#define __FRONTEND_SCALE_H__ + +void video_post_process(void); +void video_scale(uint16_t *dst, uint32_t dst_h, uint32_t dst_pitch); +void video_clear_msg(uint16_t *dst, uint32_t dst_h, uint32_t dst_pitch); +void video_print_msg(uint16_t *dst, uint32_t dst_h, uint32_t dst_pitch, char *msg); + +#endif /* __FRONTEND_SCALE_H__ */ diff --git a/gba_memory.c b/gba_memory.c index 4376cf8..f397797 100644 --- a/gba_memory.c +++ b/gba_memory.c @@ -18,6 +18,7 @@ */ #include "common.h" +#include "zip.h" /* Sound */ #define gbc_sound_tone_control_low(channel, address) \ @@ -309,7 +310,8 @@ u16 io_registers[1024 * 16]; u8 ewram[1024 * 256 * 2]; u8 iwram[1024 * 32 * 2]; u8 vram[1024 * 96]; -u8 bios_rom[1024 * 16]; + +u8 bios_rom[1024 * 16 * 2]; u32 bios_read_protect; // Up to 128kb, store SRAM, flash ROM, or EEPROM here. @@ -2374,11 +2376,16 @@ char gamepak_code[5]; char gamepak_maker[3]; char gamepak_filename[512]; -u32 load_gamepak(const struct retro_game_info* info, const char *name) +u32 load_gamepak(const char *name) { + char *dot_position = strrchr(name, '.'); + s32 file_size; char *p; - s32 file_size = load_gamepak_raw(name); + if(!strcmp(dot_position, ".zip")) + file_size = load_file_zip(name); + else + file_size = load_gamepak_raw(name); if(file_size == -1) return -1; @@ -3259,6 +3266,17 @@ void memory_term(void) } } +void bios_region_read_allow(void) +{ + memory_map_read[0] = bios_rom; +} + +void bios_region_read_protect(void) +{ + memory_map_read[0] = NULL; +} + + #define savestate_block(type) \ cpu_##type##_savestate(); \ input_##type##_savestate(); \ diff --git a/gba_memory.h b/gba_memory.h index 0f9dc3a..7e5141f 100644 --- a/gba_memory.h +++ b/gba_memory.h @@ -195,13 +195,15 @@ extern char gamepak_filename[512]; cpu_alert_type dma_transfer(dma_transfer_type *dma); u8 *memory_region(u32 address, u32 *memory_limit); -u32 load_gamepak(const struct retro_game_info* info, const char *name); +u32 load_gamepak(const char *name); u32 load_backup(char *name); s32 load_bios(char *name); void update_backup(void); void init_memory(void); void init_gamepak_buffer(void); void memory_term(void); +void bios_region_read_allow(void); +void bios_region_read_protect(void); u8 *load_gamepak_page(u32 physical_index); extern u8 *gamepak_rom; @@ -218,8 +220,8 @@ extern u16 oam_ram[512]; extern u16 palette_ram_converted[512]; extern u16 io_registers[1024 * 16]; extern u8 vram[1024 * 96]; -extern u8 bios_rom[1024 * 16]; // Double buffer used for SMC detection +extern u8 bios_rom[1024 * 16 * 2]; extern u8 ewram[1024 * 256 * 2]; extern u8 iwram[1024 * 32 * 2]; diff --git a/gpsp_config.h b/gpsp_config.h index ea8db95..a3d8ce1 100644 --- a/gpsp_config.h +++ b/gpsp_config.h @@ -6,10 +6,12 @@ #if defined(PSP) #define ROM_TRANSLATION_CACHE_SIZE (1024 * 512 * 4) #define RAM_TRANSLATION_CACHE_SIZE (1024 * 384) + #define BIOS_TRANSLATION_CACHE_SIZE (1024 * 128) #define TRANSLATION_CACHE_LIMIT_THRESHOLD (1024) #else #define ROM_TRANSLATION_CACHE_SIZE (1024 * 512 * 4 * 5) #define RAM_TRANSLATION_CACHE_SIZE (1024 * 384 * 2) + #define BIOS_TRANSLATION_CACHE_SIZE (1024 * 128 * 2) #define TRANSLATION_CACHE_LIMIT_THRESHOLD (1024 * 32) #endif @@ -18,6 +18,10 @@ */ #include "common.h" +#ifndef __LIBRETRO__ +#include "frontend/main.h" +#include "frontend/libpicofe/input.h" +#endif bool libretro_supports_bitmasks = false; bool libretro_supports_ff_override = false; @@ -57,6 +61,7 @@ static void trigger_key(u32 key) } } +#ifdef __LIBRETRO__ u32 update_input(void) { unsigned i; @@ -132,6 +137,35 @@ u32 update_input(void) return 0; } +#else + +u32 update_input(void) +{ + int actions[IN_BINDTYPE_COUNT] = { 0, }; + uint32_t new_key = 0; + unsigned int emu_act; + int which = EACTION_NONE; + + in_update(actions); + emu_act = actions[IN_BINDTYPE_EMU]; + if (emu_act) { + for (; !(emu_act & 1); emu_act >>= 1, which++) + ; + emu_act = which; + } + handle_emu_action(which); + + new_key = actions[IN_BINDTYPE_PLAYER12]; + + if ((new_key | old_key) != old_key) + trigger_key(new_key); + + old_key = new_key; + io_registers[REG_P1] = (~old_key) & 0x3FF; + + return 0; +} +#endif #define input_savestate_builder(type) \ void input_##type##_savestate(void) \ @@ -22,7 +22,8 @@ static inline int align(int x, int n) { #define MB_ALIGN(x) align(x, 20) int _newlib_vm_size_user = ROM_TRANSLATION_CACHE_SIZE + - RAM_TRANSLATION_CACHE_SIZE; + RAM_TRANSLATION_CACHE_SIZE + + BIOS_TRANSLATION_CACHE_SIZE; int getVMBlock(); @@ -460,6 +461,8 @@ void retro_init(void) PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); ram_translation_cache = mmap(NULL, RAM_TRANSLATION_CACHE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + bios_translation_cache = mmap(NULL, BIOS_TRANSLATION_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); #elif defined(_3DS) if (__ctr_svchax && !translation_caches_inited) { @@ -468,6 +471,7 @@ void retro_init(void) rom_translation_cache_ptr = memalign(0x1000, ROM_TRANSLATION_CACHE_SIZE); ram_translation_cache_ptr = memalign(0x1000, RAM_TRANSLATION_CACHE_SIZE); + bios_translation_cache_ptr = memalign(0x1000, BIOS_TRANSLATION_CACHE_SIZE); svcDuplicateHandle(¤tHandle, 0xFFFF8001); svcControlProcessMemory(currentHandle, @@ -476,9 +480,13 @@ void retro_init(void) svcControlProcessMemory(currentHandle, ram_translation_cache, ram_translation_cache_ptr, RAM_TRANSLATION_CACHE_SIZE, MEMOP_MAP, 0b111); + svcControlProcessMemory(currentHandle, + bios_translation_cache, bios_translation_cache_ptr, + BIOS_TRANSLATION_CACHE_SIZE, MEMOP_MAP, 0b111); svcCloseHandle(currentHandle); rom_translation_ptr = rom_translation_cache; ram_translation_ptr = ram_translation_cache; + bios_translation_ptr = bios_translation_cache; ctr_flush_invalidate_cache(); translation_caches_inited = 1; } @@ -502,8 +510,10 @@ void retro_init(void) rom_translation_cache = (u8*)currentHandle; ram_translation_cache = rom_translation_cache + ROM_TRANSLATION_CACHE_SIZE; + bios_translation_cache = ram_translation_cache + RAM_TRANSLATION_CACHE_SIZE; rom_translation_ptr = rom_translation_cache; ram_translation_ptr = ram_translation_cache; + bios_translation_ptr = bios_translation_cache; sceKernelOpenVMDomain(); translation_caches_inited = 1; } @@ -550,6 +560,7 @@ void retro_deinit(void) #if defined(HAVE_MMAP) && defined(HAVE_DYNAREC) munmap(rom_translation_cache, ROM_TRANSLATION_CACHE_SIZE); munmap(ram_translation_cache, RAM_TRANSLATION_CACHE_SIZE); + munmap(bios_translation_cache, BIOS_TRANSLATION_CACHE_SIZE); #endif #if defined(_3DS) && defined(HAVE_DYNAREC) @@ -563,9 +574,13 @@ void retro_deinit(void) svcControlProcessMemory(currentHandle, ram_translation_cache, ram_translation_cache_ptr, RAM_TRANSLATION_CACHE_SIZE, MEMOP_UNMAP, 0b111); + svcControlProcessMemory(currentHandle, + bios_translation_cache, bios_translation_cache_ptr, + BIOS_TRANSLATION_CACHE_SIZE, MEMOP_UNMAP, 0b111); svcCloseHandle(currentHandle); free(rom_translation_cache_ptr); free(ram_translation_cache_ptr); + free(bios_translation_cache_ptr); translation_caches_inited = 0; } #endif @@ -976,7 +991,7 @@ bool retro_load_game(const struct retro_game_info* info) memset(gamepak_backup, -1, sizeof(gamepak_backup)); gamepak_filename[0] = 0; - if (load_gamepak(info, info->path) != 0) + if (load_gamepak(info->path) != 0) { error_msg("Could not load the game file."); return false; diff --git a/psp/mips_stub.S b/psp/mips_stub.S index 7b9bcb0..eb672c7 100644 --- a/psp/mips_stub.S +++ b/psp/mips_stub.S @@ -685,6 +685,7 @@ fnptrs: .global stub_arena .global rom_translation_cache .global ram_translation_cache +.global bios_translation_cache stub_arena: .space STUB_ARENA_SIZE @@ -692,6 +693,8 @@ rom_translation_cache: .space ROM_TRANSLATION_CACHE_SIZE ram_translation_cache: .space RAM_TRANSLATION_CACHE_SIZE +bios_translation_cache: + .space BIOS_TRANSLATION_CACHE_SIZE #endif @@ -19,7 +19,12 @@ #include "common.h" +#ifndef __LIBRETRO__ +#include "frontend/plat.h" +#endif + u32 global_enable_audio = 1; +u32 global_process_audio = 1; direct_sound_struct direct_sound_channel[2]; gbc_sound_struct gbc_sound_channel[4]; @@ -658,7 +663,12 @@ void render_audio(void) stream_base[i] = current_sample * 16; source[i] = 0; } +#ifdef __LIBRETRO__ audio_batch_cb(stream_base, 256); +#else + if (global_process_audio) + plat_sound_write(stream_base, 1024); +#endif sound_buffer_base += 512; sound_buffer_base &= BUFFER_SIZE_MASK; } @@ -24,8 +24,15 @@ #define BUFFER_SIZE_MASK (BUFFER_SIZE - 1) #define GBA_SOUND_FREQUENCY (64 * 1024) +#define GBA_60HZ_RATE 16853760.0f /* 228*(272+960)*60 */ +#if !defined(PSP_BUILD) && !defined(__LIBRETRO__) +// run GBA at 60Hz (~0.5% faster) to better match host display +#define GBC_BASE_RATE GBA_60HZ_RATE +#else #define GBC_BASE_RATE ((float)(16 * 1024 * 1024)) +#endif + typedef enum { @@ -108,6 +115,7 @@ extern u32 sound_frequency; extern u32 sound_on; extern u32 global_enable_audio; +extern u32 global_process_audio; extern u32 enable_low_pass_filter; void sound_timer_queue8(u32 channel, u8 value); diff --git a/x86/x86_stub.S b/x86/x86_stub.S index 333c8fd..b110787 100644 --- a/x86/x86_stub.S +++ b/x86/x86_stub.S @@ -558,6 +558,8 @@ defsymbl(rom_translation_cache) .space ROM_TRANSLATION_CACHE_SIZE defsymbl(ram_translation_cache) .space RAM_TRANSLATION_CACHE_SIZE +defsymbl(bios_translation_cache) + .space BIOS_TRANSLATION_CACHE_SIZE #endif @@ -0,0 +1,160 @@ +/* gameplaySP + * + * Copyright (C) 2006 Exophase <exophase@gmail.com> + * Copyright (C) 2006 SiberianSTAR + * + * 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 <zlib.h> + +#define ZIP_BUFFER_SIZE (128 * 1024) + +struct SZIPFileDataDescriptor +{ + s32 CRC32; + s32 CompressedSize; + s32 UncompressedSize; +} __attribute__((packed)); + +struct SZIPFileHeader +{ + char Sig[4]; // EDIT: Used to be s32 Sig; + s16 VersionToExtract; + s16 GeneralBitFlag; + s16 CompressionMethod; + s16 LastModFileTime; + s16 LastModFileDate; + struct SZIPFileDataDescriptor DataDescriptor; + s16 FilenameLength; + s16 ExtraFieldLength; +} __attribute__((packed)); + +u32 load_file_zip(const char *filename) +{ + struct SZIPFileHeader data; + char tmp[1024]; + s32 retval = -1; + u8 *buffer = NULL; + u8 *cbuffer; + char *ext; + int ret; + FILE *fd; + + fd = fopen(filename, "rb"); + + if(!fd) + return -1; + + while (1) + { + ret = fread(&data, 1, sizeof(data), fd); + if (ret != sizeof(data)) + break; + + // It checks for the following: 0x50 0x4B 0x03 0x04 (PK..) + if( data.Sig[0] != 0x50 || data.Sig[1] != 0x4B || + data.Sig[2] != 0x03 || data.Sig[3] != 0x04 ) + { + break; + } + + ret = fread(tmp, 1, data.FilenameLength, fd); + if (ret != data.FilenameLength) + break; + + tmp[data.FilenameLength] = 0; // end string + + if(data.ExtraFieldLength) + fseek(fd, data.ExtraFieldLength, SEEK_CUR); + + if(data.GeneralBitFlag & 0x0008) + { + fread(&data.DataDescriptor, + 1, sizeof(struct SZIPFileDataDescriptor), fd); + } + + ext = strrchr(tmp, '.') + 1; + + // file is too big + if(data.DataDescriptor.UncompressedSize > gamepak_ram_buffer_size) + goto skip; + + if(!strcasecmp(ext, "bin") || !strcasecmp(ext, "gba")) + { + buffer = gamepak_rom; + + // ok, found + switch(data.CompressionMethod) + { + case 0: + retval = data.DataDescriptor.UncompressedSize; + fread(buffer, 1, retval, fd); + goto outcode; + + case 8: + { + z_stream stream; + s32 err; + + cbuffer = malloc(ZIP_BUFFER_SIZE); + + stream.next_in = (Bytef*)cbuffer; + stream.avail_in = (u32)ZIP_BUFFER_SIZE; + + stream.next_out = (Bytef*)buffer; + + // EDIT: Now uses proper conversion of data types for retval. + retval = (u32)data.DataDescriptor.UncompressedSize; + stream.avail_out = data.DataDescriptor.UncompressedSize; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit2(&stream, -MAX_WBITS); + + fread(cbuffer, 1, ZIP_BUFFER_SIZE, fd); + + if(err == Z_OK) + { + while(err != Z_STREAM_END) + { + err = inflate(&stream, Z_SYNC_FLUSH); + if(err == Z_BUF_ERROR) + { + stream.avail_in = ZIP_BUFFER_SIZE; + stream.next_in = (Bytef*)cbuffer; + fread(cbuffer, 1, ZIP_BUFFER_SIZE, fd); + } + } + err = Z_OK; + inflateEnd(&stream); + } + free(cbuffer); + goto outcode; + } + } + } + +skip: + fseek(fd, data.DataDescriptor.CompressedSize, SEEK_CUR); + } + +outcode: + fclose(fd); + + return retval; +} @@ -0,0 +1,26 @@ +/* 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 ZIP_H +#define ZIP_H + +u32 load_file_zip(const char *filename); + +#endif + |