all repos — mgba @ fdae17020df101fc68ccfda8f8b2937312197c8c

mGBA Game Boy Advance Emulator

DS: Implement hardware DIV
Vicki Pfau vi@endrift.com
Fri, 17 Feb 2017 12:18:22 -0800
commit

fdae17020df101fc68ccfda8f8b2937312197c8c

parent

03817e5293cff053edbdace6deedd4be9ff8165e

3 files changed, 74 insertions(+), 0 deletions(-)

jump to
M include/mgba/internal/ds/ds.hinclude/mgba/internal/ds/ds.h

@@ -101,6 +101,8 @@ struct VFile* bios9Vf;

struct mKeyCallback* keyCallback; struct mCoreCallbacks* coreCallbacks; + + struct mTimingEvent divEvent; }; struct DSCartridge {
M src/ds/ds.csrc/ds/ds.c

@@ -77,6 +77,50 @@ ds->sliceStart = mTimingCurrentTime(timing);

ds->earlyExit = true; } +static void _divide(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + struct DS* ds = context; + ds->memory.io9[DS9_REG_DIVCNT >> 1] &= ~0x8000; + int64_t numerator; + int64_t denominator; + LOAD_64LE(numerator, DS9_REG_DIV_NUMER_0, ds->memory.io9); + LOAD_64LE(denominator, DS9_REG_DIV_DENOM_0, ds->memory.io9); + bool max = false; + switch (ds->memory.io9[DS9_REG_DIVCNT >> 1] & 0x3) { + case 0: + denominator &= 0xFFFFFFFFLL; + case 1: + case 3: + numerator &= 0xFFFFFFFFLL; + if (numerator == INT32_MIN) { + max = true; + } + break; + } + if (numerator == INT64_MIN) { + max = true; + } + if (!denominator) { + ds->memory.io9[DS9_REG_DIVCNT >> 1] |= 0x4000; + STORE_64LE(numerator, DS9_REG_DIVREM_RESULT_0, ds->memory.io9); + numerator >>= 63LL; + numerator = -numerator; + STORE_64LE(numerator, DS9_REG_DIV_RESULT_0, ds->memory.io9); + return; + } + if (denominator == -1LL && max) { + ds->memory.io9[DS9_REG_DIVCNT >> 1] |= 0x4000; + STORE_64LE(numerator, DS9_REG_DIV_RESULT_0, ds->memory.io9); + return; + } + ds->memory.io9[DS9_REG_DIVCNT >> 1] &= ~0x4000; + int64_t result = numerator / denominator; + int64_t remainder = numerator % denominator; // TODO: defined behavior for negative denominator? + STORE_64LE(result, DS9_REG_DIV_RESULT_0, ds->memory.io9); + STORE_64LE(remainder, DS9_REG_DIVREM_RESULT_0, ds->memory.io9); +} + void DSCreate(struct DS* ds) { ds->d.id = DS_COMPONENT_MAGIC; ds->d.init = DSInit;

@@ -130,6 +174,11 @@

ds->romVf = NULL; ds->keyCallback = NULL; + + ds->divEvent.name = "DS Hardware Divide"; + ds->divEvent.callback = _divide; + ds->divEvent.context = ds; + ds->divEvent.priority = 0x50; mTimingInit(&ds->ds7.timing, &ds->ds7.cpu->cycles, &ds->ds7.cpu->nextEvent); mTimingInit(&ds->ds9.timing, &ds->ds9.cpu->cycles, &ds->ds9.cpu->nextEvent);
M src/ds/io.csrc/ds/io.c

@@ -28,6 +28,12 @@ break;

} } +static uint16_t _scheduleDiv(struct DS* ds, uint16_t control) { + mTimingDeschedule(&ds->ds9.timing, &ds->divEvent); + mTimingSchedule(&ds->ds9.timing, &ds->divEvent, (control & 3) ? 36 : 68); + return control | 0x8000; +} + static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) { switch (address) { // Video

@@ -291,6 +297,7 @@ }

void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) { switch (address) { + // VRAM control case DS9_REG_VRAMCNT_A: case DS9_REG_VRAMCNT_C: case DS9_REG_VRAMCNT_E:

@@ -300,6 +307,22 @@ // Fall through

case DS9_REG_VRAMCNT_I: DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8); break; + + // Math + case DS9_REG_DIVCNT: + value = _scheduleDiv(ds, value); + break; + case DS9_REG_DIV_NUMER_0: + case DS9_REG_DIV_NUMER_1: + case DS9_REG_DIV_NUMER_2: + case DS9_REG_DIV_NUMER_3: + case DS9_REG_DIV_DENOM_0: + case DS9_REG_DIV_DENOM_1: + case DS9_REG_DIV_DENOM_2: + case DS9_REG_DIV_DENOM_3: + ds->memory.io9[DS9_REG_DIVCNT >> 1] = _scheduleDiv(ds, ds->memory.io9[DS9_REG_DIVCNT >> 1]); + break; + default: { uint32_t v2 = DSIOWrite(&ds->ds9, address, value);