mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 21:36:06 -04:00
docs(phase-29): the cold-start experiment — func_8017BF14 to 45/4763; §79; full R22 discharged
- COLD-START RESULT (verified independently): 4763/4763 ins, 45 mismatched = 99.06% byte / 99.94% structural, exact frame, exact opcode histogram. NOT a match; nothing banked (45 != 0, the byte-gate is the sole arbiter). The residual is 3 register-grant ties, 0 structural divergence. Named next move: variable REUSE across c0..c3/a0v..a3v, the one §76 lever class the pass never reached. - MY BRIEF'S PREMISE WAS WRONG BY CONSTRUCTION -> §79. I chose this target partly because §71's callee-set fingerprint returned 0.00 against every matched giant = "a genuine cold start". But the function makes ZERO jal calls, so its callee fingerprint is EMPTY and §71 CANNOT FIRE: 0.00 meant "cannot answer", not "no relative". Grepping the target's DATA symbol D_800A5E60 found the matched func_8017BEBC at once -- func_8017BF14 is the 4-light-box member of the same renderer family whose 3-box sibling func_8017D960 was matched hours earlier. RULE: when §71 returns an empty/zero-overlap callee set, fall back to DATA-symbol fingerprinting; an empty fingerprint must never become a cold-start brief. - NEW LEVER (§79): THE FRAME LAYOUT IS A DECLARATION-ORDER ORACLE. gcc-2.7.2 assigns stack slots to spilled pseudos in pseudo-number order, and pseudo numbers follow first use ~ declaration order -- so the target's frame map reads back its source's declaration order. Moving ONE line took 73% -> 84% structural and brought all 127 slots into exact correspondence. - §76 CONFIRMED AT SCALE: the entire -62 length residual was ONE allocno-class decision (c0..c3 declared inside the cull blocks -> 1-death local allocnos -> global.c:668-671 removes those regs from the global pool -> r1lo spills), 52% -> 93%. An __asm__ ref-dial reached the same spill and scored WORSE -- declaration scope beat the ref dial again. - PIN NUANCE: pins are safe on a 0-jal function (§74's hazard cannot arise), 4 pins took 94% -> 99%; but §72 held -- pins 5 and 6 made it worse. - EFFORT ANSWER, HONEST: xHigh from a genuine cold start on a 4,763-ins giant bought the decode, the exact length, the exact frame and 99.06%, and did NOT close. Budget a SECOND pass at this size: the first buys structure, the last ~1% is register grants. - FULL R22 DISCHARGED: make clean + extract-all + check-all -> 140 passed, 0 failed of 140 (run after the agent finished, per the deferral recorded in the pool commit). tools-health OK.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""Anchor-segmented shape alignment (robust against repetitive-block confusion).
|
||||
Anchors = the 56 `mult` sites (+ start/end). Aligns segment-by-segment.
|
||||
usage: bf14_ali.py <obj.o> [--worst N]"""
|
||||
import re,subprocess,difflib,sys
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
def load_tgt():
|
||||
W=[];T=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/\s+(.*?)\s*$',l)
|
||||
if m:
|
||||
v=int.from_bytes(bytes.fromhex(m.group(1)),'little')
|
||||
W.append(('R',v) if ('%hi(' in l or '%lo(' in l) else v); T.append(re.sub(r'\s+',' ',m.group(2)))
|
||||
return W,T
|
||||
def load_mine():
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
W=[];T=[];inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l) and W: break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s+(.*)',l)
|
||||
if m: W.append(int(m.group(1),16)); T.append(re.sub(r'\s+',' ',m.group(2).strip()))
|
||||
elif 'R_MIPS_' in l and W and not isinstance(W[-1],tuple) and (W[-1]>>26) not in (2,3):
|
||||
W[-1]=('R',W[-1])
|
||||
return W,T
|
||||
MEM=set(range(0x20,0x30))|{0x32,0x3A}
|
||||
def shape(x):
|
||||
rel=isinstance(x,tuple); v=x[1] if rel else x
|
||||
op=(v>>26)&0x3F
|
||||
if op==0 or op==0x1C: return ((op<<12)|(v&0x3F)|(((v>>6)&0x1F)<<7),'R' if rel else '')
|
||||
if op in (2,3): return (op<<26,'')
|
||||
if op==0x12: return (v&0xFC1F07FF,'')
|
||||
if op in (1,4,5,6,7): return ((op<<20)|(((v>>16)&0x1F)<<8),'')
|
||||
imm=0 if rel else (v&0xFFFF)
|
||||
if op in MEM and ((v>>21)&0x1F)==29: imm=0
|
||||
return ((op<<20)|imm,'R' if rel else '')
|
||||
def full(x):
|
||||
rel=isinstance(x,tuple); v=x[1] if rel else x
|
||||
op=(v>>26)&0x3F
|
||||
if rel: return (v&0xFFFF0000,'R')
|
||||
if op in (2,3): return (op<<26,'')
|
||||
if op in (1,4,5,6,7): return (v&0xFFFF0000,'')
|
||||
return (v,'')
|
||||
|
||||
def anchors(W):
|
||||
return [i for i,v in enumerate(W) if not isinstance(v,tuple) and (v&0xFC00003F)==0x18] # SPECIAL mult
|
||||
TW,TT=load_tgt(); MW,MT=load_mine()
|
||||
ta=anchors(TW); ma=anchors(MW)
|
||||
if len(ta)!=len(ma):
|
||||
print('ANCHOR-MISMATCH tgt=%d mine=%d (falling back to global)'%(len(ta),len(ma)))
|
||||
ta=[];ma=[]
|
||||
tb=[0]+ta+[len(TW)]; mb=[0]+ma+[len(MW)]
|
||||
tot=0; ftot=0; segs=[]
|
||||
for k in range(len(tb)-1):
|
||||
a=[shape(x) for x in MW[mb[k]:mb[k+1]]]; b=[shape(x) for x in TW[tb[k]:tb[k+1]]]
|
||||
sm=difflib.SequenceMatcher(None,a,b,autojunk=False)
|
||||
eq=sum(i2-i1 for t,i1,i2,j1,j2 in sm.get_opcodes() if t=='equal')
|
||||
fa=[full(x) for x in MW[mb[k]:mb[k+1]]]; fb=[full(x) for x in TW[tb[k]:tb[k+1]]]
|
||||
fsm=difflib.SequenceMatcher(None,fa,fb,autojunk=False)
|
||||
feq=sum(i2-i1 for t,i1,i2,j1,j2 in fsm.get_opcodes() if t=='equal')
|
||||
tot+=eq; ftot+=feq; segs.append((tb[k+1]-tb[k]-eq, k, tb[k],tb[k+1], mb[k],mb[k+1]))
|
||||
print('ANCHOR-ALIGNED shape %d / %d = %.2f%% BYTE %d = %.2f%% (mine=%d target=%d)'
|
||||
%(tot,len(TW),100.0*tot/len(TW),ftot,100.0*ftot/len(TW),len(MW),len(TW)))
|
||||
if '--worst' in sys.argv:
|
||||
n=int(sys.argv[sys.argv.index('--worst')+1])
|
||||
for bad,k,t0,t1,m0,m1 in sorted(segs,reverse=True)[:n]:
|
||||
print(' seg %3d bad=%4d tgt[%d:%d](%d) mine[%d:%d](%d)'%(k,bad,t0,t1,t1-t0,m0,m1,m1-m0))
|
||||
@@ -0,0 +1,25 @@
|
||||
"""opcode-histogram delta mine-vs-target (alignment free). usage: bf14_hist.py <obj.o> [--sum]"""
|
||||
import re,collections,subprocess,sys
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
t=collections.Counter()
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ \w+ \w+ \*/\s+(\S+)',l)
|
||||
if m: t['GTE' if m.group(1) in ('rtps','rtpt','nclip') else m.group(1)]+=1
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
alias={'move':'addu','li':'addiu','b':'j','negu':'subu','c2':'GTE'}
|
||||
m=collections.Counter(); inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True; continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l): break
|
||||
mm=re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(\S+)',l)
|
||||
if mm: m[alias.get(mm.group(1),mm.group(1))]+=1
|
||||
d={k:m[k]-t[k] for k in set(t)|set(m) if m[k]!=t[k]}
|
||||
tot=sum(m.values())-sum(t.values())
|
||||
if '--sum' in sys.argv:
|
||||
print('len%+d L1=%d %s'%(tot,sum(abs(v) for v in d.values()),
|
||||
' '.join('%s%+d'%(k,v) for k,v in sorted(d.items(),key=lambda kv:-abs(kv[1])))))
|
||||
else:
|
||||
for k,v in sorted(d.items(),key=lambda kv:-abs(kv[1])): print('%-8s %+d'%(k,v))
|
||||
print('TOTAL %+d'%tot)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Landmark alignment: index of each occurrence of rare opcodes in mine vs target."""
|
||||
import re,subprocess,sys,collections
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
def tgt():
|
||||
o=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ \w+ \w+ \*/\s+(\S+)',l)
|
||||
if m: o.append(m.group(1))
|
||||
return o
|
||||
def mine():
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
o=[];inside=False
|
||||
alias={'move':'addu','li':'addiu','b':'j','negu':'subu'}
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l): break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(\S+)',l)
|
||||
if m: o.append(alias.get(m.group(1),m.group(1)))
|
||||
return o
|
||||
T,M=tgt(),mine()
|
||||
OPS=sys.argv[2].split(',') if len(sys.argv)>2 else ['mult']
|
||||
ti=[i for i,x in enumerate(T) if x in OPS]
|
||||
mi=[i for i,x in enumerate(M) if x in OPS]
|
||||
print('target %d occurrences, mine %d'%(len(ti),len(mi)))
|
||||
for k in range(max(len(ti),len(mi))):
|
||||
a=ti[k] if k<len(ti) else None; b=mi[k] if k<len(mi) else None
|
||||
d=(b-a) if (a is not None and b is not None) else None
|
||||
print('%3d tgt=%-6s mine=%-6s delta=%s'%(k,a,b,d))
|
||||
@@ -0,0 +1,407 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Lever/variant generator for func_8017BF14 drafts.
|
||||
usage: bf14_mk.py <base.c> <out.c> lever [lever ...]
|
||||
Every transformation asserts it applied, so a "neutral" reading can never be a
|
||||
silent no-op edit (d960_mk.py discipline)."""
|
||||
import sys, re
|
||||
|
||||
def _one(src, old, new, n=1):
|
||||
c = src.count(old)
|
||||
assert c == n, 'expected %d occurrences, found %d of %r' % (n, c, old[:80])
|
||||
return src.replace(old, new)
|
||||
|
||||
RDECL = (' s16 r0;\n s16 r1;\n s16 r2;\n s16 r3;\n'
|
||||
' s16 r0lo;\n s16 r1lo;\n s16 r2lo;\n s16 r3lo;\n')
|
||||
|
||||
LEVERS = {}
|
||||
def lever(fn):
|
||||
LEVERS[fn.__name__] = fn
|
||||
return fn
|
||||
|
||||
def rdecl_order(src, order):
|
||||
"""order: e.g. '0,0lo,1,1lo,2,2lo,3,3lo'"""
|
||||
new = ''.join(' s16 r%s;\n' % t for t in order.split(','))
|
||||
return _one(src, RDECL, new)
|
||||
|
||||
# ---- assignment order inside each box's if-block ---------------------------
|
||||
BOXASN = {
|
||||
0: (' r0lo = D_80197C34 - 0x200;\n r0 = D_80197C34;\n',
|
||||
' r0 = D_80197C34;\n r0lo = D_80197C34 - 0x200;\n'),
|
||||
1: (' r1 = D_80197C50;\n r1lo = D_80197C50 - 0x200;\n',
|
||||
' r1lo = D_80197C50 - 0x200;\n r1 = D_80197C50;\n'),
|
||||
2: (' r2 = D_80197C6C;\n r2lo = D_80197C6C - 0x200;\n',
|
||||
' r2lo = D_80197C6C - 0x200;\n r2 = D_80197C6C;\n'),
|
||||
3: (' r3lo = D_80197C6C - 0x200;\n r3 = D_80197C88;\n',
|
||||
' r3 = D_80197C88;\n r3lo = D_80197C6C - 0x200;\n'),
|
||||
}
|
||||
def swapbox(src, k):
|
||||
a, b = BOXASN[k]
|
||||
return _one(src, a, b)
|
||||
|
||||
for k in range(4):
|
||||
def mk(k=k):
|
||||
def f(src): return swapbox(src, k)
|
||||
return f
|
||||
g = mk(); g.__name__ = 'swapasn%d' % k; lever(g)
|
||||
|
||||
# ---- the r-init block -------------------------------------------------------
|
||||
RINIT = (' r2lo = 0;\n r1lo = 0;\n r0lo = 0;\n'
|
||||
' r2 = 0;\n r1 = 0;\n r0 = 0;\n')
|
||||
def rinit(src, spec):
|
||||
new = ''.join(' r%s = 0;\n' % t for t in spec.split(',')) if spec else ''
|
||||
return _one(src, RINIT, new)
|
||||
|
||||
# ---- misc -------------------------------------------------------------------
|
||||
@lever
|
||||
def basefn(src):
|
||||
src = _one(src, ' base = D_800AF630;\n', '')
|
||||
return _one(src, '(*(u16 *)(base + 0xA3D2))', '(*(u16 *)(D_800AF630 + 0xA3D2))')
|
||||
|
||||
@lever
|
||||
def baselate(src):
|
||||
src = _one(src, ' base = D_800AF630;\n', '')
|
||||
return _one(src, ' pkt = D_800A5E60;\n', ' base = D_800AF630;\n pkt = D_800A5E60;\n')
|
||||
|
||||
@lever
|
||||
def fzero_fwd(src):
|
||||
return _one(src, 'f3 = 0; f2 = 0; f1 = 0; f0 = 0;',
|
||||
'f0 = 0; f1 = 0; f2 = 0; f3 = 0;', 2)
|
||||
|
||||
@lever
|
||||
def mnydial1(src):
|
||||
o = ' s32 za, zb;\n'
|
||||
return _one(src, o, o + ' __asm__ __volatile__ ("" :: "r" (mny));\n')
|
||||
|
||||
@lever
|
||||
def primorder(src):
|
||||
return _one(src,
|
||||
' nprim = part->nprim;\n prim = (Prim *)part->prim;',
|
||||
' prim = (Prim *)part->prim;\n nprim = part->nprim;')
|
||||
|
||||
@lever
|
||||
def r3own(src):
|
||||
"""box3 uses its OWN range global for r3lo (not the copy-paste of box2's)."""
|
||||
return _one(src, ' r3lo = D_80197C6C - 0x200;\n', ' r3lo = D_80197C88 - 0x200;\n')
|
||||
|
||||
|
||||
@lever
|
||||
def cb101010(src):
|
||||
"""unlit rgbc is (tp[0] & 0xFF000000) | 0x101010, not plain black."""
|
||||
return _one(src, 'cb = tp[0] & 0xFF000000;',
|
||||
'cb = (tp[0] & 0xFF000000) | 0x101010;', 2)
|
||||
|
||||
@lever
|
||||
def k101010(src):
|
||||
"""same, but through a dedicated function-scope constant holder."""
|
||||
src = _one(src, ' u32 cb;\n', ' u32 cb;\n u32 kk;\n')
|
||||
src = _one(src, ' base = D_800AF630;\n', ' base = D_800AF630;\n kk = 0x101010;\n')
|
||||
return _one(src, 'cb = tp[0] & 0xFF000000;', 'cb = (tp[0] & 0xFF000000) | kk;', 2)
|
||||
|
||||
|
||||
# ---- declaration-scope levers (cookbook 76) --------------------------------
|
||||
CULL = ['\n' + ' '*44 + 's32 za, zb;\n',
|
||||
'\n' + ' '*48 + 's32 za, zb;\n']
|
||||
LIT = [' u32 *otp;\n',
|
||||
' u32 *otp;\n']
|
||||
UNLIT= [' u32 *otp;\n',
|
||||
' u32 *otp;\n']
|
||||
|
||||
def _movedecl(src, fndecl, anchors):
|
||||
"""delete the function-scope decl line and re-insert after each anchor."""
|
||||
src = _one(src, ' %s\n' % fndecl, '')
|
||||
for a in anchors:
|
||||
ind = ' ' * (len(a.rstrip('\n').split('\n')[-1]) - len(a.rstrip('\n').split('\n')[-1].lstrip()))
|
||||
src = _one(src, a, a + ind + fndecl + '\n')
|
||||
return src
|
||||
|
||||
def scope(src, fndecl, where):
|
||||
if where == 'cull': return _movedecl(src, fndecl, CULL)
|
||||
if where == 'lit':
|
||||
# 4 occurrences of `u32 *otp;` (2 lit + 2 unlit arms); target only the LIT ones
|
||||
out = src.replace(' %s\n' % fndecl, '', 1)
|
||||
assert out != src
|
||||
for a in (' if (f0 | f1 | f2 | f3) {\n u32 *otp;\n',
|
||||
' if (f0 | f1 | f2 | f3) {\n u32 *otp;\n'):
|
||||
ind = ' ' * (len(a.split('\n')[1]) - len(a.split('\n')[1].lstrip()))
|
||||
out = _one(out, a, a + ind + fndecl + '\n')
|
||||
return out
|
||||
raise KeyError(where)
|
||||
|
||||
def _mkscope(name, decl, where):
|
||||
def f(src): return scope(src, decl, where)
|
||||
f.__name__ = name; lever(f); return f
|
||||
|
||||
_mkscope('xyz_cull', 's16 x0, y0, z0, x1, y1, z1, x2, y2, z2;', 'cull')
|
||||
_mkscope('a_cull', 's32 a0v, a1v, a2v, a3v;', 'cull')
|
||||
_mkscope('c_cull', 's32 c0, c1, c2, c3;', 'cull')
|
||||
_mkscope('d_cull', 's32 d;', 'cull')
|
||||
_mkscope('f_cull', 's32 f0, f1, f2, f3;', 'cull')
|
||||
_mkscope('tp_cull', 'u32 *tp;', 'cull')
|
||||
_mkscope('a_lit', 's32 a0v, a1v, a2v, a3v;', 'lit')
|
||||
_mkscope('c_lit', 's32 c0, c1, c2, c3;', 'lit')
|
||||
_mkscope('d_lit', 's32 d;', 'lit')
|
||||
_mkscope('xyz_lit', 's16 x0, y0, z0, x1, y1, z1, x2, y2, z2;', 'lit')
|
||||
|
||||
@lever
|
||||
def novw(src):
|
||||
"""drop the dedicated vertex-word temps; reuse w / wz."""
|
||||
src = _one(src, ' u32 vw, vzw;\n', '')
|
||||
src = src.replace('vw = *(u32 *)', 'w = *(u32 *)').replace('vzw = *(u32 *)', 'wz = *(u32 *)')
|
||||
src = re.sub(r'= vw;( |$)', r'= w;\1', src)
|
||||
src = src.replace('vw >> 16', 'w >> 16').replace('= vzw;', '= wz;')
|
||||
assert 'vw' not in src and 'vzw' not in src
|
||||
return src
|
||||
|
||||
def dial(src, name, n=2):
|
||||
"""RC-15 zero-byte ref dial: first statement of each cull block."""
|
||||
for a in CULL:
|
||||
last = a.rstrip('\n').split('\n')[-1]
|
||||
ind = ' ' * (len(last) - len(last.lstrip()))
|
||||
src = _one(src, a, a + ind + '__asm__ __volatile__ ("" :: "r" (%s));\n' % name)
|
||||
return src
|
||||
|
||||
@lever
|
||||
def dial_mny(src): return dial(src, 'mny')
|
||||
@lever
|
||||
def dial_my(src): return dial(src, 'my')
|
||||
@lever
|
||||
def dial_r1lo(src): return dial(src, 'r1lo')
|
||||
@lever
|
||||
def dial_r0lo(src): return dial(src, 'r0lo')
|
||||
@lever
|
||||
def dial_r1(src): return dial(src, 'r1')
|
||||
|
||||
|
||||
# ---- generic zero-byte ref dial on any variable, at a chosen anchor --------
|
||||
PRIMHEAD = (' for (i = 0; i < nprim; i++, prim++) {\n'
|
||||
' w = prim->w1;\n')
|
||||
LITARM = ['\n' + ' '*44 + 'if (f0 | f1 | f2 | f3) {\n' + ' '*44 + 'u32 *otp;\n',
|
||||
'\n' + ' '*48 + 'if (f0 | f1 | f2 | f3) {\n' + ' '*48 + 'u32 *otp;\n']
|
||||
|
||||
def dialat(src, name, where):
|
||||
stmt = '__asm__ __volatile__ ("" :: "r" (%s));\n'
|
||||
if where == 'cull':
|
||||
for a in CULL:
|
||||
last = a.rstrip('\n').split('\n')[-1]
|
||||
ind = ' ' * (len(last) - len(last.lstrip()))
|
||||
src = _one(src, a, a + ind + stmt % name)
|
||||
elif where == 'prim':
|
||||
src = _one(src, PRIMHEAD, PRIMHEAD + ' '*24 + stmt % name)
|
||||
elif where == 'lit':
|
||||
for a in LITARM:
|
||||
last = a.rstrip('\n').split('\n')[-1]
|
||||
ind = ' ' * (len(last) - len(last.lstrip()))
|
||||
src = _one(src, a, a + ind + stmt % name)
|
||||
else: raise KeyError(where)
|
||||
return src
|
||||
|
||||
# ---- move the whole r-declaration block ------------------------------------
|
||||
def rpos(src, where):
|
||||
src2 = _one(src, RDECL, '')
|
||||
if where == 'top':
|
||||
return _one(src2, ' s32 j;\n', RDECL + ' s32 j;\n')
|
||||
if where == 'bot':
|
||||
return _one(src2, ' u32 rgbw;\n', ' u32 rgbw;\n' + RDECL)
|
||||
if where == 'prelo':
|
||||
return _one(src2, ' s16 lo0x, hi0x,', RDECL + ' s16 lo0x, hi0x,')
|
||||
if where == 'precxy':
|
||||
return _one(src2, ' s16 cx0, cy0, cz0,', RDECL + ' s16 cx0, cy0, cz0,')
|
||||
raise KeyError(where)
|
||||
|
||||
# ---- per-EMIT-ARM declaration scope (cookbook 76 / d960 L1) ----------------
|
||||
def armdecl(src, decl, arms='all'):
|
||||
src = _one(src, ' %s\n' % decl, '')
|
||||
for ind, n in ((44, 2), (48, 2)):
|
||||
a = '\n' + ' ' * ind + 'u32 *otp;\n'
|
||||
c = src.count(a)
|
||||
assert c == n, 'arm anchor %d: expected %d found %d' % (ind, n, c)
|
||||
src = src.replace(a, a + ' ' * ind + decl + '\n')
|
||||
return src
|
||||
|
||||
# ---- reposition a declaration line (slot order == pseudo order == decl order) ----
|
||||
def declmove(src, decl, anchor):
|
||||
src = _one(src, ' %s\n' % decl, '')
|
||||
return _one(src, ' %s\n' % anchor, ' %s\n %s\n' % (anchor, decl))
|
||||
|
||||
def armdecl_pre(src, decl):
|
||||
"""same as armdecl but the decl goes BEFORE `u32 *otp;` in each arm."""
|
||||
src = _one(src, ' %s\n' % decl, '')
|
||||
for ind, n in ((44, 2), (48, 2)):
|
||||
a = '\n' + ' ' * ind + 'u32 *otp;\n'
|
||||
assert src.count(a) == n
|
||||
src = src.replace(a, '\n' + ' ' * ind + decl + '\n' + ' ' * ind + 'u32 *otp;\n')
|
||||
return src
|
||||
|
||||
@lever
|
||||
def dial_mny_tri(src):
|
||||
a = CULL[0]
|
||||
last = a.rstrip('\n').split('\n')[-1]
|
||||
ind = ' ' * (len(last) - len(last.lstrip()))
|
||||
return _one(src, a, a + ind + '__asm__ __volatile__ ("" :: "r" (mny));\n')
|
||||
|
||||
@lever
|
||||
def dial_mny_quad(src):
|
||||
a = CULL[1]
|
||||
last = a.rstrip('\n').split('\n')[-1]
|
||||
ind = ' ' * (len(last) - len(last.lstrip()))
|
||||
return _one(src, a, a + ind + '__asm__ __volatile__ ("" :: "r" (mny));\n')
|
||||
|
||||
FDECL = ' s32 f0, f1, f2, f3;\n'
|
||||
@lever
|
||||
def fsplit_pre(src):
|
||||
"""f0,f1 declared BEFORE pkt (lower pseudo nos); f2,f3 after pkt (slot order kept)."""
|
||||
src = _one(src, FDECL, '')
|
||||
return _one(src, ' u8 *pkt;\n',
|
||||
' s32 f0, f1;\n u8 *pkt;\n s32 f2, f3;\n')
|
||||
@lever
|
||||
def fsplit_post(src):
|
||||
src = _one(src, FDECL, '')
|
||||
return _one(src, ' u8 *pkt;\n', ' u8 *pkt;\n s32 f2, f3;\n s32 f0, f1;\n')
|
||||
@lever
|
||||
def fdecl_pkt(src):
|
||||
src = _one(src, FDECL, '')
|
||||
return _one(src, ' u8 *pkt;\n', ' u8 *pkt;\n' + FDECL)
|
||||
@lever
|
||||
def xyz_split(src):
|
||||
"""x2,y2,z2 declared last (higher pseudo nos -> lower tiebreak priority)."""
|
||||
o = ' s16 x0, y0, z0, x1, y1, z1, x2, y2, z2;\n'
|
||||
return _one(src, o, ' s16 x0, y0, z0, x1, y1, z1;\n s16 x2, y2, z2;\n')
|
||||
@lever
|
||||
def xyz_late(src):
|
||||
o = ' s16 x0, y0, z0, x1, y1, z1, x2, y2, z2;\n'
|
||||
src = _one(src, o, '')
|
||||
return _one(src, ' u32 rgbw;\n', ' u32 rgbw;\n' + o)
|
||||
|
||||
def pin(src, decl, reg):
|
||||
"""turn a plain decl into a `register ... __asm__("$N")` pin (72: PREFERENCE)."""
|
||||
return _one(src, ' %s;\n' % decl, ' register %s __asm__("%s");\n' % (decl, reg))
|
||||
|
||||
@lever
|
||||
def pin_va(src):
|
||||
return _one(src, ' u8 *va, *vb, *vc;\n',
|
||||
' register u8 *va __asm__("$10");\n u8 *vb, *vc;\n')
|
||||
@lever
|
||||
def pin_f0(src):
|
||||
return _one(src, ' s32 f0, f1, f2, f3;\n',
|
||||
' register s32 f0 __asm__("$19");\n s32 f1, f2, f3;\n')
|
||||
@lever
|
||||
def pin_f0_pkt(src):
|
||||
return _one(src, ' u8 *pkt;\n s32 f0, f1, f2, f3;\n',
|
||||
' u8 *pkt;\n register s32 f0 __asm__("$19");\n s32 f1, f2, f3;\n')
|
||||
|
||||
@lever
|
||||
def pin_w(src):
|
||||
return _one(src, ' u32 w; s32 code;\n',
|
||||
' register u32 w __asm__("$5");\n s32 code;\n')
|
||||
@lever
|
||||
def pin_c1(src):
|
||||
o = ' s32 c0, c1, c2, c3;\n'
|
||||
o2 = ' s32 c0, c1, c2, c3;\n'
|
||||
assert src.count(o) >= 1
|
||||
src = src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c1 __asm__("$4"); s32 c0, c2, c3;')
|
||||
return src
|
||||
|
||||
@lever
|
||||
def pin_call(src):
|
||||
"""pin the four vertex colours to the target's grants: c0=$t4 c1=$a0 c2=$t2 c3=$a2."""
|
||||
n = src.count('s32 c0, c1, c2, c3;')
|
||||
assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c0 __asm__("$12"); register s32 c1 __asm__("$4"); '
|
||||
'register s32 c2 __asm__("$10"); register s32 c3 __asm__("$6");')
|
||||
|
||||
@lever
|
||||
def pin_c01(src):
|
||||
n = src.count('s32 c0, c1, c2, c3;')
|
||||
assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c0 __asm__("$12"); register s32 c1 __asm__("$4"); s32 c2, c3;')
|
||||
|
||||
@lever
|
||||
def pin_c012(src):
|
||||
n = src.count('s32 c0, c1, c2, c3;')
|
||||
assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c0 __asm__("$12"); register s32 c1 __asm__("$4"); '
|
||||
'register s32 c2 __asm__("$10"); s32 c3;')
|
||||
|
||||
@lever
|
||||
def pin_c0(src):
|
||||
n = src.count('s32 c0, c1, c2, c3;'); assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c0 __asm__("$12"); s32 c1, c2, c3;')
|
||||
@lever
|
||||
def pin_c2(src):
|
||||
n = src.count('s32 c0, c1, c2, c3;'); assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c2 __asm__("$10"); s32 c0, c1, c3;')
|
||||
@lever
|
||||
def pin_c1c2(src):
|
||||
n = src.count('s32 c0, c1, c2, c3;'); assert n == 2, n
|
||||
return src.replace('s32 c0, c1, c2, c3;',
|
||||
'register s32 c1 __asm__("$4"); register s32 c2 __asm__("$10"); s32 c0, c3;')
|
||||
@lever
|
||||
def vab_swap(src):
|
||||
o = (' va = vtx + (w & 0xFFFF);\n'
|
||||
' vb = vtx + (w >> 16);\n')
|
||||
return _one(src, o, (' vb = vtx + (w >> 16);\n'
|
||||
' va = vtx + (w & 0xFFFF);\n'))
|
||||
@lever
|
||||
def vd_late(src):
|
||||
o = ' vd = vtx + (w & 0xFFF8);\n'
|
||||
src = _one(src, o, '')
|
||||
a = ' gte_stopz(&g.opz);\n'
|
||||
return _one(src, a, o + a)
|
||||
|
||||
@lever
|
||||
def cb_two(src):
|
||||
"""unlit rgbc as a 2-statement accumulator."""
|
||||
n = src.count('cb = (tp[0] & 0xFF000000) | 0x101010;'); assert n == 2, n
|
||||
return src.replace('cb = (tp[0] & 0xFF000000) | 0x101010;',
|
||||
'cb = tp[0] & 0xFF000000;\n cb |= 0x101010;')
|
||||
@lever
|
||||
def rgb_chain(src):
|
||||
"""lit rgb word as a 3-statement accumulator instead of one 4-term expr."""
|
||||
out, n = re.subn(r'rgbw = \((c\d) \| cb\) \| \((c\d) << 8\) \| \((c\d) << 16\);',
|
||||
lambda m: 'rgbw = %s | cb; rgbw |= %s << 8; rgbw |= %s << 16;'
|
||||
% (m.group(1), m.group(2), m.group(3)), src)
|
||||
assert n == 7, n
|
||||
return out
|
||||
@lever
|
||||
def rgb_chain_q(src):
|
||||
"""3-statement accumulator in the QUAD lit arm only (4 sites)."""
|
||||
i = src.index('PolyGT4 *)pkt)->rgb0')
|
||||
head, tail = src[:i], src[i:]
|
||||
out, n = re.subn(r'rgbw = \((c\d) \| cb\) \| \((c\d) << 8\) \| \((c\d) << 16\);',
|
||||
lambda m: 'rgbw = %s | cb; rgbw |= %s << 8; rgbw |= %s << 16;'
|
||||
% (m.group(1), m.group(2), m.group(3)), tail)
|
||||
assert n == 3, n
|
||||
# the rgb0 one sits just before the marker
|
||||
j = head.rindex('rgbw = ')
|
||||
h2 = head[:j] + re.sub(r'rgbw = \((c\d) \| cb\) \| \((c\d) << 8\) \| \((c\d) << 16\);',
|
||||
lambda m: 'rgbw = %s | cb; rgbw |= %s << 8; rgbw |= %s << 16;'
|
||||
% (m.group(1), m.group(2), m.group(3)), head[j:])
|
||||
return h2 + out
|
||||
def main():
|
||||
base, out, levers = sys.argv[1], sys.argv[2], sys.argv[3:]
|
||||
src = open(base).read()
|
||||
for lv in levers:
|
||||
if lv.startswith('D:'):
|
||||
src = rdecl_order(src, lv[2:]); continue
|
||||
if lv.startswith('W:'):
|
||||
_, d, a = lv.split('@'); src = declmove(src, d.replace('~',' '), a.replace('~',' ')); continue
|
||||
if lv.startswith('E:'):
|
||||
src = armdecl_pre(src, lv[2:].replace('~', ' ')); continue
|
||||
if lv.startswith('A:'):
|
||||
src = armdecl(src, lv[2:].replace('~', ' ')); continue
|
||||
if lv.startswith('V:'):
|
||||
_, nm, wh = lv.split(':'); src = dialat(src, nm, wh); continue
|
||||
if lv.startswith('P:'):
|
||||
src = rpos(src, lv[2:]); continue
|
||||
if lv.startswith('I:'):
|
||||
src = rinit(src, lv[2:]); continue
|
||||
src = LEVERS[lv](src)
|
||||
open(out, 'w').write(src)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,43 @@
|
||||
"""Register-correspondence census over index-aligned instructions (same length only)."""
|
||||
import sys,os,collections
|
||||
sys.path.insert(0,'/home/musashi/bfm-decomp/tools')
|
||||
import masked_diff, re, subprocess, shutil
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
mine=masked_diff.insns_from_object(OBJ,'func_8017BF14')
|
||||
tgt=masked_diff.insns_from_s(TGT)
|
||||
R=['zero','at','v0','v1','a0','a1','a2','a3','t0','t1','t2','t3','t4','t5','t6','t7',
|
||||
's0','s1','s2','s3','s4','s5','s6','s7','t8','t9','k0','k1','gp','sp','s8','ra']
|
||||
pairs=collections.Counter()
|
||||
for i,(a,b) in enumerate(zip(mine,tgt)):
|
||||
aw = a if isinstance(a,int) else None
|
||||
# use raw words
|
||||
for i,(a,b) in enumerate(zip(mine,tgt)):
|
||||
pass
|
||||
# simpler: raw words via objdump/target parse
|
||||
def words_t():
|
||||
w=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/',l)
|
||||
if m: w.append(int.from_bytes(bytes.fromhex(m.group(1)),'little'))
|
||||
return w
|
||||
OD=[c for c in ["mips-linux-gnu-objdump","mipsel-linux-gnu-objdump"] if shutil.which(c)][0]
|
||||
out=subprocess.run([OD,"-drz",OBJ],capture_output=True,text=True).stdout
|
||||
def words_m():
|
||||
w=[];inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l) and w: break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s',l)
|
||||
if m: w.append(int(m.group(1),16))
|
||||
return w
|
||||
T,M=words_t(),words_m()
|
||||
print('len',len(M),len(T))
|
||||
for a,b in zip(M,T):
|
||||
if a==b: continue
|
||||
if (a>>26)!=(b>>26): continue
|
||||
for sh in (21,16,11):
|
||||
ra=(a>>sh)&0x1F; rb=(b>>sh)&0x1F
|
||||
if ra!=rb: pairs[(R[ra],R[rb])]+=1
|
||||
for (a,b),n in pairs.most_common(20): print(' mine $%-4s -> target $%-4s x%d'%(a,b,n))
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/bin/bash
|
||||
# bf14_score.sh <draft.c> <workdir> [tag]
|
||||
# bf14_score.sh <draft.c> <workdir> [tag] -> "TAG :: len=N d=+X mism=M shape=P% L1=Q"
|
||||
cd /home/musashi/bfm-decomp
|
||||
SRC="$1"; WD="$2"; TAG="${3:-$(basename $1 .c)}"
|
||||
bash .run/giants/bf14_cc.sh "$SRC" "$WD" >/dev/null 2>"$WD.err" || { echo "$TAG :: COMPILE-FAIL $(tail -5 $WD.err|tr '\n' ' ')"; exit 1; }
|
||||
A=$(python3 .run/giants/bf14_full.py "$WD/t.o" --count)
|
||||
echo "$TAG :: $A"
|
||||
bash .run/giants/bf14_cc.sh "$SRC" "$WD" >/dev/null 2>"$WD.err" || { echo "$TAG :: COMPILE-FAIL $(tail -4 $WD.err|tr '\n' ' ')"; exit 1; }
|
||||
A=$(python3 .run/giants/bf14_full.py "$WD/t.o" --count 2>/dev/null)
|
||||
H=$(python3 .run/giants/bf14_hist.py "$WD/t.o" --sum 2>/dev/null)
|
||||
S=$(python3 .run/giants/bf14_shape.py "$WD/t.o" 0 2>/dev/null | head -1 | grep -oP 'SHAPE-aligned \d+/\d+ = \K[0-9.]+')
|
||||
echo "$TAG :: $A | shape=$S% | $H"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""Segment-anchored shape diff. bf14_seg.py <obj.o> <tlo> <thi> <mlo> <mhi> [nh]"""
|
||||
import re,subprocess,difflib,sys
|
||||
OBJ=sys.argv[1]; TLO,THI,MLO,MHI=[int(x) for x in sys.argv[2:6]]
|
||||
NH=int(sys.argv[6]) if len(sys.argv)>6 else 30
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
TW=[];TT=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/\s+(.*?)\s*$',l)
|
||||
if m:
|
||||
v=int.from_bytes(bytes.fromhex(m.group(1)),'little')
|
||||
TW.append(('R',v) if ('%hi(' in l or '%lo(' in l) else v); TT.append(re.sub(r'\s+',' ',m.group(2)))
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
MW=[];MT=[];inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l) and MW: break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s+(.*)',l)
|
||||
if m: MW.append(int(m.group(1),16)); MT.append(re.sub(r'\s+',' ',m.group(2).strip()))
|
||||
elif 'R_MIPS_' in l and MW and not isinstance(MW[-1],tuple) and (MW[-1]>>26) not in (2,3):
|
||||
MW[-1]=('R',MW[-1])
|
||||
MEM=set(range(0x20,0x30))|{0x32,0x3A}
|
||||
def shape(x):
|
||||
rel=isinstance(x,tuple); v=x[1] if rel else x
|
||||
op=(v>>26)&0x3F
|
||||
if op==0 or op==0x1C: return ((op<<12)|(v&0x3F)|(((v>>6)&0x1F)<<7),'R' if rel else '')
|
||||
if op in (2,3): return (op<<26,'')
|
||||
if op==0x12: return (v&0xFC1F07FF,'')
|
||||
if op in (1,4,5,6,7): return ((op<<20)|(((v>>16)&0x1F)<<8),'')
|
||||
imm=0 if rel else (v&0xFFFF)
|
||||
if op in MEM and ((v>>21)&0x1F)==29: imm=0
|
||||
return ((op<<20)|imm,'R' if rel else '')
|
||||
a=[shape(x) for x in MW[MLO:MHI]]; b=[shape(x) for x in TW[TLO:THI]]
|
||||
sm=difflib.SequenceMatcher(None,a,b,autojunk=False); ops=sm.get_opcodes()
|
||||
eq=sum(i2-i1 for t,i1,i2,j1,j2 in ops if t=='equal')
|
||||
print('seg tgt[%d:%d](%d) mine[%d:%d](%d) shape-eq %d = %.1f%%'%(TLO,THI,THI-TLO,MLO,MHI,MHI-MLO,eq,100.0*eq/max(1,THI-TLO)))
|
||||
n=0
|
||||
for t,i1,i2,j1,j2 in ops:
|
||||
if t=='equal': continue
|
||||
n+=1
|
||||
if n>NH: break
|
||||
print(' %-8s tgt[%d:%d](%d) mine[%d:%d](%d)'%(t,TLO+j1,TLO+j2,j2-j1,MLO+i1,MLO+i2,i2-i1))
|
||||
for k in range(j1,min(j2,j1+7)): print(' T %4d %s'%(TLO+k,TT[TLO+k]))
|
||||
for k in range(i1,min(i2,i1+7)): print(' M %4d %s'%(MLO+k,MT[MLO+k]))
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Per-anchor-segment length + opcode delta. usage: bf14_segh.py <obj.o>"""
|
||||
import re,subprocess,collections,sys
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
T=[];TW=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/\s+(\S+)',l)
|
||||
if m: TW.append(int.from_bytes(bytes.fromhex(m.group(1)),'little')); T.append('GTE' if m.group(2) in ('rtps','rtpt','nclip') else m.group(2))
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
alias={'move':'addu','li':'addiu','b':'j','negu':'subu','c2':'GTE'}
|
||||
M=[];MW=[];inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l) and MW: break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s+(\S+)',l)
|
||||
if m: MW.append(int(m.group(1),16)); M.append(alias.get(m.group(2),m.group(2)))
|
||||
ta=[i for i,v in enumerate(TW) if (v&0xFC00003F)==0x18]
|
||||
ma=[i for i,v in enumerate(MW) if (v&0xFC00003F)==0x18]
|
||||
tb=[0]+ta+[len(T)]; mb=[0]+ma+[len(M)]
|
||||
print('segments %d/%d'%(len(ta),len(ma)))
|
||||
for k in range(len(tb)-1):
|
||||
ct=collections.Counter(T[tb[k]:tb[k+1]]); cm=collections.Counter(M[mb[k]:mb[k+1]])
|
||||
d={x:cm[x]-ct[x] for x in set(ct)|set(cm) if cm[x]!=ct[x]}
|
||||
if d: print('seg %2d tgt[%d:%d] mine[%d:%d] len%+d %s'%(k,tb[k],tb[k+1],mb[k],mb[k+1],
|
||||
(mb[k+1]-mb[k])-(tb[k+1]-tb[k]),' '.join('%s%+d'%(a,b) for a,b in sorted(d.items(),key=lambda kv:-abs(kv[1])))))
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Structural aligner for func_8017BF14: masks registers AND sp-relative offsets,
|
||||
so a frame-size drift does not hide real structural divergence.
|
||||
usage: bf14_shape.py <obj.o> [nhunks] [--min N]"""
|
||||
import re, subprocess, difflib, shutil, sys
|
||||
OBJ = sys.argv[1]
|
||||
TGT = '/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
NH = int(sys.argv[2]) if len(sys.argv) > 2 and not sys.argv[2].startswith('-') else 40
|
||||
MIN = int(sys.argv[sys.argv.index('--min')+1]) if '--min' in sys.argv else 1
|
||||
OD = [c for c in ["mips-linux-gnu-objdump", "mipsel-linux-gnu-objdump"] if shutil.which(c)][0]
|
||||
out = subprocess.run([OD, "-drz", OBJ], capture_output=True, text=True).stdout
|
||||
def words_mine():
|
||||
w=[]
|
||||
for line in out.splitlines():
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s',line)
|
||||
if m: w.append(int(m.group(1),16))
|
||||
elif 'R_MIPS_' in line and w and not isinstance(w[-1],tuple) and (w[-1]>>26) not in (2,3):
|
||||
w[-1]=('R',w[-1])
|
||||
return w
|
||||
def words_tgt():
|
||||
w=[]
|
||||
for line in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/\s+(\S+)\s*(.*)',line)
|
||||
if m:
|
||||
v=int.from_bytes(bytes.fromhex(m.group(1)),'little')
|
||||
w.append(('R',v) if ('%hi(' in line or '%lo(' in line) else v)
|
||||
return w
|
||||
MEM = set(range(0x20,0x30)) | {0x32,0x3A} # lb..sw, lwc2, swc2
|
||||
def shape(x):
|
||||
rel=isinstance(x,tuple); v=x[1] if rel else x
|
||||
op=(v>>26)&0x3F
|
||||
if op==0 or op==0x1C: k=(op<<12)|(v&0x3F)|(((v>>6)&0x1F)<<7)
|
||||
elif op in (2,3): k=(op<<26)
|
||||
elif op==0x12: k=v&0xFC1F07FF
|
||||
elif op in (1,4,5,6,7): k=(op<<20)|(((v>>16)&0x1F)<<8)
|
||||
else:
|
||||
imm = 0 if rel else (v & 0xFFFF)
|
||||
base=(v>>21)&0x1F
|
||||
if op in MEM and base==29: imm=0 # mask sp offsets
|
||||
k=(op<<20)|imm
|
||||
return (k,'R' if rel else '')
|
||||
mt=[m.group(1).strip() for m in (re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(.*)',l) for l in out.splitlines()) if m]
|
||||
tt=[m.group(1).strip() for m in (re.match(r'\s*/\* \w+ [0-9A-F]{8} [0-9A-F]{8} \*/\s+(.*)',l) for l in open(TGT)) if m]
|
||||
wm,wt=words_mine(),words_tgt()
|
||||
a=[shape(x) for x in wm]; b=[shape(x) for x in wt]
|
||||
sm=difflib.SequenceMatcher(None,a,b,autojunk=False); ops=sm.get_opcodes()
|
||||
eq=sum(i2-i1 for t,i1,i2,j1,j2 in ops if t=='equal')
|
||||
bad=[o for o in ops if o[0]!='equal']
|
||||
print("mine %d target %d SHAPE-aligned %d/%d = %.1f%% hunks=%d div-tgt=%d"
|
||||
% (len(wm),len(wt),eq,len(b),100.0*eq/len(b),len(bad),sum(o[4]-o[3] for o in bad)))
|
||||
n=0
|
||||
for t,i1,i2,j1,j2 in bad:
|
||||
if max(j2-j1,i2-i1)<MIN: continue
|
||||
n+=1
|
||||
if n>NH: break
|
||||
print(" %-8s tgt[%d:%d](%d) mine[%d:%d](%d)"%(t,j1,j2,j2-j1,i1,i2,i2-i1))
|
||||
for k in range(j1,min(j2,j1+8)): print(" T %4d %s"%(k,tt[k] if k<len(tt) else '??'))
|
||||
for k in range(i1,min(i2,i1+8)): print(" M %4d %s"%(k,mt[k] if k<len(mt) else '??'))
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Stack-slot census: sp-offset -> access counts, for target and one object, side by side."""
|
||||
import re,subprocess,sys,collections
|
||||
OBJ=sys.argv[1]
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
def cens_t():
|
||||
c=collections.defaultdict(collections.Counter)
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ \w+ \w+ \*/\s+(\S+)\s+\$\w+, (-?0x[0-9A-Fa-f]+)\(\$sp\)',l)
|
||||
if m: c[int(m.group(2),16)][m.group(1)]+=1
|
||||
return c
|
||||
def cens_m():
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
c=collections.defaultdict(collections.Counter); inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True; continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l): break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(\S+)\s+\S+,(-?\d+)\(sp\)',l)
|
||||
if m: c[int(m.group(2))][m.group(1)]+=1
|
||||
return c
|
||||
T,M=cens_t(),cens_m()
|
||||
def fmt(cc): return ' '.join('%s:%d'%(k,v) for k,v in sorted(cc.items()))
|
||||
print('target slots=%d mine slots=%d'%(len(T),len(M)))
|
||||
ks=sorted(set(T)|set(M))
|
||||
for k in ks:
|
||||
print('0x%03X T[%-28s] M[%s]'%(k,fmt(T[k]),fmt(M[k])))
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# bf14_sweep.sh <base.c> "<tag>|<lever> [lever...]" ... -> one line per variant, parallel
|
||||
cd /home/musashi/bfm-decomp
|
||||
BASE="$1"; shift
|
||||
run() {
|
||||
local spec="$1"; local tag="${spec%%|*}"; local lv="${spec#*|}"
|
||||
local wd=".run/giants/bf14_sw/$tag"
|
||||
mkdir -p "$wd"
|
||||
python3 .run/giants/bf14_mk.py "$BASE" "$wd/v.c" $lv 2>"$wd/mk.err" || { echo "$tag :: MK-FAIL $(tail -2 $wd/mk.err|tr '\n' ' ')"; return; }
|
||||
bash .run/giants/bf14_score.sh "$wd/v.c" "$wd/w" "$tag"
|
||||
}
|
||||
export -f run; export BASE
|
||||
printf '%s\n' "$@" | xargs -P 8 -I{} bash -c 'run "$@"' _ {}
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Windowed side-by-side: bf14_win.py <obj.o> <tlo> <thi> <mlo>"""
|
||||
import re,subprocess,sys
|
||||
OBJ=sys.argv[1]; TLO=int(sys.argv[2]); THI=int(sys.argv[3]); MLO=int(sys.argv[4])
|
||||
TGT='/home/musashi/bfm-decomp/asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C/func_8017BF14.s'
|
||||
T=[]
|
||||
for l in open(TGT):
|
||||
m=re.match(r'\s*/\* \w+ \w+ \w+ \*/\s+(.*?)\s*$',l)
|
||||
if m: T.append(re.sub(r'\s+',' ',m.group(1)))
|
||||
out=subprocess.run(['mipsel-linux-gnu-objdump','-drz',OBJ],capture_output=True,text=True).stdout
|
||||
M=[];inside=False
|
||||
for l in out.splitlines():
|
||||
if re.match(r'^[0-9a-f]+ <func_8017BF14>:',l): inside=True;continue
|
||||
if inside:
|
||||
if re.match(r'^[0-9a-f]+ <',l): break
|
||||
m=re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(.*)',l)
|
||||
if m: M.append(re.sub(r'\s+',' ',m.group(1).strip()))
|
||||
n=THI-TLO
|
||||
for k in range(n):
|
||||
ti=TLO+k; mi=MLO+k
|
||||
print('%5d %-38s | %5d %s'%(ti,T[ti] if ti<len(T) else '',mi,M[mi] if mi<len(M) else ''))
|
||||
@@ -0,0 +1,339 @@
|
||||
# `func_8017BF14` — behemoth #4, 4,763 ins, `ov_SC03_116` — COLD-START REPORT
|
||||
|
||||
**Session 20, 2026-07-25.** Cold start: no prior draft, no `h_norm`/`h_seq` sibling
|
||||
(family size 1), 0 callees.
|
||||
|
||||
---
|
||||
|
||||
## 1. FINAL NUMBER (measured, `tools/match_one.py`, the CANDIDATE gate)
|
||||
|
||||
```
|
||||
python3 tools/match_one.py func_8017BF14 --c .run/giants/s19_func_8017BF14_b1.c \
|
||||
--asm-subdir asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C
|
||||
-> DIFF func_8017BF14 mine=4763 ins, target=4763 ins, 45 mismatched
|
||||
```
|
||||
|
||||
| metric | value |
|
||||
|---|---|
|
||||
| instruction count | **4763 / 4763 — EXACT** |
|
||||
| opcode histogram L1 distance | **0 — EXACT** (every mnemonic count agrees) |
|
||||
| stack frame | **EXACT** — frame `0x360`, all **127 slots at the target's offsets** |
|
||||
| register-masked structural alignment (anchored) | **4760 / 4763 = 99.94 %** |
|
||||
| byte-identical instructions | **4718 / 4763 = 99.06 %** |
|
||||
| **NOT A MATCH** | 45 instructions differ |
|
||||
| pin-free fallback | `s19_func_8017BF14_b1_pinfree.c` → 4763/4763, **789 mismatched**, 100.0 % structural |
|
||||
|
||||
The whole-binary SHA1 arbiter (G3/P9) was **not** run — the task forbade touching the
|
||||
build tree. `match_one` is the candidate check only.
|
||||
|
||||
**Class: three register-grant ties.** No structural, length, scheduling-of-blocks,
|
||||
frame, or expression-shape divergence remains.
|
||||
|
||||
---
|
||||
|
||||
## 2. THE DECODE
|
||||
|
||||
### 2.1 What it is
|
||||
|
||||
The **four-light-box** member of the volumetric-light renderer family:
|
||||
|
||||
| member | ins | overlay | status |
|
||||
|---|---|---|---|
|
||||
| `func_8017BEBC` | ~500 | ov_SC03_099 | MATCHED (unlit ancestor) |
|
||||
| `func_8017CA80` | 952 | ov_SC03_090 | MATCHED |
|
||||
| `func_8017F510` | 1511 | — | MATCHED (behemoth #3) |
|
||||
| `func_8017D960` | 3338 | ov_SC03_090 | MATCHED (behemoth #2, **3 light boxes**) |
|
||||
| **`func_8017BF14`** | **4763** | ov_SC03_116 | **this one, 4 light boxes** |
|
||||
|
||||
`h_norm`/`h_seq` reported family size 1 and the prompt said "no matched relative".
|
||||
That was true of the *hash* families but **false of the codegen family** — cookbook §71's
|
||||
callee-set fingerprint could not fire because this function has **zero callees**. The
|
||||
relative was found instead by **reading the target's globals**: `D_800A5E60` (the packet
|
||||
cursor) is grepped in `src/`, which lands directly on the matched `func_8017BEBC`.
|
||||
**Generalisable: for a 0-callee giant, fingerprint by its DATA symbols, not its callees.**
|
||||
|
||||
### 2.2 Signature and why it is a leaf
|
||||
|
||||
`void func_8017BF14(s32 arg0, s32 lim)` — `sw $a1, 0xB0($sp)` at entry proves arg1.
|
||||
|
||||
Every other family member opens with `lim = func_800491EC() + *(s32*)(arg0+0x64);
|
||||
func_800547D8(arg0+0x10,&mtx); func_80052E38(&mtx);`. Here the caller has already done
|
||||
that work and passes `lim` in. Consequences, all visible in the frame:
|
||||
no `$ra` save, no `MATRIX2 mtx` local, and **no 0x10-byte o32 argument area** — so
|
||||
`tmpxy[4]` starts at `sp+0x00` instead of `sp+0x10`.
|
||||
|
||||
### 2.3 Loop nest (2 loops, 414 labels, 0 `jal`)
|
||||
|
||||
```
|
||||
for (j = 0; j < nparts; j++, part++) // idx 214..4746, stride 0x14
|
||||
build box[8] from part->xx/yy/zz
|
||||
gte_ldv3c/rtpt/stsxy3 x2 + gte_ldv0/rtps/stsxy x2 -> sxy[8]
|
||||
gte_stszotz(&g.otz); if (lim >= g.otz) {
|
||||
screen bbox X in [-0xA0, 0xA1) and Y in [-0x6E, 0x6F)
|
||||
for (i = 0; i < nprim; i++, prim++) // idx 504..4742, stride 0xC
|
||||
gte_ldv3(va,vb,vc); gte_rtpt(); gte_stflg(); if (!(flag & 0x7F85E000)) {
|
||||
gte_nclip(); code = w & 7; vd = vtx + (w & 0xFFF8); gte_stopz();
|
||||
if (g.opz > 0) switch (code) { // range tree, cases 6,7 then 2,3
|
||||
case 6/7: TRI -> 3 vertices, 3 colours
|
||||
case 2/3: QUAD -> 4 vertices, 4 colours
|
||||
}
|
||||
```
|
||||
Codes 0,1,4,5 are dropped (they are the untextured F3/F4 cases of the unlit ancestor).
|
||||
gcc emits the 4-case switch as the balanced range tree
|
||||
`code<2 → skip; code<4 → quad; code>=8 → skip; code<6 → skip; else tri`.
|
||||
|
||||
### 2.4 The four light boxes
|
||||
|
||||
Stride `0x1C`, `{ s32 enable; u16 cx, cy, cz; s32 range; }` at
|
||||
`D_80197C28 / D_80197C44 / D_80197C60 / D_80197C7C`.
|
||||
|
||||
Falloff geometry differs from the 3-box sibling:
|
||||
|
||||
| | `func_8017D960` (3 boxes) | `func_8017BF14` (4 boxes) |
|
||||
|---|---|---|
|
||||
| low radius | `RLO = R - 0x80` | `RLO = R - 0x200` |
|
||||
| x-axis ramp | `A = R - d` | `A = (R - d) / 4` |
|
||||
| z/y-axis ramp | `A = (A*(R-d)) >> 7` | `A = (A*((R-d)/4)) >> 7` |
|
||||
| colour sum | `c = a0+a1+a2` | `c = a0+a1+a2+a3 + 0x10` |
|
||||
| unlit rgbc | `tp[0] & 0xFF000000` | `(tp[0] & 0xFF000000) \| 0x101010` |
|
||||
|
||||
The `/ 4` is a **signed divide** (`bgez / addiu 3 / sra 2`), not `>> 2`.
|
||||
The `+ 0x10` ambient bias is applied before the `> 0x80` clamp.
|
||||
|
||||
### 2.5 The frame (0x360)
|
||||
|
||||
```
|
||||
0x000 tmpxy[4] 0x010 box[8] 0x050 sxy[8]
|
||||
0x090 g{otz,flag,opz,sz0..sz3}
|
||||
0x0B0 lim 0x0B8 j 0x0C0 i 0x0C8 vd 0x0D0 ot 0x0D8 pkt
|
||||
0x0E0 f2 0x0E8 f3
|
||||
0x0F0/0x0F8/0x100 x3, z3, y3
|
||||
0x108 prim 0x110 nprim 0x118 vtx 0x120 nparts 0x128 part
|
||||
0x130..0x1D0 hi0y,lo0z,hi0z,lo1x,...,hi3z (21 s16 slots, 8-byte pitch)
|
||||
0x1D8..0x230 cx0,cy0,cz0 .. cx3,cy3,cz3 (12)
|
||||
0x238 r0 0x240 r1lo 0x248 r2lo 0x250 r3lo
|
||||
0x288..0x2D0 LICM-hoisted sign-extended bounds (9 s32 + 1 staging slot)
|
||||
0x328/0x330 reload-spilled vertex coords (y0, z2)
|
||||
0x338..0x358 s0-s7, fp (NO $ra — leaf)
|
||||
```
|
||||
`lo0x`, `hi0x`, `lo0y` never get s16 slots: they live only as the hoisted
|
||||
sign-extended `0x288/0x290/0x2A0` copies.
|
||||
|
||||
---
|
||||
|
||||
## 3. FOUR ORIGINAL-SOURCE COPY-PASTE ARTEFACTS (all byte-proven)
|
||||
|
||||
The 4th light box was **bolted onto a copy of the 3-box source by hand**, and the hand
|
||||
edit was incomplete in four places. Every one of these was *read off the target*, and
|
||||
every one removed a measured delta.
|
||||
|
||||
| # | artefact | proof in the target |
|
||||
|---|---|---|
|
||||
| 1 | `r3lo = r2 - 0x200;` — box 3's low radius comes from box 2's **range variable** | `addiu $t6, $s0, -0x200` at idx 93 reuses the register box 2's `lw D_80197C6C` filled. Spelling it `D_80197C6C - 0x200` re-loads the global (+2 ins, and it re-materialises `lui/lw`). No sign-extension is emitted because the result is `sh`-truncated. |
|
||||
| 2 | only **six** of the eight radius variables are zero-initialised | entry emits exactly 3 `move rX,zero` + 3 `sh $zero` = r0,r1,r2,r0lo,r1lo,r2lo. `r3`/`r3lo` are left uninitialised — precisely the init list the 3-box version needed. |
|
||||
| 3 | the box-3 ATTEN **kill test** still says `r2` in 3 of the 7 hand-written copies | all 24 `sll $v0,$s0,16` (r2 sign-extension) sites enumerated: 3 per vertex-group inside box 2, **plus exactly three extras** at idx **1471, 1504** (tri v0 z and y) and **2178** (tri v2 z). All other copies use `$s6` = r3. |
|
||||
| 4 | quad-lit `rgb2`/`rgb3` take their `<< 16` term from **c1** | the target CSEs **one** `sll $a0,$a0,16` (idx 4641) and re-uses `$a0` at idx 4647 and 4652. Writing `c2<<16`/`c3<<16` costs +2 `sll`. |
|
||||
|
||||
Modelling these is not optional cosmetics — #3 alone was `sra+11 / sll+9` and #4 was the
|
||||
last `sll+2`.
|
||||
|
||||
---
|
||||
|
||||
## 4. THE LEVERS THAT MOVED THE NUMBER (measured)
|
||||
|
||||
Metric below is **byte-identical %** from the anchored aligner
|
||||
(`.run/giants/bf14_ali.py`), which is length-drift-proof.
|
||||
|
||||
| # | lever | before → after |
|
||||
|---|---|---|
|
||||
| L1 | unlit `cb = (tp[0] & 0xFF000000) \| 0x101010` | len −64 → −62; retired the whole `or`/`ori`/`lui` histogram delta |
|
||||
| L2 | artefacts 1 + 3 (box-3 kill register per copy, `r3lo = r2 - 0x200`) | shape 89.15 % → 96.96 %; killed `sra+11 / sll+9` |
|
||||
| L3 | artefact 4 (quad-lit `c1 << 16`) | killed the last `sll+2` |
|
||||
| **L4** | **`s32 c0,c1,c2,c3;` declared inside the two CULL blocks** | **52.26 % → 92.86 % — the single biggest lever, and it is what spills `r1lo`** |
|
||||
| **L5** | **`s32 f0,f1,f2,f3;` moved to immediately after `u8 *pkt;`** | **73.00 % → 83.98 %, and ALL 127 stack slots then match exactly** |
|
||||
| L6 | `u32 rgbw;` per emit arm | 93.45 % → 94.21 % (with L7) |
|
||||
| L7 | RC-15 zero-byte ref dial on `mny`, head of the TRI cull block | 93.47 % → 94.21 % |
|
||||
| L8 | `cb` 2-statement accumulator + quad-lit rgb 3-statement accumulator | 99.06 % → 99.12 % |
|
||||
| L9 | four register pins: `va→$t2`, `w→$a1`, `f0→$s3`, `c1→$a0` | 94.21 % → **99.06 %** |
|
||||
|
||||
### 4.1 L4 — why declaration scope, and not a dial (cookbook §76)
|
||||
|
||||
The whole −62 length residual was **one** decision: the target spills `r1lo` to `0x240`
|
||||
and reloads it 21 times (`lhu` + load-delay `nop` at each of 3 axes × 7 vertex-colour
|
||||
groups = exactly the measured `lhu −21` / `nop −21` / `sh −2`), while every draft kept
|
||||
it in `$fp`.
|
||||
|
||||
`$fp` is register 30 — the **last** register a plain-ascending `find_reg` scan reaches
|
||||
(MIPS defines no `REG_ALLOC_ORDER` in gcc-2.7.2, verified in
|
||||
`tools/reference/gcc-2.7.2/config/mips/mips.h`). So `r1lo` was the *marginal* allocno:
|
||||
the target simply had **one more competitor** than my draft.
|
||||
|
||||
Declaring `c0..c3` inside the two cull blocks supplies that competitor the way the
|
||||
compiler actually models it: at function scope `c0..c3` have a death in each of the four
|
||||
emit arms, so `local-alloc.c:472` (`REG_BASIC_BLOCK >= 0 && REG_N_DEATHS == 1`) refuses
|
||||
them and they become global allocnos; per cull block they are 1-death **local** pseudos,
|
||||
local-alloc places them, and `global.c:668-671` re-marks those placements as **hard
|
||||
registers** for global-alloc's conflict scan — removing them from the global pool and
|
||||
pushing `r1lo` onto the stack.
|
||||
|
||||
An `__asm__` ref-dial on `mn`/`mx`/`my`/`mny` inside the lit arm reached the same spill
|
||||
(84.78 %) and was found *first*; declaration scope is both better (92.86 %) and real C.
|
||||
**Lesson: when a spill is missing, look for the missing LOCAL allocno before reaching for
|
||||
a ref dial.**
|
||||
|
||||
### 4.2 L5 — the stack-slot order is a declaration-order ORACLE (new, generalisable)
|
||||
|
||||
Spilled pseudos are given stack slots in **pseudo-number order** (`reload1.c: alter_reg`),
|
||||
and pseudo numbers are handed out in **declaration order** (`expand_decl`). Therefore the
|
||||
target's stack layout is a direct read-out of the original declaration order.
|
||||
|
||||
Target: `pkt` at `0xD8`, then **two `lw:9 sw:9` slots at `0xE0`/`0xE8`** (the f2/f3
|
||||
flags), then three `lhu:7 sh:1` slots (`x3,z3,y3`), then `prim/nprim/vtx/nparts/part`.
|
||||
My draft had the flags declared late, so everything above `0xD8` was shifted by `0x10`.
|
||||
Moving one declaration line fixed **every** slot: 127/127.
|
||||
|
||||
This is a cheap, deterministic, repeatable technique — `.run/giants/bf14_slots.py`
|
||||
prints the slot census side by side and any mismatch is a declaration-order bug.
|
||||
**It should go in the cookbook.**
|
||||
|
||||
### 4.3 L9 — pins are safe *here*, and §72 still holds
|
||||
|
||||
Cookbook §72/§74: a `register __asm__` pin is a **preference, not a reservation**, and
|
||||
the real hazard is a caller-saved pin spanning a `jal`. **This function has zero `jal`s**,
|
||||
so that hazard cannot arise — which is exactly why pins are usable on this family member
|
||||
and were a trap on the others. Four pins bought 94.21 % → 99.06 %.
|
||||
|
||||
§72 was nevertheless reproduced: adding a 5th and 6th pin (`c0→$t4`, `c2→$t2`, both
|
||||
values the target genuinely puts there) made it **worse — 92.86 %**. Pins do not compose.
|
||||
|
||||
A **pin-free** draft is preserved at `.run/giants/s19_func_8017BF14_b1_pinfree.c`
|
||||
(4763/4763 ins, 789 mismatched, 100.0 % structural, exact frame) for anyone who wants the
|
||||
conservative variant.
|
||||
|
||||
---
|
||||
|
||||
## 5. DO-NOT-RE-BUY TABLE (every lever measured, neutral or negative)
|
||||
|
||||
Baselines are stated per block because the base moved as levers landed.
|
||||
|
||||
### 5.1 Neutral — no effect at all
|
||||
|
||||
| lever | result |
|
||||
|---|---|
|
||||
| declaration-order permutations of `r0..r3 / r0lo..r3lo` (5 orders swept) | neutral (±4 on a 4526 base) |
|
||||
| swapping `rN` / `rNlo` assignment order inside each box's `if` (all 4 boxes) | neutral |
|
||||
| `a0v..a3v` / `d` / `tp` declared per cull block | neutral |
|
||||
| `a0v..a3v` / `c0..c3` / `d` declared inside the lit arm | neutral |
|
||||
| moving the whole r-declaration block (top / bottom / before lo-hi / before centres) | neutral |
|
||||
| declaration position of `s16 my,mny,mx,mn` (6 anchors swept) | **neutral — cannot substitute for the L7 dial** |
|
||||
| splitting `f0,f1` before `pkt` and `f2,f3` after | neutral |
|
||||
| `x2,y2,z2` split out / declared late | neutral |
|
||||
| arm-declaration ORDER (`tp`/`rgbw`/`cb` before vs after `u32 *otp;`) | neutral (4 permutations) |
|
||||
| `vd` computed later; `va`/`vb` assignment swapped | neutral |
|
||||
| second ref dial on any of f0..f3, x0..z2, mn/mx/my/mny, w, wz, vw, vzw, i, j, nprim, at prim/cull scope, on top of the best base | neutral or worse (40 probes) |
|
||||
|
||||
### 5.2 Negative — actively harmful
|
||||
|
||||
| lever | result vs its base |
|
||||
|---|---|
|
||||
| `s16 x0..z2` declared per cull block (`xyz_cull`) | 92.86 % → **57.23 %** |
|
||||
| `s32 f0..f3` declared per cull block (`f_cull`) | 92.86 % → **67.16 %** |
|
||||
| ref dial on `mn` in the lit arm, once `c_cull` is in | 92.86 % → **63.20 %** (the two levers do the same job and collide) |
|
||||
| `cb` declared per emit arm | 94.21 % → **91.31 %** |
|
||||
| `a0v..a3v` per cull block, on top of `c_cull` | 92.86 % → **90.32 %** |
|
||||
| pinning `c0` and `c2` in addition to `c1` | 99.06 % → **92.86 %** |
|
||||
| `rgbw` as a 3-statement accumulator in **both** arms | 99.06 % → 98.22 % (quad-only is +0.02 %) |
|
||||
| `base = D_800AF630` rematerialised at use / hoisted late | 52.26 % → 52.11 % |
|
||||
| `prim`/`nprim` read order swapped | neutral-to-worse |
|
||||
| `f0=0..f3=0` in forward instead of reverse order | 92.86 % → 92.78 % |
|
||||
|
||||
### 5.3 Rejected diagnoses (each was measured and refuted)
|
||||
|
||||
* **"the −62 is missing code."** It was not: the opcode-histogram delta was
|
||||
`nop −34 / lhu −21 / sh −2 / addu +1` — a pure spill signature. Confirmed by
|
||||
§78's rule: *a `nop` present in the target and absent from the draft is a register
|
||||
fact, not missing code.*
|
||||
* **"`r3lo` comes from `D_80197C88 - 0x200`."** Measured: that spelling re-loads the
|
||||
global (`lui/lhu` + `addiu`) instead of reusing `$s0`. It scored *better* on the raw
|
||||
mismatch counter purely because it shuffled the allocation — a metric trap. The
|
||||
anchored aligner and the slot census disagreed, and they were right.
|
||||
* **"the box-3 ATTEN is uniform across the 7 copies."** Refuted by enumerating all 24
|
||||
`sll $v0,$s0,16` sites: three copies differ.
|
||||
* **"declaration order can replace the `mny` ref dial."** Six anchor positions swept, all
|
||||
neutral. Declaration order moves the *tiebreak*; the dial moves `reg_n_refs`
|
||||
(× loop depth) and therefore the `allocno_compare` **score**. They are not
|
||||
interchangeable.
|
||||
|
||||
---
|
||||
|
||||
## 6. THE REMAINING 45 INSTRUCTIONS
|
||||
|
||||
Three independent register-grant ties. No structural residual.
|
||||
|
||||
**(a) The prim-word producer temps (≈8 ins, idx 508–514, 539–542).**
|
||||
Target: `andi $v1,$a1,0xFFFF` / `srl $a0,$a1,16` / `addu $t2,$t6,$v1`.
|
||||
Draft: the `andi` result is written straight into `$t2` (`va`'s register) and the `addu`
|
||||
is in-place. This is `combine_regs` (`local-alloc.c:1825`) tying the producer chain into
|
||||
`va` — which the **`va→$t2` pin invites**, because a pinned pseudo is a hard register from
|
||||
the start and local-alloc will always tie into it. Removing the pin unties the chain but
|
||||
costs 4 % elsewhere. *Most promising next move: keep the tie broken by giving the two
|
||||
offsets their own named, multi-death variables (raising their death count above 1 so
|
||||
`local-alloc.c:472` refuses them), rather than by removing the pin.*
|
||||
|
||||
**(b) `c0` and `c2` grants (≈15 ins).** Target `c0→$t4, c1→$a0, c2→$t2, c3→$a2`; draft
|
||||
gets `c1` (pinned) right and `c0→$t2, c2→$a2`. Pinning them directly is refuted (§5.2).
|
||||
The lever is `allocno_compare` order among the four, i.e. their relative ref counts —
|
||||
reachable by a variable-REUSE merge (§45-A / RC-14), which was **not** swept for `c0..c3`
|
||||
and is the obvious next experiment.
|
||||
|
||||
**(c) The quad-lit rgb accumulator (≈20 ins, idx 4635–4653).** Target accumulates in
|
||||
`$v1` and stores from `$v1`; the draft accumulates in `$v0`. Three one-slot
|
||||
store/shift transpositions ride along with it (`sll $v0,$a0,8` before vs after
|
||||
`sw $v1,-0x20($t3)`). **Before calling these a scheduling residual, run §76's attribution
|
||||
primitive** (`-fno-schedule-insns` and `-fno-schedule-insns2`): if the pair keeps source
|
||||
order under both, it was fixed at RTL expansion and the lever is statement order, not
|
||||
`sched.c`. That test was *not* run here and is the cheapest remaining probe.
|
||||
|
||||
### Single most promising next move
|
||||
**Sweep variable REUSE across `c0..c3` and the `a0v..a3v` accumulators** (merge two temps
|
||||
into one, §45-A / RC-14 MERGE) to move `allocno_compare` priority. That is the one §76
|
||||
lever class this session never reached — declaration scope was swept exhaustively,
|
||||
reuse was not. It targets residual (b) directly and plausibly (c) as well.
|
||||
|
||||
---
|
||||
|
||||
## 7. TOOLS BUILT (all preserved in `.run/giants/`, ~0.3–0.8 s per probe)
|
||||
|
||||
| tool | what it does |
|
||||
|---|---|
|
||||
| `bf14_cc.sh` / `bf14_score.sh` / `bf14_probe.sh` | compile + score one draft (0.28 s) |
|
||||
| `bf14_mk.py` | lever generator; **every transformation asserts it applied**, so a "neutral" reading can never be a silent no-op |
|
||||
| `bf14_sweep.sh` | 8-way parallel lever sweep |
|
||||
| `bf14_full.py` / `bf14_side.py` / `bf14_win.py` | masked diff, side-by-side, windowed side-by-side |
|
||||
| `bf14_hist.py` | opcode-histogram delta — **alignment-free, length-drift-proof** |
|
||||
| `bf14_shape.py` | difflib shape diff with sp offsets masked |
|
||||
| **`bf14_ali.py`** | **anchor-segmented aligner** — segments the function at the 56 `mult` sites and aligns each segment independently. Plain difflib gets hopelessly lost in this function's repeated blocks (it once reported a bogus 1,836-instruction insertion); anchoring fixes it. Reports both register-masked and byte-level alignment. |
|
||||
| `bf14_segh.py` | per-segment length + opcode delta — localises a drift to one ATTEN axis |
|
||||
| `bf14_land.py` | landmark drift table (`mult` index deltas) |
|
||||
| **`bf14_slots.py`** | **stack-slot census vs the target — the declaration-order oracle of §4.2** |
|
||||
| `bf14_regmap.py` | register-correspondence census; exposes allocation *cycles* rather than a list of diffs |
|
||||
| `bf14_struct.py` | label / branch / loop map of the target |
|
||||
|
||||
### Reusable methodology notes
|
||||
1. **Anchor your aligner.** On a function with 7 near-identical 400-instruction blocks,
|
||||
raw `difflib` is worse than useless — it reports confident nonsense. Segment on a rare
|
||||
opcode first.
|
||||
2. **The opcode histogram is the honest early metric.** Index-wise "mismatched" is
|
||||
dominated by length drift and will rank a *worse* draft higher (this happened, §5.3).
|
||||
3. **The stack-slot census is a free oracle for declaration order** (§4.2).
|
||||
4. **The register-correspondence census turns a wall of diffs into cycles.** Seeing
|
||||
`$t2→$t3→$t5→$t4→$t2` as a rotation, and `$s3↔$s4` as a swap, is what made the last
|
||||
5 % tractable.
|
||||
|
||||
---
|
||||
|
||||
## 8. FILES
|
||||
|
||||
* `.run/giants/s19_func_8017BF14_b1.c` — best draft, full dossier header. **45/4763.**
|
||||
* `.run/giants/s19_func_8017BF14_b1_pinfree.c` — pin-free variant. 789/4763, 100 % structural.
|
||||
* `.run/giants/bf14_*.py`, `.run/giants/bf14_*.sh` — the harness.
|
||||
@@ -0,0 +1,770 @@
|
||||
#include "common.h"
|
||||
#include "/home/musashi/bfm-decomp/src/shared/engine_types.h"
|
||||
|
||||
/* ===========================================================================
|
||||
* func_8017BF14 -- 4,763 ins, ov_SC03_116 (behemoth #4). COLD START.
|
||||
*
|
||||
* STATUS (2026-07-25, session 20, gcc-2.7.2 pinned triple):
|
||||
* python3 tools/match_one.py func_8017BF14 --c <this file> \
|
||||
* --asm-subdir asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C
|
||||
* -> DIFF 4763/4763 ins, 45 mismatched (99.06% byte-identical)
|
||||
* LENGTH EXACT | OPCODE HISTOGRAM EXACT (L1 = 0) | STACK FRAME EXACT
|
||||
* (all 127 slots at the target's offsets, frame 0x360)
|
||||
* register-masked structural alignment 4760/4763 = 99.94%
|
||||
* NOT A MATCH. The whole-binary SHA1 arbiter (G3/P9) has NOT been run.
|
||||
* Pin-free fallback (see LEVERS L6..L9): 4763/4763, 789 mismatched.
|
||||
*
|
||||
* WHAT IT IS
|
||||
* The *four*-light-box variant of the volumetric-light renderer whose
|
||||
* 3-box sibling func_8017D960 (3,338 ins, ov_SC03_090) is MATCHED, and whose
|
||||
* unlit ancestor func_8017BEBC (ov_SC03_099) is MATCHED. Same family, same
|
||||
* skeleton; this is the biggest member.
|
||||
*
|
||||
* Signature: func_8017BF14(s32 arg0, s32 lim). Unlike every other member of
|
||||
* the family this one is a LEAF -- 0 callees. The 3-call prologue
|
||||
* (func_800491EC / func_800547D8 / func_80052E38) of the siblings is gone;
|
||||
* `lim` arrives as arg1 (spilled to 0xB0). That is why the frame has no
|
||||
* 0x10 argument area (tmpxy[] starts at sp+0x00) and no $ra save.
|
||||
*
|
||||
* Per part (stride 0x14, outer loop): build the 8-corner AABB in box[],
|
||||
* rtpt/rtpt + rtps/rtps -> sxy[8], stszotz -> g.otz, reject on
|
||||
* `lim >= g.otz`, then screen-space bbox reject on X (-0xA0..0xA1) and
|
||||
* Y (-0x6E..0x6F).
|
||||
* Per prim (stride 0xC, inner loop): rtpt the 3 vertices, stflg mask
|
||||
* 0x7F85E000, nclip, stopz > 0, then a 4-way range tree on `code = w & 7`
|
||||
* that keeps ONLY codes 6,7 (tri) and 2,3 (quad); 0,1,4,5 fall through to
|
||||
* the loop tail.
|
||||
* Per drawn poly: screen bbox reject, then each vertex is tested against
|
||||
* FOUR axis-aligned light boxes (flags f0..f3), and if any is lit a
|
||||
* 0x00..0x80 attenuation per active box is computed, summed, biased +0x10
|
||||
* and clamped to 0x80 -> a grey gouraud vertex colour.
|
||||
* lit -> POLY_GT3 (0x28, tag 0x34000000, OT 0x9000000)
|
||||
* POLY_GT4 (0x34, tag 0x3C000000, OT 0xC000000)
|
||||
* unlit -> POLY_FT3 (0x20, OT 0x7000000) / POLY_FT4 (0x28, OT 0x9000000)
|
||||
* with rgbc = (tp[0] & 0xFF000000) | 0x101010 <-- NOT black,
|
||||
* unlike func_8017D960 where the unlit colour is plain black.
|
||||
*
|
||||
* THE FOUR LIGHT BOXES (stride 0x1C, {s32 enable; u16 cx,cy,cz; s32 range})
|
||||
* D_80197C28 / D_80197C44 / D_80197C60 / D_80197C7C.
|
||||
* Falloff geometry differs from the 3-box sibling: RLO = R - 0x200 (not
|
||||
* -0x80) and the ramp is ((R - d) / 4) (not (R - d)), so the 0x80 ceiling is
|
||||
* reached over a 0x200-wide band instead of 0x80. The `/ 4` is a SIGNED
|
||||
* divide -- `bgez / addiu 3 / sra 2` -- not a shift.
|
||||
*
|
||||
* THREE ORIGINAL-SOURCE COPY-PASTE ARTEFACTS, all byte-proven (see report)
|
||||
* (1) `r3lo = r2 - 0x200;` -- box 3's low radius is derived from box 2's
|
||||
* RANGE VARIABLE, not from its own D_80197C88. Proven by the target's
|
||||
* `addiu $t6, $s0, -0x200` reusing the register that box 2's `lw` filled;
|
||||
* spelling it `D_80197C6C - 0x200` re-loads the global (+2 ins).
|
||||
* (2) Only SIX of the eight radius variables are zero-initialised
|
||||
* (r0,r1,r2,r0lo,r1lo,r2lo) -- r3/r3lo are left uninitialised, exactly
|
||||
* the init list the 3-box version needed.
|
||||
* (3) The ATTEN body is written out LONGHAND 7 times (3 tri vertices +
|
||||
* 4 quad vertices). When box 3 was bolted on, the `R` of the z- and
|
||||
* y-axis KILL tests was left as r2 in three of those copies:
|
||||
* tri v0: z and y use r2 tri v2: z uses r2 all others use r3.
|
||||
* Proven by the 24 `sll $v0,$s0,16` sites: 3 per group in box 2 plus
|
||||
* exactly three extra at idx 1471, 1504 (tri v0) and 2178 (tri v2).
|
||||
* (4) In the QUAD lit arm only, rgb2 and rgb3 take their `<< 16` term from
|
||||
* c1, not from c2/c3. Proven by the target CSE-ing ONE
|
||||
* `sll $a0, $a0, 16` and re-using $a0 for all three stores.
|
||||
* These are not guesses: each was read off the target and each removed a
|
||||
* measured instruction-count or opcode-histogram delta.
|
||||
*
|
||||
* FRAME (0x360, leaf -- no $ra, no argument area)
|
||||
* 0x000 tmpxy[4] | 0x010 box[8] | 0x050 sxy[8] |
|
||||
* 0x090 g{otz,flag,opz,sz0..sz3} | 0x0B0 lim | 0x0B8 j | 0x0C0 i |
|
||||
* 0x0C8 vd | 0x0D0 ot | 0x0D8 pkt | 0x0E0 f2 | 0x0E8 f3 |
|
||||
* 0x0F0/0x0F8/0x100 x3,z3,y3 | 0x108 prim | 0x110 nprim | 0x118 vtx |
|
||||
* 0x120 nparts | 0x128 part | 0x130..0x1D0 lo/hi bounds (21 s16 slots) |
|
||||
* 0x1D8..0x230 cx0..cz3 (12) | 0x238 r0 | 0x240 r1lo | 0x248 r2lo |
|
||||
* 0x250 r3lo | 0x288..0x2D0 the LICM-hoisted sign-extended bounds |
|
||||
* 0x328/0x330 spilled vertex coords | 0x338..0x358 s0-s7,fp.
|
||||
* *** THE SLOT ORDER IS THE DECLARATION-ORDER ORACLE (see L5). ***
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
* THE LEVERS, each with its MEASURED effect (byte-identical %, anchored)
|
||||
*
|
||||
* L1 `cb = (tp[0] & 0xFF000000) | 0x101010;` in both UNLIT arms.
|
||||
* Was plain black (copied from the 3-box sibling). -64 -> -62 length,
|
||||
* and it retired the whole or/ori/lui histogram delta.
|
||||
* L2 box-3 ATTEN kill-register per copy (artefact 3 above) + `r3lo = r2 -
|
||||
* 0x200` (artefact 1). 89.15% -> 96.96% shape; killed sra+11 / sll+9.
|
||||
* L3 quad-lit rgb2/rgb3 use `c1 << 16` (artefact 4). Killed the last sll+2.
|
||||
* L4 **`s32 c0, c1, c2, c3;` DECLARED INSIDE THE TWO CULL BLOCKS**, not at
|
||||
* function scope. THE SINGLE BIGGEST LEVER: 52.26% -> 92.86% byte, and it
|
||||
* is what finally spills r1lo (21 reloads + 21 nops, the entire -62
|
||||
* residual length). Cookbook Sec.76 exactly: at function scope c0..c3 have
|
||||
* a death in each of the four emit arms, so local-alloc.c:472
|
||||
* (REG_BASIC_BLOCK >= 0 && REG_N_DEATHS == 1) refuses them; per-cull-block
|
||||
* they are 1-death local pseudos, local-alloc places them, and via
|
||||
* global.c:668-671 those placements remove registers from the GLOBAL pool
|
||||
* -- which is what pushes r1lo out of $fp and onto the stack.
|
||||
* An `__asm__` ref-dial on mn/mx/my/mny reached the same spill (84.78%)
|
||||
* but is strictly worse AND artificial. Declaration scope wins.
|
||||
* L5 **`s32 f0, f1, f2, f3;` MOVED TO IMMEDIATELY AFTER `u8 *pkt;`.**
|
||||
* 73.00% -> 83.98% byte. Reason (and the reusable trick): spilled pseudos
|
||||
* get stack slots in PSEUDO-NUMBER order, and pseudo numbers are assigned
|
||||
* in DECLARATION order -- so the target's stack-slot layout is a direct
|
||||
* read-out of its declaration order. The target has f2 at 0xE0 and f3 at
|
||||
* 0xE8, i.e. between `pkt` (0xD8) and `x3,z3,y3` (0xF0..0x100). After this
|
||||
* one move ALL 127 stack slots agree with the target exactly.
|
||||
* L6 `u32 rgbw;` per emit arm (+0.6%).
|
||||
* L7 RC-15 zero-byte ref dial on `mny`, first statement of the TRI cull block
|
||||
* -- identical to func_8017D960's L4, same cause: `mny` is defined first
|
||||
* but outlives `my`, so allocno_compare (global.c:594) ranks `my` above it
|
||||
* and `my` takes the lower $a2. The dial flips it to the target's
|
||||
* mny->$a2 / my->$a3. 93.47% -> 94.21%.
|
||||
* L8 `cb` as a 2-statement accumulator; quad-lit rgb word as a 3-statement
|
||||
* accumulator (+0.1% together).
|
||||
* L9 FOUR REGISTER PINS: va->$t2, w->$a1, f0->$s3, c1->$a0.
|
||||
* 94.21% -> 99.06%. Sec.72/Sec.74 note: this function has NO `jal`, so the
|
||||
* caller-saved-pin-spanning-a-call hazard cannot arise here -- that is why
|
||||
* pins are usable on this member of the family and were a trap on the
|
||||
* others. Pins remain PREFERENCES: adding a 5th and 6th (c0->$t4,
|
||||
* c2->$t2) made it WORSE (92.86%), which is Sec.72 reproduced.
|
||||
* The PIN-FREE draft is kept at .run/giants/s19_func_8017BF14_b1_pinfree.c
|
||||
* (4763/4763, 789 mismatched, 100.0% structural) for anyone who wants the
|
||||
* safe variant.
|
||||
*
|
||||
* REMAINING RESIDUAL (45 instructions, 0.9%): three register-grant ties --
|
||||
* the `w & 0xFFFF` / `w >> 16` producer temps that local-alloc combine_regs
|
||||
* ties into the pinned `va`; the c0/c2 grants ($t4/$t2 vs $t2/$a2); and the
|
||||
* lit rgb accumulator $v1 vs $v0 in the quad arm with its 3 one-slot
|
||||
* store/shift transpositions. See .run/giants/s19_bf14_report.md.
|
||||
* =========================================================================== */
|
||||
|
||||
#define gte_ldv0(r0) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
#define gte_ldv3(r0, r1, r2) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 0( %1 );" \
|
||||
"lwc2 $3, 4( %1 );" \
|
||||
"lwc2 $4, 0( %2 );" \
|
||||
"lwc2 $5, 4( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) )
|
||||
|
||||
#define gte_ldv3c(r0) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 8( %0 );" \
|
||||
"lwc2 $3, 12( %0 );" \
|
||||
"lwc2 $4, 16( %0 );" \
|
||||
"lwc2 $5, 20( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
#define gte_rtps() __asm__ volatile ("nop;nop;rtps")
|
||||
#define gte_rtpt() __asm__ volatile ("nop;nop;rtpt")
|
||||
#define gte_nclip() __asm__ volatile ("nop;nop;nclip")
|
||||
|
||||
#define gte_stsxy(r0) __asm__ volatile ( \
|
||||
"swc2 $14, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsxy3(r0, r1, r2) __asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 0( %1 );" \
|
||||
"swc2 $14, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsxy3c(r0) __asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 4( %0 );" \
|
||||
"swc2 $14, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsz3(r0, r1, r2) __asm__ volatile ( \
|
||||
"swc2 $17, 0( %0 );" \
|
||||
"swc2 $18, 0( %1 );" \
|
||||
"swc2 $19, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsz4(r0, r1, r2, r3) __asm__ volatile ( \
|
||||
"swc2 $16, 0( %0 );" \
|
||||
"swc2 $17, 0( %1 );" \
|
||||
"swc2 $18, 0( %2 );" \
|
||||
"swc2 $19, 0( %3 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stszotz(r0) __asm__ volatile ( \
|
||||
"mfc2 $12, $19;" \
|
||||
"nop;" \
|
||||
"sra $12, $12, 2;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
#define gte_stflg(r0) __asm__ volatile ( \
|
||||
"cfc2 $12, $31;" \
|
||||
"nop;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
#define gte_stopz(r0) __asm__ volatile ( \
|
||||
"swc2 $24, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* ---- the two gouraud-textured packet layouts this function emits ---------- */
|
||||
typedef struct {
|
||||
u32 tag;
|
||||
u32 rgb0; s16 x0, y0; u32 uv0;
|
||||
u32 rgb1; s16 x1, y1; u32 uv1;
|
||||
u32 rgb2; s16 x2, y2; u16 uv2, p2;
|
||||
} PolyGT3; /* 0x28 */
|
||||
|
||||
typedef struct {
|
||||
u32 tag;
|
||||
u32 rgb0; s16 x0, y0; u32 uv0;
|
||||
u32 rgb1; s16 x1, y1; u32 uv1;
|
||||
u32 rgb2; s16 x2, y2; u16 uv2, p2;
|
||||
u32 rgb3; s16 x3, y3; u16 uv3, p3;
|
||||
} PolyGT4; /* 0x34 */
|
||||
|
||||
|
||||
/* ---- the four light-volume descriptors (stride 0x1C) --------------------- */
|
||||
extern s32 D_80197C28;
|
||||
extern u16 D_80197C2C, D_80197C2E, D_80197C30;
|
||||
extern s32 D_80197C34;
|
||||
extern s32 D_80197C44;
|
||||
extern u16 D_80197C48, D_80197C4A, D_80197C4C;
|
||||
extern s32 D_80197C50;
|
||||
extern s32 D_80197C60;
|
||||
extern u16 D_80197C64, D_80197C66, D_80197C68;
|
||||
extern s32 D_80197C6C;
|
||||
extern s32 D_80197C7C;
|
||||
extern u16 D_80197C80, D_80197C82, D_80197C84;
|
||||
extern u16 D_80197C88;
|
||||
|
||||
/* ---- the box-containment test for one vertex against one light box ------- */
|
||||
#define BOXTEST(F, X, Y, Z, LX, HX, LY, HY, LZ, HZ) \
|
||||
if ((LX) < (X) && (X) < (HX) && (LY) < (Y) && (Y) < (HY) && (LZ) < (Z) && (Z) < (HZ)) F = 1
|
||||
|
||||
/* ---- the separable per-axis falloff, visited in x, z, y order ------------ */
|
||||
#define ATTEN(A, F, X, Y, Z, CX, CY, CZ, R, RLO) \
|
||||
A = 0; \
|
||||
if (F) { \
|
||||
d = (X) - (CX); if (d < 0) d = (CX) - (X); \
|
||||
if (d < (R)) { A = 0x80; if (d >= (RLO)) A = ((R) - d) / 4; } \
|
||||
d = (Z) - (CZ); if (d < 0) d = (CZ) - (Z); \
|
||||
if ((R) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
d = (Y) - (CY); if (d < 0) d = (CY) - (Y); \
|
||||
if ((R) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
}
|
||||
|
||||
#define ATTEN3(A, F, X, Y, Z, CX, CY, CZ, R, RLO, RZ, RY) \
|
||||
A = 0; \
|
||||
if (F) { \
|
||||
d = (X) - (CX); if (d < 0) d = (CX) - (X); \
|
||||
if (d < (R)) { A = 0x80; if (d >= (RLO)) A = ((R) - d) / 4; } \
|
||||
d = (Z) - (CZ); if (d < 0) d = (CZ) - (Z); \
|
||||
if ((RZ) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
d = (Y) - (CY); if (d < 0) d = (CY) - (Y); \
|
||||
if ((RY) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
}
|
||||
|
||||
#define CLAMP80(C, A0, A1, A2, A3) C = (A0) + (A1) + (A2) + (A3) + 0x10; if ((C) > 0x80) C = 0x80
|
||||
|
||||
|
||||
void func_8017BF14(s32 arg0, s32 lim)
|
||||
{
|
||||
typedef struct { u32 w0, w1, w2; } Prim;
|
||||
|
||||
extern u8 *D_800A5E60;
|
||||
extern u8 D_800A6610[];
|
||||
extern u8 D_800AF630[];
|
||||
|
||||
DVECTOR2 tmpxy[4];
|
||||
SVECTOR2 box[8];
|
||||
SVECTOR2 sxy[8];
|
||||
struct { long otz, flag, opz, sz0, sz1, sz2, sz3; } g;
|
||||
|
||||
s32 j;
|
||||
u32 i;
|
||||
u8 *vd;
|
||||
u32 ot;
|
||||
u8 *pkt;
|
||||
register s32 f0 __asm__("$19");
|
||||
s32 f1, f2, f3;
|
||||
s16 x3, z3, y3;
|
||||
Prim *prim;
|
||||
u32 nprim;
|
||||
u8 *vtx;
|
||||
s32 nparts;
|
||||
Part *part;
|
||||
s16 lo0x, hi0x, lo0y, hi0y, lo0z, hi0z;
|
||||
s16 lo1x, hi1x, lo1y, hi1y, lo1z, hi1z;
|
||||
s16 lo2x, hi2x, lo2y, hi2y, lo2z, hi2z;
|
||||
s16 lo3x, hi3x, lo3y, hi3y, lo3z, hi3z;
|
||||
s16 cx0, cy0, cz0, cx1, cy1, cz1, cx2, cy2, cz2, cx3, cy3, cz3;
|
||||
s16 r0;
|
||||
s16 r1;
|
||||
s16 r2;
|
||||
s16 r3;
|
||||
s16 r0lo;
|
||||
s16 r1lo;
|
||||
s16 r2lo;
|
||||
s16 r3lo;
|
||||
register u8 *va __asm__("$10");
|
||||
u8 *vb, *vc;
|
||||
register u32 w __asm__("$5");
|
||||
s32 code;
|
||||
u32 vw, vzw;
|
||||
u32 wx, wy, wz;
|
||||
s32 xa32, xb32, t32;
|
||||
s32 xmn1, xmx1, xmn2, xmx2;
|
||||
s32 mnc, mxc;
|
||||
s16 my, mny, mx, mn;
|
||||
u8 *base;
|
||||
s16 x0, y0, z0, x1, y1, z1, x2, y2, z2;
|
||||
s32 a0v, a1v, a2v, a3v;
|
||||
s32 d;
|
||||
u32 *tp;
|
||||
u32 uvw;
|
||||
u32 cb;
|
||||
|
||||
base = D_800AF630;
|
||||
|
||||
r2lo = 0;
|
||||
r1lo = 0;
|
||||
r0lo = 0;
|
||||
r2 = 0;
|
||||
r1 = 0;
|
||||
r0 = 0;
|
||||
|
||||
if (D_80197C28) {
|
||||
cx0 = D_80197C2C;
|
||||
cy0 = D_80197C2E;
|
||||
r0lo = D_80197C34 - 0x200;
|
||||
r0 = D_80197C34;
|
||||
cz0 = D_80197C30;
|
||||
} else {
|
||||
cz0 = 0x6000;
|
||||
cy0 = 0x6000;
|
||||
cx0 = 0x6000;
|
||||
}
|
||||
if (D_80197C44) {
|
||||
cx1 = D_80197C48;
|
||||
cy1 = D_80197C4A;
|
||||
r1 = D_80197C50;
|
||||
r1lo = D_80197C50 - 0x200;
|
||||
cz1 = D_80197C4C;
|
||||
} else {
|
||||
cz1 = 0x6000;
|
||||
cy1 = 0x6000;
|
||||
cx1 = 0x6000;
|
||||
}
|
||||
if (D_80197C60) {
|
||||
cx2 = D_80197C64;
|
||||
cy2 = D_80197C66;
|
||||
r2 = D_80197C6C;
|
||||
r2lo = D_80197C6C - 0x200;
|
||||
cz2 = D_80197C68;
|
||||
} else {
|
||||
cz2 = 0x6000;
|
||||
cy2 = 0x6000;
|
||||
cx2 = 0x6000;
|
||||
}
|
||||
if (D_80197C7C) {
|
||||
cx3 = D_80197C80;
|
||||
cy3 = D_80197C82;
|
||||
r3lo = r2 - 0x200;
|
||||
r3 = D_80197C88;
|
||||
cz3 = D_80197C84;
|
||||
} else {
|
||||
cz3 = 0x6000;
|
||||
cy3 = 0x6000;
|
||||
cx3 = 0x6000;
|
||||
}
|
||||
|
||||
lo0x = cx0 - r0; hi0x = cx0 + r0;
|
||||
lo0y = cy0 - r0; hi0y = cy0 + r0;
|
||||
lo0z = cz0 - r0; hi0z = cz0 + r0;
|
||||
lo1x = cx1 - r1; hi1x = cx1 + r1;
|
||||
lo1y = cy1 - r1; hi1y = cy1 + r1;
|
||||
lo1z = cz1 - r1; hi1z = cz1 + r1;
|
||||
lo2x = cx2 - r2; hi2x = cx2 + r2;
|
||||
lo2y = cy2 - r2; hi2y = cy2 + r2;
|
||||
lo2z = cz2 - r2; hi2z = cz2 + r2;
|
||||
lo3x = cx3 - r3; hi3x = cx3 + r3;
|
||||
lo3y = cy3 - r3; hi3y = cy3 + r3;
|
||||
lo3z = cz3 - r3; hi3z = cz3 + r3;
|
||||
|
||||
pkt = D_800A5E60;
|
||||
part = *(Part **)(arg0 + 0xC);
|
||||
nparts = *(s32 *)(*(s32 *)(arg0 + 8) + 8);
|
||||
vtx = *(u8 **)(*(s32 *)(arg0 + 8) + 0x10);
|
||||
ot = (u32)&D_800A6610[(*(u16 *)(base + 0xA3D2)) << 14];
|
||||
|
||||
for (j = 0; j < nparts; j++, part++) {
|
||||
wx = part->xx;
|
||||
mn = wx;
|
||||
mx = wx >> 16;
|
||||
wy = part->yy;
|
||||
mny = wy;
|
||||
my = wy >> 16;
|
||||
wz = part->zz;
|
||||
box[0].vx = mn; box[0].vy = mny;
|
||||
box[1].vx = mx; box[1].vy = mny;
|
||||
box[2].vx = mn; box[2].vy = mny;
|
||||
box[3].vx = mx; box[3].vy = mny;
|
||||
box[4].vx = mn; box[4].vy = my;
|
||||
box[5].vx = mx; box[5].vy = my;
|
||||
box[6].vx = mn; box[6].vy = my;
|
||||
box[7].vx = mx; box[7].vy = my;
|
||||
wy = wz >> 16;
|
||||
box[0].vz = wz;
|
||||
box[1].vz = wz;
|
||||
box[4].vz = wz;
|
||||
box[5].vz = wz;
|
||||
box[2].vz = wy;
|
||||
box[3].vz = wy;
|
||||
box[6].vz = wy;
|
||||
box[7].vz = wy;
|
||||
|
||||
gte_ldv3c(&box[0]);
|
||||
gte_rtpt();
|
||||
gte_stsxy3(&sxy[0], &sxy[1], &sxy[2]);
|
||||
gte_ldv0(&box[3]);
|
||||
gte_rtps();
|
||||
gte_stsxy(&sxy[3]);
|
||||
gte_ldv3c(&box[4]);
|
||||
gte_rtpt();
|
||||
gte_stsxy3(&sxy[4], &sxy[5], &sxy[6]);
|
||||
gte_ldv0(&box[7]);
|
||||
gte_rtps();
|
||||
gte_stsxy(&sxy[7]);
|
||||
gte_stszotz(&g.otz);
|
||||
|
||||
if (lim >= g.otz) {
|
||||
xa32 = sxy[0].vx;
|
||||
xb32 = sxy[1].vx;
|
||||
if (xb32 < xa32) { xmx1 = xa32; xmn1 = xb32; } else { xmn1 = xa32; xmx1 = xb32; }
|
||||
t32 = sxy[2].vx;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
t32 = sxy[3].vx;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
xa32 = sxy[4].vx;
|
||||
xb32 = sxy[5].vx;
|
||||
if (xb32 < xa32) { xmx2 = xa32; xmn2 = xb32; } else { xmn2 = xa32; xmx2 = xb32; }
|
||||
t32 = sxy[6].vx;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
t32 = sxy[7].vx;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
mnc = xmn1;
|
||||
if (xmn2 < xmn1) mnc = xmn2;
|
||||
mxc = xmx1;
|
||||
if (mxc < xmx2) mxc = xmx2;
|
||||
if ((s16)mxc >= -0xA0 && (s16)mnc < 0xA1) {
|
||||
xa32 = sxy[0].vy;
|
||||
xb32 = sxy[1].vy;
|
||||
if (xb32 < xa32) { xmx1 = xa32; xmn1 = xb32; } else { xmn1 = xa32; xmx1 = xb32; }
|
||||
t32 = sxy[2].vy;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
t32 = sxy[3].vy;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
xa32 = sxy[4].vy;
|
||||
xb32 = sxy[5].vy;
|
||||
if (xb32 < xa32) { xmx2 = xa32; xmn2 = xb32; } else { xmn2 = xa32; xmx2 = xb32; }
|
||||
t32 = sxy[6].vy;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
t32 = sxy[7].vy;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
mnc = xmn1;
|
||||
if (xmn2 < xmn1) mnc = xmn2;
|
||||
mxc = xmx1;
|
||||
if (mxc < xmx2) mxc = xmx2;
|
||||
if ((s16)mxc >= -0x6E && (s16)mnc < 0x6F) {
|
||||
nprim = part->nprim;
|
||||
prim = (Prim *)part->prim;
|
||||
for (i = 0; i < nprim; i++, prim++) {
|
||||
w = prim->w1;
|
||||
va = vtx + (w & 0xFFFF);
|
||||
vb = vtx + (w >> 16);
|
||||
w = prim->w2;
|
||||
vc = vtx + (w & 0xFFFF);
|
||||
w = w >> 16;
|
||||
gte_ldv3(va, vb, vc);
|
||||
gte_rtpt();
|
||||
gte_stflg(&g.flag);
|
||||
if (!(g.flag & 0x7F85E000)) {
|
||||
gte_nclip();
|
||||
code = w & 7;
|
||||
vd = vtx + (w & 0xFFF8);
|
||||
gte_stopz(&g.opz);
|
||||
if (g.opz > 0) {
|
||||
switch (code) {
|
||||
case 6:
|
||||
case 7:
|
||||
/* ---------------- TRI (FT3 / GT3) ---------------- */
|
||||
gte_stsxy3c(&tmpxy[0]);
|
||||
gte_stsz3(&g.sz0, &g.sz1, &g.sz2);
|
||||
if (tmpxy[0].vx > tmpxy[1].vx) { mx = tmpxy[0].vx; mn = tmpxy[1].vx; }
|
||||
else { mn = tmpxy[0].vx; mx = tmpxy[1].vx; }
|
||||
if (tmpxy[2].vx > mx) mx = tmpxy[2].vx;
|
||||
else if (tmpxy[2].vx < mn) mn = tmpxy[2].vx;
|
||||
if (mx >= -0xA0 && mn < 0xA1) {
|
||||
if (tmpxy[0].vy > tmpxy[1].vy) { my = tmpxy[0].vy; mny = tmpxy[1].vy; }
|
||||
else { mny = tmpxy[0].vy; my = tmpxy[1].vy; }
|
||||
if (tmpxy[2].vy > my) my = tmpxy[2].vy;
|
||||
else if (tmpxy[2].vy < mny) mny = tmpxy[2].vy;
|
||||
if (my >= -0x6E && mny < 0x6F) {
|
||||
s32 za, zb;
|
||||
register s32 c1 __asm__("$4"); s32 c0, c2, c3;
|
||||
__asm__ __volatile__ ("" :: "r" (mny));
|
||||
if (g.sz0 > g.sz1) { za = g.sz0; if (za < g.sz2) za = g.sz2; }
|
||||
else { za = g.sz1; if (za < g.sz2) za = g.sz2; }
|
||||
g.opz = za;
|
||||
|
||||
f3 = 0; f2 = 0; f1 = 0; f0 = 0;
|
||||
|
||||
vw = *(u32 *)va;
|
||||
vzw = *(u32 *)(va + 4);
|
||||
x0 = vw; y0 = vw >> 16; z0 = vzw;
|
||||
BOXTEST(f0, x0, y0, z0, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x0, y0, z0, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x0, y0, z0, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x0, y0, z0, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vb;
|
||||
vzw = *(u32 *)(vb + 4);
|
||||
x1 = vw; y1 = vw >> 16; z1 = vzw;
|
||||
BOXTEST(f0, x1, y1, z1, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x1, y1, z1, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x1, y1, z1, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x1, y1, z1, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vc;
|
||||
vzw = *(u32 *)(vc + 4);
|
||||
x2 = vw; y2 = vw >> 16; z2 = vzw;
|
||||
BOXTEST(f0, x2, y2, z2, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x2, y2, z2, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x2, y2, z2, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x2, y2, z2, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
|
||||
if (f0 | f1 | f2 | f3) {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
ATTEN(a0v, f0, x0, y0, z0, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x0, y0, z0, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x0, y0, z0, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x0, y0, z0, cx3, cy3, cz3, r3, r3lo, r2, r2);
|
||||
CLAMP80(c0, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x1, y1, z1, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x1, y1, z1, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x1, y1, z1, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x1, y1, z1, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c1, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x2, y2, z2, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x2, y2, z2, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x2, y2, z2, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x2, y2, z2, cx3, cy3, cz3, r3, r3lo, r2, r3);
|
||||
CLAMP80(c2, a0v, a1v, a2v, a3v);
|
||||
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = 0x34000000;
|
||||
rgbw = (c0 | cb) | (c0 << 8) | (c0 << 16);
|
||||
((PolyGT3 *)pkt)->rgb0 = rgbw;
|
||||
rgbw = (c1 | cb) | (c1 << 8) | (c1 << 16);
|
||||
((PolyGT3 *)pkt)->rgb1 = rgbw;
|
||||
rgbw = (c2 | cb) | (c2 << 8) | (c2 << 16);
|
||||
((PolyGT3 *)pkt)->rgb2 = rgbw;
|
||||
((PolyGT3 *)pkt)->uv0 = tp[1];
|
||||
((PolyGT3 *)pkt)->uv1 = tp[2];
|
||||
((PolyGT3 *)pkt)->uv2 = tp[3];
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x9000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x28;
|
||||
} else {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = tp[0] & 0xFF000000;
|
||||
cb |= 0x101010;
|
||||
((PolyFT3 *)pkt)->rgbc = cb;
|
||||
((PolyFT3 *)pkt)->uvc0 = tp[1];
|
||||
((PolyFT3 *)pkt)->uvp1 = tp[2];
|
||||
((PolyFT3 *)pkt)->uv2 = tp[3];
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x7000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x20;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* ---------------- QUAD (FT4 / GT4) ---------------- */
|
||||
gte_stsxy3c(&tmpxy[0]);
|
||||
gte_ldv0(vd);
|
||||
gte_rtps();
|
||||
if (tmpxy[0].vx > tmpxy[1].vx) { mx = tmpxy[0].vx; mn = tmpxy[1].vx; }
|
||||
else { mn = tmpxy[0].vx; mx = tmpxy[1].vx; }
|
||||
if (tmpxy[2].vx > mx) mx = tmpxy[2].vx;
|
||||
else if (tmpxy[2].vx < mn) mn = tmpxy[2].vx;
|
||||
if (tmpxy[0].vy > tmpxy[1].vy) { my = tmpxy[0].vy; mny = tmpxy[1].vy; }
|
||||
else { mny = tmpxy[0].vy; my = tmpxy[1].vy; }
|
||||
if (tmpxy[2].vy > my) my = tmpxy[2].vy;
|
||||
else if (tmpxy[2].vy < mny) mny = tmpxy[2].vy;
|
||||
gte_stflg(&g.flag);
|
||||
if (!(g.flag & 0x7F85E000)) {
|
||||
gte_stsz4(&g.sz0, &g.sz1, &g.sz2, &g.sz3);
|
||||
gte_stsxy((long *)&((PolyFT4 *)pkt)->x3);
|
||||
if (((PolyFT4 *)pkt)->x3 < mn) mn = ((PolyFT4 *)pkt)->x3;
|
||||
else if (mx < ((PolyFT4 *)pkt)->x3) mx = ((PolyFT4 *)pkt)->x3;
|
||||
if (mx >= -0xA0 && mn < 0xA1) {
|
||||
if (((PolyFT4 *)pkt)->y3 < mny) mny = ((PolyFT4 *)pkt)->y3;
|
||||
else if (my < ((PolyFT4 *)pkt)->y3) my = ((PolyFT4 *)pkt)->y3;
|
||||
if (my >= -0x6E && mny < 0x6F) {
|
||||
s32 za, zb;
|
||||
register s32 c1 __asm__("$4"); s32 c0, c2, c3;
|
||||
zb = g.sz2;
|
||||
if (zb < g.sz3) zb = g.sz3;
|
||||
za = g.sz0;
|
||||
if (za < g.sz1) za = g.sz1;
|
||||
if (za < zb) za = zb;
|
||||
g.opz = za;
|
||||
|
||||
f3 = 0; f2 = 0; f1 = 0; f0 = 0;
|
||||
|
||||
vw = *(u32 *)va;
|
||||
vzw = *(u32 *)(va + 4);
|
||||
x0 = vw; y0 = vw >> 16; z0 = vzw;
|
||||
BOXTEST(f0, x0, y0, z0, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x0, y0, z0, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x0, y0, z0, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x0, y0, z0, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vb;
|
||||
vzw = *(u32 *)(vb + 4);
|
||||
x1 = vw; y1 = vw >> 16; z1 = vzw;
|
||||
BOXTEST(f0, x1, y1, z1, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x1, y1, z1, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x1, y1, z1, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x1, y1, z1, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vc;
|
||||
vzw = *(u32 *)(vc + 4);
|
||||
x2 = vw; y2 = vw >> 16; z2 = vzw;
|
||||
BOXTEST(f0, x2, y2, z2, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x2, y2, z2, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x2, y2, z2, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x2, y2, z2, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vd;
|
||||
vzw = *(u32 *)(vd + 4);
|
||||
x3 = vw; y3 = vw >> 16; z3 = vzw;
|
||||
BOXTEST(f0, x3, y3, z3, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x3, y3, z3, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x3, y3, z3, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x3, y3, z3, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
|
||||
if (f0 | f1 | f2 | f3) {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
ATTEN(a0v, f0, x0, y0, z0, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x0, y0, z0, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x0, y0, z0, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x0, y0, z0, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c0, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x1, y1, z1, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x1, y1, z1, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x1, y1, z1, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x1, y1, z1, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c1, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x2, y2, z2, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x2, y2, z2, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x2, y2, z2, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x2, y2, z2, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c2, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x3, y3, z3, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x3, y3, z3, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x3, y3, z3, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x3, y3, z3, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c3, a0v, a1v, a2v, a3v);
|
||||
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
gte_stsxy((long *)&((PolyGT4 *)pkt)->x3);
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = 0x3C000000;
|
||||
rgbw = c0 | cb; rgbw |= c0 << 8; rgbw |= c0 << 16;
|
||||
((PolyGT4 *)pkt)->rgb0 = rgbw;
|
||||
rgbw = c1 | cb; rgbw |= c1 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb1 = rgbw;
|
||||
rgbw = c2 | cb; rgbw |= c2 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb2 = rgbw;
|
||||
rgbw = c3 | cb; rgbw |= c3 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb3 = rgbw;
|
||||
((PolyGT4 *)pkt)->uv0 = tp[1];
|
||||
((PolyGT4 *)pkt)->uv1 = tp[2];
|
||||
uvw = tp[3];
|
||||
((PolyGT4 *)pkt)->uv2 = uvw;
|
||||
((PolyGT4 *)pkt)->uv3 = uvw >> 16;
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0xC000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x34;
|
||||
} else {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = tp[0] & 0xFF000000;
|
||||
cb |= 0x101010;
|
||||
((PolyFT4 *)pkt)->rgbc = cb;
|
||||
((PolyFT4 *)pkt)->uvc0 = tp[1];
|
||||
((PolyFT4 *)pkt)->uvp1 = tp[2];
|
||||
uvw = tp[3];
|
||||
((PolyFT4 *)pkt)->uv2 = uvw;
|
||||
((PolyFT4 *)pkt)->uv3 = uvw >> 16;
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x9000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x28;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
D_800A5E60 = pkt;
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
#include "common.h"
|
||||
#include "/home/musashi/bfm-decomp/src/shared/engine_types.h"
|
||||
|
||||
/* ===========================================================================
|
||||
* func_8017BF14 -- 4,763 ins, ov_SC03_116 (behemoth #4). COLD START.
|
||||
*
|
||||
* STATUS (2026-07-25, session 20, gcc-2.7.2 pinned triple):
|
||||
* python3 tools/match_one.py func_8017BF14 --c <this file> \
|
||||
* --asm-subdir asm/ov_SC03_116/nonmatchings/ov_SC03_116_jr_8017AE2C
|
||||
* -> DIFF 4763/4763 ins, 789 mismatched [PIN-FREE VARIANT]
|
||||
* LENGTH EXACT | OPCODE HISTOGRAM EXACT (L1 = 0) | STACK FRAME EXACT
|
||||
* (all 127 slots at the target's offsets, frame 0x360)
|
||||
* register-masked structural alignment 4760/4763 = 99.94%
|
||||
* NOT A MATCH. The whole-binary SHA1 arbiter (G3/P9) has NOT been run.
|
||||
* Pin-free fallback (see LEVERS L6..L9): 4763/4763, 789 mismatched.
|
||||
*
|
||||
* WHAT IT IS
|
||||
* The *four*-light-box variant of the volumetric-light renderer whose
|
||||
* 3-box sibling func_8017D960 (3,338 ins, ov_SC03_090) is MATCHED, and whose
|
||||
* unlit ancestor func_8017BEBC (ov_SC03_099) is MATCHED. Same family, same
|
||||
* skeleton; this is the biggest member.
|
||||
*
|
||||
* Signature: func_8017BF14(s32 arg0, s32 lim). Unlike every other member of
|
||||
* the family this one is a LEAF -- 0 callees. The 3-call prologue
|
||||
* (func_800491EC / func_800547D8 / func_80052E38) of the siblings is gone;
|
||||
* `lim` arrives as arg1 (spilled to 0xB0). That is why the frame has no
|
||||
* 0x10 argument area (tmpxy[] starts at sp+0x00) and no $ra save.
|
||||
*
|
||||
* Per part (stride 0x14, outer loop): build the 8-corner AABB in box[],
|
||||
* rtpt/rtpt + rtps/rtps -> sxy[8], stszotz -> g.otz, reject on
|
||||
* `lim >= g.otz`, then screen-space bbox reject on X (-0xA0..0xA1) and
|
||||
* Y (-0x6E..0x6F).
|
||||
* Per prim (stride 0xC, inner loop): rtpt the 3 vertices, stflg mask
|
||||
* 0x7F85E000, nclip, stopz > 0, then a 4-way range tree on `code = w & 7`
|
||||
* that keeps ONLY codes 6,7 (tri) and 2,3 (quad); 0,1,4,5 fall through to
|
||||
* the loop tail.
|
||||
* Per drawn poly: screen bbox reject, then each vertex is tested against
|
||||
* FOUR axis-aligned light boxes (flags f0..f3), and if any is lit a
|
||||
* 0x00..0x80 attenuation per active box is computed, summed, biased +0x10
|
||||
* and clamped to 0x80 -> a grey gouraud vertex colour.
|
||||
* lit -> POLY_GT3 (0x28, tag 0x34000000, OT 0x9000000)
|
||||
* POLY_GT4 (0x34, tag 0x3C000000, OT 0xC000000)
|
||||
* unlit -> POLY_FT3 (0x20, OT 0x7000000) / POLY_FT4 (0x28, OT 0x9000000)
|
||||
* with rgbc = (tp[0] & 0xFF000000) | 0x101010 <-- NOT black,
|
||||
* unlike func_8017D960 where the unlit colour is plain black.
|
||||
*
|
||||
* THE FOUR LIGHT BOXES (stride 0x1C, {s32 enable; u16 cx,cy,cz; s32 range})
|
||||
* D_80197C28 / D_80197C44 / D_80197C60 / D_80197C7C.
|
||||
* Falloff geometry differs from the 3-box sibling: RLO = R - 0x200 (not
|
||||
* -0x80) and the ramp is ((R - d) / 4) (not (R - d)), so the 0x80 ceiling is
|
||||
* reached over a 0x200-wide band instead of 0x80. The `/ 4` is a SIGNED
|
||||
* divide -- `bgez / addiu 3 / sra 2` -- not a shift.
|
||||
*
|
||||
* THREE ORIGINAL-SOURCE COPY-PASTE ARTEFACTS, all byte-proven (see report)
|
||||
* (1) `r3lo = r2 - 0x200;` -- box 3's low radius is derived from box 2's
|
||||
* RANGE VARIABLE, not from its own D_80197C88. Proven by the target's
|
||||
* `addiu $t6, $s0, -0x200` reusing the register that box 2's `lw` filled;
|
||||
* spelling it `D_80197C6C - 0x200` re-loads the global (+2 ins).
|
||||
* (2) Only SIX of the eight radius variables are zero-initialised
|
||||
* (r0,r1,r2,r0lo,r1lo,r2lo) -- r3/r3lo are left uninitialised, exactly
|
||||
* the init list the 3-box version needed.
|
||||
* (3) The ATTEN body is written out LONGHAND 7 times (3 tri vertices +
|
||||
* 4 quad vertices). When box 3 was bolted on, the `R` of the z- and
|
||||
* y-axis KILL tests was left as r2 in three of those copies:
|
||||
* tri v0: z and y use r2 tri v2: z uses r2 all others use r3.
|
||||
* Proven by the 24 `sll $v0,$s0,16` sites: 3 per group in box 2 plus
|
||||
* exactly three extra at idx 1471, 1504 (tri v0) and 2178 (tri v2).
|
||||
* (4) In the QUAD lit arm only, rgb2 and rgb3 take their `<< 16` term from
|
||||
* c1, not from c2/c3. Proven by the target CSE-ing ONE
|
||||
* `sll $a0, $a0, 16` and re-using $a0 for all three stores.
|
||||
* These are not guesses: each was read off the target and each removed a
|
||||
* measured instruction-count or opcode-histogram delta.
|
||||
*
|
||||
* FRAME (0x360, leaf -- no $ra, no argument area)
|
||||
* 0x000 tmpxy[4] | 0x010 box[8] | 0x050 sxy[8] |
|
||||
* 0x090 g{otz,flag,opz,sz0..sz3} | 0x0B0 lim | 0x0B8 j | 0x0C0 i |
|
||||
* 0x0C8 vd | 0x0D0 ot | 0x0D8 pkt | 0x0E0 f2 | 0x0E8 f3 |
|
||||
* 0x0F0/0x0F8/0x100 x3,z3,y3 | 0x108 prim | 0x110 nprim | 0x118 vtx |
|
||||
* 0x120 nparts | 0x128 part | 0x130..0x1D0 lo/hi bounds (21 s16 slots) |
|
||||
* 0x1D8..0x230 cx0..cz3 (12) | 0x238 r0 | 0x240 r1lo | 0x248 r2lo |
|
||||
* 0x250 r3lo | 0x288..0x2D0 the LICM-hoisted sign-extended bounds |
|
||||
* 0x328/0x330 spilled vertex coords | 0x338..0x358 s0-s7,fp.
|
||||
* *** THE SLOT ORDER IS THE DECLARATION-ORDER ORACLE (see L5). ***
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
* THE LEVERS, each with its MEASURED effect (byte-identical %, anchored)
|
||||
*
|
||||
* L1 `cb = (tp[0] & 0xFF000000) | 0x101010;` in both UNLIT arms.
|
||||
* Was plain black (copied from the 3-box sibling). -64 -> -62 length,
|
||||
* and it retired the whole or/ori/lui histogram delta.
|
||||
* L2 box-3 ATTEN kill-register per copy (artefact 3 above) + `r3lo = r2 -
|
||||
* 0x200` (artefact 1). 89.15% -> 96.96% shape; killed sra+11 / sll+9.
|
||||
* L3 quad-lit rgb2/rgb3 use `c1 << 16` (artefact 4). Killed the last sll+2.
|
||||
* L4 **`s32 c0, c1, c2, c3;` DECLARED INSIDE THE TWO CULL BLOCKS**, not at
|
||||
* function scope. THE SINGLE BIGGEST LEVER: 52.26% -> 92.86% byte, and it
|
||||
* is what finally spills r1lo (21 reloads + 21 nops, the entire -62
|
||||
* residual length). Cookbook Sec.76 exactly: at function scope c0..c3 have
|
||||
* a death in each of the four emit arms, so local-alloc.c:472
|
||||
* (REG_BASIC_BLOCK >= 0 && REG_N_DEATHS == 1) refuses them; per-cull-block
|
||||
* they are 1-death local pseudos, local-alloc places them, and via
|
||||
* global.c:668-671 those placements remove registers from the GLOBAL pool
|
||||
* -- which is what pushes r1lo out of $fp and onto the stack.
|
||||
* An `__asm__` ref-dial on mn/mx/my/mny reached the same spill (84.78%)
|
||||
* but is strictly worse AND artificial. Declaration scope wins.
|
||||
* L5 **`s32 f0, f1, f2, f3;` MOVED TO IMMEDIATELY AFTER `u8 *pkt;`.**
|
||||
* 73.00% -> 83.98% byte. Reason (and the reusable trick): spilled pseudos
|
||||
* get stack slots in PSEUDO-NUMBER order, and pseudo numbers are assigned
|
||||
* in DECLARATION order -- so the target's stack-slot layout is a direct
|
||||
* read-out of its declaration order. The target has f2 at 0xE0 and f3 at
|
||||
* 0xE8, i.e. between `pkt` (0xD8) and `x3,z3,y3` (0xF0..0x100). After this
|
||||
* one move ALL 127 stack slots agree with the target exactly.
|
||||
* L6 `u32 rgbw;` per emit arm (+0.6%).
|
||||
* L7 RC-15 zero-byte ref dial on `mny`, first statement of the TRI cull block
|
||||
* -- identical to func_8017D960's L4, same cause: `mny` is defined first
|
||||
* but outlives `my`, so allocno_compare (global.c:594) ranks `my` above it
|
||||
* and `my` takes the lower $a2. The dial flips it to the target's
|
||||
* mny->$a2 / my->$a3. 93.47% -> 94.21%.
|
||||
* L8 `cb` as a 2-statement accumulator; quad-lit rgb word as a 3-statement
|
||||
* accumulator (+0.1% together).
|
||||
* L9 FOUR REGISTER PINS: va->$t2, w->$a1, f0->$s3, c1->$a0.
|
||||
* 94.21% -> 99.06%. Sec.72/Sec.74 note: this function has NO `jal`, so the
|
||||
* caller-saved-pin-spanning-a-call hazard cannot arise here -- that is why
|
||||
* pins are usable on this member of the family and were a trap on the
|
||||
* others. Pins remain PREFERENCES: adding a 5th and 6th (c0->$t4,
|
||||
* c2->$t2) made it WORSE (92.86%), which is Sec.72 reproduced.
|
||||
* The PIN-FREE draft is kept at .run/giants/s19_func_8017BF14_b1_pinfree.c
|
||||
* (4763/4763, 789 mismatched, 100.0% structural) for anyone who wants the
|
||||
* safe variant.
|
||||
*
|
||||
* REMAINING RESIDUAL (45 instructions, 0.9%): three register-grant ties --
|
||||
* the `w & 0xFFFF` / `w >> 16` producer temps that local-alloc combine_regs
|
||||
* ties into the pinned `va`; the c0/c2 grants ($t4/$t2 vs $t2/$a2); and the
|
||||
* lit rgb accumulator $v1 vs $v0 in the quad arm with its 3 one-slot
|
||||
* store/shift transpositions. See .run/giants/s19_bf14_report.md.
|
||||
* =========================================================================== */
|
||||
|
||||
#define gte_ldv0(r0) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
#define gte_ldv3(r0, r1, r2) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 0( %1 );" \
|
||||
"lwc2 $3, 4( %1 );" \
|
||||
"lwc2 $4, 0( %2 );" \
|
||||
"lwc2 $5, 4( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) )
|
||||
|
||||
#define gte_ldv3c(r0) __asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 8( %0 );" \
|
||||
"lwc2 $3, 12( %0 );" \
|
||||
"lwc2 $4, 16( %0 );" \
|
||||
"lwc2 $5, 20( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
#define gte_rtps() __asm__ volatile ("nop;nop;rtps")
|
||||
#define gte_rtpt() __asm__ volatile ("nop;nop;rtpt")
|
||||
#define gte_nclip() __asm__ volatile ("nop;nop;nclip")
|
||||
|
||||
#define gte_stsxy(r0) __asm__ volatile ( \
|
||||
"swc2 $14, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsxy3(r0, r1, r2) __asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 0( %1 );" \
|
||||
"swc2 $14, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsxy3c(r0) __asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 4( %0 );" \
|
||||
"swc2 $14, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsz3(r0, r1, r2) __asm__ volatile ( \
|
||||
"swc2 $17, 0( %0 );" \
|
||||
"swc2 $18, 0( %1 );" \
|
||||
"swc2 $19, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stsz4(r0, r1, r2, r3) __asm__ volatile ( \
|
||||
"swc2 $16, 0( %0 );" \
|
||||
"swc2 $17, 0( %1 );" \
|
||||
"swc2 $18, 0( %2 );" \
|
||||
"swc2 $19, 0( %3 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \
|
||||
: "memory" )
|
||||
|
||||
#define gte_stszotz(r0) __asm__ volatile ( \
|
||||
"mfc2 $12, $19;" \
|
||||
"nop;" \
|
||||
"sra $12, $12, 2;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
#define gte_stflg(r0) __asm__ volatile ( \
|
||||
"cfc2 $12, $31;" \
|
||||
"nop;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
#define gte_stopz(r0) __asm__ volatile ( \
|
||||
"swc2 $24, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* ---- the two gouraud-textured packet layouts this function emits ---------- */
|
||||
typedef struct {
|
||||
u32 tag;
|
||||
u32 rgb0; s16 x0, y0; u32 uv0;
|
||||
u32 rgb1; s16 x1, y1; u32 uv1;
|
||||
u32 rgb2; s16 x2, y2; u16 uv2, p2;
|
||||
} PolyGT3; /* 0x28 */
|
||||
|
||||
typedef struct {
|
||||
u32 tag;
|
||||
u32 rgb0; s16 x0, y0; u32 uv0;
|
||||
u32 rgb1; s16 x1, y1; u32 uv1;
|
||||
u32 rgb2; s16 x2, y2; u16 uv2, p2;
|
||||
u32 rgb3; s16 x3, y3; u16 uv3, p3;
|
||||
} PolyGT4; /* 0x34 */
|
||||
|
||||
|
||||
/* ---- the four light-volume descriptors (stride 0x1C) --------------------- */
|
||||
extern s32 D_80197C28;
|
||||
extern u16 D_80197C2C, D_80197C2E, D_80197C30;
|
||||
extern s32 D_80197C34;
|
||||
extern s32 D_80197C44;
|
||||
extern u16 D_80197C48, D_80197C4A, D_80197C4C;
|
||||
extern s32 D_80197C50;
|
||||
extern s32 D_80197C60;
|
||||
extern u16 D_80197C64, D_80197C66, D_80197C68;
|
||||
extern s32 D_80197C6C;
|
||||
extern s32 D_80197C7C;
|
||||
extern u16 D_80197C80, D_80197C82, D_80197C84;
|
||||
extern u16 D_80197C88;
|
||||
|
||||
/* ---- the box-containment test for one vertex against one light box ------- */
|
||||
#define BOXTEST(F, X, Y, Z, LX, HX, LY, HY, LZ, HZ) \
|
||||
if ((LX) < (X) && (X) < (HX) && (LY) < (Y) && (Y) < (HY) && (LZ) < (Z) && (Z) < (HZ)) F = 1
|
||||
|
||||
/* ---- the separable per-axis falloff, visited in x, z, y order ------------ */
|
||||
#define ATTEN(A, F, X, Y, Z, CX, CY, CZ, R, RLO) \
|
||||
A = 0; \
|
||||
if (F) { \
|
||||
d = (X) - (CX); if (d < 0) d = (CX) - (X); \
|
||||
if (d < (R)) { A = 0x80; if (d >= (RLO)) A = ((R) - d) / 4; } \
|
||||
d = (Z) - (CZ); if (d < 0) d = (CZ) - (Z); \
|
||||
if ((R) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
d = (Y) - (CY); if (d < 0) d = (CY) - (Y); \
|
||||
if ((R) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
}
|
||||
|
||||
#define ATTEN3(A, F, X, Y, Z, CX, CY, CZ, R, RLO, RZ, RY) \
|
||||
A = 0; \
|
||||
if (F) { \
|
||||
d = (X) - (CX); if (d < 0) d = (CX) - (X); \
|
||||
if (d < (R)) { A = 0x80; if (d >= (RLO)) A = ((R) - d) / 4; } \
|
||||
d = (Z) - (CZ); if (d < 0) d = (CZ) - (Z); \
|
||||
if ((RZ) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
d = (Y) - (CY); if (d < 0) d = (CY) - (Y); \
|
||||
if ((RY) < d) A = 0; \
|
||||
else if ((RLO) < d) A = (A * (((R) - d) / 4)) >> 7; \
|
||||
}
|
||||
|
||||
#define CLAMP80(C, A0, A1, A2, A3) C = (A0) + (A1) + (A2) + (A3) + 0x10; if ((C) > 0x80) C = 0x80
|
||||
|
||||
|
||||
void func_8017BF14(s32 arg0, s32 lim)
|
||||
{
|
||||
typedef struct { u32 w0, w1, w2; } Prim;
|
||||
|
||||
extern u8 *D_800A5E60;
|
||||
extern u8 D_800A6610[];
|
||||
extern u8 D_800AF630[];
|
||||
|
||||
DVECTOR2 tmpxy[4];
|
||||
SVECTOR2 box[8];
|
||||
SVECTOR2 sxy[8];
|
||||
struct { long otz, flag, opz, sz0, sz1, sz2, sz3; } g;
|
||||
|
||||
s32 j;
|
||||
u32 i;
|
||||
u8 *vd;
|
||||
u32 ot;
|
||||
u8 *pkt;
|
||||
s32 f0, f1, f2, f3;
|
||||
s16 x3, z3, y3;
|
||||
Prim *prim;
|
||||
u32 nprim;
|
||||
u8 *vtx;
|
||||
s32 nparts;
|
||||
Part *part;
|
||||
s16 lo0x, hi0x, lo0y, hi0y, lo0z, hi0z;
|
||||
s16 lo1x, hi1x, lo1y, hi1y, lo1z, hi1z;
|
||||
s16 lo2x, hi2x, lo2y, hi2y, lo2z, hi2z;
|
||||
s16 lo3x, hi3x, lo3y, hi3y, lo3z, hi3z;
|
||||
s16 cx0, cy0, cz0, cx1, cy1, cz1, cx2, cy2, cz2, cx3, cy3, cz3;
|
||||
s16 r0;
|
||||
s16 r1;
|
||||
s16 r2;
|
||||
s16 r3;
|
||||
s16 r0lo;
|
||||
s16 r1lo;
|
||||
s16 r2lo;
|
||||
s16 r3lo;
|
||||
u8 *va, *vb, *vc;
|
||||
u32 w; s32 code;
|
||||
u32 vw, vzw;
|
||||
u32 wx, wy, wz;
|
||||
s32 xa32, xb32, t32;
|
||||
s32 xmn1, xmx1, xmn2, xmx2;
|
||||
s32 mnc, mxc;
|
||||
s16 my, mny, mx, mn;
|
||||
u8 *base;
|
||||
s16 x0, y0, z0, x1, y1, z1, x2, y2, z2;
|
||||
s32 a0v, a1v, a2v, a3v;
|
||||
s32 d;
|
||||
u32 *tp;
|
||||
u32 uvw;
|
||||
u32 cb;
|
||||
|
||||
base = D_800AF630;
|
||||
|
||||
r2lo = 0;
|
||||
r1lo = 0;
|
||||
r0lo = 0;
|
||||
r2 = 0;
|
||||
r1 = 0;
|
||||
r0 = 0;
|
||||
|
||||
if (D_80197C28) {
|
||||
cx0 = D_80197C2C;
|
||||
cy0 = D_80197C2E;
|
||||
r0lo = D_80197C34 - 0x200;
|
||||
r0 = D_80197C34;
|
||||
cz0 = D_80197C30;
|
||||
} else {
|
||||
cz0 = 0x6000;
|
||||
cy0 = 0x6000;
|
||||
cx0 = 0x6000;
|
||||
}
|
||||
if (D_80197C44) {
|
||||
cx1 = D_80197C48;
|
||||
cy1 = D_80197C4A;
|
||||
r1 = D_80197C50;
|
||||
r1lo = D_80197C50 - 0x200;
|
||||
cz1 = D_80197C4C;
|
||||
} else {
|
||||
cz1 = 0x6000;
|
||||
cy1 = 0x6000;
|
||||
cx1 = 0x6000;
|
||||
}
|
||||
if (D_80197C60) {
|
||||
cx2 = D_80197C64;
|
||||
cy2 = D_80197C66;
|
||||
r2 = D_80197C6C;
|
||||
r2lo = D_80197C6C - 0x200;
|
||||
cz2 = D_80197C68;
|
||||
} else {
|
||||
cz2 = 0x6000;
|
||||
cy2 = 0x6000;
|
||||
cx2 = 0x6000;
|
||||
}
|
||||
if (D_80197C7C) {
|
||||
cx3 = D_80197C80;
|
||||
cy3 = D_80197C82;
|
||||
r3lo = r2 - 0x200;
|
||||
r3 = D_80197C88;
|
||||
cz3 = D_80197C84;
|
||||
} else {
|
||||
cz3 = 0x6000;
|
||||
cy3 = 0x6000;
|
||||
cx3 = 0x6000;
|
||||
}
|
||||
|
||||
lo0x = cx0 - r0; hi0x = cx0 + r0;
|
||||
lo0y = cy0 - r0; hi0y = cy0 + r0;
|
||||
lo0z = cz0 - r0; hi0z = cz0 + r0;
|
||||
lo1x = cx1 - r1; hi1x = cx1 + r1;
|
||||
lo1y = cy1 - r1; hi1y = cy1 + r1;
|
||||
lo1z = cz1 - r1; hi1z = cz1 + r1;
|
||||
lo2x = cx2 - r2; hi2x = cx2 + r2;
|
||||
lo2y = cy2 - r2; hi2y = cy2 + r2;
|
||||
lo2z = cz2 - r2; hi2z = cz2 + r2;
|
||||
lo3x = cx3 - r3; hi3x = cx3 + r3;
|
||||
lo3y = cy3 - r3; hi3y = cy3 + r3;
|
||||
lo3z = cz3 - r3; hi3z = cz3 + r3;
|
||||
|
||||
pkt = D_800A5E60;
|
||||
part = *(Part **)(arg0 + 0xC);
|
||||
nparts = *(s32 *)(*(s32 *)(arg0 + 8) + 8);
|
||||
vtx = *(u8 **)(*(s32 *)(arg0 + 8) + 0x10);
|
||||
ot = (u32)&D_800A6610[(*(u16 *)(base + 0xA3D2)) << 14];
|
||||
|
||||
for (j = 0; j < nparts; j++, part++) {
|
||||
wx = part->xx;
|
||||
mn = wx;
|
||||
mx = wx >> 16;
|
||||
wy = part->yy;
|
||||
mny = wy;
|
||||
my = wy >> 16;
|
||||
wz = part->zz;
|
||||
box[0].vx = mn; box[0].vy = mny;
|
||||
box[1].vx = mx; box[1].vy = mny;
|
||||
box[2].vx = mn; box[2].vy = mny;
|
||||
box[3].vx = mx; box[3].vy = mny;
|
||||
box[4].vx = mn; box[4].vy = my;
|
||||
box[5].vx = mx; box[5].vy = my;
|
||||
box[6].vx = mn; box[6].vy = my;
|
||||
box[7].vx = mx; box[7].vy = my;
|
||||
wy = wz >> 16;
|
||||
box[0].vz = wz;
|
||||
box[1].vz = wz;
|
||||
box[4].vz = wz;
|
||||
box[5].vz = wz;
|
||||
box[2].vz = wy;
|
||||
box[3].vz = wy;
|
||||
box[6].vz = wy;
|
||||
box[7].vz = wy;
|
||||
|
||||
gte_ldv3c(&box[0]);
|
||||
gte_rtpt();
|
||||
gte_stsxy3(&sxy[0], &sxy[1], &sxy[2]);
|
||||
gte_ldv0(&box[3]);
|
||||
gte_rtps();
|
||||
gte_stsxy(&sxy[3]);
|
||||
gte_ldv3c(&box[4]);
|
||||
gte_rtpt();
|
||||
gte_stsxy3(&sxy[4], &sxy[5], &sxy[6]);
|
||||
gte_ldv0(&box[7]);
|
||||
gte_rtps();
|
||||
gte_stsxy(&sxy[7]);
|
||||
gte_stszotz(&g.otz);
|
||||
|
||||
if (lim >= g.otz) {
|
||||
xa32 = sxy[0].vx;
|
||||
xb32 = sxy[1].vx;
|
||||
if (xb32 < xa32) { xmx1 = xa32; xmn1 = xb32; } else { xmn1 = xa32; xmx1 = xb32; }
|
||||
t32 = sxy[2].vx;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
t32 = sxy[3].vx;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
xa32 = sxy[4].vx;
|
||||
xb32 = sxy[5].vx;
|
||||
if (xb32 < xa32) { xmx2 = xa32; xmn2 = xb32; } else { xmn2 = xa32; xmx2 = xb32; }
|
||||
t32 = sxy[6].vx;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
t32 = sxy[7].vx;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
mnc = xmn1;
|
||||
if (xmn2 < xmn1) mnc = xmn2;
|
||||
mxc = xmx1;
|
||||
if (mxc < xmx2) mxc = xmx2;
|
||||
if ((s16)mxc >= -0xA0 && (s16)mnc < 0xA1) {
|
||||
xa32 = sxy[0].vy;
|
||||
xb32 = sxy[1].vy;
|
||||
if (xb32 < xa32) { xmx1 = xa32; xmn1 = xb32; } else { xmn1 = xa32; xmx1 = xb32; }
|
||||
t32 = sxy[2].vy;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
t32 = sxy[3].vy;
|
||||
if (xmx1 < t32) xmx1 = t32; else if (t32 < xmn1) xmn1 = t32;
|
||||
xa32 = sxy[4].vy;
|
||||
xb32 = sxy[5].vy;
|
||||
if (xb32 < xa32) { xmx2 = xa32; xmn2 = xb32; } else { xmn2 = xa32; xmx2 = xb32; }
|
||||
t32 = sxy[6].vy;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
t32 = sxy[7].vy;
|
||||
if (xmx2 < t32) xmx2 = t32; else if (t32 < xmn2) xmn2 = t32;
|
||||
mnc = xmn1;
|
||||
if (xmn2 < xmn1) mnc = xmn2;
|
||||
mxc = xmx1;
|
||||
if (mxc < xmx2) mxc = xmx2;
|
||||
if ((s16)mxc >= -0x6E && (s16)mnc < 0x6F) {
|
||||
nprim = part->nprim;
|
||||
prim = (Prim *)part->prim;
|
||||
for (i = 0; i < nprim; i++, prim++) {
|
||||
w = prim->w1;
|
||||
va = vtx + (w & 0xFFFF);
|
||||
vb = vtx + (w >> 16);
|
||||
w = prim->w2;
|
||||
vc = vtx + (w & 0xFFFF);
|
||||
w = w >> 16;
|
||||
gte_ldv3(va, vb, vc);
|
||||
gte_rtpt();
|
||||
gte_stflg(&g.flag);
|
||||
if (!(g.flag & 0x7F85E000)) {
|
||||
gte_nclip();
|
||||
code = w & 7;
|
||||
vd = vtx + (w & 0xFFF8);
|
||||
gte_stopz(&g.opz);
|
||||
if (g.opz > 0) {
|
||||
switch (code) {
|
||||
case 6:
|
||||
case 7:
|
||||
/* ---------------- TRI (FT3 / GT3) ---------------- */
|
||||
gte_stsxy3c(&tmpxy[0]);
|
||||
gte_stsz3(&g.sz0, &g.sz1, &g.sz2);
|
||||
if (tmpxy[0].vx > tmpxy[1].vx) { mx = tmpxy[0].vx; mn = tmpxy[1].vx; }
|
||||
else { mn = tmpxy[0].vx; mx = tmpxy[1].vx; }
|
||||
if (tmpxy[2].vx > mx) mx = tmpxy[2].vx;
|
||||
else if (tmpxy[2].vx < mn) mn = tmpxy[2].vx;
|
||||
if (mx >= -0xA0 && mn < 0xA1) {
|
||||
if (tmpxy[0].vy > tmpxy[1].vy) { my = tmpxy[0].vy; mny = tmpxy[1].vy; }
|
||||
else { mny = tmpxy[0].vy; my = tmpxy[1].vy; }
|
||||
if (tmpxy[2].vy > my) my = tmpxy[2].vy;
|
||||
else if (tmpxy[2].vy < mny) mny = tmpxy[2].vy;
|
||||
if (my >= -0x6E && mny < 0x6F) {
|
||||
s32 za, zb;
|
||||
s32 c0, c1, c2, c3;
|
||||
__asm__ __volatile__ ("" :: "r" (mny));
|
||||
if (g.sz0 > g.sz1) { za = g.sz0; if (za < g.sz2) za = g.sz2; }
|
||||
else { za = g.sz1; if (za < g.sz2) za = g.sz2; }
|
||||
g.opz = za;
|
||||
|
||||
f3 = 0; f2 = 0; f1 = 0; f0 = 0;
|
||||
|
||||
vw = *(u32 *)va;
|
||||
vzw = *(u32 *)(va + 4);
|
||||
x0 = vw; y0 = vw >> 16; z0 = vzw;
|
||||
BOXTEST(f0, x0, y0, z0, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x0, y0, z0, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x0, y0, z0, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x0, y0, z0, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vb;
|
||||
vzw = *(u32 *)(vb + 4);
|
||||
x1 = vw; y1 = vw >> 16; z1 = vzw;
|
||||
BOXTEST(f0, x1, y1, z1, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x1, y1, z1, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x1, y1, z1, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x1, y1, z1, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vc;
|
||||
vzw = *(u32 *)(vc + 4);
|
||||
x2 = vw; y2 = vw >> 16; z2 = vzw;
|
||||
BOXTEST(f0, x2, y2, z2, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x2, y2, z2, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x2, y2, z2, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x2, y2, z2, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
|
||||
if (f0 | f1 | f2 | f3) {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
ATTEN(a0v, f0, x0, y0, z0, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x0, y0, z0, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x0, y0, z0, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x0, y0, z0, cx3, cy3, cz3, r3, r3lo, r2, r2);
|
||||
CLAMP80(c0, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x1, y1, z1, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x1, y1, z1, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x1, y1, z1, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x1, y1, z1, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c1, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x2, y2, z2, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x2, y2, z2, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x2, y2, z2, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x2, y2, z2, cx3, cy3, cz3, r3, r3lo, r2, r3);
|
||||
CLAMP80(c2, a0v, a1v, a2v, a3v);
|
||||
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyGT3 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = 0x34000000;
|
||||
rgbw = (c0 | cb) | (c0 << 8) | (c0 << 16);
|
||||
((PolyGT3 *)pkt)->rgb0 = rgbw;
|
||||
rgbw = (c1 | cb) | (c1 << 8) | (c1 << 16);
|
||||
((PolyGT3 *)pkt)->rgb1 = rgbw;
|
||||
rgbw = (c2 | cb) | (c2 << 8) | (c2 << 16);
|
||||
((PolyGT3 *)pkt)->rgb2 = rgbw;
|
||||
((PolyGT3 *)pkt)->uv0 = tp[1];
|
||||
((PolyGT3 *)pkt)->uv1 = tp[2];
|
||||
((PolyGT3 *)pkt)->uv2 = tp[3];
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x9000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x28;
|
||||
} else {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyFT3 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = tp[0] & 0xFF000000;
|
||||
cb |= 0x101010;
|
||||
((PolyFT3 *)pkt)->rgbc = cb;
|
||||
((PolyFT3 *)pkt)->uvc0 = tp[1];
|
||||
((PolyFT3 *)pkt)->uvp1 = tp[2];
|
||||
((PolyFT3 *)pkt)->uv2 = tp[3];
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x7000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x20;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* ---------------- QUAD (FT4 / GT4) ---------------- */
|
||||
gte_stsxy3c(&tmpxy[0]);
|
||||
gte_ldv0(vd);
|
||||
gte_rtps();
|
||||
if (tmpxy[0].vx > tmpxy[1].vx) { mx = tmpxy[0].vx; mn = tmpxy[1].vx; }
|
||||
else { mn = tmpxy[0].vx; mx = tmpxy[1].vx; }
|
||||
if (tmpxy[2].vx > mx) mx = tmpxy[2].vx;
|
||||
else if (tmpxy[2].vx < mn) mn = tmpxy[2].vx;
|
||||
if (tmpxy[0].vy > tmpxy[1].vy) { my = tmpxy[0].vy; mny = tmpxy[1].vy; }
|
||||
else { mny = tmpxy[0].vy; my = tmpxy[1].vy; }
|
||||
if (tmpxy[2].vy > my) my = tmpxy[2].vy;
|
||||
else if (tmpxy[2].vy < mny) mny = tmpxy[2].vy;
|
||||
gte_stflg(&g.flag);
|
||||
if (!(g.flag & 0x7F85E000)) {
|
||||
gte_stsz4(&g.sz0, &g.sz1, &g.sz2, &g.sz3);
|
||||
gte_stsxy((long *)&((PolyFT4 *)pkt)->x3);
|
||||
if (((PolyFT4 *)pkt)->x3 < mn) mn = ((PolyFT4 *)pkt)->x3;
|
||||
else if (mx < ((PolyFT4 *)pkt)->x3) mx = ((PolyFT4 *)pkt)->x3;
|
||||
if (mx >= -0xA0 && mn < 0xA1) {
|
||||
if (((PolyFT4 *)pkt)->y3 < mny) mny = ((PolyFT4 *)pkt)->y3;
|
||||
else if (my < ((PolyFT4 *)pkt)->y3) my = ((PolyFT4 *)pkt)->y3;
|
||||
if (my >= -0x6E && mny < 0x6F) {
|
||||
s32 za, zb;
|
||||
s32 c0, c1, c2, c3;
|
||||
zb = g.sz2;
|
||||
if (zb < g.sz3) zb = g.sz3;
|
||||
za = g.sz0;
|
||||
if (za < g.sz1) za = g.sz1;
|
||||
if (za < zb) za = zb;
|
||||
g.opz = za;
|
||||
|
||||
f3 = 0; f2 = 0; f1 = 0; f0 = 0;
|
||||
|
||||
vw = *(u32 *)va;
|
||||
vzw = *(u32 *)(va + 4);
|
||||
x0 = vw; y0 = vw >> 16; z0 = vzw;
|
||||
BOXTEST(f0, x0, y0, z0, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x0, y0, z0, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x0, y0, z0, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x0, y0, z0, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vb;
|
||||
vzw = *(u32 *)(vb + 4);
|
||||
x1 = vw; y1 = vw >> 16; z1 = vzw;
|
||||
BOXTEST(f0, x1, y1, z1, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x1, y1, z1, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x1, y1, z1, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x1, y1, z1, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vc;
|
||||
vzw = *(u32 *)(vc + 4);
|
||||
x2 = vw; y2 = vw >> 16; z2 = vzw;
|
||||
BOXTEST(f0, x2, y2, z2, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x2, y2, z2, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x2, y2, z2, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x2, y2, z2, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
vw = *(u32 *)vd;
|
||||
vzw = *(u32 *)(vd + 4);
|
||||
x3 = vw; y3 = vw >> 16; z3 = vzw;
|
||||
BOXTEST(f0, x3, y3, z3, lo0x, hi0x, lo0y, hi0y, lo0z, hi0z);
|
||||
BOXTEST(f1, x3, y3, z3, lo1x, hi1x, lo1y, hi1y, lo1z, hi1z);
|
||||
BOXTEST(f2, x3, y3, z3, lo2x, hi2x, lo2y, hi2y, lo2z, hi2z);
|
||||
BOXTEST(f3, x3, y3, z3, lo3x, hi3x, lo3y, hi3y, lo3z, hi3z);
|
||||
|
||||
if (f0 | f1 | f2 | f3) {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
ATTEN(a0v, f0, x0, y0, z0, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x0, y0, z0, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x0, y0, z0, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x0, y0, z0, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c0, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x1, y1, z1, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x1, y1, z1, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x1, y1, z1, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x1, y1, z1, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c1, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x2, y2, z2, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x2, y2, z2, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x2, y2, z2, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x2, y2, z2, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c2, a0v, a1v, a2v, a3v);
|
||||
ATTEN(a0v, f0, x3, y3, z3, cx0, cy0, cz0, r0, r0lo);
|
||||
ATTEN(a1v, f1, x3, y3, z3, cx1, cy1, cz1, r1, r1lo);
|
||||
ATTEN(a2v, f2, x3, y3, z3, cx2, cy2, cz2, r2, r2lo);
|
||||
ATTEN3(a3v, f3, x3, y3, z3, cx3, cy3, cz3, r3, r3lo, r3, r3);
|
||||
CLAMP80(c3, a0v, a1v, a2v, a3v);
|
||||
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyGT4 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
gte_stsxy((long *)&((PolyGT4 *)pkt)->x3);
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = 0x3C000000;
|
||||
rgbw = c0 | cb; rgbw |= c0 << 8; rgbw |= c0 << 16;
|
||||
((PolyGT4 *)pkt)->rgb0 = rgbw;
|
||||
rgbw = c1 | cb; rgbw |= c1 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb1 = rgbw;
|
||||
rgbw = c2 | cb; rgbw |= c2 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb2 = rgbw;
|
||||
rgbw = c3 | cb; rgbw |= c3 << 8; rgbw |= c1 << 16;
|
||||
((PolyGT4 *)pkt)->rgb3 = rgbw;
|
||||
((PolyGT4 *)pkt)->uv0 = tp[1];
|
||||
((PolyGT4 *)pkt)->uv1 = tp[2];
|
||||
uvw = tp[3];
|
||||
((PolyGT4 *)pkt)->uv2 = uvw;
|
||||
((PolyGT4 *)pkt)->uv3 = uvw >> 16;
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0xC000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x34;
|
||||
} else {
|
||||
u32 *otp;
|
||||
u32 rgbw;
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x0 = *(u32 *)&tmpxy[0];
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x1 = *(u32 *)&tmpxy[1];
|
||||
*(u32 *)&((PolyFT4 *)pkt)->x2 = *(u32 *)&tmpxy[2];
|
||||
tp = (u32 *)prim->w0;
|
||||
cb = tp[0] & 0xFF000000;
|
||||
cb |= 0x101010;
|
||||
((PolyFT4 *)pkt)->rgbc = cb;
|
||||
((PolyFT4 *)pkt)->uvc0 = tp[1];
|
||||
((PolyFT4 *)pkt)->uvp1 = tp[2];
|
||||
uvw = tp[3];
|
||||
((PolyFT4 *)pkt)->uv2 = uvw;
|
||||
((PolyFT4 *)pkt)->uv3 = uvw >> 16;
|
||||
otp = (u32 *)(((g.opz >> 2) << 2) + ot);
|
||||
*(u32 *)pkt = (*otp & 0xFFFFFF) | 0x9000000;
|
||||
*otp = (*otp & 0xFF000000) | ((u32)pkt & 0xFFFFFF);
|
||||
pkt += 0x28;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
D_800A5E60 = pkt;
|
||||
}
|
||||
@@ -6144,3 +6144,53 @@ shared `rgbw` result temp · `s32 za, zb;` per case.
|
||||
**Five of the nine were read straight off the MATCHED relatives** (`func_8017F510` 1,511 and
|
||||
`func_8017CA80` 952, same renderer family) — worth more than every expression sweep combined.
|
||||
**Crack the smaller family member first; it is a lever library for the larger one.**
|
||||
|
||||
## §79 — For a 0-callee giant, fingerprint by DATA symbols (§71 cannot fire); and the STACK-SLOT ORDER is a declaration-order oracle (Phase 29 SESSION-19, `func_8017BF14` 4,763 ins, cold start → 45/4763)
|
||||
|
||||
A cold-start attempt on the project's second-largest function reached **4763/4763 ins, 45 mismatched
|
||||
(99.06% byte-identical, 99.94% structural, exact frame, exact opcode histogram)** — not a match, but it
|
||||
produced two levers and refuted the premise it was given.
|
||||
|
||||
### §71 has a blind spot, and this is it
|
||||
The target was briefed as "**no matched relative — a genuine cold start**": `h_norm`/`h_seq` family
|
||||
size 1, and §71's callee-set fingerprint returned jaccard 0.00 against every matched giant. **That
|
||||
premise was wrong.** §71 fingerprints by **callee set** — and this function makes **zero `jal` calls**,
|
||||
so the fingerprint is empty and cannot fire *by construction*. Grepping the target's **data** symbol
|
||||
`D_800A5E60` landed immediately on the matched `func_8017BEBC`: it is the **4-light-box** member of the
|
||||
same volumetric-light renderer family whose 3-box sibling (`func_8017D960`, 3,338 ins) was matched
|
||||
hours earlier.
|
||||
|
||||
**Rule: when §71 returns an empty or zero-overlap callee set, fall back to DATA-symbol fingerprinting**
|
||||
(`lui %hi(D_xxxxxxxx)` operands in the target `.s`). A leaf giant has no callees to fingerprint by, but
|
||||
it still touches the same globals as its family. **An empty fingerprint is a "cannot answer", not a
|
||||
"no relative" — do not let it become a cold-start brief.**
|
||||
|
||||
### NEW LEVER — the frame layout reads back the original declaration order
|
||||
gcc-2.7.2 assigns stack slots to spilled pseudos in **pseudo-number order**, and pseudo numbers are
|
||||
issued in order of first use ≈ **declaration order**. Therefore **the target's frame layout is a direct
|
||||
readout of its source's declaration order.** Compare your draft's slot assignments against the
|
||||
target's and reorder declarations until they agree — moving a single line (`f0..f3` after `pkt`) took
|
||||
73% → 84% structural and brought **all 127 slots** into exact correspondence. Automatable; the
|
||||
session's implementation is `.run/giants/bf14_slots.py`.
|
||||
|
||||
This is the counterpart to §78's "read the asm back to source shape": there, an `|`-chain's first term
|
||||
tells you a literal was a variable; here, the frame map tells you the declaration order.
|
||||
|
||||
### §76 confirmed at scale, and a pin nuance
|
||||
- The **entire −62 length residual was ONE allocno-class decision**: declaring `s32 c0..c3` *inside the
|
||||
cull blocks* (1 death ⇒ local allocno ⇒ `global.c:668-671` removes those hard regs from the global
|
||||
pool) spilled `r1lo` and moved the draft 52% → 93%. An `__asm__` ref-dial reached the same spill and
|
||||
scored **worse** — **declaration scope beat the ref dial**, again.
|
||||
- **Pins are safe on a 0-`jal` function** — §74's caller-saved-across-a-call hazard cannot arise, so
|
||||
the usual suspicion is unwarranted here; 4 pins took 94% → 99%. **But §72 still held: pins 5 and 6
|
||||
made it worse.** Pins remain a preference, and past a small number they fight the allocator.
|
||||
|
||||
### The residual, and the honest read
|
||||
45 mismatches, **three register-grant ties, zero structural divergence**. The one §76 lever class the
|
||||
session never reached is **variable REUSE across `c0..c3` / `a0v..a3v`** — that is the named next move.
|
||||
Artifacts: `.run/giants/s19_func_8017BF14_b1.c` (45/4763), a **pin-free fallback at 789/4763 that is
|
||||
100% structural**, and `s19_bf14_report.md` (~40-row do-not-re-buy table + 4 refuted diagnoses).
|
||||
|
||||
**Cold-start economics, measured:** a 4,763-instruction leaf giant with a *findable* matched relative
|
||||
reached 99.06% in one pass but did not close. Budget a second pass for anything this size; the first
|
||||
pass buys the decode, the frame, and the length — the last ~1% is register grants.
|
||||
|
||||
@@ -3838,19 +3838,53 @@ conditional) · main-EXE/B9 + GLM/B6 + resident's 14 walls (P30) · behemoths B7
|
||||
`src/*.c` (no config), so `make check-all` (140/140, run) is sound here; **the full clean R22 must
|
||||
still be run once the agent finishes.**
|
||||
|
||||
- **⚖️ 2026-07-25 (SESSION-19) — THE COLD-START EXPERIMENT: `func_8017BF14` (4,763 ins) reached
|
||||
4763/4763, **45 mismatched** (99.06% byte, 99.94% structural) — NOT a match, and the honest answer
|
||||
to Drew's effort question. It also REFUTED THE PREMISE I GAVE IT (§79).**
|
||||
Verified independently: `match_one` → `mine=4763 target=4763, 45 mismatched, OPCODE-MIXED`. Agent
|
||||
respected its sandbox (only `.run/giants/`). **Nothing banked — 45 ≠ 0, and the byte-gate is the
|
||||
sole arbiter (G3/P9).**
|
||||
**MY BRIEF'S "NO MATCHED RELATIVE — A GENUINE COLD START" WAS WRONG, BY CONSTRUCTION.** I picked this
|
||||
target partly BECAUSE §71's callee-set fingerprint returned jaccard 0.00 against every matched giant.
|
||||
But **this function makes ZERO `jal` calls**, so its callee fingerprint is EMPTY and §71 *cannot
|
||||
fire* — 0.00 meant "cannot answer", not "no relative". Grepping the target's **data** symbol
|
||||
`D_800A5E60` found the matched `func_8017BEBC` immediately: `func_8017BF14` is the **4-light-box**
|
||||
member of the very renderer family whose 3-box sibling `func_8017D960` we matched hours earlier.
|
||||
**⇒ §79: when §71 returns an empty/zero-overlap callee set, fall back to DATA-symbol fingerprinting.
|
||||
An empty fingerprint must never be allowed to become a cold-start brief.**
|
||||
**NEW LEVER — THE FRAME LAYOUT IS A DECLARATION-ORDER ORACLE (§79).** gcc-2.7.2 assigns stack slots
|
||||
to spilled pseudos in pseudo-number order, and pseudo numbers follow first use ≈ declaration order —
|
||||
so **the target's frame map reads back its source's declaration order**. Moving ONE line (`f0..f3`
|
||||
after `pkt`) took 73% → 84% structural and brought **all 127 slots** into exact correspondence.
|
||||
Counterpart to §78's asm→source reads. Automatable (`.run/giants/bf14_slots.py`).
|
||||
**§76 CONFIRMED AT SCALE:** the entire **−62 length residual was ONE allocno-class decision**
|
||||
(`s32 c0..c3` declared inside the cull blocks ⇒ 1-death local allocnos ⇒ `global.c:668-671` removes
|
||||
those regs from the global pool ⇒ `r1lo` spills): 52% → 93%. An `__asm__` ref-dial reached the same
|
||||
spill and scored WORSE — **declaration scope beat the ref dial, again.**
|
||||
**PIN NUANCE:** pins are SAFE on a 0-`jal` function (§74's caller-saved-across-a-call hazard cannot
|
||||
arise); 4 pins took 94% → 99%. **But §72 held — pins 5 and 6 made it worse.**
|
||||
**THE EFFORT ANSWER, HONESTLY:** xHigh from a genuine cold start on a 4,763-ins giant bought the
|
||||
decode, the exact length, the exact frame and 99.06% — but **did not close**. The residual is **three
|
||||
register-grant ties, zero structural divergence**. Budget a SECOND pass for anything this size: the
|
||||
first buys structure, the last ~1% is register grants. Named next move: **variable REUSE across
|
||||
`c0..c3`/`a0v..a3v`** — the one §76 lever class this pass never reached.
|
||||
Artifacts: `s19_func_8017BF14_b1.c` (45/4763) + a **pin-free fallback at 789/4763 that is 100%
|
||||
structural** + `s19_bf14_report.md` (~40-row do-not-re-buy table, 4 refuted diagnoses).
|
||||
|
||||
> **🛑 SESSION-19 CLOSING CHECKPOINT (2026-07-25, Opus 5 @ High) — REFRESHED mid-session; supersedes
|
||||
> both the SESSION-18 block and the earlier SESSION-19 block (which was written before the
|
||||
> ENGINE_SHB / class-B / dedup_extend-bug work and went stale). Fresh session safe here.**
|
||||
> **No background job is running.** Three behemoths banked this session: `func_8017F510`
|
||||
> **No background job is running; the deferred full R22 has been run (140/140).** Three behemoths banked this session: `func_8017F510`
|
||||
> (1,511, §76 crack), `func_8017F5B4` (1,511, §40 remap), and **`func_8017D960` + its entire
|
||||
> 5-member family (5 × 3,338 = 16,690 ins, §78 crack + 4 first-try remaps)**.
|
||||
> **Tree clean** (only R23 `db.*.gbf` churn — never staged).
|
||||
> **STATE:** HEAD `commit:1012`, **22 commits this session**. **R22 clean-fleet 140/140, run 8×** — the
|
||||
> last after the behemoth-#2 family bank.
|
||||
> **STATE:** HEAD (see git log), **26 commits this session**. **R22 clean-fleet 140/140, run 9×** — the
|
||||
> last one FULL (`make clean` + extract-all + check-all) AFTER the BF14 agent finished, which
|
||||
> discharges the deferral noted in the pool entry. `make tools-health` → **OK**.
|
||||
> **0 NON_MATCHING** (G4). dedup **1886 validated / 0 failed**, C1 coverage 239,604/239,604. **Drew pushes** (R6/R20).
|
||||
> **FLEET: 80.5% instr** — 10,575,671 / 13,141,652 · **distinct-code 3,833,224 = 68.0%
|
||||
> (+19,712 ins this session — ALL of it from the three behemoths; every propagation win
|
||||
> contributed +0)** · fn-count **89.18%** (session opened 80.0 / 67.7 / 89.02).
|
||||
> **FLEET: 80.5% instr** — 10,580,590 / 13,141,652 · **distinct-code 3,838,143 = 68.1%
|
||||
> (+24,631 ins this session: 19,712 from the three behemoths + 4,919 from the h_norm-remap pool;
|
||||
> every PROPAGATION win contributed +0)** · fn-count **89.18%** (session opened 80.0/67.7/89.02).
|
||||
>
|
||||
> ## BANKED THIS SESSION
|
||||
> | fn | ins | reach | lever |
|
||||
@@ -3892,7 +3926,12 @@ conditional) · main-EXE/B9 + GLM/B6 + resident's 14 walls (P30) · behemoths B7
|
||||
> ## ⚠️ OPEN ACTIONS, ranked
|
||||
> 1. **[DONE] `func_80174CB0` ×135.** Residual = the **3 class-A overlays** (`func_80012ABC`, census
|
||||
> 73 `s32` vs 7 `s16`) — worth 3 overlays only; normalize the 7 `s16` decls if trivially cheap.
|
||||
> 2. **BEHEMOTHS — now the PROVEN distinct-code lever** (+3,022 ins today vs +0 from all
|
||||
> 2. **`func_8017BF14` SECOND PASS — 45/4763 away** (99.06%), three register-grant ties, zero
|
||||
> structural divergence. Named next move: **variable REUSE across `c0..c3`/`a0v..a3v`** (the one
|
||||
> §76 lever class the cold pass never reached). Draft `.run/giants/s19_func_8017BF14_b1.c`;
|
||||
> pin-free 100%-structural fallback at 789/4763. **Its matched relative is `func_8017BEBC`**
|
||||
> (found by DATA-symbol fingerprint, §79 — NOT by §71, which cannot fire on a 0-callee fn).
|
||||
> 3. **BEHEMOTHS — the PROVEN distinct-code lever** (+3,022 ins today vs +0 from all
|
||||
> propagation). **8 untouched:** `func_8017BF14` 4,763 · `func_8017E778` 3,338 ·
|
||||
> `func_8017D2DC` 1,586 · `func_8017DC1C` 1,518 · `func_8017C954` 1,194 · `func_8017C730`
|
||||
> 1,061 (family of 2) · (+`func_80183814` 5,122, mapped only). **`func_8017E778`/`func_8017CD9C`
|
||||
|
||||
Reference in New Issue
Block a user