[bios] Fix lz77 and add BIOS_SndDriverVsyncOn

The lz77 uncompresssion software BIOS implementation was exiting early
when uncompressing data, if the overall length was larger than
advertised in the function parameter. However, real GBA BIOS does read
further than the advertised length, so we do here too. This fixes
Advance Wars title screen.

This also adds a SoundDriverVSyncOn implementation, silencing an error
for some Shrek games, though they still cannot properly boot with an
emulated BIOS.

Fixes #789
This commit is contained in:
Fabrice de Gans 2023-05-23 18:41:16 -07:00 committed by Rafael Kitover
parent 4c8b54de89
commit e26f807355
3 changed files with 77 additions and 65 deletions

View File

@ -2083,8 +2083,8 @@ void CPUSoftwareInterrupt(int comment)
case 0x02: case 0x02:
#ifdef GBA_LOGGING #ifdef GBA_LOGGING
if (systemVerbose & VERBOSE_SWI) { if (systemVerbose & VERBOSE_SWI) {
/*log("Halt: (VCOUNT = %2d)\n", log("Halt: (VCOUNT = %2d)\n",
VCOUNT);*/ VCOUNT);
} }
#endif #endif
holdState = true; holdState = true;
@ -2094,8 +2094,8 @@ void CPUSoftwareInterrupt(int comment)
case 0x03: case 0x03:
#ifdef GBA_LOGGING #ifdef GBA_LOGGING
if (systemVerbose & VERBOSE_SWI) { if (systemVerbose & VERBOSE_SWI) {
/*log("Stop: (VCOUNT = %2d)\n", log("Stop: (VCOUNT = %2d)\n",
VCOUNT);*/ VCOUNT);
} }
#endif #endif
holdState = true; holdState = true;
@ -2271,11 +2271,14 @@ void CPUSoftwareInterrupt(int comment)
case 0x1E: case 0x1E:
BIOS_SndChannelClear(); BIOS_SndChannelClear();
break; break;
case 0x1F:
BIOS_MidiKey2Freq();
break;
case 0x28: case 0x28:
BIOS_SndDriverVSyncOff(); BIOS_SndDriverVSyncOff();
break; break;
case 0x1F: case 0x29:
BIOS_MidiKey2Freq(); BIOS_SndDriverVSyncOn();
break; break;
case 0xE0: case 0xE0:
case 0xE1: case 0xE1:

View File

@ -688,34 +688,18 @@ void BIOS_LZ77UnCompVram()
while (len > 0) { while (len > 0) {
uint8_t d = CPUReadByte(source++); uint8_t d = CPUReadByte(source++);
if (d) { for (int i = 0; i < 8; i++) {
for (int i = 0; i < 8; i++) { if (d & 0x80) {
if (d & 0x80) { uint16_t data = CPUReadByte(source++) << 8;
uint16_t data = CPUReadByte(source++) << 8; data |= CPUReadByte(source++);
data |= CPUReadByte(source++); int length = (data >> 12) + 3;
int length = (data >> 12) + 3; int offset = (data & 0x0FFF);
int offset = (data & 0x0FFF); uint32_t windowOffset = dest + byteCount - offset - 1;
uint32_t windowOffset = dest + byteCount - offset - 1; for (int i2 = 0; i2 < length; i2++) {
for (int i2 = 0; i2 < length; i2++) { writeValue |= (CPUReadByte(windowOffset++) << byteShift);
writeValue |= (CPUReadByte(windowOffset++) << byteShift);
byteShift += 8;
byteCount++;
if (byteCount == 2) {
CPUWriteHalfWord(dest, DowncastU16(writeValue));
dest += 2;
byteCount = 0;
byteShift = 0;
writeValue = 0;
}
len--;
if (len == 0)
return;
}
} else {
writeValue |= (CPUReadByte(source++) << byteShift);
byteShift += 8; byteShift += 8;
byteCount++; byteCount++;
if (byteCount == 2) { if (byteCount == 2) {
CPUWriteHalfWord(dest, DowncastU16(writeValue)); CPUWriteHalfWord(dest, DowncastU16(writeValue));
dest += 2; dest += 2;
@ -724,29 +708,34 @@ void BIOS_LZ77UnCompVram()
writeValue = 0; writeValue = 0;
} }
len--; len--;
if (len == 0)
return;
} }
d <<= 1; } else {
}
} else {
for (int i = 0; i < 8; i++) {
writeValue |= (CPUReadByte(source++) << byteShift); writeValue |= (CPUReadByte(source++) << byteShift);
byteShift += 8; byteShift += 8;
byteCount++; byteCount++;
if (byteCount == 2) { if (byteCount == 2) {
CPUWriteHalfWord(dest, DowncastU16(writeValue)); CPUWriteHalfWord(dest, DowncastU16(writeValue));
dest += 2; dest += 2;
byteShift = 0;
byteCount = 0; byteCount = 0;
byteShift = 0;
writeValue = 0; writeValue = 0;
} }
len--; len--;
if (len == 0) }
return;
d <<= 1;
if (len <= 0) {
// This can happen if the parameter was incorrectly set. Real
// hardware does a buffer overflow so we do it here too.
break;
} }
} }
} }
reg[0].I = source;
reg[1].I = dest;
reg[3].I = 0;
} }
void BIOS_LZ77UnCompWram() void BIOS_LZ77UnCompWram()
@ -772,37 +761,35 @@ void BIOS_LZ77UnCompWram()
while (len > 0) { while (len > 0) {
uint8_t d = CPUReadByte(source++); uint8_t d = CPUReadByte(source++);
if (d) { for (int i = 0; i < 8; i++) {
for (int i = 0; i < 8; i++) { if (d & 0x80) {
if (d & 0x80) { uint16_t data = CPUReadByte(source++) << 8;
uint16_t data = CPUReadByte(source++) << 8; data |= CPUReadByte(source++);
data |= CPUReadByte(source++); int length = (data >> 12) + 3;
int length = (data >> 12) + 3; int offset = (data & 0x0FFF);
int offset = (data & 0x0FFF); uint32_t windowOffset = dest - offset - 1;
uint32_t windowOffset = dest - offset - 1; for (int i2 = 0; i2 < length; i2++) {
for (int i2 = 0; i2 < length; i2++) { CPUWriteByte(dest++, CPUReadByte(windowOffset++));
CPUWriteByte(dest++, CPUReadByte(windowOffset++));
len--;
if (len == 0)
return;
}
} else {
CPUWriteByte(dest++, CPUReadByte(source++));
len--; len--;
if (len == 0)
return;
} }
d <<= 1; } else {
}
} else {
for (int i = 0; i < 8; i++) {
CPUWriteByte(dest++, CPUReadByte(source++)); CPUWriteByte(dest++, CPUReadByte(source++));
len--; len--;
if (len == 0) }
return;
d <<= 1;
if (len <= 0) {
// This can happen if the parameter was incorrectly set. Real
// hardware does a buffer overflow so we do it here too.
break;
} }
} }
} }
reg[0].I = source;
reg[1].I = dest;
reg[3].I = 0;
} }
void BIOS_ObjAffineSet() void BIOS_ObjAffineSet()
@ -1695,6 +1682,13 @@ void BIOS_SndDriverVSync()
void BIOS_SndDriverVSyncOff() // 0x1878 void BIOS_SndDriverVSyncOff() // 0x1878
{ {
#ifdef GBA_LOGGING
if (systemVerbose & VERBOSE_SWI) {
log("SndDriverVSyncOff: (VCOUNT = %2d)\n",
VCOUNT);
}
#endif
uint32_t const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30 uint32_t const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30
uint32_t user1 = CPUReadMemory(puser1); uint32_t user1 = CPUReadMemory(puser1);
@ -1718,6 +1712,20 @@ void BIOS_SndDriverVSyncOff() // 0x1878
//0x18b0 //0x18b0
} }
void BIOS_SndDriverVSyncOn()
{
#ifdef GBA_LOGGING
if (systemVerbose & VERBOSE_SWI) {
log("SndDriverVSyncOn: (VCOUNT = %2d)\n",
VCOUNT);
}
#endif
const uint16_t r1 = 0x8600; // 91 << 9
CPUWriteHalfWord(base1 + 0x6, r1);
CPUWriteHalfWord(base1 + 0x12, r1);
}
// This functions is verified but lacks proper register settings before calling user func // This functions is verified but lacks proper register settings before calling user func
// it might be that user func modifies or uses those? // it might be that user func modifies or uses those?
// r7 should be puser1, r6 should be flags, .... // r7 should be puser1, r6 should be flags, ....

View File

@ -30,6 +30,7 @@ extern void BIOS_SndDriverMode();
extern void BIOS_SndDriverMain(); extern void BIOS_SndDriverMain();
extern void BIOS_SndDriverVSync(); extern void BIOS_SndDriverVSync();
extern void BIOS_SndDriverVSyncOff(); extern void BIOS_SndDriverVSyncOff();
extern void BIOS_SndDriverVSyncOn();
extern void BIOS_SndChannelClear(); extern void BIOS_SndChannelClear();
#endif // BIOS_H #endif // BIOS_H