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 mCoreCallbacks* coreCallbacks;
|
||||
|
||||
struct mTimingEvent divEvent;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
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) {
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue