mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into optimization/idle-loops
Conflicts: src/gba/gba.c
This commit is contained in:
commit
dc5d1b40ee
5
CHANGES
5
CHANGES
|
@ -15,6 +15,9 @@ Features:
|
||||||
- Remappable shortcuts for keyboard and gamepad
|
- Remappable shortcuts for keyboard and gamepad
|
||||||
- Rewinding of emulation
|
- Rewinding of emulation
|
||||||
- Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter
|
- Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter
|
||||||
|
- Support IPv6
|
||||||
|
- Save directory of last loaded file
|
||||||
|
- Support BPS patches
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- Qt: Fix issue with set frame sizes being the wrong height
|
- Qt: Fix issue with set frame sizes being the wrong height
|
||||||
- Qt: Fix emulator crashing when full screen if a game is not running
|
- Qt: Fix emulator crashing when full screen if a game is not running
|
||||||
|
@ -46,6 +49,7 @@ Bugfixes:
|
||||||
- Qt: Fix some cases where key mapping can break if focus is adjusted
|
- Qt: Fix some cases where key mapping can break if focus is adjusted
|
||||||
- GBA Memory: Filter out top nybble of DMA addresses
|
- GBA Memory: Filter out top nybble of DMA addresses
|
||||||
- Debugger: Fix binary print putting spaces between digits
|
- Debugger: Fix binary print putting spaces between digits
|
||||||
|
- GBA BIOS: Fix LZ77UnCompVram to use 16-bit loads from decompressed memory
|
||||||
Misc:
|
Misc:
|
||||||
- Qt: Disable sync to video by default
|
- Qt: Disable sync to video by default
|
||||||
- GBA: Exit cleanly on FATAL if the port supports it
|
- GBA: Exit cleanly on FATAL if the port supports it
|
||||||
|
@ -55,6 +59,7 @@ Misc:
|
||||||
- Qt: Clarify some phrasing in the menus
|
- Qt: Clarify some phrasing in the menus
|
||||||
- GBA Memory: Implement 16- and 32-bit loads from SRAM
|
- GBA Memory: Implement 16- and 32-bit loads from SRAM
|
||||||
- Qt: Clear active buttons when focus is lost
|
- Qt: Clear active buttons when focus is lost
|
||||||
|
- GBA Memory: Simplify memory API and use fixed bus width
|
||||||
|
|
||||||
0.1.0: (2014-12-13)
|
0.1.0: (2014-12-13)
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
|
@ -93,11 +93,9 @@ union PSR {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ARMMemory {
|
struct ARMMemory {
|
||||||
int32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
uint32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
int16_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
uint32_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
uint16_t (*loadU16)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
uint32_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
||||||
int8_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
|
||||||
uint8_t (*loadU8)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
|
||||||
|
|
||||||
void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter);
|
void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter);
|
||||||
void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter);
|
void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter);
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
#define ADDR_MODE_1_IMM \
|
#define ADDR_MODE_1_IMM \
|
||||||
int rotate = (opcode & 0x00000F00) >> 7; \
|
int rotate = (opcode & 0x00000F00) >> 7; \
|
||||||
int immediate = opcode & 0x000000FF; \
|
int immediate = opcode & 0x000000FF; \
|
||||||
info->op3.immediate = ARM_ROR(immediate, rotate); \
|
info->op3.immediate = ROR(immediate, rotate); \
|
||||||
info->operandFormat |= ARM_OPERAND_IMMEDIATE_3;
|
info->operandFormat |= ARM_OPERAND_IMMEDIATE_3;
|
||||||
|
|
||||||
#define ADDR_MODE_2_SHIFT(OP) \
|
#define ADDR_MODE_2_SHIFT(OP) \
|
||||||
|
@ -213,7 +213,6 @@
|
||||||
ARM_MEMORY_WRITEBACK, \
|
ARM_MEMORY_WRITEBACK, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE) \
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## P, MNEMONIC, \
|
||||||
ARM_MEMORY_PRE_INCREMENT | \
|
|
||||||
ARM_MEMORY_OFFSET_SUBTRACT, \
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE) \
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PW, MNEMONIC, \
|
||||||
|
@ -222,10 +221,9 @@
|
||||||
ARM_MEMORY_OFFSET_SUBTRACT, \
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE) \
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PU, MNEMONIC, \
|
||||||
ARM_MEMORY_PRE_INCREMENT, \
|
0, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE) \
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## PUW, MNEMONIC, \
|
||||||
ARM_MEMORY_PRE_INCREMENT | \
|
|
||||||
ARM_MEMORY_WRITEBACK, \
|
ARM_MEMORY_WRITEBACK, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE)
|
ADDRESSING_MODE, CYCLES, TYPE)
|
||||||
|
|
||||||
|
@ -243,10 +241,12 @@
|
||||||
#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
|
#define DEFINE_LOAD_STORE_T_DECODER_SET_ARM(NAME, MNEMONIC, ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME, MNEMONIC, \
|
||||||
ARM_MEMORY_POST_INCREMENT | \
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK | \
|
||||||
ARM_MEMORY_OFFSET_SUBTRACT, \
|
ARM_MEMORY_OFFSET_SUBTRACT, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE) \
|
ADDRESSING_MODE, CYCLES, TYPE) \
|
||||||
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
|
DEFINE_LOAD_STORE_DECODER_EX_ARM(NAME ## U, MNEMONIC, \
|
||||||
ARM_MEMORY_POST_INCREMENT, \
|
ARM_MEMORY_POST_INCREMENT | \
|
||||||
|
ARM_MEMORY_WRITEBACK, \
|
||||||
ADDRESSING_MODE, CYCLES, TYPE)
|
ADDRESSING_MODE, CYCLES, TYPE)
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
|
#define DEFINE_LOAD_STORE_T_DECODER_ARM(NAME, MNEMONIC, CYCLES, TYPE) \
|
||||||
|
@ -416,7 +416,7 @@ DEFINE_DECODER_ARM(MRSR, MRS, info->affectsCPSR = 1;
|
||||||
|
|
||||||
DEFINE_DECODER_ARM(MSRI, MSR, info->affectsCPSR = 1;
|
DEFINE_DECODER_ARM(MSRI, MSR, info->affectsCPSR = 1;
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate);
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
info->affectsCPSR = 1;
|
info->affectsCPSR = 1;
|
||||||
info->op1.reg = ARM_CPSR;
|
info->op1.reg = ARM_CPSR;
|
||||||
info->op2.immediate = operand;
|
info->op2.immediate = operand;
|
||||||
|
@ -426,7 +426,7 @@ DEFINE_DECODER_ARM(MSRI, MSR, info->affectsCPSR = 1;
|
||||||
|
|
||||||
DEFINE_DECODER_ARM(MSRRI, MSR, info->affectsCPSR = 1;
|
DEFINE_DECODER_ARM(MSRRI, MSR, info->affectsCPSR = 1;
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate);
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
info->affectsCPSR = 1;
|
info->affectsCPSR = 1;
|
||||||
info->op1.reg = ARM_SPSR;
|
info->op1.reg = ARM_SPSR;
|
||||||
info->op2.immediate = operand;
|
info->op2.immediate = operand;
|
||||||
|
|
|
@ -138,7 +138,7 @@ static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
int rm = opcode & 0x0000000F;
|
int rm = opcode & 0x0000000F;
|
||||||
int immediate = (opcode & 0x00000F80) >> 7;
|
int immediate = (opcode & 0x00000F80) >> 7;
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
cpu->shifterOperand = ARM_ROR(cpu->gprs[rm], immediate);
|
cpu->shifterOperand = ROR(cpu->gprs[rm], immediate);
|
||||||
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
||||||
} else {
|
} else {
|
||||||
// RRX
|
// RRX
|
||||||
|
@ -165,7 +165,7 @@ static inline void _shiftRORR(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
cpu->shifterOperand = shiftVal;
|
cpu->shifterOperand = shiftVal;
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
} else if (rotate) {
|
} else if (rotate) {
|
||||||
cpu->shifterOperand = ARM_ROR(shiftVal, rotate);
|
cpu->shifterOperand = ROR(shiftVal, rotate);
|
||||||
cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1;
|
cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1;
|
||||||
} else {
|
} else {
|
||||||
cpu->shifterOperand = shiftVal;
|
cpu->shifterOperand = shiftVal;
|
||||||
|
@ -180,7 +180,7 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
cpu->shifterOperand = immediate;
|
cpu->shifterOperand = immediate;
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
cpu->shifterCarryOut = cpu->cpsr.c;
|
||||||
} else {
|
} else {
|
||||||
cpu->shifterOperand = ARM_ROR(immediate, rotate);
|
cpu->shifterOperand = ROR(immediate, rotate);
|
||||||
cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand);
|
cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
||||||
#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0)
|
#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0)
|
||||||
#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31)
|
#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31)
|
||||||
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ARM_ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
|
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
|
||||||
|
|
||||||
#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS
|
#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS
|
||||||
#define ADDR_MODE_3_RN ADDR_MODE_2_RN
|
#define ADDR_MODE_3_RN ADDR_MODE_2_RN
|
||||||
|
@ -524,10 +524,10 @@ DEFINE_MULTIPLY_INSTRUCTION_ARM(UMULL,
|
||||||
// Begin load/store definitions
|
// Begin load/store definitions
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.loadU16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, cpu->gprs[rd] = ARM_SXT_16(cpu->memory.load16(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
||||||
|
@ -535,7 +535,7 @@ DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, cpu->memory.store16(cpu, address,
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT,
|
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT,
|
||||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
enum PrivilegeMode priv = cpu->privilegeMode;
|
||||||
ARMSetPrivilegeMode(cpu, MODE_USER);
|
ARMSetPrivilegeMode(cpu, MODE_USER);
|
||||||
cpu->gprs[rd] = cpu->memory.loadU8(cpu, address, ¤tCycles);
|
cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles);
|
||||||
ARMSetPrivilegeMode(cpu, priv);
|
ARMSetPrivilegeMode(cpu, priv);
|
||||||
ARM_LOAD_POST_BODY;)
|
ARM_LOAD_POST_BODY;)
|
||||||
|
|
||||||
|
@ -583,7 +583,7 @@ DEFINE_INSTRUCTION_ARM(SWPB,
|
||||||
int rm = opcode & 0xF;
|
int rm = opcode & 0xF;
|
||||||
int rd = (opcode >> 12) & 0xF;
|
int rd = (opcode >> 12) & 0xF;
|
||||||
int rn = (opcode >> 16) & 0xF;
|
int rn = (opcode >> 16) & 0xF;
|
||||||
int32_t d = cpu->memory.loadU8(cpu, cpu->gprs[rn], ¤tCycles);
|
int32_t d = cpu->memory.load8(cpu, cpu->gprs[rn], ¤tCycles);
|
||||||
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
||||||
cpu->gprs[rd] = d;)
|
cpu->gprs[rd] = d;)
|
||||||
|
|
||||||
|
@ -662,7 +662,7 @@ DEFINE_INSTRUCTION_ARM(MSRI,
|
||||||
int c = opcode & 0x00010000;
|
int c = opcode & 0x00010000;
|
||||||
int f = opcode & 0x00080000;
|
int f = opcode & 0x00080000;
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate);
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
if (mask & PSR_USER_MASK) {
|
if (mask & PSR_USER_MASK) {
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
||||||
|
@ -677,7 +677,7 @@ DEFINE_INSTRUCTION_ARM(MSRRI,
|
||||||
int c = opcode & 0x00010000;
|
int c = opcode & 0x00010000;
|
||||||
int f = opcode & 0x00080000;
|
int f = opcode & 0x00080000;
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
int rotate = (opcode & 0x00000F00) >> 7;
|
||||||
int32_t operand = ARM_ROR(opcode & 0x000000FF, rotate);
|
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
||||||
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
||||||
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask);)
|
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask);)
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
#define ARM_COND_AL 1
|
#define ARM_COND_AL 1
|
||||||
|
|
||||||
#define ARM_SIGN(I) ((I) >> 31)
|
#define ARM_SIGN(I) ((I) >> 31)
|
||||||
#define ARM_ROR(I, ROTATE) ((((uint32_t) (I)) >> ROTATE) | ((uint32_t) (I) << ((-ROTATE) & 31)))
|
#define ARM_SXT_8(I) (((int8_t) (I) << 24) >> 24)
|
||||||
|
#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16)
|
||||||
|
|
||||||
#define ARM_CARRY_FROM(M, N, D) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31))
|
#define ARM_CARRY_FROM(M, N, D) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31))
|
||||||
#define ARM_BORROW_FROM(M, N, D) (((uint32_t) (M)) >= ((uint32_t) (N)))
|
#define ARM_BORROW_FROM(M, N, D) (((uint32_t) (M)) >= ((uint32_t) (N)))
|
||||||
|
|
|
@ -97,8 +97,8 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
|
||||||
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
|
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
|
||||||
|
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rm] + immediate * 4, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDR1, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rm] + immediate * 4, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rm] + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRB1, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rm] + immediate, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rm] + immediate * 2, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LDRH1, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rm] + immediate * 2, ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STR1, cpu->memory.store32(cpu, cpu->gprs[rm] + immediate * 4, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRB1, cpu->memory.store8(cpu, cpu->gprs[rm] + immediate, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(STRH1, cpu->memory.store16(cpu, cpu->gprs[rm] + immediate * 2, cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
|
@ -219,7 +219,7 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ROR,
|
||||||
int r4 = rs & 0x1F;
|
int r4 = rs & 0x1F;
|
||||||
if (r4 > 0) {
|
if (r4 > 0) {
|
||||||
cpu->cpsr.c = (cpu->gprs[rd] >> (r4 - 1)) & 1;
|
cpu->cpsr.c = (cpu->gprs[rd] >> (r4 - 1)) & 1;
|
||||||
cpu->gprs[rd] = ARM_ROR(cpu->gprs[rd], r4);
|
cpu->gprs[rd] = ROR(cpu->gprs[rd], r4);
|
||||||
} else {
|
} else {
|
||||||
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]);
|
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]);
|
||||||
}
|
}
|
||||||
|
@ -286,10 +286,10 @@ DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(ADD6, cpu->gprs[rd] = cpu->gprs[ARM_SP] + i
|
||||||
COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY)
|
COUNT_CALL_3(DEFINE_LOAD_STORE_WITH_REGISTER_EX_THUMB, NAME ## _R, BODY)
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDR2, cpu->gprs[rd] = cpu->memory.load32(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.loadU8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRB2, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.loadU16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRH2, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSB, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles)); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles); THUMB_LOAD_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(LDRSH, cpu->gprs[rd] = ARM_SXT_16(cpu->memory.load16(cpu, cpu->gprs[rn] + cpu->gprs[rm], ¤tCycles)); THUMB_LOAD_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STR2, cpu->memory.store32(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRB2, cpu->memory.store8(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
DEFINE_LOAD_STORE_WITH_REGISTER_THUMB(STRH2, cpu->memory.store16(cpu, cpu->gprs[rn] + cpu->gprs[rm], cpu->gprs[rd], ¤tCycles); THUMB_STORE_POST_BODY;)
|
||||||
|
|
|
@ -69,4 +69,7 @@
|
||||||
|
|
||||||
#define LIKELY(X) __builtin_expect(!!(X), 1)
|
#define LIKELY(X) __builtin_expect(!!(X), 1)
|
||||||
#define UNLIKELY(X) __builtin_expect(!!(X), 0)
|
#define UNLIKELY(X) __builtin_expect(!!(X), 0)
|
||||||
|
|
||||||
|
#define ROR(I, ROTATE) ((((uint32_t) (I)) >> ROTATE) | ((uint32_t) (I) << ((-ROTATE) & 31)))
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -248,7 +248,7 @@ static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, en
|
||||||
ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
|
ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
|
||||||
printf("%08X\t%s\n", instruction, disassembly);
|
printf("%08X\t%s\n", instruction, disassembly);
|
||||||
} else {
|
} else {
|
||||||
uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
|
uint16_t instruction = debugger->d.cpu->memory.load16(debugger->d.cpu, address, 0);
|
||||||
ARMDecodeThumb(instruction, &info);
|
ARMDecodeThumb(instruction, &info);
|
||||||
ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
|
ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
|
||||||
printf("%04X\t%s\n", instruction, disassembly);
|
printf("%04X\t%s\n", instruction, disassembly);
|
||||||
|
@ -287,7 +287,7 @@ static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t address = dv->intValue;
|
uint32_t address = dv->intValue;
|
||||||
uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0);
|
uint8_t value = debugger->d.cpu->memory.load8(debugger->d.cpu, address, 0);
|
||||||
printf(" 0x%02X\n", value);
|
printf(" 0x%02X\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t address = dv->intValue;
|
uint32_t address = dv->intValue;
|
||||||
uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
|
uint16_t value = debugger->d.cpu->memory.load16(debugger->d.cpu, address & ~1, 0);
|
||||||
printf(" 0x%04X\n", value);
|
printf(" 0x%04X\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t address = dv->intValue;
|
uint32_t address = dv->intValue;
|
||||||
uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
|
uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address & ~3, 0);
|
||||||
printf(" 0x%08X\n", value);
|
printf(" 0x%08X\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -446,11 +446,10 @@ void GDBStubCreate(struct GDBStub* stub) {
|
||||||
stub->d.log = 0;
|
stub->d.log = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GDBStubListen(struct GDBStub* stub, int port, uint32_t bindAddress) {
|
int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
|
||||||
if (!SOCKET_FAILED(stub->socket)) {
|
if (!SOCKET_FAILED(stub->socket)) {
|
||||||
GDBStubShutdown(stub);
|
GDBStubShutdown(stub);
|
||||||
}
|
}
|
||||||
// TODO: support IPv6
|
|
||||||
stub->socket = SocketOpenTCP(port, bindAddress);
|
stub->socket = SocketOpenTCP(port, bindAddress);
|
||||||
if (SOCKET_FAILED(stub->socket)) {
|
if (SOCKET_FAILED(stub->socket)) {
|
||||||
if (stub->d.log) {
|
if (stub->d.log) {
|
||||||
|
@ -500,7 +499,7 @@ void GDBStubUpdate(struct GDBStub* stub) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stub->connection == INVALID_SOCKET) {
|
if (stub->connection == INVALID_SOCKET) {
|
||||||
stub->connection = SocketAccept(stub->socket, 0, 0);
|
stub->connection = SocketAccept(stub->socket, 0);
|
||||||
if (!SOCKET_FAILED(stub->connection)) {
|
if (!SOCKET_FAILED(stub->connection)) {
|
||||||
if (!SocketSetBlocking(stub->connection, 0)) {
|
if (!SocketSetBlocking(stub->connection, 0)) {
|
||||||
goto connectionLost;
|
goto connectionLost;
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct GDBStub {
|
||||||
};
|
};
|
||||||
|
|
||||||
void GDBStubCreate(struct GDBStub*);
|
void GDBStubCreate(struct GDBStub*);
|
||||||
int GDBStubListen(struct GDBStub*, int port, uint32_t bindAddress);
|
int GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress);
|
||||||
|
|
||||||
void GDBStubHangup(struct GDBStub*);
|
void GDBStubHangup(struct GDBStub*);
|
||||||
void GDBStubShutdown(struct GDBStub*);
|
void GDBStubShutdown(struct GDBStub*);
|
||||||
|
|
|
@ -40,11 +40,9 @@ static bool _checkWatchpoints(struct DebugBreakpoint* watchpoints, uint32_t addr
|
||||||
return debugger->originalMemory.NAME(cpu, ARGS); \
|
return debugger->originalMemory.NAME(cpu, ARGS); \
|
||||||
}
|
}
|
||||||
|
|
||||||
CREATE_WATCHPOINT_SHIM(load32, 4, int32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(load32, 4, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||||
CREATE_WATCHPOINT_SHIM(load16, 2, int16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(load16, 2, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||||
CREATE_WATCHPOINT_SHIM(loadU16, 2, uint16_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(load8, 1, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||||
CREATE_WATCHPOINT_SHIM(load8, 1, int8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
|
||||||
CREATE_WATCHPOINT_SHIM(loadU8, 1, uint8_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
|
||||||
CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter)
|
||||||
CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter)
|
||||||
CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)
|
CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)
|
||||||
|
@ -67,9 +65,7 @@ void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) {
|
||||||
debugger->cpu->memory.store8 = ARMDebuggerShim_store8;
|
debugger->cpu->memory.store8 = ARMDebuggerShim_store8;
|
||||||
debugger->cpu->memory.load32 = ARMDebuggerShim_load32;
|
debugger->cpu->memory.load32 = ARMDebuggerShim_load32;
|
||||||
debugger->cpu->memory.load16 = ARMDebuggerShim_load16;
|
debugger->cpu->memory.load16 = ARMDebuggerShim_load16;
|
||||||
debugger->cpu->memory.loadU16 = ARMDebuggerShim_loadU16;
|
|
||||||
debugger->cpu->memory.load8 = ARMDebuggerShim_load8;
|
debugger->cpu->memory.load8 = ARMDebuggerShim_load8;
|
||||||
debugger->cpu->memory.loadU8 = ARMDebuggerShim_loadU8;
|
|
||||||
debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion;
|
debugger->cpu->memory.setActiveRegion = ARMDebuggerShim_setActiveRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +75,6 @@ void ARMDebuggerRemoveMemoryShim(struct ARMDebugger* debugger) {
|
||||||
debugger->cpu->memory.store8 = debugger->originalMemory.store8;
|
debugger->cpu->memory.store8 = debugger->originalMemory.store8;
|
||||||
debugger->cpu->memory.load32 = debugger->originalMemory.load32;
|
debugger->cpu->memory.load32 = debugger->originalMemory.load32;
|
||||||
debugger->cpu->memory.load16 = debugger->originalMemory.load16;
|
debugger->cpu->memory.load16 = debugger->originalMemory.load16;
|
||||||
debugger->cpu->memory.loadU16 = debugger->originalMemory.loadU16;
|
|
||||||
debugger->cpu->memory.load8 = debugger->originalMemory.load8;
|
debugger->cpu->memory.load8 = debugger->originalMemory.load8;
|
||||||
debugger->cpu->memory.loadU8 = debugger->originalMemory.loadU8;
|
|
||||||
debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion;
|
debugger->cpu->memory.setActiveRegion = debugger->originalMemory.setActiveRegion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ static void _BgAffineSet(struct GBA* gba) {
|
||||||
cy = cpu->memory.load16(cpu, offset + 10, 0);
|
cy = cpu->memory.load16(cpu, offset + 10, 0);
|
||||||
sx = cpu->memory.load16(cpu, offset + 12, 0) / 256.f;
|
sx = cpu->memory.load16(cpu, offset + 12, 0) / 256.f;
|
||||||
sy = cpu->memory.load16(cpu, offset + 14, 0) / 256.f;
|
sy = cpu->memory.load16(cpu, offset + 14, 0) / 256.f;
|
||||||
theta = (cpu->memory.loadU16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI;
|
theta = (cpu->memory.load16(cpu, offset + 16, 0) >> 8) / 128.f * M_PI;
|
||||||
offset += 20;
|
offset += 20;
|
||||||
// Rotation
|
// Rotation
|
||||||
a = d = cosf(theta);
|
a = d = cosf(theta);
|
||||||
|
@ -131,7 +131,7 @@ static void _ObjAffineSet(struct GBA* gba) {
|
||||||
// [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ]
|
// [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ]
|
||||||
sx = cpu->memory.load16(cpu, offset, 0) / 256.f;
|
sx = cpu->memory.load16(cpu, offset, 0) / 256.f;
|
||||||
sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f;
|
sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f;
|
||||||
theta = (cpu->memory.loadU16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI;
|
theta = (cpu->memory.load16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI;
|
||||||
offset += 8;
|
offset += 8;
|
||||||
// Rotation
|
// Rotation
|
||||||
a = d = cosf(theta);
|
a = d = cosf(theta);
|
||||||
|
@ -312,7 +312,6 @@ static void _unLz77(struct GBA* gba, int width) {
|
||||||
int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set
|
int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set
|
||||||
source += 4;
|
source += 4;
|
||||||
int blocksRemaining = 0;
|
int blocksRemaining = 0;
|
||||||
int block;
|
|
||||||
uint32_t disp;
|
uint32_t disp;
|
||||||
int bytes;
|
int bytes;
|
||||||
int byte;
|
int byte;
|
||||||
|
@ -321,29 +320,32 @@ static void _unLz77(struct GBA* gba, int width) {
|
||||||
if (blocksRemaining) {
|
if (blocksRemaining) {
|
||||||
if (blockheader & 0x80) {
|
if (blockheader & 0x80) {
|
||||||
// Compressed
|
// Compressed
|
||||||
block = cpu->memory.loadU8(cpu, source, 0) | (cpu->memory.loadU8(cpu, source + 1, 0) << 8);
|
int block = cpu->memory.load8(cpu, source + 1, 0) | (cpu->memory.load8(cpu, source, 0) << 8);
|
||||||
source += 2;
|
source += 2;
|
||||||
disp = dest - (((block & 0x000F) << 8) | ((block & 0xFF00) >> 8)) - 1;
|
disp = dest - (block & 0x0FFF) - 1;
|
||||||
bytes = ((block & 0x00F0) >> 4) + 3;
|
bytes = (block >> 12) + 3;
|
||||||
while (bytes-- && remaining) {
|
while (bytes-- && remaining) {
|
||||||
--remaining;
|
--remaining;
|
||||||
byte = cpu->memory.loadU8(cpu, disp, 0);
|
|
||||||
++disp;
|
|
||||||
if (width == 2) {
|
if (width == 2) {
|
||||||
|
byte = cpu->memory.load16(cpu, disp & ~1, 0);
|
||||||
if (dest & 1) {
|
if (dest & 1) {
|
||||||
|
byte >>= (disp & 1) * 8;
|
||||||
halfword |= byte << 8;
|
halfword |= byte << 8;
|
||||||
cpu->memory.store16(cpu, dest ^ 1, halfword, 0);
|
cpu->memory.store16(cpu, dest ^ 1, halfword, 0);
|
||||||
} else {
|
} else {
|
||||||
halfword = byte;
|
byte >>= (disp & 1) * 8;
|
||||||
|
halfword = byte & 0xFF;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
byte = cpu->memory.load8(cpu, disp, 0);
|
||||||
cpu->memory.store8(cpu, dest, byte, 0);
|
cpu->memory.store8(cpu, dest, byte, 0);
|
||||||
}
|
}
|
||||||
|
++disp;
|
||||||
++dest;
|
++dest;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Uncompressed
|
// Uncompressed
|
||||||
byte = cpu->memory.loadU8(cpu, source, 0);
|
byte = cpu->memory.load8(cpu, source, 0);
|
||||||
++source;
|
++source;
|
||||||
if (width == 2) {
|
if (width == 2) {
|
||||||
if (dest & 1) {
|
if (dest & 1) {
|
||||||
|
@ -361,7 +363,7 @@ static void _unLz77(struct GBA* gba, int width) {
|
||||||
blockheader <<= 1;
|
blockheader <<= 1;
|
||||||
--blocksRemaining;
|
--blocksRemaining;
|
||||||
} else {
|
} else {
|
||||||
blockheader = cpu->memory.loadU8(cpu, source, 0);
|
blockheader = cpu->memory.load8(cpu, source, 0);
|
||||||
++source;
|
++source;
|
||||||
blocksRemaining = 8;
|
blocksRemaining = 8;
|
||||||
}
|
}
|
||||||
|
@ -390,7 +392,7 @@ static void _unHuffman(struct GBA* gba) {
|
||||||
int padding = (4 - remaining) & 0x3;
|
int padding = (4 - remaining) & 0x3;
|
||||||
remaining &= 0xFFFFFFFC;
|
remaining &= 0xFFFFFFFC;
|
||||||
// We assume the signature byte (0x20) is correct
|
// We assume the signature byte (0x20) is correct
|
||||||
int treesize = (cpu->memory.loadU8(cpu, source + 4, 0) << 1) + 1;
|
int treesize = (cpu->memory.load8(cpu, source + 4, 0) << 1) + 1;
|
||||||
int block = 0;
|
int block = 0;
|
||||||
uint32_t treeBase = source + 5;
|
uint32_t treeBase = source + 5;
|
||||||
source += 5 + treesize;
|
source += 5 + treesize;
|
||||||
|
@ -458,13 +460,13 @@ static void _unRl(struct GBA* gba, int width) {
|
||||||
uint32_t dest = cpu->gprs[1];
|
uint32_t dest = cpu->gprs[1];
|
||||||
int halfword = 0;
|
int halfword = 0;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
blockheader = cpu->memory.loadU8(cpu, source, 0);
|
blockheader = cpu->memory.load8(cpu, source, 0);
|
||||||
++source;
|
++source;
|
||||||
if (blockheader & 0x80) {
|
if (blockheader & 0x80) {
|
||||||
// Compressed
|
// Compressed
|
||||||
blockheader &= 0x7F;
|
blockheader &= 0x7F;
|
||||||
blockheader += 3;
|
blockheader += 3;
|
||||||
block = cpu->memory.loadU8(cpu, source, 0);
|
block = cpu->memory.load8(cpu, source, 0);
|
||||||
++source;
|
++source;
|
||||||
while (blockheader-- && remaining) {
|
while (blockheader-- && remaining) {
|
||||||
--remaining;
|
--remaining;
|
||||||
|
@ -485,7 +487,7 @@ static void _unRl(struct GBA* gba, int width) {
|
||||||
blockheader++;
|
blockheader++;
|
||||||
while (blockheader-- && remaining) {
|
while (blockheader-- && remaining) {
|
||||||
--remaining;
|
--remaining;
|
||||||
int byte = cpu->memory.loadU8(cpu, source, 0);
|
int byte = cpu->memory.load8(cpu, source, 0);
|
||||||
++source;
|
++source;
|
||||||
if (width == 2) {
|
if (width == 2) {
|
||||||
if (dest & 1) {
|
if (dest & 1) {
|
||||||
|
@ -532,9 +534,9 @@ static void _unFilter(struct GBA* gba, int inwidth, int outwidth) {
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
uint16_t new;
|
uint16_t new;
|
||||||
if (inwidth == 1) {
|
if (inwidth == 1) {
|
||||||
new = cpu->memory.loadU8(cpu, source, 0);
|
new = cpu->memory.load8(cpu, source, 0);
|
||||||
} else {
|
} else {
|
||||||
new = cpu->memory.loadU16(cpu, source, 0);
|
new = cpu->memory.load16(cpu, source, 0);
|
||||||
}
|
}
|
||||||
new += old;
|
new += old;
|
||||||
if (outwidth > inwidth) {
|
if (outwidth > inwidth) {
|
||||||
|
|
|
@ -112,7 +112,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
|
|
||||||
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
||||||
|
|
||||||
GBALoadState(gbaDebugger->context->gba, gbaDebugger->context->stateDir, dv->intValue);
|
GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
|
@ -139,6 +139,6 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
|
|
||||||
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
||||||
|
|
||||||
GBASaveState(gbaDebugger->context->gba, gbaDebugger->context->stateDir, dv->intValue, true);
|
GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, true);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,9 +31,7 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
struct ARMCore* cpu = gba->cpu;
|
struct ARMCore* cpu = gba->cpu;
|
||||||
cpu->memory.load32 = GBALoad32;
|
cpu->memory.load32 = GBALoad32;
|
||||||
cpu->memory.load16 = GBALoad16;
|
cpu->memory.load16 = GBALoad16;
|
||||||
cpu->memory.loadU16 = GBALoadU16;
|
|
||||||
cpu->memory.load8 = GBALoad8;
|
cpu->memory.load8 = GBALoad8;
|
||||||
cpu->memory.loadU8 = GBALoadU8;
|
|
||||||
cpu->memory.loadMultiple = GBALoadMultiple;
|
cpu->memory.loadMultiple = GBALoadMultiple;
|
||||||
cpu->memory.store32 = GBAStore32;
|
cpu->memory.store32 = GBAStore32;
|
||||||
cpu->memory.store16 = GBAStore16;
|
cpu->memory.store16 = GBAStore16;
|
||||||
|
@ -227,7 +225,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
||||||
value |= value << 8; \
|
value |= value << 8; \
|
||||||
value |= value << 16;
|
value |= value << 16;
|
||||||
|
|
||||||
int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
struct GBA* gba = (struct GBA*) cpu->master;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
uint32_t value = 0;
|
uint32_t value = 0;
|
||||||
|
@ -278,17 +276,13 @@ int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
}
|
}
|
||||||
// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
|
// Unaligned 32-bit loads are "rotated" so they make some semblance of sense
|
||||||
int rotate = (address & 3) << 3;
|
int rotate = (address & 3) << 3;
|
||||||
return (value >> rotate) | (value << (32 - rotate));
|
return ROR(value, rotate);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
return GBALoad16(cpu, address, cycleCounter);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
struct GBA* gba = (struct GBA*) cpu->master;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
uint16_t value = 0;
|
uint32_t value = 0;
|
||||||
int wait = 0;
|
int wait = 0;
|
||||||
|
|
||||||
switch (address >> BASE_OFFSET) {
|
switch (address >> BASE_OFFSET) {
|
||||||
|
@ -298,14 +292,14 @@ int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
LOAD_16(value, address, memory->bios);
|
LOAD_16(value, address, memory->bios);
|
||||||
} else {
|
} else {
|
||||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad BIOS Load16: 0x%08X", address);
|
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad BIOS Load16: 0x%08X", address);
|
||||||
value = memory->biosPrefetch;
|
value = memory->biosPrefetch & 0xFFFF;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
|
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
|
||||||
if (cpu->cycles >= cpu->nextEvent) {
|
if (cpu->cycles >= cpu->nextEvent) {
|
||||||
value = gba->bus;
|
value = gba->bus & 0xFFFF;
|
||||||
} else {
|
} else {
|
||||||
value = cpu->prefetch[1];
|
value = cpu->prefetch[1] & 0xFFFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -373,17 +367,13 @@ int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
}
|
}
|
||||||
// Unaligned 16-bit loads are "unpredictable", but the GBA rotates them, so we have to, too.
|
// Unaligned 16-bit loads are "unpredictable", but the GBA rotates them, so we have to, too.
|
||||||
int rotate = (address & 1) << 3;
|
int rotate = (address & 1) << 3;
|
||||||
return (value >> rotate) | (value << (16 - rotate));
|
return ROR(value, rotate);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
return GBALoad8(cpu, address, cycleCounter);
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
struct GBA* gba = (struct GBA*) cpu->master;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
int8_t value = 0;
|
uint8_t value = 0;
|
||||||
int wait = 0;
|
int wait = 0;
|
||||||
|
|
||||||
switch (address >> BASE_OFFSET) {
|
switch (address >> BASE_OFFSET) {
|
||||||
|
@ -440,7 +430,7 @@ int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
case REGION_CART_SRAM:
|
case REGION_CART_SRAM:
|
||||||
case REGION_CART_SRAM_MIRROR:
|
case REGION_CART_SRAM_MIRROR:
|
||||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||||
GBALog(gba, GBA_LOG_INFO, "Detected SRAM savegame");
|
GBALog(gba, GBA_LOG_INFO, "Detected SRAM savegame");
|
||||||
GBASavedataInitSRAM(&memory->savedata);
|
GBASavedataInitSRAM(&memory->savedata);
|
||||||
}
|
}
|
||||||
|
@ -596,7 +586,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REGION_CART2_EX:
|
case REGION_CART2_EX:
|
||||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||||
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
|
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
|
||||||
GBASavedataInitEEPROM(&memory->savedata);
|
GBASavedataInitEEPROM(&memory->savedata);
|
||||||
}
|
}
|
||||||
|
@ -652,7 +642,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
||||||
break;
|
break;
|
||||||
case REGION_CART_SRAM:
|
case REGION_CART_SRAM:
|
||||||
case REGION_CART_SRAM_MIRROR:
|
case REGION_CART_SRAM_MIRROR:
|
||||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||||
if (address == SAVEDATA_FLASH_BASE) {
|
if (address == SAVEDATA_FLASH_BASE) {
|
||||||
GBALog(gba, GBA_LOG_INFO, "Detected Flash savegame");
|
GBALog(gba, GBA_LOG_INFO, "Detected Flash savegame");
|
||||||
GBASavedataInitFlash(&memory->savedata);
|
GBASavedataInitFlash(&memory->savedata);
|
||||||
|
@ -1152,7 +1142,7 @@ void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
dest += destOffset;
|
dest += destOffset;
|
||||||
--wordsRemaining;
|
--wordsRemaining;
|
||||||
} else if (destRegion == REGION_CART2_EX) {
|
} else if (destRegion == REGION_CART2_EX) {
|
||||||
if (memory->savedata.type == SAVEDATA_NONE) {
|
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||||
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
|
GBALog(gba, GBA_LOG_INFO, "Detected EEPROM savegame");
|
||||||
GBASavedataInitEEPROM(&memory->savedata);
|
GBASavedataInitEEPROM(&memory->savedata);
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,11 +144,9 @@ void GBAMemoryDeinit(struct GBA* gba);
|
||||||
|
|
||||||
void GBAMemoryReset(struct GBA* gba);
|
void GBAMemoryReset(struct GBA* gba);
|
||||||
|
|
||||||
int32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
||||||
int16_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
||||||
uint16_t GBALoadU16(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
||||||
int8_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
|
||||||
uint8_t GBALoadU8(struct ARMCore* cpu, uint32_t address, int* cycleCounter);
|
|
||||||
|
|
||||||
void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter);
|
void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter);
|
||||||
void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter);
|
void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter);
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "gba-overrides.h"
|
||||||
|
|
||||||
|
#include "gba.h"
|
||||||
|
#include "gba-gpio.h"
|
||||||
|
|
||||||
|
#include "util/configuration.h"
|
||||||
|
|
||||||
|
static const struct GBACartridgeOverride _overrides[] = {
|
||||||
|
// Boktai: The Sun is in Your Hand
|
||||||
|
{ "U3IJ", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
{ "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
{ "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
|
||||||
|
// Boktai 2: Solar Boy Django
|
||||||
|
{ "U32J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
{ "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
{ "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
|
||||||
|
// Drill Dozer
|
||||||
|
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
||||||
|
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
||||||
|
|
||||||
|
// Final Fantasy Tactics Advance
|
||||||
|
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 },
|
||||||
|
|
||||||
|
// Koro Koro Puzzle - Happy Panechu!
|
||||||
|
{ "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
||||||
|
|
||||||
|
// Mega Man Battle Network
|
||||||
|
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
|
||||||
|
|
||||||
|
// Pokemon Ruby
|
||||||
|
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
|
||||||
|
// Pokemon Sapphire
|
||||||
|
{ "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
|
||||||
|
// Pokemon Emerald
|
||||||
|
{ "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
{ "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
||||||
|
|
||||||
|
// Pokemon Mystery Dungeon
|
||||||
|
{ "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
|
||||||
|
// Pokemon FireRed
|
||||||
|
{ "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
|
||||||
|
// Pokemon LeafGreen
|
||||||
|
{ "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
|
||||||
|
// RockMan EXE 4.5 - Real Operation
|
||||||
|
{ "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 },
|
||||||
|
|
||||||
|
// Shin Bokura no Taiyou: Gyakushuu no Sabata
|
||||||
|
{ "U33J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
||||||
|
|
||||||
|
// Super Mario Advance 4
|
||||||
|
{ "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
{ "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
||||||
|
|
||||||
|
// Top Gun - Combat Zones
|
||||||
|
{ "A2YE", SAVEDATA_FORCE_NONE, GPIO_NONE, -1 },
|
||||||
|
|
||||||
|
// Wario Ware Twisted
|
||||||
|
{ "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
||||||
|
{ "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
||||||
|
{ "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
||||||
|
|
||||||
|
// Yoshi's Universal Gravitation
|
||||||
|
{ "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
||||||
|
{ "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
||||||
|
{ "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
||||||
|
|
||||||
|
{ { 0, 0, 0, 0 }, 0, 0, -1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) {
|
||||||
|
override->savetype = SAVEDATA_AUTODETECT;
|
||||||
|
override->hardware = GPIO_NONE;
|
||||||
|
override->idleLoop = -1;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
if (override->id[0] == 'F') {
|
||||||
|
// Classic NES Series
|
||||||
|
override->savetype = SAVEDATA_EEPROM;
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
for (i = 0; _overrides[i].id[0]; ++i) {
|
||||||
|
if (memcmp(override->id, _overrides[i].id, sizeof(override->id)) == 0) {
|
||||||
|
*override = _overrides[i];
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
char sectionName[16];
|
||||||
|
snprintf(sectionName, sizeof(sectionName), "override.%c%c%c%c", override->id[0], override->id[1], override->id[2], override->id[3]);
|
||||||
|
const char* savetype = ConfigurationGetValue(config, sectionName, "savetype");
|
||||||
|
const char* hardware = ConfigurationGetValue(config, sectionName, "hardware");
|
||||||
|
const char* idleLoop = ConfigurationGetValue(config, sectionName, "idleLoop");
|
||||||
|
|
||||||
|
if (savetype) {
|
||||||
|
if (strcasecmp(savetype, "SRAM") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_SRAM;
|
||||||
|
} else if (strcasecmp(savetype, "EEPROM") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_EEPROM;
|
||||||
|
} else if (strcasecmp(savetype, "FLASH512") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_FLASH512;
|
||||||
|
} else if (strcasecmp(savetype, "FLASH1M") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_FLASH1M;
|
||||||
|
} else if (strcasecmp(savetype, "NONE") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_FORCE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hardware) {
|
||||||
|
char* end;
|
||||||
|
long type = strtoul(hardware, &end, 0);
|
||||||
|
if (end && !*end) {
|
||||||
|
override->hardware = type;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idleLoop) {
|
||||||
|
char* end;
|
||||||
|
uint32_t address = strtoul(idleLoop, &end, 16);
|
||||||
|
if (end && !*end) {
|
||||||
|
override->idleLoop = address;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) {
|
||||||
|
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
||||||
|
|
||||||
|
if (override->hardware & GPIO_RTC) {
|
||||||
|
GBAGPIOInitRTC(&gba->memory.gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->hardware & GPIO_GYRO) {
|
||||||
|
GBAGPIOInitGyro(&gba->memory.gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->hardware & GPIO_RUMBLE) {
|
||||||
|
GBAGPIOInitRumble(&gba->memory.gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->hardware & GPIO_LIGHT_SENSOR) {
|
||||||
|
GBAGPIOInitLightSensor(&gba->memory.gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override->hardware & GPIO_TILT) {
|
||||||
|
GBAGPIOInitTilt(&gba->memory.gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
gba->busyLoop = override->idleLoop;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef GBA_OVERRIDES_H
|
||||||
|
#define GBA_OVERRIDES_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
#include "gba-savedata.h"
|
||||||
|
|
||||||
|
struct GBACartridgeOverride {
|
||||||
|
char id[4];
|
||||||
|
enum SavedataType savetype;
|
||||||
|
int hardware;
|
||||||
|
uint32_t idleLoop;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Configuration;
|
||||||
|
bool GBAOverrideFind(const struct Configuration*, struct GBACartridgeOverride* override);
|
||||||
|
bool GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* override);
|
||||||
|
|
||||||
|
struct GBA;
|
||||||
|
void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*);
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,7 +18,7 @@ static void _flashErase(struct GBASavedata* savedata);
|
||||||
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
|
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
|
||||||
|
|
||||||
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
||||||
savedata->type = SAVEDATA_NONE;
|
savedata->type = SAVEDATA_AUTODETECT;
|
||||||
savedata->data = 0;
|
savedata->data = 0;
|
||||||
savedata->command = EEPROM_COMMAND_NULL;
|
savedata->command = EEPROM_COMMAND_NULL;
|
||||||
savedata->flashState = FLASH_STATE_RAW;
|
savedata->flashState = FLASH_STATE_RAW;
|
||||||
|
@ -42,7 +42,8 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
|
||||||
break;
|
break;
|
||||||
case SAVEDATA_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
|
case SAVEDATA_AUTODETECT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
savedata->vf = 0;
|
savedata->vf = 0;
|
||||||
|
@ -60,12 +61,13 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
||||||
break;
|
break;
|
||||||
case SAVEDATA_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
|
case SAVEDATA_AUTODETECT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
savedata->data = 0;
|
savedata->data = 0;
|
||||||
savedata->type = SAVEDATA_NONE;
|
savedata->type = SAVEDATA_AUTODETECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) {
|
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) {
|
||||||
|
@ -94,7 +96,8 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
||||||
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
||||||
case SAVEDATA_NONE:
|
case SAVEDATA_AUTODETECT:
|
||||||
|
case SAVEDATA_FORCE_NONE:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (savedata->vf) {
|
} else if (savedata->vf) {
|
||||||
|
@ -122,14 +125,16 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type)
|
||||||
case SAVEDATA_SRAM:
|
case SAVEDATA_SRAM:
|
||||||
GBASavedataInitSRAM(savedata);
|
GBASavedataInitSRAM(savedata);
|
||||||
break;
|
break;
|
||||||
case SAVEDATA_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
// TODO: Force none
|
savedata->type = SAVEDATA_FORCE_NONE;
|
||||||
|
break;
|
||||||
|
case SAVEDATA_AUTODETECT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
||||||
if (savedata->type == SAVEDATA_NONE) {
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
||||||
savedata->type = SAVEDATA_FLASH512;
|
savedata->type = SAVEDATA_FLASH512;
|
||||||
}
|
}
|
||||||
if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
|
if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
|
||||||
|
@ -142,7 +147,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
||||||
end = 0;
|
end = 0;
|
||||||
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
||||||
} else {
|
} else {
|
||||||
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
|
end = savedata->vf->size(savedata->vf);
|
||||||
if (end < SIZE_CART_FLASH512) {
|
if (end < SIZE_CART_FLASH512) {
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
||||||
flashSize = SIZE_CART_FLASH1M;
|
flashSize = SIZE_CART_FLASH1M;
|
||||||
|
@ -157,7 +162,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
||||||
if (savedata->type == SAVEDATA_NONE) {
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
||||||
savedata->type = SAVEDATA_EEPROM;
|
savedata->type = SAVEDATA_EEPROM;
|
||||||
} else {
|
} else {
|
||||||
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
||||||
|
@ -168,7 +173,7 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
||||||
end = 0;
|
end = 0;
|
||||||
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
||||||
} else {
|
} else {
|
||||||
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
|
end = savedata->vf->size(savedata->vf);
|
||||||
if (end < SIZE_CART_EEPROM) {
|
if (end < SIZE_CART_EEPROM) {
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +185,7 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataInitSRAM(struct GBASavedata* savedata) {
|
void GBASavedataInitSRAM(struct GBASavedata* savedata) {
|
||||||
if (savedata->type == SAVEDATA_NONE) {
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
||||||
savedata->type = SAVEDATA_SRAM;
|
savedata->type = SAVEDATA_SRAM;
|
||||||
} else {
|
} else {
|
||||||
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
|
||||||
|
@ -191,7 +196,7 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) {
|
||||||
end = 0;
|
end = 0;
|
||||||
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
|
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
|
||||||
} else {
|
} else {
|
||||||
end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
|
end = savedata->vf->size(savedata->vf);
|
||||||
if (end < SIZE_CART_SRAM) {
|
if (end < SIZE_CART_SRAM) {
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
|
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
|
||||||
enum SavedataType {
|
enum SavedataType {
|
||||||
SAVEDATA_NONE = 0,
|
SAVEDATA_AUTODETECT = -1,
|
||||||
|
SAVEDATA_FORCE_NONE = 0,
|
||||||
SAVEDATA_SRAM,
|
SAVEDATA_SRAM,
|
||||||
SAVEDATA_FLASH512,
|
SAVEDATA_FLASH512,
|
||||||
SAVEDATA_FLASH1M,
|
SAVEDATA_FLASH1M,
|
||||||
|
|
|
@ -175,22 +175,23 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) {
|
bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, bool screenshot) {
|
||||||
struct VFile* vf = GBAGetState(gba, dir, slot, true);
|
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, true);
|
||||||
if (!vf) {
|
if (!vf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = GBASaveStateNamed(gba, vf, screenshot);
|
bool success = GBASaveStateNamed(threadContext->gba, vf, screenshot);
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) {
|
bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot) {
|
||||||
struct VFile* vf = GBAGetState(gba, dir, slot, false);
|
struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, false);
|
||||||
if (!vf) {
|
if (!vf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool success = GBALoadStateNamed(gba, vf);
|
threadContext->rewindBufferSize = 0;
|
||||||
|
bool success = GBALoadStateNamed(threadContext->gba, vf);
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,12 +282,13 @@ struct GBASerializedState {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VDir;
|
struct VDir;
|
||||||
|
struct GBAThread;
|
||||||
|
|
||||||
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
||||||
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state);
|
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state);
|
||||||
|
|
||||||
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot);
|
bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, bool screenshot);
|
||||||
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot);
|
bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot);
|
||||||
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
|
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
|
||||||
|
|
||||||
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot);
|
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot);
|
||||||
|
@ -296,7 +297,6 @@ bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf);
|
||||||
struct GBASerializedState* GBAAllocateState(void);
|
struct GBASerializedState* GBAAllocateState(void);
|
||||||
void GBADeallocateState(struct GBASerializedState* state);
|
void GBADeallocateState(struct GBASerializedState* state);
|
||||||
|
|
||||||
struct GBAThread;
|
|
||||||
void GBARecordFrame(struct GBAThread* thread);
|
void GBARecordFrame(struct GBAThread* thread);
|
||||||
void GBARewindSettingsChanged(struct GBAThread* thread, int newCapacity, int newInterval);
|
void GBARewindSettingsChanged(struct GBAThread* thread, int newCapacity, int newInterval);
|
||||||
void GBARewind(struct GBAThread* thread, int nStates);
|
void GBARewind(struct GBAThread* thread, int nStates);
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
|
|
||||||
#include "gba-io.h"
|
#include "gba-io.h"
|
||||||
|
|
||||||
|
const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = {
|
||||||
|
{ 31457, 62914, 94371, 125829 },
|
||||||
|
{ 7864, 15728, 23592, 31457 },
|
||||||
|
{ 5242, 10485, 15728, 20971 },
|
||||||
|
{ 2621, 5242, 7864, 10485 }
|
||||||
|
};
|
||||||
|
|
||||||
static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) {
|
static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SIO_NORMAL_8:
|
case SIO_NORMAL_8:
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
|
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
|
#define MAX_GBAS 4
|
||||||
|
|
||||||
|
extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS];
|
||||||
|
|
||||||
enum GBASIOMode {
|
enum GBASIOMode {
|
||||||
SIO_NORMAL_8 = 0,
|
SIO_NORMAL_8 = 0,
|
||||||
SIO_NORMAL_32 = 1,
|
SIO_NORMAL_32 = 1,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-config.h"
|
#include "gba-config.h"
|
||||||
|
#include "gba-overrides.h"
|
||||||
#include "gba-serialize.h"
|
#include "gba-serialize.h"
|
||||||
|
|
||||||
#include "debugger/debugger.h"
|
#include "debugger/debugger.h"
|
||||||
|
@ -141,6 +142,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
||||||
|
|
||||||
if (threadContext->rom) {
|
if (threadContext->rom) {
|
||||||
GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
|
GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
|
||||||
|
|
||||||
|
struct GBACartridgeOverride override;
|
||||||
|
const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom;
|
||||||
|
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||||
|
if (GBAOverrideFind(threadContext->overrides, &override)) {
|
||||||
|
GBAOverrideApply(&gba, &override);
|
||||||
|
}
|
||||||
|
|
||||||
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
|
||||||
GBALoadBIOS(&gba, threadContext->bios);
|
GBALoadBIOS(&gba, threadContext->bios);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct GBAThread {
|
||||||
const char* fname;
|
const char* fname;
|
||||||
int activeKeys;
|
int activeKeys;
|
||||||
struct GBAAVStream* stream;
|
struct GBAAVStream* stream;
|
||||||
|
struct Configuration* overrides;
|
||||||
|
|
||||||
// Run-time options
|
// Run-time options
|
||||||
int frameskip;
|
int frameskip;
|
||||||
|
|
153
src/gba/gba.c
153
src/gba/gba.c
|
@ -24,111 +24,6 @@ const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
|
||||||
static const size_t GBA_ROM_MAGIC_OFFSET = 2;
|
static const size_t GBA_ROM_MAGIC_OFFSET = 2;
|
||||||
static const uint8_t GBA_ROM_MAGIC[] = { 0x00, 0xEA };
|
static const uint8_t GBA_ROM_MAGIC[] = { 0x00, 0xEA };
|
||||||
|
|
||||||
struct GBACartridgeOverride {
|
|
||||||
const char id[4];
|
|
||||||
enum SavedataType type;
|
|
||||||
int gpio;
|
|
||||||
uint32_t busyLoop;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct GBACartridgeOverride _overrides[] = {
|
|
||||||
// Boktai: The Sun is in Your Hand
|
|
||||||
{ "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
|
||||||
{ "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
|
||||||
|
|
||||||
// Boktai 2: Solar Boy Django
|
|
||||||
{ "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
|
||||||
{ "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 },
|
|
||||||
|
|
||||||
// Drill Dozer
|
|
||||||
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
|
||||||
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 },
|
|
||||||
|
|
||||||
// Final Fantasy Tactics Advance
|
|
||||||
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 },
|
|
||||||
|
|
||||||
// Golden Sun: The Lost Age
|
|
||||||
{ "AGFE", SAVEDATA_FLASH512, GPIO_NONE, 0x801353A },
|
|
||||||
|
|
||||||
// Koro Koro Puzzle - Happy Panechu!
|
|
||||||
{ "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
|
||||||
|
|
||||||
// Mega Man Battle Network
|
|
||||||
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
|
|
||||||
|
|
||||||
// Metal Slug Advance
|
|
||||||
{ "BSME", SAVEDATA_EEPROM, GPIO_NONE, 0x8000290 },
|
|
||||||
|
|
||||||
// Pokemon Ruby
|
|
||||||
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
|
|
||||||
// Pokemon Sapphire
|
|
||||||
{ "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
|
|
||||||
// Pokemon Emerald
|
|
||||||
{ "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, 0x80008C6 },
|
|
||||||
{ "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
{ "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 },
|
|
||||||
|
|
||||||
// Pokemon Mystery Dungeon
|
|
||||||
{ "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
|
|
||||||
// Pokemon FireRed
|
|
||||||
{ "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
|
|
||||||
// Pokemon LeafGreen
|
|
||||||
{ "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
|
|
||||||
// RockMan EXE 4.5 - Real Operation
|
|
||||||
{ "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 },
|
|
||||||
|
|
||||||
// Super Mario Advance 2
|
|
||||||
{ "AA2E", SAVEDATA_EEPROM, GPIO_NONE, 0x800052E },
|
|
||||||
|
|
||||||
// Super Mario Advance 3
|
|
||||||
{ "A3AE", SAVEDATA_EEPROM, GPIO_NONE, 0x8002B9C },
|
|
||||||
|
|
||||||
// Super Mario Advance 4
|
|
||||||
{ "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
{ "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 },
|
|
||||||
|
|
||||||
// Wario Ware Twisted
|
|
||||||
{ "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
|
||||||
{ "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
|
||||||
{ "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 },
|
|
||||||
|
|
||||||
// Yoshi's Universal Gravitation
|
|
||||||
{ "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
|
||||||
{ "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
|
||||||
{ "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 },
|
|
||||||
|
|
||||||
{ { 0, 0, 0, 0 }, 0, 0, -1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component);
|
static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component);
|
||||||
static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh);
|
static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh);
|
||||||
static void GBAProcessEvents(struct ARMCore* cpu);
|
static void GBAProcessEvents(struct ARMCore* cpu);
|
||||||
|
@ -136,8 +31,6 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
|
||||||
static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
|
static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
|
||||||
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
|
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
|
||||||
|
|
||||||
static void _checkOverrides(struct GBA* gba, uint32_t code);
|
|
||||||
|
|
||||||
void GBACreate(struct GBA* gba) {
|
void GBACreate(struct GBA* gba) {
|
||||||
gba->d.id = GBA_COMPONENT_MAGIC;
|
gba->d.id = GBA_COMPONENT_MAGIC;
|
||||||
gba->d.init = GBAInit;
|
gba->d.init = GBAInit;
|
||||||
|
@ -452,7 +345,7 @@ void GBADetachDebugger(struct GBA* gba) {
|
||||||
|
|
||||||
void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname) {
|
void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname) {
|
||||||
gba->romVf = vf;
|
gba->romVf = vf;
|
||||||
gba->pristineRomSize = vf->seek(vf, 0, SEEK_END);
|
gba->pristineRomSize = vf->size(vf);
|
||||||
vf->seek(vf, 0, SEEK_SET);
|
vf->seek(vf, 0, SEEK_SET);
|
||||||
if (gba->pristineRomSize > SIZE_CART0) {
|
if (gba->pristineRomSize > SIZE_CART0) {
|
||||||
gba->pristineRomSize = SIZE_CART0;
|
gba->pristineRomSize = SIZE_CART0;
|
||||||
|
@ -468,7 +361,6 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char
|
||||||
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
|
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
|
||||||
GBASavedataInit(&gba->memory.savedata, sav);
|
GBASavedataInit(&gba->memory.savedata, sav);
|
||||||
GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
||||||
_checkOverrides(gba, ((struct GBACartridge*) gba->memory.rom)->id);
|
|
||||||
// TODO: error check
|
// TODO: error check
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,8 +395,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gba->memory.rom = anonymousMemoryMap(patchedSize);
|
gba->memory.rom = anonymousMemoryMap(patchedSize);
|
||||||
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize > patchedSize ? patchedSize : gba->memory.romSize);
|
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
|
||||||
if (!patch->applyPatch(patch, gba->memory.rom, patchedSize)) {
|
|
||||||
mappedMemoryFree(gba->memory.rom, patchedSize);
|
mappedMemoryFree(gba->memory.rom, patchedSize);
|
||||||
gba->memory.rom = gba->pristineRom;
|
gba->memory.rom = gba->pristineRom;
|
||||||
return;
|
return;
|
||||||
|
@ -744,43 +635,3 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
|
ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkOverrides(struct GBA* gba, uint32_t id) {
|
|
||||||
int i;
|
|
||||||
gba->busyLoop = -1;
|
|
||||||
if ((id & 0xFF) == 'F') {
|
|
||||||
GBALog(gba, GBA_LOG_DEBUG, "Found Classic NES Series game, using EEPROM saves");
|
|
||||||
GBASavedataInitEEPROM(&gba->memory.savedata);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (i = 0; _overrides[i].id[0]; ++i) {
|
|
||||||
const uint32_t* overrideId = (const uint32_t*) _overrides[i].id;
|
|
||||||
if (*overrideId == id) {
|
|
||||||
GBALog(gba, GBA_LOG_DEBUG, "Found override for game %s!", _overrides[i].id);
|
|
||||||
GBASavedataForceType(&gba->memory.savedata, _overrides[i].type);
|
|
||||||
|
|
||||||
if (_overrides[i].gpio & GPIO_RTC) {
|
|
||||||
GBAGPIOInitRTC(&gba->memory.gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_overrides[i].gpio & GPIO_GYRO) {
|
|
||||||
GBAGPIOInitGyro(&gba->memory.gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_overrides[i].gpio & GPIO_RUMBLE) {
|
|
||||||
GBAGPIOInitRumble(&gba->memory.gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_overrides[i].gpio & GPIO_LIGHT_SENSOR) {
|
|
||||||
GBAGPIOInitLightSensor(&gba->memory.gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_overrides[i].gpio & GPIO_TILT) {
|
|
||||||
GBAGPIOInitTilt(&gba->memory.gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
gba->busyLoop = _overrides[i].busyLoop;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
context.debugger = createDebugger(&args, &context);
|
context.debugger = createDebugger(&args, &context);
|
||||||
|
context.overrides = &config.configTable;
|
||||||
char gameCode[5] = { 0 };
|
char gameCode[5] = { 0 };
|
||||||
|
|
||||||
GBAConfigMap(&config, &opts);
|
GBAConfigMap(&config, &opts);
|
||||||
|
@ -121,7 +122,7 @@ int main(int argc, char** argv) {
|
||||||
printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
|
printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return GBAThreadHasCrashed(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) {
|
static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet) {
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
QList<QString> getMRU() const;
|
QList<QString> getMRU() const;
|
||||||
void setMRU(const QList<QString>& mru);
|
void setMRU(const QList<QString>& mru);
|
||||||
|
|
||||||
|
Configuration* overrides() { return &m_config.configTable; } // TODO: Make this not return the whole table
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setOption(const char* key, bool value);
|
void setOption(const char* key, bool value);
|
||||||
void setOption(const char* key, int value);
|
void setOption(const char* key, int value);
|
||||||
|
|
|
@ -26,15 +26,15 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
||||||
QApplication::setApplicationName(PROJECT_NAME);
|
QApplication::setApplicationName(PROJECT_NAME);
|
||||||
QApplication::setApplicationVersion(PROJECT_VERSION);
|
QApplication::setApplicationVersion(PROJECT_VERSION);
|
||||||
|
|
||||||
|
m_window.show();
|
||||||
|
|
||||||
GBAArguments args = {};
|
GBAArguments args = {};
|
||||||
if (m_configController.parseArguments(&args, argc, argv)) {
|
if (m_configController.parseArguments(&args, argc, argv)) {
|
||||||
m_window.argumentsPassed(&args);
|
m_window.argumentsPassed(&args);
|
||||||
} else {
|
} else {
|
||||||
m_window.loadConfig();
|
m_window.loadConfig();
|
||||||
}
|
}
|
||||||
freeArguments(&args);
|
freeArguments(&args);
|
||||||
|
|
||||||
m_window.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAApp::event(QEvent* event) {
|
bool GBAApp::event(QEvent* event) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ GDBController::GDBController(GameController* controller, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_gameController(controller)
|
, m_gameController(controller)
|
||||||
, m_port(2345)
|
, m_port(2345)
|
||||||
, m_bindAddress(0)
|
, m_bindAddress({ IPV4, 0 })
|
||||||
{
|
{
|
||||||
GDBStubCreate(&m_gdbStub);
|
GDBStubCreate(&m_gdbStub);
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,6 @@ ushort GDBController::port() {
|
||||||
return m_port;
|
return m_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GDBController::bindAddress() {
|
|
||||||
return m_bindAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GDBController::isAttached() {
|
bool GDBController::isAttached() {
|
||||||
return m_gameController->debugger() == &m_gdbStub.d;
|
return m_gameController->debugger() == &m_gdbStub.d;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +31,8 @@ void GDBController::setPort(ushort port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBController::setBindAddress(uint32_t bindAddress) {
|
void GDBController::setBindAddress(uint32_t bindAddress) {
|
||||||
m_bindAddress = bindAddress;
|
m_bindAddress.version = IPV4;
|
||||||
|
m_bindAddress.ipv4 = htonl(bindAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBController::attach() {
|
void GDBController::attach() {
|
||||||
|
@ -62,7 +59,7 @@ void GDBController::listen() {
|
||||||
attach();
|
attach();
|
||||||
}
|
}
|
||||||
connect(m_gameController, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateGDB()));
|
connect(m_gameController, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateGDB()));
|
||||||
GDBStubListen(&m_gdbStub, m_port, m_bindAddress);
|
GDBStubListen(&m_gdbStub, m_port, &m_bindAddress);
|
||||||
m_gameController->threadContinue();
|
m_gameController->threadContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ushort port();
|
ushort port();
|
||||||
uint32_t bindAddress();
|
|
||||||
bool isAttached();
|
bool isAttached();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -44,7 +43,7 @@ private:
|
||||||
GameController* m_gameController;
|
GameController* m_gameController;
|
||||||
|
|
||||||
ushort m_port;
|
ushort m_port;
|
||||||
uint32_t m_bindAddress;
|
Address m_bindAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,7 @@ void GameController::openGame() {
|
||||||
|
|
||||||
if (!GBAThreadStart(&m_threadContext)) {
|
if (!GBAThreadStart(&m_threadContext)) {
|
||||||
m_gameOpen = false;
|
m_gameOpen = false;
|
||||||
|
emit gameFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +361,7 @@ void GameController::setSkipBIOS(bool set) {
|
||||||
|
|
||||||
void GameController::loadState(int slot) {
|
void GameController::loadState(int slot) {
|
||||||
threadInterrupt();
|
threadInterrupt();
|
||||||
GBALoadState(m_threadContext.gba, m_threadContext.stateDir, slot);
|
GBALoadState(&m_threadContext, m_threadContext.stateDir, slot);
|
||||||
threadContinue();
|
threadContinue();
|
||||||
emit stateLoaded(&m_threadContext);
|
emit stateLoaded(&m_threadContext);
|
||||||
emit frameAvailable(m_drawContext);
|
emit frameAvailable(m_drawContext);
|
||||||
|
@ -368,7 +369,7 @@ void GameController::loadState(int slot) {
|
||||||
|
|
||||||
void GameController::saveState(int slot) {
|
void GameController::saveState(int slot) {
|
||||||
threadInterrupt();
|
threadInterrupt();
|
||||||
GBASaveState(m_threadContext.gba, m_threadContext.stateDir, slot, true);
|
GBASaveState(&m_threadContext, m_threadContext.stateDir, slot, true);
|
||||||
threadContinue();
|
threadContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ extern "C" {
|
||||||
|
|
||||||
struct GBAAudio;
|
struct GBAAudio;
|
||||||
struct GBAVideoSoftwareRenderer;
|
struct GBAVideoSoftwareRenderer;
|
||||||
|
struct Configuration;
|
||||||
|
|
||||||
class QThread;
|
class QThread;
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ public:
|
||||||
bool videoSync() const { return m_videoSync; }
|
bool videoSync() const { return m_videoSync; }
|
||||||
|
|
||||||
void setInputController(InputController* controller) { m_inputController = controller; }
|
void setInputController(InputController* controller) { m_inputController = controller; }
|
||||||
|
void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; }
|
||||||
|
|
||||||
#ifdef USE_GDB_STUB
|
#ifdef USE_GDB_STUB
|
||||||
ARMDebugger* debugger();
|
ARMDebugger* debugger();
|
||||||
|
@ -66,6 +68,7 @@ signals:
|
||||||
void gamePaused(GBAThread*);
|
void gamePaused(GBAThread*);
|
||||||
void gameUnpaused(GBAThread*);
|
void gameUnpaused(GBAThread*);
|
||||||
void gameCrashed(const QString& errorMessage);
|
void gameCrashed(const QString& errorMessage);
|
||||||
|
void gameFailed();
|
||||||
void stateLoaded(GBAThread*);
|
void stateLoaded(GBAThread*);
|
||||||
|
|
||||||
void postLog(int level, const QString& log);
|
void postLog(int level, const QString& log);
|
||||||
|
|
|
@ -50,7 +50,7 @@ void GamePakView::gameStarted(GBAThread* thread) {
|
||||||
}
|
}
|
||||||
SavedataType savetype = thread->gba->memory.savedata.type;
|
SavedataType savetype = thread->gba->memory.savedata.type;
|
||||||
if (m_ui.savetype->currentIndex() > 0) {
|
if (m_ui.savetype->currentIndex() > 0) {
|
||||||
if (savetype > SAVEDATA_NONE) {
|
if (savetype > SAVEDATA_FORCE_NONE) {
|
||||||
VFile* vf = thread->gba->memory.savedata.vf;
|
VFile* vf = thread->gba->memory.savedata.vf;
|
||||||
GBASavedataDeinit(&thread->gba->memory.savedata);
|
GBASavedataDeinit(&thread->gba->memory.savedata);
|
||||||
GBASavedataInit(&thread->gba->memory.savedata, vf);
|
GBASavedataInit(&thread->gba->memory.savedata, vf);
|
||||||
|
@ -59,7 +59,7 @@ void GamePakView::gameStarted(GBAThread* thread) {
|
||||||
GBASavedataForceType(&thread->gba->memory.savedata, savetype);
|
GBASavedataForceType(&thread->gba->memory.savedata, savetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savetype > SAVEDATA_NONE) {
|
if (savetype > SAVEDATA_AUTODETECT) {
|
||||||
m_ui.savetype->setCurrentIndex(savetype + 1);
|
m_ui.savetype->setCurrentIndex(savetype + 1);
|
||||||
}
|
}
|
||||||
m_ui.savetype->setEnabled(false);
|
m_ui.savetype->setEnabled(false);
|
||||||
|
|
|
@ -27,9 +27,5 @@ qint64 VFileDevice::writeData(const char* data, qint64 maxSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 VFileDevice::size() const {
|
qint64 VFileDevice::size() const {
|
||||||
// TODO: Add size method to VFile so this can be actually const
|
return m_vf->size(m_vf);
|
||||||
ssize_t pos = m_vf->seek(m_vf, 0, SEEK_CUR);
|
|
||||||
qint64 size = m_vf->seek(m_vf, 0, SEEK_END);
|
|
||||||
m_vf->seek(m_vf, pos, SEEK_SET);
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
|
@ -55,6 +56,7 @@ Window::Window(ConfigController* config, QWidget* parent)
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
m_controller = new GameController(this);
|
m_controller = new GameController(this);
|
||||||
m_controller->setInputController(&m_inputController);
|
m_controller->setInputController(&m_inputController);
|
||||||
|
m_controller->setOverrides(m_config->overrides());
|
||||||
|
|
||||||
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
|
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
|
||||||
format.setSwapInterval(1);
|
format.setSwapInterval(1);
|
||||||
|
@ -82,6 +84,7 @@ Window::Window(ConfigController* config, QWidget* parent)
|
||||||
connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&)));
|
connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&)));
|
||||||
connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame()));
|
connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame()));
|
||||||
connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&)));
|
connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&)));
|
||||||
|
connect(m_controller, SIGNAL(gameFailed()), this, SLOT(gameFailed()));
|
||||||
connect(m_logView, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int)));
|
connect(m_logView, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int)));
|
||||||
connect(m_logView, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int)));
|
connect(m_logView, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int)));
|
||||||
connect(m_logView, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int)));
|
connect(m_logView, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int)));
|
||||||
|
@ -175,15 +178,17 @@ void Window::saveConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::selectROM() {
|
void Window::selectROM() {
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM"));
|
QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM"), m_config->getQtOption("lastDirectory").toString(), tr("Game Boy Advance ROMs (*.gba *.zip *.rom *.bin)"));
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
|
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||||
m_controller->loadGame(filename);
|
m_controller->loadGame(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::selectBIOS() {
|
void Window::selectBIOS() {
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select BIOS"));
|
QString filename = QFileDialog::getOpenFileName(this, tr("Select BIOS"), m_config->getQtOption("lastDirectory").toString());
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
|
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||||
m_config->setOption("bios", filename);
|
m_config->setOption("bios", filename);
|
||||||
m_config->updateOption("bios");
|
m_config->updateOption("bios");
|
||||||
m_controller->loadBIOS(filename);
|
m_controller->loadBIOS(filename);
|
||||||
|
@ -191,8 +196,9 @@ void Window::selectBIOS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::selectPatch() {
|
void Window::selectPatch() {
|
||||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select patch"), QString(), tr("Patches (*.ips *.ups)"));
|
QString filename = QFileDialog::getOpenFileName(this, tr("Select patch"), m_config->getQtOption("lastDirectory").toString(), tr("Patches (*.ips *.ups)"));
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
|
m_config->setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||||
m_controller->loadPatch(filename);
|
m_controller->loadPatch(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,6 +386,14 @@ void Window::gameCrashed(const QString& errorMessage) {
|
||||||
crash->show();
|
crash->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::gameFailed() {
|
||||||
|
QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Load"),
|
||||||
|
tr("Could not load game. Are you sure it's in the correct format?"),
|
||||||
|
QMessageBox::Ok, this, Qt::Sheet);
|
||||||
|
fail->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
fail->show();
|
||||||
|
}
|
||||||
|
|
||||||
void Window::redoLogo() {
|
void Window::redoLogo() {
|
||||||
if (m_controller->isLoaded()) {
|
if (m_controller->isLoaded()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -96,6 +96,7 @@ private slots:
|
||||||
void gameStarted(GBAThread*);
|
void gameStarted(GBAThread*);
|
||||||
void gameStopped();
|
void gameStopped();
|
||||||
void gameCrashed(const QString&);
|
void gameCrashed(const QString&);
|
||||||
|
void gameFailed();
|
||||||
void redoLogo();
|
void redoLogo();
|
||||||
|
|
||||||
void recordFrame();
|
void recordFrame();
|
||||||
|
|
|
@ -103,6 +103,7 @@ int main(int argc, char** argv) {
|
||||||
GBASDLInitBindings(&inputMap);
|
GBASDLInitBindings(&inputMap);
|
||||||
GBASDLInitEvents(&renderer.events);
|
GBASDLInitEvents(&renderer.events);
|
||||||
GBASDLEventsLoadConfig(&renderer.events, &config.configTable); // TODO: Don't use this directly
|
GBASDLEventsLoadConfig(&renderer.events, &config.configTable); // TODO: Don't use this directly
|
||||||
|
context.overrides = &config.configTable;
|
||||||
|
|
||||||
GBAThreadStart(&context);
|
GBAThreadStart(&context);
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
case SDLK_F8:
|
case SDLK_F8:
|
||||||
case SDLK_F9:
|
case SDLK_F9:
|
||||||
GBAThreadInterrupt(context);
|
GBAThreadInterrupt(context);
|
||||||
GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
|
GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true);
|
||||||
GBAThreadContinue(context);
|
GBAThreadContinue(context);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -231,7 +231,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
||||||
case SDLK_F8:
|
case SDLK_F8:
|
||||||
case SDLK_F9:
|
case SDLK_F9:
|
||||||
GBAThreadInterrupt(context);
|
GBAThreadInterrupt(context);
|
||||||
GBALoadState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
|
GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1);
|
||||||
GBAThreadContinue(context);
|
GBAThreadContinue(context);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
|
static size_t _IPSOutputSize(struct Patch* patch, size_t inSize);
|
||||||
static bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
|
static bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
|
||||||
|
|
||||||
bool loadPatchIPS(struct Patch* patch) {
|
bool loadPatchIPS(struct Patch* patch) {
|
||||||
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
||||||
|
@ -42,10 +42,11 @@ size_t _IPSOutputSize(struct Patch* patch, size_t inSize) {
|
||||||
return inSize;
|
return inSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _IPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
|
bool _IPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
|
||||||
if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
|
if (patch->vf->seek(patch->vf, 5, SEEK_SET) != 5) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
memcpy(out, in, inSize > outSize ? outSize : inSize);
|
||||||
uint8_t* buf = out;
|
uint8_t* buf = out;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -16,8 +16,11 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
static size_t _UPSOutputSize(struct Patch* patch, size_t inSize);
|
||||||
static bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize);
|
|
||||||
static size_t _UPSDecodeLength(struct VFile* vf);
|
static bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
|
||||||
|
static bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
|
||||||
|
|
||||||
|
static size_t _decodeLength(struct VFile* vf);
|
||||||
|
|
||||||
bool loadPatchUPS(struct Patch* patch) {
|
bool loadPatchUPS(struct Patch* patch) {
|
||||||
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
patch->vf->seek(patch->vf, 0, SEEK_SET);
|
||||||
|
@ -27,11 +30,15 @@ bool loadPatchUPS(struct Patch* patch) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(buffer, "UPS1", 4) != 0) {
|
if (memcmp(buffer, "UPS1", 4) == 0) {
|
||||||
|
patch->applyPatch = _UPSApplyPatch;
|
||||||
|
} else if (memcmp(buffer, "BPS1", 4) == 0) {
|
||||||
|
patch->applyPatch = _BPSApplyPatch;
|
||||||
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
|
size_t filesize = patch->vf->size(patch->vf);
|
||||||
|
|
||||||
uint32_t goodCrc32;
|
uint32_t goodCrc32;
|
||||||
patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END);
|
patch->vf->seek(patch->vf, PATCH_CHECKSUM, SEEK_END);
|
||||||
|
@ -45,34 +52,35 @@ bool loadPatchUPS(struct Patch* patch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
patch->outputSize = _UPSOutputSize;
|
patch->outputSize = _UPSOutputSize;
|
||||||
patch->applyPatch = _UPSApplyPatch;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
|
size_t _UPSOutputSize(struct Patch* patch, size_t inSize) {
|
||||||
UNUSED(inSize);
|
UNUSED(inSize);
|
||||||
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
if (_UPSDecodeLength(patch->vf) != inSize) {
|
if (_decodeLength(patch->vf) != inSize) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return _UPSDecodeLength(patch->vf);
|
return _decodeLength(patch->vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
|
bool _UPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
|
||||||
// TODO: Input checksum
|
// TODO: Input checksum
|
||||||
|
|
||||||
size_t filesize = patch->vf->seek(patch->vf, 0, SEEK_END);
|
size_t filesize = patch->vf->size(patch->vf);
|
||||||
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
_UPSDecodeLength(patch->vf); // Discard input size
|
_decodeLength(patch->vf); // Discard input size
|
||||||
if (_UPSDecodeLength(patch->vf) != outSize) {
|
if (_decodeLength(patch->vf) != outSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(out, in, inSize > outSize ? outSize : inSize);
|
||||||
|
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
size_t alreadyRead = 0;
|
size_t alreadyRead = 0;
|
||||||
uint8_t* buf = out;
|
uint8_t* buf = out;
|
||||||
while (alreadyRead < filesize + IN_CHECKSUM) {
|
while (alreadyRead < filesize + IN_CHECKSUM) {
|
||||||
offset += _UPSDecodeLength(patch->vf);
|
offset += _decodeLength(patch->vf);
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -101,7 +109,100 @@ bool _UPSApplyPatch(struct Patch* patch, void* out, size_t outSize) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _UPSDecodeLength(struct VFile* vf) {
|
bool _BPSApplyPatch(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize) {
|
||||||
|
patch->vf->seek(patch->vf, IN_CHECKSUM, SEEK_END);
|
||||||
|
uint32_t expectedInChecksum;
|
||||||
|
uint32_t expectedOutChecksum;
|
||||||
|
patch->vf->read(patch->vf, &expectedInChecksum, sizeof(expectedInChecksum));
|
||||||
|
patch->vf->read(patch->vf, &expectedOutChecksum, sizeof(expectedOutChecksum));
|
||||||
|
|
||||||
|
uint32_t inputChecksum = doCrc32(in, inSize);
|
||||||
|
uint32_t outputChecksum = 0;
|
||||||
|
|
||||||
|
if (inputChecksum != expectedInChecksum) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t filesize = patch->vf->size(patch->vf);
|
||||||
|
patch->vf->seek(patch->vf, 4, SEEK_SET);
|
||||||
|
_decodeLength(patch->vf); // Discard input size
|
||||||
|
if (_decodeLength(patch->vf) != outSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t metadataLength = _decodeLength(patch->vf);
|
||||||
|
patch->vf->seek(patch->vf, metadataLength, SEEK_CUR); // Skip metadata
|
||||||
|
size_t writeLocation = 0;
|
||||||
|
ssize_t readSourceLocation = 0;
|
||||||
|
ssize_t readTargetLocation = 0;
|
||||||
|
size_t readOffset;
|
||||||
|
uint8_t* writeBuffer = out;
|
||||||
|
uint8_t* readBuffer = in;
|
||||||
|
while (patch->vf->seek(patch->vf, 0, SEEK_CUR) < filesize + IN_CHECKSUM) {
|
||||||
|
size_t command = _decodeLength(patch->vf);
|
||||||
|
size_t length = (command >> 2) + 1;
|
||||||
|
if (writeLocation + length > outSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t i;
|
||||||
|
switch (command & 0x3) {
|
||||||
|
case 0x0:
|
||||||
|
// SourceRead
|
||||||
|
memmove(&writeBuffer[writeLocation], &readBuffer[writeLocation], length);
|
||||||
|
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
|
||||||
|
writeLocation += length;
|
||||||
|
break;
|
||||||
|
case 0x1:
|
||||||
|
// TargetRead
|
||||||
|
if (patch->vf->read(patch->vf, &writeBuffer[writeLocation], length) != length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
|
||||||
|
writeLocation += length;
|
||||||
|
break;
|
||||||
|
case 0x2:
|
||||||
|
// SourceCopy
|
||||||
|
readOffset = _decodeLength(patch->vf);
|
||||||
|
if (readOffset & 1) {
|
||||||
|
readSourceLocation -= readOffset >> 1;
|
||||||
|
} else {
|
||||||
|
readSourceLocation += readOffset >> 1;
|
||||||
|
}
|
||||||
|
if (readSourceLocation < 0 || readSourceLocation > inSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memmove(&writeBuffer[writeLocation], &readBuffer[readSourceLocation], length);
|
||||||
|
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation], length);
|
||||||
|
writeLocation += length;
|
||||||
|
readSourceLocation += length;
|
||||||
|
break;
|
||||||
|
case 0x3:
|
||||||
|
// TargetCopy
|
||||||
|
readOffset = _decodeLength(patch->vf);
|
||||||
|
if (readOffset & 1) {
|
||||||
|
readTargetLocation -= readOffset >> 1;
|
||||||
|
} else {
|
||||||
|
readTargetLocation += readOffset >> 1;
|
||||||
|
}
|
||||||
|
if (readTargetLocation < 0 || readTargetLocation > outSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < length; ++i) {
|
||||||
|
// This needs to be bytewise as it can overlap
|
||||||
|
writeBuffer[writeLocation] = writeBuffer[readTargetLocation];
|
||||||
|
++writeLocation;
|
||||||
|
++readTargetLocation;
|
||||||
|
}
|
||||||
|
outputChecksum = updateCrc32(outputChecksum, &writeBuffer[writeLocation - length], length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (expectedOutChecksum != outputChecksum) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _decodeLength(struct VFile* vf) {
|
||||||
size_t shift = 1;
|
size_t shift = 1;
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct Patch {
|
||||||
struct VFile* vf;
|
struct VFile* vf;
|
||||||
|
|
||||||
size_t (*outputSize)(struct Patch* patch, size_t inSize);
|
size_t (*outputSize)(struct Patch* patch, size_t inSize);
|
||||||
bool (*applyPatch)(struct Patch* patch, void* out, size_t outSize);
|
bool (*applyPatch)(struct Patch* patch, void* in, size_t inSize, void* out, size_t outSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool loadPatch(struct VFile* vf, struct Patch* patch);
|
bool loadPatch(struct VFile* vf, struct Patch* patch);
|
||||||
|
|
|
@ -29,6 +29,18 @@ typedef SOCKET Socket;
|
||||||
typedef int Socket;
|
typedef int Socket;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum IP {
|
||||||
|
IPV4,
|
||||||
|
IPV6
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Address {
|
||||||
|
enum IP version;
|
||||||
|
union {
|
||||||
|
uint32_t ipv4;
|
||||||
|
uint8_t ipv6[16];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
static inline void SocketSubsystemInitialize() {
|
static inline void SocketSubsystemInitialize() {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -44,18 +56,35 @@ static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
|
||||||
return read(socket, buffer, size);
|
return read(socket, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) {
|
static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
|
||||||
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
if (SOCKET_FAILED(sock)) {
|
if (SOCKET_FAILED(sock)) {
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in bindInfo;
|
int err;
|
||||||
memset(&bindInfo, 0, sizeof(bindInfo));
|
if (!bindAddress) {
|
||||||
bindInfo.sin_family = AF_INET;
|
struct sockaddr_in bindInfo;
|
||||||
bindInfo.sin_port = htons(port);
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
bindInfo.sin_addr.s_addr = htonl(bindAddress);
|
bindInfo.sin_family = AF_INET;
|
||||||
int err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in));
|
bindInfo.sin_port = htons(port);
|
||||||
|
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
} else if (bindAddress->version == IPV4) {
|
||||||
|
struct sockaddr_in bindInfo;
|
||||||
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
|
bindInfo.sin_family = AF_INET;
|
||||||
|
bindInfo.sin_port = htons(port);
|
||||||
|
bindInfo.sin_addr.s_addr = bindAddress->ipv4;
|
||||||
|
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
} else {
|
||||||
|
struct sockaddr_in6 bindInfo;
|
||||||
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
|
bindInfo.sin6_family = AF_INET6;
|
||||||
|
bindInfo.sin6_port = htons(port);
|
||||||
|
memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
|
||||||
|
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
|
||||||
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
close(sock);
|
close(sock);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -63,18 +92,35 @@ static inline Socket SocketOpenTCP(int port, uint32_t bindAddress) {
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Socket SocketConnectTCP(int port, uint32_t destinationAddress) {
|
static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
|
||||||
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
if (SOCKET_FAILED(sock)) {
|
if (SOCKET_FAILED(sock)) {
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in bindInfo;
|
int err;
|
||||||
memset(&bindInfo, 0, sizeof(bindInfo));
|
if (!destinationAddress) {
|
||||||
bindInfo.sin_family = AF_INET;
|
struct sockaddr_in bindInfo;
|
||||||
bindInfo.sin_port = htons(port);
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
bindInfo.sin_addr.s_addr = htonl(destinationAddress);
|
bindInfo.sin_family = AF_INET;
|
||||||
int err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(struct sockaddr_in));
|
bindInfo.sin_port = htons(port);
|
||||||
|
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
} else if (destinationAddress->version == IPV4) {
|
||||||
|
struct sockaddr_in bindInfo;
|
||||||
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
|
bindInfo.sin_family = AF_INET;
|
||||||
|
bindInfo.sin_port = htons(port);
|
||||||
|
bindInfo.sin_addr.s_addr = destinationAddress->ipv4;
|
||||||
|
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
} else {
|
||||||
|
struct sockaddr_in6 bindInfo;
|
||||||
|
memset(&bindInfo, 0, sizeof(bindInfo));
|
||||||
|
bindInfo.sin6_family = AF_INET6;
|
||||||
|
bindInfo.sin6_port = htons(port);
|
||||||
|
memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
|
||||||
|
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
|
||||||
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
close(sock);
|
close(sock);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -86,8 +132,25 @@ static inline Socket SocketListen(Socket socket, int queueLength) {
|
||||||
return listen(socket, queueLength);
|
return listen(socket, queueLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Socket SocketAccept(Socket socket, struct sockaddr* restrict address, socklen_t* restrict addressLength) {
|
static inline Socket SocketAccept(Socket socket, struct Address* address) {
|
||||||
return accept(socket, address, addressLength);
|
if (!address) {
|
||||||
|
return accept(socket, 0, 0);
|
||||||
|
}
|
||||||
|
if (address->version == IPV4) {
|
||||||
|
struct sockaddr_in addrInfo;
|
||||||
|
memset(&addrInfo, 0, sizeof(addrInfo));
|
||||||
|
addrInfo.sin_family = AF_INET;
|
||||||
|
addrInfo.sin_addr.s_addr = address->ipv4;
|
||||||
|
socklen_t len = sizeof(addrInfo);
|
||||||
|
return accept(socket, (struct sockaddr*) &addrInfo, &len);
|
||||||
|
} else {
|
||||||
|
struct sockaddr_in6 addrInfo;
|
||||||
|
memset(&addrInfo, 0, sizeof(addrInfo));
|
||||||
|
addrInfo.sin6_family = AF_INET6;
|
||||||
|
memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
|
||||||
|
socklen_t len = sizeof(addrInfo);
|
||||||
|
return accept(socket, (struct sockaddr*) &addrInfo, &len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int SocketClose(Socket socket) {
|
static inline int SocketClose(Socket socket) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
@ -35,6 +36,7 @@ static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||||
static void* _vfdMap(struct VFile* vf, size_t size, int flags);
|
static void* _vfdMap(struct VFile* vf, size_t size, int flags);
|
||||||
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
|
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
|
||||||
static void _vfdTruncate(struct VFile* vf, size_t size);
|
static void _vfdTruncate(struct VFile* vf, size_t size);
|
||||||
|
static ssize_t _vfdSize(struct VFile* vf);
|
||||||
|
|
||||||
static bool _vdClose(struct VDir* vd);
|
static bool _vdClose(struct VDir* vd);
|
||||||
static void _vdRewind(struct VDir* vd);
|
static void _vdRewind(struct VDir* vd);
|
||||||
|
@ -73,6 +75,7 @@ struct VFile* VFileFromFD(int fd) {
|
||||||
vfd->d.map = _vfdMap;
|
vfd->d.map = _vfdMap;
|
||||||
vfd->d.unmap = _vfdUnmap;
|
vfd->d.unmap = _vfdUnmap;
|
||||||
vfd->d.truncate = _vfdTruncate;
|
vfd->d.truncate = _vfdTruncate;
|
||||||
|
vfd->d.size = _vfdSize;
|
||||||
|
|
||||||
return &vfd->d;
|
return &vfd->d;
|
||||||
}
|
}
|
||||||
|
@ -137,9 +140,12 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
|
||||||
createFlags = PAGE_READWRITE;
|
createFlags = PAGE_READWRITE;
|
||||||
mapFiles = FILE_MAP_WRITE;
|
mapFiles = FILE_MAP_WRITE;
|
||||||
}
|
}
|
||||||
size_t location = lseek(vfd->fd, 0, SEEK_CUR);
|
size_t fileSize;
|
||||||
size_t fileSize = lseek(vfd->fd, 0, SEEK_END);
|
struct stat stat;
|
||||||
lseek(vfd->fd, location, SEEK_SET);
|
if (fstat(vfd->fd, &stat) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fileSize = stat.st_size;
|
||||||
if (size > fileSize) {
|
if (size > fileSize) {
|
||||||
size = fileSize;
|
size = fileSize;
|
||||||
}
|
}
|
||||||
|
@ -161,6 +167,15 @@ static void _vfdTruncate(struct VFile* vf, size_t size) {
|
||||||
ftruncate(vfd->fd, size);
|
ftruncate(vfd->fd, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t _vfdSize(struct VFile* vf) {
|
||||||
|
struct VFileFD* vfd = (struct VFileFD*) vf;
|
||||||
|
struct stat stat;
|
||||||
|
if (fstat(vfd->fd, &stat) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return stat.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
struct VDirEntryDE {
|
struct VDirEntryDE {
|
||||||
struct VDirEntry d;
|
struct VDirEntry d;
|
||||||
struct dirent* ent;
|
struct dirent* ent;
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct VFile {
|
||||||
void* (*map)(struct VFile* vf, size_t size, int flags);
|
void* (*map)(struct VFile* vf, size_t size, int flags);
|
||||||
void (*unmap)(struct VFile* vf, void* memory, size_t size);
|
void (*unmap)(struct VFile* vf, void* memory, size_t size);
|
||||||
void (*truncate)(struct VFile* vf, size_t size);
|
void (*truncate)(struct VFile* vf, size_t size);
|
||||||
|
ssize_t (*size)(struct VFile* vf);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VDirEntry {
|
struct VDirEntry {
|
||||||
|
|
|
@ -43,6 +43,7 @@ static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||||
static void* _vfzMap(struct VFile* vf, size_t size, int flags);
|
static void* _vfzMap(struct VFile* vf, size_t size, int flags);
|
||||||
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
|
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
|
||||||
static void _vfzTruncate(struct VFile* vf, size_t size);
|
static void _vfzTruncate(struct VFile* vf, size_t size);
|
||||||
|
static ssize_t _vfzSize(struct VFile* vf);
|
||||||
|
|
||||||
static bool _vdzClose(struct VDir* vd);
|
static bool _vdzClose(struct VDir* vd);
|
||||||
static void _vdzRewind(struct VDir* vd);
|
static void _vdzRewind(struct VDir* vd);
|
||||||
|
@ -229,6 +230,11 @@ void _vfzTruncate(struct VFile* vf, size_t size) {
|
||||||
UNUSED(size);
|
UNUSED(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t _vfzSize(struct VFile* vf) {
|
||||||
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
||||||
|
return vfz->fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
bool _vdzClose(struct VDir* vd) {
|
bool _vdzClose(struct VDir* vd) {
|
||||||
struct VDirZip* vdz = (struct VDirZip*) vd;
|
struct VDirZip* vdz = (struct VDirZip*) vd;
|
||||||
if (zip_close(vdz->z) < 0) {
|
if (zip_close(vdz->z) < 0) {
|
||||||
|
@ -295,6 +301,7 @@ struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
|
||||||
vfz->d.map = _vfzMap;
|
vfz->d.map = _vfzMap;
|
||||||
vfz->d.unmap = _vfzUnmap;
|
vfz->d.unmap = _vfzUnmap;
|
||||||
vfz->d.truncate = _vfzTruncate;
|
vfz->d.truncate = _vfzTruncate;
|
||||||
|
vfz->d.size = _vfzSize;
|
||||||
|
|
||||||
return &vfz->d;
|
return &vfz->d;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ class PerfTest(object):
|
||||||
except:
|
except:
|
||||||
proc.kill()
|
proc.kill()
|
||||||
raise
|
raise
|
||||||
if proc.returncode < 0:
|
if proc.returncode:
|
||||||
print('Game crashed!', file=sys.stderr)
|
print('Game crashed!', file=sys.stderr)
|
||||||
return
|
return
|
||||||
reader = csv.DictReader(proc.stdout)
|
reader = csv.DictReader(proc.stdout)
|
||||||
|
|
Loading…
Reference in New Issue