Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2022-05-30 17:42:31 -07:00
commit 64c3c82c2a
30 changed files with 1021 additions and 346 deletions

View File

@ -65,6 +65,7 @@ Emulation fixes:
- GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339)
- GBA: Improve timing when not booting from BIOS
- 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: 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)

View File

@ -58,6 +58,7 @@ The following mappers are fully supported:
- Pokémon Jade/Diamond (unlicensed)
- BBD (unlicensed MBC5-like)
- Hitek (unlicensed MBC5-like)
- Sachen MMC1
The following mappers are partially supported:
@ -67,6 +68,7 @@ The following mappers are partially supported:
- TAMA5 (missing RTC support)
- HuC-1 (missing IR support)
- HuC-3 (missing IR support)
- Sachen MMC2 (missing alternate wiring support)
### Planned features

View File

@ -10,7 +10,7 @@
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

View File

@ -11,13 +11,27 @@
CXX_GUARD_START
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 TableList* table;
size_t tableSize;
size_t size;
void (*deinitializer)(void*);
uint32_t seed;
struct TableFunctions fn;
};
struct TableIterator {
size_t bucket;
size_t entry;
};
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);
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 HashTableInitCustom(struct Table* table, size_t initialSize, const struct TableFunctions* funcs);
void HashTableDeinit(struct Table* table);
void* HashTableLookup(const struct Table*, const char* key);
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 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 HashTableRemoveBinary(struct Table*, const void* key, size_t keylen);
void HashTableRemoveCustom(struct Table*, void* key);
void HashTableClear(struct Table*);
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 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* HashTableSearchPointer(const struct Table* table, const void* value);
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
const char* HashTableSearchString(const struct Table* table, const char* value);
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
#endif

View File

@ -140,6 +140,10 @@ struct mCore {
size_t (*listMemoryBlocks)(const struct mCore*, const struct mCoreMemoryBlock**);
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
bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType);
struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*);

View File

@ -287,6 +287,21 @@ struct mCoreMemoryBlock {
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
#endif

View File

@ -118,8 +118,6 @@ struct mDebuggerPlatform {
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);
uint32_t (*getStackTraceMode)(struct mDebuggerPlatform*);

View File

@ -241,8 +241,6 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
static void ARMDebuggerTrace(struct mDebuggerPlatform*, 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 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 void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
@ -260,8 +258,6 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
platform->trace = ARMDebuggerTrace;
platform->getRegister = ARMDebuggerGetRegister;
platform->setRegister = ARMDebuggerSetRegister;
platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
@ -510,82 +506,6 @@ static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out
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) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
return debugger->stackTraceMode;

View File

@ -306,7 +306,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
SHIFTER(cpu, opcode); \
int rd = (opcode >> 12) & 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)) { \
n += WORD_SIZE_ARM; \
} \
@ -421,7 +421,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
uint32_t address; \
int rn = (opcode >> 16) & 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)) { \
d += WORD_SIZE_ARM; \
} \

View File

@ -452,7 +452,7 @@ static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector*
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
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);
}
}

View File

@ -156,7 +156,7 @@ bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int
if (debugger->core->lookupIdentifier(debugger->core, name, value, segment)) {
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 false;

View File

@ -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 },
};
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 GBCore {
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
static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
UNUSED(core);
@ -1142,6 +1294,9 @@ struct mCore* GBCoreCreate(void) {
core->rawWrite32 = _GBCoreRawWrite32;
core->listMemoryBlocks = _GBListMemoryBlocks;
core->getMemoryBlock = _GBGetMemoryBlock;
core->listRegisters = _GBCoreListRegisters;
core->readRegister = _GBCoreReadRegister;
core->writeRegister = _GBCoreWriteRegister;
#ifdef USE_DEBUGGERS
core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
core->debuggerPlatform = _GBCoreDebuggerPlatform;

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h>
#include <mgba/core/log.h>
#include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/arm/isa-inlines.h>
#include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h>
@ -32,6 +33,7 @@
#include <mgba-util/memory.h>
#include <mgba-util/patch.h>
#include <mgba-util/vfs.h>
#include <errno.h>
#ifndef MINIMAL_CORE
#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 },
};
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;
#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);
}
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;
switch (gba->memory.savedata.type) {
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;
switch (id) {
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
static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
UNUSED(core);
@ -1257,8 +1416,11 @@ struct mCore* GBACoreCreate(void) {
core->rawWrite8 = _GBACoreRawWrite8;
core->rawWrite16 = _GBACoreRawWrite16;
core->rawWrite32 = _GBACoreRawWrite32;
core->listMemoryBlocks = _GBAListMemoryBlocks;
core->getMemoryBlock = _GBAGetMemoryBlock;
core->listMemoryBlocks = _GBACoreListMemoryBlocks;
core->getMemoryBlock = _GBACoreGetMemoryBlock;
core->listRegisters = _GBACoreListRegisters;
core->readRegister = _GBACoreReadRegister;
core->writeRegister = _GBACoreWriteRegister;
#ifdef USE_DEBUGGERS
core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
core->debuggerPlatform = _GBACoreDebuggerPlatform;

View File

@ -3,11 +3,11 @@
#include <mgba/internal/gba/memory.h>
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,
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x58, 0x01, 0x9f, 0xe5,
0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x03, 0xa0, 0x03,
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03,
0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xc0, 0x5e, 0xe5,
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, 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,
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5,
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,
0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3,
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
};

View File

@ -16,16 +16,14 @@ nop
b irqBase
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
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
swiBase:
cmp sp, #0
@ -113,6 +111,7 @@ swiTable:
.word SoundDriverGetJumpList @ 0x2A
.ltorg
.word 0 @ Padding for back-compat
irqBase:
stmfd sp!, {r0-r3, r12, lr}
@ -313,3 +312,19 @@ StallCall:
subs r11, #4
bhi StallCall
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

View File

@ -934,6 +934,11 @@ void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
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) {
int r = M_R5(glRenderer->d.palette[i]);
int g = M_G5(glRenderer->d.palette[i]) << 1;

View File

@ -7,22 +7,57 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES)
set(${FEATURE_NAME} OFF PARENT_SCOPE)
return()
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})
string(REPLACE "|" ";" NAMELIST "${NAMES}")
set(FOUND OFF)
foreach(REQUIRE ${NAMELIST})
if(NOT ${REQUIRE}_FOUND)
find_package(${REQUIRE} QUIET)
find_package(${REQUIRE} ${FIND_VERSION} QUIET)
if(NOT ${REQUIRE}_FOUND)
pkg_search_module(${REQUIRE} ${REQUIRE})
pkg_search_module(${REQUIRE} "${REQUIRE}${PKG_CONFIG_VERSION_CHECK}")
endif()
endif()
if(${REQUIRE}_FOUND)
string(TOUPPER ${REQUIRE} UREQUIRE)
set(${UREQUIRE}_CFLAGS_OTHER ${${REQUIRE}_CFLAGS_OTHER} PARENT_SCOPE)
set(${UREQUIRE}_FOUND ${${REQUIRE}_FOUND} PARENT_SCOPE)
set(${UREQUIRE}_INCLUDE_DIRS ${${REQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE)
if(DEFINED ${REQUIRE}_CFLAGS_OTHER)
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)
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)
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)
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)
set(IS_FRAMEWORK OFF)
set(LIBS)

View File

@ -107,6 +107,7 @@ set(SOURCE_FILES
LogController.cpp
LogConfigModel.cpp
LogView.cpp
LogWidget.cpp
MapView.cpp
MemoryDump.cpp
MemoryModel.cpp

View File

@ -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
void CoreController::screenshot() {
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
@ -1133,7 +1142,10 @@ void CoreController::setFramebufferHandle(int fb) {
}
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);
}

View File

@ -261,6 +261,7 @@ private:
QMutex m_bufferMutex;
int m_activeKeys = 0;
int m_removedKeys = 0;
bool m_autofire[32] = {};
int m_autofireStatus[32] = {};
int m_autofireThreshold = 1;

View File

@ -8,7 +8,7 @@
#include "DebuggerConsoleController.h"
#include "GBAApp.h"
#include <QScrollBar>
#include <QKeyEvent>
using namespace QGBA;
@ -19,23 +19,16 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
m_ui.setupUi(this);
m_ui.prompt->installEventFilter(this);
m_ui.log->setFont(GBAApp::app()->monospaceFont());
m_ui.prompt->setFont(GBAApp::app()->monospaceFont());
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::breakInto);
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() {
m_consoleController->attach();
QString line = m_ui.prompt->text();
@ -44,7 +37,7 @@ void DebuggerConsole::postLine() {
m_consoleController->enterLine(QString("\n"));
} else {
m_historyOffset = 0;
log(QString("> %1\n").arg(line));
m_ui.log->log(QString("> %1\n").arg(line));
m_consoleController->enterLine(line);
}
}

View File

@ -18,7 +18,6 @@ public:
DebuggerConsole(DebuggerConsoleController* controller, QWidget* parent = nullptr);
private slots:
void log(const QString&);
void postLine();
protected:

View File

@ -29,7 +29,7 @@
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QPlainTextEdit" name="log">
<widget class="QGBA::LogWidget" name="log">
<property name="readOnly">
<bool>true</bool>
</property>
@ -37,6 +37,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QGBA::LogWidget</class>
<extends>QPlainTextEdit</extends>
<header>LogWidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -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());
}

View File

@ -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&);
};
}

View File

@ -72,8 +72,6 @@ static void SM83DebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatch
static void SM83DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
static bool SM83DebuggerHasBreakpoints(struct mDebuggerPlatform*);
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 SM83Debugger* platform = malloc(sizeof(struct SM83Debugger));
@ -88,8 +86,6 @@ struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
platform->d.checkBreakpoints = SM83DebuggerCheckBreakpoints;
platform->d.hasBreakpoints = SM83DebuggerHasBreakpoints;
platform->d.trace = SM83DebuggerTrace;
platform->d.getRegister = SM83DebuggerGetRegister;
platform->d.setRegister = SM83DebuggerSetRegister;
platform->d.getStackTraceMode = NULL;
platform->d.setStackTraceMode = 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->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;
}

View File

@ -30,6 +30,7 @@ set(GUI_FILES
set(TEST_FILES
test/string-parser.c
test/string-utf8.c
test/table.c
test/text-codec.c
test/vfs.c)

View File

@ -15,11 +15,11 @@
#define FORCE_INLINE inline __attribute__((always_inline))
static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
return (x << r) | (x >> (32 - r));
static inline uint32_t rotl32(uint32_t x, int8_t r) {
return (x << r) | (x >> (32 - r));
}
#define ROTL32(x,y) rotl32(x,y)
#define ROTL32(x, y) rotl32(x, y)
#endif
@ -27,81 +27,82 @@ static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here
static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) {
uint32_t ret;
LOAD_32LE(ret, i << 2, p);
return ret;
static FORCE_INLINE uint32_t getblock32(const uint32_t* p, ssize_t i) {
uint32_t ret;
LOAD_32LE(ret, i << 2, p);
return ret;
}
//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche
static FORCE_INLINE uint32_t fmix32 (uint32_t h) {
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
static FORCE_INLINE uint32_t fmix32(uint32_t h) {
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
return h;
}
//-----------------------------------------------------------------------------
uint32_t hash32(const void* key, int len, uint32_t seed) {
const uint8_t * data = (const uint8_t*)key;
const int nblocks = len / 4;
uint32_t hash32(const void* key, size_t len, uint32_t seed) {
const uint8_t* data = (const uint8_t*) key;
const int nblocks = len / 4;
uint32_t h1 = seed;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
//----------
// body
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
const uint32_t* blocks = (const uint32_t*)(data + nblocks * 4);
int i;
for(i = -nblocks; i; i++)
{
uint32_t k1 = getblock32(blocks,i);
int i;
for (i = -nblocks; i; i++) {
uint32_t k1 = getblock32(blocks, i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 13);
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)
{
case 3:
k1 ^= tail[2] << 16;
// Fall through
case 2:
k1 ^= tail[1] << 8;
// Fall through
case 1:
k1 ^= tail[0];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
switch(len & 3) {
case 3:
k1 ^= tail[2] << 16;
// Fall through
case 2:
k1 ^= tail[1] << 8;
// Fall through
case 1:
k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
};
//----------
// finalization
//----------
// finalization
h1 ^= len;
h1 ^= len;
h1 = fmix32(h1);
h1 = fmix32(h1);
return h1;
return h1;
}

View File

@ -16,6 +16,7 @@
#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_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) \
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) {
--list->nEntries;
--table->size;
free(list->list[item].stringKey);
if (table->deinitializer) {
table->deinitializer(list->list[item].value);
if (table->fn.deref) {
table->fn.deref(list->list[item].stringKey);
} else {
free(list->list[item].stringKey);
}
if (table->fn.deinitializer) {
table->fn.deinitializer(list->list[item].value);
}
if (item != 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) {
struct Table newTable;
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
memcpy(&newTable.fn, &table->fn, sizeof(newTable.fn));
newTable.seed = table->seed * 134775813 + 1;
size_t i;
for (i = 0; i < table->tableSize; ++i) {
const struct TableList* list = &table->table[i];
struct TableList* list = &table->table[i];
size_t j;
for (j = 0; j < list->nEntries; ++j) {
HashTableInsertBinaryMoveKey(&newTable, list->list[j].stringKey, list->list[j].keylen, list->list[j].value);
if (!table->fn.equal) {
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);
}
@ -105,7 +116,9 @@ void TableInit(struct Table* table, size_t initialSize, void (*deinitializer)(vo
table->tableSize = initialSize;
table->table = calloc(table->tableSize, sizeof(struct TableList));
table->size = 0;
table->deinitializer = deinitializer;
table->fn = (struct TableFunctions) {
.deinitializer = deinitializer
};
table->seed = 0;
size_t i;
@ -122,9 +135,13 @@ void TableDeinit(struct Table* table) {
struct TableList* list = &table->table[i];
size_t j;
for (j = 0; j < list->nEntries; ++j) {
free(list->list[j].stringKey);
if (table->deinitializer) {
table->deinitializer(list->list[j].value);
if (table->fn.deref) {
table->fn.deref(list->list[j].stringKey);
} else {
free(list->list[j].stringKey);
}
if (table->fn.deinitializer) {
table->fn.deinitializer(list->list[j].value);
}
}
free(list->list);
@ -146,8 +163,8 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
struct TableList* list = _getList(table, key);
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
if (table->fn.deinitializer) {
table->fn.deinitializer(lookupResult->value);
}
lookupResult->value = value;
}
@ -172,10 +189,10 @@ void TableClear(struct Table* table) {
size_t i;
for (i = 0; i < table->tableSize; ++i) {
struct TableList* list = &table->table[i];
if (table->deinitializer) {
if (table->fn.deinitializer) {
size_t j;
for (j = 0; j < list->nEntries; ++j) {
table->deinitializer(list->list[j].value);
table->fn.deinitializer(list->list[j].value);
}
}
free(list->list);
@ -200,17 +217,73 @@ size_t TableSize(const struct Table* table) {
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*)) {
TableInit(table, initialSize, deinitializer);
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) {
TableDeinit(table);
}
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);
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
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) {
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);
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
return lookupResult->value;
@ -227,18 +305,36 @@ void* HashTableLookupBinary(const struct Table* table, const void* key, size_t k
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) {
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);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_rebalance(table);
hash = hash32(key, strlen(key), table->seed);
if (table->fn.hash) {
hash = table->fn.hash(key, strlen(key), table->seed);
} else {
hash = hash32(key, strlen(key), table->seed);
}
list = _getList(table, hash);
}
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
if (table->fn.deinitializer) {
table->fn.deinitializer(lookupResult->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) {
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);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_rebalance(table);
hash = hash32(key, keylen, table->seed);
if (table->fn.hash) {
hash = table->fn.hash(key, keylen, table->seed);
} else {
hash = hash32(key, keylen, table->seed);
}
list = _getList(table, hash);
}
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
if (table->fn.deinitializer) {
table->fn.deinitializer(lookupResult->value);
}
lookupResult->value = value;
}
@ -280,18 +385,53 @@ void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen,
++table->size;
}
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value) {
uint32_t hash = hash32(key, keylen, table->seed);
void HashTableInsertCustom(struct Table* table, void* key, void* value) {
uint32_t hash = table->fn.hash(key, 0, table->seed);
struct TableList* list = _getList(table, hash);
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
_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);
}
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);
}
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
if (value != lookupResult->value) {
if (table->deinitializer) {
table->deinitializer(lookupResult->value);
if (table->fn.deinitializer) {
table->fn.deinitializer(lookupResult->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) {
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);
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
@ -315,23 +461,40 @@ void HashTableRemove(struct Table* table, const char* key) {
}
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);
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
} 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) {
size_t i;
for (i = 0; i < table->tableSize; ++i) {
struct TableList* list = &table->table[i];
size_t j;
for (j = 0; j < list->nEntries; ++j) {
if (table->deinitializer) {
table->deinitializer(list->list[j].value);
if (table->fn.deinitializer) {
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);
list->listSize = LIST_INITIAL_SIZE;
@ -414,3 +577,77 @@ const char* HashTableSearchString(const struct Table* table, const char* value)
size_t HashTableSize(const struct Table* table) {
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;
}

158
src/util/test/table.c Normal file
View File

@ -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),
)