commit
5bfd9ced9a
32
Makefile
32
Makefile
|
@ -200,7 +200,7 @@ else ifeq ($(platform), psp1)
|
|||
TARGET := $(TARGET_NAME)_libretro_$(platform).a
|
||||
CC = psp-gcc$(EXE_EXT)
|
||||
AR = psp-ar$(EXE_EXT)
|
||||
CFLAGS += -DPSP -G0 -DUSE_BGR_FORMAT
|
||||
CFLAGS += -DPSP -G0 -DUSE_BGR_FORMAT -DMIPS_HAS_R2_INSTS
|
||||
CFLAGS += -I$(shell psp-config --pspsdk-path)/include
|
||||
CFLAGS += -march=allegrex -mfp32 -mgp32 -mlong32 -mabi=eabi
|
||||
CFLAGS += -fomit-frame-pointer -ffast-math
|
||||
|
@ -375,7 +375,16 @@ else ifeq ($(platform), mips32)
|
|||
SHARED := -shared -nostdlib -Wl,--version-script=link.T
|
||||
fpic := -fPIC -DPIC
|
||||
CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
|
||||
CFLAGS += -fno-caller-saves
|
||||
CFLAGS += -DMIPS_HAS_R2_INSTS
|
||||
HAVE_DYNAREC := 1
|
||||
CPU_ARCH := mips
|
||||
|
||||
# MIPS64
|
||||
else ifeq ($(platform), mips64n32)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
SHARED := -shared -nostdlib -Wl,--version-script=link.T
|
||||
fpic := -fPIC -DPIC
|
||||
CFLAGS += -fomit-frame-pointer -ffast-math -march=mips64 -mabi=n32 -mhard-float
|
||||
HAVE_DYNAREC := 1
|
||||
CPU_ARCH := mips
|
||||
|
||||
|
@ -384,7 +393,7 @@ else ifeq ($(platform), emscripten)
|
|||
TARGET := $(TARGET_NAME)_libretro_$(platform).bc
|
||||
STATIC_LINKING = 1
|
||||
|
||||
# GCW0
|
||||
# GCW0 (OD and OD Beta)
|
||||
else ifeq ($(platform), gcw0)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
|
@ -393,22 +402,7 @@ else ifeq ($(platform), gcw0)
|
|||
SHARED := -shared -nostdlib -Wl,--version-script=link.T
|
||||
fpic := -fPIC -DPIC
|
||||
CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
|
||||
HAVE_DYNAREC := 1
|
||||
CPU_ARCH := mips
|
||||
|
||||
# GCW0 (OpenDingux Beta)
|
||||
else ifeq ($(platform), gcw0-odbeta)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
CXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
|
||||
AR = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar
|
||||
SHARED := -shared -nostdlib -Wl,--version-script=link.T
|
||||
fpic := -fPIC -DPIC
|
||||
CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
|
||||
# The ASM code and/or MIPS dynarec of GPSP does not respect
|
||||
# MIPS calling conventions, so we must use '-fno-caller-saves'
|
||||
# for the OpenDingux Beta build
|
||||
CFLAGS += -fno-caller-saves
|
||||
CFLAGS += -DMIPS_HAS_R2_INSTS
|
||||
HAVE_DYNAREC := 1
|
||||
CPU_ARCH := mips
|
||||
|
||||
|
|
8
cpu.c
8
cpu.c
|
@ -684,14 +684,6 @@ void print_register_usage(void)
|
|||
reg[REG_CPSR] = (n_flag << 31) | (z_flag << 30) | (c_flag << 29) | \
|
||||
(v_flag << 28) | (reg[REG_CPSR] & 0xFF) \
|
||||
|
||||
#define memory_region(r_dest, l_dest, address) \
|
||||
r_dest = memory_regions[address >> 24]; \
|
||||
l_dest = memory_limits[address >> 24] \
|
||||
|
||||
|
||||
#define pc_region() \
|
||||
memory_region(pc_region, pc_limit, pc) \
|
||||
|
||||
#define check_pc_region() \
|
||||
new_pc_region = (pc >> 15); \
|
||||
if(new_pc_region != pc_region) \
|
||||
|
|
35
gba_memory.c
35
gba_memory.c
|
@ -321,9 +321,6 @@ u32 gamepak_size;
|
|||
|
||||
dma_transfer_type dma[4];
|
||||
|
||||
u8 *memory_regions[16];
|
||||
u32 memory_limits[16];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 page_timestamp;
|
||||
|
@ -3196,38 +3193,6 @@ void init_memory(void)
|
|||
{
|
||||
u32 map_offset = 0;
|
||||
|
||||
memory_regions[0x00] = (u8 *)bios_rom;
|
||||
memory_regions[0x01] = (u8 *)bios_rom;
|
||||
memory_regions[0x02] = (u8 *)ewram;
|
||||
memory_regions[0x03] = (u8 *)iwram + 0x8000;
|
||||
memory_regions[0x04] = (u8 *)io_registers;
|
||||
memory_regions[0x05] = (u8 *)palette_ram;
|
||||
memory_regions[0x06] = (u8 *)vram;
|
||||
memory_regions[0x07] = (u8 *)oam_ram;
|
||||
memory_regions[0x08] = (u8 *)gamepak_rom;
|
||||
memory_regions[0x09] = (u8 *)(gamepak_rom + 0xFFFFFF);
|
||||
memory_regions[0x0A] = (u8 *)gamepak_rom;
|
||||
memory_regions[0x0B] = (u8 *)(gamepak_rom + 0xFFFFFF);
|
||||
memory_regions[0x0C] = (u8 *)gamepak_rom;
|
||||
memory_regions[0x0D] = (u8 *)(gamepak_rom + 0xFFFFFF);
|
||||
memory_regions[0x0E] = (u8 *)gamepak_backup;
|
||||
|
||||
memory_limits[0x00] = 0x3FFF;
|
||||
memory_limits[0x01] = 0x3FFF;
|
||||
memory_limits[0x02] = 0x3FFFF;
|
||||
memory_limits[0x03] = 0x7FFF;
|
||||
memory_limits[0x04] = 0x7FFF;
|
||||
memory_limits[0x05] = 0x3FF;
|
||||
memory_limits[0x06] = 0x17FFF;
|
||||
memory_limits[0x07] = 0x3FF;
|
||||
memory_limits[0x08] = 0x1FFFFFF;
|
||||
memory_limits[0x09] = 0x1FFFFFF;
|
||||
memory_limits[0x0A] = 0x1FFFFFF;
|
||||
memory_limits[0x0B] = 0x1FFFFFF;
|
||||
memory_limits[0x0C] = 0x1FFFFFF;
|
||||
memory_limits[0x0D] = 0x1FFFFFF;
|
||||
memory_limits[0x0E] = 0xFFFF;
|
||||
|
||||
// Fill memory map regions, areas marked as NULL must be checked directly
|
||||
map_region(read, 0x0000000, 0x1000000, 1, bios_rom);
|
||||
map_null(read, 0x1000000, 0x2000000);
|
||||
|
|
|
@ -171,9 +171,6 @@ u8 read_backup(u32 address);
|
|||
void function_cc write_backup(u32 address, u32 value);
|
||||
void function_cc write_rtc(u32 address, u32 value);
|
||||
|
||||
extern u8 *memory_regions[16];
|
||||
extern u32 memory_limits[16];
|
||||
|
||||
/* EDIT: Shouldn't this be extern ?! */
|
||||
extern u32 waitstate_cycles_sequential[16][3];
|
||||
|
||||
|
|
312
libretro.h
312
libretro.h
|
@ -1388,6 +1388,111 @@ enum retro_mod
|
|||
* fastforwarding state will occur in this case).
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE 65
|
||||
/* const struct retro_system_content_info_override * --
|
||||
* Allows an implementation to override 'global' content
|
||||
* info parameters reported by retro_get_system_info().
|
||||
* Overrides also affect subsystem content info parameters
|
||||
* set via RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO.
|
||||
* This function must be called inside retro_set_environment().
|
||||
* If callback returns false, content info overrides
|
||||
* are unsupported by the frontend, and will be ignored.
|
||||
* If callback returns true, extended game info may be
|
||||
* retrieved by calling RETRO_ENVIRONMENT_GET_GAME_INFO_EXT
|
||||
* in retro_load_game() or retro_load_game_special().
|
||||
*
|
||||
* 'data' points to an array of retro_system_content_info_override
|
||||
* structs terminated by a { NULL, false, false } element.
|
||||
* If 'data' is NULL, no changes will be made to the frontend;
|
||||
* a core may therefore pass NULL in order to test whether
|
||||
* the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and
|
||||
* RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks are supported
|
||||
* by the frontend.
|
||||
*
|
||||
* For struct member descriptions, see the definition of
|
||||
* struct retro_system_content_info_override.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* - struct retro_system_info:
|
||||
* {
|
||||
* "My Core", // library_name
|
||||
* "v1.0", // library_version
|
||||
* "m3u|md|cue|iso|chd|sms|gg|sg", // valid_extensions
|
||||
* true, // need_fullpath
|
||||
* false // block_extract
|
||||
* }
|
||||
*
|
||||
* - Array of struct retro_system_content_info_override:
|
||||
* {
|
||||
* {
|
||||
* "md|sms|gg", // extensions
|
||||
* false, // need_fullpath
|
||||
* true // persistent_data
|
||||
* },
|
||||
* {
|
||||
* "sg", // extensions
|
||||
* false, // need_fullpath
|
||||
* false // persistent_data
|
||||
* },
|
||||
* { NULL, false, false }
|
||||
* }
|
||||
*
|
||||
* Result:
|
||||
* - Files of type m3u, cue, iso, chd will not be
|
||||
* loaded by the frontend. Frontend will pass a
|
||||
* valid path to the core, and core will handle
|
||||
* loading internally
|
||||
* - Files of type md, sms, gg will be loaded by
|
||||
* the frontend. A valid memory buffer will be
|
||||
* passed to the core. This memory buffer will
|
||||
* remain valid until retro_deinit() returns
|
||||
* - Files of type sg will be loaded by the frontend.
|
||||
* A valid memory buffer will be passed to the core.
|
||||
* This memory buffer will remain valid until
|
||||
* retro_load_game() (or retro_load_game_special())
|
||||
* returns
|
||||
*
|
||||
* NOTE: If an extension is listed multiple times in
|
||||
* an array of retro_system_content_info_override
|
||||
* structs, only the first instance will be registered
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_GAME_INFO_EXT 66
|
||||
/* const struct retro_game_info_ext ** --
|
||||
* Allows an implementation to fetch extended game
|
||||
* information, providing additional content path
|
||||
* and memory buffer status details.
|
||||
* This function may only be called inside
|
||||
* retro_load_game() or retro_load_game_special().
|
||||
* If callback returns false, extended game information
|
||||
* is unsupported by the frontend. In this case, only
|
||||
* regular retro_game_info will be available.
|
||||
* RETRO_ENVIRONMENT_GET_GAME_INFO_EXT is guaranteed
|
||||
* to return true if RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE
|
||||
* returns true.
|
||||
*
|
||||
* 'data' points to an array of retro_game_info_ext structs.
|
||||
*
|
||||
* For struct member descriptions, see the definition of
|
||||
* struct retro_game_info_ext.
|
||||
*
|
||||
* - If function is called inside retro_load_game(),
|
||||
* the retro_game_info_ext array is guaranteed to
|
||||
* have a size of 1 - i.e. the returned pointer may
|
||||
* be used to access directly the members of the
|
||||
* first retro_game_info_ext struct, for example:
|
||||
*
|
||||
* struct retro_game_info_ext *game_info_ext;
|
||||
* if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &game_info_ext))
|
||||
* printf("Content Directory: %s\n", game_info_ext->dir);
|
||||
*
|
||||
* - If the function is called inside retro_load_game_special(),
|
||||
* the retro_game_info_ext array is guaranteed to have a
|
||||
* size equal to the num_info argument passed to
|
||||
* retro_load_game_special()
|
||||
*/
|
||||
|
||||
/* VFS functionality */
|
||||
|
||||
/* File paths:
|
||||
|
@ -2791,6 +2896,213 @@ struct retro_system_info
|
|||
bool block_extract;
|
||||
};
|
||||
|
||||
/* Defines overrides which modify frontend handling of
|
||||
* specific content file types.
|
||||
* An array of retro_system_content_info_override is
|
||||
* passed to RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE
|
||||
* NOTE: In the following descriptions, references to
|
||||
* retro_load_game() may be replaced with
|
||||
* retro_load_game_special() */
|
||||
struct retro_system_content_info_override
|
||||
{
|
||||
/* A list of file extensions for which the override
|
||||
* should apply, delimited by a 'pipe' character
|
||||
* (e.g. "md|sms|gg")
|
||||
* Permitted file extensions are limited to those
|
||||
* included in retro_system_info::valid_extensions
|
||||
* and/or retro_subsystem_rom_info::valid_extensions */
|
||||
const char *extensions;
|
||||
|
||||
/* Overrides the need_fullpath value set in
|
||||
* retro_system_info and/or retro_subsystem_rom_info.
|
||||
* To reiterate:
|
||||
*
|
||||
* If need_fullpath is true and retro_load_game() is called:
|
||||
* - retro_game_info::path is guaranteed to contain a valid
|
||||
* path to an existent file
|
||||
* - retro_game_info::data and retro_game_info::size are invalid
|
||||
*
|
||||
* If need_fullpath is false and retro_load_game() is called:
|
||||
* - retro_game_info::path may be NULL
|
||||
* - retro_game_info::data and retro_game_info::size are guaranteed
|
||||
* to be valid
|
||||
*
|
||||
* In addition:
|
||||
*
|
||||
* If need_fullpath is true and retro_load_game() is called:
|
||||
* - retro_game_info_ext::full_path is guaranteed to contain a valid
|
||||
* path to an existent file
|
||||
* - retro_game_info_ext::archive_path may be NULL
|
||||
* - retro_game_info_ext::archive_file may be NULL
|
||||
* - retro_game_info_ext::dir is guaranteed to contain a valid path
|
||||
* to the directory in which the content file exists
|
||||
* - retro_game_info_ext::name is guaranteed to contain the
|
||||
* basename of the content file, without extension
|
||||
* - retro_game_info_ext::ext is guaranteed to contain the
|
||||
* extension of the content file in lower case format
|
||||
* - retro_game_info_ext::data and retro_game_info_ext::size
|
||||
* are invalid
|
||||
*
|
||||
* If need_fullpath is false and retro_load_game() is called:
|
||||
* - If retro_game_info_ext::file_in_archive is false:
|
||||
* - retro_game_info_ext::full_path is guaranteed to contain
|
||||
* a valid path to an existent file
|
||||
* - retro_game_info_ext::archive_path may be NULL
|
||||
* - retro_game_info_ext::archive_file may be NULL
|
||||
* - retro_game_info_ext::dir is guaranteed to contain a
|
||||
* valid path to the directory in which the content file exists
|
||||
* - retro_game_info_ext::name is guaranteed to contain the
|
||||
* basename of the content file, without extension
|
||||
* - retro_game_info_ext::ext is guaranteed to contain the
|
||||
* extension of the content file in lower case format
|
||||
* - If retro_game_info_ext::file_in_archive is true:
|
||||
* - retro_game_info_ext::full_path may be NULL
|
||||
* - retro_game_info_ext::archive_path is guaranteed to
|
||||
* contain a valid path to an existent compressed file
|
||||
* inside which the content file is located
|
||||
* - retro_game_info_ext::archive_file is guaranteed to
|
||||
* contain a valid path to an existent content file
|
||||
* inside the compressed file referred to by
|
||||
* retro_game_info_ext::archive_path
|
||||
* e.g. for a compressed file '/path/to/foo.zip'
|
||||
* containing 'bar.sfc'
|
||||
* > retro_game_info_ext::archive_path will be '/path/to/foo.zip'
|
||||
* > retro_game_info_ext::archive_file will be 'bar.sfc'
|
||||
* - retro_game_info_ext::dir is guaranteed to contain a
|
||||
* valid path to the directory in which the compressed file
|
||||
* (containing the content file) exists
|
||||
* - retro_game_info_ext::name is guaranteed to contain
|
||||
* EITHER
|
||||
* 1) the basename of the compressed file (containing
|
||||
* the content file), without extension
|
||||
* OR
|
||||
* 2) the basename of the content file inside the
|
||||
* compressed file, without extension
|
||||
* In either case, a core should consider 'name' to
|
||||
* be the canonical name/ID of the the content file
|
||||
* - retro_game_info_ext::ext is guaranteed to contain the
|
||||
* extension of the content file inside the compressed file,
|
||||
* in lower case format
|
||||
* - retro_game_info_ext::data and retro_game_info_ext::size are
|
||||
* guaranteed to be valid */
|
||||
bool need_fullpath;
|
||||
|
||||
/* If need_fullpath is false, specifies whether the content
|
||||
* data buffer available in retro_load_game() is 'persistent'
|
||||
*
|
||||
* If persistent_data is false and retro_load_game() is called:
|
||||
* - retro_game_info::data and retro_game_info::size
|
||||
* are valid only until retro_load_game() returns
|
||||
* - retro_game_info_ext::data and retro_game_info_ext::size
|
||||
* are valid only until retro_load_game() returns
|
||||
*
|
||||
* If persistent_data is true and retro_load_game() is called:
|
||||
* - retro_game_info::data and retro_game_info::size
|
||||
* are valid until retro_deinit() returns
|
||||
* - retro_game_info_ext::data and retro_game_info_ext::size
|
||||
* are valid until retro_deinit() returns */
|
||||
bool persistent_data;
|
||||
};
|
||||
|
||||
/* Similar to retro_game_info, but provides extended
|
||||
* information about the source content file and
|
||||
* game memory buffer status.
|
||||
* And array of retro_game_info_ext is returned by
|
||||
* RETRO_ENVIRONMENT_GET_GAME_INFO_EXT
|
||||
* NOTE: In the following descriptions, references to
|
||||
* retro_load_game() may be replaced with
|
||||
* retro_load_game_special() */
|
||||
struct retro_game_info_ext
|
||||
{
|
||||
/* - If file_in_archive is false, contains a valid
|
||||
* path to an existent content file (UTF-8 encoded)
|
||||
* - If file_in_archive is true, may be NULL */
|
||||
const char *full_path;
|
||||
|
||||
/* - If file_in_archive is false, may be NULL
|
||||
* - If file_in_archive is true, contains a valid path
|
||||
* to an existent compressed file inside which the
|
||||
* content file is located (UTF-8 encoded) */
|
||||
const char *archive_path;
|
||||
|
||||
/* - If file_in_archive is false, may be NULL
|
||||
* - If file_in_archive is true, contain a valid path
|
||||
* to an existent content file inside the compressed
|
||||
* file referred to by archive_path (UTF-8 encoded)
|
||||
* e.g. for a compressed file '/path/to/foo.zip'
|
||||
* containing 'bar.sfc'
|
||||
* > archive_path will be '/path/to/foo.zip'
|
||||
* > archive_file will be 'bar.sfc' */
|
||||
const char *archive_file;
|
||||
|
||||
/* - If file_in_archive is false, contains a valid path
|
||||
* to the directory in which the content file exists
|
||||
* (UTF-8 encoded)
|
||||
* - If file_in_archive is true, contains a valid path
|
||||
* to the directory in which the compressed file
|
||||
* (containing the content file) exists (UTF-8 encoded) */
|
||||
const char *dir;
|
||||
|
||||
/* Contains the canonical name/ID of the content file
|
||||
* (UTF-8 encoded). Intended for use when identifying
|
||||
* 'complementary' content named after the loaded file -
|
||||
* i.e. companion data of a different format (a CD image
|
||||
* required by a ROM), texture packs, internally handled
|
||||
* save files, etc.
|
||||
* - If file_in_archive is false, contains the basename
|
||||
* of the content file, without extension
|
||||
* - If file_in_archive is true, then string is
|
||||
* implementation specific. A frontend may choose to
|
||||
* set a name value of:
|
||||
* EITHER
|
||||
* 1) the basename of the compressed file (containing
|
||||
* the content file), without extension
|
||||
* OR
|
||||
* 2) the basename of the content file inside the
|
||||
* compressed file, without extension
|
||||
* RetroArch sets the 'name' value according to (1).
|
||||
* A frontend that supports routine loading of
|
||||
* content from archives containing multiple unrelated
|
||||
* content files may set the 'name' value according
|
||||
* to (2). */
|
||||
const char *name;
|
||||
|
||||
/* - If file_in_archive is false, contains the extension
|
||||
* of the content file in lower case format
|
||||
* - If file_in_archive is true, contains the extension
|
||||
* of the content file inside the compressed file,
|
||||
* in lower case format */
|
||||
const char *ext;
|
||||
|
||||
/* String of implementation specific meta-data. */
|
||||
const char *meta;
|
||||
|
||||
/* Memory buffer of loaded game content. Will be NULL:
|
||||
* IF
|
||||
* - retro_system_info::need_fullpath is true and
|
||||
* retro_system_content_info_override::need_fullpath
|
||||
* is unset
|
||||
* OR
|
||||
* - retro_system_content_info_override::need_fullpath
|
||||
* is true */
|
||||
const void *data;
|
||||
|
||||
/* Size of game content memory buffer, in bytes */
|
||||
size_t size;
|
||||
|
||||
/* True if loaded content file is inside a compressed
|
||||
* archive */
|
||||
bool file_in_archive;
|
||||
|
||||
/* - If data is NULL, value is unset/ignored
|
||||
* - If data is non-NULL:
|
||||
* - If persistent_data is false, data and size are
|
||||
* valid only until retro_load_game() returns
|
||||
* - If persistent_data is true, data and size are
|
||||
* are valid until retro_deinit() returns */
|
||||
bool persistent_data;
|
||||
};
|
||||
|
||||
struct retro_game_geometry
|
||||
{
|
||||
unsigned base_width; /* Nominal video width of game. */
|
||||
|
|
211
psp/mips_emit.h
211
psp/mips_emit.h
|
@ -58,8 +58,6 @@ u32 execute_lsr_flags_reg(u32 value, u32 shift);
|
|||
u32 execute_asr_flags_reg(u32 value, u32 shift);
|
||||
u32 execute_ror_flags_reg(u32 value, u32 shift);
|
||||
|
||||
void reg_check();
|
||||
|
||||
typedef enum
|
||||
{
|
||||
mips_reg_zero,
|
||||
|
@ -201,18 +199,18 @@ typedef enum
|
|||
|
||||
#define mips_emit_reg(opcode, rs, rt, rd, shift, function) \
|
||||
*((u32 *)translation_ptr) = (mips_opcode_##opcode << 26) | \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | (shift << 6) | function; \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | ((shift) << 6) | function; \
|
||||
translation_ptr += 4 \
|
||||
|
||||
#define mips_emit_special(function, rs, rt, rd, shift) \
|
||||
*((u32 *)translation_ptr) = (mips_opcode_special << 26) | \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | (shift << 6) | \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | ((shift) << 6) | \
|
||||
mips_special_##function; \
|
||||
translation_ptr += 4 \
|
||||
|
||||
#define mips_emit_special2(function, rs, rt, rd, shift) \
|
||||
*((u32 *)translation_ptr) = (mips_opcode_special2 << 26) | \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | (shift << 6) | \
|
||||
(rs << 21) | (rt << 16) | (rd << 11) | ((shift) << 6) | \
|
||||
mips_special2_##function; \
|
||||
translation_ptr += 4 \
|
||||
|
||||
|
@ -558,9 +556,6 @@ u32 arm_to_mips_reg[] =
|
|||
#define generate_shift_right_arithmetic(ireg, imm) \
|
||||
mips_emit_sra(ireg, ireg, imm) \
|
||||
|
||||
#define generate_rotate_right(ireg, imm) \
|
||||
mips_emit_rotr(ireg, ireg, imm) \
|
||||
|
||||
#define generate_add(ireg_dest, ireg_src) \
|
||||
mips_emit_addu(ireg_dest, ireg_dest, ireg_src) \
|
||||
|
||||
|
@ -796,12 +791,13 @@ u32 arm_to_mips_reg[] =
|
|||
check_load_reg_pc(arm_reg, _rm, 8); \
|
||||
if(_shift != 0) \
|
||||
{ \
|
||||
mips_emit_rotr(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], _shift); \
|
||||
rotate_right(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], \
|
||||
reg_temp, _shift); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
{ /* Special case: RRX (no carry update) */ \
|
||||
mips_emit_srl(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], 1); \
|
||||
mips_emit_ins(arm_to_mips_reg[arm_reg], reg_c_cache, 31, 1); \
|
||||
insert_bits(arm_to_mips_reg[arm_reg], reg_c_cache, reg_temp, 31, 1); \
|
||||
} \
|
||||
_rm = arm_reg \
|
||||
|
||||
|
@ -809,7 +805,7 @@ u32 arm_to_mips_reg[] =
|
|||
check_load_reg_pc(arm_reg, _rm, 8); \
|
||||
if(_shift != 0) \
|
||||
{ \
|
||||
mips_emit_ext(reg_c_cache, arm_to_mips_reg[_rm], (32 - _shift), 1); \
|
||||
extract_bits(reg_c_cache, arm_to_mips_reg[_rm], (32 - _shift), 1); \
|
||||
mips_emit_sll(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], _shift); \
|
||||
_rm = arm_reg; \
|
||||
} \
|
||||
|
@ -818,7 +814,7 @@ u32 arm_to_mips_reg[] =
|
|||
check_load_reg_pc(arm_reg, _rm, 8); \
|
||||
if(_shift != 0) \
|
||||
{ \
|
||||
mips_emit_ext(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
extract_bits(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
mips_emit_srl(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], _shift); \
|
||||
} \
|
||||
else \
|
||||
|
@ -832,7 +828,7 @@ u32 arm_to_mips_reg[] =
|
|||
check_load_reg_pc(arm_reg, _rm, 8); \
|
||||
if(_shift != 0) \
|
||||
{ \
|
||||
mips_emit_ext(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
extract_bits(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
mips_emit_sra(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], _shift); \
|
||||
} \
|
||||
else \
|
||||
|
@ -846,15 +842,16 @@ u32 arm_to_mips_reg[] =
|
|||
check_load_reg_pc(arm_reg, _rm, 8); \
|
||||
if(_shift != 0) \
|
||||
{ \
|
||||
mips_emit_ext(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
mips_emit_rotr(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], _shift); \
|
||||
extract_bits(reg_c_cache, arm_to_mips_reg[_rm], (_shift - 1), 1); \
|
||||
rotate_right(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], \
|
||||
reg_temp, _shift); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
mips_emit_andi(reg_temp, arm_to_mips_reg[_rm], 1); \
|
||||
{ /* Special case: RRX (carry update) */ \
|
||||
mips_emit_sll(reg_temp, reg_c_cache, 31); \
|
||||
mips_emit_andi(reg_c_cache, arm_to_mips_reg[_rm], 1); \
|
||||
mips_emit_srl(arm_to_mips_reg[arm_reg], arm_to_mips_reg[_rm], 1); \
|
||||
mips_emit_ins(arm_to_mips_reg[arm_reg], reg_c_cache, 31, 1); \
|
||||
mips_emit_addu(reg_c_cache, reg_temp, reg_zero); \
|
||||
mips_emit_or(arm_to_mips_reg[arm_reg], arm_to_mips_reg[arm_reg],reg_temp);\
|
||||
} \
|
||||
_rm = arm_reg \
|
||||
|
||||
|
@ -875,7 +872,8 @@ u32 arm_to_mips_reg[] =
|
|||
mips_emit_sra(reg_a0, reg_a0, 31) \
|
||||
|
||||
#define generate_shift_reg_ror_no_flags(_rm, _rs) \
|
||||
mips_emit_rotrv(reg_a0, arm_to_mips_reg[_rm], arm_to_mips_reg[_rs]) \
|
||||
rotate_right_var(reg_a0, arm_to_mips_reg[_rm], \
|
||||
reg_temp, arm_to_mips_reg[_rs]) \
|
||||
|
||||
#define generate_shift_reg_lsl_flags(_rm, _rs) \
|
||||
generate_load_reg_pc(reg_a0, _rm, 12); \
|
||||
|
@ -897,7 +895,8 @@ u32 arm_to_mips_reg[] =
|
|||
mips_emit_addiu(reg_temp, arm_to_mips_reg[_rs], -1); \
|
||||
mips_emit_srlv(reg_temp, arm_to_mips_reg[_rm], reg_temp); \
|
||||
mips_emit_andi(reg_c_cache, reg_temp, 1); \
|
||||
mips_emit_rotrv(reg_a0, arm_to_mips_reg[_rm], arm_to_mips_reg[_rs]) \
|
||||
rotate_right_var(reg_a0, arm_to_mips_reg[_rm], \
|
||||
reg_temp, arm_to_mips_reg[_rs]) \
|
||||
|
||||
#define generate_shift_imm(arm_reg, name, flags_op) \
|
||||
u32 shift = (opcode >> 7) & 0x1F; \
|
||||
|
@ -1899,7 +1898,7 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
|
|||
} \
|
||||
else \
|
||||
{ \
|
||||
mips_emit_ins(reg_a2, reg_zero, 0, 2); \
|
||||
emit_align_reg(reg_a2, 2); \
|
||||
\
|
||||
for(i = 0; i < 16; i++) \
|
||||
{ \
|
||||
|
@ -2075,20 +2074,6 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
|
|||
check_store_reg_pc_thumb(dest_rd); \
|
||||
} \
|
||||
|
||||
/*
|
||||
|
||||
#define thumb_data_proc_hi(name) \
|
||||
{ \
|
||||
thumb_decode_hireg_op(); \
|
||||
check_load_reg_pc(arm_reg_a0, rs, 4); \
|
||||
check_load_reg_pc(arm_reg_a1, rd, 4); \
|
||||
generate_op_##name##_reg(arm_to_mips_reg[rd], arm_to_mips_reg[rd], \
|
||||
arm_to_mips_reg[rs]); \
|
||||
check_store_reg_pc_thumb(rd); \
|
||||
} \
|
||||
|
||||
*/
|
||||
|
||||
#define thumb_data_proc_test_hi(name) \
|
||||
{ \
|
||||
thumb_decode_hireg_op(); \
|
||||
|
@ -2336,7 +2321,7 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
|
|||
} \
|
||||
else \
|
||||
{ \
|
||||
mips_emit_ins(reg_a2, reg_zero, 0, 2); \
|
||||
emit_align_reg(reg_a2, 2); \
|
||||
\
|
||||
for(i = 0; i < 8; i++) \
|
||||
{ \
|
||||
|
@ -2533,6 +2518,71 @@ u8 swi_hle_handle[256] =
|
|||
generate_load_pc(reg_a0, pc); \
|
||||
mips_emit_sw(reg_a0, reg_base, (REG_PC * 4)) \
|
||||
|
||||
// Some macros to wrap device-specific instructions
|
||||
|
||||
/* MIPS32R2 and PSP support ins, ext, seb, rotr */
|
||||
#ifdef MIPS_HAS_R2_INSTS
|
||||
// Inserts LSB bits into another register
|
||||
#define insert_bits(rdest, rsrc, rtemp, pos, size) \
|
||||
mips_emit_ins(rdest, rsrc, pos, size);
|
||||
// Doubles a byte into a halfword
|
||||
#define double_byte(reg, rtmp) \
|
||||
mips_emit_ins(reg, reg, 8, 8);
|
||||
// Clears numbits at LSB position (to align an address)
|
||||
#define emit_align_reg(reg, numbits) \
|
||||
mips_emit_ins(reg, reg_zero, 0, numbits)
|
||||
// Extract a bitfield (pos, size) to a register
|
||||
#define extract_bits(rt, rs, pos, size) \
|
||||
mips_emit_ext(rt, rs, pos, size)
|
||||
// Extends signed byte to u32
|
||||
#define extend_byte_signed(rt, rs) \
|
||||
mips_emit_seb(rt, rs)
|
||||
// Rotates a word using a temp reg if necessary
|
||||
#define rotate_right(rdest, rsrc, rtemp, amount) \
|
||||
mips_emit_rotr(rdest, rsrc, amount);
|
||||
// Same but variable amount rotation (register)
|
||||
#define rotate_right_var(rdest, rsrc, rtemp, ramount) \
|
||||
mips_emit_rotrv(rdest, rsrc, ramount);
|
||||
#else
|
||||
// Inserts LSB bits into another register
|
||||
// *assumes dest bits are cleared*!
|
||||
#define insert_bits(rdest, rsrc, rtemp, pos, size) \
|
||||
mips_emit_sll(rtemp, rsrc, 32 - size); \
|
||||
mips_emit_srl(rtemp, rtemp, 32 - size - pos); \
|
||||
mips_emit_or(rdest, rdest, rtemp);
|
||||
// Doubles a byte into a halfword
|
||||
#define double_byte(reg, rtmp) \
|
||||
mips_emit_sll(rtmp, reg, 8); \
|
||||
mips_emit_andi(reg, reg, 0xff); \
|
||||
mips_emit_or(reg, reg, rtmp);
|
||||
// Clears numbits at LSB position (to align an address)
|
||||
#define emit_align_reg(reg, numbits) \
|
||||
mips_emit_srl(reg, reg, numbits); \
|
||||
mips_emit_sll(reg, reg, numbits)
|
||||
// Extract a bitfield (pos, size) to a register
|
||||
#define extract_bits(rt, rs, pos, size) \
|
||||
mips_emit_sll(rt, rs, 32 - ((pos) + (size))); \
|
||||
mips_emit_srl(rt, rt, 32 - (size))
|
||||
// Extends signed byte to u32
|
||||
#define extend_byte_signed(rt, rs) \
|
||||
mips_emit_sll(rt, rs, 24); \
|
||||
mips_emit_sra(rt, rt, 24)
|
||||
// Rotates a word (uses temp reg)
|
||||
#define rotate_right(rdest, rsrc, rtemp, amount) \
|
||||
mips_emit_sll(rtemp, rsrc, 32 - (amount)); \
|
||||
mips_emit_srl(rdest, rsrc, (amount)); \
|
||||
mips_emit_or(rdest, rdest, rtemp)
|
||||
// Variable rotation using temp reg (dst != src)
|
||||
#define rotate_right_var(rdest, rsrc, rtemp, ramount) \
|
||||
mips_emit_andi(rtemp, ramount, 0x1F); \
|
||||
mips_emit_srlv(rdest, rsrc, rtemp); \
|
||||
mips_emit_subu(rtemp, reg_zero, rtemp); \
|
||||
mips_emit_addiu(rtemp, rtemp, 32); \
|
||||
mips_emit_sllv(rtemp, rsrc, rtemp); \
|
||||
mips_emit_or(rdest, rdest, rtemp)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Register save layout as follows:
|
||||
#define ReOff_RegPC (15*4) // REG_PC
|
||||
|
@ -2573,17 +2623,18 @@ u8 swi_hle_handle[256] =
|
|||
emit_save_regs(true); \
|
||||
genccall(fnptr); \
|
||||
mips_emit_andi(reg_a0, reg_a0, (mask)); \
|
||||
emit_restore_regs(true); \
|
||||
mips_emit_lw(mips_reg_ra, reg_base, ReOff_SaveR1); \
|
||||
mips_emit_jr(mips_reg_ra);
|
||||
emit_restore_regs(true);
|
||||
|
||||
#define emit_mem_call(fnptr, mask) \
|
||||
emit_mem_call_ds(fnptr, mask) \
|
||||
mips_emit_jr(mips_reg_ra); \
|
||||
mips_emit_nop();
|
||||
|
||||
// Pointer table to stubs, indexed by type and region
|
||||
extern u32 tmemld[11][16];
|
||||
extern u32 tmemst[ 4][16];
|
||||
extern u32 thnjal[15*16];
|
||||
void mips_lookup_pc();
|
||||
void smc_write();
|
||||
cpu_alert_type write_io_register8 (u32 address, u32 value);
|
||||
|
@ -2702,7 +2753,7 @@ static void emit_pmemld_stub(
|
|||
// Address checking: jumps to handler if bad region/alignment
|
||||
mips_emit_srl(reg_temp, reg_a0, (32 - regionbits));
|
||||
if (!aligned && size != 0) { // u8 or aligned u32 dont need to check alignment bits
|
||||
mips_emit_ins(reg_temp, reg_a0, regionbits, size); // Add 1 or 2 bits of alignment
|
||||
insert_bits(reg_temp, reg_a0, reg_rv, regionbits, size); // Add 1 or 2 bits of alignment
|
||||
}
|
||||
if (regioncheck || alignment) { // If region and alignment are zero, can skip
|
||||
mips_emit_xori(reg_temp, reg_temp, regioncheck | (alignment << regionbits));
|
||||
|
@ -2739,7 +2790,7 @@ static void emit_pmemld_stub(
|
|||
// This code call the C routine to map the relevant ROM page
|
||||
emit_save_regs(aligned);
|
||||
mips_emit_sw(mips_reg_ra, reg_base, ReOff_SaveR3);
|
||||
mips_emit_ext(reg_a0, reg_a0, 15, 10); // a0 = (addr >> 15) & 0x3ff
|
||||
extract_bits(reg_a0, reg_a0, 15, 10); // a0 = (addr >> 15) & 0x3ff
|
||||
genccall(&load_gamepak_page);
|
||||
mips_emit_sw(reg_temp, reg_base, ReOff_SaveR1);
|
||||
|
||||
|
@ -2754,14 +2805,13 @@ static void emit_pmemld_stub(
|
|||
// Read from flash, is a bit special, fn call
|
||||
emit_mem_call_ds(&read_backup, 0xFFFF);
|
||||
if (!size && signext) {
|
||||
mips_emit_seb(reg_rv, reg_rv);
|
||||
extend_byte_signed(reg_rv, reg_rv);
|
||||
} else if (size == 1 && alignment) {
|
||||
mips_emit_seb(reg_rv, reg_rv);
|
||||
extend_byte_signed(reg_rv, reg_rv);
|
||||
} else if (size == 2) {
|
||||
mips_emit_rotr(reg_rv, reg_rv, 8 * alignment);
|
||||
} else {
|
||||
mips_emit_nop();
|
||||
rotate_right(reg_rv, reg_rv, reg_temp, 8 * alignment);
|
||||
}
|
||||
generate_function_return_swap_delay();
|
||||
*tr_ptr = translation_ptr;
|
||||
return;
|
||||
} else {
|
||||
|
@ -2775,21 +2825,22 @@ static void emit_pmemld_stub(
|
|||
|
||||
if (region == 2) {
|
||||
// Can't do EWRAM with an `andi` instruction (18 bits mask)
|
||||
mips_emit_ext(reg_a0, reg_a0, 0, 18); // &= 0x3ffff
|
||||
extract_bits(reg_a0, reg_a0, 0, 18); // &= 0x3ffff
|
||||
if (!aligned && alignment != 0) {
|
||||
mips_emit_ins(reg_a0, reg_zero, 0, size);// addr & ~1/2 (align to size)
|
||||
emit_align_reg(reg_a0, size); // addr & ~1/2 (align to size)
|
||||
}
|
||||
// Need to insert a zero in the addr (due to how it's mapped)
|
||||
mips_emit_addu(reg_rv, reg_rv, reg_a0); // Adds to the base addr
|
||||
} else if (region == 6) {
|
||||
// VRAM is mirrored every 128KB but the last 32KB is mapped to the previous
|
||||
mips_emit_ext(reg_temp, reg_a0, 15, 2); // Extract bits 15 and 16
|
||||
extract_bits(reg_temp, reg_a0, 15, 2); // Extract bits 15 and 16
|
||||
mips_emit_addiu(reg_temp, reg_temp, -3); // Check for 3 (last block)
|
||||
if (!aligned && alignment != 0) {
|
||||
mips_emit_ins(reg_a0, reg_zero, 0, size);// addr & ~1/2 (align to size)
|
||||
emit_align_reg(reg_a0, size); // addr & ~1/2 (align to size)
|
||||
}
|
||||
mips_emit_b(bne, reg_zero, reg_temp, 2); // Skip unless last block
|
||||
mips_emit_ext(reg_a0, reg_a0, 0, 17); // addr & 0x1FFFF [delay]
|
||||
extract_bits(reg_a0, reg_a0, 0, 17); // addr & 0x1FFFF [delay]
|
||||
mips_emit_b(bne, reg_zero, reg_temp, 1); // Skip unless last block
|
||||
generate_swap_delay();
|
||||
mips_emit_addiu(reg_a0, reg_a0, 0x8000); // addr - 0x8000 (mirror last block)
|
||||
mips_emit_addu(reg_rv, reg_rv, reg_a0); // addr = base + adjusted offset
|
||||
} else {
|
||||
|
@ -2800,16 +2851,13 @@ static void emit_pmemld_stub(
|
|||
}
|
||||
}
|
||||
|
||||
// Aligned accesses (or the weird s16u1 case) are just one inst
|
||||
if (alignment == 0 || (size == 1 && signext)) {
|
||||
emit_mem_access_loadop(translation_ptr, base_addr, size, alignment, signext); // Delay slot
|
||||
translation_ptr += 4;
|
||||
}
|
||||
else {
|
||||
// Unaligned accesses (require rotation) need two insts
|
||||
// Emit load operation
|
||||
emit_mem_access_loadop(translation_ptr, base_addr, size, alignment, signext);
|
||||
translation_ptr += 4;
|
||||
mips_emit_rotr(reg_rv, reg_rv, alignment * 8); // Delay slot
|
||||
|
||||
if (!(alignment == 0 || (size == 1 && signext))) {
|
||||
// Unaligned accesses require rotation, except for size=1 & signext
|
||||
rotate_right(reg_rv, reg_rv, reg_temp, alignment * 8);
|
||||
}
|
||||
|
||||
generate_function_return_swap_delay(); // Return. Move prev inst to delay slot
|
||||
|
@ -2847,26 +2895,27 @@ static void emit_pmemst_stub(
|
|||
mips_emit_lui(reg_rv, ((base_addr + 0x8000) >> 16));
|
||||
|
||||
if (doubleaccess) {
|
||||
mips_emit_ins(reg_a1, reg_a1, 8, 8); // value = value | (value << 8)
|
||||
double_byte(reg_a1, reg_temp); // value = value | (value << 8)
|
||||
}
|
||||
|
||||
if (region == 2) {
|
||||
// Can't do EWRAM with an `andi` instruction (18 bits mask)
|
||||
mips_emit_ext(reg_a0, reg_a0, 0, 18); // &= 0x3ffff
|
||||
extract_bits(reg_a0, reg_a0, 0, 18); // &= 0x3ffff
|
||||
if (!aligned && realsize != 0) {
|
||||
mips_emit_ins(reg_a0, reg_zero, 0, size);// addr & ~1/2 (align to size)
|
||||
emit_align_reg(reg_a0, size); // addr & ~1/2 (align to size)
|
||||
}
|
||||
// Need to insert a zero in the addr (due to how it's mapped)
|
||||
mips_emit_addu(reg_rv, reg_rv, reg_a0); // Adds to the base addr
|
||||
} else if (region == 6) {
|
||||
// VRAM is mirrored every 128KB but the last 32KB is mapped to the previous
|
||||
mips_emit_ext(reg_temp, reg_a0, 15, 2); // Extract bits 15 and 16
|
||||
extract_bits(reg_temp, reg_a0, 15, 2); // Extract bits 15 and 16
|
||||
mips_emit_addiu(reg_temp, reg_temp, -3); // Check for 3 (last block)
|
||||
if (!aligned && realsize != 0) {
|
||||
mips_emit_ins(reg_a0, reg_zero, 0, realsize);// addr & ~1/2 (align to size)
|
||||
emit_align_reg(reg_a0, realsize); // addr & ~1/2 (align to size)
|
||||
}
|
||||
mips_emit_b(bne, reg_zero, reg_temp, 2); // Skip unless last block
|
||||
mips_emit_ext(reg_a0, reg_a0, 0, 17); // addr & 0x1FFFF [delay]
|
||||
extract_bits(reg_a0, reg_a0, 0, 17); // addr & 0x1FFFF [delay]
|
||||
mips_emit_b(bne, reg_zero, reg_temp, 1); // Skip next inst unless last block
|
||||
generate_swap_delay();
|
||||
mips_emit_addiu(reg_a0, reg_a0, 0x8000); // addr - 0x8000 (mirror last block)
|
||||
mips_emit_addu(reg_rv, reg_rv, reg_a0); // addr = base + adjusted offset
|
||||
} else {
|
||||
|
@ -2956,7 +3005,7 @@ static void emit_palette_hdl(
|
|||
mips_emit_b(bne, reg_zero, reg_temp, st_phndlr_branch(memop_number));
|
||||
mips_emit_andi(reg_rv, reg_a0, memmask); // Clear upper bits (mirroring)
|
||||
if (size == 0) {
|
||||
mips_emit_ins(reg_a1, reg_a1, 8, 8); // value = value | (value << 8)
|
||||
double_byte(reg_a1, reg_temp); // value = value | (value << 8)
|
||||
}
|
||||
mips_emit_addu(reg_rv, reg_rv, reg_base);
|
||||
|
||||
|
@ -3192,23 +3241,23 @@ static void emit_phand(
|
|||
mips_emit_min(reg_temp, reg_temp, reg_rv);// Do not overflow table
|
||||
#else
|
||||
mips_emit_sltiu(reg_rv, reg_temp, 0x0F); // Check for addr 0x1XXX.. 0xFXXX
|
||||
mips_emit_b(bne, reg_zero, reg_rv, 2); // Skip two insts (well, cant skip ds)
|
||||
mips_emit_sll(reg_temp, reg_temp, 2); // Table is word indexed
|
||||
mips_emit_b(bne, reg_zero, reg_rv, 1); // Skip next inst if region is good
|
||||
generate_swap_delay();
|
||||
mips_emit_addiu(reg_temp, reg_zero, 15*4);// Simulate ld/st to 0x0FXXX (open/ignore)
|
||||
#endif
|
||||
|
||||
// Stores or byte-accesses do not care about alignment
|
||||
if (check_alignment) {
|
||||
// Move alignment bits for the table lookup
|
||||
mips_emit_ins(reg_temp, reg_a0, 6, size); // Alignment bits (1 or 2, to bits 6 (and 7)
|
||||
// Move alignment bits for the table lookup (1 or 2, to bits 6 and 7)
|
||||
insert_bits(reg_temp, reg_a0, reg_rv, 6, size);
|
||||
}
|
||||
|
||||
unsigned tbloff = 256 + 3*1024 + 220 + 4 * toff; // Skip regs and RAMs
|
||||
mips_emit_addu(reg_rv, reg_temp, reg_base); // Add to the base_reg the table offset
|
||||
mips_emit_lw(reg_rv, reg_rv, tbloff); // Read addr from table
|
||||
mips_emit_sll(reg_temp, reg_rv, 4); // 26 bit immediate to the MSB
|
||||
mips_emit_ori(reg_temp, reg_temp, 0x3); // JAL opcode
|
||||
mips_emit_rotr(reg_temp, reg_temp, 6); // Swap opcode and immediate
|
||||
unsigned tbloff2 = tbloff + 960; // JAL opcode table
|
||||
mips_emit_addu(reg_temp, reg_temp, reg_base); // Add to the base_reg the table offset
|
||||
mips_emit_lw(reg_rv, reg_temp, tbloff); // Get func addr from 1st table
|
||||
mips_emit_lw(reg_temp, reg_temp, tbloff2); // Get opcode from 2nd table
|
||||
mips_emit_sw(reg_temp, mips_reg_ra, -8); // Patch instruction!
|
||||
|
||||
#ifdef PSP
|
||||
|
@ -3220,7 +3269,8 @@ static void emit_phand(
|
|||
mips_emit_synci(mips_reg_ra, -8);
|
||||
#endif
|
||||
|
||||
// Round up handlers to 16 instructions for easy addressing :)
|
||||
// Round up handlers to 16 instructions for easy addressing
|
||||
// PSP/MIPS32r2 uses up to 12 insts
|
||||
while (translation_ptr - *tr_ptr < 64) {
|
||||
mips_emit_nop();
|
||||
}
|
||||
|
@ -3333,6 +3383,11 @@ void init_emitter() {
|
|||
handler(2, &stinfo[i], 2, false, &translation_ptr); // st u32
|
||||
handler(3, &stinfo[i], 2, true, &translation_ptr); // st aligned 32
|
||||
}
|
||||
|
||||
// Generate JAL tables
|
||||
u32 *tmemptr = &tmemld[0][0];
|
||||
for (i = 0; i < 15*16; i++)
|
||||
thnjal[i] = ((tmemptr[i] >> 2) & 0x3FFFFFF) | (mips_opcode_jal << 26);
|
||||
}
|
||||
|
||||
u32 execute_arm_translate_internal(u32 cycles, void *regptr);
|
||||
|
|
117
psp/mips_stub.S
117
psp/mips_stub.S
|
@ -18,7 +18,19 @@
|
|||
|
||||
#include "../gpsp_config.h"
|
||||
|
||||
// This is also defined in sys/asm.h but doesn't seem portable?
|
||||
#ifdef __mips64
|
||||
.set mips64
|
||||
#define SZREG 8
|
||||
#define REG_L ld
|
||||
#define REG_S sd
|
||||
#else
|
||||
.set mips32r2
|
||||
#define SZREG 4
|
||||
#define REG_L lw
|
||||
#define REG_S sw
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
|
||||
.global mips_update_gba
|
||||
|
@ -34,14 +46,10 @@
|
|||
.global execute_lsl_flags_reg
|
||||
.global execute_lsr_flags_reg
|
||||
.global execute_asr_flags_reg
|
||||
.global execute_ror_flags_reg
|
||||
.global execute_arm_translate_internal
|
||||
.global icache_region_sync
|
||||
.global reg_check
|
||||
.global palette_ram
|
||||
.global palette_ram_converted
|
||||
.global oam_ram
|
||||
.global init_emitter
|
||||
.global mips_lookup_pc
|
||||
.global smc_write
|
||||
.global mips_cheat_hook
|
||||
|
@ -50,7 +58,7 @@
|
|||
.global memory_map_read
|
||||
.global tmemld
|
||||
.global tmemst
|
||||
.global tmemst
|
||||
.global thnjal
|
||||
.global reg
|
||||
.global spsr
|
||||
.global reg_mode
|
||||
|
@ -121,23 +129,40 @@
|
|||
.equ COMPLETED_FRAME, (32 * 4)
|
||||
.equ OAM_UPDATED, (33 * 4)
|
||||
.equ GP_SAVE, (34 * 4)
|
||||
.equ GP_SAVE_HI, (35 * 4)
|
||||
|
||||
.equ SPSR_BASE, (0x100 + 0x400 * 3)
|
||||
.equ REGMODE_BASE, (SPSR_BASE + 24)
|
||||
.equ SUPERVISOR_SPSR, (3 * 4 + SPSR_BASE)
|
||||
.equ SUPERVISOR_LR, ((3 * (7 * 4)) + (6 * 4) + REGMODE_BASE)
|
||||
.equ FNPTRS_MEMOPS, (REGMODE_BASE + 196)
|
||||
.equ FNPTRS_BASE, (FNPTRS_MEMOPS + 960)
|
||||
.equ FNPTRS_BASE, (FNPTRS_MEMOPS + 960*2)
|
||||
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
# make sure $16 has the register base for these macros
|
||||
|
||||
#ifdef MIPS_HAS_R2_INSTS
|
||||
.macro collapse_flag flag_reg, shift
|
||||
ins $2, $\flag_reg, \shift, 1 # insert flag into CPSR
|
||||
.endm
|
||||
|
||||
.macro extract_flag shift, flag_reg
|
||||
ext $\flag_reg, $1, \shift, 1 # extract flag from CPSR
|
||||
.endm
|
||||
#else
|
||||
.macro collapse_flag flag_reg, shift
|
||||
sll $1, $\flag_reg, \shift
|
||||
or $2, $2, $1
|
||||
.endm
|
||||
|
||||
.macro extract_flag shift, flag_reg
|
||||
srl $\flag_reg, $1, \shift
|
||||
andi $\flag_reg, $\flag_reg, 1
|
||||
.endm
|
||||
#endif
|
||||
|
||||
.macro collapse_flags
|
||||
lw $2, REG_CPSR($16) # load CPSR
|
||||
andi $2, $2, 0xFF # isolate lower 8bits
|
||||
|
@ -148,10 +173,6 @@
|
|||
sw $2, REG_CPSR($16) # store CPSR
|
||||
.endm
|
||||
|
||||
.macro extract_flag shift, flag_reg
|
||||
ext $\flag_reg, $1, \shift, 1 # extract flag from CPSR
|
||||
.endm
|
||||
|
||||
.macro extract_flags_body # extract flags from $1
|
||||
extract_flag 31, 20 # load flags
|
||||
extract_flag 30, 21
|
||||
|
@ -182,7 +203,7 @@
|
|||
sw $28, REG_R13($16)
|
||||
sw $30, REG_R14($16)
|
||||
|
||||
lw $28, GP_SAVE($16)
|
||||
REG_L $28, GP_SAVE($16)
|
||||
.endm
|
||||
|
||||
.macro restore_registers
|
||||
|
@ -271,20 +292,19 @@ mips_cheat_hook:
|
|||
# Loads the main context and returns to it.
|
||||
# ARM regs must be saved before branching here
|
||||
return_to_main:
|
||||
lw $28, GP_SAVE($16) # Restore previous state
|
||||
lw $s0, 0($sp)
|
||||
lw $s1, 4($sp)
|
||||
lw $s2, 8($sp)
|
||||
lw $s3, 12($sp)
|
||||
lw $s4, 16($sp)
|
||||
lw $s5, 20($sp)
|
||||
lw $s6, 24($sp)
|
||||
lw $s7, 28($sp)
|
||||
lw $fp, 32($sp)
|
||||
lw $ra, 36($sp)
|
||||
REG_L $28, GP_SAVE($16) # Restore previous state
|
||||
REG_L $s0, 4*SZREG($sp)
|
||||
REG_L $s1, 5*SZREG($sp)
|
||||
REG_L $s2, 6*SZREG($sp)
|
||||
REG_L $s3, 7*SZREG($sp)
|
||||
REG_L $s4, 8*SZREG($sp)
|
||||
REG_L $s5, 9*SZREG($sp)
|
||||
REG_L $s6, 10*SZREG($sp)
|
||||
REG_L $s7, 11*SZREG($sp)
|
||||
REG_L $fp, 12*SZREG($sp)
|
||||
REG_L $ra, 13*SZREG($sp)
|
||||
jr $ra # Return to main
|
||||
add $sp, $sp, 48 # Restore stack pointer (delay slot)
|
||||
|
||||
addiu $sp, $sp, 112 # Restore stack pointer (delay slot)
|
||||
|
||||
# Perform an indirect branch.
|
||||
|
||||
|
@ -407,7 +427,8 @@ execute_swi:
|
|||
sw $4, SUPERVISOR_LR($16) # store next PC in the supervisor's LR
|
||||
collapse_flags # get cpsr in $2
|
||||
sw $2, SUPERVISOR_SPSR($16) # save cpsr in SUPERVISOR_CPSR
|
||||
ins $2, $0, 0, 6 # zero out bottom 6 bits of CPSR
|
||||
srl $2, $2, 6 # zero out bottom 6 bits of CPSR
|
||||
sll $2, $2, 6
|
||||
ori $2, 0x13 # set mode to supervisor
|
||||
sw $2, REG_CPSR($16) # write back CPSR
|
||||
save_registers
|
||||
|
@ -513,11 +534,11 @@ lsl_shift_high:
|
|||
bne $1, $0, lsl_shift_done # jump if shift == 32
|
||||
andi $22, $4, 1 # c flag = value & 0x01 (delay)
|
||||
|
||||
add $22, $0, $0 # c flag = 0 otherwise
|
||||
addu $22, $0, $0 # c flag = 0 otherwise
|
||||
|
||||
lsl_shift_done:
|
||||
jr $ra # return
|
||||
add $4, $0, $0 # value = 0 no matter what
|
||||
addu $4, $0, $0 # value = 0 no matter what
|
||||
|
||||
|
||||
execute_lsr_flags_reg:
|
||||
|
@ -538,11 +559,11 @@ lsr_shift_high:
|
|||
bne $1, $0, lsr_shift_done # jump if shift == 32
|
||||
srl $22, $4, 31 # c flag = value >> 31 (delay)
|
||||
|
||||
add $22, $0, $0 # c flag = 0 otherwise
|
||||
addu $22, $0, $0 # c flag = 0 otherwise
|
||||
|
||||
lsr_shift_done:
|
||||
jr $ra # return
|
||||
add $4, $0, $0 # value = 0 no matter what
|
||||
addu $4, $0, $0 # value = 0 no matter what
|
||||
|
||||
|
||||
execute_asr_flags_reg:
|
||||
|
@ -564,35 +585,25 @@ asr_shift_high:
|
|||
andi $22, $4, 1 # c flag = value & 0x01
|
||||
|
||||
|
||||
execute_ror_flags_reg:
|
||||
beq $5, $0, ror_zero_shift # is the shift zero?
|
||||
addiu $1, $5, -1 # $1 = (shift - 1) (delay)
|
||||
|
||||
srav $1, $4, $1 # $1 = (value >> (shift - 1))
|
||||
andi $22, $1, 1 # c flag = $1 & 1
|
||||
|
||||
ror_zero_shift:
|
||||
jr $ra # return
|
||||
rotrv $4, $4, $5 # return (value ror shift) delay
|
||||
|
||||
# $4: cycle counter argument
|
||||
# $5: pointer to reg
|
||||
|
||||
execute_arm_translate_internal:
|
||||
add $sp, $sp, -48 # Store the main thread context
|
||||
sw $s0, 0($sp)
|
||||
sw $s1, 4($sp)
|
||||
sw $s2, 8($sp)
|
||||
sw $s3, 12($sp)
|
||||
sw $s4, 16($sp)
|
||||
sw $s5, 20($sp)
|
||||
sw $s6, 24($sp)
|
||||
sw $s7, 28($sp)
|
||||
sw $fp, 32($sp)
|
||||
sw $ra, 36($sp)
|
||||
|
||||
addiu $sp, $sp, -112 # Store the main thread context
|
||||
REG_S $s0, 4*SZREG($sp)
|
||||
REG_S $s1, 5*SZREG($sp)
|
||||
REG_S $s2, 6*SZREG($sp)
|
||||
REG_S $s3, 7*SZREG($sp)
|
||||
REG_S $s4, 8*SZREG($sp)
|
||||
REG_S $s5, 9*SZREG($sp)
|
||||
REG_S $s6, 10*SZREG($sp)
|
||||
REG_S $s7, 11*SZREG($sp)
|
||||
REG_S $fp, 12*SZREG($sp)
|
||||
REG_S $ra, 13*SZREG($sp)
|
||||
|
||||
move $16, $5
|
||||
sw $28, GP_SAVE($16)
|
||||
REG_S $28, GP_SAVE($16)
|
||||
|
||||
addu $17, $4, $0 # load cycle counter register
|
||||
|
||||
|
@ -652,6 +663,8 @@ tmemld:
|
|||
.space 704
|
||||
tmemst:
|
||||
.space 256
|
||||
thnjal:
|
||||
.space 960
|
||||
fnptrs:
|
||||
.long update_gba # 0
|
||||
.long block_lookup_address_arm # 1
|
||||
|
|
Loading…
Reference in New Issue