diff --git a/src/gba/GBA.cpp b/src/gba/GBA.cpp index a3d7a48f..291020e4 100644 --- a/src/gba/GBA.cpp +++ b/src/gba/GBA.cpp @@ -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; diff --git a/src/gba/bios.cpp b/src/gba/bios.cpp index b03162b0..b0539eb2 100644 --- a/src/gba/bios.cpp +++ b/src/gba/bios.cpp @@ -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 diff --git a/src/gba/bios.h b/src/gba/bios.h index 68495cf4..35d88659 100644 --- a/src/gba/bios.h +++ b/src/gba/bios.h @@ -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