Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/seq_decoder.py
7854 views
1
#!/usr/bin/env python3
2
import sys
3
4
commands = {}
5
commands['seq'] = {
6
# non-arg commands
7
0xff: ['end'],
8
0xfe: ['delay1'],
9
0xfd: ['delay', 'var'],
10
0xfc: ['call', 'addr'],
11
0xfb: ['jump', 'addr'],
12
0xfa: ['beqz', 'addr'],
13
0xf9: ['bltz', 'addr'],
14
0xf8: ['loop', 'u8'],
15
0xf7: ['loopend'],
16
0xf5: ['bgez', 'addr'],
17
0xf2: ['reservenotes', 'u8'],
18
0xf1: ['unreservenotes'],
19
0xdf: ['transpose', 's8'],
20
0xde: ['transposerel', 's8'],
21
0xdd: ['settempo', 'u8'],
22
0xdc: ['addtempo', 's8'],
23
0xdb: ['setvol', 'u8'],
24
0xda: ['changevol', 's8'],
25
0xd7: ['initchannels', 'hex16'],
26
0xd6: ['disablechannels', 'hex16'],
27
0xd5: ['setmutescale', 's8'],
28
0xd4: ['mute'],
29
0xd3: ['setmutebhv', 'hex8'],
30
0xd2: ['setshortnotevelocitytable', 'addr'],
31
0xd1: ['setshortnotedurationtable', 'addr'],
32
0xd0: ['setnoteallocationpolicy', 'u8'],
33
0xcc: ['setval', 'u8'],
34
0xc9: ['bitand', 'u8'],
35
0xc8: ['subtract', 'u8'],
36
# arg commands
37
0x00: ['testchdisabled', 'bits:4'],
38
0x50: ['subvariation', 'bits:4:ign'],
39
0x70: ['setvariation', 'bits:4:ign'],
40
0x80: ['getvariation', 'bits:4:ign'],
41
0x90: ['startchannel', 'bits:4', 'addr'],
42
}
43
44
commands['chan'] = {
45
# non-arg commands
46
0xff: ['end'],
47
0xfe: ['delay1'],
48
0xfd: ['delay', 'var'],
49
0xfc: ['call', 'addr'],
50
0xfb: ['jump', 'addr'],
51
0xfa: ['beqz', 'addr'],
52
0xf9: ['bltz', 'addr'],
53
0xf8: ['loop', 'u8'],
54
0xf7: ['loopend'],
55
0xf6: ['break'],
56
0xf5: ['bgez', 'addr'],
57
0xf3: ['hang'],
58
0xf2: ['reservenotes', 'u8'],
59
0xf1: ['unreservenotes'],
60
0xe4: ['dyncall'],
61
0xe3: ['setvibratodelay', 'u8'],
62
0xe2: ['setvibratoextentlinear', 'u8', 'u8', 'u8'],
63
0xe1: ['setvibratoratelinear', 'u8', 'u8', 'u8'],
64
0xe0: ['setvolscale', 'u8'],
65
0xdf: ['setvol', 'u8'],
66
0xde: ['freqscale', 'u16'],
67
0xdd: ['setpan', 'u8'],
68
0xdc: ['setpanmix', 'u8'],
69
0xdb: ['transpose', 's8'],
70
0xda: ['setenvelope', 'addr'],
71
0xd9: ['setdecayrelease', 'u8'],
72
0xd8: ['setvibratoextent', 'u8'],
73
0xd7: ['setvibratorate', 'u8'],
74
0xd6: ['setupdatesperframe_unimplemented', 'u8'],
75
0xd4: ['setreverb', 'u8'],
76
0xd3: ['pitchbend', 's8'],
77
0xd2: ['setsustain', 'u8'],
78
0xd1: ['setnoteallocationpolicy', 'u8'],
79
0xd0: ['stereoheadseteffects', 'u8'],
80
0xcc: ['setval', 'u8'],
81
0xcb: ['readseq', 'addr'],
82
0xca: ['setmutebhv', 'hex8'],
83
0xc9: ['bitand', 'u8'],
84
0xc8: ['subtract', 'u8'],
85
0xc7: ['writeseq', 'u8', 'addr'],
86
0xc6: ['setbank', 'u8'],
87
0xc5: ['dynsetdyntable'],
88
0xc4: ['largenoteson'],
89
0xc3: ['largenotesoff'],
90
0xc2: ['setdyntable', 'addr'],
91
0xc1: ['setinstr', 'u8'],
92
# arg commands
93
0x00: ['testlayerfinished', 'bits:4'],
94
0x10: ['startchannel', 'bits:4', 'addr'],
95
0x20: ['disablechannel', 'bits:4'],
96
0x30: ['iowriteval2', 'bits:4', 'u8'],
97
0x40: ['ioreadval2', 'bits:4', 'u8'],
98
0x50: ['ioreadvalsub', 'bits:4'],
99
0x60: ['setnotepriority', 'bits:4'],
100
0x70: ['iowriteval', 'bits:4'],
101
0x80: ['ioreadval', 'bits:4'],
102
0x90: ['setlayer', 'bits:4', 'addr'],
103
0xa0: ['freelayer', 'bits:4'],
104
0xb0: ['dynsetlayer', 'bits:4'],
105
}
106
107
commands_layer_base = {
108
# non-arg commands
109
0xc0: ['delay', 'var'],
110
0xc1: ['setshortnotevelocity', 'u8'],
111
0xc2: ['transpose', 's8'],
112
0xc3: ['setshortnotedefaultplaypercentage', 'var'],
113
0xc4: ['somethingon'], # ?? (something to do with decay behavior)
114
0xc5: ['somethingoff'], # ??
115
0xc6: ['setinstr', 'u8'],
116
0xc7: ['portamento', 'hex8', 'u8', 'u8'],
117
0xc8: ['disableportamento'],
118
0xc9: ['setshortnoteduration', 'u8'],
119
0xca: ['setpan', 'u8'],
120
0xf7: ['loopend'],
121
0xf8: ['loop', 'u8'],
122
0xfb: ['jump', 'addr'],
123
0xfc: ['call', 'addr'],
124
0xff: ['end'],
125
# arg commands
126
0xd0: ['setshortnotevelocityfromtable', 'bits:4'],
127
0xe0: ['setshortnotedurationfromtable', 'bits:4'],
128
}
129
130
commands['layer_large'] = dict(list(commands_layer_base.items()) + list({
131
0x00: ['note0', 'bits:6', 'var', 'u8', 'u8'],
132
0x40: ['note1', 'bits:6', 'var', 'u8'],
133
0x80: ['note2', 'bits:6', 'u8', 'u8'],
134
}.items()))
135
136
commands['layer_small'] = dict(list(commands_layer_base.items()) + list({
137
0x00: ['smallnote0', 'bits:6', 'var'],
138
0x40: ['smallnote1', 'bits:6'],
139
0x80: ['smallnote2', 'bits:6'],
140
}.items()))
141
142
sh_chan_overrides = [
143
(0x80, ['testlayerfinished', 'bits:3']),
144
(0x88, ['setlayer', 'bits:3', 'addr']),
145
(0x60, ['ioreadval', 'bits:4']),
146
(0x90, ['freelayer', 'bits:4']),
147
]
148
149
def valid_cmd_for_nbits(cmd_list, nbits):
150
for arg in cmd_list:
151
if arg.startswith('bits:'):
152
return int(arg.split(':')[1]) == nbits
153
return nbits == 0
154
155
print_end_padding = False
156
if "--print-end-padding" in sys.argv:
157
print_end_padding = True
158
sys.argv.remove("--print-end-padding")
159
160
if len(sys.argv) != 2:
161
print(f"Usage: {sys.argv[0]} (--emit-asm-macros | input.m64)")
162
sys.exit(0)
163
164
if sys.argv[1] == "--emit-asm-macros":
165
print("// Macros for disassembled sequence files. This file was automatically generated by seq_decoder.py.")
166
print("// To regenerate it, run: ./tools/seq_decoder.py --emit-asm-macros > include/seq_macros.inc")
167
print()
168
def print_hword(x):
169
print(f" .byte {x} >> 8, {x} & 0xff")
170
171
def emit_cmd(key, op, cmd):
172
mn = cmd[0]
173
args = cmd[1:]
174
param_names = []
175
param_list = []
176
bits_param_name = None
177
for i, arg in enumerate(args):
178
param_name = chr(97 + i)
179
param_names.append(param_name)
180
param_list.append(param_name + ("=0" if arg.endswith(":ign") else ""))
181
if arg.startswith("bits:"):
182
bits_param_name = param_name
183
print(f".macro {key}_{mn} {', '.join(param_list)}".rstrip())
184
if bits_param_name is not None:
185
print(f" .byte {hex(op)} + \\{bits_param_name}")
186
else:
187
print(f" .byte {hex(op)}")
188
for arg, param_name in zip(args, param_names):
189
if arg.startswith('bits:'):
190
pass
191
elif arg in ['s8', 'u8', 'hex8']:
192
print(f" .byte \\{param_name}")
193
elif arg in ['u16', 'hex16']:
194
print_hword("\\" + param_name)
195
elif arg == 'addr':
196
print_hword(f"(\\{param_name} - sequence_start)")
197
elif arg == 'var_long':
198
print(f" var_long \\{param_name}")
199
elif arg == 'var':
200
print(f" var \\{param_name}")
201
else:
202
raise Exception("Unknown argument type " + arg)
203
print(".endm")
204
print()
205
206
def emit_env_cmd(op, cmd):
207
mn = cmd[0]
208
param_names = []
209
param_list = []
210
for i, arg in enumerate(cmd[1:]):
211
param_name = chr(97 + i)
212
param_names.append(param_name)
213
param_list.append(param_name + ("=0" if arg.endswith(":ign") else ""))
214
print(f".macro envelope_{mn} {', '.join(param_list)}".rstrip())
215
if op is not None:
216
print(f" .byte {hex(op >> 8)}, {hex(op & 0xff)}")
217
for param in param_names:
218
print_hword("\\" + param)
219
print(".endm\n")
220
221
for key in ['seq', 'chan', 'layer']:
222
print(f"// {key} commands\n")
223
if key == 'layer':
224
cmds = commands['layer_large']
225
for op in sorted(commands['layer_small'].keys()):
226
if op not in cmds:
227
emit_cmd(key, op, commands['layer_small'][op])
228
else:
229
cmds = commands[key]
230
eu_sh = []
231
us_jp = []
232
sh = sh_chan_overrides if key == 'chan' else []
233
non_sh = []
234
for op in sorted(cmds.keys()):
235
mn = cmds[op][0]
236
if mn == 'setnotepriority':
237
eu_sh.append((0xe9, ['setnotepriority', 'u8']))
238
us_jp.append((op, cmds[op]))
239
elif mn in ['reservenotes', 'unreservenotes']:
240
eu_sh.append((op - 1, cmds[op]))
241
us_jp.append((op, cmds[op]))
242
elif mn in ['testlayerfinished', 'setlayer', 'ioreadval', 'freelayer']:
243
non_sh.append((op, cmds[op]))
244
elif mn not in ['portamento', 'writeseq']:
245
emit_cmd(key, op, cmds[op])
246
247
if key == 'chan':
248
print(".macro chan_writeseq val, pos, offset")
249
print(" .byte 0xc7, \\val")
250
print_hword("(\\pos - sequence_start + \\offset)")
251
print(".endm\n")
252
print(".macro chan_writeseq_nextinstr val, offset")
253
print(" .byte 0xc7, \\val")
254
print_hword("(writeseq\\@ - sequence_start + \\offset)")
255
print(" writeseq\\@:")
256
print(".endm\n")
257
print(".macro layer_portamento a, b, c")
258
print(" .byte 0xc7, \\a, \\b")
259
print(" .if ((\\a & 0x80) == 0)")
260
print(" var \\c")
261
print(" .else")
262
print(" .byte \\c")
263
print(" .endif")
264
print(".endm\n")
265
emit_cmd(key, 0xfd, ['delay_long', 'var_long'])
266
if key == 'layer':
267
emit_cmd(key, 0xc0, ['delay_long', 'var_long'])
268
emit_cmd(key, 0x40, ['note1_long', 'bits:4', 'var_long', 'u8'])
269
if eu_sh or us_jp or sh or non_sh:
270
print("#ifdef VERSION_SH\n")
271
for (op, cmd) in eu_sh:
272
emit_cmd(key, op, cmd)
273
for (op, cmd) in sh:
274
emit_cmd(key, op, cmd)
275
print("#else\n")
276
for (op, cmd) in non_sh:
277
emit_cmd(key, op, cmd)
278
print("#ifdef VERSION_EU\n")
279
for (op, cmd) in eu_sh:
280
emit_cmd(key, op, cmd)
281
print("#else\n")
282
for (op, cmd) in us_jp:
283
emit_cmd(key, op, cmd)
284
print("#endif\n")
285
print("#endif\n")
286
287
print("// envelope commands\n")
288
emit_env_cmd(0, ['disable', 'u16'])
289
emit_env_cmd(2**16-1, ['hang', 'u16:ign'])
290
emit_env_cmd(2**16-2, ['goto', 'u16'])
291
emit_env_cmd(2**16-3, ['restart', 'u16:ign'])
292
emit_env_cmd(None, ['line', 'u16', 'u16'])
293
294
print("// other commands\n")
295
print(".macro var_long x")
296
print(" .byte (0x80 | (\\x & 0x7f00) >> 8), (\\x & 0xff)")
297
print(".endm\n")
298
print(".macro var x")
299
print(" .if (\\x >= 0x80)")
300
print(" var_long \\x")
301
print(" .else")
302
print(" .byte \\x")
303
print(" .endif")
304
print(".endm\n")
305
print(".macro sound_ref a")
306
print_hword("(\\a - sequence_start)")
307
print(".endm")
308
sys.exit(0)
309
310
filename = sys.argv[1]
311
try:
312
lang = filename.split('/')[-2]
313
assert lang in ['us', 'jp', 'eu', 'sh']
314
seq_num = int(filename.split('/')[-1].split('_')[0], 16)
315
except Exception:
316
lang = ''
317
seq_num = -1
318
319
try:
320
with open(filename, 'rb') as f:
321
data = f.read()
322
except Exception:
323
print("Error: could not open file {filename} for reading.", file=sys.stderr)
324
sys.exit(1)
325
326
output = [None] * len(data)
327
output_instate = [None] * len(data)
328
label_name = [None] * len(data)
329
script_start = [False] * len(data)
330
hit_eof = False
331
errors = []
332
seq_writes = []
333
334
# Our analysis of large notes mode doesn't persist through multiple channel activations
335
# For simplicity, we force large notes always instead, which is valid for SM64.
336
force_large_notes = True
337
338
if lang in ['eu', 'sh']:
339
# unreservenotes moved to 0xf0 in EU, and reservenotes took its place
340
commands['chan'][0xf0] = commands['chan'][0xf1]
341
commands['chan'][0xf1] = commands['chan'][0xf2]
342
del commands['chan'][0xf2]
343
# total guess: the same is true for the 'seq'-type command
344
commands['seq'][0xf0] = commands['seq'][0xf1]
345
commands['seq'][0xf1] = commands['seq'][0xf2]
346
del commands['seq'][0xf2]
347
# setnotepriority moved to 0xe9, becoming a non-arg command
348
commands['chan'][0xe9] = ['setnotepriority', 'u8']
349
del commands['chan'][0x60]
350
351
if lang == 'sh':
352
del commands['chan'][0x00]
353
del commands['chan'][0xa0]
354
for op, cmd_list in sh_chan_overrides:
355
commands['chan'][op] = cmd_list
356
357
def gen_label(ind, tp):
358
nice_tp = tp.replace('_small', '').replace('_large', '')
359
addr = hex(ind)[2:].upper()
360
ret = f".{nice_tp}_{addr}"
361
if ind >= len(data):
362
errors.append(f"reference to oob label {ret}")
363
return ret
364
365
if label_name[ind] is not None:
366
return label_name[ind]
367
label_name[ind] = ret
368
return ret
369
370
def gen_mnemonic(tp, b):
371
nice_tp = tp.split('_')[0]
372
mn = commands[tp][b][0]
373
if not mn:
374
mn = f"{b:02X}"
375
return f"{nice_tp}_{mn}"
376
377
decode_list = []
378
379
def decode_one(state):
380
pos, tp, nesting, large = state
381
orig_pos = pos
382
383
if pos >= len(data):
384
global hit_eof
385
hit_eof = True
386
return
387
388
if output[pos] is not None:
389
if output_instate[pos] != state:
390
errors.append(f"got to {gen_label(orig_pos, tp)} with both state {state} and {output_instate[pos]}")
391
return
392
393
def u8():
394
nonlocal pos
395
global hit_eof
396
if pos == len(data):
397
hit_eof = True
398
return 0
399
ret = data[pos]
400
pos += 1
401
return ret
402
403
def u16():
404
hi = u8()
405
lo = u8()
406
return (hi << 8) | lo
407
408
def var():
409
ret = u8()
410
if ret & 0x80:
411
ret = (ret << 8) & 0x7f00;
412
ret |= u8()
413
return (ret, ret < 0x80)
414
return (ret, False)
415
416
if tp == 'soundref':
417
sound = u16()
418
decode_list.append((sound, 'chan', 0, True))
419
if sound < len(data):
420
script_start[sound] = True
421
for p in range(orig_pos, pos):
422
output[p] = ''
423
output_instate[p] = state
424
output[orig_pos] = 'sound_ref ' + gen_label(sound, 'chan')
425
return
426
427
if tp == 'envelope':
428
a = u16()
429
b = u16()
430
for p in range(orig_pos, pos):
431
output[p] = ''
432
output_instate[p] = state
433
if a >= 2**16 - 3:
434
a -= 2**16
435
if a <= 0:
436
mn = ['disable', 'hang', 'goto', 'restart'][-a]
437
output[orig_pos] = f'envelope_{mn} {b}'
438
# assume any goto is backwards and stop decoding
439
else:
440
output[orig_pos] = f'envelope_line {a} {b}'
441
decode_list.append((pos, tp, nesting, large))
442
return
443
444
ins_byte = u8()
445
446
cmds = commands[tp]
447
nbits = 0
448
used_b = ins_byte
449
while True:
450
if used_b in cmds and valid_cmd_for_nbits(cmds[used_b], nbits):
451
break
452
used_b &= ~(1 << nbits)
453
nbits += 1
454
if nbits == 8:
455
errors.append(f"unrecognized instruction {hex(ins_byte)} for type {tp} at label {gen_label(orig_pos, tp)}")
456
return
457
458
out_mn = gen_mnemonic(tp, used_b)
459
out_args = []
460
cmd_mn = cmds[used_b][0]
461
cmd_args = cmds[used_b][1:]
462
long_var = False
463
464
for a in cmd_args:
465
if cmd_mn == 'portamento' and len(out_args) == 2 and (int(out_args[0], 0) & 0x80) == 0:
466
a = 'var'
467
if a.startswith('bits:'):
468
low_bits = ins_byte & ((1 << nbits) - 1)
469
if not (a.endswith(':ign') and low_bits == 0):
470
out_args.append(str(low_bits))
471
elif a == 'u8':
472
out_args.append(str(u8()))
473
elif a == 'hex8':
474
out_args.append(hex(u8()))
475
elif a == 's8':
476
v = u8()
477
out_args.append(str(v if v < 128 else v - 256))
478
elif a == 'u16':
479
out_args.append(str(u16()))
480
elif a == 'hex16':
481
out_args.append(hex(u16()))
482
elif a == 'var':
483
val, bad = var()
484
out_args.append(hex(val))
485
if bad:
486
long_var = True
487
elif a == 'addr':
488
v = u16()
489
490
kind = 'addr'
491
if cmd_mn == 'call':
492
kind = tp + '_fn'
493
elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
494
kind = tp
495
elif cmd_mn == 'startchannel':
496
kind = 'chan'
497
elif cmd_mn == 'setlayer':
498
kind = 'layer'
499
elif cmd_mn == 'setdyntable':
500
kind = 'table'
501
elif cmd_mn == 'setenvelope':
502
kind = 'envelope'
503
504
if v >= len(data):
505
label = gen_label(v, kind)
506
out_args.append(label)
507
errors.append(f"reference to oob label {label}")
508
elif cmd_mn == 'writeseq':
509
out_args.append('<fixup>')
510
seq_writes.append((orig_pos, v))
511
else:
512
out_args.append(gen_label(v, kind))
513
if cmd_mn == 'call':
514
decode_list.append((v, tp, 0, large))
515
script_start[v] = True
516
elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
517
decode_list.append((v, tp, nesting, large))
518
elif cmd_mn == 'startchannel':
519
decode_list.append((v, 'chan', 0, force_large_notes))
520
script_start[v] = True
521
elif cmd_mn == 'setlayer':
522
if large:
523
decode_list.append((v, 'layer_large', 0, True))
524
else:
525
decode_list.append((v, 'layer_small', 0, True))
526
script_start[v] = True
527
elif cmd_mn == 'setenvelope':
528
decode_list.append((v, 'envelope', 0, True))
529
script_start[v] = True
530
else:
531
script_start[v] = True
532
533
out_all = out_mn
534
if long_var:
535
out_all += "_long"
536
if out_args:
537
out_all += ' '
538
out_all += ', '.join(out_args)
539
for p in range(orig_pos, pos):
540
output[p] = ''
541
output_instate[p] = state
542
output[orig_pos] = out_all
543
544
if cmd_mn in ['hang', 'jump']:
545
return
546
if cmd_mn in ['loop']:
547
nesting += 1
548
if cmd_mn == 'end':
549
nesting -= 1
550
if cmd_mn in ['break', 'loopend']:
551
nesting -= 1
552
if nesting < 0:
553
# This is iffy, and actually happens in sequence 0. It will make us
554
# return to the caller's caller at function end.
555
nesting = 0
556
if cmd_mn == 'largenoteson':
557
large = True
558
if cmd_mn == 'largenotesoff':
559
large = False
560
if nesting >= 0:
561
decode_list.append((pos, tp, nesting, large))
562
563
def decode_rec(state, initial):
564
if not initial:
565
v = state[0]
566
gen_label(v, state[1])
567
script_start[v] = True
568
decode_list.append(state)
569
while decode_list:
570
decode_one(decode_list.pop())
571
572
def main():
573
decode_rec((0, 'seq', 0, False), initial=True)
574
575
if seq_num == 0:
576
if lang == 'jp':
577
sound_banks = [
578
(0x14C, 0x70),
579
(0x8A8, 0x38), # stated as 0x30
580
(0xB66, 0x38), # stated as 0x30
581
(0xE09, 0x80),
582
(0x194B, 0x28), # stated as 0x20
583
(0x1CA6, 0x80),
584
(0x27C9, 0x20),
585
(0x2975, 0x30),
586
# same script as bank 3
587
# same script as bank 5
588
]
589
unused = [
590
(0x1FC4, 'layer_large'),
591
(0x2149, 'layer_large'),
592
(0x2223, 'layer_large'),
593
(0x28C5, 'chan'),
594
(0x3110, 'envelope'),
595
(0x31EC, 'envelope'),
596
]
597
elif lang == 'us':
598
sound_banks = [
599
(0x14C, 0x70),
600
(0x8F6, 0x38), # stated as 0x30
601
(0xBB4, 0x40),
602
(0xF8E, 0x80),
603
(0x1AF3, 0x28), # stated as 0x20
604
(0x1E4E, 0x80),
605
(0x2971, 0x20),
606
(0x2B1D, 0x40),
607
# same script as bank 3
608
# same script as bank 5
609
]
610
unused = [
611
(0x216C, 'layer_large'),
612
(0x22F1, 'layer_large'),
613
(0x23CB, 'layer_large'),
614
(0x2A6D, 'chan'),
615
(0x339C, 'envelope'),
616
(0x3478, 'envelope'),
617
]
618
elif lang == 'eu':
619
sound_banks = [
620
(0x154, 0x70),
621
(0x8FE, 0x38), # stated as 0x30
622
(0xBBC, 0x40),
623
(0xFA5, 0x80),
624
(0x1B0C, 0x28), # stated as 0x20
625
(0x1E67, 0x80),
626
(0x298A, 0x20),
627
(0x2B36, 0x40),
628
# same script as bank 3
629
# same script as bank 5
630
]
631
unused = [
632
(0xF9A, 'chan'),
633
(0x2185, 'layer_large'),
634
(0x230A, 'layer_large'),
635
(0x23E4, 'layer_large'),
636
(0x2A86, 'chan'),
637
(0x33CC, 'envelope'),
638
(0x34A8, 'envelope'),
639
]
640
elif lang == 'sh':
641
sound_banks = [
642
(0x154, 0x70),
643
(0x8FE, 0x38), # stated as 0x30
644
(0xBBC, 0x40),
645
(0xFA5, 0x80),
646
(0x1B11, 0x28), # stated as 0x20
647
(0x1E6C, 0x80),
648
(0x298F, 0x20),
649
(0x2B3B, 0x40),
650
# same script as bank 3
651
# same script as bank 5
652
]
653
654
unused = [
655
(0xF9A, 'chan'),
656
(0x218A, 'layer_large'),
657
(0x230F, 'layer_large'),
658
(0x23E9, 'layer_large'),
659
(0x2A8B, 'chan'),
660
(0x33D0, 'envelope'),
661
(0x34AC, 'envelope'),
662
]
663
664
665
for (addr, count) in sound_banks:
666
for i in range(count):
667
decode_rec((addr + 2*i, 'soundref', 0, False), initial=True)
668
669
for (addr, tp) in unused:
670
gen_label(addr, tp + '_unused')
671
decode_rec((addr, tp, 0, force_large_notes), initial=False)
672
673
for (pos, write_to) in seq_writes:
674
assert '<fixup>' in output[pos]
675
delta = 0
676
while output[write_to] == '':
677
write_to -= 1
678
delta += 1
679
if write_to > pos and all(output[i] == '' for i in range(pos+1, write_to)):
680
nice_target = str(delta)
681
output[pos] = output[pos].replace('writeseq', 'writeseq_nextinstr')
682
else:
683
tp = output_instate[write_to][1] if output_instate[write_to] is not None else 'addr'
684
nice_target = gen_label(write_to, tp) + ", " + str(delta)
685
output[pos] = output[pos].replace('<fixup>', nice_target)
686
687
# Add unreachable 'end' markers
688
for i in range(1, len(data)):
689
if (data[i] == 0xff and output[i] is None and output[i - 1] is not None
690
and label_name[i] is None):
691
tp = output_instate[i - 1][1]
692
if tp in ["seq", "chan", "layer_small", "layer_large"]:
693
output[i] = gen_mnemonic(tp, 0xff)
694
695
# Add envelope padding
696
for i in range(1, len(data) - 1):
697
if (data[i] == 0 and output[i] is None and output[i - 1] is not None and
698
output[i + 1] is not None and label_name[i] is None and
699
output[i + 1].startswith('envelope')):
700
script_start[i] = True
701
output[i] = "# padding\n.byte 0"
702
703
# Add 'unused' marker labels
704
for i in range(1, len(data)):
705
if (output[i] is None and output[i - 1] is not None and label_name[i] is None):
706
script_start[i] = True
707
gen_label(i, 'unused')
708
709
# Remove up to 15 bytes of padding at the end
710
end_padding = 0
711
for i in range(len(data)-1, -1, -1):
712
if output[i] is not None:
713
break
714
end_padding += 1
715
if end_padding > 15:
716
end_padding = 0
717
718
if print_end_padding:
719
print(end_padding)
720
sys.exit(0)
721
722
print(".include \"seq_macros.inc\"")
723
print(".section .rodata")
724
print(".align 0")
725
print("sequence_start:")
726
print()
727
for i in range(len(data) - end_padding):
728
if script_start[i] and i > 0:
729
print()
730
if label_name[i] is not None:
731
print(f"{label_name[i]}:")
732
o = output[i]
733
if o is None:
734
print(f".byte {hex(data[i])}")
735
elif o:
736
print(o)
737
elif label_name[i] is not None:
738
print("<mid-instruction>")
739
errors.append(f"mid-instruction label {label_name[i]}")
740
741
if hit_eof:
742
errors.append("hit eof!?")
743
744
if errors:
745
print(f"[{filename}] errors:", file=sys.stderr)
746
for w in errors:
747
print(w, file=sys.stderr)
748
749
main()
750
751