DS: Implement hardware DIV

This commit is contained in:
Vicki Pfau 2017-02-17 12:18:22 -08:00
parent 03817e5293
commit fdae17020d
3 changed files with 74 additions and 0 deletions

View File

@ -101,6 +101,8 @@ struct DS {
struct mKeyCallback* keyCallback;
struct mCoreCallbacks* coreCallbacks;
struct mTimingEvent divEvent;
};
struct DSCartridge {

View File

@ -77,6 +77,50 @@ static void _slice(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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;
@ -131,6 +175,11 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
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);
}

View File

@ -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) {
switch (address) {
// Video
@ -291,6 +297,7 @@ void DS9IOInit(struct DS* ds) {
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 @@ void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
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);