ys2-intro/loader/samples/minexample/furC64/convert_to_asm.py
2025-11-26 19:42:34 +07:00

781 lines
22 KiB
Python

from chipchune.furnace.module import FurnaceModule
from chipchune.furnace.data_types import InsFeatureMacro, InsFeatureC64, InsFeatureAmiga
from chipchune.furnace.enums import MacroCode, MacroItem, MacroType
from chipchune.furnace.enums import InstrumentType
import sys
subsong = 0
note_transpose = 0
dups = {}
print(sys.argv)
module = FurnaceModule(sys.argv[1])
chnum = module.get_num_channels()
speed_type = len(module.subsongs[subsong].speed_pattern)
notes = ["C_","Cs","D_","Ds","E_","F_","Fs","G_","Gs","A_","As","B_"]
def comp(pat):
i = 0
o = []
n = 0
while i < len(pat):
j = i
k = 0
if pat[i] >= 0x40 and pat[i] < 128: i += 1
elif pat[i] == 0xFB: i += 2
elif pat[i] == 0xFC: i += 2
elif pat[i] == 0xE0: i += 2
elif pat[i] == 0xE1: i += 2
elif pat[i] == 0xE2: i += 2
elif pat[i] == 0xE3: i += 2
elif pat[i] == 0xE4: i += 2
elif pat[i] == 0xE5: i += 3
elif pat[i] == 0xE6: i += 2
elif pat[i] == 0xE7: i += 2
elif pat[i] == 0xE8: i += 2
elif pat[i] == 0xE9: i += 3
elif pat[i] == 0xEA: i += 3
elif pat[i] == 0xEB: i += 2
elif pat[i] == 0xEC: i += 2
elif pat[i] == 0xED: i += 2
elif pat[i] == 0xEE: i += 2
elif pat[i] == 0xEF: i += 1
elif pat[i] == 0xF0: i += 1
elif pat[i] == 0xF1: i += 2
elif pat[i] == 0xFF: i += 2
elif pat[i] == 0xFD:
i += 1
n = 2
elif pat[i] == 0xFE:
i += 1
n = 2
elif pat[i] >= 128:
i += 1
n = 2
else:
k = 1
if n == 0:
o.append(pat[i])
elif pat[i] > 1:
o.append(pat[i]-1)
#print(i,pat[i])
i += 1
n = max(n-1,0)
if k == 0:
o.extend(pat[j:i])
#print(pat,"\n",o,"\n")
return o
def conv_pattern(pattern):
out = [0]
oldtemp = [0,0]
r = 0
bitind = 0
oldins = -1
for row in pattern.data:
has03xx = 0
for l in row.effects:
k = list(l)
if k[0] == 0x03 and k[1] > 0:
has03xx = 1
break
temp = []
notnote = 0
new_byte = 0
if row.instrument != 65535 and oldins != row.instrument:
new_byte = 1
if row.instrument < 0x40:
temp.append(row.instrument+0x40)
else:
temp.append(0xFB)
temp.append(row.instrument)
oldins = row.instrument
if row.volume != 65535:
new_byte = 1
temp.append(0xFC)
temp.append(row.volume)
hasEffect = [-1,-1]
has0Dxx = -1
for l in row.effects:
k = list(l)
if k[1] == 65535:
k[1] = 0
if k[0] == 0xD:
new_byte = 1
has0Dxx = k[1]
continue
if k[0] == 0x0B:
new_byte = 1
temp.extend([0xED, k[1]])
has0Dxx = 0
continue
if (k[0] == 0x09 or k[0] == 0x0F) and (speed_type == 1):
new_byte = 1
temp.extend([0xE1, k[1]])
temp.extend([0xE0, k[1]])
continue
if k[0] == 0x0F and (speed_type == 2):
new_byte = 1
temp.extend([0xE1, k[1]])
continue
if k[0] == 0x09 and (speed_type == 2):
new_byte = 1
temp.extend([0xE0, k[1]])
continue
if k[0] == 0x00:
new_byte = 1
temp.extend([0xE2, k[1]])
continue
if k[0] == 0x01:
new_byte = 1
temp.extend([0xE3, k[1]])
continue
if k[0] == 0x02:
new_byte = 1
temp.extend([0xE4, k[1]])
continue
if k[0] == 0x03 and k[1] == 0:
new_byte = 1
temp.extend([0xE4, 0])
continue
if k[0] == 0x03 and k[1] > 0:
new_byte = 1
temp.extend([0xE5, k[1], max(min(notes.index(str(row.note))+(row.octave*12)+note_transpose,95),0)])
continue
if k[0] == 0x04:
new_byte = 1
temp.extend([0xE6, k[1]])
continue
if k[0] == 0x1B:
new_byte = 1
temp.extend([0xE7, k[1]])
continue
if k[0] == 0x1C:
new_byte = 1
temp.extend([0xE8, k[1]])
continue
if k[0] == 0xE1:
new_byte = 1
temp.extend([0xE9, k[1]>>4, k[1]&15])
continue
if k[0] == 0xE2:
new_byte = 1
temp.extend([0xEA, k[1]>>4, k[1]&15])
continue
if k[0] == 0xE5:
new_byte = 1
temp.extend([0xEB, k[1]])
continue
if k[0] == 0xEC:
new_byte = 1
temp.extend([0xEC, k[1]])
continue
if (k[0]>>4) == 4:
new_byte = 1
temp.extend([0xEE, (k[1]|(k[0]&0xf)<<8)>>3])
continue
if k[0] == 0xEA:
new_byte = 1
if k[1] == 0:
temp.extend([0xEF])
else:
temp.extend([0xF0])
continue
if k[0] == 0x1A:
new_byte = 1
if k[1] > 0:
temp.extend([0xF1, 0x00])
else:
temp.extend([0xF1, 0xFF])
continue
if str(row.note) == "OFF_REL":
notnote = 1
new_byte = 1
temp.append(0xFD)
elif str(row.note) == "REL":
notnote = 1
new_byte = 1
temp.append(0xFD)
elif str(row.note) == "OFF":
notnote = 1
new_byte = 1
temp.append(0xFE)
elif str(row.note) == "__" or (has03xx == 1):
if has03xx == 0:
notnote = 1
#temp.append(0x80)
else:
new_byte = 1
temp.append(max(min(notes.index(str(row.note))+(row.octave*12)+note_transpose,95),0)+0x80)
if new_byte == 1:
temp.append(0)
out.extend(temp)
durpass = False
if out[-1] >= 63:
out.append(0)
if has0Dxx > -1:
out[-1] += 1
if out[0] == 0: out = out[1:]
out.extend([0xFF, has0Dxx])
return out
out[-1] += 1
r += 1
out.extend([0xFF, 0])
if out[0] == 0: out = out[1:]
return out
f = open("asm/song.asm","w")
relW = []
relA = []
relD = []
relC = []
f.write("ticks_init:")
f.write(".byte ")
if speed_type == 1:
f.write(str(module.subsongs[subsong].speed_pattern[0])+", ")
f.write(str(module.subsongs[subsong].speed_pattern[0])+"\n")
elif speed_type == 2:
f.write(str(module.subsongs[subsong].speed_pattern[0])+", ")
f.write(str(module.subsongs[subsong].speed_pattern[1])+"\n")
f.write("insFL:\n")
f.write(".lobytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"F")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insFH:\n")
f.write(".hibytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"F")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insAL:\n")
f.write(".lobytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"A")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insAH:\n")
f.write(".hibytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"A")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insDL:\n")
f.write(".lobytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"D")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insDH:\n")
f.write(".hibytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"D")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insWL:\n")
f.write(".lobytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"W")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insWH:\n")
f.write(".hibytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"W")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insCL:\n")
f.write(".lobytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"C")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
f.write("insCH:\n")
f.write(".hibytes ")
for i in range(len(module.instruments)):
f.write("ins"+str(i)+"C")
if i == len(module.instruments)-1:
f.write("\n")
else:
f.write(", ")
for i in range(len(module.instruments)):
features = module.instruments[i].features
a = filter(
lambda x: (
type(x) == InsFeatureMacro
), features
)
macros = []
for j in a:
macros = j.macros
hasWaveMacro = 0
hasCutMacro = 0
for j in macros:
kind = j.kind
if kind == MacroCode.WAVE:
hasWaveMacro = 1
continue
if kind == MacroCode.ALG:
hasCutMacro = 1
continue
hasAbsFilter = False
hasAbsDuty = False
written_ins = False
a = filter(
lambda x: (
type(x) == InsFeatureMacro
), features
)
macros = []
for j in a:
macros = j.macros
for j in macros:
kind = j.kind
if kind == MacroCode.DUTY and j.type == MacroType.LFO:
hasAbsDuty = True
a = filter(
lambda x: (
type(x) == InsFeatureC64
), features
)
for j in a:
wave = j.tri_on
wave |= j.saw_on<<1
wave |= j.pulse_on<<2
wave |= j.noise_on<<3
wave <<= 4
wave |= j.ring_mod<<2
wave |= j.osc_sync<<1
f.write("ins"+str(i)+"F:\n")
f.write(".byte ")
f.write(str(wave)+", ")
ad = (j.envelope.a<<4)|j.envelope.d
sr = (j.envelope.s<<4)|j.envelope.r
f.write(str(ad)+", ")
f.write(str(sr)+", ")
f.write(str(j.duty&0xff)+", ")
f.write(str(j.duty>>8)+", ")
flags = 0
if j.duty_is_abs:
flags |= 1
if hasWaveMacro:
flags |= 2
if j.to_filter:
flags |= 4
if j.init_filter:
flags |= 8
if j.filter_is_abs:
flags |= 16
if hasCutMacro:
flags |= 32
if not j.no_test:
flags |= 64
f.write(str(flags)+", ")
fil = 0
fil = j.lp<<4
fil |= j.bp<<5
fil |= j.hp<<6
fil |= j.ch3_off<<7
f.write(str(j.res<<4)+", ")
f.write(str(fil)+", ")
f.write(str(j.cut&0xff)+", ")
f.write(str((j.cut>>8)&15)+"\n")
f.write("\n")
written_ins = True
hasAbsDuty |= j.duty_is_abs
hasAbsFilter = j.filter_is_abs
if written_ins == False:
f.write("ins"+str(i)+"F:\n")
f.write(".byte 32, 8, 0, 255, 7, 0\n")
a = filter(
lambda x: (
type(x) == InsFeatureMacro
), features
)
arp = [128,0xFF,0xFF]
duty = [0xFF,0xFF]
wave = [0xFF,0xFF]
cutoff = [0xFF,0xFF]
macros = []
for j in a:
macros = j.macros
hasRelTotal = [0,0,0,0]
for j in macros:
kind = j.kind
if kind == MacroCode.ARP:
s = j.speed
arp = []
loop = 0xff
hasRel = 0
oldlen = 0
if j.data[-1] == MacroItem.LOOP:
arr = [MacroItem.LOOP, j.data[-2]]
j.data = j.data[:-2] + arr
for k in j.data:
if k == MacroItem.LOOP:
loop = oldlen
elif k == MacroItem.RELEASE:
arp.append(0xFF)
arp.append(loop)
relA.append(len(arp))
oldlen = max(len(arp),0)
hasRel = 1
elif (k>>30) > 0:
arp.append(0xFE)
k = abs(k^(1<<30))
k = max(min(k,95),0)
arp.append(k%120)
oldlen = max(len(arp),0)
else:
if k < 0:
arp.append((k%120)-120+128)
else:
arp.append((k%120)+128)
oldlen = max(len(arp),0)
if hasRel == 0:
relA.append(len(arp))
hasRelTotal[1] = 1
len_temp = len(arp)
arp.append(0xFF)
arp.append(loop if loop!=len_temp else 0xff)
if kind == MacroCode.DUTY and j.type == MacroType.LFO:
while type(j.data[0]) is not int:
j.data = j.data[1:]
s = j.speed
duty = []
hasRel = 0
lfo = j.data[13]
while lfo <= 1023:
lfo += j.data[11]
if lfo > 1023: break
k = lfo
if k & 512:
k = 1023-lfo
k >>= 1
k = j.data[0]+((k+(j.data[1]-j.data[0])*k)>>8)
if (k&0xff) == 255:
duty.append(254)
else:
duty.append(k&0xff)
duty.append(k>>8)
if hasRel == 0:
relD.append(0)
hasRelTotal[2] = 1
duty.append(0xFF)
duty.append(0)
elif kind == MacroCode.DUTY:
s = j.speed
duty = []
loop = 0xff
loop2 = 0
hasRel = 0
for k in j.data:
if k == MacroItem.LOOP:
loop = loop2
elif k == MacroItem.RELEASE:
duty.append(0xFF)
duty.append(loop)
relD.append(len(duty))
hasRel = 1
else:
loop2 = len(duty)+2
if hasAbsDuty:
if (k&0xff) == 255:
duty.append(254)
else:
duty.append(k&0xff)
duty.append(k>>8)
else:
k = 32768-(k*4) #4)
duty.append(k&0xff)
duty.append(k>>8)
if hasRel == 0:
relD.append(len(duty))
hasRelTotal[2] = 1
len_temp = len(duty)
duty.append(0xFF)
duty.append(loop if loop!=len_temp else 0xff)
if kind == MacroCode.ALG and j.type == MacroType.LFO:
while type(j.data[0]) is not int:
j.data = j.data[1:]
s = j.speed
cutoff = []
hasRel = 0
lfo = j.data[13]
while lfo <= 1023:
lfo += j.data[11]
k = lfo
if k & 512:
k = 1023-lfo
k >>= 1
k = j.data[0]+((k+(j.data[1]-j.data[0])*k)>>8)
if (k&0xff) == 255:
cutoff.append(254)
else:
cutoff.append(k&0xff)
cutoff.append(k>>8)
if hasRel == 0:
relC.append(len(cutoff))
hasRelTotal[3] = 1
cutoff.append(0xFF)
cutoff.append(0)
elif kind == MacroCode.ALG:
s = j.speed
cutoff = []
loop = 0xff
loop2 = 0
hasRel = 0
for k in j.data:
if k == MacroItem.LOOP:
loop = loop2
elif k == MacroItem.RELEASE:
cutoff.append(0xFF)
cutoff.append(loop)
relC.append(len(cutoff))
hasRel = 1
else:
loop2 = len(cutoff)+1
if hasAbsFilter:
if (k&0xff) == 255:
cutoff.append(254)
else:
cutoff.append(k&0xff)
cutoff.append(k>>8)
else:
k = 32768+k*7
cutoff.append(k&0xff)
cutoff.append(k>>8)
if hasRel == 0:
relC.append(len(cutoff))
hasRelTotal[3] = 1
len_temp = len(cutoff)
cutoff.append(0xFF)
cutoff.append(loop if loop!=len_temp else 0xff)
if kind == MacroCode.WAVE:
s = j.speed
wave = []
loop = 0xff
loop2 = 0
hasRel = 0
for k in j.data:
if k == MacroItem.LOOP:
loop = len(wave)
elif k == MacroItem.RELEASE:
wave.append(0xFF)
wave.append(loop)
relW.append(len(wave))
hasRel = 1
else:
wave.append(k<<4)
if hasRel == 0:
relW.append(len(wave))
hasRelTotal[0] = 1
len_temp = len(wave)
wave.append(0xFF)
wave.append(loop if loop!=len_temp else 0xff)
if hasRelTotal[0] == 0:
relW.append(0)
if hasRelTotal[1] == 0:
relA.append(0)
if hasRelTotal[2] == 0:
relD.append(0)
if hasRelTotal[3] == 0:
relC.append(0)
wave = str(wave)[1:-1]
duty = str(duty)[1:-1]
arp = str(arp)[1:-1]
cutoff = str(cutoff)[1:-1]
if arp in dups:
f.write("ins"+str(i)+"A = "+dups[arp]+"\n")
else:
f.write("ins"+str(i)+"A:\n")
f.write(".byte "+arp+"\n")
dups[arp] = "ins"+str(i)+"A"
if duty in dups:
f.write("ins"+str(i)+"D = "+dups[duty]+"\n")
else:
f.write("ins"+str(i)+"D:\n")
f.write(".byte "+duty+"\n")
dups[duty] = "ins"+str(i)+"D"
if wave in dups:
f.write("ins"+str(i)+"W = "+dups[wave]+"\n")
else:
f.write("ins"+str(i)+"W:\n")
f.write(".byte "+wave+"\n")
dups[wave] = "ins"+str(i)+"W"
if cutoff in dups:
f.write("ins"+str(i)+"C = "+dups[cutoff]+"\n")
else:
f.write("ins"+str(i)+"C:\n")
f.write(".byte "+cutoff+"\n")
dups[cutoff] = "ins"+str(i)+"C"
relW = str(relW)[1:-1]
relD = str(relD)[1:-1]
relA = str(relA)[1:-1]
f.write("insArel:\n")
f.write(".byte "+relA+"\n")
f.write("insDrel:\n")
f.write(".byte "+relD+"\n")
f.write("insWrel:\n")
f.write(".byte "+relW+"\n")
for i in range(chnum):
order = module.subsongs[subsong].order[i]
f.write("order"+str(i)+"len = "+str(len(order))+"\n")
f.write("order"+str(i)+"L:\n")
f.write(".byte ")
for o in range(len(order)):
f.write("<(patCH"+str(i)+"N"+str(order[o])+"-1)")
if o == len(order)-1:
f.write("\n")
else:
f.write(", ")
f.write("order"+str(i)+"H:\n")
f.write(".byte ")
for o in range(len(order)):
f.write(">(patCH"+str(i)+"N"+str(order[o])+"-1)")
if o == len(order)-1:
f.write("\n")
else:
f.write(", ")
for i in range(chnum):
order = module.subsongs[subsong].order[i]
avail_patterns = filter(
lambda x: (
x.channel == i and
x.subsong == subsong
),
module.patterns
)
for p in avail_patterns:
patnum = p.index
#print(patnum,i)
g = str(comp(conv_pattern(p)))[1:-1]
f.write("patCH"+str(i)+"N"+str(patnum)+":\n")
f.write(".byte "+g+"\n")
if chnum == 4:
f.write("sampleHS:\n.hibytes ")
for i in range(len(module.samples)):
f.write("PCM"+str(i))
if i == (len(module.samples)-1):
f.write("\n")
else:
f.write(", ")
f.write("sampleHE:\n.hibytes ")
for i in range(len(module.samples)):
f.write("PCMe"+str(i))
if i == (len(module.samples)-1):
f.write("\n")
else:
f.write(", ")
total_maps = []
f.write("insSI:\n.byte ")
for i in range(len(module.instruments)):
features = module.instruments[i].features
a = filter(
lambda x: (
type(x) == InsFeatureAmiga
), features
)
init_sample = 0
for j in a:
if j.init_sample > 0 and j.init_sample != 65535:
init_sample = j.init_sample
break
f.write(str(init_sample))
if i == (len(module.instruments)-1):
f.write("\n")
else:
f.write(", ")
f.write(".res 256-(*&$ff), 0\n")
for i in range(len(module.samples)):
sample = []
rate = max(min(module.samples[i].meta.sample_rate,384000),100)
smp = list(module.samples[i].data)
k = 0
while k < len(smp):
j = smp[int(k)]
s = float(((int(j)+128)&0xff))
s = ((s-128)/1.65)+128
#s = ((s-128)/1.6)+128
s = int(s/16)
#s = (s>>1)+8
sample.append(s)
k += rate/7812
s = sample[-1]
if (len(sample)%256) == 0:
sample.extend([s]*256)
else:
while (len(sample)%256) != 0:
sample.append(s)
f.write("PCM"+str(i)+":\n.byte "+str(sample)[1:-1]+"\n")
f.write("PCMe"+str(i)+":\n")
f.close()
# frequency calculation code taken from
# https://codebase64.org/doku.php?id=base:how_to_calculate_your_own_sid_frequency_table
tuning = module.meta.tuning
f = open("asm/note_lo.bin","wb")
for i in range(96):
hz = tuning * (2**(float(i-57)/12.0))
cnst = (256**3)/985248.0 # PAL frequency
freq = min(max(hz*cnst,0),0xffff)
f.write(bytearray([int(freq)&0xff]))
f.close()
f = open("asm/note_hi.bin","wb")
for i in range(96):
hz = tuning * (2**(float(i-57)/12.0))
cnst = (256**3)/985248.0 # PAL frequency
freq = min(max(hz*cnst,0),0xffff)
f.write(bytearray([(int(freq)>>8)&0xff]))
f.close()