-
Notifications
You must be signed in to change notification settings - Fork 2
/
secret_of_mana_msu1.asm
447 lines (380 loc) · 6.47 KB
/
secret_of_mana_msu1.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
arch snes.cpu
// MSU memory map I/O
constant MSU_STATUS($002000)
constant MSU_ID($002002)
constant MSU_AUDIO_TRACK_LO($002004)
constant MSU_AUDIO_TRACK_HI($002005)
constant MSU_AUDIO_VOLUME($002006)
constant MSU_AUDIO_CONTROL($002007)
// SPC communication ports
constant SPC_COMM_0($2140)
constant SPC_COMM_1($2141)
constant SPC_COMM_2($2142)
constant SPC_COMM_3($2143)
// MSU_STATUS possible values
constant MSU_STATUS_TRACK_MISSING($8)
constant MSU_STATUS_AUDIO_PLAYING(%00010000)
constant MSU_STATUS_AUDIO_REPEAT(%00100000)
constant MSU_STATUS_AUDIO_BUSY($40)
constant MSU_STATUS_DATA_BUSY(%10000000)
// SNES Multiply register
constant SNES_MUL_OPERAND_A($004202)
constant SNES_MUL_OPERAND_B($004203)
constant SNES_DIV_DIVIDEND_L($004204)
constant SNES_DIV_DIVIDEND_H($004205)
constant SNES_DIV_DIVISOR($004206)
constant SNES_DIV_QUOTIENT_L($004214)
constant SNES_DIV_QUOTIENT_H($004215)
constant SNES_MUL_DIV_RESULT_L($004216)
constant SNES_MUL_DIV_RESULT_H($004217)
// Constants
if {defined EMULATOR_VOLUME} {
constant FULL_VOLUME($60)
} else {
constant FULL_VOLUME($FF)
}
// Game variables
variable MusicCommand($7E1E00)
variable RequestedSong($7E1E01)
variable PreviousSong($7E1E05)
// My variables
variable FadeState($7E1EDD)
variable FadeVolume($7E1EDE)
variable FadeStep($7E1EE0)
variable FadeTemp($7E1EE2)
variable FadeCount($7E1EE4)
variable FrameCount($7E1EE5)
// **********
// * Macros *
// **********
// seek converts SNES HiROM address to physical address
macro seek(variable offset) {
origin (offset & $3FFFFF)
base offset
}
macro CheckMSUPresence(labelToJump) {
lda MSU_ID
cmp.b #'S'
bne {labelToJump}
}
macro WaitMulResult() {
nop
nop
nop
nop
}
macro WaitDivResult() {
nop
nop
nop
nop
nop
nop
nop
nop
}
// ********
// * Code *
// ********
seek($008008)
jml Reset_Hijack
// Override NMI vector to call a custom RAM address
// with my hijack
seek($C0FFEA)
db $22,$01
seek($C30004)
jmp MSU_Hijack
seek($C324C0)
MSU_Hijack:
jsl MSU_Main
bcc ReturnToCode
jmp $0160
ReturnToCode:
rtl
// MSU code
seek($CB3400)
scope MSU_Main: {
php
rep #$30
pha
phx
phy
sep #$20
CheckMSUPresence(OriginalCode)
lda MusicCommand
beq DoNothing
cmp #$01
bne +;
stz MusicCommand
jsr MSU_PlayMusic
bcs OriginalCode
bcc DoNothing
+;
cmp #$80
bne +;
stz MusicCommand
jsr MSU_Fade
bra OriginalCode
+;
cmp #$F1
bne +;
stz MusicCommand
jsr MSU_Stop
+;
OriginalCode:
rep #$30
ply
plx
pla
plp
sec
rtl
DoNothing:
rep #$30
ply
plx
pla
plp
clc
rtl
}
scope MSU_PlayMusic: {
// Check if the song is already playing
xba
lda $7E1E02
and #$0F
pha
lda RequestedSong
pha
plx
cpx PreviousSong
beq Exit
// Current current SPC song playing, if any
jsr Stop_SPC
// Set MSU-1 track
lda RequestedSong
sta MSU_AUDIO_TRACK_LO
lda.b #$00
sta MSU_AUDIO_TRACK_HI
IsMSUReady:
lda MSU_STATUS
and.b #MSU_STATUS_AUDIO_BUSY
bne IsMSUReady
// Check if the track is missing
lda MSU_STATUS
and.b #MSU_STATUS_TRACK_MISSING
bne TrackMissing
// Play the song and add repeat if needed
lda RequestedSong
jsr TrackNeedLooping
sta MSU_AUDIO_CONTROL
// Set volume
lda.b #FULL_VOLUME
sta MSU_AUDIO_VOLUME
sta FadeVolume+1
// Set previous song for game
lda RequestedSong
sta PreviousSong
lda $7E1E02
and #$0F
sta $7E1E06
lda $7E1E03
sta $7E1E07
Exit:
clc
rts
TrackMissing:
lda.b #$01
sta MusicCommand
lda.b #$00
sta MSU_AUDIO_VOLUME
sec
rts
}
scope TrackNeedLooping: {
// $17 / 23 = Victory ! (Jingle)
cmp.b #$17
beq noLooping
// $19 Cannon Travel Lunch (SFX)
cmp.b #$19
beq noLooping
// $1A Cannon Travel (SFX)
cmp.b #$1A
beq noLooping
// $1E New Contient Rises (SFX)
cmp.b #$1E
beq noLooping
// $21 Unused Jingle 1
cmp.b #$21
beq noLooping
// $22 Midge Mallet (SFX)
cmp.b #$22
beq noLooping
// $23 Unknown Jingle 2
cmp.b #$23
beq noLooping
// $28 Flammie Coming (SFX)
cmp.b #$28
beq noLooping
// $2D Mysterious Moaning
cmp.b #$2D
beq noLooping
// $2E Mara's Key (Jingle)
cmp.b #$2E
beq noLooping
// $2F Got an Item (Jingle)
cmp.b #$2F
beq noLooping
// $30 Elemental Acquired (Jingle)
cmp.b #$30
beq noLooping
// $35 Ally Joins (Jingle)
cmp.b #$35
beq noLooping
// Track loops
lda.b #$03
rts
noLooping:
lda #$01
rts
}
scope MSU_Fade: {
lda RequestedSong
sta FadeCount
and #$0F
sta SNES_MUL_OPERAND_A
lda FadeCount
and #$F0
sta FadeCount
lda #$11
sta SNES_MUL_OPERAND_B
WaitMulResult()
lda FadeCount
bne +;
sta FadeStep
jmp Exit
+;
lda SNES_MUL_DIV_RESULT_L
sec
sbc FadeVolume+1
bcs +;
eor #$FF
inc
+;
sta SNES_DIV_DIVIDEND_L
lda #$00
sta SNES_DIV_DIVIDEND_H
lda FadeCount
sta SNES_DIV_DIVISOR
WaitDivResult()
lda SNES_DIV_QUOTIENT_L
sta FadeTemp+1
lda #$00
sta SNES_DIV_DIVIDEND_L
lda SNES_MUL_DIV_RESULT_L
sta SNES_DIV_DIVIDEND_H
lda FadeCount
sta SNES_DIV_DIVISOR
WaitDivResult()
lda SNES_DIV_QUOTIENT_L
sta FadeTemp
bcs +;
rep #$20
lda FadeTemp
eor #$FFFF
inc
sta FadeTemp
sep #$20
+;
rep #$20
lda FadeTemp
sta FadeStep
sep #$20
lda #$00
sta FadeVolume
lda #$01
sta FadeState
Exit:
rts
}
scope MSU_Stop: {
lda #$00
sta MSU_AUDIO_CONTROL
rts
}
scope Stop_SPC: {
// Stop SPC music
lda #$F1
sta SPC_COMM_0
Handshake:
cmp SPC_COMM_0
bne Handshake
lda #$00
sta SPC_COMM_0
// Stop looping SFX
lda #$F2
sta SPC_COMM_0
Handshake2:
cmp SPC_COMM_0
bne Handshake2
lda #$00
sta SPC_COMM_0
rts
}
// NMI & Reset hijack code
scope Reset_Hijack: {
lda #$FF
sta FadeVolume
sta FadeVolume+1
lda #$00
sta FadeState
ldx #$00
hijackLoop:
lda codeData,x
sta $0122,x
inx
cpx #$04
bne hijackLoop
// Original code hijacked
jml $C10010
codeData:
jml NMI_Update
}
scope NMI_Update: {
php
rep #$20
pha
sep #$20
lda FrameCount
inc
sta FrameCount
lda FadeState
beq Exit
lda FrameCount
lsr
bcs Exit
rep #$20
clc
lda FadeVolume
adc FadeStep
sta FadeVolume
sep #$20
lda FadeVolume+1
sta MSU_AUDIO_VOLUME
lda FadeCount
dec
bne +;
lda #$00
sta FadeState
+;
sta FadeCount
Exit:
rep #$20
pla
plp
// Call actual NMI code
jml $000100
}
if (pc() > $CB3FFF) {
error "Overflow detected"
}