/* //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// All code is now wrapped in void Function() {} - the new DSP LLE debugger can parse this file and auto read symbols using those. BIG Questions: - Who resets the 0x0350 to the beginning of the command block? Wrap register? - What does 00eb_Unk_BufferMultAddToDest?? - Why is a PB-Transfer from RAM to DMEM 0xC0 shorts long but DMEM to RAM just 0x80 DSP functionality to test: - Interrupts (7) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */ // // memory map // todo - define syntax for this so it can be auto read. // 0x0000 to 0x280 // Unknown table 0x0280 // command queue /////////////////////////////////////////// 0x0300 to 0x0320 // AFC COEF table 0x0342 // low part of the DSetupTable /(Luigi2us) thinks it is the number of PBs 0x0343 // current command code (the code for the switch - casement table) seems to be for debug... nobody loads it 0x0344 // high part of the (command & 0xFF) ... prolly some kind of paramter 0x0345 // low part of the command 0x034e (0x0000) /////////////////////////////////////////// /////////////////////////////////////////// // Initialized at 0688_InitCommandBlock() 0x0350 (0x0280) // address to end of the command queue 0x0351 (0x0280) // address to start of the command queue 0x0352 (0x0000) // number of commands 0x0356[] // current command queue... eg: 0x81000040 for DsetupTable 0x0358 // first parameter of the command ... 0x0364 // Temp Buffer for AFC Decoder /////////////////////////////////////////// 0x0380 // RAM Address of the PBs 0x0381 // 0x038e // used by COMMAND_02 copies the low part of the command to this place 0x03a8 // COMMAND_02 copies a struct with the size of 0x40 to the place /////////////////////////////////////////// // used by 05b8_NewMail() exception to store register and restore registers 0x03fa to 0x03ff // shared and used by COMMAND_01 (DSetupTable) 0x03f0 // 0x00 0x03f1 // 0x96 0x03f2 // 0xFF 0x03f3 // 0x30 0x03f5 // 0x10 0x03f6 // 0x00 0x03f7 // 0x100 ... got some more math... have to recheck it 0x03f9 // used by COMMAND_02... if the program decides to continue the UCode it stores the continue address... ////////////////////////////////////////////// // current PB in DSyncFrame 0x0400 . . . 0x04C0 /// 0x0400 - must be != 0 to be a valid block 0x0401 - must be != 0 to be a valid block 0x0402 - Sample Decode Offset ?? Offset = (*0x402 * 0x50) 0x0404 - Initialize Sample Decoder 0x0406 - Direct Stream ... no sample decoder is needed. At the end of the PB Handling 0x0404 is set to 0 if it is NOT a direct stream 0x0430 - Base Address ?? (0x0430 + Offset) << 4 0x042c flag if > 0 033d_unk() does something 0x0433 stored to 0x03f8 ... i think that is the current sample buffer 0x0458 hmmm.... 0x0480 if 0x0406 is zero check how we have to decode this data... 0x0484 seems to contain filter settings... 0x0484 & 0x1f -> (02ed to 030f) 0x0484 & 0x20 0x04a8 if != zero sample is modified with function at 0x0c84.. perhaps an filter or volume //////////////////////////////////////// 0x04d3 "offset to sample data" ???? There's definitely a bunch of sample data stored in each PB but I don't know exactly how. /////////////////////////////////////////// // Initialized at 0e14_Unk() 0x04e8 (0x8240) 0x04e9 (0x7FFF) 0x04ea (0x7DBF) 0x04eb (0x843f) 0x04ec (0x0000) 0x04ed (0x0000) 0x04ee (0x0000) 0x04ef (0x0000) 0x04f0 (0xb23b) 0x04f1 (0x7FFF) 0x04f2 (0x4dc4) 0x04f3 (0xd808) 0x04f4 (0x0000) 0x04f5 (0x0000) 0x04f6 (0x0000) 0x04f7 (0x0000) /////////////////////////////////////////// 0x04fc... // used for some kind of data exchange between SyncFrame and MailExceptionHandler // It is like this: // (0x04fc + lastRenderedFrame) must be "something" :) 0x0580.. Unresampled audio data is decoded to here /////////////////////////////////////////// // Initialized at 04c0_Unk()... used by SyncFrame 0x0B00 to 0x0C00 // The memory at 0b00 seems to be generally used as scratch space for various things. ////////////////////////////////////////// 0x0dc0 -- ?????????? // // exception vector // 0000 029f 0012 jmp 0x0012 -> ResetVector() 0002 0000 nop 0003 0000 nop 0004 02ff rti 0005 0000 nop 0006 02ff rti 0007 0000 nop 0008 02ff rti 0009 0000 nop 000a 02ff rti 000b 0000 nop 000c 02ff rti 000d 0000 nop 000e 029f 05b8 jmp 0x05b8 -> 05b8_NewMail() 0010 029f 004e jmp 0x004e -> 004e_ContinueUCode() ??? // reset vector void 0012_ResetVector() { // 0012 1205 sbclr #0x05 // 0013 02bf 0057 call 0x0057 0057_InitHardware() /* 0015 8100 clr $ACC0 0016 009f 1000 lri $AC1.M, #0x1000 0018 0080 0000 lri $AR0, #0x0000 001a 005f loop $AC1.M 001b 1b1e srri @$AR0, $AC0.M Clear memory */ { short ACC0 = 0; short AR0 = 0x0000; for (int i=0; i<0x1000; i++) { *AR0 = ACC0; AR0++; } } // 001c 02bf 0688 call 0x0688 0688_InitCommandBlock(); // 001e 02bf 04c0 call 0x04c0 04c0_UnknownInit(); // 0020 02bf 0e14 call 0x0e14 0e14_DolbyInit(); // Init from 0x04e8 to 0x04f8 // 0022 0e00 lris $AC0.M, #0x00 // 0023 02bf 066a call 0x066a SendMB_DCD1(0x0000) // 0025 009e 1111 lri $AC0.M, #0x1111 // 0027 02bf 0674 call 0x0674 SendMB_F355(0x1111) // 0029 0e00 lris $AC0.M, #0x00 // 002a 00fe 034e sr @0x034e, $AC0.M *0x034e = 0x00 002c 1305 sbset #0x05 // 002d 029f 06c5 jmp 0x06c5 // // an exception will be raised if a new mail is inside the mailbox // the exception handler will copy the command to an address and the // 06c5_CopyCommandBlock creates a copy of the command to 0x0356 // :WaitForNewCommandBlock while (06c5_CopyCommandBlock() == 0x002d); // 002f 00df 0357 lr $AC1.M, @0x0357 // 0031 00ff 0345 sr @0x0345, $AC1.M *0x0345 = *0x0357 // 0033 00de 0356 lr $AC0.M, @0x0356 // 0035 1ffe mrr $AC1.M, $AC0.M // 0036 0340 00ff andi $AC1.M, #0x00ff // 0038 00ff 0344 sr @0x0344, $AC1.M short upperCommand = *0x0356 // AC0 = upperCommand *0x0344 = upperCommand & 0x00FF // 003a 1479 lsr $ACC0, #-7 // 003b 0240 007e andi $AC0.M, #0x007e // 003d 00fe 0343 sr @0x0343, $AC0.M upperCommand = (upperCommand >> 7) & 0x7e // F|RES: i think duddy is wrong here ... a negative must be a shift right *0x0343 = upperCommand // 003f 0200 0075 addi $AC0.M, #0x0075 // 0041 1c1e mrr $AR0, $AC0.M // 0042 170f jmpr $AR0 // switch casement of the commands.. jump table is at 0075 switch (upperCommand >> 1) // command must be shift >> 1 in our source code because the jump table is aligned with 2 Bytes { // case 0x00: 0x0043 case 0x01: 0095_COMMAND_01(); break; case 0x02: 0243_COMMAND_02(); break; case 0x03: 0x0073 case 0x04: 0095_COMMAND_04(); break; case 0x05: 0x0592 case 0x06: 0x0469 case 0x07: 0x041d case 0x08: 0x0485 case 0x09: 0x044d // case 0x0A: 0x0043 // case 0x0B: 0x0043 // case 0x0C: 0x0043 case 0x0D: 0x00b2 // case 0x0E: 0x0043 // case 0x0F: 0x0043 } # 0043 0092 00ff lri $CR, #0x00ff // 0045 0e04 lris $AC0.M, #0x04 // 0046 02bf 066a call 0x066a SendMB_DCD1(0x0004) // 0048 00de 0356 lr $AC0.M, @0x0356 // 004a 02bf 0674 call 0x0674 SendMB_F355(@0x0356) // 004c 029f 002d jmp 0x002d GOTO :WaitForNewCommandBlock } void 004e_ContinueUCode??() { # 004e 1205 sbclr #0x05 // 004f 02bf 0057 call 0x0057 0057_InitHardware() // 0051 0e01 lris $AC0.M, #0x01 // 0052 02bf 066a call 0x066a SendMB_DCD1(0x0001) # 0054 1305 sbset #0x05 // 0055 029f 002d jmp 0x002d GOTO :WaitForNewCommandBlock } void 0057_InitHardware() { 0057 1202 sbclr #0x02 0058 1203 sbclr #0x03 0059 1204 sbclr #0x04 005a 1306 sbset #0x06 005b 8e00 set16 005c 8c00 clr15 005d 8b00 m0 // Set all indexing wrappers to max range. 005e 009e ffff lri $AC0.M, #0xffff 0060 1d1e mrr $WR0, $AC0.M 0061 1d3e mrr $WR1, $AC0.M 0062 1d5e mrr $WR2, $AC0.M 0063 1d7e mrr $WR3, $AC0.M // Have CR point to the HW interface. 0064 0092 00ff lri $CR, #0x00ff // 0066 02df ret } void 0067_CopyCommand(_destAddr($AR0), _loopCount($AC0.M)) { // 0067 0090 0000 lri $AC0.H, #0x0000 // 0069 0c00 lris $AC0.L, #0x00 // 006a 0081 0358 lri $AR1, #0x0358 AC0.H = 0x0000 AC0.L = 0x00 AR1 = 0x0358 // 006c 007e 0071 bloop $AC0.M, 0x0071 // 006e 193e lrri $AC0.M, @$AR1 // 006f 1b1e srri @$AR0, $AC0.M // 0070 193e lrri $AC0.M, @$AR1 // 0071 1b1e srri @$AR0, $AC0.M for (int i=0; i<_loopCount; i++) { *_destAddr++ = *AR1.M++ *_destAddr++ = *AR1.M++ } // 0072 02df ret } /* 0073 029f 0043 jmp 0x0043 0075 029f 0043 jmp 0x0043 0077 029f 0095 jmp 0x0095 0079 029f 0243 jmp 0x0243 007b 029f 0073 jmp 0x0073 007d 029f 0580 jmp 0x0580 007f 029f 0592 jmp 0x0592 0081 029f 0469 jmp 0x0469 0083 029f 041d jmp 0x041d 0085 029f 0485 jmp 0x0485 0087 029f 044d jmp 0x044d 0089 029f 0043 jmp 0x0043 008b 029f 0043 jmp 0x0043 008d 029f 0043 jmp 0x0043 008f 029f 00b2 jmp 0x00b2 0091 029f 0043 jmp 0x0043 0093 029f 0043 jmp 0x0043 jump table for command code decoding */ // DsetupTable void 0095_COMMAND_01() { // 0095 0080 0380 lri $AR0, #0x0380 // 0097 0e04 lris $AC0.M, #0x04 // 0098 02bf 0067 call 0x0067 0067_CopyCommand(0x0380, 0x04) // 009a 0081 0382 lri $AR1, #0x0382 // 009c 009f 0000 lri $AC1.M, #0x0000 // 009e 0080 0280 lri $AR0, #0x0280 // 00a0 02bf 0523 call 0x0523 0523_CopyRAMtoDMEM(0x0382, 0x0000, 0x0280) // 00a2 0081 0384 lri $AR1, #0x0384 // 00a4 009f 0300 lri $AC1.M, #0x0300 // 00a6 0080 0020 lri $AR0, #0x0020 // 00a8 02bf 0523 call 0x0523 0523_CopyRAMtoDMEM(0x0384, 0x0300, 0x0020) // 00aa 00de 0345 lr $AC0.M, @0x0345 // 00ac 00fe 0342 sr @0x0342, $AC0.M *0x0342 = *0x0345 // 00ae 02bf 0bec call 0x0bec 0bec_Unk() // 00b0 029f 0043 jmp 0x0043 } // Command 0xD void 00b2_Unk_CommandD() { 00b2 0080 0374 lri $AR0, #0x0374 00b4 0e01 lris $AC0.M, #0x01 00b5 00fe 0377 sr @0x0377, $AC0.M 00b7 00fe 037c sr @0x037c, $AC0.M 00b9 02bf 0067 call 0x0067 // CopyCommand // 00bb 00de 0345 lr $AC0.M, @0x0345 // 00bd 00fe 0376 sr @0x0376, $AC0.M *0x0376 = *0x0345; 00bf 029f 0043 jmp 0x0043 } void 00c1_CopyPBToDMEM() { // 00c1 0081 034c lri $AR1, #0x034c // 00c3 009f 0400 lri $AC1.M, #0x0400 // 00c5 0080 00c0 lri $AR0, #0x00c0 // 00c7 02bf 0523 call 0x0523 // 00c9 02df ret 0523_CopyRAMtoDMEM(0x034c, 0x0400, 0x00c0); } void 00ca_CopyPBToRAM() { // 00ca 0081 034c lri $AR1, #0x034c // 00cc 009f 0400 lri $AC1.M, #0x0400 // 00ce 0080 0080 lri $AR0, #0x0080 // 00d0 0081 034c lri $AR1, #0x034c AR1 = 0x034c AC1.M = 0x0400 AR0 = 0x0080 // 00d2 193e lrri $AC0.M, @$AR1 // 00d3 193c lrri $AC0.L, @$AR1 AC0.M = *0x034c AC0.L = *0x034d // 00d4 0098 0000 lri $AX0.L, #0x0000 // 00d6 7000 addaxl $ACC0, $AX0.L // 00d7 02bf 0532 call 0x0532 0532_DMEMtoRAM(0x0400, ACC0, 0x0080) // 00d9 02df ret } void 00da_CopyBuffer(_src($AR0), _dest($AR3), LenMinusOne(_AC1.M)) { 00da 191e lrri $AC0.M, @$AR0 00db 191a lrri $AX0.H, @$AR0 00dc 005f loop $AC1.M 00dd 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 00de 1b7e srri @$AR3, $AC0.M 00df 1b7a srri @$AR3, $AX0.H // 00e0 02df ret } void 00e1_XorBuffer( .., _LenInDWORDs(_AC1.M)) { 00e1 191e lrri $AC0.M, @$AR0 00e2 191a lrri $AX0.H, @$AR0 // 00e3 007f 00e8 bloop $AC1.M, 0x00e8 for (int i = 0; i < AC1.M; i++) { 00e5 32b2 xorr'sl $AC0.M, $AX1.H : $AC0.M, $AX1.H 00e6 65a0 movr'ls $ACC1, $AX0.H : $AX0.H, $AC0.M 00e7 33ba xorr'slm $AC1.M, $AX1.H : $AC0.M, $AX1.H 00e8 64a1 movr'ls $ACC0, $AX0.H : $AX0.H, $AC1.M } // 00e9 0000 nop // 00ea 02df ret } // --- the disasm looks buggy... AR3 seems to be the destination but it isnt used at all... no idea // Hm, SL actually is able to use AR3 implicitly. void 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) { // 00eb 8a00 m2 // All multiplication results are multiplied by 2. // 00ec 157f lsr $ACC1, #-1 //_size/2 00ed 1c20 mrr $AR1, $AR0 00ee 1c03 mrr $AR0, $AR3 // 'sl stores to AR0 // SW pipelineing strikes again... 00ef 193a lrri $AX0.H, @$AR1 00f0 9051 mul'l $AX0.L, $AX0.H : $AX0.H, @$AR1 00f1 925b mulmvz'l $AX0.L, $AX0.H, $ACC0 : $AX1.H, @$AR3 // 00f2 007f 00f7 bloop $AC1.M, 0x00f7 for (int i = 0; i < _size/2; i++) { AX0.H = *AR1; PROD = * // 00f4 4651 addr'l $ACC0, $AX1.H : $AX0.H, @$AR1 // 00f5 92b2 mulmvz'sl $AX0.L, $AX0.H, $ACC0 : $AC0.M, $AX1.H // 00f6 4651 addr'l $ACC0, $AX1.H : $AX0.H, @$AR1 // 00f7 92b2 mulmvz'sl $AX0.L, $AX0.H, $ACC0 : $AC0.M, $AX1.H ACC0 += AX1 AX0.H = *AR1; *(AR3++) = AC0.M; ACC0 = PROD; PROD = AX0.L * AX0.H; ... } // In simplified form: AR1 = AR0; AR0 = AR3; for (int i = 0; i < _size; i++) { *AR0 = ((*AR0 << 16) + *AR1 * Factor) >> 16); } // 00f8 8b00 m0 // Restore multiplication results. // 00f9 02df ret } void 00fa_BufferMultiply(src($AR0), dst($AR3), count($AC1.M), $mult($AX0.L)) { //00fa 8a00 m2 00fb 191a lrri $AX0.H, @$AR0 00fc 9050 mul'l $AX0.L, $AX0.H : $AX0.H, @$AR0 00fd 9250 mulmvz'l $AX0.L, $AX0.H, $ACC0 : $AX0.H, @$AR0 00fe 005f loop $AC1.M 00ff 92a0 mulmvz'ls $AX0.L, $AX0.H, $ACC0 : $AX0.H, $AC0.M //0100 8b00 m0 //0101 02df ret } // Clears the 0d00 and 0d60 buffers, plus a lot of other intermediate buffers. // Also does some other things. void 0102_PrepareFrameBuffers() { // 0102 8100 clr $ACC0 // 0103 8900 clr $ACC1 // 0104 0e50 lris $AC0.M, #0x50 ACC0 = 0 ACC1 = 0 AC0.M = 0x50 // 0105 0080 0d00 lri $AR0, #0x0d00 // 0107 005e loop $AC0.M // 0108 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0d00++ = 0x00 // 0109 0080 0d60 lri $AR0, #0x0d60 // 010b 005e loop $AC0.M // 010c 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0d60++ = 0x00 // 10d 02bf 0e3f call 0x0e3f // This one adds and multiplies buffers together. Maybe surround or reverb stuff. 0e3f_DolbyMixdown() // 010f 8100 clr $ACC0 // 0110 8900 clr $ACC1 // 0111 0e50 lris $AC0.M, #0x50 ACC0 = 0 ACC1 = 0 AC0.M = 0x50 // 0112 0080 0ca0 lri $AR0, #0x0ca0 // 0114 005e loop $AC0.M // 0115 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0ca0++ = 0x00 // 0116 0080 0f40 lri $AR0, #0x0f40 // 0118 005e loop $AC0.M // 0119 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0f40++ = 0x00 // 011a 0080 0fa0 lri $AR0, #0x0fa0 // 011c 005e loop $AC0.M // 011d 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0fa0++ = 0x00 // 011e 0080 0a00 lri $AR0, #0x0a00 // 0120 005e loop $AC0.M // 0121 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x0a00++ = 0x00 // 0122 0080 09a0 lri $AR0, #0x09a0 // 0124 005e loop $AC0.M // 0125 1b1f srri @$AR0, $AC1.M for (int i=0; i<0x50; i++) *0x09a0++ = 0x00 // 0126 02df ret } void 0127_Unk() { 0127 00c0 03a0 lr $AR0, @0x03a0 0129 191a lrri $AX0.H, @$AR0 012a 00df 03a1 lr $AC1.M, @0x03a1 012c 009b 00a0 lri $AX1.H, #0x00a0 012e 0081 0393 lri $AR1, #0x0393 0130 18bc lrrd $AC0.L, @$AR1 0131 b871 mulx'l $AX0.H, $AX1.H : $AC0.M, @$AR1 0132 bc00 mulxac $AX0.H, $AX1.H, $ACC0 0133 0080 0050 lri $AR0, #0x0050 0135 0508 addis $ACC1, #0x08 0136 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM // 0525_CopyRAMtoDMEM(... ,.. , 0x50) // 0138 00de 0390 lr $AC0.M, @0x0390 // 013a 02a0 0001 andf $AC0.M, #0x0001 // 013c 029d 0145 jlz 0x0145 if (*0x0390 & 1) { 013e 0080 0398 lri $AR0, #0x0398 0140 0e08 lris $AC0.M, #0x08 0141 00c1 03a1 lr $AR1, @0x03a1 0143 02bf 0b2e call 0x0b2e // 0b2e_Unk_Multiply } 0145 0f50 lris $AC1.M, #0x50 0146 00c0 03a1 lr $AR0, @0x03a1 0148 00da 0394 lr $AX0.H, @0x0394 // 014a 8600 tstaxh $AX0.H // 014b 0295 0152 jz 0x0152 if (*0x0394 != 0) { 014d 1c7a mrr $AR3, $AX0.H 014e 00d8 0395 lr $AX0.L, @0x0395 0150 02bf 00eb call 0x00eb // 00eb_Unk_BufferMultAddToDest } 0152 0f50 lris $AC1.M, #0x50 0153 00c0 03a1 lr $AR0, @0x03a1 0155 00da 0396 lr $AX0.H, @0x0396 0157 8600 tstaxh $AX0.H 0158 0295 015f jz 0x015f 015a 1c7a mrr $AR3, $AX0.H 015b 00d8 0397 lr $AX0.L, @0x0397 015d 02bf 00eb call 0x00eb // 00eb_Unk_BufferMultAddToDest // 015f 00de 0390 lr $AC0.M, @0x0390 // 0161 02a0 0002 andf $AC0.M, #0x0002 // 0163 02dd retlz if (*0x390 & 2) return; 0164 0080 0398 lri $AR0, #0x0398 0166 0e08 lris $AC0.M, #0x08 0167 00c1 03a1 lr $AR1, @0x03a1 0169 02bf 0b2e call 0x0b2e // 0b2e_Unk_Multiply 016b 02df ret } // Looks similar to something else... void 016c_Unk_SetupMemAt_0c00() { 016c 8900 clr $ACC1 // 016d 009f 0dc0 lri $AC1.M, #0x0dc0 // 016f 00ff 03a1 sr @0x03a1, $AC1.M *0x03a1 = 0x0dc0; // 0171 009f 03a8 lri $AC1.M, #0x03a8 // 0173 00ff 03a2 sr @0x03a2, $AC1.M *0x03a2 = 0x03a8; // 0175 009f 03a4 lri $AC1.M, #0x03a4 // 0177 00ff 03a0 sr @0x03a0, $AC1.M *0x03a0 = 0x03a4; // Dangerous bloopi! It points to the SECOND HALF of a 2-word instruction so // a naive check won't catch it! I think our current code does, though. // 0179 1104 019f bloopi #0x04, 0x019f for (int i = 0; i < 4; i++) { // 017b 00c0 03a2 lr $AR0, @0x03a2 // 017d 0083 0390 lri $AR3, #0x0390 // 017f 0f0e lris $AC1.M, #0x0e // 0180 02bf 00da call 0x00da 00da_CopyBuffer(@0x03a2, 0x0390, 0x0e) 0182 00da 0390 lr $AX0.H, @0x0390 0184 8600 tstaxh $AX0.H 0185 0295 0191 jz 0x0191 if (*0x0390) { 0187 00df 03a1 lr $AC1.M, @0x03a1 0189 1c7f mrr $AR3, $AC1.M 018a 0550 addis $ACC1, #0x50 // 018b 1c1f mrr $AR0, $AC1.M // 018c 0f06 lris $AC1.M, #0x06 // 018d 02bf 00da call 0x00da 00da_CopyBuffer($AC1.M, $AR3, 0x06); // 018f 02bf 0127 call 0x0127 0127_Unk(); } // 0191 00de 03a2 lr $AC0.M, @0x03a2 // 0193 0410 addis $ACC0, #0x10 // 0194 00fe 03a2 sr @0x03a2, $AC0.M (*0x03a2) += 0x10; // 0196 00de 03a1 lr $AC0.M, @0x03a1 // 0198 0460 addis $ACC0, #0x60 // 0199 00fe 03a1 sr @0x03a1, $AC0.M (*0x03a1) += 0x60; // 019b 00de 03a0 lr $AC0.M, @0x03a0 // 019d 7400 incm $AC0.M // 019e 00fe 03a0 sr @0x03a0, $AC0.M (*0x0ea0)++; } // 01a0 0f50 lris $AC1.M, #0x50 // 01a1 0080 0c00 lri $AR0, #0x0c00 // 01a3 0083 0e80 lri $AR3, #0x0e80 // 01a5 0098 7fff lri $AX0.L, #0x7fff // 01a7 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src(0x0c00), _Dest(0x0e80), _size(0x50), _factor(0x7fff)) // 01a9 0f50 lris $AC1.M, #0x50 // 01aa 0080 0c00 lri $AR0, #0x0c00 // 01ac 0083 0ee0 lri $AR3, #0x0ee0 // 01ae 0098 b820 lri $AX0.L, #0xb820 // 01b0 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src(0x0c00), _Dest(0x0ee0), _size(0x50), _factor(0xb820)) // 01b2 0f28 lris $AC1.M, #0x28 // 01b3 0080 0c78 lri $AR0, #0x0c78 // 01b5 0083 0e80 lri $AR3, #0x0e80 // 01b7 0098 b820 lri $AX0.L, #0xb820 // 01b9 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src(0x0c78), _Dest(0x0e80), _size(0x28), _factor(0xb820)) // 01bb 0f28 lris $AC1.M, #0x28 // 01bc 0080 0c78 lri $AR0, #0x0c78 // 01be 0083 0ee0 lri $AR3, #0x0ee0 // 01c0 0098 7fff lri $AX0.L, #0x7fff // 01c2 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src(0x0c78), _Dest(0x0e80), _size(0x28), _factor(0x7fff)) // Zero the temporary buffers 0x0c00 and 0x0c50 01c4 8100 clr $ACC0 01c5 8900 clr $ACC1 01c6 0e50 lris $AC0.M, #0x50 01c7 0080 0c00 lri $AR0, #0x0c00 01c9 005e loop $AC0.M 01ca 1b1f srri @$AR0, $AC1.M 01cb 0080 0c50 lri $AR0, #0x0c50 01cd 005e loop $AC0.M 01ce 1b1f srri @$AR0, $AC1.M // 01cf 02df ret } void 01d0_Unk() { // 01d0 00c0 03a0 lr $AR0, @0x03a0 // 01d2 181a lrr $AX0.H, @$AR0 AX0.H = *0x03a0; // 01d3 8100 clr $ACC0 // 01d4 181e lrr $AC0.M, @$AR0 AC0.M = *0x03a0; // 01d5 00db 0391 lr $AX1.H, @0x0391 AX1.H = *0x0391; // 01d7 7400 incm $AC0.M AC0.M++; // 01d8 d100 cmpar $ACC1, $AX0.H // 01d9 0270 ifge if (ACC1 - AX0.H >= 0) { 01da 8100 clr $ACC0 } 01db 1b1e srri @$AR0, $AC0.M 01dc 00df 03a1 lr $AC1.M, @0x03a1 01de 009b 00a0 lri $AX1.H, #0x00a0 01e0 0081 0393 lri $AR1, #0x0393 01e2 18bc lrrd $AC0.L, @$AR1 01e3 b871 mulx'l $AX0.H, $AX1.H : $AC0.M, @$AR1 01e4 bc00 mulxac $AX0.H, $AX1.H, $ACC0 // 01e5 0080 0050 lri $AR0, #0x0050 // 01e7 02bf 0532 call 0x0532 0532_DMEMtoRAM(_DMEM(AC1.M), _pMemAddr(ACC0), 0x50) 01e9 02df ret } void 01ea_Unk() { 01ea 8900 clr $ACC1 // 01eb 0f28 lris $AC1.M, #0x28 // half of 0x50 // 01ec 0080 0c50 lri $AR0, #0x0c50 // 01ee 0083 0ea8 lri $AR3, #0x0ea8 // 01f0 0098 b820 lri $AX0.L, #0xb820 // 01f2 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) 01f4 8900 clr $ACC1 // 01f5 0f28 lris $AC1.M, #0x28 // 01f6 0080 0c50 lri $AR0, #0x0c50 // 01f8 0083 0f08 lri $AR3, #0x0f08 // 01fa 0098 7fff lri $AX0.L, #0x7fff // 01fc 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) // 01fe 009f 0dc0 lri $AC1.M, #0x0dc0 // 0200 00ff 03a1 sr @0x03a1, $AC1.M // 0202 009f 03a8 lri $AC1.M, #0x03a8 // 0204 00ff 03a2 sr @0x03a2, $AC1.M // 0206 009f 03a4 lri $AC1.M, #0x03a4 // 0208 00ff 03a0 sr @0x03a0, $AC1.M (*0x03a1) = 0x0dc0; (*0x03a2) = 0x03a8; (*0x03a0) = 0x03a4; // Dangerous bloopi! It points to the SECOND HALF of a 2-word instruction so // a naive check won't catch it! I think our current code does, though. // 020a 1104 0228 bloopi #0x04, 0x0228 for (int i = 0; i < 4; i++) { // 020c 00c0 03a2 lr $AR0, @0x03a2 // 020e 0083 0390 lri $AR3, #0x0390 // 0210 0f0e lris $AC1.M, #0x0e // 0211 02bf 00da call 0x00da 00da_CopyBuffer(_src(*0x03a2), _dest(0x0390), _LenInDWORDs(0xE)) // 0213 00da 0390 lr $AX0.H, @0x0390 // 0215 8600 tstaxh $AX0.H // 0216 0295 021a jz 0x021a if (*0x0390) { // 0218 02bf 01d0 call 0x01d0 // Copy some buffer to RAM? 01d0_Unk(); } // 021a 00de 03a2 lr $AC0.M, @0x03a2 // 021c 0410 addis $ACC0, #0x10 // 021d 00fe 03a2 sr @0x03a2, $AC0.M (*0x03a2) += 0x10; // 021f 00de 03a1 lr $AC0.M, @0x03a1 // 0221 0460 addis $ACC0, #0x60 // 0222 00fe 03a1 sr @0x03a1, $AC0.M (*0x03a1) += 0x60; // 0224 00de 03a0 lr $AC0.M, @0x03a0 // 0226 7400 incm $AC0.M // 0227 00fe 03a0 sr @0x03a0, $AC0.M (*0x03a0)++; } 0229 02df ret } void 022a_Copy_XXX_From_RAM_To_0x03a8() { // 022a 0081 0386 lri $AR1, #0x0386 // 022c 009f 03a8 lri $AC1.M, #0x03a8 // 022e 0080 0040 lri $AR0, #0x0040 // 0230 02bf 0523 call 0x0523 0523_CopyRAMtoDMEM(0x0386, 0x03a8, 0x0040); // 0232 02df ret } void 0233_Increase_32BitAddress_InMem(_MemAddr(AR0), _Bytes(AX0.L)) { // 0233 191e lrri $AC0.M, @$AR0 // 0234 189c lrrd $AC0.L, @$AR0 // 0235 4800 addax $ACC0, $AX0 // 0236 1b1e srri @$AR0, $AC0.M // 0237 1b1c srri @$AR0, $AC0.L *((u32*)AR0) += AX0 // 0238 02df ret } void 0239_WaitUntilLastFrameGotSynced() { // 0239 8100 clr $ACC0 // 023a 8900 clr $ACC1 // 023b 00df 0354 lr $AC1.M, @0x0354 // 023d 00de 034e lr $AC0.M, @0x034e // 023f 8200 cmp // 0240 0293 0239 jle 0x0239 // loop do { } while (*0x0354 > *0x034e); // TODO: CHECK // 0242 02df ret } ///////////////////////// // // // 0x0341: Number of Frames to render ... // 0x034c + 0x034d: RAM address of the current PB block // 0x034e: Last Sync message for rendered frame // 0x0354: PB loop counter // 0x0355: Current Frame // 0x0380: ??? // 0x0381: ??? // 0x0388: RAM Address of Output Buffer1 // 0x038a: RAM Address of Output Buffer2 // // 0x038f: Output Buffer Address (0x0520 most of the time) // // 0x03f8: *0x0433 // 0x0520: Some kind of sample buffer // 0x0d00: Left mix buffer // 0x0d60: Right mix buffer void 0243_COMMAND_02() // sync frame { // 0243 0080 0388 lri $AR0, #0x0388 // 0245 0081 0067 lri $AR1, #0x0067 // 0247 0e02 lris $AC0.M, #0x02 // 0248 173f callr $AR1 0067_CopyCommand(0x0388, 0x02); // 0249 00de 0344 lr $AC0.M, @0x0344 // 024b 00fe 0341 sr @0x0341, $AC0.M *0x0341 = *0x0344; // 024d 00de 0345 lr $AC0.M, @0x0345 // 024f 00fe 038e sr @0x038e, $AC0.M *0x038e = *0x0345; // 0251 8100 clr $ACC0 // 0252 00fe 0355 sr @0x0355, $AC0.M *0x0355 = 0x00; // 0254 02bf 022a call 0x022a 022a_Copy_XXX_From_RAM_To_0x03a8(); // perhaps an PB ?? // 0256 02bf 05a4 call 0x05a4 05A4_ResetAccelerator(); // 0258 00de 0341 lr $AC0.M, @0x0341 // 025a 007e 0418 bloop $AC0.M, 0x0418 // Frame size is 0xa0 * *0x0341 for (int j=0; j< *0x0341; j++) // 0x0341 = high part of the (command & 0xFF) ... perhaps number of frames to render?? { // 025c 02bf 0102 call 0x0102 0102_PrepareFrameBuffers(); // 025e 02bf 016c call 0x016c 016c_Unk_SetupMemAt_0c00(); // 0260 02bf 095f call 0x095f 095f_Unk_SetupMemAt0_0180(); // 0262 00de 0355 lr $AC0.M, @0x0355 // 0264 7400 incm $AC0.M // 0265 00fe 0355 sr @0x0355, $AC0.M (*0x0355)++; // 0267 8100 clr $ACC0 // 0268 00fe 0354 sr @0x0354, $AC0.M *0x0354 = 0; // PB counter // 026a 00de 0342 lr $AC0.M, @0x0342 // 026c 007e 03c0 bloop $AC0.M, 0x03c0 for (int i=0; i<*0x0342; i++) // 0x0342 - low part of the DSetupTable Command. Number of PBs? { // 026e 02bf 0239 call 0x0239 0239_WaitUntilLastFrameGotSynced(); // 0270 8100 clr $ACC0 // 0271 8900 clr $ACC1 ACC0 = 0; ACC1 = 0; // this block is for masking out PBs... the lower part of the sync messages are updating this mask // but i am not 100 percent sure how it works { // 0272 00de 0354 lr $AC0.M, @0x0354 // 0274 147c lsr $ACC0, #-4 // 0275 0200 04fc addi $AC0.M, #0x04fc // 0277 1c1e mrr $AR0, $AC0.M AC0.M = *0x0354 >> 4; AR0 = AC0.M + 0x04fc; 0278 181f lrr $AC1.M, @$AR0 0279 00de 0354 lr $AC0.M, @0x0354 027b 0240 000f andi $AC0.M, #0x000f // 027d 3d80 lsrnr ACC1 <<= ACC0 027e 03c0 8000 andcf $AC1.M, #0x8000 // 0280 029c 03bc jlnz 0x03bc GOTO NEXT_BLOCK: } // 0282 00d8 0354 lr $AX0.L, @0x0354 // 0284 009a 0180 lri $AX0.H, #0x0180 // 0286 8100 clr $ACC0 // 0287 00de 0380 lr $AC0.M, @0x0380 // 0289 00dc 0381 lr $AC0.L, @0x0381 // 028b 9000 mul $AX0.L, $AX0.H // 028c 9400 mulac $AX0.L, $AX0.H, $ACC0 // 028d 00fe 034c sr @0x034c, $AC0.M // 028f 00fc 034d sr @0x034d, $AC0.L AX0.L = *0x0354 // number of rendered frames AX0.H = 0x0180 // PB Size with dummy buffer ACC0 = (*0x0380 << 16) | *0x0381 ACC0 += AX0.L * AX0.H // Compute the RAM address of the current PB. *0x034C = AC0.M *0x034D = AC0.L // Copy the current PB to 0x400, so we can access it from DSP code. // 0291 02bf 00c1 call 0x00c1 00c1_CopyPBToDMEM() // 0293 00da 0400 lr $AX0.H, @0x0400 // 0295 8600 tstaxh $AX0.H // 0296 0295 03bc jz 0x03bc if (*0x0400 == 0x00) GOTO NEXT_BLOCK: // 0298 00da 0401 lr $AX0.H, @0x0401 // 029a 8600 tstaxh $AX0.H // 029b 0294 03bc jnz 0x03bc if (*0x0401 != 0x00) GOTO NEXT_BLOCK: // 029d 00da 0433 lr $AX0.H, @0x0433 // 029f 00fa 03f8 sr @0x03f8, $AX0.H *0x03f8 = *0x0433 // 02a1 00da 0406 lr $AX0.H, @0x0406 // 02a3 8600 tstaxh $AX0.H // 02a4 0294 0dff jnz 0x0dff if (*0x0406 != 0x00) { // The Code at 0x0dff sets the value from *0x0433 to 0x50 shorts at 0x0520. // Then it JMPs to ContinueWithBlock. 0dff_Zero520_50(). } else { // 02a6 8100 clr $ACC0 // 02a7 00de 0480 lr $AC0.M, @0x0480 // 02a9 0609 cmpis $ACC0, #0x09 // 02aa 0295 02bd jz 0x02bd // 02ac 0605 cmpis $ACC0, #0x05 // 02ad 0295 02bd jz 0x02bd // 02af 0608 cmpis $ACC0, #0x08 // 02b0 0295 098f jz 0x098f // 02b2 0610 cmpis $ACC0, #0x10 // 02b3 0295 0a14 jz 0x0a14 // 02b5 0620 cmpis $ACC0, #0x20 // 02b6 0295 0a9a jz 0x0a9a // 02b8 0621 cmpis $ACC0, #0x21 // 02b9 0295 0aa2 jz 0x0aa2 // 02bb 029f 087c jmp 0x087c switch(*0x0480) { case 0x05: case 0x09: GOTO 02BD: case 0x08: GOTO 098f: case 0x10: GOTO 0a14: case 0x20: GOTO 0a9a: case 0x21: GOTO 0aa2: default: GOTO 087C: } // This is the common decoding prep for 0x05 and 0x09. // 02bd 00d8 0402 lr $AX0.L, @0x0402 // delta? // 02bf 8100 clr $ACC0 // 02c0 8900 clr $ACC1 // 02c1 00dc 0430 lr $AC0.L, @0x0430 // current fraction? // 02c3 8d00 set15 // unsigned multiplication // 02c4 0950 lris $AX1.L, #0x50 // 02c5 a000 mulx $AX0.L, $AX1.L // 02c6 a400 mulxac $AX0.L, $AX1.L, $ACC0 // 02c7 1404 lsl $ACC0, #4 // 02c8 8c00 clr15 // 0x0402 is delta ("Ratio"). // Is this a computation of the starting point for decoding? // If so 0x430 is the current sample position fraction and AX0.L = *0x0402 ACC0 = *0x430 + (AX0.L * 0x50) ACC0 >>= 4 // 02c9 1ffe mrr $AC1.M, $AC0.M // 02ca 0083 0580 lri $AR3, #0x0580 // 02cc 02bf 073d call 0x073d // AC1.M here is ACC0.M! Effectively a shift right 16. The above fraction stuff seems to make sense. 073d_DECODE_0x05_0x09(0x0580, AC1.M, 0x50) // NOP jump here. // 02ce 029f 02d0 jmp 0x02d0 Resample_From0580To0520: // 02d0 0080 0580 lri $AR0, #0x0580 // 02d2 0081 0520 lri $AR1, #0x0520 // 02d4 0099 0000 lri $AX1.L, #0x0000 // 02d6 02bf 0d7f call 0x0d7f 0d7f_ResampleAudioData(0x0580, 0x0520, 0x0000); } // A block of audio is now present at 0x520. ContinueWithBlock: // Apply various per-voice effects. // First up, a trivial in-place filter, if $0x04a8 is set. // 02d8 00da 04a8 lr $AX0.H, @0x04a8 // 02da 8600 tstaxh $AX0.H // 02db 0295 02e1 jz 0x02e1 // 02dd 0080 0520 lri $AR0, #0x0520 // 02df 02bf 0c84 call 0x0c84 if (0x04a8 != 0) void 0c84_FilterBufferInPlace(_sampleAddr($AR0), multiplier($AX0.H)) // 02e1 009e 0520 lri $AC0.M, #0x0520 // 02e3 00fe 038f sr @0x038f, $AC0.M *0x038f = 0x0520 // 02e5 8900 clr $ACC1 // 02e6 00df 0484 lr $AC1.M, @0x0484 // 02e8 0340 001f andi $AC1.M, #0x001f // 02ea b900 tst $ACC1 // 02eb 0295 0311 jz 0x0311 if ((*0x0484 & 0x1f) != 0x00) { // 02ed 00de 038f lr $AC0.M, @0x038f // 02ef 5c00 sub $ACC0, $ACC1 // 02f0 00fe 038f sr @0x038f, $AC0.M (*0x038f) -= AC1.M; // 02f2 1c7e mrr $AR3, $AC0.M // 02f3 0080 0440 lri $AR0, #0x0440 // 02f5 05fe addis $ACC1, #0xfe // 02f6 02bf 00da call 0x00da 00da_CopyBuffer(0x0440, (*0x038f), (*0x0484) + 0xfe) // 02f8 0080 0490 lri $AR0, #0x0490 // 02fa 00c1 038f lr $AR1, @0x038f // 02fc 8900 clr $ACC1 // 02fd 00df 0484 lr $AC1.M, @0x0484 // 02ff 0340 001f andi $AC1.M, #0x001f // 0301 02bf 0b4d call 0x0b4d 0b4d_IIR_Filter(In(0x0490), Out(*0x038f), FilterLength(*0x0484 & 0x1f)) 0303 00de 038f lr $AC0.M, @0x038f 0305 0450 addis $ACC0, #0x50 0306 1c1e mrr $AR0, $AC0.M // 0307 0083 0440 lri $AR3, #0x0440 0309 8900 clr $ACC1 030a 00df 0484 lr $AC1.M, @0x0484 030c 0340 001f andi $AC1.M, #0x001f 030e 05fe addis $ACC1, #0xfe // 030f 02bf 00da call 0x00da 00da_CopyBuffer(, 0x0440) } // 0311 00de 0484 lr $AC0.M, @0x0484 // 0313 0240 0020 andi $AC0.M, #0x0020 // 0315 0295 0333 jz 0x0333 if ((*0x0484 & 0x0020) != 0) { 0317 0080 04a4 lri $AR0, #0x04a4 0319 00c1 038f lr $AR1, @0x038f 031b 0082 0454 lri $AR2, #0x0454 031d 0083 04a7 lri $AR3, #0x04a7 // 031f 18fa lrrd $AX0.H, @$AR3 // 0320 8600 tstaxh $AX0.H // 0321 0294 0331 jnz 0x0331 if (!*0x04a7) { // 0323 18fa lrrd $AX0.H, @$AR3 // 0324 8600 tstaxh $AX0.H // 0325 0294 0331 jnz 0x0331 if (!*0x04a6) { // 0327 18fa lrrd $AX0.H, @$AR3 // 0328 8600 tstaxh $AX0.H // 0329 0294 0331 jnz 0x0331 if (!*0x04a5) { // 032b 8100 clr $ACC0 // 032c 18fe lrrd $AC0.M, @$AR3 // 032d 0280 7fff cmpi $AC0.M, #0x7fff // 032f 0295 0333 jz 0x0333 if (*0x04a4 != 0x7FFF) { // 0331 02bf 0b68 call 0x0b68 0b68_4TapFIR(InBuffer($AR2), FilterBuffer($AR0), OutBuffer($AR1)); } } } } } // Check volume mode, apply volume as appropriate // 0333 8100 clr $ACC0 // 0334 00de 042c lr $AC0.M, @0x042c // 0336 b100 tst $ACC0 // 0337 0295 033d jz 0x033d if (*0x042c != 0) // Volume mode != 0 { // 0339 02bf 0cd3 call 0x0cd3 // 033b 029f 03b2 jmp 0x03b2 0cd3_VolumeMixer1() // The complex one, probably with surround and stuff } else { // Volume mode == 0 - simple(r) volumes // 033d 8100 clr $ACC0 // 033e 1c9e mrr $IX0, $AC0.M // 033f 1cde mrr $IX2, $AC0.M // 0340 7400 incm $AC0.M // 0341 1cfe mrr $IX3, $AC0.M // 0342 8100 clr $ACC0 $IX0 = 0; $IX2 = 0; $IX3 = 1; // 0343 00de 0407 lr $AC0.M, @0x0407 // 0345 b100 tst $ACC0 // 0346 0295 0355 jz 0x0355 if (*0x0407 != 0) // Unknown, in zelda always 0x10, apparently. { // Seems like this all boils down to a backwards copy of // 0x0470-0x0477 to *(*(0x038f)); // Is that where we save samples in the PB, so that filters // have something to read from at the start of each block? // 0348 00c3 038f lr $AR3, @0x038f // 034a 0007 dar $AR3 $AR3 = *(0x038f) - 1; // 034b 0080 0477 lri $AR0, #0x0477 // 034d 0084 ffff lri $IX0, #0xffff // 034f 0087 ffff lri $IX3, #0xffff $AR0 = 0x477; $IX0 = -1; $IX3 = -1; // 0351 199a lrrn $AX0.H, @$AR0 AX0.H = *$AR0; AR0 += IX0; // 0352 6554 movr'ln $ACC1, $AX0.H : $AX0.H, @$AR0 $ACC1 = $AX0.H; $AX0.H = *$AR0; $AR0 += IX0; // 0353 005e loop $AC0.M for (int i = 0; i < AC0.M; i++) { 0354 65ad movr'lsnm $ACC1, $AX0.H : $AX0.H, $AC1.M } } // 0355 00da 0485 lr $AX0.H, @0x0485 // 0357 8600 tstaxh $AX0.H // 0358 0295 036b jz 0x036b if (*0x0485 != 0) { 035a 8900 clr $ACC1 035b 0086 0005 lri $IX2, #0x0005 // 5 - 1 = 4, see loop 035d 0082 040a lri $AR2, #0x040a // 035f 1106 0363 bloopi #0x06, 0x0363 // Store half of every 4th value from 0x040a onwards in the position before. (!!!!) // This really doesn't make a lot of sense. // At the same time, keep their sum in ACC1. for (int i = 0; i < 0x6; i++) { // 0361 18de lrrd $AC0.M, @$AR2 // 0362 147f lsr $ACC0, #-1 // 0363 4d36 add'sn $ACC1, $ACC0 : @$AR2, $AC0.M $AC0.M = *$AR2 >> 1; $AR2--; $ACC1 += $ACC0; *$AR2 = $ACC0; $AR2 += 5; } // 0364 b900 tst $ACC1 // 0365 0294 036b jnz 0x036b // Volume has dropped to 0 on all channels, stop sound? if (sum == 0) { // 0367 009a 0001 lri $AX0.H, #0x0001 // 0369 00fa 0401 sr @0x0401, $AX0.H // Write 1 to KeyOff. } } // 036b 8f00 set40 // 036c 0086 0002 lri $IX2, #0x0002 // 036e 0082 0408 lri $AR2, #0x0408 $IX2 = 0x0002; $AR2 = 0x0408; // Volume data starts at 0x0408, it's like this: // 1 word controls sbset #0x00 apparently // 2 volume values // 1 other word. // 0370 1106 039b bloopi #0x06, 0x039b for (int i = 0; i < 6; i++) { // 0372 8100 clr $ACC0 // 0373 195e lrri $AC0.M, @$AR2 // 0374 1200 sbclr #0x00 // W T F??? // 0375 b100 tst $ACC0 // 0376 0275 ifz // 0377 1300 sbset #0x00 // sbset #0x00 is logic zero ... we use it to store a bit here. see 0394 if (*$AR2 == 0) { sbset #0x00 } else { sbclr #0x00 } 0378 1c7e mrr $AR3, $AC0.M 0379 195e lrri $AC0.M, @$AR2 // Load the two volume values 037a 195f lrri $AC1.M, @$AR2 // 037b 5c00 sub $ACC0, $ACC1 // Subtract them - find volume delta? // 037c 14fb asr $ACC0, #-5 // 037d 1f5e mrr $AX0.H, $AC0.M // 037e 1f1c mrr $AX0.L, $AC0.L $AX0 = (vol1 - vol2) >> 5; // 32 steps .. // Read the value after the volumes. // 037f 185e lrr $AC0.M, @$AR2 // 0380 0240 00ff andi $AC0.M, #0x00ff // 0382 1f7e mrr $AX1.H, $AC0.M $AX1.H = *$AR2 & 0xFF; // 0383 185e lrr $AC0.M, @$AR2 // 0384 1478 lsr $ACC0, #-8 // 0385 009c 0000 lri $AC0.L, #0x0000 $AC0.M = *$AR2 >> 8; // ACC1 is here the second volume. Compare to delta. // Adjust *$AR2 for some reason accordingly... // 0387 d100 cmpar $ACC1, $AX0.H // 0388 0295 0390 jz 0x0390 // 038a 185e lrr $AC0.M, @$AR2 // 038b 0272 ifg // 038c 7400 incm $AC0.M // 038d 0271 ifl // 038e 7800 decm $AC0.M // 038f 1a5e srr @$AR2, $AC0.M if ($ACC1 < $AX0.H) { (*$AR2)--; } else if ($ACC1 > $AX0.H) { (*$AR2)++ } // 0390 0006 dar $AR2 $AR2--; // $AR2 again points at the second volume. 0391 00de 038f lr $AC0.M, @0x038f // Per channel mini-delay? 0393 5600 subr $ACC0, $AX1.H // see 0382 // Use that stored logic zero bit, to skip mixing if the first word is (or isn't? not sure) 0. // 0394 029d 0399 jlz 0x0399 if (!logic zero) { // 0396 1c1e mrr $AR0, $AC0.M // 0397 02bf 0ca9 call 0x0ca9 0ca9_RampedMultiplyAddBuffer(Volume($ACC1), VolumeDelta($AX0), MultiplierData($AR0), Buffer($AR3)) } // 0399 0000 nop // 039a 1b5f srri @$AR2, $AC1.M // Update the second volume. *($AR2++) = $AC1.M; // 039b 000a iar $AR2 $AR2++; // Next block of four values. } // 039c 8e00 set16 // 039d 8100 clr $ACC0 // 039e 00de 0407 lr $AC0.M, @0x0407 // 03a0 b100 tst $ACC0 // 03a1 0295 03b2 jz 0x03b2 if (*0x0407 != 0) { // Stash away the last bunch of samples into 0x0477 and backwards, // so that filter kernels and resampler have some previous data to // read from the next time. 03a3 00c3 038f lr $AR3, @0x038f 03a5 0087 004f lri $IX3, #0x004f 03a7 001f addarn $AR3, $IX3 03a8 0080 0477 lri $AR0, #0x0477 03aa 0084 ffff lri $IX0, #0xffff 03ac 0087 ffff lri $IX3, #0xffff 03ae 19fa lrrn $AX0.H, @$AR3 03af 6557 movr'ln $ACC1, $AX0.H : $AX0.H, @$AR3 03b0 005e loop $AC0.M 03b1 65af movr'slnm $ACC1, $AX0.H : $AC1.M, $AX0.H } } // 03b2 00da 0406 lr $AX0.H, @0x0406 // 03b4 8600 tstaxh $AX0.H // 03b5 0294 03ba jnz 0x03ba if (*0x0406 == 0) { // 03b7 8100 clr $ACC0 // 03b8 00fe 0404 sr @0x0404, $AC0.M *0x0404 = 0x0000 } // 03ba 02bf 00ca call 0x00ca 00ca_CopyPBToRAM() NEXT_BLOCK: 03bc 00de 0354 lr $AC0.M, @0x0354 03be 7400 incm $AC0.M 03bf 00fe 0354 sr @0x0354, $AC0.M } // Done mixing all voices, sync up with host before the final mixdown. // 03c1 0e00 lris $AC0.M, #0x00 // 03c2 00fe 034e sr @0x034e, $AC0.M *0x034e = 0x00 // 03c4 0e04 lris $AC0.M, #0x04 // 03c5 02bf 066a call 0x066a SendMB_DCD1(0x0004) // 03c7 00de 0355 lr $AC0.M, @0x0355 // 03c9 0260 ff00 ori $AC0.M, #0xff00 // 03cb 02bf 0674 call 0x0674 SendMB_F355(*0x0355 | 0xFF00) // *0x0355 - current frame // Buffer 0D00 and 0D60 are the final L & R mixing buffers. // This is where global effects are applied, and final mixdown is done. // 03cd 02bf 0c0a call 0x0c0a 0c0a_Unk() // Copy 0a00 to 0a60? // 03cf 02bf 0c1c call 0x0c1c 0c1c_ComputeReverbFrom0a60To0a00() // Not sure if this really is reverb but could be. // 03d1 02bf 0c71 call 0x0c71 0c71_AddBufferA00ToD60AndD00(); // add A00 on top of 0x0D00 and 0x0D60 // 03d3 00de 0341 lr $AC0.M, @0x0341 // 03d5 7800 decm $AC0.M // 03d6 00fe 0341 sr @0x0341, $AC0.M (*0x0341)--; // The audio at 09a0 is added to both channels, // then the channel buffers are copied to RAM. // For unknown reasons, the audio at 0x0fa0 is ONLY added to the right channel. // 03d8 0080 09a0 lri $AR0, #0x09a0 // 03da 0083 0d00 lri $AR3, #0x0d00 // 03dc 0f50 lris $AC1.M, #0x50 // 03dd 0098 5a82 lri $AX0.L, #0x5a82 // 03df 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(0x09a0, 0x0d00, 0x50, 0x5a82) // 03e1 0080 09a0 lri $AR0, #0x09a0 // 03e3 0083 0d60 lri $AR3, #0x0d60 // 03e5 0f50 lris $AC1.M, #0x50 // 03e6 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(0x09a0, 0x0d60, 0x50, 0x5a82) // 03e8 0083 0d00 lri $AR3, #0x0d00 // 03ea 02bf 0cc1 call 0x0cc1 0cc1_StrangeORRFilter(0x0d00) // 03ec 0081 0388 lri $AR1, #0x0388 // 03ee 009f 0d00 lri $AC1.M, #0x0d00 // 03f0 0080 0050 lri $AR0, #0x0050 // 03f2 02bf 0530 call 0x0530 0530_DMEMtoRAM_Ind(0x0d00, 0x0388, 0x050) // 03f4 0080 0fa0 lri $AR0, #0x0fa0 // 03f6 0083 0d60 lri $AR3, #0x0d60 // 03f8 0f50 lris $AC1.M, #0x50 // 03f9 0098 8000 lri $AX0.L, #0x8000 // 03fb 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(0x0fa0, 0x0d60, 0x50, 0x8000) // 03fd 0083 0d60 lri $AR3, #0x0d60 // 03ff 02bf 0cc1 call 0x0cc1 0cc1_StrangeORRFilter(0x0d60) // 0401 0081 038a lri $AR1, #0x038a // 0403 009f 0d60 lri $AC1.M, #0x0d60 // 0405 0080 0050 lri $AR0, #0x0050 // 0407 02bf 0530 call 0x0530 0530_DMEMtoRAM_Ind(0x0d60, 0x038a, 0x050) // Move both RAM output buffer pointers forward, 0xa0 bytes (0x50 samples). // 0409 009a 0000 lri $AX0.H, #0x0000 // 040b 0098 00a0 lri $AX0.L, #0x00a0 // 040d 0080 0388 lri $AR0, #0x0388 // 040f 02bf 0233 call 0x0233 0233_Increase_32BitAddress_InMem(0x0388, 0xa0) // 0411 0080 038a lri $AR0, #0x038a // 0413 02bf 0233 call 0x0233 0233_Increase_32BitAddress_InMem(0x038a, 0xa0) // 0415 02bf 01ea call 0x01ea 01ea_Unk(); 0417 0000 nop 0418 0000 nop } // 0419 0080 002d lri $AR0, #0x002d // 041b 029f 0603 jmp 0x0603 0603_FinalizeFrame(0x02d) } // Command 07 - not seen used. void 041d_Unk() { 041d 0080 0346 lri $AR0, #0x0346 // 041f 02bf 0067 call 0x0067 0067_CopyCommand(_destAddr(#0x0346), _loopCount($AC0.M)) // 0421 02bf 0067 call 0x0067 0067_CopyCommand(_destAddr(#0x0346), _loopCount($AC0.M)) 0423 0081 0346 lri $AR1, #0x0346 0425 193e lrri $AC0.M, @$AR1 0426 193c lrri $AC0.L, @$AR1 0427 009f 0400 lri $AC1.M, #0x0400 0429 00c0 0345 lr $AR0, @0x0345 042b 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 042d 0081 0348 lri $AR1, #0x0348 042f 193e lrri $AC0.M, @$AR1 0430 193c lrri $AC0.L, @$AR1 0431 009f 0800 lri $AC1.M, #0x0800 0433 00c0 0345 lr $AR0, @0x0345 0435 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 0437 0081 0346 lri $AR1, #0x0346 0439 193e lrri $AC0.M, @$AR1 043a 193c lrri $AC0.L, @$AR1 043b 009f 0800 lri $AC1.M, #0x0800 043d 00c0 0345 lr $AR0, @0x0345 043f 02bf 0532 call 0x0532 0441 0081 0348 lri $AR1, #0x0348 0443 193e lrri $AC0.M, @$AR1 0444 193c lrri $AC0.L, @$AR1 0445 009f 0400 lri $AC1.M, #0x0400 0447 00c0 0345 lr $AR0, @0x0345 0449 02bf 0532 call 0x0532 044b 029f 0043 jmp 0x0043 } void 044d_COMMAND_07() { 044d 0080 0346 lri $AR0, #0x0346 044f 02bf 0067 call 0x0067 0451 02bf 0067 call 0x0067 0453 0081 0346 lri $AR1, #0x0346 0455 193e lrri $AC0.M, @$AR1 0456 193c lrri $AC0.L, @$AR1 0457 009f 0400 lri $AC1.M, #0x0400 0459 00c0 0345 lr $AR0, @0x0345 045b 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 045d 0081 0348 lri $AR1, #0x0348 045f 193e lrri $AC0.M, @$AR1 0460 193c lrri $AC0.L, @$AR1 0461 009f 0400 lri $AC1.M, #0x0400 0463 00c0 0345 lr $AR0, @0x0345 0465 02bf 0532 call 0x0532 0467 029f 0043 jmp 0x0043 } void 0469_COMMAND_06_09() { 0469 0080 0346 lri $AR0, #0x0346 046b 02bf 0067 call 0x0067 046d 02bf 0067 call 0x0067 046f 0081 0346 lri $AR1, #0x0346 0471 193e lrri $AC0.M, @$AR1 0472 193c lrri $AC0.L, @$AR1 0473 009f 0400 lri $AC1.M, #0x0400 0475 00c0 0345 lr $AR0, @0x0345 // 0477 02bf 0555 call 0x0555 // ReadFromMysteryReg 0555_UnknownReadFromMysteryReg(ARAMAddress(ACC0), DestBuffer(AC1.M), Length(AC0.M)) { 0479 0081 0348 lri $AR1, #0x0348 047b 193e lrri $AC0.M, @$AR1 047c 193c lrri $AC0.L, @$AR1 047d 009f 0400 lri $AC1.M, #0x0400 047f 00c0 0345 lr $AR0, @0x0345 0481 02bf 0532 call 0x0532 0483 029f 0043 jmp 0x0043 } void 0485_COMMAND_08() { 0485 0080 0346 lri $AR0, #0x0346 0487 02bf 0067 call 0x0067 0489 02bf 0067 call 0x0067 048b 0081 0346 lri $AR1, #0x0346 048d 193e lrri $AC0.M, @$AR1 048e 193c lrri $AC0.L, @$AR1 048f 009f 0400 lri $AC1.M, #0x0400 0491 00c0 0344 lr $AR0, @0x0344 0493 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 0495 0081 0348 lri $AR1, #0x0348 0497 193e lrri $AC0.M, @$AR1 0498 193c lrri $AC0.L, @$AR1 0499 009f 0800 lri $AC1.M, #0x0800 049b 00c0 0344 lr $AR0, @0x0344 049d 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 049f 0080 0400 lri $AR0, #0x0400 04a1 0083 0800 lri $AR3, #0x0800 04a3 0084 0000 lri $IX0, #0x0000 04a5 00da 0345 lr $AX0.H, @0x0345 04a7 00df 0344 lr $AC1.M, @0x0344 04a9 8f00 set40 04aa 197b lrri $AX1.H, @$AR3 04ab b800 mulx $AX0.H, $AX1.H 04ac 197b lrri $AX1.H, @$AR3 04ad 007f 04b2 bloop $AC1.M, 0x04b2 04af 199e lrrn $AC0.M, @$AR0 04b0 bc00 mulxac $AX0.H, $AX1.H, $ACC0 04b1 80b2 nx'sl : $AC0.M, $AX1.H 04b2 0000 nop 04b3 8e00 set16 04b4 0081 0346 lri $AR1, #0x0346 04b6 193e lrri $AC0.M, @$AR1 04b7 193c lrri $AC0.L, @$AR1 04b8 009f 0400 lri $AC1.M, #0x0400 04ba 00c0 0344 lr $AR0, @0x0344 04bc 02bf 0532 call 0x0532 04be 029f 0043 jmp 0x0043 } // Zeroes 256 words @ 0x0b00, calls 0x0573 void 04c0_UnknownInit() { 04c0 0092 00ff lri $CR, #0x00ff 04c2 8100 clr $ACC0 04c3 0080 0b00 lri $AR0, #0x0b00 04c5 10ff loopi #0xff 04c6 1b1e srri @$AR0, $AC0.M 04c7 1b1e srri @$AR0, $AC0.M 04c8 8100 clr $ACC0 04c9 009f 0b00 lri $AC1.M, #0x0b00 04cb 0080 0100 lri $AR0, #0x0100 04cd 02bf 0573 call 0x0573 04cf 02df ret } void 04d0_Unk() { // 04d0 02bf 04e1 call 0x04e1 04e1_Read0x40WordsFromZeroTo0b00() // 04fb is incremented when you reset a voice 04d2 00df 04fb lr $AC1.M, @0x04fb 04d4 009e 0b00 lri $AC0.M, #0x0b00 04d6 4c00 add $ACC0, $ACC1 04d7 1c1e mrr $AR0, $AC0.M 04d8 181e lrr $AC0.M, @$AR0 04d9 7400 incm $AC0.M 04da 1a1e srr @$AR0, $AC0.M *(AR0)++ 04db 02bf 04ea call 0x04ea 04dd 8100 clr $ACC0 04de 00fe 04fb sr @0x04fb, $AC0.M 04e0 02df ret } void 04e1_Read0x40WordsFromZeroTo0b00() { 04e1 0092 00ff lri $CR, #0x00ff 04e3 8100 clr $ACC0 04e4 009f 0b00 lri $AC1.M, #0x0b00 04e6 0080 0040 lri $AR0, #0x0040 // 04e8 029f 0555 jmp 0x0555 GOTO 0555_UnknownReadFromMysteryReg(ARAMAddress(ACC0), DestBuffer(AC1.M), Length(AR0)) { } void 04ea_Call0573With0b00And0050() { 04ea 8100 clr $ACC0 04eb 009f 0b00 lri $AC1.M, #0x0b00 04ed 0080 0050 lri $AR0, #0x0050 04ef 029f 0573 jmp 0x0573 // I don't get it, the above doesn't match the parameters 0573_Mystery_Write(InBuffer($AR1), _COUNT(AX0.H)); } void 04f1_Read0x40WordsFromZeroTo0b00() { // 04f1 02bf 04e1 call 0x04e1 04e1_Read0x40WordsFromZeroTo0b00(); } void 04f3_strange() { 04f3 8900 clr $ACC1 04f4 0080 04fc lri $AR0, #0x04fc 04f6 8100 clr $ACC0 // Count the masked voices, look at all four mask words // 04f7 1104 0505 bloopi #0x04, 0x0505 for (int j = 0; j < 4; j++) { 04f9 0000 nop 04fa 191e lrri $AC0.M, @$AR0 04fb 0000 nop 04fc 1110 0503 bloopi #0x10, 0x0503 for (int i = 0; i < 0x10; i++) { 04fe 02c0 0001 andcf $AC0.M, #0x0001 0500 027d iflz 0501 7500 incm $AC1.M // count up 0502 147f lsr $ACC0, #-1 0503 0000 nop } 0504 0000 nop 0505 0000 nop } // AC1.M now contains the count of all voices masked/unmasked (not sure which) // Copy the voice masks to 0x0b48 ... 0506 00de 04fc lr $AC0.M, @0x04fc 0508 00fe 0b48 sr @0x0b48, $AC0.M 050a 00de 04fd lr $AC0.M, @0x04fd 050c 00fe 0b49 sr @0x0b49, $AC0.M 050e 00de 04fe lr $AC0.M, @0x04fe 0510 00fe 0b4a sr @0x0b4a, $AC0.M 0512 00de 04ff lr $AC0.M, @0x04ff 0514 00fe 0b4b sr @0x0b4b, $AC0.M 0516 009e 0b00 lri $AC0.M, #0x0b00 0518 4c00 add $ACC0, $ACC1 // The value is in AC1.M, this must be wrong disasm 0519 1c1e mrr $AR0, $AC0.M // Increment the counter at [ #0b00 + masked voice count] // why would you do this? making bucket histogram over the number of active voices? 051a 181e lrr $AC0.M, @$AR0 051b 7400 incm $AC0.M 051c 1a1e srr @$AR0, $AC0.M 051d 02bf 04ea call 0x04ea // 04ea_Call0573With0b00And0050() 051f 02df ret } void PointlessFunction_Unused() { 0520 02bf 04ea call 0x04ea // 04ea_Call0573With0b00And0050() 0522 02df ret } // the first parameter is a pointer to the real RAM addr void 0523_CopyRAMtoDMEM(&_srcRAM($AR1), _destDMEM($AC1.M), _len($AR0)) { // 0523 193e lrri $AC0.M, @$AR1 // 0524 193c lrri $AC0.L, @$AR1 AC0 = *AR1++ << 16 | *AR1 void 0525_CopyRAMtoDMEM(_srcRAM($AR1), _destDMEM($AC1.M), _len($AR0)) { 0525 2fcd srs @DSPA, $AC1.M 0526 0f00 lris $AC1.M, #0x00 // ugly jump to share code... i am not going to document it ^^ 0527 2fc9 srs @DSCR, $AC1.M 0528 2ece srs @DSMAH, $AC0.M 0529 2ccf srs @DSMAL, $AC0.L 052a 1fe0 mrr $AC1.M, $AR0 052b 1501 lsl $ACC1, #1 052c 2fcb srs @DSBL, $AC1.M // 052d 02bf 0536 call 0x0536 0536_WaitForDMATransfer() // 052f 02df ret } void 0530_DMEMtoRAM_Ind(_DMEM(AC1.M), _pMemAddr(AR1), _len(AR0)) { // 0530 193e lrri $AC0.M, @$AR1 // 0531 193c lrri $AC0.L, @$AR1 AC0 = *AR1++ << 16 | *AR1 // continues.... void 0532_DMEMtoRAM(_DMEM(AC1.M), _pMemAddr(ACC0), _len(AR0)) { 0532 2fcd srs @DSPA, $AC1.M 0533 0f01 lris $AC1.M, #0x01 0534 029f 0527 jmp 0x0527 } void 0536_WaitForDMATransfer() { 0536 26c9 lrs $AC0.M, @DSCR 0537 02a0 0004 andf $AC0.M, #0x0004 0539 029c 0536 jlnz 0x0536 053b 02df ret } // Can't find any calls to this one. void 053c_Unk_Unused() { 053c 193e lrri $AC0.M, @$AR1 053d 193c lrri $AC0.L, @$AR1 053e 00ff ffcd sr @DSPA, $AC1.M 0540 0f00 lris $AC1.M, #0x00 0541 00ff ffc9 sr @DSCR, $AC1.M 0543 00fe ffce sr @DSMAH, $AC0.M 0545 00fc ffcf sr @DSMAL, $AC0.L 0547 1fe0 mrr $AC1.M, $AR0 0548 1501 lsl $ACC1, #1 0549 00ff ffcb sr @DSBL, $AC1.M 054b 02df ret } void 054c_WaitForDMATransfer2_Unused() { 054c 00de ffc9 lr $AC0.M, @DSCR 054e 02a0 0004 andf $AC0.M, #0x0004 0550 029c 054c jlnz 0x054c 0552 02df ret } void 0553_UnknownReadFromMysteryReg_Unused() { 0553 193e lrri $AC0.M, @$AR1 0554 193c lrri $AC0.L, @$AR1 // continues... void 0555_UnknownReadFromMysteryReg(ARAMAddress(ACC0), DestBuffer(AC1.M), Length(AC0.M)) { // 0555 0240 7fff andi $AC0.M, #0x7fff // 0557 02bf 0561 call 0x0561 0561_SetupAcceleratorForMysteryAccess(ACC0 & 0x7FFFFFFF, AR0, AC1.M); // After that, length is now in AX0.H // 0559 007a 055f bloop $AX0.H, 0x055f for (int i = 0; i < AX0.H; i++) { // 055b 26d3 lrs $AC0.M, @Unk Zelda // 055c 1b3e srri @$AR1, $AC0.M *(AR1++) = ReadFromUnknownAcceleratorRegister(); } // 055d 0000 nop // 055e 0000 nop // 055f 0000 nop // 0560 02df ret } void 0561_SetupAcceleratorForMysteryAccess(ARAMAddress(ACC0), DestBuffer(AC1.M), Length(AR0)) { 0561 1c3f mrr $AR1, $AC1.M 0562 0f0a lris $AC1.M, #0x0a 0563 2fd1 srs @SampleFormat, $AC1.M 0564 1f5e mrr $AX0.H, $AC0.M 0565 1f1c mrr $AX0.L, $AC0.L // Store 0xFFFFFFFF value as end address - Zelda does not use the looping hardware 0566 009e ffff lri $AC0.M, #0xffff 0568 2ed6 srs @ACEAH, $AC0.M // end address 0569 2ed7 srs @ACEAL, $AC0.M 056a 1fda mrr $AC0.M, $AX0.H 056b 1f98 mrr $AC0.L, $AX0.L // Divide "current" address by 2. 056c 147f lsr $ACC0, #-1 056d 2ed8 srs @ACCAH, $AC0.M // Current address 056e 2cd9 srs @ACCAL, $AC0.L 056f 1f40 mrr $AX0.H, $AR0 0570 02df ret } // Writes to UnkZelda, nops void 0571_Mystery_Write_FirstLoadTwoRegs_Unused() { 0571 193e lrri $AC0.M, @$AR1 0572 193c lrri $AC0.L, @$AR1 void 0573_Mystery_Write(InBuffer($AR1), SourceBuffer(AC1.M), _COUNT(AX0.H)) { 0573 0090 0001 lri $AC0.H, #0x0001 // 0575 02bf 0561 call 0x0561 0561_SetupAcceleratorForMysteryAccess(ACC0, AR0, AC1.M); 0577 007a 057e bloop $AX0.H, 0x057e 0579 193e lrri $AC0.M, @$AR1 057a 2ed3 srs @Unk Zelda, $AC0.M 057b 0000 nop // seems the above store takes some time, 057c 0000 nop // whatever it does, so ... 4 nops does the trick I guess 057d 0000 nop 057e 0000 nop 057f 02df ret } void 0580_COMMAND_04() { // commando looks buggy... // it copies data to the switch casement data address... sounds like BS 0580 0080 0346 lri $AR0, #0x0346 0582 02bf 0067 call 0x0067 0584 02bf 0067 call 0x0067 0067_CopyCommand(0x0346, mssing AC0.M??) 0586 0081 0346 lri $AR1, #0x0346 0588 00df 0349 lr $AC1.M, @0x0349 058a 0340 ffff andi $AC1.M, #0xffff 058c 00c0 0345 lr $AR0, @0x0345 058e 02bf 0523 call 0x0523 0590 029f 0043 jmp 0x0043 } void 0592_COMMAND_05() { 0592 0080 0346 lri $AR0, #0x0346 0594 02bf 0067 call 0x0067 0596 02bf 0067 call 0x0067 // 0598 0081 0346 lri $AR1, #0x0346 // 059a 00df 0349 lr $AC1.M, @0x0349 // 059c 0340 ffff andi $AC1.M, #0xffff // 059e 00c0 0345 lr $AR0, @0x0345 // 05a0 02bf 0530 call 0x0530 0530_DMEMtoRAM_Ind((*0x0349)&0xFFFF, 0x0346, *0x0345) // 05a2 029f 0043 jmp 0x0043 // jumps back to send sync messages .... } void 05A4_ResetAccelerator() { 05a4 0092 00ff lri $CR, #0x00ff 05a6 009e ffff lri $AC0.M, #0xffff 05a8 2ed4 srs @ACSAH, $AC0.M 05a9 2ed5 srs @ACSAL, $AC0.M 05aa 2ed6 srs @ACEAH, $AC0.M 05ab 2ed7 srs @ACEAL, $AC0.M 05ac 02df ret } void 05ad_SetupAccelerator(_acceleratorH(AC0.M), _accleratorL(AC0.L), _format(AC1.M)) { // 05ad 00ff ffd1 sr @SampleFormat, $AC1.M *SampleFormat = AC1.M // 05af 0340 0003 andi $AC1.M, #0x0003 // 05b1 7900 decm $AC1.M // 05b2 02ca lsrn // ACC0 >>= AC1.M // 05b3 00fe ffd8 sr @ACCAH, $AC0.M // 05b5 00fc ffd9 sr @ACCAL, $AC0.L *ACCAH/ACCAL = address >> ((sampleFormat & 3) - 1); // ACCAH/ACCAL is current read address // Hm, this seems to imply some direct relationship between the sample format number and // the nibbles-per-sample value // 05b7 02df ret } void 05b8_NewMail() { # 05b8 1205 sbclr #0x05 # 05b9 8e00 set16 /* 05ba 00f0 03fd sr @0x03fd, $AC0.H 05bc 00fc 03ff sr @0x03ff, $AC0.L 05be f400 lsr16 $ACC0 05bf 00fc 03fe sr @0x03fe, $AC0.L 05c1 00fa 03fa sr @0x03fa, $AX0.H 05c3 8100 clr $ACC0 save AC0 register */ *0x03fd = AC0.H *0x03ff = AC0.L ACC0 >>= 16 *0x03fe = AC0.L *0x03fa = AX0.H // 05c4 00de fffe lr $AC0.M, @CMBH // 05c6 02c0 8000 andcf $AC0.M, #0x8000 // 05c8 029c 06b9 jlnz 0x06b9 if (@CMBH & 0x8000 > 0) { !MISSING! } // 05ca 00da ffff lr $AX0.H, @CMBL // 05cc 8600 tstaxh $AX0.H // 05cd 0294 0692 jnz 0x0692 if (*CMBL != 0) { !MISSING! } // 05cf 00de fffe lr $AC0.M, @CMBH // 05d1 02c0 8000 andcf $AC0.M, #0x8000 // 05d3 029c 05cf jlnz 0x05cf while(@CMBH & 0x8000 > 0); // 05d5 0240 000f andi $AC0.M, #0x000f // 05d7 1f5e mrr $AX0.H, $AC0.M // 05d8 7400 incm $AC0.M // 05d9 0c00 lris $AC0.L, #0x00 // 05da 1404 lsl $ACC0, #4 // 05db 00fe 034e sr @0x034e, $AC0.M AC0.M = *CMBH & 0x000F AX0.H = AC0.M *0x034e = (AC0.M++) << 4 // 05dd 1fda mrr $AC0.M, $AX0.H // 05de 1f40 mrr $AX0.H, $AR0 // 05df 0200 04fc addi $AC0.M, #0x04fc // 05e1 1c1e mrr $AR0, $AC0.M AX0.H = AR0 // save AR0 AC0.M = *CMBH & 0x000F AC0.M += 0x04fc AR0 = AC0.M // 05e2 00de ffff lr $AC0.M, @CMBL // 05e4 1a1e srr @$AR0, $AC0.M // 05e5 1c1a mrr $AR0, $AX0.H AC0.M = *CMBL *AR0 = AC0.M AR0 = AX.H // restore AR0 EndOfMailException: /* 05e6 00de 03fe lr $AC0.M, @0x03fe 05e8 00dc 03ff lr $AC0.L, @0x03ff 05ea 00d0 03fd lr $AC0.H, @0x03fd 05ec 00da 03fa lr $AX0.H, @0x03fa restore AC0 register */ AC0.M = *0x03fe AC0.L = *0x03ff AC0.H = *0x03fd AX0.H = *0x03fa # 05ee 1305 sbset #0x05 05ef 02ff rti } void 05f0_HaltUCode() { 05f0 009a 0002 lri $AX0.H, #0x0002 05f2 00fa 03a3 sr @0x03a3, $AX0.H 05f4 00e0 03f9 sr @0x03f9, $AR0 /* 05f6 02bf 067c call 0x067c */ 067c_Unk() 05f8 16fc dcd1 si @DMBH, #0xdcd1 05fa 16fd 0002 si @DMBL, #0x0002 05fc 16fb 0001 si @DIRQ, #0x0001 05fe 0021 halt } // Sync Table 05ff 0617 cmpis $ACC0, #0x17 0600 0618 cmpis $ACC0, #0x18 0601 0658 cmpis $ACC0, #0x58 0602 065b cmpis $ACC0, #0x5b // at the end of a frame, we get a mail telling ucode what to do next void 0603_FinalizeFrame(_returnAddr($AR0)) { // 0603 00e0 03f9 sr @0x03f9, $AR0 0x03f9 = _returnAddr // 0605 009e 0005 lri $AC0.M, #0x0005 // 0607 02bf 066a call 0x066a SendMB_DCD1(0x0005) # 0609 8e00 set16 // 060a 8100 clr $ACC0 // 060b 8900 clr $ACC1 ACC0 = 0 ACC1 = 0 // 060c 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() // 060e 27ff lrs $AC1.M, @CMBL // 060f 009e 05ff lri $AC0.M, #0x05ff // 0611 4c00 add $ACC0, $ACC1 AC0.M = 0x05ff + *CMBL // 0612 1c7e mrr $AR3, $AC0.M // 0613 0313 ilrr $AC1.M, @$AR3 // 0614 1c7f mrr $AR3, $AC1.M AR3 = *AC0.M <- "BUT FROM Instrcution Memory (look at sync table about)" // 0615 176f jmpr $AR3 switch(AR3 - 0x05FF) { case 0x00: HALT(); break; case 0x01: 0618_PrepareBootUcode(); break; case 0x02: 0658_SoftReset(); break; case 0x03: 065b_ContinueWithUCode(); break; default: HALT(); // 0616 0021 halt } } 0617 0021 halt // Sets up info needed to dma in a chunk to iram or dram, // and calls irom to do actual dma. irom returns to address given in AR0 void 0618_PrepareBootUcode() { // Dunno what that's about... 0618 009a 0002 lri $AX0.H, #0x0002 061a 00fa 03a3 sr @0x03a3, $AX0.H 061c 8100 clr $ACC0 061d 8900 clr $ACC1 //061e 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() 0620 24ff lrs $AC0.L, @CMBL //0621 02bf 0664 call 0x0664 0664_WaitForCPUMailBox_AC1() 0623 25ff lrs $AC1.L, @CMBL //0624 02bf 0664 call 0x0664 0664_WaitForCPUMailBox_AC1() 0626 27ff lrs $AC1.M, @CMBL 0627 2ece srs @DSMAH, $AC0.M 0628 2ccf srs @DSMAL, $AC0.L // 0 0629 16c9 0001 si @DSCR, #0x0001 062b 2fcd srs @DSPA, $AC1.M // 2 062c 2dcb srs @DSBL, $AC1.L // 1 062d 8100 clr $ACC0 062e 8900 clr $ACC1 //062f 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() 0631 24ff lrs $AC0.L, @CMBL 0632 1c9e mrr $IX0, $AC0.M 0633 1cbc mrr $IX1, $AC0.L //0634 02bf 0664 call 0x0664 0664_WaitForCPUMailBox_AC1() 0636 25ff lrs $AC1.L, @CMBL //0637 02bf 0664 call 0x0664 0664_WaitForCPUMailBox_AC1() 0639 27ff lrs $AC1.M, @CMBL 063a 1cdf mrr $IX2, $AC1.M 063b 1cfd mrr $IX3, $AC1.L 063c 8100 clr $ACC0 //063d 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() 063f 26ff lrs $AC0.M, @CMBL 0640 1c1e mrr $AR0, $AC0.M 0641 8900 clr $ACC1 //0642 02bf 0664 call 0x0664 0664_WaitForCPUMailBox_AC1() 0644 20ff lrs $AX0.L, @CMBL 0645 1f5f mrr $AX0.H, $AC1.M //0646 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() 0648 21ff lrs $AX1.L, @CMBL //0649 02bf 065e call 0x065e 065e_WaitForCPUMailBox_AC0() 064b 23ff lrs $AX1.H, @CMBL // Make sure dma is ready // 064c 26c9 lrs $AC0.M, @DSCR // 064d 02a0 0004 andf $AC0.M, #0x0004 // 064f 029c 064c jlnz 0x064c // Reset some of the state // 0651 1206 sbclr #0x06 // 0652 1203 sbclr #0x03 // 0653 1204 sbclr #0x04 // 0654 1205 sbclr #0x05 // 0655 029f 80b5 jmp 0x80b5 80b5_BootUcode(); // Should not reach here 0657 0021 halt } void 0658_SoftReset() { 0658 029f 8000 jmp 0x8000 065a 0021 halt } void 065b_ContinueWithUCode() { // 065b 00c0 03f9 lr $AR0, @0x03f9 // 065d 170f jmpr $AR0 GOTO *0x03f9; } void 065e_WaitForCPUMailBox_AC0() { 065e 26fe lrs $AC0.M, @CMBH 065f 02c0 8000 andcf $AC0.M, #0x8000 0661 029c 065e jlnz 0x065e 0663 02df ret } void 0664_WaitForCPUMailBox_AC1() { 0664 27fe lrs $AC1.M, @CMBH 0665 03c0 8000 andcf $AC1.M, #0x8000 0667 029c 0664 jlnz 0x0664 0669 02df ret } void SendMB_DCD1(_low) { // 066a 02bf 0682 call 0x0682 WaitForEmptyDSPMailBox_ovAC1(); // 066c 16fc dcd1 si @DMBH, #0xdcd1 // 066e 2efd srs @DMBL, $AC0.M // 066f 16fb 0001 si @DIRQ, #0x0001 *DMBH = 0xDCD1 *DMBL = _low *DIRQ = 0x0001 // 0671 02bf 0682 call 0x0682 WaitForEmptyDSPMailBox_ovAC1() // 0673 02df ret } void SendMB_F355(_low) { // 0674 02bf 0682 call 0x0682 WaitForEmptyDSPMailBox_ovAC1(); // 0676 16fc f355 si @DMBH, #0xf355 // 0678 2efd srs @DMBL, $AC0.M *DMBH = 0xf355 *DMBL = _low // 0679 02bf 0682 call 0x0682 WaitForEmptyDSPMailBox_ovAC1(); // 067b 02df ret } void 067c_Unk() { 067c 26fc lrs $AC0.M, @DMBH 067d 02c0 8000 andcf $AC0.M, #0x8000 067f 029d 067c jlz 0x067c 0681 02df ret } void WaitForEmptyDSPMailBox_ovAC1.M() { // 0682 27fc lrs $AC1.M, @DMBH // 0683 03c0 8000 andcf $AC1.M, #0x8000 // 0685 029d 0682 jlz 0x0682 while (*DMBH & 0x8000); // 0687 02df ret } void 0688_InitCommandBlock() { // 0688 009a 0280 lri $AX0.H, #0x0280 // 068a 00fa 0350 sr @0x0350, $AX0.H // 068c 00fa 0351 sr @0x0351, $AX0.H *0x0350 = 0x0280 *0x0351 = 0x0280 // 068e 0a00 lris $AX0.H, #0x00 // 068f 00fa 0352 sr @0x0352, $AX0.H *0x0352 = 0x00 // 0691 02df ret } // // this block is called by the new mail exception // it seems to copy a new command to the address @0x0350 and increase the // number of commands at 0x0352 // { 0692 00e0 03fb sr @0x03fb, $AR0 0694 00e8 03fc sr @0x03fc, $WR0 0696 00c0 0350 lr $AR0, @0x0350 0698 0088 002f lri $WR0, #0x002f do { 069a 1b1a srri @$AR0, $AX0.H // 069b 00de fffe lr $AC0.M, @CMBH // 069d 02c0 8000 andcf $AC0.M, #0x8000 // 069f 029c 069b jlnz 0x069b while (!CMBH & 0x8000) ; 06a1 00dc ffff lr $AC0.L, @CMBL 06a3 1b1e srri @$AR0, $AC0.M 06a4 1b1c srri @$AR0, $AC0.L 06a5 1fda mrr $AC0.M, $AX0.H 06a6 7800 decm $AC0.M 06a7 1f5e mrr $AX0.H, $AC0.M // 06a8 8600 tstaxh $AX0.H // 06a9 0294 069b jnz 0x069b } while (AX0.H); /* 06ab 8100 clr $ACC0 06ac 00de 0352 lr $AC0.M, @0x0352 06ae 7400 incm $AC0.M 06af 00fe 0352 sr @0x0352, $AC0.M increase number of commands */ *0x0352 = *0x0352++ 06b1 00e0 0350 sr @0x0350, $AR0 06b3 00c0 03fb lr $AR0, @0x03fb 06b5 00c8 03fc lr $WR0, @0x03fc // 06b7 029f 05e6 jmp 0x05e6 GOTO EndOfMailException // return values and leave exception // looks like a read from ring buffer [0x350, 0x37f] // note the use of the wrap register WR0. 06b9 00e0 03fb sr @0x03fb, $AR0 06bb 00e8 03fc sr @0x03fc, $WR0 06bd 00c0 0350 lr $AR0, @0x0350 06bf 0088 002f lri $WR0, #0x002f 06c1 0a00 lris $AX0.H, #0x00 06c2 1b1a srri @$AR0, $AX0.H 06c3 029f 06ab jmp 0x06ab } void 06c5_CopyCommandBlock() { // 06c5 00c0 0351 lr $AR0, @0x0351 short srcCommandQueueAddr = *0x0351 // 06c7 0088 002f lri $WR0, #0x002f $WR0 = #0x002f :start /* 06c9 00da 0352 lr $AX0.H, @0x0352 06cb 8600 tstaxh $AX0.H 06cc 0295 06ed jz 0x06ed check how many commands are inside the "queue" */ if (*0x352 == 0) { $WR0 = #0xffff return 0x2d; } /* 06ce 1205 sbclr #0x05 06cf 00da 0352 lr $AX0.H, @0x0352 06d1 1fda mrr $AC0.M, $AX0.H 06d2 7800 decm $AC0.M 06d3 00fe 0352 sr @0x0352, $AC0.M 06d5 1305 sbset #0x05 decrement number of commands in queue */ *0x352--; // 06d6 0081 0356 lri $AR1, #0x0356 short destCommandQueueAddr = 0x0356 // 06d8 191e lrri $AC0.M, @$AR0 // 06d9 02c0 8000 andcf $AC0.M, #0x8000 // 06db 029d 06f1 jlz 0x06f1 // 06dd 1f5e mrr $AX0.H, $AC0.M // 06de 8600 tstaxh $AX0.H // 06df 0295 06f5 jz 0x06f5 // check if command is valid short numCommands = *srcCommandQueueAddr++ numCommands &= 0x8000 if (numCommands < 0) { *0x0351 = srcCommandQueueAddr GOTO :start } if (numCommands == 0) { 05f0_HaltUCode() } /* 06e1 007a 06e6 bloop $AX0.H, 0x06e6 06e3 191e lrri $AC0.M, @$AR0 06e4 1b3e srri @$AR1, $AC0.M 06e5 191e lrri $AC0.M, @$AR0 06e6 1b3e srri @$AR1, $AC0.M copy command queue */ for (int i=0; i> 4 //07c5 2380 lrs $AX1.H, @0xff80 //07c6 8d00 set15 //07c7 c810 mulc'mv $AC0.M, $AX1.H : $AX0.L, $AC0.L $AX0.l = (PB.LoopStartPos >> 4) & 0xffff; prod = (PB.LoopStartPos >> 4 & 0xffff0000)*PB.Format; //07c8 ae00 mulxmv $AX0.L, $AX1.H, $ACC0 $ACC0 = (PB.LoopStartPos >> 4 & 0xffff0000)*PB.Format; prod = ((PB.LoopStartPos >> 4) & 0xffff)*PB.Format; //07c9 8c00 clr15 //07ca f000 lsl16 $ACC0 $ACC0 = (((PB.LoopStartPos >> 4) & 0xffff0000)*PB.Format)<<16 //07cb 4e00 addp $ACC0 $ACC0 = ((((PB.LoopStartPos >> 4) & 0xffff0000)*PB.Format)<<16)+ (((PB.LoopStartPos >> 4) & 0xffff)*PB.Format) // 07cc 238c lrs $AX1.H, @0xff8c // 07cd 218d lrs $AX1.L, @0xff8d // 07ce 4a00 addax $ACC0, $AX1 $ACC0 = (((((PB.LoopStartPos >> 4) & 0xffff0000)*PB.Format)<<16)+ (((PB.LoopStartPos >> 4) & 0xffff)*PB.Format))+PB.StartAddr // 07cf 2e38 srs @0x0038, $AC0.M // 07d0 2c39 srs @0x0039, $AC0.L PB.CurAddr = $ACC0 & 0xffffffff; // 07d1 2682 lrs $AC0.M, @0xff82 // 07d2 2e67 srs @0x0067, $AC0.M // 07d3 2683 lrs $AC0.M, @0xff83 // 07d4 2e66 srs @0x0066, $AC0.M //Unconditionally (!) copy YN1 and YN2 from loopyn2 and loopyn1 PB.YN1 = PB.LoopYN1; PB.YN2 = PB.LoopYN2; 07d5 00e3 0363 sr @0x0363, $AR3 07d7 0083 0458 lri $AR3, #0x0458 07d9 8100 clr $ACC0 07da 0e01 lris $AC0.M, #0x01 // 07db 02bf 07eb call 0x07eb 07eb_AFCDecoder(); 07dd 00c3 0363 lr $AR3, @0x0363 07df 02bf 0729 call 0x0729 07e1 029f 0749 jmp 0x0749 // No repeat // stop rendering of this PB (0x401 == 1) and clear the output buffer with zeroes... //07e3 0e01 lris $AC0.M, #0x01 //07e4 2e01 srs @0x0001, $AC0.M PB.KeyOff = 1; early_out: // Zero the buffer. 07e5 8100 clr $ACC0 07e6 005f loop $AC1.M 07e7 1b7e srri @$AR3, $AC0.M 07e8 0092 00ff lri $CR, #0x00ff // 07ea 02df ret return } } void 07eb_AFCDecoder(_numberOfSample(AC0.M)) { // 07eb 00ff 0360 sr @0x0360, $AC1.M // 07ed 00fe 0361 sr @0x0361, $AC0.M // 07ef 2638 lrs $AC0.M, @0x0038 // 07f0 2439 lrs $AC0.L, @0x0039 // 07f1 0f05 lris $AC1.M, #0x05 // 07f2 02bf 05ad call 0x05ad 05ad_SetupAccelerator(AC0.M, AC0.L, AC1.M) // 07f4 2638 lrs $AC0.M, @0x0038 // 07f5 2439 lrs $AC0.L, @0x0039 // 07f6 8900 clr $ACC1 // 07f7 00df 0361 lr $AC1.M, @0x0361 // 07f9 2280 lrs $AX0.H, @0xff80 // 07fa d000 mulc $AC1.M, $AX0.H // 07fb 6f00 movp $ACC1 // 07fc 4c00 add $ACC0, $ACC1 // 07fd 2e38 srs @0x0038, $AC0.M // 07fe 2c39 srs @0x0039, $AC0.L // increase sample offset in ARAM AC0 = (*0x0038 << 16) | *0x0039 AC1 = AC0 + _numberOfSample * *0x0480 // bytes per sample *0x0038 = AC0.M *0x0039 = AC0.L // 07ff 8100 clr $ACC0 // 0800 00de 0361 lr $AC0.M, @0x0361 //0802 007e 086b bloop $AC0.M, 0x086b for (int i = 0; i < _numberOfSample; i++) { // Look for the lrrn below to find the ARAM reads. // FFD3 seems to be some interface to do plain single byte reads // from ARAM with no ADPCM fanciness or similar. // It loads through AR0 loaded with immediate #ffd3, not through // lrs, so CR doesn't affect the effective address. 0804 0080 ffd3 lri $AR0, #0xffd3 0806 0084 0000 lri $IX0, #0x0000 0808 199e lrrn $AC0.M, @$AR0 0809 8900 clr $ACC1 080a 1ffe mrr $AC1.M, $AC0.M 080b 1401 lsl $ACC0, #1 080c 0240 001e andi $AC0.M, #0x001e 080e 0200 0300 addi $AC0.M, #0x0300 // AFC COEF Table 0810 1c3e mrr $AR1, $AC0.M 0811 157c lsr $ACC1, #-4 0812 0340 000f andi $AC1.M, #0x000f 0814 0a11 lris $AX0.H, #0x11 0815 5500 subr $ACC1, $AX0.H // 0816 8100 clr $ACC0 // 0817 2680 lrs $AC0.M, @0xff80 // 0818 0605 cmpis $ACC0, #0x05 // 0819 0295 0832 jz 0x0832 if (*0x480 != 0x5) // ( == 0x09) { 081b 009a 00f0 lri $AX0.H, #0x00f0 081d 0b0f lris $AX1.H, #0x0f 081e 0082 0364 lri $AR2, #0x0364 0820 1998 lrrn $AX0.L, @$AR0 0821 6000 movr $ACC0, $AX0.L // Unpack 14 of the nibbles.. 0822 1107 0829 bloopi #0x07, 0x0829 for (int j=0; j<7; j++) { 0824 3400 andr $AC0.M, $AX0.H 0825 1408 lsl $ACC0, #8 0826 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 0827 3644 andr'ln $AC0.M, $AX1.H : $AX0.L, @$AR0 0828 140c lsl $ACC0, #12 0829 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M } // Then do the last two .. 082a 3400 andr $AC0.M, $AX0.H 082b 1408 lsl $ACC0, #8 082c 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 082d 3600 andr $AC0.M, $AX1.H 082e 140c lsl $ACC0, #12 082f 1b5e srri @$AR2, $AC0.M 0830 029f 0852 jmp 0x0852 } else // (*0x480 == 5) { 0832 009a c000 lri $AX0.H, #0xc000 0834 0082 0364 lri $AR2, #0x0364 0836 1998 lrrn $AX0.L, @$AR0 0837 6000 movr $ACC0, $AX0.L // Unpack half nibbles (half quality, ~half space) //0838 1103 0845 bloopi #0x03, 0x0845 for (j=0; j<3; j++) { 083a 1408 lsl $ACC0, #8 083b 3400 andr $AC0.M, $AX0.H 083c 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 083d 140a lsl $ACC0, #10 083e 3400 andr $AC0.M, $AX0.H 083f 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 0840 140c lsl $ACC0, #12 0841 3400 andr $AC0.M, $AX0.H 0842 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 0843 140e lsl $ACC0, #14 0844 3444 andr'ln $AC0.M, $AX0.H : $AX0.L, @$AR0 0845 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M } 0846 1408 lsl $ACC0, #8 0847 3400 andr $AC0.M, $AX0.H 0848 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 0849 140a lsl $ACC0, #10 084a 3400 andr $AC0.M, $AX0.H 084b 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 084c 140c lsl $ACC0, #12 084d 3400 andr $AC0.M, $AX0.H 084e 6032 movr's $ACC0, $AX0.L : @$AR2, $AC0.M 084f 140e lsl $ACC0, #14 0850 3400 andr $AC0.M, $AX0.H 0851 1b5e srri @$AR2, $AC0.M } 0852 8f00 set40 0853 1f7f mrr $AX1.H, $AC1.M 0854 2066 lrs $AX0.L, @0x0066 0855 2767 lrs $AC1.M, @0x0067 0856 193a lrri $AX0.H, @$AR1 0857 1939 lrri $AX1.L, @$AR1 0858 0080 0364 lri $AR0, #0x0364 085a 1c80 mrr $IX0, $AR0 085b a000 mulx $AX0.L, $AX1.L 085c ea70 maddc'l $AC1.M, $AX1.L : $AC0.M, @$AR0 // ADPCM decoding main loop. 085d 1108 0866 bloopi #0x08, 0x0866 for (int i=0; i<8; i++) { 085f 3a93 orr'sl $AC0.M, $AX1.H : $AC1.M, $AX1.L 0860 a478 mulxac'l $AX0.L, $AX1.L, $ACC0 : $AC1.M, @$AR0 0861 1485 asl $ACC0, #5 0862 e833 maddc's $AC0.M, $AX1.L : @$AR3, $AC0.M 0863 3b92 orr'sl $AC1.M, $AX1.H : $AC0.M, $AX1.L 0864 a570 mulxac'l $AX0.L, $AX1.L, $ACC1 : $AC0.M, @$AR0 0865 1585 asl $ACC1, #5 0866 ea3b maddc's $AC1.M, $AX1.L : @$AR3, $AC1.M } 0867 2f67 srs @0x0067, $AC1.M 0868 8e00 set16 0869 1ff8 mrr $AC1.M, $AX0.L 086a 2f66 srs @0x0066, $AC1.M 086b 8900 clr $ACC1 } 086c 00df 0360 lr $AC1.M, @0x0360 086e 02df ret } // probably unreachable { // 086f b100 tst $ACC0 // 0870 02d5 retz if (!$ACC0) return; 0871 04fe addis $ACC0, #0xfe 0872 1f1e mrr $AX0.L, $AC0.M 0873 191e lrri $AC0.M, @$AR0 0874 0291 087a jl 0x087a 0876 191a lrri $AX0.H, @$AR0 0877 0058 loop $AX0.L 0878 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0879 6433 movr's $ACC0, $AX0.H : @$AR3, $AC0.M 087a 1b7e srri @$AR3, $AC0.M 087b 02df ret } //////////////////////////////////////////// DEFAULT DECODER void 087c_DefaultDecoder() { // 087c 8100 clr $ACC0 // 087d 1f5e mrr $AX0.H, $AC0.M // 087e 00d8 0402 lr $AX0.L, @0x0402 ACC0 = 0; AX0.L = *0x0402; // == PB.RatioInt // Sample fraction is stored in a common way, but sample position is not, so // it's in the individual decoders. Some decoders, like square wave, only care about // fractional sample position. So here we just load up the fractional sample // position before handing control over. // 0880 00dc 0430 lr $AC0.L, @0x0430 // 0882 0080 0520 lri $AR0, #0x0520 AC0.L = *0x430; // == PB.CurSampleFrac AR0 = 0x0520; // 0884 00df 0480 lr $AC1.M, @0x0480 // 0886 1501 lsl $ACC1, #1 // 0887 0340 007e andi $AC1.M, #0x007e // 0889 0300 0891 addi $AC1.M, #0x0891 // 088b 1c5f mrr $AR2, $AC1.M // 088c 175f callr $AR2 // (*$AR2)() <-- See jump table at 0x0891 AC1.M = ((*0x480 * 2) & 0x007e) + 0x0891; // == ((PB.Format * 2) & 0x007e) + 0x0891 AR2 = AC1.M; JumpTable0891(PB.Format); //088d 00fc 0430 sr @0x0430, $AC0.L *0x430 = AC0.L; // *0x430 == PB.CurSampleFrac // 088f 029f 02d8 jmp 0x02d8 GOTO ContinueWithBlock: // in SyncFrame } // Jump table // switch(PB.Format) 0891 029f 08b2 jmp 0x08b2 // case 0x0 // 08b2_Decoder0x0_SquareWave (used in ZWW) 0893 029f 08ed jmp 0x08ed // case 0x1 // 08ed_Decoder0x1_SawWave (used in ZWW) 0895 029f 08d5 jmp 0x08d5 // case 0x2 // 08d5_Decoder0x2_SquareSaw (hasn't been spotted) 0897 029f 08c2 jmp 0x08c2 // case 0x3 // 08c2_Decoder0x3_RectangleWave (hasn't been spotted) 0899 029f 08fb jmp 0x08fb // case 0x4 // void 08f3_Decoder0x4_0xb_0xc_WaveTable (used in Pikmin) 089b 029f 08b1 jmp 0x08b1 // case 0x5 (can never happen) 089d 029f 0919 jmp 0x0919 // case 0x6 // 0919_Decoder0x6_Constant (hasn't been spotted) 089f 029f 091c jmp 0x091c // case 0x7 // 091c_Decoder0x7_WaveTable (used in Pikmin) 08a1 029f 08b1 jmp 0x08b1 // case 0x8 (can never happen) 08a3 029f 08b1 jmp 0x08b1 // case 0x9 (can never happen) 08a5 029f 093a jmp 0x093a // case 0xa (hasn't been spotted) 08a7 029f 08f3 jmp 0x08f3 // case 0xb // void 08f3_Decoder0x4_0xb_0xc_WaveTable (used in Pikmin) (used in Pikmin) 08a9 029f 08f7 jmp 0x08f7 // case 0xc // void 08f3_Decoder0x4_0xb_0xc_WaveTable (used in Pikmin) (Zelda force field in temple of gods) 08ab 029f 08b1 jmp 0x08b1 // case 0xd (unused) 08ad 029f 08b1 jmp 0x08b1 // case 0xe (unused) 08af 029f 08b1 jmp 0x08b1 // case 0xf (unused) 08b1 02df ret void 08b2_Decoder0x0_SquareWave(ACC0, AR0, AX0.L) { // 08b2 1401 lsl $ACC0, #1 t = samplePosition * 2; // Set up sound buffers // 08b3 009b c000 lri $AX1.H, #0xc000 // 08b5 0099 4000 lri $AX1.L, #0x4000 // 08b7 1150 08bf bloopi #0x50, 0x08bf for(int i = 0; i < 80; i++) { //08b9 02c0 0001 andcf $AC0.M, #0x0001 //08bb 027c iflnz // 08bc 1b1b srri @$AR0, $AX1.H //08bd 027d iflz // 08be 1b19 srri @$AR0, $AX1.L if(($AC0.M & 1) == 1) *$AR0++ = 0x4000; else *$AR0++ = 0xc000; // 08bf 4800 addax $ACC0, $AX0 t += PB.Ratio; } // 08c0 147f lsr $ACC0, #-1 t /= 2; // 08c1 02df ret } void 08c2_Decoder0x3_RectangleWave(ACC0, AR0, AX0.L) { 08c2 1402 lsl $ACC0, #2 // t = PB.CurSampleFrac * 4 08c3 8900 clr $ACC1 // ACC1 = 0 08c4 1fb8 mrr $AC1.L, $AX0.L // AC1.L = PB.RatioInt 08c5 1501 lsl $ACC1, #1 // ACC1 *= 2 08c6 009b c000 lri $AX1.H, #0xc000 08c8 0099 4000 lri $AX1.L, #0x4000 // 08ca 1150 08d2 bloopi #0x50, 0x08d2 for(int i = 0; i < 80; i++) { // 08cc 02c0 0003 andcf $AC0.M, #0x0003 // 08ce 027c iflnz // 08cf 1b1b srri @$AR0, $AX1.H // 08d0 027d iflz // 08d1 1b19 srri @$AR0, $AX1.L // 08d2 4c00 add $ACC0, $ACC1 if (($AC0.M & 3) == 3) *$AR0++ = 0x4000; else *$AR0++ = 0xc000; t += (PB.RatioInt * 2); } // 08d3 147e lsr $ACC0, #-2 t /= 4; // 08d4 02df ret } void 08d5_Decoder0x2_SquareSaw(ACC0, AR0, AX0.L) { 08d5 1401 lsl $ACC0, #1 08d6 0081 0ca0 lri $AR1, #0x0ca0 08d8 009b c000 lri $AX1.H, #0xc000 08da 0099 4000 lri $AX1.L, #0x4000 08dc 8900 clr $ACC1 08dd 0082 0000 lri $AR2, #0x0000 08df 1150 08ea bloopi #0x50, 0x08ea 08e1 02c0 0001 andcf $AC0.M, #0x0001 08e3 027c iflnz 08e4 1b1b srri @$AR0, $AX1.H 08e5 027d iflz 08e6 1b19 srri @$AR0, $AX1.L 08e7 183d lrr $AC1.L, @$AR1 08e8 4900 addax $ACC1, $AX0 08e9 1fe2 mrr $AC1.M, $AR2 08ea 4c39 add's $ACC0, $ACC1 : @$AR1, $AC1.M 08eb 147f lsr $ACC0, #-1 // 08ec 02df ret } void 08ed_Decoder0x1_SawWave(ACC0, AR0, AX0.L) { // 08ed 8900 clr $ACC1 // 08ee 1fb8 mrr $AC1.L, $AX0.L // 08ef 157f lsr $ACC1, #-1 // At this point AX0.L is PB.RatioInt and AC0.L is PB.CurSampleFrac ACC1 = 0; AC1.L = AX0.L * 2; // 08f0 1050 loopi #0x50 for(int i = 0; i < 0x50; i++) { // 08f1 4c20 add's $ACC0, $ACC1 : @$AR0, $AC0.L ACC0 += ACC1; *$AR0++ = AC0.L; } // 08f2 02df ret } void 08f3_Decoder0x4_0xb_0xc_WaveTable(ACC0, AR0, AX0.L) { // See 091c 08f3 0082 0180 lri $AR2, #0x0180 // Entrance 1, 0x0b 08f5 029f 08fd jmp 0x08fd 08f7 0082 01c0 lri $AR2, #0x01c0 // Entrance 2, 0x0c 08f9 029f 08fd jmp 0x08fd 08fb 0082 0140 lri $AR2, #0x0140 // Entrance 3, 0x04 08fd 008a 003f lri $WR2, #0x003f 08ff 0086 0000 lri $IX2, #0x0000 0901 1406 lsl $ACC0, #6 0902 8900 clr $ACC1 0903 1fb8 mrr $AC1.L, $AX0.L 0904 1505 lsl $ACC1, #5 0905 009b 003f lri $AX1.H, #0x003f 0907 009a 0000 lri $AX0.H, #0x0000 0909 3600 andr $AC0.M, $AX1.H 090a 1cde mrr $IX2, $AC0.M 090b 001a addarn $AR2, $IX2 090c 3400 andr $AC0.M, $AX0.H 090d 1150 0913 bloopi #0x50, 0x0913 090f 4c4a add'l $ACC0, $ACC1 : $AX1.L, @$AR2 0910 3606 andr'dr $AC0.M, $AX1.H : $AR2 0911 1cde mrr $IX2, $AC0.M 0912 340e andr'nr $AC0.M, $AX0.H : $AR2 0913 1b19 srri @$AR0, $AX1.L 0914 1fc2 mrr $AC0.M, $AR2 0915 147a lsr $ACC0, #-6 0916 008a ffff lri $WR2, #0xffff // 0918 02df ret } void 0919_Decoder0x6_Constant(AR0, AX0.L) { // case 0x6: Fills the buffer with PB.RatioInt (zero?) // 0919 1050 loopi #0x50 // 091a 1b18 srri @$AR0, $AX0.L for(int i = 0; i < 0x50; i++) *AR0++ = AX0.L; // PB.RatioInt // 091b 02df ret } void 091c_Decoder0x7_WaveTable(ACC0, AR0, AX0.L) { // So AR2 is where it reads the data from, and it updates ACC0 to the final read address in the end // Questions: How does the wrap register change the data access? // 091c 0082 0100 lri $AR2, #0x0100 // 091e 008a 003f lri $WR2, #0x003f // 0920 0086 0000 lri $IX2, #0x0000 // 0922 1406 lsl $ACC0, #6 // 0923 8900 clr $ACC1 // 0924 1fb8 mrr $AC1.L, $AX0.L // 0925 1505 lsl $ACC1, #5 WR2 = 0x003f; ACC0 <<= 6; ACC1 = AX0.L << 5; // 0926 009b 003f lri $AX1.H, #0x003f // 0928 009a 0000 lri $AX0.H, #0x0000 // 092a 3600 andr $AC0.M, $AX1.H // 092b 1cde mrr $IX2, $AC0.M // 092c 001a addarn $AR2, $IX2 // 092d 3400 andr $AC0.M, $AX0.H AC0.M &= 0x003f; IX2 = AC0.M; AR2 = 0x0100 + IX2; AC0.M = 0; // 092e 1150 0934 bloopi #0x50, 0x0934 for(int i = 0; i < 0x50; i++) { // 0930 4c4a add'l $ACC0, $ACC1 : $AX1.L, @$AR2 ACC0 += ACC1; AX1.L = *AR2++; // 0931 3606 andr'dr $AC0.M, $AX1.H : $AR2 AC0.M &= 0x003f; AR2--; // 0932 1cde mrr $IX2, $AC0.M IX2 = AC0.M; // 0933 340e andr'nr $AC0.M, $AX0.H : $AR2 AC0.M = 0; AR2 += IX2; // 0934 1b19 srri @$AR0, $AX1.L *AR0++ = AX1.L; } // 0935 1fc2 mrr $AC0.M, $AR2 // 0936 147a lsr $ACC0, #-6 // 0937 008a ffff lri $WR2, #0xffff AC0.M = AR2; ACC0 >>= 6; WR2 = 0xffff; // 0939 02df ret } void 093a_Unk() { 093a 0082 0100 lri $AR2, #0x0100 093c 008a 003f lri $WR2, #0x003f 093e 0086 0000 lri $IX2, #0x0000 0940 0081 0ca0 lri $AR1, #0x0ca0 0942 1406 lsl $ACC0, #6 0943 8900 clr $ACC1 0944 1fb8 mrr $AC1.L, $AX0.L 0945 1505 lsl $ACC1, #5 0946 009b 003f lri $AX1.H, #0x003f 0948 009a 0000 lri $AX0.H, #0x0000 094a 3600 andr $AC0.M, $AX1.H 094b 1cde mrr $IX2, $AC0.M 094c 001a addarn $AR2, $IX2 094d 3400 andr $AC0.M, $AX0.H 094e 1150 0959 bloopi #0x50, 0x0959 0950 1939 lrri $AX1.L, @$AR1 0951 a000 mulx $AX0.L, $AX1.L 0952 140a lsl $ACC0, #10 0953 4e00 addp $ACC0 0954 1476 lsr $ACC0, #-10 0955 4c4a add'l $ACC0, $ACC1 : $AX1.L, @$AR2 0956 3606 andr'dr $AC0.M, $AX1.H : $AR2 0957 1cde mrr $IX2, $AC0.M 0958 340e andr'nr $AC0.M, $AX0.H : $AR2 0959 1b19 srri @$AR0, $AX1.L 095a 1fc2 mrr $AC0.M, $AR2 095b 147a lsr $ACC0, #-6 095c 008a ffff lri $WR2, #0xffff 095e 02df ret } void 095f_Unk_SetupMemAt0_0180() { 095f 0080 01be lri $AR0, #0x01be 0961 1918 lrri $AX0.L, @$AR0 0962 191a lrri $AX0.H, @$AR0 0963 0080 0180 lri $AR0, #0x0180 0965 0083 0180 lri $AR3, #0x0180 0967 9070 mul'l $AX0.L, $AX0.H : $AC0.M, @$AR0 0968 1ffe mrr $AC1.M, $AC0.M // 0969 1120 0970 bloopi #0x20, 0x0970 for (int i = 0; i < 0x20; i++) { 096b 7c00 neg $ACC0 096c d450 mulcac'l $AC1.M, $AX0.H, $ACC0 : $AX0.H, @$AR0 096d 6533 movr's $ACC1, $AX0.H : @$AR3, $AC0.M 096e c550 mulcac'l $AC0.M, $AX0.H, $ACC1 : $AX0.H, @$AR0 096f 1501 lsl $ACC1, #1 0970 643b movr's $ACC0, $AX0.H : @$AR3, $AC1.M } 0971 0080 01fe lri $AR0, #0x01fe 0973 191a lrri $AX0.H, @$AR0 0974 1918 lrri $AX0.L, @$AR0 0975 0080 01c0 lri $AR0, #0x01c0 0977 0083 01c0 lri $AR3, #0x01c0 0979 1ff8 mrr $AC1.M, $AX0.L 097a 9070 mul'l $AX0.L, $AX0.H : $AC0.M, @$AR0 097b f800 addpaxz $ACC0, $AX0.H 097c 0240 01ff andi $AC0.M, #0x01ff 097e 0260 2000 ori $AC0.M, #0x2000 //0980 02bf 0983 call 0x0983 0983_WriteRamp($ACC0, $ACC1, Dest($AR3)) // 0982 02df ret } void 0983_WriteRamp(ACC0, ACC1, Dest($AR3)) { // 0983 b900 tst $ACC1 // 0984 0272 ifg // 0985 7c00 neg $ACC0 if ($ACC1 > 0) { $ACC0 = -$ACC0; } // 0986 1f7e mrr $AX1.H, $AC0.M // 0987 4700 addr $ACC1, $AX1.H // 0988 1110 098d bloopi #0x10, 0x098d for (int i = 0; i < 0x10; i++) { 098a 473b addr's $ACC1, $AX1.H : @$AR3, $AC1.M 098b 473b addr's $ACC1, $AX1.H : @$AR3, $AC1.M 098c 473b addr's $ACC1, $AX1.H : @$AR3, $AC1.M 098d 473b addr's $ACC1, $AX1.H : @$AR3, $AC1.M } 098e 02df ret } //////////////////////////////////////////// 0x08 DECODER // Hardcoded destination 0x0580. void Decoder0x08() { 098f 0092 0004 lri $CR, #0x0004 0991 2002 lrs $AX0.L, @0x0002 0992 8100 clr $ACC0 0993 8900 clr $ACC1 0994 2430 lrs $AC0.L, @0x0030 // CurSampleFrac // 0995 8d00 set15 // 0996 0950 lris $AX1.L, #0x50 // 0997 a000 mulx $AX0.L, $AX1.L // 0998 a400 mulxac $AX0.L, $AX1.L, $ACC0 // 0999 1404 lsl $ACC0, #4 // 099a 8c00 clr15 // Compute how much data we need to read, to get 0x50 samples after resampling. // AC0.L is cursamplefrace, AX0.L is ratio. $ACC0 = PB.CurrentSampleFrac + 0x50 * PB.Ratio; 099b 1ffe mrr $AC1.M, $AC0.M 099c 0083 0580 lri $AR3, #0x0580 099e 2201 lrs $AX0.H, @0x0001 099f 8600 tstaxh $AX0.H 09a0 0294 09b1 jnz 0x09b1 // 09a2 2204 lrs $AX0.H, @0x0004 // 09a3 8600 tstaxh $AX0.H // 09a4 02b4 09f9 callne 0x09f9 if (*0x0404) { // NeedsReset 09f9_UpdateSampleCounters8(); } 09a6 8100 clr $ACC0 09a7 2605 lrs $AC0.M, @0x0005 09a8 b100 tst $ACC0 09a9 0295 09be jz 0x09be label09ab: 09ab 8100 clr $ACC0 09ac 2e05 srs @0x0005, $AC0.M 09ad 2281 lrs $AX0.H, @0xff81 09ae 8600 tstaxh $AX0.H 09af 0294 09b8 jnz 0x09b8 09b1 8100 clr $ACC0 09b2 005f loop $AC1.M 09b3 1b7e srri @$AR3, $AC0.M 09b4 7400 incm $AC0.M 09b5 2e01 srs @0x0001, $AC0.M 09b6 029f 09f2 jmp 0x09f2 09b8 2688 lrs $AC0.M, @0xff88 09b9 2489 lrs $AC0.L, @0xff89 09ba 2e34 srs @0x0034, $AC0.M 09bb 2c35 srs @0x0035, $AC0.L // 09bc 02bf 09f9 call 0x09f9 09f9_UpdateSampleCounters8(); 09be 00ff 0360 sr @0x0360, $AC1.M 09c0 2638 lrs $AC0.M, @0x0038 09c1 2439 lrs $AC0.L, @0x0039 09c2 0f05 lris $AC1.M, #0x05 // Sample format 5 // 09c3 02bf 05ad call 0x05ad 05ad_SetupAccelerator(AC0.M, AC0.L, AC1.M) 09c5 00df 0360 lr $AC1.M, @0x0360 09c7 8100 clr $ACC0 // 09c8 263a lrs $AC0.M, @0x003a // 09c9 b100 tst $ACC0 // 09ca 0294 09d9 jnz 0x09d9 if (*(0x043a)) { 09cc 263b lrs $AC0.M, @0x003b 09cd 5c00 sub $ACC0, $ACC1 09ce 0290 09d9 jge 0x09d9 09d0 223b lrs $AX0.H, @0x003b // 09d1 02bf 0a0a call 0x0a0a // Load more samples. 0a0a_ReadFromAccelerator8To16(OutBuffer($AR3), Count($AX0.H)) 09d3 5500 subr $ACC1, $AX0.H // 09d4 0a01 lris $AX0.H, #0x01 // 09d5 00fa 0405 sr @0x0405, $AX0.H *0x405 = 1; // PB.ReachedEnd = 1; 09d7 029f 09ab jmp 0x09ab } 09d9 1f5f mrr $AX0.H, $AC1.M // 09da 02bf 0a0a call 0x0a0a // Load more samples. 0a0a_ReadFromAccelerator8To16(OutBuffer($AR3), Count($AX0.H)); // Stash AX0.H away, it gets read again at 09ef. 09dc 00fa 0362 sr @0x0362, $AX0.H 09de 8100 clr $ACC0 09df 263a lrs $AC0.M, @0x003a 09e0 243b lrs $AC0.L, @0x003b 09e1 1570 lsr $ACC1, #-16 // 09e2 0a01 lris $AX0.H, #0x01 // 09e3 0081 0405 lri $AR1, #0x0405 // 09e5 5c00 sub $ACC0, $ACC1 // 09e6 b100 tst $ACC0 // 09e7 0275 ifz // 09e8 1a3a srr @$AR1, $AX0.H ACC0 -= ACC1; if(ACC0 == 0) *0x405 = 1; // PB.ReachedEnd = 1 09e9 2e3a srs @0x003a, $AC0.M 09ea 2c3b srs @0x003b, $AC0.L 09eb 2638 lrs $AC0.M, @0x0038 09ec 2439 lrs $AC0.L, @0x0039 09ed 00d8 0362 lr $AX0.L, @0x0362 09ef 7000 addaxl $ACC0, $AX0.L 09f0 2c39 srs @0x0039, $AC0.L 09f1 2e38 srs @0x0038, $AC0.M // 09f2 0092 00ff lri $CR, #0x00ff // 09f4 029f 02d0 jmp 0x02d0 GOTO Resample_From0580To0520: } // unreachable code void Unreachable() { 09f6 8100 clr $ACC0 09f7 2e34 srs @0x0034, $AC0.M 09f8 2e35 srs @0x0035, $AC0.M } void 09f9_UpdateSampleCounters8() { 09f9 2334 lrs $AX1.H, @0x0034 09fa 2135 lrs $AX1.L, @0x0035 09fb 268a lrs $AC0.M, @0xff8a 09fc 248b lrs $AC0.L, @0xff8b 09fd 5a00 subax $ACC0, $AX1 09fe 2e3a srs @0x003a, $AC0.M 09ff 2c3b srs @0x003b, $AC0.L 0a00 2634 lrs $AC0.M, @0x0034 0a01 2435 lrs $AC0.L, @0x0035 0a02 238c lrs $AX1.H, @0xff8c 0a03 218d lrs $AX1.L, @0xff8d 0a04 4a00 addax $ACC0, $AX1 0a05 2e38 srs @0x0038, $AC0.M 0a06 2c39 srs @0x0039, $AC0.L 0a07 8100 clr $ACC0 0a08 2e05 srs @0x0005, $AC0.M // 0a09 02df ret } void 0a0a_ReadFromAccelerator8To16(OutBuffer($AR3), Count($AX0.H)) { // Read from ARAM. Convert 8-bit samples to 16-bit. // 0a0a 0080 ffd3 lri $AR0, #0xffd3 // 0a0c 0084 0000 lri $IX0, #0x0000 // 0a0e 007a 0a12 bloop $AX0.H, 0x0a12 // 0a10 199e lrrn $AC0.M, @$AR0 // 0a11 1488 asl $ACC0, #8 // 0a12 1b7e srri @$AR3, $AC0.M // 0a13 02df ret for (int i = 0; i < $AX0.H; i++) { *($AR3++) = (*0xffd3) << 8; // ffd3 is the non-adpcm alternative read address for the accelerator. } } //////////////////////////////////////////// 0x10 DECODER // Hardcoded destination 0x0580. // This should be the easiest decoder to decipher in full -- except the really // trivial ones like the synths. // It's almost identical to Decoder0x08 void Decoder0x10() { // 0a14 0092 0004 lri $CR, #0x0004 0a16 2002 lrs $AX0.L, @0x0002 0a17 8100 clr $ACC0 0a18 8900 clr $ACC1 // 0a19 2430 lrs $AC0.L, @0x0030 0a1a 8d00 set15 // 0a1b 0950 lris $AX1.L, #0x50 // 0a1c a000 mulx $AX0.L, $AX1.L // 0a1d a400 mulxac $AX0.L, $AX1.L, $ACC0 // 0a1e 1404 lsl $ACC0, #4 // 0a1f 8c00 clr15 // Compute how much data we need to read, to get 0x50 samples after resampling. // AC0.L is cursamplefrac, AX0.L is ratio. $ACC0 = (PB.CurrentSampleFrac + 0x50 * PB.Ratio) << 4; 0a20 1ffe mrr $AC1.M, $AC0.M 0a21 0083 0580 lri $AR3, #0x0580 0a23 2201 lrs $AX0.H, @0x0001 0a24 8600 tstaxh $AX0.H 0a25 0294 0a36 jnz 0x0a36 /// Jump! See jump destination below. 0a27 2204 lrs $AX0.H, @0x0004 0a28 8600 tstaxh $AX0.H 0a29 02b4 0a7f callne 0x0a7f if (*0x0404) // "NeedsReset" { 0a7f_UpdateSampleCounters10() } 0a2b 8100 clr $ACC0 0a2c 2605 lrs $AC0.M, @0x0005 0a2d b100 tst $ACC0 0a2e 0295 0a43 jz 0x0a43 if (*0x405) { retry_0a30: 0a30 8100 clr $ACC0 0a31 2e05 srs @0x0005, $AC0.M 0a32 2281 lrs $AX0.H, @0xff81 0a33 8600 tstaxh $AX0.H 0a34 0294 0a3d jnz 0x0a3d if (!*401) { //// <<<<<<<<<<<< Jump destination 0a36 8100 clr $ACC0 0a37 005f loop $AC1.M 0a38 1b7e srri @$AR3, $AC0.M 0a39 7400 incm $AC0.M 0a3a 2e01 srs @0x0001, $AC0.M 0a3b 029f 0a78 jmp 0x0a78 // quit } else { // Copy [88,89] to [34,35] 0a3d 2688 lrs $AC0.M, @0xff88 0a3e 2489 lrs $AC0.L, @0xff89 0a3f 2e34 srs @0x0034, $AC0.M 0a40 2c35 srs @0x0035, $AC0.L 0a41 02bf 0a7f call 0x0a7f 0a7f_UpdateSampleCounters10() } } 0a43: 0a43 00ff 0360 sr @0x0360, $AC1.M 0a45 2638 lrs $AC0.M, @0x0038 0a46 2439 lrs $AC0.L, @0x0039 0a47 0f06 lris $AC1.M, #0x06 // Sample format 6 // 0a48 02bf 05ad call 0x05ad 05ad_SetupAccelerator(AC0.M, AC0.L, AC1.M) 0a4a 00df 0360 lr $AC1.M, @0x0360 0a4c 8100 clr $ACC0 0a4d 263a lrs $AC0.M, @0x003a 0a4e b100 tst $ACC0 0a4f 0294 0a5e jnz 0x0a5e if (!*0x043a) { 0a51 263b lrs $AC0.M, @0x003b 0a52 5c00 sub $ACC0, $ACC1 0a53 0290 0a5e jge 0x0a5e if (0x43b <= ACC1) { // not sure, but .. not enough samples? 0a55 223b lrs $AX0.H, @0x003b // 0a56 02bf 0a91 call 0x0a91 // Read more samples 0a91_ReadFromAccelerator(OutBuffer($AR3), Count($AX0.H)); 0a58 5500 subr $ACC1, $AX0.H // 0a59 0a01 lris $AX0.H, #0x01 // 0a5a 00fa 0405 sr @0x0405, $AX0.H *0x405 = 1; // PB.ReachedEnd 0a5c 029f 0a30 jmp 0x0a30 // GOTO retry_0a30; } } 0a5e 1f5f mrr $AX0.H, $AC1.M // 0a5f 02bf 0a91 call 0x0a91 // Read more samples 0a91_ReadFromAccelerator(OutBuffer($AR3), Count($AX0.H)) // Stash AX0.H away, it gets read again at 0a72. 0a61 00fa 0362 sr @0x0362, $AX0.H 0a63 8100 clr $ACC0 0a64 263a lrs $AC0.M, @0x003a 0a65 243b lrs $AC0.L, @0x003b 0a66 1570 lsr $ACC1, #-16 // 0a67 0a01 lris $AX0.H, #0x01 // 0a68 0081 0405 lri $AR1, #0x0405 // 0a6a 5c00 sub $ACC0, $ACC1 // 0a6b b100 tst $ACC0 // 0a6c 0275 ifz // 0a6d 1a3a srr @$AR1, $AX0.H ACC0 -= ACC1; if(ACC0 == 0) *0x405 = 1; // PB.ReachedEnd = 1 0a6e 2e3a srs @0x003a, $AC0.M 0a6f 2c3b srs @0x003b, $AC0.L 0a70 2638 lrs $AC0.M, @0x0038 0a71 2439 lrs $AC0.L, @0x0039 0a72 00d8 0362 lr $AX0.L, @0x0362 0a74 7000 addaxl $ACC0, $AX0.L 0a75 7000 addaxl $ACC0, $AX0.L 0a76 2c39 srs @0x0039, $AC0.L 0a77 2e38 srs @0x0038, $AC0.M // 0a78 0092 00ff lri $CR, #0x00ff // 0a7a 029f 02d0 jmp 0x02d0 GOTO Resample_From0580To0520: } void 0a7c_UnkUnused() { 0a7c 8100 clr $ACC0 0a7d 2e34 srs @0x0034, $AC0.M 0a7e 2e35 srs @0x0035, $AC0.M // used by 0x10 decoder void 0a7f_UpdateSampleCounters10() { 0a7f 2334 lrs $AX1.H, @0x0034 0a80 2135 lrs $AX1.L, @0x0035 0a81 268a lrs $AC0.M, @0xff8a 0a82 248b lrs $AC0.L, @0xff8b 0a83 5a00 subax $ACC0, $AX1 // Subtract [34,35] from [8a, 8b] 0a84 2e3a srs @0x003a, $AC0.M 0a85 2c3b srs @0x003b, $AC0.L 0a86 2634 lrs $AC0.M, @0x0034 0a87 2435 lrs $AC0.L, @0x0035 0a88 1401 lsl $ACC0, #1 // This shift is not done in UpdateSampleCounters8. 0a89 238c lrs $AX1.H, @0xff8c 0a8a 218d lrs $AX1.L, @0xff8d 0a8b 4a00 addax $ACC0, $AX1 // Add [34,35]<<1 to [8c, 8d] 0a8c 2e38 srs @0x0038, $AC0.M 0a8d 2c39 srs @0x0039, $AC0.L 0a8e 8100 clr $ACC0 0a8f 2e05 srs @0x0005, $AC0.M 0a90 02df ret } // Read AX0.H samples from the accelerator. void 0a91_ReadFromAccelerator(OutBuffer($AR3), Count($AX0.H)) { // 0a91 0080 ffd3 lri $AR0, #0xffd3 // 0a93 0084 0000 lri $IX0, #0x0000 // 0a95 007a 0a98 bloop $AX0.H, 0x0a98 // 0a97 199e lrrn $AC0.M, @$AR0 // 0a98 1b7e srri @$AR3, $AC0.M // 0a99 02df ret for (int i = 0; i < $AX0.H; i++) { *($AR3++) = *0xffd3; // ffd3 is the non-adpcm alternative read address for the accelerator. } } //////////////////////////////////////////// 0x20 DECODER // Same as 0x21 but with no resampling. { // 0a9a 8900 clr $ACC1 // 0a9b 0f50 lris $AC1.M, #0x50 // 0a9c 0083 0520 lri $AR3, #0x0520 // 0a9e 02bf 0ab3 call 0x0ab3 ACC1 = 0; AC1.M = 0x50; AR3 = 0x520; 0ab3_Decoder0x21Core(AC1.M=0x50, AR3=#0x0520); // 0aa0 029f 02d8 jmp 0x02d8 GOTO ContinueWithBlock: // in SyncFrame } //////////////////////////////////////////// 0x21 DECODER void 0aa2_Decoder0x21() { // 0aa2 00d8 0402 lr $AX0.L, @0x0402 // 0aa4 8100 clr $ACC0 // 0aa5 8900 clr $ACC1 AX0.L = *0x0402; ACC0 = 0 ACC1 = 0 // 0aa6 00dc 0430 lr $AC0.L, @0x0430 // 0aa8 0a50 lris $AX0.H, #0x50 // 0aa9 9000 mul $AX0.L, $AX0.H // 0aaa 9400 mulac $AX0.L, $AX0.H, $ACC0 // 0aab 1404 lsl $ACC0, #4 ACC0 = (*0x0430 + (*0x0402 * 0x50)) << 4; // 0aac 1ffe mrr $AC1.M, $AC0.M ACC1 = ACC0 & 0xFFFF0000; // 0aad 0083 0580 lri $AR3, #0x0580 // 0aaf 02bf 0ab3 call 0x0ab3 // 0ab3_Decoder0x21Core 0ab3_Decoder0x21Core(AC1.M, AR3=#0x0580); // 0ab1 029f 02d0 jmp 0x02d0 GOTO Resample_From0580To0520: } // 0x21 Decoder Core // Decoder 0x21 simply streams raw audio from RAM (not ARAM!) by using DMA. // Lots of buffer wrap trickery etc but no actual decoding. void 0ab3_Decoder0x21Core(AC1.M, AR3) { // 0ab3 0092 0004 lri $CR, #0x0004 // 0ab5 8100 clr $ACC0 // 0ab6 263a lrs $AC0.M, @0x003a // 0ab7 243b lrs $AC0.L, @0x003b // 0ab8 1f1f mrr $AX0.L, $AC1.M // 0ab9 0a00 lris $AX0.H, #0x00 // 0aba 5800 subax $ACC0, $AX0 ACC0 = [0x043a,0x043b]; ACC0 -= AC1.M; // 0abb 0292 0ad1 jg 0x0ad1 if ([0x043a,0x043b] <= AC1.M) { // Happens when sound has finished playing? // 0abd 8900 clr $ACC1 // 0abe 00c0 043b lr $AR0, @0x043b ACC1 = 0; AR0 = *0x043b; // 0ac0 02bf 0af6 call 0x0af6 // 0af6_Decoder0x21_MoreStuff() 0af6_Decoder0x21_MoreStuff(AR0=*0x043b, AR3); // 0ac2 8100 clr $ACC0 // 0ac3 1fd8 mrr $AC0.M, $AX0.L // 0ac4 223b lrs $AX0.H, @0x003b // 0ac5 5400 subr $ACC0, $AX0.H // 0ac6 0007 dar $AR3 // 0ac7 1979 lrri $AX1.L, @$AR3 ACC0 = 0; AX0.H = *0x043b; AC0.M = AX0.L - *0x043b; AX1.L = *$AR3; // Looks like duplication of the first memory address AC0.M times // 0ac8 005e loop $AC0.M for(int i = 0; i < AC0.M; i++) { // 0ac9 1b79 srri @$AR3, $AX1.L *$AR3++ = AX1.L; } // 0aca 0f01 lris $AC1.M, #0x01 // 0acb 2f01 srs @0x0001, $AC1.M // 0acc 8900 clr $ACC1 // 0acd 2f3b srs @0x003b, $AC1.M ACC1 = 0; // Looks like some finalization of the PB *0x0401 = 1; // KeyOff *0x043b = 0; // RemLength 0ace 0092 00ff lri $CR, #0x00ff //0ad0 02df ret return; } // 0ad1 2e3a srs @0x003a, $AC0.M // 0ad2 2c3b srs @0x003b, $AC0.L *0x043a = AC0.M; *0x043b = AC0.L; // 0ad3 8100 clr $ACC0 // 0ad4 8900 clr $ACC1 // 0ad5 268a lrs $AC0.M, @0xff8a // 0ad6 2734 lrs $AC1.M, @0x0034 ACC0 = 0; ACC1 = 0; AC0.M = *0x048a; AC1.M = *0x0434; // 0ad7 5c00 sub $ACC0, $ACC1 // 0ad8 2e36 srs @0x0036, $AC0.M ACC0 -= AC1.L; *0x0436 = (ACC0 & 0xFFFF0000) >> 16; // 0ad9 5000 subr $ACC0, $AX0.L ACC0 -= AX0.L; 0ada 0290 0af0 jge 0x0af0 if (ACC0 < 0) { // 0adc 00c0 0436 lr $AR0, @0x0436 // 0ade 02bf 0af6 call 0x0af6 0af6_Decoder0x21_MoreStuff(AR0=*0x0436, AR3); // 0ae0 8100 clr $ACC0 // 0ae1 1fd8 mrr $AC0.M, $AX0.L ACC0 = 0; AC0.M = AX0.L; // 0ae2 2236 lrs $AX0.H, @0x0036 // 0x0436 AX0.H = *0x0436; // 0ae3 5400 subr $ACC0, $AX0.H // 0ae4 1c1e mrr $AR0, $AC0.M // 0ae5 8100 clr $ACC0 // 0ae6 2e34 srs @0x0034, $AC0.M ACC0 -= AX0.H; AR0 = (ACC0 & 0xFFFF0000) >> 16; ACC0 = 0; *0x0434 = 0; // 0ae7 2688 lrs $AC0.M, @0xff88 // 0x0488 // 0ae8 2489 lrs $AC0.L, @0xff89 // 0x0489 // 0ae9 2e8c srs @0xff8c, $AC0.M // 0aea 2c8d srs @0xff8d, $AC0.L *0x048c = *0x0488; *0x048d = *0x0489; // 0aeb 02bf 0af6 call 0x0af6 0af6_Decoder0x21_MoreStuff(AR0=*0x0436, AR3); 0aed 0092 00ff lri $CR, #0x00ff // 0aef 02df ret return; } // 0af0 1c18 mrr $AR0, $AX0.L // 0af1 02bf 0af6 call 0x0af6 AR0 = AX0.L; 0af6_Decoder0x21_MoreStuff(AR0=AX0.L, AR3); 0af3 0092 00ff lri $CR, #0x00ff // 0af5 02df ret } // CR = 0x4 // Does strange stuff with PB[0x34] and the address PB[0x8c,d] // Does not touch AX0.L void 0af6_Decoder0x21_MoreStuff($AR0, $AR3) { // 0af6 8100 clr $ACC0 // 0af7 1fc0 mrr $AC0.M, $AR0 // 0af8 b100 tst $ACC0 // 0af9 02d5 retz if (!AR0) return; // 0afa 8900 clr $ACC1 // 0afb 2734 lrs $AC1.M, @0x0034 // 0afc 0340 0001 andi $AC1.M, #0x0001 // 0afe 0b00 lris $AX1.H, #0x00 // 0aff 1f3f mrr $AX1.L, $AC1.M AX1.L = *0x0434 & 1; // 0b00 268c lrs $AC0.M, @0xff8c // 0b01 248d lrs $AC0.L, @0xff8d // 0b02 8900 clr $ACC1 // 0b03 2534 lrs $AC1.L, @0x0034 // 0b04 1501 lsl $ACC1, #1 // 0b05 4c00 add $ACC0, $ACC1 // 0b06 5a00 subax $ACC0, $AX1 // 0b07 5a00 subax $ACC0, $AX1 ACC0 = [8c,8d] + *0x0434 * 2 - ((*0x0434 & 1) * 2); // 0b08 1c20 mrr $AR1, $AR0 AR1 = AR0; // 0b09 1fe0 mrr $AC1.M, $AR0 // 0b0a 0502 addis $ACC1, #0x02 ACC1 = ($AR0 << 16) + 0x20000; // // 0b0b 1c1f mrr $AR0, $AC1.M // 0b0c 009f 0b00 lri $AC1.M, #0x0b00 0b0e 0092 00ff lri $CR, #0x00ff AR0 = AC1.M; AC1.M = 0x0b00; // Load more audio from RAM by DMA?? // 0b10 02bf 0525 call 0x0525 // 0525_CopyRAMtoDMEM 0525_CopyRAMtoDMEM($AR1 == $ACC1 >> 16, 0x0b00, $AR0) // 0b12 0092 0004 lri $CR, #0x0004 // 0b14 2734 lrs $AC1.M, @0x0034 // 0b15 1f61 mrr $AX1.H, $AR1 // 0b16 4700 addr $ACC1, $AX1.H // 0b17 2f34 srs @0x0034, $AC1.M *0x0434 += AR1; // 0b18 0080 0b00 lri $AR0, #0x0b00 // 0b1a 8900 clr $ACC1 // 0b1b 1ff9 mrr $AC1.M, $AX1.L // 0b1c b900 tst $ACC1 // 0b1d 0274 ifnz if (AX1.L) { // 0b1e 0008 iar $AR0 $AR0++; } // 0b1f 8900 clr $ACC1 // 0b20 1fe1 mrr $AC1.M, $AR1 // 0b21 191e lrri $AC0.M, @$AR0 // 0b22 0701 cmpis $ACC1, #0x01 ACC1 = 0; AC1.M = AR1; AC0.M = *$AR0++; // 0b23 0293 0b2c jle 0x0b2c if (AC1.M > 1) { // 0b25 191a lrri $AX0.H, @$AR0 // 0b26 05fe addis $ACC1, #0xfe AX0.H = *$AR0++; ACC1 += 0xfe0000; // 0b27 005f loop $AC1.M for(int i = 0; i < AC1.M; i++) { // 0b28 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M AC0.L = 0; AC0.H = AX0.H; AC0.M = AX0.H; AX0.H = *$AR0++; *$AR3++ = AC0.M; } //0b29 1b7e srri @$AR3, $AC0.M //0b2a 1b7a srri @$AR3, $AX0.H //0b2b 02df ret *$AR3++ = AC0.M; *$AR3++ = AX0.H; } else { //0b2c 1b7e srri @$AR3, $AC0.M //0b2d 02df ret *$AR3++ = AC0.M; } } void 0b2e_Unk_Multiply() { // ZWW: 01c2_Unk 0b2e 8a00 m2 0b2f 0083 03e8 lri $AR3, #0x03e8 0b31 191e lrri $AC0.M, @$AR0 0b32 191a lrri $AX0.H, @$AR0 0b33 1006 loopi #0x06 0b34 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0b35 1b7e srri @$AR3, $AC0.M 0b36 1b7a srri @$AR3, $AX0.H 0b37 0080 03e8 lri $AR0, #0x03e8 0b39 0088 0007 lri $WR0, #0x0007 0b3b 1150 0b48 bloopi #0x50, 0x0b48 for (int i = 0; i < 0x50; i++) { 0b3d 1c61 mrr $AR3, $AR1 0b3e 84c3 clrp'ld : $AX0.L, $AX1.L, @$AR3 0b3f f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b40 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b41 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b42 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b43 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b44 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b45 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b46 f200 madd $AX0.L, $AX0.H 0b47 fe00 movpz $ACC0 0b48 1b3e srri @$AR1, $AC0.M } 0b49 0088 ffff lri $WR0, #0xffff 0b4b 8b00 m0 0b4c 02df ret } // looks kind of like a IIR filter? // Hardcoded buffer length = 0x50 void 0b4d_IIR_Filter(InBuffer($AR0), OutBuffer($AR1), FilterLength(AC1.M)) { 0b4d 8a00 m2 0b4e 05fe addis $ACC1, #0xfe 0b4f 0083 03e8 lri $AR3, #0x03e8 0b51 191e lrri $AC0.M, @$AR0 0b52 191a lrri $AX0.H, @$AR0 0b53 005f loop $AC1.M 0b54 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0b55 1b7e srri @$AR3, $AC0.M 0b56 1b7a srri @$AR3, $AX0.H 0b57 0080 03e8 lri $AR0, #0x03e8 0b59 0501 addis $ACC1, #0x01 0b5a 1d1f mrr $WR0, $AC1.M // 0b5b 1150 0b63 bloopi #0x50, 0x0b63 for (int i = 0; i < 0x50; i++) { 0b5d 1c61 mrr $AR3, $AR1 0b5e 84c3 clrp'ld : $AX0.L, $AX1.L, @$AR3 0b5f 005f loop $AC1.M 0b60 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0b61 f200 madd $AX0.L, $AX0.H 0b62 fe00 movpz $ACC0 0b63 1b3e srri @$AR1, $AC0.M } 0b64 0088 ffff lri $WR0, #0xffff 0b66 8b00 m0 0b67 02df ret } // Looks like a 4-tap FIR filter. // Hardcoded buffer length = 0x50 // Uses 0x03e8 as some scratch space, i think . void 0b68_4TapFIR(InBuffer($AR2), FilterBuffer($AR0), OutBuffer($AR1)) { 0b68 0083 03e8 lri $AR3, #0x03e8 0b6a 191e lrri $AC0.M, @$AR0 0b6b 191a lrri $AX0.H, @$AR0 0b6c 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0b6d 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0b6e 1b7e srri @$AR3, $AC0.M 0b6f 1b7a srri @$AR3, $AX0.H 0b70 0080 03e8 lri $AR0, #0x03e8 0b72 0088 0003 lri $WR0, #0x0003 // That's a short wrap - filter coefs? 0b74 0085 0000 lri $IX1, #0x0000 0b76 0087 0000 lri $IX3, #0x0000 0b78 1fc2 mrr $AC0.M, $AR2 0b79 195b lrri $AX1.H, @$AR2 0b7a 1959 lrri $AX1.L, @$AR2 0b7b 195f lrri $AC1.M, @$AR2 0b7c 195a lrri $AX0.H, @$AR2 0b7d 1c5e mrr $AR2, $AC0.M 0b7e 1fda mrr $AC0.M, $AX0.H 0b7f 1c61 mrr $AR3, $AR1 0b80 8a00 m2 0b81 8f00 set40 0b82 191a lrri $AX0.H, @$AR0 0b83 b850 mulx'l $AX0.H, $AX1.H : $AX0.H, @$AR0 0b84 e250 maddx'l $AX0.H, $AX1.L : $AX0.H, @$AR0 0b85 ea50 maddc'l $AC1.M, $AX1.L : $AX0.H, @$AR0 0b86 e8e8 maddc'ldm $AC0.M, $AX1.L : $AX0.H, $AX1.L, @$AR0 0b87 b650 mulxmv'l $AX0.H, $AX1.L, $ACC0 : $AX0.H, @$AR0 0b88 1127 0b93 bloopi #0x27, 0x0b93 for (int i = 0; i < 0x27; i++) { 0b8a e3a8 maddx'lsm $AX0.H, $AX1.H : $AX0.H, $AC0.M // end of first iteration 0b8b 197e lrri $AC0.M, @$AR3 0b8c e850 maddc'l $AC0.M, $AX1.L : $AX0.H, @$AR0 0b8d eaf8 maddc'ldm $AC1.M, $AX1.L : $AX0.H, $AX1.H, @$AR0 0b8e bf50 mulxmv'l $AX0.H, $AX1.H, $ACC1 : $AX0.H, @$AR0 0b8f e2a9 maddx'lsm $AX0.H, $AX1.L : $AX0.H, $AC1.M // e 0b90 197f lrri $AC1.M, @$AR3 // start of first iteration 0b91 ea50 maddc'l $AC1.M, $AX1.L : $AX0.H, @$AR0 0b92 e8e8 maddc'ldm $AC0.M, $AX1.L : $AX0.H, $AX1.L, @$AR0 0b93 b650 mulxmv'l $AX0.H, $AX1.L, $ACC0 : $AX0.H, @$AR0 } 0b94 e3a8 maddx'lsm $AX0.H, $AX1.H : $AX0.H, $AC0.M 0b95 197e lrri $AC0.M, @$AR3 0b96 e850 maddc'l $AC0.M, $AX1.L : $AX0.H, @$AR0 0b97 eaf8 maddc'ldm $AC1.M, $AX1.L : $AX0.H, $AX1.H, @$AR0 0b98 bf00 mulxmv $AX0.H, $AX1.H, $ACC1 0b99 1bff srrn @$AR3, $AC1.M 0b9a 197f lrri $AC1.M, @$AR3 0b9b 8e00 set16 0b9c 8b00 m0 0b9d 0088 ffff lri $WR0, #0xffff 0b9f 1b5b srri @$AR2, $AX1.H 0ba0 1b59 srri @$AR2, $AX1.L 0ba1 1b5f srri @$AR2, $AC1.M 0ba2 1b5e srri @$AR2, $AC0.M 0ba3 02df ret } // Fixed length 0x50. void 0ba4_UnknownFilter(params($AR0), buffer($AR1), filter_state($AR2)) { 0ba4 0083 03e8 lri $AR3, #0x03e8 // Load 4 parameters from *$AR0, copy them into the tiny ring buffer // later handled by $AR0/$WR0 (filter state?) 0ba6 191e lrri $AC0.M, @$AR0 0ba7 191a lrri $AX0.H, @$AR0 0ba8 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0ba9 64a0 movr'ls $ACC0, $AX0.H : $AX0.H, $AC0.M 0baa 1b7e srri @$AR3, $AC0.M 0bab 1b7a srri @$AR3, $AX0.H 0bac 0080 03e8 lri $AR0, #0x03e8 0bae 0088 0003 lri $WR0, #0x0003 // That's a short wrap - filter coefs? 0bb0 0085 0000 lri $IX1, #0x0000 0bb2 0087 0000 lri $IX3, #0x0000 // Load more parameters from *$AR2 0bb4 1fc2 mrr $AC0.M, $AR2 0bb5 195b lrri $AX1.H, @$AR2 0bb6 1959 lrri $AX1.L, @$AR2 0bb7 195f lrri $AC1.M, @$AR2 0bb8 195a lrri $AX0.H, @$AR2 0bb9 1c5e mrr $AR2, $AC0.M 0bba 1fda mrr $AC0.M, $AX0.H // Setup AR3, now ready to read in data. 0bbb 1c61 mrr $AR3, $AR1 0bbc 8a00 m2 0bbd 8f00 set40 // Start the pipeline 0bbe 191a lrri $AX0.H, @$AR0 0bbf b800 mulx $AX0.H, $AX1.H 0bc0 e350 maddx'l $AX0.H, $AX1.H : $AX0.H, @$AR0 0bc1 e250 maddx'l $AX0.H, $AX1.L : $AX0.H, @$AR0 0bc2 ea00 maddc $AC1.M, $AX1.L 0bc3 ea50 maddc'l $AC1.M, $AX1.L : $AX0.H, @$AR0 0bc4 e877 maddc'ln $AC0.M, $AX1.L : $AC0.M, @$AR3 0bc5 ece8 msubc'ldm $AC0.M, $AX1.L : $AX0.H, $AX1.L, @$AR0 0bc6 b200 mulxmvz $AX0.H, $AX1.L, $ACC0 0bc7 1127 0bd8 bloopi #0x27, 0x0bd8 // (half of 0x50) - 1, -1 due to the software pipelining. for (int i = 0; i < 0x27; i++) { 0bc9 e250 maddx'l $AX0.H, $AX1.L : $AX0.H, @$AR0 0bca e3a8 maddx'lsm $AX0.H, $AX1.H : $AX0.H, $AC0.M 0bcb 197e lrri $AC0.M, @$AR3 0bcc e800 maddc $AC0.M, $AX1.L 0bcd e850 maddc'l $AC0.M, $AX1.L : $AX0.H, @$AR0 0bce ea7f maddc'ln $AC1.M, $AX1.L : $AC1.M, @$AR3 0bcf eef8 msubc'ldm $AC1.M, $AX1.L : $AX0.H, $AX1.H, @$AR0 0bd0 bb00 mulxmvz $AX0.H, $AX1.H, $ACC1 0bd1 e350 maddx'l $AX0.H, $AX1.H : $AX0.H, @$AR0 0bd2 e2a9 maddx'lsm $AX0.H, $AX1.L : $AX0.H, $AC1.M 0bd3 197f lrri $AC1.M, @$AR3 0bd4 ea00 maddc $AC1.M, $AX1.L 0bd5 ea50 maddc'l $AC1.M, $AX1.L : $AX0.H, @$AR0 0bd6 e877 maddc'ln $AC0.M, $AX1.L : $AC0.M, @$AR3 0bd7 ece8 msubc'ldm $AC0.M, $AX1.L : $AX0.H, $AX1.L, @$AR0 0bd8 b200 mulxmvz $AX0.H, $AX1.L, $ACC0 } 0bd9 e250 maddx'l $AX0.H, $AX1.L : $AX0.H, @$AR0 0bda e3a8 maddx'lsm $AX0.H, $AX1.H : $AX0.H, $AC0.M 0bdb 197e lrri $AC0.M, @$AR3 0bdc e800 maddc $AC0.M, $AX1.L 0bdd e850 maddc'l $AC0.M, $AX1.L : $AX0.H, @$AR0 0bde ea7f maddc'ln $AC1.M, $AX1.L : $AC1.M, @$AR3 0bdf eef8 msubc'ldm $AC1.M, $AX1.L : $AX0.H, $AX1.H, @$AR0 0be0 bb00 mulxmvz $AX0.H, $AX1.H, $ACC1 0be1 1bff srrn @$AR3, $AC1.M 0be2 197f lrri $AC1.M, @$AR3 0be3 8e00 set16 0be4 8b00 m0 0be5 0088 ffff lri $WR0, #0xffff 0be7 1b5b srri @$AR2, $AX1.H 0be8 1b59 srri @$AR2, $AX1.L 0be9 1b5f srri @$AR2, $AC1.M 0bea 1b5e srri @$AR2, $AC0.M // 0beb 02df ret } void 0bec_Unk() { // 0bec 0eff lris $AC0.M, #0xff // 0bed 00fe 03f2 sr @0x03f2, $AC0.M *0x03f2 = 0xFF // 0bef 8100 clr $ACC0 // 0bf0 00fe 03f0 sr @0x03f0, $AC0.M // 0bf2 00fe 03f6 sr @0x03f6, $AC0.M *0x03f0 = 0x00 *0x03f6 = 0x00 // 0bf4 009e 0100 lri $AC0.M, #0x0100 // 0bf6 00fe 03f7 sr @0x03f7, $AC0.M *0x03f7 = 0x100 0bf8 00da 03f7 lr $AX0.H, @0x03f7 0bfa 009e 8000 lri $AC0.M, #0x8000 0bfc 5400 subr $ACC0, $AX0.H 0bfd 00fe 03f5 sr @0x03f5, $AC0.M // 0bff 0e30 lris $AC0.M, #0x30 // 0c00 00fe 03f3 sr @0x03f3, $AC0.M *0x03f3 = 0x0030 // 0c02 0e10 lris $AC0.M, #0x10 // 0c03 00fe 03f4 sr @0x03f4, $AC0.M *0x03f5 = 0x0010 // 0c05 009e 0096 lri $AC0.M, #0x0096 // 0c07 00fe 03f1 sr @0x03f1, $AC0.M *0x03f1 = 0x0096 // 0c09 02df ret } void 0c0a_Unk() { // 0c0a 0080 0a00 lri $AR0, #0x0a00 // 0c0c 8100 clr $ACC0 // 0c0d 00de 03f0 lr $AC0.M, @0x03f0 // 0c0f 8900 clr $ACC1 // 0c10 b100 tst $ACC0 // 0c11 0275 ifz // 0c12 0550 addis $ACC1, #0x50 $AC0.M = *(0x03f0); if (*(0x03f0) == 0) { $ACC1 = 0x50 << 16; } // 0c13 00ff 03f0 sr @0x03f0, $AC1.M *(0x03f0) = $ACC1; // 0c15 0200 0a60 addi $AC0.M, #0x0a60 // 0c17 1c7e mrr $AR3, $AC0.M // 0c18 0f4e lris $AC1.M, #0x4e $AC0.M += 0xa60; $AR3 = $AC0.M $AC1.M = 0x4e; // 0c19 02bf 00da call 0x00da 00da_CopyBuffer(src=0x0a00, dst=$AC0.M, #0x4e) // 0c1b 02df ret } // The control flow of this thing is NOT easy ... // Reads from buffer at 0x0a60 // Writes to buffer at 0x0a00 void 0c1c_ComputeReverbFrom0a60To0a00() { // 0c1c 00de 03f1 lr $AC0.M, @0x03f1 // 0c1e 0200 0a60 addi $AC0.M, #0x0a60 // 0c20 1c7e mrr $AR3, $AC0.M $AR3 = 0x0a60 + *(0x03f1); 0c21 8100 clr $ACC0 0c22 8900 clr $ACC1 0c23 009f 00a0 lri $AC1.M, #0x00a0 0c25 00de 03f1 lr $AC0.M, @0x03f1 0c27 5d00 sub $ACC1, $ACC0 0c28 0e50 lris $AC0.M, #0x50 0c29 0750 cmpis $ACC1, #0x50 0c2a 0270 ifge 0c2b 5d00 sub $ACC1, $ACC0 0c2c 00da 03f2 lr $AX0.H, @0x03f2 0c2e 8600 tstaxh $AX0.H 0c2f 0290 0c4d jge 0x0c4d 0c31 00de 03f3 lr $AC0.M, @0x03f3 0c33 5c00 sub $ACC0, $ACC1 0c34 0293 0c38 jle 0x0c38 0c36 029f 0c52 jmp 0x0c52 // done: 0c38 00db 03f7 lr $AX1.H, @0x03f7 0c3a 009e 8000 lri $AC0.M, #0x8000 0c3c 4600 addr $ACC0, $AX1.H 0c3d 029f 0c44 jmp 0x0c44 0c3f 00db 03f7 lr $AX1.H, @0x03f7 0c41 009e 8000 lri $AC0.M, #0x8000 0c43 5600 subr $ACC0, $AX1.H 0c44 00fe 03f5 sr @0x03f5, $AC0.M 0c46 1fda mrr $AC0.M, $AX0.H 0c47 7c00 neg $ACC0 0c48 1f5e mrr $AX0.H, $AC0.M 0c49 00fe 03f2 sr @0x03f2, $AC0.M 0c4b 029f 0c52 jmp 0x0c52 // done: 0c4d 00de 03f4 lr $AC0.M, @0x03f4 0c4f 5d00 sub $ACC1, $ACC0 0c50 0293 0c3f jle 0x0c3f done: 0c52 8900 clr $ACC1 0c53 00dd 03f5 lr $AC1.L, @0x03f5 0c55 1501 lsl $ACC1, #1 0c56 8100 clr $ACC0 0c57 00dc 03f6 lr $AC0.L, @0x03f6 0c59 008b 009f lri $WR3, #0x009f // 0xa0 wrap 0c5b 0080 0a00 lri $AR0, #0x0a00 0c5d 0900 lris $AX1.L, #0x00 // This is the loop that used to go crazy in the LLE emulation // before we fixed addarn to obey the wrapping register. // Feels like some crazy delay function with a slowly drifting delay time. // Could this be part of a reverb? Or just a flanger? // 0c5e 1150 0c65 bloopi #0x50, 0x0c65 for (int i = 0; i < 0x50; i++) { 0c60 1878 lrr $AX0.L, @$AR3 0c61 4c00 add $ACC0, $ACC1 0c62 1cfe mrr $IX3, $AC0.M 0c63 001f addarn $AR3, $IX3 0c64 1fd9 mrr $AC0.M, $AX1.L 0c65 1b18 srri @$AR0, $AX0.L $AX0.L = *AR3; $ACC0 += $ACC1; $IX3 = $AC0.M; $AR3 += $IX3; $AC0.M = $AX1.L; *(AR0++) = $AX0.L; } 0c66 009f 0a60 lri $AC1.M, #0x0a60 0c68 1fc3 mrr $AC0.M, $AR3 0c69 5c00 sub $ACC0, $ACC1 0c6a 00fe 03f1 sr @0x03f1, $AC0.M 0c6c 00fc 03f6 sr @0x03f6, $AC0.L 0c6e 008b ffff lri $WR3, #0xffff // restore wrap // 0c70 02df ret } void 0c71_AddBufferA00ToD60AndD00() { // 0c71 0f50 lris $AC1.M, #0x50 // 0c72 0080 0a00 lri $AR0, #0x0a00 // 0c74 0083 0d60 lri $AR3, #0x0d60 // 0c76 0098 3fff lri $AX0.L, #0x3fff // 0c78 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(0x0a00, 0x0d60, 0x50, 0x3fff) // 0c7a 0f50 lris $AC1.M, #0x50 // 0c7b 0080 0a00 lri $AR0, #0x0a00 // 0c7d 0083 0d00 lri $AR3, #0x0d00 // 0c7f 0098 3fff lri $AX0.L, #0x3fff // 0c81 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(0x0a00, 0x0d00, 0x50, 0x3fff) // 0c83 02df ret } void 0c84_FilterBufferInPlace(_sampleAddr($AR0), multiplier($AX0.H)) { // 0c84 8a00 m2 // 0c85 8f00 set40 // 0c86 8100 clr $ACC0 // 0c87 00de 0404 lr $AC0.M, @0x0404 // 0c89 b100 tst $ACC0 // 0c8a 0295 0c91 jz 0x0c91 if (*(0x0404)) { // 0c8c 8100 clr $ACC0 // 0c8d 00fe 0478 sr @0x0478, $AC0.M // 0c8f 00fe 0479 sr @0x0479, $AC0.M *0x0478 = 0; *0x0479 = 0; } // 0c91 00df 0479 lr $AC1.M, @0x0479 // 0c93 00db 0478 lr $AX1.H, @0x0478 // 0c95 0900 lris $AX1.L, #0x00 // 0c96 0084 0000 lri $IX0, #0x0000 // 0c98 1150 0ca1 bloopi #0x50, 0x0ca1 $AC1.M = *0x0479; $AX1.H = *0x0478; // ACC1 always contains the value from the previous iteration. for (int i = 0; i < 0x50; i++) { // 0c9a 199e lrrn $AC0.M, @$AR0 // 0c9b 5c7c sub'ln $ACC0, $ACC1 : $AC1.M, @$AR0 // 0c9c c000 mulc $AC0.M, $AX0.H // Where does AX0.H get set? // 0c9d 6e00 movp $ACC0 // 0c9e 1488 asl $ACC0, #8 // 0c9f 4a00 addax $ACC0, $AX1 // 0ca0 1b1e srri @$AR0, $AC0.M // 0ca1 1f7e mrr $AX1.H, $AC0.M *$AC0.M = *$AR0; $ACC0 -= $ACC1; ( $AC1.M = *AR0; ) $ACC0 = ($AC0.M * $AX0.H * 2 << 8) + ($AX1.L); *($AR0++) = $AC0.M; $AX1.H = $AC0.M; // Write back 0ca2 00fb 0478 sr @0x0478, $AX1.H 0ca4 00ff 0479 sr @0x0479, $AC1.M // 0ca6 8b00 m0 // 0ca7 8e00 set16 // 0ca8 02df ret } // Called from both volume handlers. // ACC1 is volume, AX is volume delta. void 0ca9_RampedMultiplyAddBuffer(Volume($ACC1), Delta($AX0), InBuffer($AR0), Buffer($AR3)) { // 0ca9 b900 tst $ACC1 // 0caa 0294 0caf jnz 0x0caf if (!ACC1) { // 0cac 6800 movax $ACC0, $AX0.L // 0cad b100 tst $ACC0 // 0cae 02d5 retz if (!AX0.L) // If incoming volume is zero and ramp delta is zero, // not really much point to do anything. return } 0caf 1c23 mrr $AR1, $AR3 0cb0 197e lrri $AC0.M, @$AR3 // This is another heavily software pipelined loop, so it's very confusing. // See the docs for mulc and mulcac if you want to have any hope of understanding it. // // Produce the first result, so it's ready in the prod register. 0cb1 191b lrri $AX1.H, @$AR0 0cb2 d858 mulc'l $AC1.M, $AX1.H : $AX1.H, @$AR0 // 0cb3 1120 0cb9 bloopi #0x20, 0x0cb9 for (int i = 0; i < 0x20; i++) { 0cb5 dcd3 mulcac'ld $AC1.M, $AX1.H, $ACC0 : $AX0.L, $AX1.H, @$AR3 0cb6 6231 movr's $ACC0, $AX1.L : @$AR1, $AC0.M 0cb7 dcd3 mulcac'ld $AC1.M, $AX1.H, $ACC0 : $AX0.L, $AX1.H, @$AR3 0cb8 6231 movr's $ACC0, $AX1.L : @$AR1, $AC0.M // Store 1 // Walk the ramp. Somewhat odd that it's done only every 2 samples. 0cb9 4900 addax $ACC1, $AX0 } // 0cba 1108 0cbf bloopi #0x08, 0x0cbf for (int i = 0; i < 0x8; i++) { 0cbc dcd3 mulcac'ld $AC1.M, $AX1.H, $ACC0 : $AX0.L, $AX1.H, @$AR3 0cbd 6231 movr's $ACC0, $AX1.L : @$AR1, $AC0.M 0cbe dcd3 mulcac'ld $AC1.M, $AX1.H, $ACC0 : $AX0.L, $AX1.H, @$AR3 0cbf 6231 movr's $ACC0, $AX1.L : @$AR1, $AC0.M } // NOTE - The above two loops are very similar and the sum of their lengths is // 0x28 - which is half of 0x50. And each does two loads and two stores, so together // it's 50. Just strange that the addax is missing in the second loop. // It looks like we're dealing with crappy volume ramping - the delta is computed using // (vol2 - vol1) >> 5! That's why it can only ramp the volume the first 64 (0x20 * 2) samples! 0cc0 02df ret } // What a strange filter .. ORR? void 0cc1_StrangeORRFilter(_pBuffer(AR3)) { 0cc1 8f00 set40 0cc2 8d00 set15 // X multiplications unsigned 0cc3 1c03 mrr $AR0, $AR3 0cc4 00d9 038e lr $AX1.L, @0x038e 0cc6 0b04 lris $AX1.H, #0x04 // pipeline starts here. 0cc7 197a lrri $AX0.H, @$AR3 0cc8 b053 mulx'l $AX0.H, $AX1.L : $AX0.H, @$AR3 0cc9 b600 mulxmv $AX0.H, $AX1.L, $ACC0 0cca 1128 0ccf bloopi #0x28, 0x0ccf 0ccc 3ad3 orr'ld $AC0.M, $AX1.H : $AX0.L, $AX1.H, @$AR3 0ccd b630 mulxmv's $AX0.H, $AX1.L, $ACC0 : @$AR0, $AC0.M 0cce 3ad3 orr'ld $AC0.M, $AX1.H : $AX0.L, $AX1.H, @$AR3 0ccf b630 mulxmv's $AX0.H, $AX1.L, $ACC0 : @$AR0, $AC0.M 0cd0 8c00 clr15 0cd1 8e00 set16 // 0cd2 02df ret } // called from sync frame if (*0x042c != 0) // That is, if volume mode != 0. // It first seems to compute a lot of parameters and store them at 0x0b00 forwards. // Then it uses those as input for the usual (ramped?) mixes. void 0cd3_VolumeMixer1() { // 0cd3 00da 0485 lr $AX0.H, @0x0485 // 0cd5 8600 tstaxh $AX0.H // 0cd6 0295 0ce5 jz 0x0ce5 if (*0x0485 != 0) { // 0cd8 8100 clr $ACC0 // 0cd9 00de 042a lr $AC0.M, @0x042a // 0cdb 147f lsr $ACC0, #-1 // 0cdc 00fe 042b sr @0x042b, $AC0.M *(0x042b) = *(0x042a) >> 1; // 0cde b100 tst $ACC0 // 0cdf 0294 0ce5 jnz 0x0ce5 if (*0x042b == 0) { // 0ce1 009a 0001 lri $AX0.H, #0x0001 // 0ce3 00fa 0401 sr @0x0401, $AX0.H *(0x0401) = 1; // KeyOff } } // 0ce5 8f00 set40 // 0ce6 8100 clr $ACC0 // 0ce7 00de 0428 lr $AC0.M, @0x0428 // 0ce9 1478 lsr $ACC0, #-8 (ACC0 = *(0x0428) << 8); // 0cea 00df 0428 lr $AC1.M, @0x0428 // 0cec 0340 007f andi $AC1.M, #0x007f // 0cee 1f1e mrr $AX0.L, $AC0.M // 0cef 1f5f mrr $AX0.H, $AC1.M // 0cf0 0220 007f xori $ACC0, #0x007f // 0cf2 1f3e mrr $AX1.L, $AC0.M // 0cf3 0320 007f xori $ACC1, #0x007f // 0cf5 1f7f mrr $AX1.H, $AC1.M AX0.L = *(0x0428) >> 8; AX0.H = *(0x0428) & 0x7F; AX1.L = AX0.L ^ 0x7f; AX1.H = AX1.H ^ 0x7f; // 0cf6 8100 clr $ACC0 // 0cf7 8900 clr $ACC1 // 0cf8 009f 0200 lri $AC1.M, #0x0200 // 0cfa 1fd8 mrr $AC0.M, $AX0.L // 0cfb 4c00 add $ACC0, $ACC1 # broken disasm? this doesn't make much sense. // 0cfc 1c1e mrr $AR0, $AC0.M // 0cfd 1818 lrr $AX0.L, @$AR0 AR0 = AX0.L + 0x0200; AX0.L = *AR0; // 0cfe 1fda mrr $AC0.M, $AX0.H // 0cff 4c00 add $ACC0, $ACC1 // 0d00 1c1e mrr $AR0, $AC0.M // 0d01 181a lrr $AX0.H, @$AR0 AR0 = AX0.H + 0x200; AX0.H = *AR0; // 0d02 1fd9 mrr $AC0.M, $AX1.L // 0d03 4c00 add $ACC0, $ACC1 // 0d04 1c1e mrr $AR0, $AC0.M // 0d05 1819 lrr $AX1.L, @$AR0 AR0 = AX1.L + 0x200; AX1.L = *AR0 // 0d06 1fdb mrr $AC0.M, $AX1.H // 0d07 4c00 add $ACC0, $ACC1 // 0d08 1c1e mrr $AR0, $AC0.M // 0d09 181b lrr $AX1.H, @$AR0 AR0 = AX1.H + 0x200; AX1.H = *AR0; // 0d0a 0080 0b00 lri $AR0, #0x0b00 // 0d0c 9800 mul $AX1.L, $AX1.H // 0d0d ae00 mulxmv $AX0.L, $AX1.H, $ACC0 // 0d0e b630 mulxmv's $AX0.H, $AX1.L, $ACC0 : @$AR0, $AC0.M // 0d0f 9630 mulmv's $AX0.L, $AX0.H, $ACC0 : @$AR0, $AC0.M // 0d10 6e30 movp's $ACC0 : @$AR0, $AC0.M // 0d11 1b1e srri @$AR0, $AC0.M // The above is heavily "sw-pipelined" but I think it turns into: $AR0 = 0x0b00; *$AR0++ = AX1.L * AX1.H; *$AR0++ = AX0.L * AX1.H; *$AR0++ = AX0.H * AX1.L; *$AR0++ = AX0.L * AX0.H; // 0d12 0080 0b00 lri $AR0, #0x0b00 // 0d14 0081 0b04 lri $AR1, #0x0b04 // 0d16 00da 042a lr $AX0.H, @0x042a // 0d18 02bf 0d62 call 0x0d62 // some tricky multiplication 0d62_Mul4ByAX0H(0x0b00, 0x0b04, *(0x042a)); // 0d1a 0081 0b08 lri $AR1, #0x0b08 // 0d1c 0080 0b04 lri $AR0, #0x0b04 // 0d1e 00da 042a lr $AX0.H, @0x042a // interesting // 0d20 00de 0429 lr $AC0.M, @0x0429 // interesting // 0d22 c000 mulc $AC0.M, $AX0.H // 0d23 6e00 movp $ACC0 // 0d24 1481 asl $ACC0, #1 // 0d25 1f5e mrr $AX0.H, $AC0.M 0d62_Mul4ByAX0H(0x0b00, 0x0b04, (*(0x042a) * *(0x0429) << 1) >> 16); // 0d26 02bf 0d62 call 0x0d62 // some tricky multiplication // 0d28 0080 0b00 lri $AR0, #0x0b00 // 0d2a 0081 0b0c lri $AR1, #0x0b0c // 0d2c 8100 clr $ACC0 // 0d2d 8900 clr $ACC1 // 0d2e 00de 042b lr $AC0.M, @0x042b // interesting // 0d30 00df 042a lr $AC1.M, @0x042a // interesting // 0d32 00fe 042a sr @0x042a, $AC0.M *(0x042a) = *(0x042b); // 0d34 5c00 sub $ACC0, $ACC1 // 0d35 1f5e mrr $AX0.H, $AC0.M // 0d36 02bf 0d6b call 0x0d6b // some other tricky multiplication 0d6b_Mul4ByAC0M_Unsigned(0xb00, 0x0b0c, $AC0.M(*(0x042a) - *(0x042b))) // does not touch AX0.H // 0d38 0080 0b0c lri $AR0, #0x0b0c // 0d3a 0081 0b10 lri $AR1, #0x0b10 // 0d3c 00da 0429 lr $AX0.H, @0x0429 // interesting 0d3e 02bf 0d62 call 0x0d62 // some tricky multiplication 0d62_Mul4ByAX0H(0x0b0c, 0x0b10, *(0x0429)); // 0d40 0081 0b04 lri $AR1, #0x0b04 // 0d42 0082 0b0c lri $AR2, #0x0b0c // 0d44 0083 0d77 lri $AR3, #0x0d77 // So basically the below loop is: // For i in 0 to 8: // Call 0ca9_RampedMultiplyAddBuffer($AR0 = *0x038f, $AR3=0x0d77[i], AX0=0xb0c[i]<<11, AC1.M=0x0b04[i]) // 0d46 1108 0d5f bloopi #0x08, 0x0d5f for (int i = 0; i < 8; i++) { // 0d48 195f lrri $AC1.M, @$AR2 // 0d49 15fb asr $ACC1, #-5 // 0d4a 1f1d mrr $AX0.L, $AC1.L // 0d4b 1f5f mrr $AX0.H, $AC1.M // Compute volume delta AX0 = *AR2++ << 11; // 0d4c 193f lrri $AC1.M, @$AR1 AC1.M = *AR1++; // 0d4d 00e1 0b24 sr @0x0b24, $AR1 // 0d4f 00e2 0b25 sr @0x0b25, $AR2 // 0d51 021b ilrri $AC0.M, @$AR3 // Buffer address table lookup (see below) // 0d52 00e3 0b26 sr @0x0b26, $AR3 (Stash AR1, AR2, AR3) // 0d54 1c7e mrr $AR3, $AC0.M // 0d55 00c0 038f lr $AR0, @0x038f // 0d57 02bf 0ca9 call 0x0ca9 0ca9_RampedMultiplyAddBuffer(Volume($ACC1), Delta($AX0), InBuffer($AR0), Buffer($AR3)) // 0d59 00c1 0b24 lr $AR1, @0x0b24 // 0d5b 00c2 0b25 lr $AR2, @0x0b25 // 0d5d 00c3 0b26 lr $AR3, @0x0b26 (Restore AR1, AR2, AR3) 0d5f 0000 nop } // 0d60 8e00 set16 // 0d61 02df ret } void 0d62_Mul4ByAX0H(in_buffer($AR0), out_buffer($AR1), multiplicand($AX0.H)) { // 0d62 191f lrri $AC1.M, @$AR0 // 0d63 d078 mulc'l $AC1.M, $AX0.H : $AC1.M, @$AR0 // 0d64 d678 mulcmv'l $AC1.M, $AX0.H, $ACC0 : $AC1.M, @$AR0 // 0d65 d631 mulcmv's $AC1.M, $AX0.H, $ACC0 : @$AR1, $AC0.M // 0d66 191f lrri $AC1.M, @$AR0 // 0d67 d631 mulcmv's $AC1.M, $AX0.H, $ACC0 : @$AR1, $AC0.M // 0d68 6e31 movp's $ACC0 : @$AR1, $AC0.M // 0d69 1b3e srri @$AR1, $AC0.M // 0d6a 02df ret // The above is a crazy sw-pipelined way to write: for (int i = 0; i < 4; i++) { out_buffer[i] = (s16)in_buffer[i] * (s16)multiplicand >> 16; } } void 0d6b_Mul4ByAC0M_Unsigned(in_buffer($AR0), out_buffer($AR1), multiplicand($AX1.H)) { // 0d6b 8d00 set15 // 0d6c 1f7e mrr $AX1.H, $AC0.M // 0d6d 1918 lrri $AX0.L, @$AR0 // 0d6e a840 mulx'l $AX0.L, $AX1.H : $AX0.L, @$AR0 // 0d6f ae40 mulxmv'l $AX0.L, $AX1.H, $ACC0 : $AX0.L, @$AR0 // 0d70 ae31 mulxmv's $AX0.L, $AX1.H, $ACC0 : @$AR1, $AC0.M // 0d71 1918 lrri $AX0.L, @$AR0 // 0d72 ae31 mulxmv's $AX0.L, $AX1.H, $ACC0 : @$AR1, $AC0.M // 0d73 6e31 movp's $ACC0 : @$AR1, $AC0.M // 0d74 1b3e srri @$AR1, $AC0.M // 0d75 8c00 clr15 // 0d76 02df ret // The above is a crazy sw-pipelined way to write: for (int i = 0; i < 4; i++) { out_buffer[i] = in_buffer[i] * multiplicand >> 16; //(unsigned multiplication) } } // table for 0cd3_Unk // This is a bunch of buffer addresses! short table = {0x0d00, 0x0d60, 0x0f40, 0x0ca0, 0x0e80, 0x0ee0, 0x0c00, 0x0c50}; 0d77 0d00 0d78 0d60 0d79 0f40 0d7a 0ca0 0d7b 0e80 0d7c 0ee0 0d7d 0c00 0d7e 0c50 void 0d7f_ResampleAudioData(_src($AR0), _dest($AR1), param(AX1.L) = 0, _option??) { 0d7f 00f9 0361 sr @0x0361, $AX1.L // always 0 // 0d81 1fc0 mrr $AC0.M, $AR0 // 0d82 0200 fffc addi $AC0.M, #0xfffc // 0d84 1c1e mrr $AR0, $AC0.M // 0d85 1c5e mrr $AR2, $AC0.M // We read a little bit BEFORE the input. The next piece of code takes care of that... $AR0 = $AR0 - 4; $AR2 = $AR0; // 0x043c to 0x043f is storage for old sample data. 0d86 0083 043c lri $AR3, #0x043c // Pipelined tiny memcpy - first four are loads, last four are stores. middle two overlap. // 0d88 197e lrri $AC0.M, @$AR3 // 0d89 197f lrri $AC1.M, @$AR3 // 0d8a 80a2 nx'sl : $AC0.M, $AX0.H // 0d8b 64a3 movr'sl $ACC0, $AX0.H : $AC1.M, $AX0.H // 0d8c 6530 movr's $ACC1, $AX0.H : @$AR0, $AC0.M // 0d8d 1b1f srri @$AR0, $AC1.M for (int i = 0; i < 4; i++) *($AR0++) = *($AR3++); // Point $AR0 back at 4 words before the start of the in buffer. // 0d8e 1c02 mrr $AR0, $AR2 0d8f 8100 clr $ACC0 // 0d90 00de 0402 lr $AC0.M, @0x0402 // Ratio int // 0d92 00fe 0362 sr @0x0362, $AC0.M // 0d94 1474 lsr $ACC0, #-12 // 0d95 1f7e mrr $AX1.H, $AC0.M // 0d96 1f3c mrr $AX1.L, $AC0.L *0x0362 = PB.Ratio; $AX1 = PB.Ratio << 4; 0d97 8900 clr $ACC1 0d98 00dd 0430 lr $AC1.L, @0x0430 // Sample position frac 0d9a 1504 lsl $ACC1, #4 // $ACC0 here still contains ratio << 12; 0d9b 0604 cmpis $ACC0, #0x04 // 0d9c 0290 0df3 jge 0x0df3 // subroutine // If ratio too low, don't bother resampling? GOTONS JustCopyWithoutResampling; 0d9e 1fdd mrr $AC0.M, $AC1.L 0d9f 0082 02b0 lri $AR2, #0x02b0 // Store a ramp at 0x2b0? Lookup table for read addresses? 0da1 1050 loopi #0x50 0da2 4b2a addax's $ACC1, $AX1 : @$AR2, $AC1.L 0da3 1fbe mrr $AC1.L, $AC0.M 0da4 00fe 0360 sr @0x0360, $AC0.M 0da6 8900 clr $ACC1 0da7 1fbe mrr $AC1.L, $AC0.M 0da8 0af8 lris $AX0.H, #0xf8 0da9 009b 00fc lri $AX1.H, #0x00fc 0dab 00d8 0361 lr $AX0.L, @0x0361 // parameter was stashed here. // 0x02b0 is where the ramp from above is stored. 0dad 0082 02b0 lri $AR2, #0x02b0 0daf 0083 02b0 lri $AR3, #0x02b0 0db1 195e lrri $AC0.M, @$AR2 // I really don't understand what the purpose of this loop is. 0db2 3480 andr'ls $AC0.M, $AX0.H : $AX0.L, $AC0.M // 0db3 1128 0db8 bloopi #0x28, 0x0db8 for (int i = 0; i < 0x50; i += 2) { 0db5 367a andr'l $AC0.M, $AX1.H : $AC1.M, @$AR2 0db6 35b3 andr'sl $AC1.M, $AX0.H : $AC1.M, $AX1.H 0db7 3772 andr'l $AC1.M, $AX1.H : $AC0.M, @$AR2 0db8 34bb andr'slm $AC0.M, $AX0.H : $AC1.M, $AX1.H } 0db9 8a00 m2 // All muls doubled. // 0x02b0 is where the ramp from above is stored. 0dba 0082 02b0 lri $AR2, #0x02b0 0dbc 00dd 0430 lr $AC1.L, @0x0430 0dbe 1504 lsl $ACC1, #4 0dbf 1fe0 mrr $AC1.M, $AR0 0dc0 8100 clr $ACC0 0dc1 00de 0362 lr $AC0.M, @0x0362 0dc3 1474 lsr $ACC0, #-12 0dc4 1f7e mrr $AX1.H, $AC0.M 0dc5 1f3c mrr $AX1.L, $AC0.L // Resample with some nice filter of some sort, using unreadable // pipelined DSP code... gah. 0dc6 8f00 set40 // Loaded ACx.M values extend to the entire ACC. Don't see any actual use though. // Yep, this pretty much confirms that 0x02b0 is precomputed read addresses. 0dc7 1943 lrri $AR3, @$AR2 0dc8 4bc3 addax'ld $ACC1, $AX1 : $AX0.L, $AX1.L, @$AR3 0dc9 90c3 mul'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0dca f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0dcb f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0dcc f200 madd $AX0.L, $AX0.H 0dcd fe00 movpz $ACC0 0dce 1c1f mrr $AR0, $AC1.M 0dcf 1943 lrri $AR3, @$AR2 0dd0 4bc3 addax'ld $ACC1, $AX1 : $AX0.L, $AX1.L, @$AR3 0dd1 90c3 mul'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 // 0dd2 114e 0dda bloopi #0x4e, 0x0dda // Count the stores - 0x4e stores in the main loop, two more afterwards. // Deeply pipelined. for (int i = 0; i < 0x4e; i++) { 0dd4 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0dd5 f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0dd6 f231 madd's $AX0.L, $AX0.H : @$AR1, $AC0.M 0dd7 1c1f mrr $AR0, $AC1.M 0dd8 1943 lrri $AR3, @$AR2 0dd9 4bc3 addax'ld $ACC1, $AX1 : $AX0.L, $AX1.L, @$AR3 0dda 92c3 mulmvz'ld $AX0.L, $AX0.H, $ACC0 : $AX0.L, $AX1.L, @$AR3 } 0ddb f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0ddc f2c3 madd'ld $AX0.L, $AX0.H : $AX0.L, $AX1.L, @$AR3 0ddd f231 madd's $AX0.L, $AX0.H : @$AR1, $AC0.M 0dde fe00 movpz $ACC0 0ddf 1b3e srri @$AR1, $AC0.M // Things back to normal. // 0de0 8b00 m0 // 0de1 8e00 set16 back_from_JustCopyWithoutResampling: 0de2 00fe 0433 sr @0x0433, $AC0.M 0de4 1c1f mrr $AR0, $AC1.M 0de5 150c lsl $ACC1, #12 0de6 0340 0fff andi $AC1.M, #0x0fff 0de8 00ff 0430 sr @0x0430, $AC1.M // Store the last 4 samples or something undecoded // back into the PB. 0dea 0083 043c lri $AR3, #0x043c 0dec 191e lrri $AC0.M, @$AR0 0ded 191f lrri $AC1.M, @$AR0 0dee 80a0 nx'ls : $AX0.H, $AC0.M 0def 64a1 movr'ls $AC0.M, $AX0.H : $AX0.H, $AC1.M 0df0 6533 movr's $AC1.M, $AX0.H : @$AR3, $AC0.M 0df1 1b7f srri @$AR3, $AC1.M // 0df2 02df ret return; JustCopyWithoutResampling: // 0df3 1fe0 mrr $AC1.M, $AR0 // 0df4 1c1f mrr $AR0, $AC1.M // This instruction looks very pointless. // 0df5 1128 0dfc bloopi #0x28, 0x0dfc // 0df7 4b70 addax'l $ACC1, $AX1 : $AC0.M, @$AR0 // 0df8 1b3e srri @$AR1, $AC0.M // 0df9 1c1f mrr $AR0, $AC1.M // 0dfa 4b70 addax'l $ACC1, $AX1 : $AC0.M, @$AR0 // 0dfb 1b3e srri @$AR1, $AC0.M // 0dfc 1c1f mrr $AR0, $AC1.M for (int i = 0; i < 0x50; i++) { $ACC1 += $AX1; // This is to still advance the playback position. $AC0.M = *($AR0++); *($AR1++) = $AC0.M; $AR0 = $AC1.M; // Undo the increment } // Looks like $AR0 stays unchanged, while $AR1 gets incremented by 0x50. 0dfd 029f 0de2 jmp 0x0de2 } // Small utility jumped to from SyncFrame. // sets 50 shorts from 0x520 to zero. void 0dff_Zero520_50() { 0dff 0083 0520 lri $AR3, #0x0520 0e01 00de 0433 lr $AC0.M, @0x0433 0e03 1050 loopi #0x50 0e04 1b7e srri @$AR3, $AC0.M // 0e05 029f 02d8 jmp 0x02d8 GOTO ContinueWithBlock: // in SyncFrame } // No-one calls this routine. void 0e07_UnUsed() { 0e07 1c20 mrr $AR1, $AR0 0e08 185f lrr $AC1.M, @$AR2 0e09 1f7f mrr $AX1.H, $AC1.M 0e0a 193a lrri $AX0.H, @$AR1 0e0b 6400 movr $ACC0, $AX0.H 0e0c 0078 0e11 bloop $AX0.L, 0x0e11 0e0e 5659 subr'l $ACC0, $AX1.H : $AX1.H, @$AR1 0e0f 6730 movr's $ACC1, $AX1.H : @$AR0, $AC0.M 0e10 5551 subr'l $ACC1, $AX0.H : $AX0.H, @$AR1 0e11 6438 movr's $ACC0, $AX0.H : @$AR0, $AC1.M 0e12 1a5b srr @$AR2, $AX1.H // 0e13 02df ret } void 0e14_DolbyInit() { // Init parameters/coefficients for UnknownFilter 0e14 0098 8240 lri $AX0.L, #0x8240 0e16 00f8 04e8 sr @0x04e8, $AX0.L 0e18 0098 7fff lri $AX0.L, #0x7fff 0e1a 00f8 04e9 sr @0x04e9, $AX0.L 0e1c 0098 7dbf lri $AX0.L, #0x7dbf 0e1e 00f8 04ea sr @0x04ea, $AX0.L 0e20 0098 843f lri $AX0.L, #0x843f 0e22 00f8 04eb sr @0x04eb, $AX0.L 0e24 0098 b23b lri $AX0.L, #0xb23b 0e26 00f8 04f0 sr @0x04f0, $AX0.L 0e28 0098 7fff lri $AX0.L, #0x7fff 0e2a 00f8 04f1 sr @0x04f1, $AX0.L 0e2c 0098 4dc4 lri $AX0.L, #0x4dc4 0e2e 00f8 04f2 sr @0x04f2, $AX0.L 0e30 0098 d808 lri $AX0.L, #0xd808 0e32 00f8 04f3 sr @0x04f3, $AX0.L // Zero the UnknownFilter states. 0e34 0098 0000 lri $AX0.L, #0x0000 0e36 0080 04ec lri $AR0, #0x04ec 0e38 1004 loopi #0x04 0e39 1b18 srri @$AR0, $AX0.L 0e3a 0080 04f4 lri $AR0, #0x04f4 0e3c 1004 loopi #0x04 0e3d 1b18 srri @$AR0, $AX0.L // 0e3e 02df ret } // I'm going to guess that this is Dolby mixing. void 0e3f_DolbyMixdown() { 0e3f 0080 0f40 lri $AR0, #0x0f40 0e41 0083 0b00 lri $AR3, #0x0b00 0e43 8900 clr $ACC1 0e44 0f50 lris $AC1.M, #0x50 0e45 0098 6784 lri $AX0.L, #0x6784 //0e47 02bf 00fa call 0x00fa // XorBuffer 00fa_BufferMultiply(src($AR0), dst($AR3), count($AC1.M), $mult($AX0.L)) 0e49 0080 04e8 lri $AR0, #0x04e8 0e4b 0082 04ec lri $AR2, #0x04ec 0e4d 0081 0b00 lri $AR1, #0x0b00 0e4f 02bf 0ba4 call 0x0ba4 // 0ba4_UnknownFilter 0ba4_UnknownFilter(params($AR0), buffer($AR1), filter_state($AR2)) 0e51 8900 clr $ACC1 0e52 0f50 lris $AC1.M, #0x50 0e53 0080 0b00 lri $AR0, #0x0b00 0e55 0083 0d00 lri $AR3, #0x0d00 0e57 0098 7fff lri $AX0.L, #0x7fff // 0e59 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) 0e5b 8900 clr $ACC1 0e5c 0f50 lris $AC1.M, #0x50 0e5d 0080 0b00 lri $AR0, #0x0b00 0e5f 0083 0d60 lri $AR3, #0x0d60 0e61 0098 b820 lri $AX0.L, #0xb820 // 0e63 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) 0e65 0080 0ca0 lri $AR0, #0x0ca0 0e67 0083 0b00 lri $AR3, #0x0b00 0e69 8900 clr $ACC1 0e6a 0f50 lris $AC1.M, #0x50 0e6b 0098 6784 lri $AX0.L, #0x6784 // 0e6d 02bf 00fa call 0x00fa // XorBuffer 00fa_BufferMultiply(src($AR0), dst($AR3), count($AC1.M), $mult($AX0.L)) 0e6f 0080 04e8 lri $AR0, #0x04e8 0e71 0082 04f4 lri $AR2, #0x04f4 0e73 0081 0b00 lri $AR1, #0x0b00 // 0e75 02bf 0ba4 call 0x0ba4 // 0ba4_UnknownFilter 0ba4_UnknownFilter(params($AR0), buffer($AR1), filter_state($AR2)) 0e77 8900 clr $ACC1 0e78 0f50 lris $AC1.M, #0x50 0e79 0080 0b00 lri $AR0, #0x0b00 0e7b 0083 0d00 lri $AR3, #0x0d00 0e7d 0098 47e0 lri $AX0.L, #0x47e0 // 0e7f 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) 0e81 8900 clr $ACC1 0e82 0f50 lris $AC1.M, #0x50 0e83 0080 0b00 lri $AR0, #0x0b00 0e85 0083 0d60 lri $AR3, #0x0d60 0e87 0098 8001 lri $AX0.L, #0x8001 // 0e89 02bf 00eb call 0x00eb 00eb_Unk_BufferMultAddToDest(_Src=($AR0), _Dest($AR3), _size($AC1.M), _factor($AX0.L)) // 0e8b 02df ret } void Nops() { 0e8c 0000 nop 0e8d 0000 nop 0e8e 0000 nop 0e8f 0000 nop }