mirror of https://github.com/mgba-emu/mgba.git
DS: Implement hardware DIV
This commit is contained in:
parent
03817e5293
commit
fdae17020d
|
@ -101,6 +101,8 @@ struct DS {
|
||||||
|
|
||||||
struct mKeyCallback* keyCallback;
|
struct mKeyCallback* keyCallback;
|
||||||
struct mCoreCallbacks* coreCallbacks;
|
struct mCoreCallbacks* coreCallbacks;
|
||||||
|
|
||||||
|
struct mTimingEvent divEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DSCartridge {
|
struct DSCartridge {
|
||||||
|
|
49
src/ds/ds.c
49
src/ds/ds.c
|
@ -77,6 +77,50 @@ static void _slice(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
ds->earlyExit = true;
|
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) {
|
void DSCreate(struct DS* ds) {
|
||||||
ds->d.id = DS_COMPONENT_MAGIC;
|
ds->d.id = DS_COMPONENT_MAGIC;
|
||||||
ds->d.init = DSInit;
|
ds->d.init = DSInit;
|
||||||
|
@ -131,6 +175,11 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
|
||||||
|
|
||||||
ds->keyCallback = 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->ds7.timing, &ds->ds7.cpu->cycles, &ds->ds7.cpu->nextEvent);
|
||||||
mTimingInit(&ds->ds9.timing, &ds->ds9.cpu->cycles, &ds->ds9.cpu->nextEvent);
|
mTimingInit(&ds->ds9.timing, &ds->ds9.cpu->cycles, &ds->ds9.cpu->nextEvent);
|
||||||
}
|
}
|
||||||
|
|
23
src/ds/io.c
23
src/ds/io.c
|
@ -28,6 +28,12 @@ static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
// Video
|
// Video
|
||||||
|
@ -291,6 +297,7 @@ void DS9IOInit(struct DS* ds) {
|
||||||
|
|
||||||
void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
|
// VRAM control
|
||||||
case DS9_REG_VRAMCNT_A:
|
case DS9_REG_VRAMCNT_A:
|
||||||
case DS9_REG_VRAMCNT_C:
|
case DS9_REG_VRAMCNT_C:
|
||||||
case DS9_REG_VRAMCNT_E:
|
case DS9_REG_VRAMCNT_E:
|
||||||
|
@ -300,6 +307,22 @@ void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
||||||
case DS9_REG_VRAMCNT_I:
|
case DS9_REG_VRAMCNT_I:
|
||||||
DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8);
|
DSVideoConfigureVRAM(&ds->memory, address - DS9_REG_VRAMCNT_A, value >> 8);
|
||||||
break;
|
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:
|
default:
|
||||||
{
|
{
|
||||||
uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
|
uint32_t v2 = DSIOWrite(&ds->ds9, address, value);
|
||||||
|
|
Loading…
Reference in New Issue