mirror of https://github.com/mgba-emu/mgba.git
GB: Add double speed
This commit is contained in:
parent
fb0555e4fb
commit
8608f11154
|
@ -27,6 +27,7 @@ static int32_t _updateChannel2(struct GBAudioChannel2* ch);
|
||||||
static int32_t _updateChannel3(struct GBAudioChannel3* ch, enum GBAudioStyle style);
|
static int32_t _updateChannel3(struct GBAudioChannel3* ch, enum GBAudioStyle style);
|
||||||
static int32_t _updateChannel4(struct GBAudioChannel4* ch);
|
static int32_t _updateChannel4(struct GBAudioChannel4* ch);
|
||||||
static void _sample(struct GBAudio* audio, int32_t cycles);
|
static void _sample(struct GBAudio* audio, int32_t cycles);
|
||||||
|
static void _scheduleEvent(struct GBAudio* audio);
|
||||||
|
|
||||||
void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) {
|
void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) {
|
||||||
audio->samples = samples;
|
audio->samples = samples;
|
||||||
|
@ -158,13 +159,7 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
|
||||||
--audio->ch1.control.length;
|
--audio->ch1.control.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Don't need p
|
_scheduleEvent(audio);
|
||||||
if (audio->p) {
|
|
||||||
audio->nextEvent = audio->p->cpu->cycles;
|
|
||||||
audio->p->cpu->nextEvent = audio->nextEvent;
|
|
||||||
} else {
|
|
||||||
audio->nextEvent = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0001;
|
*audio->nr52 &= ~0x0001;
|
||||||
*audio->nr52 |= audio->playingCh1;
|
*audio->nr52 |= audio->playingCh1;
|
||||||
|
@ -216,13 +211,7 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
|
||||||
--audio->ch2.control.length;
|
--audio->ch2.control.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Don't need p
|
_scheduleEvent(audio);
|
||||||
if (audio->p) {
|
|
||||||
audio->nextEvent = audio->p->cpu->cycles;
|
|
||||||
audio->p->cpu->nextEvent = audio->nextEvent;
|
|
||||||
} else {
|
|
||||||
audio->nextEvent = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0002;
|
*audio->nr52 &= ~0x0002;
|
||||||
*audio->nr52 |= audio->playingCh2 << 1;
|
*audio->nr52 |= audio->playingCh2 << 1;
|
||||||
|
@ -287,16 +276,9 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
|
||||||
audio->eventDiff = 0;
|
audio->eventDiff = 0;
|
||||||
}
|
}
|
||||||
audio->ch3.readable = audio->style != GB_AUDIO_DMG;
|
audio->ch3.readable = audio->style != GB_AUDIO_DMG;
|
||||||
// TODO: Don't need p
|
_scheduleEvent(audio);
|
||||||
if (audio->p) {
|
|
||||||
// TODO: Where does this cycle delay come from?
|
// TODO: Where does this cycle delay come from?
|
||||||
audio->nextCh3 = audio->eventDiff + audio->p->cpu->cycles + 4 + 2 * (2048 - audio->ch3.rate);
|
audio->nextCh3 = audio->eventDiff + audio->nextEvent + 4 + 2 * (2048 - audio->ch3.rate);
|
||||||
audio->nextEvent = audio->p->cpu->cycles;
|
|
||||||
audio->p->cpu->nextEvent = audio->nextEvent;
|
|
||||||
} else {
|
|
||||||
audio->nextCh3 = audio->eventDiff + 4 + 2 * (2048 - audio->ch3.rate);
|
|
||||||
audio->nextEvent = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0004;
|
*audio->nr52 &= ~0x0004;
|
||||||
*audio->nr52 |= audio->playingCh3 << 2;
|
*audio->nr52 |= audio->playingCh3 << 2;
|
||||||
|
@ -352,13 +334,7 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
|
||||||
--audio->ch4.length;
|
--audio->ch4.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Don't need p
|
_scheduleEvent(audio);
|
||||||
if (audio->p) {
|
|
||||||
audio->nextEvent = audio->p->cpu->cycles;
|
|
||||||
audio->p->cpu->nextEvent = audio->nextEvent;
|
|
||||||
} else {
|
|
||||||
audio->nextEvent = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0008;
|
*audio->nr52 &= ~0x0008;
|
||||||
*audio->nr52 |= audio->playingCh4 << 3;
|
*audio->nr52 |= audio->playingCh4 << 3;
|
||||||
|
@ -865,3 +841,13 @@ static int32_t _updateChannel4(struct GBAudioChannel4* ch) {
|
||||||
timing *= 8;
|
timing *= 8;
|
||||||
return timing;
|
return timing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _scheduleEvent(struct GBAudio* audio) {
|
||||||
|
// TODO: Don't need p
|
||||||
|
if (audio->p) {
|
||||||
|
audio->nextEvent = audio->p->cpu->cycles >> audio->p->doubleSpeed;
|
||||||
|
audio->p->cpu->nextEvent = audio->nextEvent;
|
||||||
|
} else {
|
||||||
|
audio->nextEvent = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
src/gb/gb.c
28
src/gb/gb.c
|
@ -62,6 +62,7 @@ static void GBInit(void* cpu, struct mCPUComponent* component) {
|
||||||
gb->yankedRomSize = 0;
|
gb->yankedRomSize = 0;
|
||||||
|
|
||||||
gb->eiPending = false;
|
gb->eiPending = false;
|
||||||
|
gb->doubleSpeed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBLoadROM(struct GB* gb, struct VFile* vf) {
|
bool GBLoadROM(struct GB* gb, struct VFile* vf) {
|
||||||
|
@ -160,7 +161,7 @@ void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
|
||||||
irqh->processEvents = GBProcessEvents;
|
irqh->processEvents = GBProcessEvents;
|
||||||
irqh->setInterrupts = GBSetInterrupts;
|
irqh->setInterrupts = GBSetInterrupts;
|
||||||
irqh->hitIllegal = GBIllegal;
|
irqh->hitIllegal = GBIllegal;
|
||||||
irqh->hitStub = GBHitStub;
|
irqh->stop = GBStop;
|
||||||
irqh->halt = GBHalt;
|
irqh->halt = GBHalt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,15 +254,21 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testEvent = GBVideoProcessEvents(&gb->video, cycles);
|
testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
|
||||||
|
if (testEvent != INT_MAX) {
|
||||||
|
testEvent <<= gb->doubleSpeed;
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
testEvent = GBAudioProcessEvents(&gb->audio, cycles);
|
testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
|
||||||
|
if (testEvent != INT_MAX) {
|
||||||
|
testEvent <<= gb->doubleSpeed;
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
testEvent = GBTimerProcessEvents(&gb->timer, cycles);
|
testEvent = GBTimerProcessEvents(&gb->timer, cycles);
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
|
@ -301,16 +308,21 @@ void GBHalt(struct LR35902Core* cpu) {
|
||||||
cpu->halted = true;
|
cpu->halted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GBStop(struct LR35902Core* cpu) {
|
||||||
|
struct GB* gb = (struct GB*) cpu->master;
|
||||||
|
if (gb->memory.io[REG_KEY1] & 1) {
|
||||||
|
gb->doubleSpeed ^= 1;
|
||||||
|
gb->memory.io[REG_KEY1] &= 1;
|
||||||
|
gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
|
||||||
|
}
|
||||||
|
// TODO: Actually stop
|
||||||
|
}
|
||||||
|
|
||||||
void GBIllegal(struct LR35902Core* cpu) {
|
void GBIllegal(struct LR35902Core* cpu) {
|
||||||
// TODO
|
// TODO
|
||||||
mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
|
mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBHitStub(struct LR35902Core* cpu) {
|
|
||||||
// TODO
|
|
||||||
mLOG(GB, STUB, "Hit stub at address %04X:%02X\n", cpu->pc, cpu->bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GBIsROM(struct VFile* vf) {
|
bool GBIsROM(struct VFile* vf) {
|
||||||
vf->seek(vf, 0x104, SEEK_SET);
|
vf->seek(vf, 0x104, SEEK_SET);
|
||||||
uint8_t header[4];
|
uint8_t header[4];
|
||||||
|
|
|
@ -67,6 +67,7 @@ struct GB {
|
||||||
struct mAVStream* stream;
|
struct mAVStream* stream;
|
||||||
|
|
||||||
int32_t eiPending;
|
int32_t eiPending;
|
||||||
|
unsigned doubleSpeed;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBCartridge {
|
struct GBCartridge {
|
||||||
|
|
|
@ -35,6 +35,7 @@ const static uint8_t _registerMask[] = {
|
||||||
[REG_NR51] = 0x00,
|
[REG_NR51] = 0x00,
|
||||||
[REG_NR52] = 0x70,
|
[REG_NR52] = 0x70,
|
||||||
[REG_STAT] = 0x80,
|
[REG_STAT] = 0x80,
|
||||||
|
[REG_KEY1] = 0x7E,
|
||||||
[REG_VBK] = 0xFE,
|
[REG_VBK] = 0xFE,
|
||||||
[REG_OCPS] = 0x40,
|
[REG_OCPS] = 0x40,
|
||||||
[REG_BCPS] = 0x40,
|
[REG_BCPS] = 0x40,
|
||||||
|
@ -308,6 +309,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||||
default:
|
default:
|
||||||
if (gb->model >= GB_MODEL_CGB) {
|
if (gb->model >= GB_MODEL_CGB) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
|
case REG_KEY1:
|
||||||
|
value &= 0x1;
|
||||||
|
value |= gb->memory.io[address] & 0x80;
|
||||||
|
break;
|
||||||
case REG_VBK:
|
case REG_VBK:
|
||||||
GBVideoSwitchBank(&gb->video, value);
|
GBVideoSwitchBank(&gb->video, value);
|
||||||
break;
|
break;
|
||||||
|
@ -455,6 +460,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||||
case REG_HDMA4:
|
case REG_HDMA4:
|
||||||
case REG_HDMA5:
|
case REG_HDMA5:
|
||||||
case REG_SVBK:
|
case REG_SVBK:
|
||||||
|
case REG_KEY1:
|
||||||
// Handled transparently by the registers
|
// Handled transparently by the registers
|
||||||
goto success;
|
goto success;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -221,7 +221,7 @@ void GBVideoProcessDots(struct GBVideo* video) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldX = video->x;
|
int oldX = video->x;
|
||||||
video->x = video->dotCounter + video->eventDiff + video->p->cpu->cycles;
|
video->x = video->dotCounter + video->eventDiff + (video->p->cpu->cycles >> video->p->doubleSpeed);
|
||||||
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
|
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
|
||||||
video->x = GB_VIDEO_HORIZONTAL_PIXELS;
|
video->x = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||||
}
|
}
|
||||||
|
@ -238,15 +238,15 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
|
video->nextMode = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
|
||||||
video->nextEvent = video->nextMode;
|
video->nextEvent = video->nextMode;
|
||||||
video->eventDiff = -video->p->cpu->cycles;
|
video->eventDiff = -video->p->cpu->cycles >> video->p->doubleSpeed;
|
||||||
// TODO: Does this read as 0 for 4 T-cycles?
|
// TODO: Does this read as 0 for 4 T-cycles?
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_LY] = 0;
|
video->p->memory.io[REG_LY] = 0;
|
||||||
|
|
||||||
if (video->p->cpu->cycles + video->nextEvent < video->p->cpu->nextEvent) {
|
if (video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed) < video->p->cpu->nextEvent) {
|
||||||
video->p->cpu->nextEvent = video->p->cpu->cycles + video->nextEvent;
|
video->p->cpu->nextEvent = video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, DECC), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, DECC), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, LDC_), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, LDC_), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, RRCA_), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, RRCA_), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, STUB), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, STOP), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, LDDE), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, LDDE), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, LDDE_A), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, LDDE_A), \
|
||||||
DECLARE_INSTRUCTION_LR35902(EMITTER, INCDE), \
|
DECLARE_INSTRUCTION_LR35902(EMITTER, INCDE), \
|
||||||
|
|
|
@ -767,7 +767,15 @@ DEFINE_RST_INSTRUCTION_LR35902(30);
|
||||||
DEFINE_RST_INSTRUCTION_LR35902(38);
|
DEFINE_RST_INSTRUCTION_LR35902(38);
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_LR35902(ILL, cpu->irqh.hitIllegal(cpu));
|
DEFINE_INSTRUCTION_LR35902(ILL, cpu->irqh.hitIllegal(cpu));
|
||||||
DEFINE_INSTRUCTION_LR35902(STUB, cpu->irqh.hitStub(cpu));
|
|
||||||
|
DEFINE_INSTRUCTION_LR35902(STOP2,
|
||||||
|
if (!cpu->bus) {
|
||||||
|
cpu->irqh.stop(cpu);
|
||||||
|
});
|
||||||
|
|
||||||
|
DEFINE_INSTRUCTION_LR35902(STOP, \
|
||||||
|
cpu->executionState = LR35902_CORE_READ_PC; \
|
||||||
|
cpu->instruction = _LR35902InstructionSTOP2;)
|
||||||
|
|
||||||
static const LR35902Instruction _lr35902CBInstructionTable[0x100] = {
|
static const LR35902Instruction _lr35902CBInstructionTable[0x100] = {
|
||||||
DECLARE_LR35902_CB_EMITTER_BLOCK(_LR35902Instruction)
|
DECLARE_LR35902_CB_EMITTER_BLOCK(_LR35902Instruction)
|
||||||
|
|
|
@ -63,9 +63,9 @@ struct LR35902InterruptHandler {
|
||||||
void (*processEvents)(struct LR35902Core* cpu);
|
void (*processEvents)(struct LR35902Core* cpu);
|
||||||
void (*setInterrupts)(struct LR35902Core* cpu, bool enable);
|
void (*setInterrupts)(struct LR35902Core* cpu, bool enable);
|
||||||
void (*halt)(struct LR35902Core* cpu);
|
void (*halt)(struct LR35902Core* cpu);
|
||||||
|
void (*stop)(struct LR35902Core* cpu);
|
||||||
|
|
||||||
void (*hitIllegal)(struct LR35902Core* cpu);
|
void (*hitIllegal)(struct LR35902Core* cpu);
|
||||||
void (*hitStub)(struct LR35902Core* cpu);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LR35902Core {
|
struct LR35902Core {
|
||||||
|
|
Loading…
Reference in New Issue