This patch adds those missing HLE BIOS functions for sound driver: https://sourceforge.net/p/vbam/bugs/24/

It is utilized by Phantasy Star Collection at intro. I don't know any other games using this BIOS driver, so I just implemented the parts that Phantasy Star Collection uses.

This patch is far away from perfect yet. Sound quality is not the same as with original BIOS. Also code is horrible to read, since I just kept translating arm instructions to C without thinking too much. Got it done quickly so if anyone has keen interest on continuing from here.

Patch by Juha Laukkanen
This commit is contained in:
skidau 2015-10-17 10:39:09 +11:00
parent 1e1b1cc35c
commit a9aa25a56f
3 changed files with 731 additions and 0 deletions

View File

@ -2342,6 +2342,28 @@ void CPUSoftwareInterrupt(int comment)
else
soundResume();
break;
case 0x1A:
BIOS_SndDriverInit();
SWITicks = 252000;
break;
case 0x1B:
BIOS_SndDriverMode();
SWITicks = 280000;
break;
case 0x1C:
BIOS_SndDriverMain();
SWITicks = 11050;//avg
break;
case 0x1D:
BIOS_SndDriverVSync();
SWITicks = 44;
break;
case 0x1E:
BIOS_SndChannelClear();
break;
case 0x28:
BIOS_SndDriverVSyncOff();
break;
case 0x1F:
BIOS_MidiKey2Freq();
break;

View File

@ -1108,6 +1108,709 @@ void BIOS_Sqrt()
#endif
}
#define ADBITS_MASK 0x3FU
u32 const base1 = 0x040000C0;
u32 const base2 = 0x04000080;
static bool BIOS_SndDriver_ba4(u32 r0, u32 r4r12) // 0xba4
{
if (r4r12)
{
r4r12 = r4r12 & ~0xFE000000;
r4r12 += r0;
if (!((r0 & 0x0E000000) && (r4r12 & 0x0E000000)))
return true;
}
return false;
}
static void BIOS_SndDriver_b4c(u32 r0, u32 r1, u32 r2) // 0xb4c
{
// @r4
u32 r4 = 4 * r2 & 0x7FFFFF;
bool ok = BIOS_SndDriver_ba4(r0, r4); // aka b9c
#if 0
int v3; // r4@1
int v4; // r0@1
int v5; // r1@1
int v6; // r2@1
char v7; // zf@1
signed int v8; // r5@2
int v9; // r5@4
int v10; // r3@6
int v11; // r3@10
unsigned int v12; // r4@11
unsigned short v13; // r3@13
#endif
// 0b56
if ( !ok )
{
u32 r5 = 0; //v8
if ( r2 >= (1<<(27-1)) ) //v6
{
r5 = r1 + r4;
if ( r2 >= (1<<(25-1)) )
{
u32 r3 = CPUReadMemory(r0);
while ( r1 < r5 )
{
CPUWriteMemory(r1, r3);
r1 += 4;
}
}
else // @todo 0b6e
{
#if 0
while ( v5 < v9 )
{
v11 = *(_DWORD *)v4;
v4 += 4;
*(_DWORD *)v5 = v11;
v5 += 4;
}
#endif
}
}
else // @todo 0b78
{
#if 0
v12 = (unsigned int)v3 >> 1;
if ( __CFSHR__(v6, 25) )
{
v13 = *(_WORD *)v4;
while ( v8 < (signed int)v12 )
{
*(_WORD *)(v5 + v8) = v13;
v8 += 2;
}
}
else
{
while ( v8 < (signed int)v12 )
{
*(_WORD *)(v5 + v8) = *(_WORD *)(v4 + v8);
v8 += 2;
}
}
#endif
}
}
// 0x0b96
}
static s32 BIOS_SndDriver_3e4(u32 const r0a, u32 const r1a) // 0x3e4
{
s32 r0 = r1a;
s32 r1 = r0a;
u32 v5 = r0 & 0x80000000;
s32 r12;
s32 r2;
bool gtr;
if ( (r1 & 0x80000000 & 0x80000000) != 0 )
r1 = -r1;
r12 = r0; //v5 ^ (r0 >> 32);
if ( r0 < 0 )
r0 = -r0;
r2 = r1;
do
{
gtr = (r2 >= r0 >> 1);
if ( r2 <= r0 >> 1 )
r2 *= 2;
} while ( !gtr );
while ( 1 )
{
v5 += (r0 >= (u32)r2) + v5;
if ( r0 >= (u32)r2 )
r0 -= r2;
if ( r2 == r1 )
break;
r2 = (u32)r2 >> 1;
}
if ( !(r12 << 1) )
return -v5;
else
return v5;
}
static void BIOS_SndDriverSub1(u32 p1) // 0x170a
{
u8 local1 = (p1 & 0x000F0000) >> 16; // param is r0
u32 const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30
// Store something
CPUWriteByte(puser1+8, local1);
u32 r0 = (0x31e8 + (local1 << 1)) - 0x20;
// @todo read from bios region
if (r0 == 0x31D0)
{
r0 = 0xE0;
}
else if (r0 == 0x31E0)
{
r0 = 0x2C0;
}
else r0 = CPUReadHalfWord(r0+0x1E);
CPUWriteMemory(puser1+0x10, r0);
u32 r1 = 0x630;
u32 const r4 = r0;
// 0x172c
r0 = BIOS_SndDriver_3e4(r0, r1);
CPUWriteByte(puser1+0xB, r0);
u32 x = 0x91d1b * r4;
r1 = x + 0x1388;
r0 = 0x1388 << 1;
r0 = BIOS_SndDriver_3e4(r0, r1);
CPUWriteMemory(puser1+0x14, r0);
r1 = 1 << 24;
r0 = BIOS_SndDriver_3e4(r0, r1) + 1;
r0 /= 2;
CPUWriteMemory(puser1+0x18, r0);
// 0x1750
u32 r4basesnd = 0x4000100;
r0 = r4;
r1 = 0x44940;
CPUWriteHalfWord(r4basesnd+2, 0);
r0 = BIOS_SndDriver_3e4(r0, r1);
r0 = (1<<16)-r0;
CPUWriteHalfWord(r4basesnd+0, r0);
// sub 0x18c8 is unrolled here
r1 = 0x5b << 9;
CPUWriteHalfWord(base1+6 , r1);
CPUWriteHalfWord(base1+12 , r1);
// 0x176a, @todo busy loop here
r0 = 0x4000000;
//do
{
r1 = CPUReadByte(r0+6);
} //while (r1 != 0x9F);
CPUWriteHalfWord(r4basesnd+2, 0x80);
}
void BIOS_SndDriverInit() // 0x166a
{
#ifdef GBA_LOGGING
if(systemVerbose & VERBOSE_SWI) {
log("SndDriverInit: WaveData=%08x mk=%08x fp=%08x\n",
reg[0].I,
reg[1].I,
reg[2].I);
}
#endif
// 7FC0 + 0x30
u32 const puser1 = 0x3007FF0;
u32 const user1 = reg[0].I;
u32 base3 = 0x040000BC;
//u32 base4 = 0x03007FF0;
CPUWriteHalfWord(base1+6 , 0);
CPUWriteHalfWord(base1+12, 0);
CPUWriteHalfWord(base2+4, 0x8F);
CPUWriteHalfWord(base2+2, 0xA90E);
u16 val9 = CPUReadHalfWord(base2+9);
CPUWriteHalfWord(base2+9, val9&ADBITS_MASK); // DA?
CPUWriteMemory(base3+0, (user1 + 0x350)); //0x350, 640int
CPUWriteMemory(base1+0, 0x40000A0);
CPUWriteMemory(base1+8, 2224); //0x980
CPUWriteMemory(base1+12, 0x40000A4);
CPUWriteMemory(puser1, user1);
u32 const r2 = 0x050003EC;
u32 const sp = reg[13].I; // 0x03003c98;
CPUWriteMemory(sp, 0);
BIOS_SndDriver_b4c(sp, user1, r2);
// 0x16b0
CPUWriteByte(user1+0x6, 0x8);
CPUWriteByte(user1+0x7, 0xF);
CPUWriteMemory(user1+0x38, 0x2425);
CPUWriteMemory(user1+0x28, 0x1709);
CPUWriteMemory(user1+0x2C, 0x1709);
CPUWriteMemory(user1+0x30, 0x1709);
CPUWriteMemory(user1+0x3C, 0x1709);
CPUWriteMemory(user1+0x34, 0x3738);
BIOS_SndDriverSub1(0x40000);
CPUWriteMemory(user1, 0x68736D53); // this ret is common among funcs
}
void BIOS_SndDriverMode() //0x179c
{
u32 input = reg[0].I;
u32 const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30
u32 user1 = CPUReadMemory(puser1);
if ( user1 == 0x68736D53 )
{
CPUWriteMemory(puser1, (++user1)); // this guard is common for funcs, unify
// reverb values at bits 0...7
u8 revb = (input & 0xFF);
if ( revb )
{
revb>>=1; // shift out the 7th enable bit
CPUWriteByte(puser1+5, revb);
}
// direct sound multi channels at bits 8...11
u8 chc = (input & 0xF00)>>8;
if ( chc > 0 )
{
CPUWriteByte(puser1+6, chc);
u32 puser2 = puser1 + 7 + 0x49;
int count=12;
while (count--)
{
CPUWriteByte(puser2, 0);
puser2+=0x40;
}
}
// direct sound master volume at bits 12...15
u8 chv = (input & 0xF000)>>12;
if ( chv > 0 )
{
CPUWriteByte(puser1+7, chv);
}
// DA converter bits at bits 20...23
u32 dab = (input & 0x00B00000);
if ( dab )
{
dab &= 0x00300000;
dab >>= 0xE;
u8 adv = CPUReadByte(puser1+9) & ADBITS_MASK; // @todo verify offset
dab |= adv;
CPUWriteByte(puser1+9, dab);
}
// Playback frequency at bits 16...19
u32 pbf = (input & 0x000F0000);
if ( pbf )
{
// Modifies puser1/user1
BIOS_SndDriverVSyncOff();
// another sub at 180c
BIOS_SndDriverSub1(pbf);
}
CPUWriteMemory(puser1, (--user1));
}
}
void BIOS_SndDriverMain() // 0x1dc4 -> 0x08004024 phantasy star
{
//// Usually addr 0x2010928
u32 const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30, //@+sp14
u32 user1 = CPUReadMemory(puser1);
if ( user1 != 0x68736D53 )
return;
// main
CPUWriteMemory(puser1, (++user1)); // this guard is common for funcs, unify
int const user2 = CPUReadMemory(puser1 + 0x20);
if ( user2 )
{
int const par1 = CPUReadMemory(puser1 + 0x24);
// Call 0x2102 sub_16A8 - -> param r1
}
int const userfunc = CPUReadMemory(puser1 + 0x28);
switch (userfunc)
{
case 0x1709: //phantasy star
default:
break;
}
u32 const v2 = CPUReadMemory(puser1 + 0x10); //r8
u8 const user4 = CPUReadByte(puser1+4) - 1;
u32 user41 = 0;
if ( user4 > 0 )
{
user41 = CPUReadByte(puser1 + 0x0B);
user41 -= user4;
user41 *= v2;
}
u32 r5;
u32 const r5c = puser1 + 0x350 + user41; //r5 @sp+8
int user6 = r5c + 0x630; //r6
int user5 = CPUReadByte(puser1 + 0x5); //r3
if ( user5 )
{
// @todo 0x1e1a
}
else // 0x1e74
{
r5 = r5c;
int count = v2>>4; //r1...v13
if ( !(v2 >> 3) )
{
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
}
if ( !(v2 >> 1) ) //0x1e7c
{
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
}
do // 0x1e8e
{
// @todo optimize this memset
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
CPUWriteMemory(r5, 0);
CPUWriteMemory(user6, 0);
r5+=4; user6+=4;
count -= 1;
} while (count > 0);
}
//0x1ea2
u32 r4 = puser1; // apparenty ch ptr?
int r9 = CPUReadMemory(r4+0x14);
int r12 = CPUReadMemory(r4+0x18);
u32 i = CPUReadByte(r4+0x6);
for (r4+=0x10; i > 0; i-- )
{
r4+=0x40;
/*lbl_0x1eb0:*/
u32 r3 = CPUReadMemory(r4+0x24);
u8 r6 = CPUReadByte(r4);
if ( (r6 & 0xC7) == 0 ) // 0x1ebc
continue; //goto lbl_20e4;
if ( (r6 & 0x80) && ((r6 & 0x40) == 0) ) // 0x1ec4
{
r6 = 0x3;
CPUWriteByte(r4, r6);
CPUWriteMemory(r4+0x28, r3+0x10);
int r0t1 = CPUReadMemory(r3+0xC);
CPUWriteMemory(r4+0x18, r0t1);
r5=0;
CPUWriteByte(r4+0x9, 0);
CPUWriteMemory(r4+0x1c, 0);
u8 r2a = CPUReadByte(r3+0x3); // seems to be LABEL_20e4
if ((r2a & 0xC0)) // 1ee4
{
}
goto lbl_0x1f46;
}
else
{
//lbl_0x1eee:
r5 = CPUReadByte(r4+0x9); //
if ( (r6 & 0x4) != 0) // @todo 0x1ef4
{
}
if ( (r6 & 0x40) != 0) // @todo 0x1f08
{
}
if ( (r6 & 0x03) == 2) // 0x1f2a
{
u8 mul1 = CPUReadByte(r4+0x5);
r5*=mul1;
r5>>=8;
u8 cmp1 = CPUReadByte(r4+0x6);
if (r5 <= cmp1)
{
r5=cmp1;
// @todo beq @ 0x1f3a -> ??
r6--;
CPUWriteByte(r4, r6);
}
}
else if ( (r6 & 0x03) == 3) // 0x1f44
{
lbl_0x1f46: //@todo check if there is really another path to here
u8 add1 = CPUReadByte(r4+0x4);
r5+=add1;
if (r5>=0xff)
{
r6--;
r5=0xff;
CPUWriteByte(r4, r6);
}
}
}
{
//lbl_0x1f54:
CPUWriteByte(r4+0x9, r5);
u32 user0 = CPUReadByte(puser1+0x7); // @sp+10
user0++;
user0*=r5;
r5=user0>>4;
user0 = CPUReadByte(r4+0x2);
user0*=r5;
user0>>=8;
CPUWriteByte(r4+0xA, user0);
user0 = CPUReadByte(r4+0x3);
user0*=r5;
user0>>=8;
CPUWriteByte(r4+0xB, user0);
user0 = r6&0x10;
if ( user0 != 0) // @todo 0x1f76
{
}
r5 = r5c; // @todo below r5 is used and updated
u32 r2 = CPUReadMemory(r4+0x18);
r3 = CPUReadMemory(r4+0x28);
u32 r8 = v2;
u8 r10 = CPUReadByte(r4+0xA);
u8 r11 = CPUReadByte(r4+0xB);
u8 r0a = CPUReadByte(r4+0x1);
if ((r0a & 8)) //@todo 0x1fa8
{
}
u32 r7 = CPUReadMemory(r4+0x1c);
u32 r14 = CPUReadMemory(r4+0x20);
lbl_0x2004:// LABEL_52:
while ( r7 >= 4 * r9 )
{
if ( r2 <= 4 )// @todo 0x2008, no phant
goto lbl_204c;
r2 -= 4;
r3 += 4;
r7 -= 4 * r9;
}
if ( r7 >= 2 * r9 )
{
if ( r2 <= 2 ) // @todo 0x2008, no phant
goto lbl_204c;
r2 -= 2;
r3 += 2;
r7 -= 2 * r9;
}
if ( r7 < r9 )
goto lbl_207c;
do
{
lbl_204c: // LABEL_59:
--r2;
if ( r2 )
{
++r3;
}
else
{
r2 = user0; //0x2054
if ( r2 )
{
r3 = CPUReadMemory(reg[13].I+0xC); // @todo stack pull 0x205c
}
else
{
CPUWriteByte(r4, r2);
goto lbl_20e4;
}
}
r7 -= r9;
} while ( r7 >= r9 );
lbl_207c:
while ( 1 )
{
s32 r0a = CPUReadByte(r3);
s32 r1a = CPUReadByte(r3+0x1);
r1a-=r0a;
s32 r6a = r1a*(s32)r7;
r1a = r6a * r12; // 208c
r6a = (r0a + ((s8)(r1a>>23)));
r1a = r6a * (s32)r11;
r0a = CPUReadByte(r5 + 0x630);
r0a = (r0a + ((s8)(r1a>>8)));
CPUWriteByte(r5 + 0x630, r0a);
r1a = r6a * (s32)r10;
r0a = CPUReadByte(r5);
r0a = (r0a + ((s8)(r1a>>8)));
CPUWriteByte(r5++, r0a); //ptr inc +1 not +4
r7+=r14;
--r8;
if ( !r8 )
break;
if ( r7 >= r9 )
goto lbl_0x2004;
}
CPUWriteMemory(r4+0x1c, r7);
//lbl_20cc:
CPUWriteMemory(r4+0x18, r2);
CPUWriteMemory(r4+0x28, r3);
}
lbl_20e4:
(void)r5;
}
// 0x20EE
CPUWriteMemory(puser1, 0x68736D53); // this guard is common for funcs, unify
}
// fully implemented @ 0x210c
void BIOS_SndDriverVSync()
{
u32 const puser1 = CPUReadMemory(0x3007FF0); // @todo some sound area, make it common.
u32 const user1 = CPUReadMemory(puser1);
if ( user1 == 0x68736D53 )
{
u8 v1 = CPUReadByte(puser1+4);
s8 v1i = v1-1;
CPUWriteByte(puser1+4, v1i);
if ( v1 <= 1 )
{
u8 v2 = CPUReadByte(puser1+0xB); //11
u32 base2 = 0x040000D2;
CPUWriteByte(puser1+4, v2);
CPUWriteHalfWord(base1+0x6 , 0);
CPUWriteHalfWord(base2, 0);
CPUWriteHalfWord(base1+0x6 , 0xB600);
CPUWriteHalfWord(base2, 0xB600); //-18944
}
}
}
void BIOS_SndDriverVSyncOff() // 0x1878
{
u32 const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30
u32 user1 = CPUReadMemory(puser1);
if ( user1 == 0x68736D53 || user1 == 0x68736D54 )
{
CPUWriteMemory(puser1, (++user1)); // this guard is common for funcs, unify
CPUWriteHalfWord(base1+0x6 , 0);
CPUWriteHalfWord(base1+0x12 , 0);
CPUWriteByte(puser1+4, 0);
u32 r1 = puser1+0x350;
u32 r2 = 0x05000318;
u32 sp = reg[13].I; //0x03003c94;
CPUWriteMemory(sp, 0);
BIOS_SndDriver_b4c(sp, r1, r2);
user1 = CPUReadMemory(puser1); // 0x18aa
CPUWriteMemory(puser1, (--user1)); // this ret is common among funcs
}
//0x18b0
}
// This functions is verified but lacks proper register settings before calling user func
// it might be that user func modifies or uses those?
// r7 should be puser1, r6 should be flags, ....
void BIOS_SndChannelClear() //0x1824
{
u32 const puser1 = CPUReadMemory(0x3007FF0); // 7FC0 + 0x30
u32 user1 = CPUReadMemory(puser1);
if ( user1 == 0x68736D53 )
{
CPUWriteMemory(puser1, (++user1));
u32 puser2 = puser1 + 0x7 + 0x49;
int count=12;
while (count--)
{
CPUWriteByte(puser2, 0);
puser2+=0x40;
}
reg[4].I = CPUReadMemory(puser1 + 0x1c); //r5 -> some user thing
if ( reg[4].I != 0 )
{
reg[3].I = 1; // r4 -> channel counter?
int puser4 = puser1 + 0x2c;
//reg[0].I = reg[3].I = 1; // r0 & r4 => 1
while (reg[3].I <= 4)
{
// @todo does user func modify these?
reg[0].I = reg[3].I << 24;
reg[0].I >>= 24;
// Get ptr to user func
reg[1].I = CPUReadMemory(puser4);
// @todo here we jump where r1 points; user func?
// @todo might modify r6 also?
// After call routines
reg[3].I += 1; // r4 -> channel counter?
reg[4].I += 0x40;// r5 -> some user thing
}
CPUWriteByte(reg[4].I, 0); // terminating record?
}
CPUWriteMemory(puser1, 0x68736D53);
}
}
void BIOS_MidiKey2Freq()
{
#ifdef GBA_LOGGING

View File

@ -25,5 +25,11 @@ extern void BIOS_SoftReset();
extern void BIOS_Sqrt();
extern void BIOS_MidiKey2Freq();
extern void BIOS_SndDriverJmpTableCopy();
extern void BIOS_SndDriverInit();
extern void BIOS_SndDriverMode();
extern void BIOS_SndDriverMain();
extern void BIOS_SndDriverVSync();
extern void BIOS_SndDriverVSyncOff();
extern void BIOS_SndChannelClear();
#endif // BIOS_H