Update README.md
[sm64pc.git] / tools / seq_decoder.py
blob07e59c3ceab39e8f5016521d9121dd2781e5bde1
1 #!/usr/bin/env python3
2 import sys
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', 'arg'],
38 0x50: ['subvariation', 'ign-arg'],
39 0x70: ['setvariation', 'ign-arg'],
40 0x80: ['getvariation', 'ign-arg'],
41 0x90: ['startchannel', 'arg', 'addr'],
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', 'arg'],
94 0x10: ['startchannel', 'arg', 'addr'],
95 0x20: ['disablechannel', 'arg'],
96 0x30: ['iowriteval2', 'arg', 'u8'],
97 0x40: ['ioreadval2', 'arg', 'u8'],
98 0x50: ['ioreadvalsub', 'arg'],
99 0x60: ['setnotepriority', 'arg'],
100 0x70: ['iowriteval', 'arg'],
101 0x80: ['ioreadval', 'arg'],
102 0x90: ['setlayer', 'arg', 'addr'],
103 0xa0: ['freelayer', 'arg'],
104 0xb0: ['dynsetlayer', 'arg'],
107 commands_layer_base = {
108 # non-arg commands
109 0xc0: ['delay', 'var'],
110 0xc1: ['setshortnotevelocity', 'u8'],
111 0xc2: ['transpose', 'u8'],
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', 'arg'],
127 0xe0: ['setshortnotedurationfromtable', 'arg'],
130 commands['layer_large'] = dict(list(commands_layer_base.items()) + list({
131 0x00: ['note0', 'arg', 'var', 'u8', 'u8'],
132 0x40: ['note1', 'arg', 'var', 'u8'],
133 0x80: ['note2', 'arg', 'u8', 'u8'],
134 }.items()))
136 commands['layer_small'] = dict(list(commands_layer_base.items()) + list({
137 0x00: ['smallnote0', 'arg', 'var'],
138 0x40: ['smallnote1', 'arg'],
139 0x80: ['smallnote2', 'arg'],
140 }.items()))
142 print_end_padding = False
143 if "--print-end-padding" in sys.argv:
144 print_end_padding = True
145 sys.argv.remove("--print-end-padding")
147 if len(sys.argv) != 2:
148 print(f"Usage: {sys.argv[0]} (--emit-asm-macros | input.m64)")
149 sys.exit(0)
151 if sys.argv[1] == "--emit-asm-macros":
152 print("# Macros for disassembled sequence files. This file was automatically generated by seq_decoder.py.")
153 print("# To regenerate it, run: ./tools/seq_decoder.py --emit-asm-macros >seq_macros.inc")
154 print()
155 def print_hword(x):
156 print(f" .byte {x} >> 8, {x} & 0xff")
158 def emit_cmd(key, op, cmd):
159 mn = cmd[0]
160 args = cmd[1:]
161 param_names = []
162 param_list = []
163 nibble_param_name = None
164 for i, arg in enumerate(args):
165 param_name = chr(97 + i)
166 param_names.append(param_name)
167 param_list.append(param_name + ("=0" if arg == "ign-arg" else ""))
168 if arg == "ign-arg" or arg == "arg":
169 nibble_param_name = param_name
170 print(f".macro {key}_{mn} {', '.join(param_list)}".rstrip())
171 if nibble_param_name is not None:
172 print(f" .byte {hex(op)} + \\{nibble_param_name}")
173 else:
174 print(f" .byte {hex(op)}")
175 for arg, param_name in zip(args, param_names):
176 if arg in ['arg', 'ign-arg']:
177 pass
178 elif arg in ['s8', 'u8', 'hex8']:
179 print(f" .byte \\{param_name}")
180 elif arg in ['u16', 'hex16']:
181 print_hword("\\" + param_name)
182 elif arg == 'addr':
183 print_hword(f"(\\{param_name} - sequence_start)")
184 elif arg == 'var_long':
185 print(f" var_long \\{param_name}")
186 elif arg == 'var':
187 print(f" var \\{param_name}")
188 else:
189 raise Exception("Unknown argument type " + arg)
190 print(".endm")
191 print()
193 def emit_env_cmd(op, cmd):
194 mn = cmd[0]
195 param_list = []
196 for i, arg in enumerate(cmd[1:]):
197 param_list.append(chr(97 + i))
198 print(f".macro envelope_{mn} {', '.join(param_list)}".rstrip())
199 if op is not None:
200 print(f" .byte {hex(op >> 8)}, {hex(op & 0xff)}")
201 for param in param_list:
202 print_hword("\\" + param)
203 print(".endm\n")
205 for key in ['seq', 'chan', 'layer']:
206 print(f"# {key} commands\n")
207 if key == 'layer':
208 cmds = commands['layer_large']
209 for op in sorted(commands['layer_small'].keys()):
210 if op not in cmds:
211 emit_cmd(key, op, commands['layer_small'][op])
212 else:
213 cmds = commands[key]
214 eu = []
215 non_eu = []
216 for op in sorted(cmds.keys()):
217 mn = cmds[op][0]
218 if mn == 'setnotepriority':
219 eu.append((0xe9, ['setnotepriority', 'u8']))
220 non_eu.append((op, cmds[op]))
221 elif mn in ['reservenotes', 'unreservenotes']:
222 eu.append((op - 1, cmds[op]))
223 non_eu.append((op, cmds[op]))
224 elif mn not in ['portamento', 'writeseq']:
225 emit_cmd(key, op, cmds[op])
227 if key == 'chan':
228 print(".macro chan_writeseq val, pos, offset")
229 print(" .byte 0xc7, \\val")
230 print_hword("(\\pos - sequence_start + \\offset)")
231 print(".endm\n")
232 print(".macro chan_writeseq_nextinstr val, offset")
233 print(" .byte 0xc7, \\val")
234 print_hword("(writeseq\\@ - sequence_start + \\offset)")
235 print(" writeseq\\@:")
236 print(".endm\n")
237 print(".macro layer_portamento a, b, c")
238 print(" .byte 0xc7, \\a, \\b")
239 print(" .if ((\\a & 0x80) == 0)")
240 print(" var \\c")
241 print(" .else")
242 print(" .byte \\c")
243 print(" .endif")
244 print(".endm\n")
245 emit_cmd(key, 0xfd, ['delay_long', 'var_long'])
246 if key == 'layer':
247 emit_cmd(key, 0xc0, ['delay_long', 'var_long'])
248 emit_cmd(key, 0x40, ['note1_long', 'arg', 'var_long', 'u8'])
249 if eu:
250 print(".ifdef VERSION_EU\n")
251 for (op, cmd) in eu:
252 emit_cmd(key, op, cmd)
253 print(".else\n")
254 for (op, cmd) in non_eu:
255 emit_cmd(key, op, cmd)
256 print(".endif\n")
258 print("# envelope commands\n")
259 emit_env_cmd(0, ['disable', 'u16'])
260 emit_env_cmd(2**16-1, ['hang', 'u16'])
261 emit_env_cmd(2**16-2, ['goto', 'u16'])
262 emit_env_cmd(2**16-3, ['restart', 'u16'])
263 emit_env_cmd(None, ['line', 'u16', 'u16'])
265 print("# other commands\n")
266 print(".macro var_long x")
267 print(" .byte (0x80 | (\\x & 0x7f00) >> 8), (\\x & 0xff)")
268 print(".endm\n")
269 print(".macro var x")
270 print(" .if (\\x >= 0x80)")
271 print(" var_long \\x")
272 print(" .else")
273 print(" .byte \\x")
274 print(" .endif")
275 print(".endm\n")
276 print(".macro sound_ref a")
277 print_hword("(\\a - sequence_start)")
278 print(".endm")
279 sys.exit(0)
281 filename = sys.argv[1]
282 try:
283 lang = filename.split('/')[-2]
284 assert lang in ['us', 'jp', 'eu']
285 seq_num = int(filename.split('/')[-1].split('_')[0], 16)
286 except Exception:
287 lang = ''
288 seq_num = -1
290 try:
291 with open(filename, 'rb') as f:
292 data = f.read()
293 except Exception:
294 print("Error: could not open file {filename} for reading.", file=sys.stderr)
295 sys.exit(1)
297 output = [None] * len(data)
298 output_instate = [None] * len(data)
299 label_name = [None] * len(data)
300 script_start = [False] * len(data)
301 hit_eof = False
302 errors = []
303 seq_writes = []
305 # Our analysis of large notes mode doesn't persist through multiple channel activations
306 # For simplicity, we force large notes always instead, which is valid for SM64.
307 force_large_notes = True
309 if lang == 'eu':
310 # unreservenotes moved to 0xf0 in EU, and reservenotes took its place
311 commands['chan'][0xf0] = commands['chan'][0xf1]
312 commands['chan'][0xf1] = commands['chan'][0xf2]
313 del commands['chan'][0xf2]
314 # total guess: the same is true for the 'seq'-type command
315 commands['seq'][0xf0] = commands['seq'][0xf1]
316 commands['seq'][0xf1] = commands['seq'][0xf2]
317 del commands['seq'][0xf2]
318 # setnotepriority moved to 0xe9, becoming a non-arg command
319 commands['chan'][0xe9] = ['setnotepriority', 'u8']
320 del commands['chan'][0x60]
322 def is_arg_command(cmd_args):
323 return 'arg' in cmd_args or 'ign-arg' in cmd_args
325 def gen_label(ind, tp):
326 nice_tp = tp.replace('_small', '').replace('_large', '')
327 addr = hex(ind)[2:].upper()
328 ret = f".{nice_tp}_{addr}"
329 if ind >= len(data):
330 errors.append(f"reference to oob label {ret}")
331 return ret
333 if label_name[ind] is not None:
334 return label_name[ind]
335 label_name[ind] = ret
336 return ret
338 def gen_mnemonic(tp, b):
339 nice_tp = tp.split('_')[0]
340 mn = commands[tp][b][0]
341 if not mn:
342 mn = f"{b:02X}"
343 return f"{nice_tp}_{mn}"
345 decode_list = []
347 def decode_one(state):
348 pos, tp, nesting, large = state
349 orig_pos = pos
351 if pos >= len(data):
352 global hit_eof
353 hit_eof = True
354 return
356 if output[pos] is not None:
357 if output_instate[pos] != state:
358 errors.append(f"got to {gen_label(orig_pos, tp)} with both state {state} and {output_instate[pos]}")
359 return
361 def u8():
362 nonlocal pos
363 global hit_eof
364 if pos == len(data):
365 hit_eof = True
366 return 0
367 ret = data[pos]
368 pos += 1
369 return ret
371 def u16():
372 hi = u8()
373 lo = u8()
374 return (hi << 8) | lo
376 def var():
377 ret = u8()
378 if ret & 0x80:
379 ret = (ret << 8) & 0x7f00;
380 ret |= u8()
381 return (ret, ret < 0x80)
382 return (ret, False)
384 if tp == 'soundref':
385 sound = u16()
386 decode_list.append((sound, 'chan', 0, True))
387 if sound < len(data):
388 script_start[sound] = True
389 for p in range(orig_pos, pos):
390 output[p] = ''
391 output_instate[p] = state
392 output[orig_pos] = 'sound_ref ' + gen_label(sound, 'chan')
393 return
395 if tp == 'envelope':
396 a = u16()
397 b = u16()
398 for p in range(orig_pos, pos):
399 output[p] = ''
400 output_instate[p] = state
401 if a >= 2**16 - 3:
402 a -= 2**16
403 if a <= 0:
404 mn = ['disable', 'hang', 'goto', 'restart'][-a]
405 output[orig_pos] = f'envelope_{mn} {b}'
406 # assume any goto is backwards and stop decoding
407 else:
408 output[orig_pos] = f'envelope_line {a} {b}'
409 decode_list.append((pos, tp, nesting, large))
410 return
412 ins_byte = u8()
414 cmds = commands[tp]
415 if ins_byte in cmds and not is_arg_command(cmds[ins_byte]):
416 used_b = ins_byte
417 arg = None
418 elif ins_byte & 0xf0 in cmds and is_arg_command(cmds[ins_byte & 0xf0]):
419 used_b = ins_byte & 0xf0
420 arg = ins_byte & 0xf
421 elif ins_byte & 0xc0 in cmds and is_arg_command(cmds[ins_byte & 0xc0]) and tp.startswith('layer'):
422 used_b = ins_byte & 0xc0
423 arg = ins_byte & 0x3f
424 else:
425 errors.append(f"unrecognized instruction {hex(ins_byte)} for type {tp} at label {gen_label(orig_pos, tp)}")
426 return
428 out_mn = gen_mnemonic(tp, used_b)
429 out_args = []
430 cmd_mn = cmds[used_b][0]
431 cmd_args = cmds[used_b][1:]
432 long_var = False
434 for a in cmd_args:
435 if cmd_mn == 'portamento' and len(out_args) == 2 and (int(out_args[0], 0) & 0x80) == 0:
436 a = 'var'
437 if a == 'arg':
438 out_args.append(str(arg))
439 elif a == 'ign-arg' and arg != 0:
440 out_args.append(str(arg))
441 elif a == 'u8':
442 out_args.append(str(u8()))
443 elif a == 'hex8':
444 out_args.append(hex(u8()))
445 elif a == 's8':
446 v = u8()
447 out_args.append(str(v if v < 128 else v - 256))
448 elif a == 'u16':
449 out_args.append(str(u16()))
450 elif a == 'hex16':
451 out_args.append(hex(u16()))
452 elif a == 'var':
453 val, bad = var()
454 out_args.append(hex(val))
455 if bad:
456 long_var = True
457 elif a == 'addr':
458 v = u16()
460 kind = 'addr'
461 if cmd_mn == 'call':
462 kind = tp + '_fn'
463 elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
464 kind = tp
465 elif cmd_mn == 'startchannel':
466 kind = 'chan'
467 elif cmd_mn == 'setlayer':
468 kind = 'layer'
469 elif cmd_mn == 'setdyntable':
470 kind = 'table'
471 elif cmd_mn == 'setenvelope':
472 kind = 'envelope'
474 if v >= len(data):
475 label = gen_label(v, kind)
476 out_args.append(label)
477 errors.append(f"reference to oob label {label}")
478 elif cmd_mn == 'writeseq':
479 out_args.append('<fixup>')
480 seq_writes.append((orig_pos, v))
481 else:
482 out_args.append(gen_label(v, kind))
483 if cmd_mn == 'call':
484 decode_list.append((v, tp, 0, large))
485 script_start[v] = True
486 elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
487 decode_list.append((v, tp, nesting, large))
488 elif cmd_mn == 'startchannel':
489 decode_list.append((v, 'chan', 0, force_large_notes))
490 script_start[v] = True
491 elif cmd_mn == 'setlayer':
492 if large:
493 decode_list.append((v, 'layer_large', 0, True))
494 else:
495 decode_list.append((v, 'layer_small', 0, True))
496 script_start[v] = True
497 elif cmd_mn == 'setenvelope':
498 decode_list.append((v, 'envelope', 0, True))
499 script_start[v] = True
500 else:
501 script_start[v] = True
503 out_all = out_mn
504 if long_var:
505 out_all += "_long"
506 if out_args:
507 out_all += ' '
508 out_all += ', '.join(out_args)
509 for p in range(orig_pos, pos):
510 output[p] = ''
511 output_instate[p] = state
512 output[orig_pos] = out_all
514 if cmd_mn in ['hang', 'jump']:
515 return
516 if cmd_mn in ['loop']:
517 nesting += 1
518 if cmd_mn == 'end':
519 nesting -= 1
520 if cmd_mn in ['break', 'loopend']:
521 nesting -= 1
522 if nesting < 0:
523 # This is iffy, and actually happens in sequence 0. It will make us
524 # return to the caller's caller at function end.
525 nesting = 0
526 if cmd_mn == 'largenoteson':
527 large = True
528 if cmd_mn == 'largenotesoff':
529 large = False
530 if nesting >= 0:
531 decode_list.append((pos, tp, nesting, large))
533 def decode_rec(state, initial):
534 if not initial:
535 v = state[0]
536 gen_label(v, state[1])
537 script_start[v] = True
538 decode_list.append(state)
539 while decode_list:
540 decode_one(decode_list.pop())
542 def main():
543 decode_rec((0, 'seq', 0, False), initial=True)
545 if seq_num == 0:
546 if lang == 'jp':
547 sound_banks = [
548 (0x14C, 0x70),
549 (0x8A8, 0x38), # stated as 0x30
550 (0xB66, 0x38), # stated as 0x30
551 (0xE09, 0x80),
552 (0x194B, 0x28), # stated as 0x20
553 (0x1CA6, 0x80),
554 (0x27C9, 0x20),
555 (0x2975, 0x30),
556 # same script as bank 3
557 # same script as bank 5
559 unused = [
560 (0x1FC4, 'layer_large'),
561 (0x2149, 'layer_large'),
562 (0x2223, 'layer_large'),
563 (0x28C5, 'chan'),
564 (0x3110, 'envelope'),
565 (0x31EC, 'envelope'),
567 elif lang == 'us':
568 sound_banks = [
569 (0x14C, 0x70),
570 (0x8F6, 0x38), # stated as 0x30
571 (0xBB4, 0x40),
572 (0xF8E, 0x80),
573 (0x1AF3, 0x28), # stated as 0x20
574 (0x1E4E, 0x80),
575 (0x2971, 0x20),
576 (0x2B1D, 0x40),
577 # same script as bank 3
578 # same script as bank 5
580 unused = [
581 (0x216C, 'layer_large'),
582 (0x22F1, 'layer_large'),
583 (0x23CB, 'layer_large'),
584 (0x2A6D, 'chan'),
585 (0x339C, 'envelope'),
586 (0x3478, 'envelope'),
588 elif lang == 'eu':
589 sound_banks = [
590 (0x154, 0x70),
591 (0x8FE, 0x38), # stated as 0x30?
592 (0xBBC, 0x40),
593 (0xFA5, 0x80),
594 (0x1B0C, 0x28), # stated as 0x20?
595 (0x1E67, 0x80),
596 (0x298A, 0x20),
597 (0x2B36, 0x40),
598 # same script as bank 3
599 # same script as bank 5
601 unused = [
602 (0xF9A, 'chan'),
603 (0x2185, 'layer_large'),
604 (0x230A, 'layer_large'),
605 (0x23E4, 'layer_large'),
606 (0x2A86, 'chan'),
607 (0x33CC, 'envelope'),
608 (0x34A8, 'envelope'),
611 for (addr, count) in sound_banks:
612 for i in range(count):
613 decode_rec((addr + 2*i, 'soundref', 0, False), initial=True)
615 for (addr, tp) in unused:
616 gen_label(addr, tp + '_unused')
617 decode_rec((addr, tp, 0, force_large_notes), initial=False)
619 for (pos, write_to) in seq_writes:
620 assert '<fixup>' in output[pos]
621 delta = 0
622 while output[write_to] == '':
623 write_to -= 1
624 delta += 1
625 if write_to > pos and all(output[i] == '' for i in range(pos+1, write_to)):
626 nice_target = str(delta)
627 output[pos] = output[pos].replace('writeseq', 'writeseq_nextinstr')
628 else:
629 tp = output_instate[write_to][1] if output_instate[write_to] is not None else 'addr'
630 nice_target = gen_label(write_to, tp) + ", " + str(delta)
631 output[pos] = output[pos].replace('<fixup>', nice_target)
633 # Add unreachable 'end' markers
634 for i in range(1, len(data)):
635 if (data[i] == 0xff and output[i] is None and output[i - 1] is not None
636 and label_name[i] is None):
637 tp = output_instate[i - 1][1]
638 if tp in ["seq", "chan", "layer_small", "layer_large"]:
639 output[i] = gen_mnemonic(tp, 0xff)
641 # Add envelope padding
642 for i in range(1, len(data) - 1):
643 if (data[i] == 0 and output[i] is None and output[i - 1] is not None and
644 output[i + 1] is not None and label_name[i] is None and
645 output[i + 1].startswith('envelope')):
646 script_start[i] = True
647 output[i] = "# padding\n.byte 0"
649 # Add 'unused' marker labels
650 for i in range(1, len(data)):
651 if (output[i] is None and output[i - 1] is not None and label_name[i] is None):
652 script_start[i] = True
653 gen_label(i, 'unused')
655 # Remove up to 15 bytes of padding at the end
656 end_padding = 0
657 for i in range(len(data)-1, -1, -1):
658 if output[i] is not None:
659 break
660 end_padding += 1
661 if end_padding > 15:
662 end_padding = 0
664 if print_end_padding:
665 print(end_padding)
666 sys.exit(0)
668 print(".include \"seq_macros.inc\"")
669 print(".section .rodata")
670 print(".align 0")
671 print("sequence_start:")
672 print()
673 for i in range(len(data) - end_padding):
674 if script_start[i] and i > 0:
675 print()
676 if label_name[i] is not None:
677 print(f"{label_name[i]}:")
678 o = output[i]
679 if o is None:
680 print(f".byte {hex(data[i])}")
681 elif o:
682 print(o)
683 elif label_name[i] is not None:
684 print("<mid-instruction>")
685 errors.append(f"mid-instruction label {label_name[i]}")
687 if hit_eof:
688 errors.append("hit eof!?")
690 if errors:
691 print(f"[{filename}] errors:", file=sys.stderr)
692 for w in errors:
693 print(w, file=sys.stderr)
695 main()