diff --git a/Info.plist b/Info.plist
index 40a8bce..772168d 100644
--- a/Info.plist
+++ b/Info.plist
@@ -17,7 +17,7 @@
CFBundleSignature
????
CFBundleVersion
- 0.7.0
+ 0.7.1
NSPrincipalClass
OEGameCoreController
OEGameCoreClass
diff --git a/src/gb/gb.c b/src/gb/gb.c
index 508483c..038401c 100644
--- a/src/gb/gb.c
+++ b/src/gb/gb.c
@@ -202,7 +202,7 @@ void GBResizeSram(struct GB* gb, size_t size) {
if (gb->memory.sram == (void*) -1) {
gb->memory.sram = NULL;
}
- } else {
+ } else if (size) {
uint8_t* newSram = anonymousMemoryMap(size);
if (gb->memory.sram) {
if (size > gb->sramSize) {
diff --git a/src/gb/serialize.c b/src/gb/serialize.c
index 4eef6ae..7a4f5de 100644
--- a/src/gb/serialize.c
+++ b/src/gb/serialize.c
@@ -113,7 +113,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
error = true;
}
LOAD_16LE(check16, 0, &state->video.x);
- if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
+ if (check16 < -7 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range");
error = true;
}
@@ -138,7 +138,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
if (error) {
return false;
}
- gb->timing.root = NULL;
+ mTimingClear(&gb->timing);
LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
gb->cpu->a = state->cpu.a;
diff --git a/src/gba/dma.c b/src/gba/dma.c
index 2eded79..96b1038 100644
--- a/src/gba/dma.c
+++ b/src/gba/dma.c
@@ -182,7 +182,7 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
dma->nextCount = 0;
bool noRepeat = !GBADMARegisterIsRepeat(dma->reg);
noRepeat |= GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_NOW;
- noRepeat |= memory->activeDMA == 3 && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM;
+ noRepeat |= memory->activeDMA == 3 && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM && gba->video.vcount == VIDEO_VERTICAL_PIXELS + 1;
if (noRepeat) {
dma->reg = GBADMARegisterClearEnable(dma->reg);
@@ -237,9 +237,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
gba->cpuBlocked = true;
if (info->count == info->nextCount) {
- if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
- cycles += 2;
- }
if (width == 4) {
cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion];
} else {
@@ -263,8 +260,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
}
gba->bus = memory->dmaTransferRegister;
cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0);
- memory->dmaTransferRegister &= 0xFFFF0000;
- memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16;
} else {
if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
@@ -272,10 +267,10 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
GBASavedataInitEEPROM(&memory->savedata);
}
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
- } else {
- if (source) {
- memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0);
- }
+ memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
+ } else if (source) {
+ memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0);
+ memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
}
if (destRegion == REGION_CART2_EX) {
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
@@ -289,7 +284,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0);
}
- memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
gba->bus = memory->dmaTransferRegister;
}
int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
@@ -306,6 +300,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
info->nextDest = dest;
if (!wordsRemaining) {
info->nextCount |= 0x80000000;
+ if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
+ info->when += 2;
+ }
}
GBADMAUpdate(gba);
}
diff --git a/src/gba/gba.c b/src/gba/gba.c
index 9441105..92e4e31 100644
--- a/src/gba/gba.c
+++ b/src/gba/gba.c
@@ -232,7 +232,8 @@ void GBASkipBIOS(struct GBA* gba) {
} else {
cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
}
- gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
+ gba->video.vcount = 0x7D;
+ gba->memory.io[REG_VCOUNT >> 1] = 0x7D;
gba->memory.io[REG_POSTFLG >> 1] = 1;
int currentCycles = 0;
ARM_WRITE_PC;
diff --git a/src/gba/memory.c b/src/gba/memory.c
index 53fd573..c779853 100644
--- a/src/gba/memory.c
+++ b/src/gba/memory.c
@@ -1647,6 +1647,10 @@ void _pristineCow(struct GBA* gba) {
}
void GBAPrintFlush(struct GBA* gba) {
+ if (!gba->memory.agbPrintBuffer) {
+ return;
+ }
+
char oolBuf[0x101];
size_t i;
for (i = 0; gba->memory.agbPrintCtx.get != gba->memory.agbPrintCtx.put && i < 0x100; ++i) {
@@ -1688,8 +1692,8 @@ static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) {
static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) {
struct GBAMemory* memory = &gba->memory;
- int16_t value = 0xFFFF;
- if (address < AGB_PRINT_TOP) {
+ int16_t value = address >> 1;
+ if (address < AGB_PRINT_TOP && memory->agbPrintBuffer) {
LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer);
} else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) {
value = (&memory->agbPrintCtx.request)[(address & 7) >> 1];
diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c
index 615e13c..ad846ac 100644
--- a/src/gba/renderers/software-obj.c
+++ b/src/gba/renderers/software-obj.c
@@ -27,9 +27,6 @@
} \
} \
for (; outX < condition; ++outX, inX += xOffset) { \
- if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
- continue; \
- } \
int localX = inX - xOffset * (outX % mosaicH); \
if (localX < 0 || localX > width - 1) { \
continue; \
@@ -43,9 +40,6 @@
unsigned widthMask = ~(width - 1); \
unsigned heightMask = ~(height - 1); \
for (; outX < condition; ++outX, ++inX) { \
- if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
- continue; \
- } \
renderer->spriteCyclesRemaining -= 2; \
xAccum += mat.a; \
yAccum += mat.c; \
@@ -151,9 +145,6 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) >= 3 && GBAObjAttributesCGetTile(sprite->c) < 512) {
return 0;
}
- if (renderer->spriteCyclesRemaining <= 0) {
- return 0;
- }
int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed);
int variant = renderer->target1Obj &&
diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c
index 0f82d44..f31239d 100644
--- a/src/gba/renderers/video-software.c
+++ b/src/gba/renderers/video-software.c
@@ -727,7 +727,8 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac
if (!active) {
renderer->bg[bg].enabled = 0;
} else if (!wasActive && active) {
- if (renderer->nextY == 0) {
+ if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
+ // TODO: Investigate in more depth how switching background works in different modes
renderer->bg[bg].enabled = 4;
} else {
renderer->bg[bg].enabled = 1;
@@ -809,7 +810,6 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer*
static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
int w;
- renderer->end = 0;
int spriteLayers = 0;
if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
if (renderer->oamDirty) {
@@ -818,31 +818,34 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
int mosaicY = y - (y % mosaicV);
- for (w = 0; w < renderer->nWindows; ++w) {
- renderer->start = renderer->end;
- renderer->end = renderer->windows[w].endX;
- renderer->currentWindow = renderer->windows[w].control;
- if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
+ int i;
+ for (i = 0; i < renderer->oamMax; ++i) {
+ struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
+ int localY = y;
+ renderer->end = 0;
+ if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
+ localY = mosaicY;
+ }
+ if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
continue;
}
- int i;
- int drawn;
-
- for (i = 0; i < renderer->oamMax; ++i) {
- int localY = y;
+ for (w = 0; w < renderer->nWindows; ++w) {
if (renderer->spriteCyclesRemaining <= 0) {
break;
}
- struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
- if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
- localY = mosaicY;
- }
- if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
+ renderer->currentWindow = renderer->windows[w].control;
+ renderer->start = renderer->end;
+ renderer->end = renderer->windows[w].endX;
+ if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
continue;
}
- drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
+
+ int drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
}
+ if (renderer->spriteCyclesRemaining <= 0) {
+ break;
+ }
}
}
diff --git a/src/gba/serialize.c b/src/gba/serialize.c
index 72cdd45..781b608 100644
--- a/src/gba/serialize.c
+++ b/src/gba/serialize.c
@@ -128,7 +128,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (error) {
return false;
}
- gba->timing.root = NULL;
+ mTimingClear(&gba->timing);
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
size_t i;
diff --git a/src/gba/sio.c b/src/gba/sio.c
index d620be5..800af8a 100644
--- a/src/gba/sio.c
+++ b/src/gba/sio.c
@@ -164,6 +164,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
}
break;
case SIO_MULTI:
+ value &= 0xFF83;
value |= 0xC;
break;
default: