11 March 2016 - Forum Rules
Started by lytron, October 16, 2013, 12:06:53 PM
Quote from: Chippy2000 on December 24, 2013, 02:18:58 PMI think my brain just exploded from this stuff
Quote from: Bregalad on March 22, 2014, 03:23:56 PMYeah you got it right, the engines always have 16 logical tracks, the last 8 alias the first 8 and uses the same physical sound channels. The first 8 are typically used for music and the last 8 for sound effects, but who knowns, nothing makes this an absolute rule.
0B68: 7D MOV A, X ;Compute and write volume to DSP0B69: 28 0F AND A, #$0F0B6B: C4 34 MOV $34, A ; ly: A = 0000.dcba0B6D: 9F XCN A ; ly: A = dcba.00000B6E: 5C LSR A ; ly: A = 0dcb.a0000B6F: C4 35 MOV $35, A0B71: E4 B2 MOV A, $B2 ; ly: Active Channel Flag register0B73: 24 C9 AND A, $C90B75: D0 03 BNE $0B7A ; ly: Leave out the JMP-command if the active Channel is the one in $C90B77: 5F 0A 0C JMP $0C0A0B7A: 8F 80 36 MOV $36, #$800B7D: 03 B1 1C BBS B0 $B1, $0B9C ; ly: Leave out the following step if the LSB of $B1 is set0B80: F5 41 FC MOV A, $FC41+X0B83: 60 CLRC0B84: 95 E1 FD ADC A, $FDE1+X0B87: 2D PUSH A0B88: F5 E1 FD MOV A, $FDE1+X0B8B: AE POP A0B8C: 30 06 BMI $0B94 ; ly: Branch if A is between #$80 and #$FF0B8E: 90 08 BCC $0B98 ; ly: Branch if A is between #$00 and #$7F and Carry is clear0B90: E8 FF MOV A, #$FF ; ly: This happens if A is between #$00 and #$7F and Carry is set0B92: 2F 04 BRA $0B980B94: B0 02 BCS $0B98 ; ly: Branch if A is between #$80 and #$FF and Carry is set0B96: E8 00 MOV A, #$00 ; ly: This happens if A is between #$80 and #$FF and Carry is clear0B98: 48 FF EOR A, #$FF ; ly: BEFORE the EOR is executed (the result after the EOR is at the end of the line): ; ly: A is #$00 if it was between #$80 and #$FF + c=0 = #$FF ; ly: A is between #$00 and #$7F if it was between #$00 and #$7F + c=0 = #$80 to #$FF ; ly: A is between #$80 and #$FF if it was between #$80 and #$FF + c=1 = #$00 to #$7F ; ly: A is #$FF if it was between #$00 and #$7F + c=1 = #$000B9A: C4 36 MOV $36, A ; ly: Store this convoluted mess0B9C: F5 01 FC MOV A, $FC01+X0B9F: FD MOV Y, A0BA0: C4 37 MOV $37, A0BA2: F5 01 FD MOV A, $FD01+X0BA5: 1C ASL A0BA6: F0 10 BEQ $0BB8 ; ly: Branch if $FD01+X = #%x000.00000BA8: 90 03 BCC $0BAD ; ly: Branch if $FD01+X = #%0xxx.xxxx (a positive number)0BAA: 48 FF EOR A, #$FF ; ly: this is only executed if $FD01+X is between #$81 and #$FF0BAC: BC INC A ; ly: This turns a negative number into a positive0BAD: CF MUL YA ; ly: Multiply $FC01+X with two times of the absolute value of $FD01+X0BAE: B0 08 BCS $0BB8 ; ly: Branch if $FD01+X = #%1xxx.xxxx (a negative number)0BB0: DD MOV A, Y ; ly: Only the high byte of the result is relevant...?0BB1: 84 37 ADC A, $37 ; ly: Add $FC01+X to the result's high byte (see lines $0B9C and $0BA0)0BB3: 90 02 BCC $0BB7 ; ly: Branch if the result is lower than #$1000BB5: E8 FF MOV A, #$FF ; ly: If the result of the addition is higher than #$FF, YA= #$FFFF0BB7: FD MOV Y, A ; ly: If the result of the addition was lower, than that byte is now both in Y and A0BB8: C8 10 MOV X, #$100BBA: B0 0E BCS $0BCA ; ly: Branch if the result of the addition was higher than #$FF0BBC: E4 84 MOV A, $840BBE: CF MUL YA0BBF: E5 88 FF MOV A, $FF880BC2: 74 5D CMP A, $5D+X ; ly: Compare with Instrument Source Number0BC4: F0 07 BEQ $0BCD ; ly: Branch if they are the same0BC6: E4 AC MOV A, $AC0BC8: 2F 02 BRA $0BCC0BCA: E8 90 MOV A, #$900BCC: CF MUL YA0BCD: CB 37 MOV $37, Y0BCF: C8 10 MOV X, #$100BD1: 90 06 BCC $0BD90BD3: AA B1 60 MOV1 C, $60B1 ; ly: Carry = bit 6 of B10BD6: CA 34 00 MOV1 $0034, C ; ly: bit 0 of 34 = Carry0BD9: E4 36 MOV A, $360BDB: FD MOV Y, A0BDC: E4 37 MOV A, $370BDE: CF MUL YA0BDF: E4 B2 MOV A, $B20BE1: 24 B3 AND A, $B30BE3: F0 02 BEQ $0BE7 ; ly: Branch if current channel is not in $B3-AND-Mask0BE5: 8D 00 MOV Y, #$00 ; ly: If it is, $00CD/E is cleared0BE7: DD MOV A, Y0BE8: EB 34 MOV Y, $340BEA: D6 CD 00 MOV $00CD+Y, A ; ly: This changes either CD or CE, depending on bit 6 of B1 (see line $0BD3)0BED: 5C LSR A0BEE: FD MOV Y, A0BEF: E4 35 MOV A, $350BF1: C8 10 CMP X, #$100BF3: 90 05 BCC $0BFA0BF5: 73 B1 02 BBC B3 $B1, $0BFA ; ly: Branch if Bit 3 of $B1 is clear0BF8: 48 01 EOR A, #$01 ; ly: If Bit 3 of $B1 is set, EOR the output address with #$010BFA: C4 F2 MOV $F2, A ; ly: Sets DSP register address0BFC: CB F3 MOV $F3, Y ; ly: Put Y into the DSP address that is in A0BFE: E4 36 MOV A, $360C00: 48 FF EOR A, #$FF0C02: EA 34 00 NOT1 $0034 ; ly: Invert bit 0 of $340C05: AB 35 INC $350C07: 33 35 D1 BBC B1 $35, $0BDB ; ly: Branch if Bit 1 of $35 is clear0C0A: 22 35 SET1 $35, 10C0C: E4 B2 MOV A, $B20C0E: 24 CA AND A, $CA0C10: F0 43 BEQ $0C55 ; ly: Jump to exit if current Channel isn't the one in $CA
Quote from: Disch on March 22, 2014, 11:39:46 PMI wish I saw this thread sooner.I just did a bunch of disassembling with Secret of Mana's music engine (I'm working on a general purpose SPC editor and am starting with that game)My notes are here:https://www.dropbox.com/s/mnruanfhptp458o/SoM_spc_disassembly.zip
; $0C5E-$0CAE - FUNCTION: (?) Trembolo-/Vibrato-Related; $0CAF-$0CC6 - FUNCTION: REACT TO 5A22: Do something or not?; $0CC7-$0CF0 - FUNCTION: REACT TO 5A22: Case decider, jump to Sub-Function; $0CF1-$0????- FUNCTION: REACT TO 5A22: ; $0FF9-$100E - FUNCTION: REACT TO 5A22: ; $11EA-$1213 - FUNCTION: REACT TO 5A22: Wait-/Transfer-Commands; $1214-$1236 - FUNCTION: 11EA-FUNCTION: Transfer three bytes at once; $1237-$1250 - FUNCTION: 11EA-FUNCTION: Transfer two bytes at once; $1251-$1262 - FUNCTION: 11EA-FUNCTION: Transfer one byte at once; $1263-$126F - FUNCTION: 11EA-FUNCTION: Wait; $1270-$12AB - FUNCTION: 11EA-FUNCTION: Internal SPC RAM transfer; $12DE-$132E - FUNCTION: Echo Initialisation (+ Changing of the Echo Buffer address [= size]), Starting Timer 1 & 2; $132F-$138D - FUNCTION: Clears Echo Buffer, waits until new Echo Buffer is accepted by Hardware, transfer Echo FIR Filter Coefficient set; $138E-$13E4 - FUNCTION: Echo the multiplication results of four OUTX-registers back to the 5A22; $1418-$1427 - Jump Addresses (16-bit-values) for $11EA
0CAF: F8 F4 MOV X, $F4 ;Communication with main CPU0CB1: F0 3D BEQ $0CF0 ; ly: If $F4 (this is $2140 on the 65816/5A22 side) is empty, exit0CB3: BA F6 MOVW YA, $F6 ; ly: Else, store the content of the four I/O registers in $B4-$B70CB5: DA B6 MOVW $B6, YA0CB7: BA F4 MOVW YA, $F40CB9: DA B4 MOVW $B4, YA0CBB: 68 83 CMP A, #$83 ; ly: A can contain the value of either $F4 or $F5, I expect it to be $F40CBD: D0 02 BNE $0CC10CBF: CB F5 MOV $F5, Y ; ly: The value of Y is only given back to $F5 if $F4 = #$830CC1: C4 F4 MOV $F4, A0CC3: 64 F4 CMP A, $F40CC5: F0 FC BEQ $0CC3 ;Wait responce from main CPU ; ly: Wait until the 5A22 gives back the value stored in $F4 ; ly: JUMP TABLE: ; ly: (X = A = value in $F4/$2140) ; ly: X = #$01: $0CF1 ; ly: X = #$02: $0E10 ; ly: X = #$FE: $11EA : ly: Everything else between X = #$00 and #$0F: $0CF0 (EXIT) : ly: Everything else between X = #$10 and #$1F: $0EF6 : ly: Everything else between X = #$20 and #$7F: $0CF0 (EXIT) : ly: Everything else between X = #$80 and #$8F: $0FF9 : ly: Everything else between X = #$90 and #$EF: $0FF9 (But does nothing instantly exit) : ly: Everything else between X = #$F0 and #$FF: $0FF90CC7: 5D MOV X, A0CC8: 10 0D BPL $0CD7 ; ly: Branch if A/X is between #$00 and #$7F0CCA: C8 FE CMP X, #$FE ; ly: A/X is between #$80 and #$FF0CCC: D0 03 BNE $0CD10CCE: 5F EA 11 JMP $11EA ;?0CD1: 8F 00 F4 MOV $F4, #$000CD4: 5F F9 0F JMP $0FF9 ;?0CD7: C8 01 CMP X, #$01 ; ly: A/X is between #$00 and #$7F0CD9: F0 16 BEQ $0CF10CDB: 8F 00 F4 MOV $F4, #$000CDE: C8 02 CMP X, #$020CE0: D0 03 BNE $0CE50CE2: 5F 10 0E JMP $0E100CE5: C8 10 CMP X, #$100CE7: 90 07 BCC $0CF00CE9: C8 20 CMP X, #$200CEB: B0 03 BCS $0CF00CED: 5F F6 0E JMP $0EF60CF0: 6F RET ; ly: The following command has to receive two double-bytes through $2142/$2143 ; ly: In the first double-byte, $2142 has to contain the Echo Delay value. The ... ; ly: ... bits 4-7 of this value will be removed. ; ly: The second double-byte is used when the subroutine at $132F is called. ; ly: $2142 gets stored in $C7 (is the Echo FIR filter coefficient set, only Bits 0 & 1 are relevant. ; ly: $2143 gets stored in $C8 (Echo Feedback value, DSP-Register $0D). ; ly: Before anything gets done with these value, all channels get a key-off and ... ; ly: ... the SPC waits 0.008 sec (= one tick on a 125 Hz timer) for this key-off to happen.0CF1: E8 FF MOV A, #$FF ; ly: Key Off all voices0CF3: 8D 5C MOV Y, #$5C0CF5: 3F 1B 06 CALL $061B0CF8: 8F 00 F1 MOV $F1, #$00 ; ly: Disable all Timers0CFB: 8F 40 FA MOV $FA, #$40 ; ly: Set Timer 0 divider to #$40 (125 Hz)0CFE: 8F 01 F1 MOV $F1, #$01 ; ly: Enable Timer 00D01: E4 FD MOV A, $FD ; ly: ... useless line?0D03: E4 FD MOV A, $FD0D05: F0 FC BEQ $0D03 ; ly: Wait until the first tick of Timer 00D07: 8F 00 F1 MOV $F1, #$00 ; ly: Disable all Timers0D0A: 8F 24 FA MOV $FA, #$24 ; ly: Set Timer 0 divider to #$24 (222 Hz)0D0D: 8F 01 F1 MOV $F1, #$01 ; ly: Enable Timer 00D10: F8 F4 MOV X, $F40D12: E4 F6 MOV A, $F60D14: 28 0F AND A, #$0F ; ly: $DF = Lower nybble of $F6(/$2142)0D16: C4 DF MOV $DF, A0D18: E4 F7 MOV A, $F70D1A: C4 A7 MOV $A7, A ; ly: $A7 = $F7(/$2143) (New Echo volume for left and right side)0D1C: D8 F4 MOV $F4, X0D1E: 3E F4 CMP X, $F40D20: F0 FC BEQ $0D1E ; ly: Wait until the 5A22 responses0D22: F8 F4 MOV X, $F4 ; ly: LOAD SECOND DOUBLE-BYTE FROM $F6/70D24: BA F6 MOVW YA, $F60D26: C4 C8 MOV $C8, A ; ly: $C8 = $F60D28: CB C7 MOV $C7, Y ; ly: $C7 = $F70D2A: D8 F4 MOV $F4, X0D2C: 3E F4 CMP X, $F40D2E: F0 FC BEQ $0D2C ; ly: Wait until the 5A22 responses0D30: 3F DE 12 CALL $12DE ; ly: Setup new Echo Buffer size according to $DF0D33: 3F EA 11 CALL $11EA0D36: E8 00 MOV A, #$000D38: FD MOV Y, A0D39: DA AD MOVW $AD, YA ; ly: Clear the double-byte that gets transfered through $F6/$F7 to the 5A220D3B: DA BC MOVW $BC, YA ; ly: 0D3D: DA BE MOVW $BE, YA ; ly: 0D3F: DA C0 MOVW $C0, YA ; ly: 0D41: C4 B8 MOV $B8, A ; ly: Clear Echo enable0D43: C4 B9 MOV $B9, A ; ly: Clear Noise enable0D45: C4 BA MOV $BA, A ; ly: Clear Pitch modulation0D47: 32 B1 CLR1 $B1, 10D49: DA AF MOVW $AF, YA ; ly: Clear Key on/Key off0D4B: C4 90 MOV $90, A0D4D: E2 90 SET1 $90, 7 ; ly: Sets global song tempo ($90) to #$800D4F: C4 97 MOV $97, A0D51: C4 9C MOV $9C, A0D53: E2 9C SET1 $9C, 70D55: C4 A3 MOV $A3, A0D57: DA C9 MOVW $C9, YA ; ly: 0D59: DA C2 MOVW $C2, YA0D5B: C4 C4 MOV $C4, A0D5D: DA CB MOVW $CB, YA0D5F: 8F 01 7D MOV $7D, #$010D62: 8F FF 7E MOV $7E, #$FF0D65: 8F FF AC MOV $AC, #$FF0D68: E3 B1 02 BBS B7 $B1, $0D6D ; ly: Branch if Bit 7 of $B1 is set0D6B: C4 B3 MOV $B3, A0D6D: 3F 2F 13 CALL $132F ; ly: New Echo buffer initialization0D70: 73 04 03 BBC B3 $04, $0D76 ; ly: If Bit 3 of $04 is clear, branch0D73: 5F DD 0D JMP $0DDD ; ly: Reset0D76: CD 10 MOV X, #$100D78: F5 01 1A MOV A, $1A01+X ; ly: Transfer Channel Data pointers from $1A02 to $1A110D7B: D4 0B MOV $0B+X, A ; ly: Bytewise transfer loop0D7D: 1D DEC X0D7E: D0 F8 BNE $0D78 ; ly: This loop is left when X reaches 0, so $1A01 is never read and it is never something stored in $0B in this loop0D80: E5 00 1A MOV A, $1A00 ; ly: Transfer $1A00 to $060D83: C4 06 MOV $06, A0D85: E5 01 1A MOV A, $1A01 ; ly: Transfer $1A01 to $070D88: C4 07 MOV $07, A0D8A: E8 14 MOV A, #$140D8C: 8D 1A MOV Y, #$1A0D8E: 9A 06 SUBW YA, $06 ; ly: YA = #$1A14 - value of $060D90: DA 06 MOVW $06, YA0D92: CD 0E MOV X, #$0E ; ly: Setting up the downwards counter for the loop from $D9F to $DBC0D94: 8F 80 B2 MOV $B2, #$80 ; ly: Setting up a "bitwise counter". This can be decremented through... ; ly: ... Logical Shift Right eight times, before it is = 0 (see lines $DBA and $DBC)0D97: E5 12 1A MOV A, $1A120D9A: EC 13 1A MOV Y, $1A130D9D: DA 34 MOVW $34, YA0D9F: F4 0C MOV A, $0C+X0DA1: FB 0D MOV Y, $0D+X0DA3: 5A 34 CMPW YA, $34 ; ly: Compare $34/5 with $0C/D+X (Channel's Song Data Pointer)0DA5: F0 11 BEQ $0DB8 ; ly: If equal: Skip this channel0DA7: 09 B2 AD OR $B2, $AD0DAA: 7A 06 ADDW YA, $060DAC: D4 0C MOV $0C+X, A0DAE: DB 0D MOV $0D+X, Y0DB0: E8 FF MOV A, #$FF0DB2: C5 88 FF MOV $FF88, A0DB5: 3F E5 0D CALL $0DE50DB8: 1D DEC X ; ly: Decrement X twice: Next channel0DB9: 1D DEC X0DBA: 4B B2 LSR $B2 ; ly: $B2 was set to #$80 in line $0D94, so this loop gets repeated eight times0DBC: D0 E1 BNE $0D9F0DBE: E4 B7 MOV A, $B70DC0: 28 F0 AND A, #$F00DC2: 9F XCN A0DC3: 8D 11 MOV Y, #$110DC5: CF MUL YA0DC6: C4 84 MOV $84, A0DC8: E4 B6 MOV A, $B60DCA: 28 F0 AND A, #$F00DCC: C4 B5 MOV $B5, A0DCE: E4 B7 MOV A, $B70DD0: 28 0F AND A, #$0F0DD2: 04 B5 OR A, $B50DD4: C4 B5 MOV $B5, A0DD6: E8 81 MOV A, #$810DD8: C4 B4 MOV $B4, A0DDA: 3F 11 10 CALL $10110DDD: CD FF MOV X, #$FF ; ly: Reset stack0DDF: BD MOV SP, X0DE0: E4 FD MOV A, $FD ; ly: Empty Timer 0 tick counter0DE2: 5F 79 02 JMP $0279 ; ly: Jump back to the beginning of the Main Loop0DE5: 7D MOV A, X ; ly: Setting up the repition level counter0DE6: 1C ASL A ; ly: $5C (channel 0) = #$00, $5E (channel 1) = #$040DE7: D4 5C MOV $5C+X, A ; ly: $60 (channel 2) = #$08 and so on0DE9: E8 00 MOV A, #$000DEB: D5 20 01 MOV $0120+X, A0DEE: D5 80 FD MOV $FD80+X, A0DF1: D5 81 FD MOV $FD81+X, A0DF4: D5 21 01 MOV $0121+X, A0DF7: D5 40 01 MOV $0140+X, A0DFA: D5 41 01 MOV $0141+X, A0DFD: D5 E0 FD MOV $FDE0+X, A0E00: D5 E1 FD MOV $FDE1+X, A0E03: D5 40 FD MOV $FD40+X, A0E06: D5 61 FD MOV $FD61+X, A0E09: D5 80 FC MOV $FC80+X, A0E0C: BC INC A0E0D: D4 3C MOV $3C+X, A0E0F: 6F RET0E10: FA B5 2C MOV $2C, $B5 ; ly: value of $F5 0E13: 8F 00 2D MOV $2D, #$000E16: 0B 2C ASL $2C ; ly: The value in $2C/D gets multiplicated by 40E18: 2B 2D ROL $2D0E1A: 0B 2C ASL $2C0E1C: 2B 2D ROL $2D0E1E: E8 00 MOV A, #$00 ; ly: Add #$2C00 to value in $2C/D0E20: 8D 2C MOV Y, #$2C0E22: 7A 2C ADDW YA, $2C0E24: DA 2C MOVW $2C, YA ; ly: Store the result in $2C/D0E26: CD 00 MOV X, #$000E28: 8D 01 MOV Y, #$010E2A: F7 2C MOV A, [$2C]+Y0E2C: D0 09 BNE $0E370E2E: 8D 03 MOV Y, #$030E30: F7 2C MOV A, [$2C]+Y0E32: F0 17 BEQ $0E4B ; ly: Exit If both addresses are empty0E34: DC DEC Y0E35: 2F 08 BRA $0E3F0E37: 8D 03 MOV Y, #$030E39: F7 2C MOV A, [$2C]+Y0E3B: D0 0F BNE $0E4C ; ly: Execute stuff beneath if both addresses are not empty0E3D: 8D 00 MOV Y, #$00 ; ly: If the first address was not empty but the second...0E3F: F7 2C MOV A, [$2C]+Y0E41: C4 30 MOV $30, A ; ly: ... load $2C/$2C+1 into $30/1...0E43: FC INC Y0E44: F7 2C MOV A, [$2C]+Y0E46: C4 31 MOV $31, A0E48: 5F 11 0F JMP $0F11 ; ly: ... and Execute $F110E4B: 6F RET
QuoteCool stuff! Am I allowed to unite your notes with ours? I guess, we would be pretty much done, then.
QuoteOh boy! You're missing a lot of code (but you know already).
Quote from: Disch on March 24, 2014, 12:35:09 AMI have a few holes in my score outline if you can fill them in. There's like 8 or so commands I didn't quite figure out.
Quote from: Disch on March 24, 2014, 12:31:56 PMNo rush, though. I'm at work and won't be able to check it until tonight anyway. And I probably won't even be able to apply the information for at least another week.
QuoteI hope we won't do the same work twice if we both work on this
Quote from: Disch on March 24, 2014, 04:32:43 PMWhat are you doing with it?I've moved passed the disassembling and am working on applying the info.
Quote from: Disch on March 24, 2014, 04:32:43 PMMy current project is a generic SPC editor (like a SPC tracker) that is game-agnostic. Game specific details are going to be stored externally in Lua scripts, so adding support for other games can be done (and importing/exporting stuff between games would be feasible).Secret of Mana is just the starter game.
QuoteMy current project is a generic SPC editor (like a SPC tracker) that is game-agnostic.
Page created in 0.071 seconds with 20 queries.