mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
64c3c82c2a
1
CHANGES
1
CHANGES
|
@ -65,6 +65,7 @@ Emulation fixes:
|
||||||
- GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339)
|
- GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339)
|
||||||
- GBA: Improve timing when not booting from BIOS
|
- GBA: Improve timing when not booting from BIOS
|
||||||
- GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450)
|
- GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450)
|
||||||
|
- GBA: Fix booting multiboot ROMs with no JOY entrypoint
|
||||||
- GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059)
|
- GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059)
|
||||||
- GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141)
|
- GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141)
|
||||||
- GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410)
|
- GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410)
|
||||||
|
|
|
@ -58,6 +58,7 @@ The following mappers are fully supported:
|
||||||
- Pokémon Jade/Diamond (unlicensed)
|
- Pokémon Jade/Diamond (unlicensed)
|
||||||
- BBD (unlicensed MBC5-like)
|
- BBD (unlicensed MBC5-like)
|
||||||
- Hitek (unlicensed MBC5-like)
|
- Hitek (unlicensed MBC5-like)
|
||||||
|
- Sachen MMC1
|
||||||
|
|
||||||
The following mappers are partially supported:
|
The following mappers are partially supported:
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ The following mappers are partially supported:
|
||||||
- TAMA5 (missing RTC support)
|
- TAMA5 (missing RTC support)
|
||||||
- HuC-1 (missing IR support)
|
- HuC-1 (missing IR support)
|
||||||
- HuC-3 (missing IR support)
|
- HuC-3 (missing IR support)
|
||||||
|
- Sachen MMC2 (missing alternate wiring support)
|
||||||
|
|
||||||
### Planned features
|
### Planned features
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
uint32_t hash32(const void* key, int len, uint32_t seed);
|
uint32_t hash32(const void* key, size_t len, uint32_t seed);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,27 @@
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
struct TableList;
|
struct TableList;
|
||||||
|
typedef uint32_t (*HashFunction)(const void* key, size_t len, uint32_t seed);
|
||||||
|
|
||||||
|
struct TableFunctions {
|
||||||
|
void (*deinitializer)(void*);
|
||||||
|
HashFunction hash;
|
||||||
|
bool (*equal)(const void*, const void*);
|
||||||
|
void* (*ref)(void*);
|
||||||
|
void (*deref)(void*);
|
||||||
|
};
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
struct TableList* table;
|
struct TableList* table;
|
||||||
size_t tableSize;
|
size_t tableSize;
|
||||||
size_t size;
|
size_t size;
|
||||||
void (*deinitializer)(void*);
|
|
||||||
uint32_t seed;
|
uint32_t seed;
|
||||||
|
struct TableFunctions fn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TableIterator {
|
||||||
|
size_t bucket;
|
||||||
|
size_t entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
void TableInit(struct Table*, size_t initialSize, void (*deinitializer)(void*));
|
void TableInit(struct Table*, size_t initialSize, void (*deinitializer)(void*));
|
||||||
|
@ -32,26 +46,48 @@ void TableClear(struct Table*);
|
||||||
void TableEnumerate(const struct Table*, void (*handler)(uint32_t key, void* value, void* user), void* user);
|
void TableEnumerate(const struct Table*, void (*handler)(uint32_t key, void* value, void* user), void* user);
|
||||||
size_t TableSize(const struct Table*);
|
size_t TableSize(const struct Table*);
|
||||||
|
|
||||||
|
bool TableIteratorStart(const struct Table*, struct TableIterator*);
|
||||||
|
bool TableIteratorNext(const struct Table*, struct TableIterator*);
|
||||||
|
uint32_t TableIteratorGetKey(const struct Table*, const struct TableIterator*);
|
||||||
|
void* TableIteratorGetValue(const struct Table*, const struct TableIterator*);
|
||||||
|
bool TableIteratorLookup(const struct Table*, struct TableIterator*, uint32_t key);
|
||||||
|
|
||||||
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*));
|
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*));
|
||||||
|
void HashTableInitCustom(struct Table* table, size_t initialSize, const struct TableFunctions* funcs);
|
||||||
void HashTableDeinit(struct Table* table);
|
void HashTableDeinit(struct Table* table);
|
||||||
|
|
||||||
void* HashTableLookup(const struct Table*, const char* key);
|
void* HashTableLookup(const struct Table*, const char* key);
|
||||||
void* HashTableLookupBinary(const struct Table*, const void* key, size_t keylen);
|
void* HashTableLookupBinary(const struct Table*, const void* key, size_t keylen);
|
||||||
|
void* HashTableLookupCustom(const struct Table*, void* key);
|
||||||
void HashTableInsert(struct Table*, const char* key, void* value);
|
void HashTableInsert(struct Table*, const char* key, void* value);
|
||||||
void HashTableInsertBinary(struct Table*, const void* key, size_t keylen, void* value);
|
void HashTableInsertBinary(struct Table*, const void* key, size_t keylen, void* value);
|
||||||
|
void HashTableInsertCustom(struct Table*, void* key, void* value);
|
||||||
|
|
||||||
void HashTableRemove(struct Table*, const char* key);
|
void HashTableRemove(struct Table*, const char* key);
|
||||||
void HashTableRemoveBinary(struct Table*, const void* key, size_t keylen);
|
void HashTableRemoveBinary(struct Table*, const void* key, size_t keylen);
|
||||||
|
void HashTableRemoveCustom(struct Table*, void* key);
|
||||||
void HashTableClear(struct Table*);
|
void HashTableClear(struct Table*);
|
||||||
|
|
||||||
void HashTableEnumerate(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
void HashTableEnumerate(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
||||||
void HashTableEnumerateBinary(const struct Table*, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user);
|
void HashTableEnumerateBinary(const struct Table*, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user);
|
||||||
|
void HashTableEnumerateCustom(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
||||||
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user);
|
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user);
|
||||||
const char* HashTableSearchPointer(const struct Table* table, const void* value);
|
const char* HashTableSearchPointer(const struct Table* table, const void* value);
|
||||||
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
|
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
|
||||||
const char* HashTableSearchString(const struct Table* table, const char* value);
|
const char* HashTableSearchString(const struct Table* table, const char* value);
|
||||||
size_t HashTableSize(const struct Table*);
|
size_t HashTableSize(const struct Table*);
|
||||||
|
|
||||||
|
bool HashTableIteratorStart(const struct Table*, struct TableIterator*);
|
||||||
|
bool HashTableIteratorNext(const struct Table*, struct TableIterator*);
|
||||||
|
const char* HashTableIteratorGetKey(const struct Table*, const struct TableIterator*);
|
||||||
|
const void* HashTableIteratorGetBinaryKey(const struct Table*, const struct TableIterator*);
|
||||||
|
size_t HashTableIteratorGetBinaryKeyLen(const struct Table*, const struct TableIterator*);
|
||||||
|
void* HashTableIteratorGetCustomKey(const struct Table*, const struct TableIterator*);
|
||||||
|
void* HashTableIteratorGetValue(const struct Table*, const struct TableIterator*);
|
||||||
|
bool HashTableIteratorLookup(const struct Table*, struct TableIterator*, const char* key);
|
||||||
|
bool HashTableIteratorLookupBinary(const struct Table*, struct TableIterator*, const void* key, size_t keylen);
|
||||||
|
bool HashTableIteratorLookupCustom(const struct Table*, struct TableIterator*, void* key);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -140,6 +140,10 @@ struct mCore {
|
||||||
size_t (*listMemoryBlocks)(const struct mCore*, const struct mCoreMemoryBlock**);
|
size_t (*listMemoryBlocks)(const struct mCore*, const struct mCoreMemoryBlock**);
|
||||||
void* (*getMemoryBlock)(struct mCore*, size_t id, size_t* sizeOut);
|
void* (*getMemoryBlock)(struct mCore*, size_t id, size_t* sizeOut);
|
||||||
|
|
||||||
|
size_t (*listRegisters)(const struct mCore*, const struct mCoreRegisterInfo**);
|
||||||
|
bool (*readRegister)(const struct mCore*, const char* name, void* out);
|
||||||
|
bool (*writeRegister)(struct mCore*, const char* name, const void* in);
|
||||||
|
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType);
|
bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType);
|
||||||
struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*);
|
struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*);
|
||||||
|
|
|
@ -287,6 +287,21 @@ struct mCoreMemoryBlock {
|
||||||
uint32_t segmentStart;
|
uint32_t segmentStart;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum mCoreRegisterType {
|
||||||
|
mCORE_REGISTER_GPR = 0,
|
||||||
|
mCORE_REGISTER_FPR,
|
||||||
|
mCORE_REGISTER_FLAGS,
|
||||||
|
mCORE_REGISTER_SIMD,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mCoreRegisterInfo {
|
||||||
|
const char* name;
|
||||||
|
const char** aliases;
|
||||||
|
unsigned width;
|
||||||
|
uint32_t mask;
|
||||||
|
enum mCoreRegisterType type;
|
||||||
|
};
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -118,8 +118,6 @@ struct mDebuggerPlatform {
|
||||||
|
|
||||||
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
|
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||||
|
|
||||||
bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
|
||||||
bool (*setRegister)(struct mDebuggerPlatform*, const char* name, int32_t value);
|
|
||||||
bool (*lookupIdentifier)(struct mDebuggerPlatform*, const char* name, int32_t* value, int* segment);
|
bool (*lookupIdentifier)(struct mDebuggerPlatform*, const char* name, int32_t* value, int* segment);
|
||||||
|
|
||||||
uint32_t (*getStackTraceMode)(struct mDebuggerPlatform*);
|
uint32_t (*getStackTraceMode)(struct mDebuggerPlatform*);
|
||||||
|
|
|
@ -241,8 +241,6 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||||
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length);
|
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length);
|
||||||
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length);
|
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length);
|
||||||
static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
|
||||||
static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
|
|
||||||
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*);
|
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*);
|
||||||
static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
|
static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
|
||||||
static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
|
static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
|
||||||
|
@ -260,8 +258,6 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
||||||
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
|
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
|
||||||
platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
|
platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
|
||||||
platform->trace = ARMDebuggerTrace;
|
platform->trace = ARMDebuggerTrace;
|
||||||
platform->getRegister = ARMDebuggerGetRegister;
|
|
||||||
platform->setRegister = ARMDebuggerSetRegister;
|
|
||||||
platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
|
platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
|
||||||
platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
|
platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
|
||||||
platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
|
platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
|
||||||
|
@ -510,82 +506,6 @@ static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out
|
||||||
ARMDebuggerFormatRegisters(frame->regs, out, length);
|
ARMDebuggerFormatRegisters(frame->regs, out, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
|
||||||
struct ARMCore* cpu = debugger->cpu;
|
|
||||||
|
|
||||||
if (strcmp(name, "sp") == 0) {
|
|
||||||
*value = cpu->gprs[ARM_SP];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "lr") == 0) {
|
|
||||||
*value = cpu->gprs[ARM_LR];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "pc") == 0) {
|
|
||||||
*value = cpu->gprs[ARM_PC];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "cpsr") == 0) {
|
|
||||||
*value = cpu->cpsr.packed;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: test if mode has SPSR
|
|
||||||
if (strcmp(name, "spsr") == 0) {
|
|
||||||
*value = cpu->spsr.packed;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name[0] == 'r') {
|
|
||||||
char* end;
|
|
||||||
uint32_t reg = strtoul(&name[1], &end, 10);
|
|
||||||
if (reg <= ARM_PC) {
|
|
||||||
*value = cpu->gprs[reg];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
|
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
|
||||||
struct ARMCore* cpu = debugger->cpu;
|
|
||||||
|
|
||||||
if (strcmp(name, "sp") == 0) {
|
|
||||||
cpu->gprs[ARM_SP] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "lr") == 0) {
|
|
||||||
cpu->gprs[ARM_LR] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "pc") == 0) {
|
|
||||||
cpu->gprs[ARM_PC] = value;
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
|
||||||
ARMWritePC(cpu);
|
|
||||||
} else {
|
|
||||||
ThumbWritePC(cpu);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name[0] == 'r') {
|
|
||||||
char* end;
|
|
||||||
uint32_t reg = strtoul(&name[1], &end, 10);
|
|
||||||
if (reg > ARM_PC) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cpu->gprs[reg] = value;
|
|
||||||
if (reg == ARM_PC) {
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
|
||||||
ARMWritePC(cpu);
|
|
||||||
} else {
|
|
||||||
ThumbWritePC(cpu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) {
|
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) {
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||||
return debugger->stackTraceMode;
|
return debugger->stackTraceMode;
|
||||||
|
|
|
@ -306,7 +306,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
SHIFTER(cpu, opcode); \
|
SHIFTER(cpu, opcode); \
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
int rn = (opcode >> 16) & 0xF; \
|
int rn = (opcode >> 16) & 0xF; \
|
||||||
int32_t n = cpu->gprs[rn]; \
|
int32_t n ATTRIBUTE_UNUSED = cpu->gprs[rn]; \
|
||||||
if (UNLIKELY(rn == ARM_PC && (opcode & 0x02000010) == 0x00000010)) { \
|
if (UNLIKELY(rn == ARM_PC && (opcode & 0x02000010) == 0x00000010)) { \
|
||||||
n += WORD_SIZE_ARM; \
|
n += WORD_SIZE_ARM; \
|
||||||
} \
|
} \
|
||||||
|
@ -421,7 +421,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
uint32_t address; \
|
uint32_t address; \
|
||||||
int rn = (opcode >> 16) & 0xF; \
|
int rn = (opcode >> 16) & 0xF; \
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
int rd = (opcode >> 12) & 0xF; \
|
||||||
int32_t d = cpu->gprs[rd]; \
|
int32_t d ATTRIBUTE_UNUSED = cpu->gprs[rd]; \
|
||||||
if (UNLIKELY(rd == ARM_PC)) { \
|
if (UNLIKELY(rd == ARM_PC)) { \
|
||||||
d += WORD_SIZE_ARM; \
|
d += WORD_SIZE_ARM; \
|
||||||
} \
|
} \
|
||||||
|
|
|
@ -452,7 +452,7 @@ static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector*
|
||||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!debugger->d.platform->setRegister(debugger->d.platform, dv->charValue, dv->next->intValue)) {
|
if (!debugger->d.core->writeRegister(debugger->d.core, dv->charValue, &dv->next->intValue)) {
|
||||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int
|
||||||
if (debugger->core->lookupIdentifier(debugger->core, name, value, segment)) {
|
if (debugger->core->lookupIdentifier(debugger->core, name, value, segment)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (debugger->platform && debugger->platform->getRegister(debugger->platform, name, value)) {
|
if (debugger->platform && debugger->core->readRegister(debugger->core, name, value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
155
src/gb/core.c
155
src/gb/core.c
|
@ -65,6 +65,23 @@ static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
|
||||||
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mCoreRegisterInfo _GBRegisters[] = {
|
||||||
|
{ "b", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "c", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "d", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "e", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "h", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "l", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "a", NULL, 1, 0xFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "f", NULL, 1, 0xF0, mCORE_REGISTER_GPR },
|
||||||
|
{ "bc", NULL, 2, 0xFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "de", NULL, 2, 0xFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "hl", NULL, 2, 0xFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "af", NULL, 2, 0xFFF0, mCORE_REGISTER_GPR },
|
||||||
|
{ "pc", NULL, 2, 0xFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "sp", NULL, 2, 0xFFFF, mCORE_REGISTER_GPR },
|
||||||
|
};
|
||||||
|
|
||||||
struct mVideoLogContext;
|
struct mVideoLogContext;
|
||||||
struct GBCore {
|
struct GBCore {
|
||||||
struct mCore d;
|
struct mCore d;
|
||||||
|
@ -860,6 +877,141 @@ void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t _GBCoreListRegisters(const struct mCore* core, const struct mCoreRegisterInfo** list) {
|
||||||
|
UNUSED(core);
|
||||||
|
*list = _GBRegisters;
|
||||||
|
return sizeof(_GBRegisters) / sizeof(*_GBRegisters);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _GBCoreReadRegister(const struct mCore* core, const char* name, void* out) {
|
||||||
|
struct SM83Core* cpu = core->cpu;
|
||||||
|
uint16_t* value16 = out;
|
||||||
|
uint8_t* value8 = out;
|
||||||
|
|
||||||
|
if (strcmp(name, "b") == 0) {
|
||||||
|
*value8 = cpu->b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "c") == 0) {
|
||||||
|
*value8 = cpu->c;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "d") == 0) {
|
||||||
|
*value8 = cpu->d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "e") == 0) {
|
||||||
|
*value8 = cpu->e;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "a") == 0) {
|
||||||
|
*value8 = cpu->a;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "f") == 0) {
|
||||||
|
*value8 = cpu->f.packed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "h") == 0) {
|
||||||
|
*value8 = cpu->h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "l") == 0) {
|
||||||
|
*value8 = cpu->l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "bc") == 0) {
|
||||||
|
*value16 = cpu->bc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "de") == 0) {
|
||||||
|
*value16 = cpu->de;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "hl") == 0) {
|
||||||
|
*value16 = cpu->hl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "af") == 0) {
|
||||||
|
*value16 = cpu->af;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "pc") == 0) {
|
||||||
|
*value16 = cpu->pc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "sp") == 0) {
|
||||||
|
*value16 = cpu->sp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _GBCoreWriteRegister(struct mCore* core, const char* name, const void* in) {
|
||||||
|
struct SM83Core* cpu = core->cpu;
|
||||||
|
uint32_t value = *(uint32_t*) in;
|
||||||
|
|
||||||
|
if (strcmp(name, "b") == 0) {
|
||||||
|
cpu->b = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "c") == 0) {
|
||||||
|
cpu->c = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "d") == 0) {
|
||||||
|
cpu->d = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "e") == 0) {
|
||||||
|
cpu->e = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "h") == 0) {
|
||||||
|
cpu->h = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "l") == 0) {
|
||||||
|
cpu->l = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "a") == 0) {
|
||||||
|
cpu->a = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "f") == 0) {
|
||||||
|
cpu->f.packed = value & 0xF0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "bc") == 0) {
|
||||||
|
cpu->bc = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "de") == 0) {
|
||||||
|
cpu->de = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "hl") == 0) {
|
||||||
|
cpu->hl = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "af") == 0) {
|
||||||
|
cpu->af = value;
|
||||||
|
cpu->f.packed &= 0xF0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "pc") == 0) {
|
||||||
|
cpu->pc = value;
|
||||||
|
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "sp") == 0) {
|
||||||
|
cpu->sp = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
||||||
UNUSED(core);
|
UNUSED(core);
|
||||||
|
@ -1142,6 +1294,9 @@ struct mCore* GBCoreCreate(void) {
|
||||||
core->rawWrite32 = _GBCoreRawWrite32;
|
core->rawWrite32 = _GBCoreRawWrite32;
|
||||||
core->listMemoryBlocks = _GBListMemoryBlocks;
|
core->listMemoryBlocks = _GBListMemoryBlocks;
|
||||||
core->getMemoryBlock = _GBGetMemoryBlock;
|
core->getMemoryBlock = _GBGetMemoryBlock;
|
||||||
|
core->listRegisters = _GBCoreListRegisters;
|
||||||
|
core->readRegister = _GBCoreReadRegister;
|
||||||
|
core->writeRegister = _GBCoreWriteRegister;
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
|
core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
|
||||||
core->debuggerPlatform = _GBCoreDebuggerPlatform;
|
core->debuggerPlatform = _GBCoreDebuggerPlatform;
|
||||||
|
|
170
src/gba/core.c
170
src/gba/core.c
|
@ -8,6 +8,7 @@
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/log.h>
|
#include <mgba/core/log.h>
|
||||||
#include <mgba/internal/arm/debugger/debugger.h>
|
#include <mgba/internal/arm/debugger/debugger.h>
|
||||||
|
#include <mgba/internal/arm/isa-inlines.h>
|
||||||
#include <mgba/internal/debugger/symbols.h>
|
#include <mgba/internal/debugger/symbols.h>
|
||||||
#include <mgba/internal/gba/cheats.h>
|
#include <mgba/internal/gba/cheats.h>
|
||||||
#include <mgba/internal/gba/gba.h>
|
#include <mgba/internal/gba/gba.h>
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include <mgba-util/memory.h>
|
#include <mgba-util/memory.h>
|
||||||
#include <mgba-util/patch.h>
|
#include <mgba-util/patch.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#ifndef MINIMAL_CORE
|
#ifndef MINIMAL_CORE
|
||||||
#include <mgba/internal/gba/input.h>
|
#include <mgba/internal/gba/input.h>
|
||||||
|
@ -131,6 +133,32 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
|
||||||
{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
|
{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mCoreRegisterInfo _GBARegisters[] = {
|
||||||
|
{ "r0", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r1", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r2", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r3", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r4", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r5", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r6", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r7", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r8", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r9", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r10", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r11", NULL, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "r12", (const char*[]) { "ip", NULL }, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "sp", (const char*[]) { "r13", NULL }, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "lr", (const char*[]) { "r14", NULL }, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "pc", (const char*[]) { "r15", NULL }, 4, 0xFFFFFFFF, mCORE_REGISTER_GPR },
|
||||||
|
{ "cpsr", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr_irq", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr_fiq", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr_svc", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr_abt", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
{ "spsr_und", NULL, 4, 0xF00000FF, mCORE_REGISTER_FLAGS },
|
||||||
|
};
|
||||||
|
|
||||||
struct mVideoLogContext;
|
struct mVideoLogContext;
|
||||||
|
|
||||||
#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1
|
#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1
|
||||||
|
@ -861,7 +889,7 @@ static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment
|
||||||
GBAPatch32(cpu, address, value, NULL);
|
GBAPatch32(cpu, address, value, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _GBAListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
|
size_t _GBACoreListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
|
||||||
const struct GBA* gba = core->board;
|
const struct GBA* gba = core->board;
|
||||||
switch (gba->memory.savedata.type) {
|
switch (gba->memory.savedata.type) {
|
||||||
case SAVEDATA_SRAM:
|
case SAVEDATA_SRAM:
|
||||||
|
@ -882,7 +910,7 @@ size_t _GBAListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* _GBAGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
void* _GBACoreGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
||||||
struct GBA* gba = core->board;
|
struct GBA* gba = core->board;
|
||||||
switch (id) {
|
switch (id) {
|
||||||
default:
|
default:
|
||||||
|
@ -922,6 +950,137 @@ void* _GBAGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t _GBACoreListRegisters(const struct mCore* core, const struct mCoreRegisterInfo** list) {
|
||||||
|
UNUSED(core);
|
||||||
|
*list = _GBARegisters;
|
||||||
|
return sizeof(_GBARegisters) / sizeof(*_GBARegisters);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _GBACoreReadRegister(const struct mCore* core, const char* name, void* out) {
|
||||||
|
struct ARMCore* cpu = core->cpu;
|
||||||
|
int32_t* value = out;
|
||||||
|
switch (name[0]) {
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
++name;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'C':
|
||||||
|
if (strcmp(name, "cpsr") == 0 || strcmp(name, "CPSR") == 0) {
|
||||||
|
*value = cpu->cpsr.packed;
|
||||||
|
_ARMReadCPSR(cpu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 'i':
|
||||||
|
case 'I':
|
||||||
|
if (strcmp(name, "ip") == 0 || strcmp(name, "IP") == 0) {
|
||||||
|
*value = cpu->gprs[12];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
if (strcmp(name, "sp") == 0 || strcmp(name, "SP") == 0) {
|
||||||
|
*value = cpu->gprs[ARM_SP];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: SPSR
|
||||||
|
return false;
|
||||||
|
case 'l':
|
||||||
|
case 'L':
|
||||||
|
if (strcmp(name, "lr") == 0 || strcmp(name, "LR") == 0) {
|
||||||
|
*value = cpu->gprs[ARM_LR];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 'p':
|
||||||
|
case 'P':
|
||||||
|
if (strcmp(name, "pc") == 0 || strcmp(name, "PC") == 0) {
|
||||||
|
*value = cpu->gprs[ARM_PC];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* parseEnd;
|
||||||
|
errno = 0;
|
||||||
|
unsigned long regId = strtoul(name, &parseEnd, 10);
|
||||||
|
if (errno || regId > 15 || *parseEnd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*value = cpu->gprs[regId];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _GBACoreWriteRegister(struct mCore* core, const char* name, const void* in) {
|
||||||
|
struct ARMCore* cpu = core->cpu;
|
||||||
|
int32_t value = *(const int32_t*) in;
|
||||||
|
switch (name[0]) {
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
++name;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'C':
|
||||||
|
if (strcmp(name, "cpsr") == 0) {
|
||||||
|
cpu->cpsr.packed = value & 0xF00000FF;
|
||||||
|
_ARMReadCPSR(cpu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 'i':
|
||||||
|
case 'I':
|
||||||
|
if (strcmp(name, "ip") == 0 || strcmp(name, "IP") == 0) {
|
||||||
|
cpu->gprs[12] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
if (strcmp(name, "sp") == 0 || strcmp(name, "SP") == 0) {
|
||||||
|
cpu->gprs[ARM_SP] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: SPSR
|
||||||
|
return false;
|
||||||
|
case 'l':
|
||||||
|
case 'L':
|
||||||
|
if (strcmp(name, "lr") == 0 || strcmp(name, "LR") == 0) {
|
||||||
|
cpu->gprs[ARM_LR] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case 'p':
|
||||||
|
case 'P':
|
||||||
|
if (strcmp(name, "pc") == 0 || strcmp(name, "PC") == 0) {
|
||||||
|
name = "15";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* parseEnd;
|
||||||
|
errno = 0;
|
||||||
|
unsigned long regId = strtoul(name, &parseEnd, 10);
|
||||||
|
if (errno || regId > 15 || *parseEnd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cpu->gprs[regId] = value;
|
||||||
|
if (regId == ARM_PC) {
|
||||||
|
if (cpu->cpsr.t) {
|
||||||
|
ThumbWritePC(cpu);
|
||||||
|
} else {
|
||||||
|
ARMWritePC(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
||||||
UNUSED(core);
|
UNUSED(core);
|
||||||
|
@ -1257,8 +1416,11 @@ struct mCore* GBACoreCreate(void) {
|
||||||
core->rawWrite8 = _GBACoreRawWrite8;
|
core->rawWrite8 = _GBACoreRawWrite8;
|
||||||
core->rawWrite16 = _GBACoreRawWrite16;
|
core->rawWrite16 = _GBACoreRawWrite16;
|
||||||
core->rawWrite32 = _GBACoreRawWrite32;
|
core->rawWrite32 = _GBACoreRawWrite32;
|
||||||
core->listMemoryBlocks = _GBAListMemoryBlocks;
|
core->listMemoryBlocks = _GBACoreListMemoryBlocks;
|
||||||
core->getMemoryBlock = _GBAGetMemoryBlock;
|
core->getMemoryBlock = _GBACoreGetMemoryBlock;
|
||||||
|
core->listRegisters = _GBACoreListRegisters;
|
||||||
|
core->readRegister = _GBACoreReadRegister;
|
||||||
|
core->writeRegister = _GBACoreWriteRegister;
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
|
core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
|
||||||
core->debuggerPlatform = _GBACoreDebuggerPlatform;
|
core->debuggerPlatform = _GBACoreDebuggerPlatform;
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
#include <mgba/internal/gba/memory.h>
|
#include <mgba/internal/gba/memory.h>
|
||||||
|
|
||||||
const uint8_t hleBios[SIZE_BIOS] = {
|
const uint8_t hleBios[SIZE_BIOS] = {
|
||||||
0x06, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea,
|
0xd3, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea,
|
||||||
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
||||||
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x58, 0x01, 0x9f, 0xe5,
|
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x03, 0xa0, 0x03,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03,
|
||||||
0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xc0, 0x5e, 0xe5,
|
0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xc0, 0x5e, 0xe5,
|
||||||
0xd4, 0xb0, 0xa0, 0xe3, 0x0c, 0xc1, 0x9b, 0xe7, 0xd2, 0xbf, 0xa0, 0xe3,
|
0xd4, 0xb0, 0xa0, 0xe3, 0x0c, 0xc1, 0x9b, 0xe7, 0xd2, 0xbf, 0xa0, 0xe3,
|
||||||
|
@ -35,7 +35,7 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
||||||
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
||||||
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
|
||||||
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
0xc0, 0x00, 0x00, 0x02, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
|
0x00, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3,
|
||||||
0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
|
0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8,
|
||||||
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5,
|
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5,
|
||||||
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03,
|
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03,
|
||||||
|
@ -73,5 +73,10 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
||||||
0x01, 0xa0, 0xa0, 0xe1, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8,
|
0x01, 0xa0, 0xa0, 0xe1, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8,
|
||||||
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
|
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
|
||||||
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00,
|
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00,
|
||||||
0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1
|
0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1,
|
||||||
|
0xc2, 0x03, 0xa0, 0xe3, 0x03, 0x10, 0x50, 0xe4, 0x00, 0x00, 0x51, 0xe3,
|
||||||
|
0x00, 0x10, 0xa0, 0x13, 0x10, 0xff, 0x2f, 0x11, 0x1c, 0x00, 0x9f, 0xe5,
|
||||||
|
0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
|
||||||
|
0x10, 0xff, 0x2f, 0x11, 0xc0, 0x00, 0x40, 0xe2, 0x10, 0xff, 0x2f, 0xe1,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,16 +16,14 @@ nop
|
||||||
b irqBase
|
b irqBase
|
||||||
b fiqBase
|
b fiqBase
|
||||||
|
|
||||||
resetBase:
|
|
||||||
ldr r0, =0x20000C0
|
|
||||||
ldr r1, [r0]
|
|
||||||
cmp r1, #0
|
|
||||||
moveq r0, #0x8000000
|
|
||||||
bx r0
|
|
||||||
.word 0
|
|
||||||
.word 0xE129F000
|
|
||||||
|
|
||||||
.word 0 @ Padding for back-compat
|
.word 0 @ Padding for back-compat
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
.word 0
|
||||||
|
|
||||||
swiBase:
|
swiBase:
|
||||||
cmp sp, #0
|
cmp sp, #0
|
||||||
|
@ -113,6 +111,7 @@ swiTable:
|
||||||
.word SoundDriverGetJumpList @ 0x2A
|
.word SoundDriverGetJumpList @ 0x2A
|
||||||
|
|
||||||
.ltorg
|
.ltorg
|
||||||
|
.word 0 @ Padding for back-compat
|
||||||
|
|
||||||
irqBase:
|
irqBase:
|
||||||
stmfd sp!, {r0-r3, r12, lr}
|
stmfd sp!, {r0-r3, r12, lr}
|
||||||
|
@ -313,3 +312,19 @@ StallCall:
|
||||||
subs r11, #4
|
subs r11, #4
|
||||||
bhi StallCall
|
bhi StallCall
|
||||||
bx lr
|
bx lr
|
||||||
|
|
||||||
|
resetBase:
|
||||||
|
mov r0, #0x8000003
|
||||||
|
ldrb r1, [r0], #-3
|
||||||
|
cmp r1, #0
|
||||||
|
movne r1, #0
|
||||||
|
bxne r0
|
||||||
|
ldr r0, =0x20000C0
|
||||||
|
ldr r1, [r0]
|
||||||
|
cmp r1, #0
|
||||||
|
mov r1, #0
|
||||||
|
bxne r0
|
||||||
|
sub r0, #0xC0
|
||||||
|
bx r0
|
||||||
|
.word 0
|
||||||
|
.word 0xE129F000
|
||||||
|
|
|
@ -934,6 +934,11 @@ void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
|
||||||
bg->offsetY = 0;
|
bg->offsetY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glRenderer->winN[0].offsetX = 0;
|
||||||
|
glRenderer->winN[0].offsetY = 0;
|
||||||
|
glRenderer->winN[1].offsetX = 0;
|
||||||
|
glRenderer->winN[1].offsetY = 0;
|
||||||
|
|
||||||
for (i = 0; i < 512; ++i) {
|
for (i = 0; i < 512; ++i) {
|
||||||
int r = M_R5(glRenderer->d.palette[i]);
|
int r = M_R5(glRenderer->d.palette[i]);
|
||||||
int g = M_G5(glRenderer->d.palette[i]) << 1;
|
int g = M_G5(glRenderer->d.palette[i]) << 1;
|
||||||
|
|
|
@ -7,22 +7,57 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES)
|
||||||
set(${FEATURE_NAME} OFF PARENT_SCOPE)
|
set(${FEATURE_NAME} OFF PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
if(ARGV2)
|
||||||
|
set(VERSION "${ARGV2}")
|
||||||
|
set(FIND_VERSION "${VERSION}" EXACT)
|
||||||
|
set(PKG_CONFIG_VERSION_CHECK " >=${VERSION}")
|
||||||
|
else()
|
||||||
|
set(VERSION)
|
||||||
|
set(FIND_VERSION)
|
||||||
|
set(PKG_CONFIG_VERSION_CHECK)
|
||||||
|
endif()
|
||||||
foreach(NAMES ${FEATURE_REQUIRES})
|
foreach(NAMES ${FEATURE_REQUIRES})
|
||||||
string(REPLACE "|" ";" NAMELIST "${NAMES}")
|
string(REPLACE "|" ";" NAMELIST "${NAMES}")
|
||||||
set(FOUND OFF)
|
set(FOUND OFF)
|
||||||
foreach(REQUIRE ${NAMELIST})
|
foreach(REQUIRE ${NAMELIST})
|
||||||
if(NOT ${REQUIRE}_FOUND)
|
if(NOT ${REQUIRE}_FOUND)
|
||||||
find_package(${REQUIRE} QUIET)
|
find_package(${REQUIRE} ${FIND_VERSION} QUIET)
|
||||||
if(NOT ${REQUIRE}_FOUND)
|
if(NOT ${REQUIRE}_FOUND)
|
||||||
pkg_search_module(${REQUIRE} ${REQUIRE})
|
pkg_search_module(${REQUIRE} "${REQUIRE}${PKG_CONFIG_VERSION_CHECK}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(${REQUIRE}_FOUND)
|
if(${REQUIRE}_FOUND)
|
||||||
string(TOUPPER ${REQUIRE} UREQUIRE)
|
string(TOUPPER ${REQUIRE} UREQUIRE)
|
||||||
|
if(DEFINED ${REQUIRE}_CFLAGS_OTHER)
|
||||||
set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
|
set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_CFLAGS_OTHER)
|
||||||
|
set(${UREQUIRE}_CFLAGS_OTHER ${${UREQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ${REQUIRE}_FOUND)
|
||||||
set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE)
|
set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_FOUND)
|
||||||
|
set(${UREQUIRE}_FOUND ${${UREQUIRE}_FOUND} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ${REQUIRE}_INCLUDE_DIRS)
|
||||||
set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
|
set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_INCLUDE_DIRS)
|
||||||
|
set(${UREQUIRE}_INCLUDE_DIRS ${${UREQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ${REQUIRE}_VERSION_STRING)
|
||||||
set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE)
|
set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_VERSION_STRING)
|
||||||
|
set(${UREQUIRE}_VERSION_STRING ${${UREQUIRE}_VERSION_STRING} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ${REQUIRE}_VERSION_MAJOR)
|
||||||
|
set(${UREQUIRE}_VERSION_MAJOR ${${REQUIRE}_VERSION_MAJOR} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_VERSION_MAJOR)
|
||||||
|
set(${UREQUIRE}_VERSION_MAJOR ${${UREQUIRE}_VERSION_MAJOR} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ${REQUIRE}_VERSION_MINOR)
|
||||||
|
set(${UREQUIRE}_VERSION_MINOR ${${REQUIRE}_VERSION_MINOR} PARENT_SCOPE)
|
||||||
|
elseif(DEFINED ${UREQUIRE}_VERSION_MINOR)
|
||||||
|
set(${UREQUIRE}_VERSION_MINOR ${${UREQUIRE}_VERSION_MINOR} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(IS_FRAMEWORK OFF)
|
set(IS_FRAMEWORK OFF)
|
||||||
set(LIBS)
|
set(LIBS)
|
||||||
|
|
|
@ -107,6 +107,7 @@ set(SOURCE_FILES
|
||||||
LogController.cpp
|
LogController.cpp
|
||||||
LogConfigModel.cpp
|
LogConfigModel.cpp
|
||||||
LogView.cpp
|
LogView.cpp
|
||||||
|
LogWidget.cpp
|
||||||
MapView.cpp
|
MapView.cpp
|
||||||
MemoryDump.cpp
|
MemoryDump.cpp
|
||||||
MemoryModel.cpp
|
MemoryModel.cpp
|
||||||
|
|
|
@ -858,6 +858,15 @@ void CoreController::yankPak() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoreController::addKey(int key) {
|
||||||
|
m_activeKeys |= 1 << key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoreController::clearKey(int key) {
|
||||||
|
m_activeKeys &= ~(1 << key);
|
||||||
|
m_removedKeys |= 1 << key;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
void CoreController::screenshot() {
|
void CoreController::screenshot() {
|
||||||
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
|
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
|
||||||
|
@ -1133,7 +1142,10 @@ void CoreController::setFramebufferHandle(int fb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::updateKeys() {
|
void CoreController::updateKeys() {
|
||||||
int activeKeys = m_inputController->updateAutofire() | m_inputController->pollEvents();
|
int polledKeys = m_inputController->pollEvents() | m_inputController->updateAutofire();
|
||||||
|
int activeKeys = m_activeKeys | polledKeys;
|
||||||
|
activeKeys |= m_threadContext.core->getKeys(m_threadContext.core) & ~m_removedKeys;
|
||||||
|
m_removedKeys = polledKeys;
|
||||||
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
|
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -261,6 +261,7 @@ private:
|
||||||
QMutex m_bufferMutex;
|
QMutex m_bufferMutex;
|
||||||
|
|
||||||
int m_activeKeys = 0;
|
int m_activeKeys = 0;
|
||||||
|
int m_removedKeys = 0;
|
||||||
bool m_autofire[32] = {};
|
bool m_autofire[32] = {};
|
||||||
int m_autofireStatus[32] = {};
|
int m_autofireStatus[32] = {};
|
||||||
int m_autofireThreshold = 1;
|
int m_autofireThreshold = 1;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "DebuggerConsoleController.h"
|
#include "DebuggerConsoleController.h"
|
||||||
#include "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
|
|
||||||
#include <QScrollBar>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
|
@ -19,23 +19,16 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
m_ui.prompt->installEventFilter(this);
|
m_ui.prompt->installEventFilter(this);
|
||||||
m_ui.log->setFont(GBAApp::app()->monospaceFont());
|
|
||||||
m_ui.prompt->setFont(GBAApp::app()->monospaceFont());
|
m_ui.prompt->setFont(GBAApp::app()->monospaceFont());
|
||||||
|
|
||||||
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
|
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
|
||||||
connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log);
|
connect(controller, &DebuggerConsoleController::log, m_ui.log, &LogWidget::log);
|
||||||
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach);
|
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach);
|
||||||
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::breakInto);
|
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::breakInto);
|
||||||
|
|
||||||
controller->historyLoad();
|
controller->historyLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerConsole::log(const QString& line) {
|
|
||||||
m_ui.log->moveCursor(QTextCursor::End);
|
|
||||||
m_ui.log->insertPlainText(line);
|
|
||||||
m_ui.log->verticalScrollBar()->setValue(m_ui.log->verticalScrollBar()->maximum());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebuggerConsole::postLine() {
|
void DebuggerConsole::postLine() {
|
||||||
m_consoleController->attach();
|
m_consoleController->attach();
|
||||||
QString line = m_ui.prompt->text();
|
QString line = m_ui.prompt->text();
|
||||||
|
@ -44,7 +37,7 @@ void DebuggerConsole::postLine() {
|
||||||
m_consoleController->enterLine(QString("\n"));
|
m_consoleController->enterLine(QString("\n"));
|
||||||
} else {
|
} else {
|
||||||
m_historyOffset = 0;
|
m_historyOffset = 0;
|
||||||
log(QString("> %1\n").arg(line));
|
m_ui.log->log(QString("> %1\n").arg(line));
|
||||||
m_consoleController->enterLine(line);
|
m_consoleController->enterLine(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ public:
|
||||||
DebuggerConsole(DebuggerConsoleController* controller, QWidget* parent = nullptr);
|
DebuggerConsole(DebuggerConsoleController* controller, QWidget* parent = nullptr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void log(const QString&);
|
|
||||||
void postLine();
|
void postLine();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QPlainTextEdit" name="log">
|
<widget class="QGBA::LogWidget" name="log">
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -37,6 +37,13 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>QGBA::LogWidget</class>
|
||||||
|
<extends>QPlainTextEdit</extends>
|
||||||
|
<header>LogWidget.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/* Copyright (c) 2013-2022 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 "LogWidget.h"
|
||||||
|
|
||||||
|
#include "GBAApp.h"
|
||||||
|
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
LogWidget::LogWidget(QWidget* parent)
|
||||||
|
: QTextEdit(parent)
|
||||||
|
{
|
||||||
|
setFont(GBAApp::app()->monospaceFont());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWidget::log(const QString& line) {
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
insertPlainText(line);
|
||||||
|
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* Copyright (c) 2013-2022 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/. */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class LogWidget : public QTextEdit {
|
||||||
|
public:
|
||||||
|
LogWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void log(const QString&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -72,8 +72,6 @@ static void SM83DebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatch
|
||||||
static void SM83DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
static void SM83DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||||
static bool SM83DebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
static bool SM83DebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||||
static void SM83DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
static void SM83DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||||
static bool SM83DebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
|
||||||
static bool SM83DebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
|
|
||||||
|
|
||||||
struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
|
struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
|
||||||
struct SM83Debugger* platform = malloc(sizeof(struct SM83Debugger));
|
struct SM83Debugger* platform = malloc(sizeof(struct SM83Debugger));
|
||||||
|
@ -88,8 +86,6 @@ struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
|
||||||
platform->d.checkBreakpoints = SM83DebuggerCheckBreakpoints;
|
platform->d.checkBreakpoints = SM83DebuggerCheckBreakpoints;
|
||||||
platform->d.hasBreakpoints = SM83DebuggerHasBreakpoints;
|
platform->d.hasBreakpoints = SM83DebuggerHasBreakpoints;
|
||||||
platform->d.trace = SM83DebuggerTrace;
|
platform->d.trace = SM83DebuggerTrace;
|
||||||
platform->d.getRegister = SM83DebuggerGetRegister;
|
|
||||||
platform->d.setRegister = SM83DebuggerSetRegister;
|
|
||||||
platform->d.getStackTraceMode = NULL;
|
platform->d.getStackTraceMode = NULL;
|
||||||
platform->d.setStackTraceMode = NULL;
|
platform->d.setStackTraceMode = NULL;
|
||||||
platform->d.updateStackTrace = NULL;
|
platform->d.updateStackTrace = NULL;
|
||||||
|
@ -227,131 +223,3 @@ static void SM83DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* le
|
||||||
cpu->d, cpu->e, cpu->h, cpu->l,
|
cpu->d, cpu->e, cpu->h, cpu->l,
|
||||||
cpu->sp, cpu->memory.currentSegment(cpu, cpu->pc), cpu->pc, disassembly);
|
cpu->sp, cpu->memory.currentSegment(cpu, cpu->pc), cpu->pc, disassembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SM83DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
|
||||||
struct SM83Debugger* debugger = (struct SM83Debugger*) d;
|
|
||||||
struct SM83Core* cpu = debugger->cpu;
|
|
||||||
|
|
||||||
if (strcmp(name, "a") == 0) {
|
|
||||||
*value = cpu->a;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "b") == 0) {
|
|
||||||
*value = cpu->b;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "c") == 0) {
|
|
||||||
*value = cpu->c;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "d") == 0) {
|
|
||||||
*value = cpu->d;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "e") == 0) {
|
|
||||||
*value = cpu->e;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "h") == 0) {
|
|
||||||
*value = cpu->h;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "l") == 0) {
|
|
||||||
*value = cpu->l;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "bc") == 0) {
|
|
||||||
*value = cpu->bc;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "de") == 0) {
|
|
||||||
*value = cpu->de;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "hl") == 0) {
|
|
||||||
*value = cpu->hl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "af") == 0) {
|
|
||||||
*value = cpu->af;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "pc") == 0) {
|
|
||||||
*value = cpu->pc;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "sp") == 0) {
|
|
||||||
*value = cpu->sp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "f") == 0) {
|
|
||||||
*value = cpu->f.packed;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SM83DebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) {
|
|
||||||
struct SM83Debugger* debugger = (struct SM83Debugger*) d;
|
|
||||||
struct SM83Core* cpu = debugger->cpu;
|
|
||||||
|
|
||||||
if (strcmp(name, "a") == 0) {
|
|
||||||
cpu->a = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "b") == 0) {
|
|
||||||
cpu->b = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "c") == 0) {
|
|
||||||
cpu->c = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "d") == 0) {
|
|
||||||
cpu->d = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "e") == 0) {
|
|
||||||
cpu->e = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "h") == 0) {
|
|
||||||
cpu->h = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "l") == 0) {
|
|
||||||
cpu->l = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "bc") == 0) {
|
|
||||||
cpu->bc = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "de") == 0) {
|
|
||||||
cpu->de = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "hl") == 0) {
|
|
||||||
cpu->hl = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "af") == 0) {
|
|
||||||
cpu->af = value;
|
|
||||||
cpu->f.packed &= 0xF0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "pc") == 0) {
|
|
||||||
cpu->pc = value;
|
|
||||||
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "sp") == 0) {
|
|
||||||
cpu->sp = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (strcmp(name, "f") == 0) {
|
|
||||||
cpu->f.packed = value & 0xF0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ set(GUI_FILES
|
||||||
set(TEST_FILES
|
set(TEST_FILES
|
||||||
test/string-parser.c
|
test/string-parser.c
|
||||||
test/string-utf8.c
|
test/string-utf8.c
|
||||||
|
test/table.c
|
||||||
test/text-codec.c
|
test/text-codec.c
|
||||||
test/vfs.c)
|
test/vfs.c)
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
|
|
||||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||||
|
|
||||||
static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
|
static inline uint32_t rotl32(uint32_t x, int8_t r) {
|
||||||
return (x << r) | (x >> (32 - r));
|
return (x << r) | (x >> (32 - r));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ROTL32(x,y) rotl32(x,y)
|
#define ROTL32(x, y) rotl32(x, y)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
|
||||||
// Block read - if your platform needs to do endian-swapping or can only
|
// Block read - if your platform needs to do endian-swapping or can only
|
||||||
// handle aligned reads, do the conversion here
|
// handle aligned reads, do the conversion here
|
||||||
|
|
||||||
static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) {
|
static FORCE_INLINE uint32_t getblock32(const uint32_t* p, ssize_t i) {
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
LOAD_32LE(ret, i << 2, p);
|
LOAD_32LE(ret, i << 2, p);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -36,7 +36,7 @@ static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) {
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Finalization mix - force all bits of a hash block to avalanche
|
// Finalization mix - force all bits of a hash block to avalanche
|
||||||
|
|
||||||
static FORCE_INLINE uint32_t fmix32 (uint32_t h) {
|
static FORCE_INLINE uint32_t fmix32(uint32_t h) {
|
||||||
h ^= h >> 16;
|
h ^= h >> 16;
|
||||||
h *= 0x85ebca6b;
|
h *= 0x85ebca6b;
|
||||||
h ^= h >> 13;
|
h ^= h >> 13;
|
||||||
|
@ -48,8 +48,8 @@ static FORCE_INLINE uint32_t fmix32 (uint32_t h) {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
uint32_t hash32(const void* key, int len, uint32_t seed) {
|
uint32_t hash32(const void* key, size_t len, uint32_t seed) {
|
||||||
const uint8_t * data = (const uint8_t*)key;
|
const uint8_t* data = (const uint8_t*) key;
|
||||||
const int nblocks = len / 4;
|
const int nblocks = len / 4;
|
||||||
|
|
||||||
uint32_t h1 = seed;
|
uint32_t h1 = seed;
|
||||||
|
@ -60,31 +60,29 @@ uint32_t hash32(const void* key, int len, uint32_t seed) {
|
||||||
//----------
|
//----------
|
||||||
// body
|
// body
|
||||||
|
|
||||||
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
const uint32_t* blocks = (const uint32_t*)(data + nblocks * 4);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for(i = -nblocks; i; i++)
|
for (i = -nblocks; i; i++) {
|
||||||
{
|
uint32_t k1 = getblock32(blocks, i);
|
||||||
uint32_t k1 = getblock32(blocks,i);
|
|
||||||
|
|
||||||
k1 *= c1;
|
k1 *= c1;
|
||||||
k1 = ROTL32(k1,15);
|
k1 = ROTL32(k1, 15);
|
||||||
k1 *= c2;
|
k1 *= c2;
|
||||||
|
|
||||||
h1 ^= k1;
|
h1 ^= k1;
|
||||||
h1 = ROTL32(h1,13);
|
h1 = ROTL32(h1, 13);
|
||||||
h1 = h1*5+0xe6546b64;
|
h1 = h1 * 5 + 0xe6546b64;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------
|
//----------
|
||||||
// tail
|
// tail
|
||||||
|
|
||||||
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
const uint8_t* tail = (const uint8_t*)(data + nblocks * 4);
|
||||||
|
|
||||||
uint32_t k1 = 0;
|
uint32_t k1 = 0;
|
||||||
|
|
||||||
switch(len & 3)
|
switch(len & 3) {
|
||||||
{
|
|
||||||
case 3:
|
case 3:
|
||||||
k1 ^= tail[2] << 16;
|
k1 ^= tail[2] << 16;
|
||||||
// Fall through
|
// Fall through
|
||||||
|
@ -93,7 +91,10 @@ uint32_t hash32(const void* key, int len, uint32_t seed) {
|
||||||
// Fall through
|
// Fall through
|
||||||
case 1:
|
case 1:
|
||||||
k1 ^= tail[0];
|
k1 ^= tail[0];
|
||||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
k1 *= c1;
|
||||||
|
k1 = ROTL32(k1, 15);
|
||||||
|
k1 *= c2;
|
||||||
|
h1 ^= k1;
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------
|
//----------
|
||||||
|
|
289
src/util/table.c
289
src/util/table.c
|
@ -16,6 +16,7 @@
|
||||||
#define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key
|
#define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key
|
||||||
#define HASH_TABLE_STRNCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
#define HASH_TABLE_STRNCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
||||||
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && LIST->list[(INDEX)].keylen == keylen && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && LIST->list[(INDEX)].keylen == keylen && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
||||||
|
#define HASH_TABLE_CUSTOM_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && table->fn.equal(LIST->list[(INDEX)].stringKey, key)
|
||||||
|
|
||||||
#define TABLE_LOOKUP_START(COMPARATOR, LIST) \
|
#define TABLE_LOOKUP_START(COMPARATOR, LIST) \
|
||||||
size_t i; \
|
size_t i; \
|
||||||
|
@ -68,9 +69,13 @@ static struct TableList* _resizeAsNeeded(struct Table* table, struct TableList*
|
||||||
static void _removeItemFromList(struct Table* table, struct TableList* list, size_t item) {
|
static void _removeItemFromList(struct Table* table, struct TableList* list, size_t item) {
|
||||||
--list->nEntries;
|
--list->nEntries;
|
||||||
--table->size;
|
--table->size;
|
||||||
|
if (table->fn.deref) {
|
||||||
|
table->fn.deref(list->list[item].stringKey);
|
||||||
|
} else {
|
||||||
free(list->list[item].stringKey);
|
free(list->list[item].stringKey);
|
||||||
if (table->deinitializer) {
|
}
|
||||||
table->deinitializer(list->list[item].value);
|
if (table->fn.deinitializer) {
|
||||||
|
table->fn.deinitializer(list->list[item].value);
|
||||||
}
|
}
|
||||||
if (item != list->nEntries) {
|
if (item != list->nEntries) {
|
||||||
list->list[item] = list->list[list->nEntries];
|
list->list[item] = list->list[list->nEntries];
|
||||||
|
@ -80,13 +85,19 @@ static void _removeItemFromList(struct Table* table, struct TableList* list, siz
|
||||||
static void _rebalance(struct Table* table) {
|
static void _rebalance(struct Table* table) {
|
||||||
struct Table newTable;
|
struct Table newTable;
|
||||||
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
|
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
|
||||||
|
memcpy(&newTable.fn, &table->fn, sizeof(newTable.fn));
|
||||||
newTable.seed = table->seed * 134775813 + 1;
|
newTable.seed = table->seed * 134775813 + 1;
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < table->tableSize; ++i) {
|
for (i = 0; i < table->tableSize; ++i) {
|
||||||
const struct TableList* list = &table->table[i];
|
struct TableList* list = &table->table[i];
|
||||||
size_t j;
|
size_t j;
|
||||||
for (j = 0; j < list->nEntries; ++j) {
|
for (j = 0; j < list->nEntries; ++j) {
|
||||||
|
if (!table->fn.equal) {
|
||||||
HashTableInsertBinaryMoveKey(&newTable, list->list[j].stringKey, list->list[j].keylen, list->list[j].value);
|
HashTableInsertBinaryMoveKey(&newTable, list->list[j].stringKey, list->list[j].keylen, list->list[j].value);
|
||||||
|
} else {
|
||||||
|
HashTableInsertCustom(&newTable, list->list[j].stringKey, list->list[j].value);
|
||||||
|
table->fn.deref(list->list[j].stringKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(list->list);
|
free(list->list);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +116,9 @@ void TableInit(struct Table* table, size_t initialSize, void (*deinitializer)(vo
|
||||||
table->tableSize = initialSize;
|
table->tableSize = initialSize;
|
||||||
table->table = calloc(table->tableSize, sizeof(struct TableList));
|
table->table = calloc(table->tableSize, sizeof(struct TableList));
|
||||||
table->size = 0;
|
table->size = 0;
|
||||||
table->deinitializer = deinitializer;
|
table->fn = (struct TableFunctions) {
|
||||||
|
.deinitializer = deinitializer
|
||||||
|
};
|
||||||
table->seed = 0;
|
table->seed = 0;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -122,9 +135,13 @@ void TableDeinit(struct Table* table) {
|
||||||
struct TableList* list = &table->table[i];
|
struct TableList* list = &table->table[i];
|
||||||
size_t j;
|
size_t j;
|
||||||
for (j = 0; j < list->nEntries; ++j) {
|
for (j = 0; j < list->nEntries; ++j) {
|
||||||
|
if (table->fn.deref) {
|
||||||
|
table->fn.deref(list->list[j].stringKey);
|
||||||
|
} else {
|
||||||
free(list->list[j].stringKey);
|
free(list->list[j].stringKey);
|
||||||
if (table->deinitializer) {
|
}
|
||||||
table->deinitializer(list->list[j].value);
|
if (table->fn.deinitializer) {
|
||||||
|
table->fn.deinitializer(list->list[j].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(list->list);
|
free(list->list);
|
||||||
|
@ -146,8 +163,8 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
|
||||||
struct TableList* list = _getList(table, key);
|
struct TableList* list = _getList(table, key);
|
||||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||||
if (value != lookupResult->value) {
|
if (value != lookupResult->value) {
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
table->deinitializer(lookupResult->value);
|
table->fn.deinitializer(lookupResult->value);
|
||||||
}
|
}
|
||||||
lookupResult->value = value;
|
lookupResult->value = value;
|
||||||
}
|
}
|
||||||
|
@ -172,10 +189,10 @@ void TableClear(struct Table* table) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < table->tableSize; ++i) {
|
for (i = 0; i < table->tableSize; ++i) {
|
||||||
struct TableList* list = &table->table[i];
|
struct TableList* list = &table->table[i];
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for (j = 0; j < list->nEntries; ++j) {
|
for (j = 0; j < list->nEntries; ++j) {
|
||||||
table->deinitializer(list->list[j].value);
|
table->fn.deinitializer(list->list[j].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(list->list);
|
free(list->list);
|
||||||
|
@ -200,17 +217,73 @@ size_t TableSize(const struct Table* table) {
|
||||||
return table->size;
|
return table->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TableIteratorStart(const struct Table* table, struct TableIterator* iter) {
|
||||||
|
iter->entry = 0;
|
||||||
|
for (iter->bucket = 0; iter->bucket < table->tableSize; ++iter->bucket) {
|
||||||
|
if (table->table[iter->bucket].nEntries) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iter->bucket < table->tableSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TableIteratorNext(const struct Table* table, struct TableIterator* iter) {
|
||||||
|
if (iter->entry + 1 < table->table[iter->bucket].nEntries) {
|
||||||
|
++iter->entry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (iter->bucket + 1 < table->tableSize) {
|
||||||
|
iter->entry = 0;
|
||||||
|
for (++iter->bucket; iter->bucket < table->tableSize; ++iter->bucket) {
|
||||||
|
if (table->table[iter->bucket].nEntries) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iter->bucket < table->tableSize;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TableIteratorGetKey(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return table->table[iter->bucket].list[iter->entry].key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* TableIteratorGetValue(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return table->table[iter->bucket].list[iter->entry].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TableIteratorLookup(const struct Table* table, struct TableIterator* iter, uint32_t key) {
|
||||||
|
uint32_t bucket = key & (table->tableSize - 1);
|
||||||
|
const struct TableList* list = &table->table[bucket];
|
||||||
|
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||||
|
iter->bucket = bucket;
|
||||||
|
iter->entry = i;
|
||||||
|
return true;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
|
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
|
||||||
TableInit(table, initialSize, deinitializer);
|
TableInit(table, initialSize, deinitializer);
|
||||||
table->seed = 1;
|
table->seed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HashTableInitCustom(struct Table* table, size_t initialSize, const struct TableFunctions* funcs) {
|
||||||
|
HashTableInit(table, initialSize, NULL);
|
||||||
|
table->fn = *funcs;
|
||||||
|
}
|
||||||
|
|
||||||
void HashTableDeinit(struct Table* table) {
|
void HashTableDeinit(struct Table* table) {
|
||||||
TableDeinit(table);
|
TableDeinit(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* HashTableLookup(const struct Table* table, const char* key) {
|
void* HashTableLookup(const struct Table* table, const char* key) {
|
||||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, strlen(key), table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, strlen(key), table->seed);
|
||||||
|
}
|
||||||
const struct TableList* list = _getConstList(table, hash);
|
const struct TableList* list = _getConstList(table, hash);
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||||
return lookupResult->value;
|
return lookupResult->value;
|
||||||
|
@ -219,7 +292,12 @@ void* HashTableLookup(const struct Table* table, const char* key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void* HashTableLookupBinary(const struct Table* table, const void* key, size_t keylen) {
|
void* HashTableLookupBinary(const struct Table* table, const void* key, size_t keylen) {
|
||||||
uint32_t hash = hash32(key, keylen, table->seed);
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
const struct TableList* list = _getConstList(table, hash);
|
const struct TableList* list = _getConstList(table, hash);
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||||
return lookupResult->value;
|
return lookupResult->value;
|
||||||
|
@ -227,18 +305,36 @@ void* HashTableLookupBinary(const struct Table* table, const void* key, size_t k
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* HashTableLookupCustom(const struct Table* table, void* key) {
|
||||||
|
uint32_t hash = table->fn.hash(key, 0, table->seed);
|
||||||
|
const struct TableList* list = _getConstList(table, hash);
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_CUSTOM_COMPARATOR, list) {
|
||||||
|
return lookupResult->value;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void HashTableInsert(struct Table* table, const char* key, void* value) {
|
void HashTableInsert(struct Table* table, const char* key, void* value) {
|
||||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, strlen(key), table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, strlen(key), table->seed);
|
||||||
|
}
|
||||||
struct TableList* list = _getList(table, hash);
|
struct TableList* list = _getList(table, hash);
|
||||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||||
_rebalance(table);
|
_rebalance(table);
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, strlen(key), table->seed);
|
||||||
|
} else {
|
||||||
hash = hash32(key, strlen(key), table->seed);
|
hash = hash32(key, strlen(key), table->seed);
|
||||||
|
}
|
||||||
list = _getList(table, hash);
|
list = _getList(table, hash);
|
||||||
}
|
}
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||||
if (value != lookupResult->value) {
|
if (value != lookupResult->value) {
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
table->deinitializer(lookupResult->value);
|
table->fn.deinitializer(lookupResult->value);
|
||||||
}
|
}
|
||||||
lookupResult->value = value;
|
lookupResult->value = value;
|
||||||
}
|
}
|
||||||
|
@ -254,17 +350,26 @@ void HashTableInsert(struct Table* table, const char* key, void* value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen, void* value) {
|
void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen, void* value) {
|
||||||
uint32_t hash = hash32(key, keylen, table->seed);
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
struct TableList* list = _getList(table, hash);
|
struct TableList* list = _getList(table, hash);
|
||||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||||
_rebalance(table);
|
_rebalance(table);
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
hash = hash32(key, keylen, table->seed);
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
list = _getList(table, hash);
|
list = _getList(table, hash);
|
||||||
}
|
}
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||||
if (value != lookupResult->value) {
|
if (value != lookupResult->value) {
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
table->deinitializer(lookupResult->value);
|
table->fn.deinitializer(lookupResult->value);
|
||||||
}
|
}
|
||||||
lookupResult->value = value;
|
lookupResult->value = value;
|
||||||
}
|
}
|
||||||
|
@ -280,18 +385,53 @@ void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen,
|
||||||
++table->size;
|
++table->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value) {
|
void HashTableInsertCustom(struct Table* table, void* key, void* value) {
|
||||||
uint32_t hash = hash32(key, keylen, table->seed);
|
uint32_t hash = table->fn.hash(key, 0, table->seed);
|
||||||
struct TableList* list = _getList(table, hash);
|
struct TableList* list = _getList(table, hash);
|
||||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||||
_rebalance(table);
|
_rebalance(table);
|
||||||
|
hash = table->fn.hash(key, 0, table->seed);
|
||||||
|
list = _getList(table, hash);
|
||||||
|
}
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_CUSTOM_COMPARATOR, list) {
|
||||||
|
if (value != lookupResult->value) {
|
||||||
|
if (table->fn.deinitializer) {
|
||||||
|
table->fn.deinitializer(lookupResult->value);
|
||||||
|
}
|
||||||
|
lookupResult->value = value;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
list = _resizeAsNeeded(table, list, hash);
|
||||||
|
list->list[list->nEntries].key = hash;
|
||||||
|
list->list[list->nEntries].stringKey = table->fn.ref(key);
|
||||||
|
list->list[list->nEntries].keylen = 0;
|
||||||
|
list->list[list->nEntries].value = value;
|
||||||
|
++list->nEntries;
|
||||||
|
++table->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value) {
|
||||||
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
hash = hash32(key, keylen, table->seed);
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
|
struct TableList* list = _getList(table, hash);
|
||||||
|
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||||
|
_rebalance(table);
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
list = _getList(table, hash);
|
list = _getList(table, hash);
|
||||||
}
|
}
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||||
if (value != lookupResult->value) {
|
if (value != lookupResult->value) {
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
table->deinitializer(lookupResult->value);
|
table->fn.deinitializer(lookupResult->value);
|
||||||
}
|
}
|
||||||
lookupResult->value = value;
|
lookupResult->value = value;
|
||||||
}
|
}
|
||||||
|
@ -307,7 +447,13 @@ void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen,
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTableRemove(struct Table* table, const char* key) {
|
void HashTableRemove(struct Table* table, const char* key) {
|
||||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
uint32_t hash;
|
||||||
|
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, strlen(key), table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, strlen(key), table->seed);
|
||||||
|
}
|
||||||
struct TableList* list = _getList(table, hash);
|
struct TableList* list = _getList(table, hash);
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||||
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||||
|
@ -315,24 +461,41 @@ void HashTableRemove(struct Table* table, const char* key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashTableRemoveBinary(struct Table* table, const void* key, size_t keylen) {
|
void HashTableRemoveBinary(struct Table* table, const void* key, size_t keylen) {
|
||||||
uint32_t hash = hash32(key, keylen, table->seed);
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
struct TableList* list = _getList(table, hash);
|
struct TableList* list = _getList(table, hash);
|
||||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||||
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||||
} TABLE_LOOKUP_END;
|
} TABLE_LOOKUP_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HashTableRemoveCustom(struct Table* table, void* key) {
|
||||||
|
uint32_t hash = table->fn.hash(key, 0, table->seed);
|
||||||
|
struct TableList* list = _getList(table, hash);
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_CUSTOM_COMPARATOR, list) {
|
||||||
|
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
}
|
||||||
|
|
||||||
void HashTableClear(struct Table* table) {
|
void HashTableClear(struct Table* table) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < table->tableSize; ++i) {
|
for (i = 0; i < table->tableSize; ++i) {
|
||||||
struct TableList* list = &table->table[i];
|
struct TableList* list = &table->table[i];
|
||||||
size_t j;
|
size_t j;
|
||||||
for (j = 0; j < list->nEntries; ++j) {
|
for (j = 0; j < list->nEntries; ++j) {
|
||||||
if (table->deinitializer) {
|
if (table->fn.deinitializer) {
|
||||||
table->deinitializer(list->list[j].value);
|
table->fn.deinitializer(list->list[j].value);
|
||||||
}
|
}
|
||||||
|
if (table->fn.deref) {
|
||||||
|
table->fn.deref(list->list[j].stringKey);
|
||||||
|
} else {
|
||||||
free(list->list[j].stringKey);
|
free(list->list[j].stringKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
free(list->list);
|
free(list->list);
|
||||||
list->listSize = LIST_INITIAL_SIZE;
|
list->listSize = LIST_INITIAL_SIZE;
|
||||||
list->nEntries = 0;
|
list->nEntries = 0;
|
||||||
|
@ -414,3 +577,77 @@ const char* HashTableSearchString(const struct Table* table, const char* value)
|
||||||
size_t HashTableSize(const struct Table* table) {
|
size_t HashTableSize(const struct Table* table) {
|
||||||
return table->size;
|
return table->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HashTableIteratorStart(const struct Table* table, struct TableIterator* iter) {
|
||||||
|
return TableIteratorStart(table, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HashTableIteratorNext(const struct Table* table, struct TableIterator* iter) {
|
||||||
|
return TableIteratorNext(table, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* HashTableIteratorGetKey(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return table->table[iter->bucket].list[iter->entry].stringKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* HashTableIteratorGetBinaryKey(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return table->table[iter->bucket].list[iter->entry].stringKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t HashTableIteratorGetBinaryKeyLen(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return table->table[iter->bucket].list[iter->entry].keylen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* HashTableIteratorGetCustomKey(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return (char*) table->table[iter->bucket].list[iter->entry].stringKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* HashTableIteratorGetValue(const struct Table* table, const struct TableIterator* iter) {
|
||||||
|
return TableIteratorGetValue(table, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HashTableIteratorLookup(const struct Table* table, struct TableIterator* iter, const char* key) {
|
||||||
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, strlen(key), table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, strlen(key), table->seed);
|
||||||
|
}
|
||||||
|
uint32_t bucket = hash & (table->tableSize - 1);
|
||||||
|
const struct TableList* list = &table->table[bucket];
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||||
|
iter->bucket = bucket;
|
||||||
|
iter->entry = i;
|
||||||
|
return true;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HashTableIteratorLookupBinary(const struct Table* table, struct TableIterator* iter, const void* key, size_t keylen) {
|
||||||
|
uint32_t hash;
|
||||||
|
if (table->fn.hash) {
|
||||||
|
hash = table->fn.hash(key, keylen, table->seed);
|
||||||
|
} else {
|
||||||
|
hash = hash32(key, keylen, table->seed);
|
||||||
|
}
|
||||||
|
uint32_t bucket = hash & (table->tableSize - 1);
|
||||||
|
const struct TableList* list = &table->table[bucket];
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||||
|
iter->bucket = bucket;
|
||||||
|
iter->entry = i;
|
||||||
|
return true;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HashTableIteratorLookupCustom(const struct Table* table, struct TableIterator* iter, void* key) {
|
||||||
|
uint32_t hash = table->fn.hash(key, 0, table->seed);
|
||||||
|
uint32_t bucket = hash & (table->tableSize - 1);
|
||||||
|
const struct TableList* list = &table->table[bucket];
|
||||||
|
TABLE_LOOKUP_START(HASH_TABLE_CUSTOM_COMPARATOR, list) {
|
||||||
|
iter->bucket = bucket;
|
||||||
|
iter->entry = i;
|
||||||
|
return true;
|
||||||
|
} TABLE_LOOKUP_END;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* Copyright (c) 2013-2022 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 "util/test/suite.h"
|
||||||
|
|
||||||
|
#include <mgba-util/table.h>
|
||||||
|
|
||||||
|
M_TEST_DEFINE(basic) {
|
||||||
|
struct Table table;
|
||||||
|
TableInit(&table, 0, NULL);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 5000; ++i) {
|
||||||
|
TableInsert(&table, i, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 5000; ++i) {
|
||||||
|
assert_int_equal(i, (size_t) TableLookup(&table, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
TableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(iterator) {
|
||||||
|
struct Table table;
|
||||||
|
struct TableIterator iter;
|
||||||
|
|
||||||
|
TableInit(&table, 0, NULL);
|
||||||
|
assert_false(TableIteratorStart(&table, &iter));
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 32; ++i) {
|
||||||
|
TableInsert(&table, i, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true(TableIteratorStart(&table, &iter));
|
||||||
|
uint32_t mask = 0;
|
||||||
|
while (true) {
|
||||||
|
assert_int_equal(TableIteratorGetKey(&table, &iter), (uintptr_t) TableIteratorGetValue(&table, &iter));
|
||||||
|
mask ^= 1 << TableIteratorGetKey(&table, &iter);
|
||||||
|
if (!TableIteratorNext(&table, &iter)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_int_equal(mask, 0xFFFFFFFFU);
|
||||||
|
|
||||||
|
TableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(iteratorLookup) {
|
||||||
|
struct Table table;
|
||||||
|
struct TableIterator iter;
|
||||||
|
|
||||||
|
TableInit(&table, 0, NULL);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 500; ++i) {
|
||||||
|
TableInsert(&table, (i * 0x5DEECE66D) >> 16, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 500; ++i) {
|
||||||
|
assert_true(TableIteratorLookup(&table, &iter, (i * 0x5DEECE66D) >> 16));
|
||||||
|
assert_int_equal(TableIteratorGetKey(&table, &iter), (i * 0x5DEECE66D) >> 16);
|
||||||
|
assert_int_equal((uintptr_t) TableIteratorGetValue(&table, &iter), i);
|
||||||
|
}
|
||||||
|
for (i = 1000; i < 1200; ++i) {
|
||||||
|
assert_false(TableIteratorLookup(&table, &iter, (i * 0x5DEECE66D) >> 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
TableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(hash) {
|
||||||
|
struct Table table;
|
||||||
|
HashTableInit(&table, 0, NULL);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 5000; ++i) {
|
||||||
|
char buffer[16];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%"PRIz"i", i);
|
||||||
|
HashTableInsert(&table, buffer, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 5000; ++i) {
|
||||||
|
char buffer[16];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%"PRIz"i", i);
|
||||||
|
assert_int_equal(i, (size_t) HashTableLookup(&table, buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(hashIterator) {
|
||||||
|
struct Table table;
|
||||||
|
struct TableIterator iter;
|
||||||
|
char buf[18];
|
||||||
|
|
||||||
|
HashTableInit(&table, 0, NULL);
|
||||||
|
assert_false(HashTableIteratorStart(&table, &iter));
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 32; ++i) {
|
||||||
|
snprintf(buf, sizeof(buf), "%zu", i);
|
||||||
|
HashTableInsert(&table, buf, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true(TableIteratorStart(&table, &iter));
|
||||||
|
uint32_t mask = 0;
|
||||||
|
while (true) {
|
||||||
|
assert_int_equal(atoi(HashTableIteratorGetKey(&table, &iter)), (uintptr_t) HashTableIteratorGetValue(&table, &iter));
|
||||||
|
mask ^= 1 << atoi(HashTableIteratorGetKey(&table, &iter));
|
||||||
|
if (!HashTableIteratorNext(&table, &iter)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_int_equal(mask, 0xFFFFFFFFU);
|
||||||
|
|
||||||
|
HashTableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
M_TEST_DEFINE(hashIteratorLookup) {
|
||||||
|
struct Table table;
|
||||||
|
struct TableIterator iter;
|
||||||
|
char buf[18];
|
||||||
|
|
||||||
|
HashTableInit(&table, 0, NULL);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < 500; ++i) {
|
||||||
|
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
|
||||||
|
HashTableInsert(&table, buf, (void*) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 500; ++i) {
|
||||||
|
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
|
||||||
|
assert_true(HashTableIteratorLookup(&table, &iter, buf));
|
||||||
|
assert_string_equal(HashTableIteratorGetKey(&table, &iter), buf);
|
||||||
|
assert_int_equal((uintptr_t) HashTableIteratorGetValue(&table, &iter), i);
|
||||||
|
}
|
||||||
|
for (i = 1000; i < 1200; ++i) {
|
||||||
|
snprintf(buf, sizeof(buf), "%zu", (i * 0x5DEECE66D) >> 4);
|
||||||
|
assert_false(HashTableIteratorLookup(&table, &iter, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableDeinit(&table);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_SUITE_DEFINE(Table,
|
||||||
|
cmocka_unit_test(basic),
|
||||||
|
cmocka_unit_test(iterator),
|
||||||
|
cmocka_unit_test(iteratorLookup),
|
||||||
|
cmocka_unit_test(hash),
|
||||||
|
cmocka_unit_test(hashIterator),
|
||||||
|
cmocka_unit_test(hashIteratorLookup),
|
||||||
|
)
|
Loading…
Reference in New Issue