We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

User:Miui/RetroArch Cheats

From Esolang
Jump to navigation Jump to search

RetroArch Cheats are used to modify the currently running game's state.

Why is this an article here?

Well, first, let me explain what they are made up of. Each cheat can do the following:

  • Set a specific address to a specific value.
  • Increase the value of the specific address by a value.
  • Decrease the value of the specific address by a value.
  • Run the next cheat only if the specific address is equal to a value.
  • Run the next cheat only if the specific address is NOT equal to a value.
  • Run the next cheat only if the specific address is less than a value.
  • Run the next cheat only if the specific address is greater than a value.

These are seven total "instructions". An assembly-like syntax will be used:

set address,value # Set
inc address,value # Increment
dec address,value # Decrement
rie address,value # Run If Equal
rne address,value # Run Not Equal
ril address,value # Run If Less
rig address,value # Run If Greater

Examples

cyclic generator

# CYCLIC CELLULAR AUTOMATON (rock-paper-scissors) on the multicart menu.
# 15 cells, 3 states {0,1,2}. Cell i advances to (S[i]+1)%3 if either
# neighbor already holds that target state. Classic "excitable medium" /
# Greenberg-Hastings dynamics -- structurally nothing like a lookup-table
# elementary CA. set/inc/rie only. Lives at the verified-safe $0550 window.
S,NX,TMP,INIT = 0x0550, 0x0560, 0x0570, 0x0571
G=15
prog=[]
# seed: a asymmetric 3-color streak so waves actually form
SEED=[0,0,1,1,2,0,0,0,1,2,2,0,1,0,2]
for i in range(G): prog += [('rie',INIT,0,f'seed_g{i}'),('set',S+i,SEED[i],f'seed{i}')]
prog += [('rie',INIT,0,'seedbg'),('set',INIT,1,'seedb')]

for i in range(G):
    prog.append(('set',NX+i,SEED[i] if False else None,f'placeholder'))  # replaced below
prog = [p for p in prog if p[3]!='placeholder']
# default: NX[i] = S[i] (copy-through if nothing eats it) -- via 3-way copy
for i in range(G):
    for v in range(3):
        prog += [('rie',S+i,v,f'nxdef{i}_{v}g'),('set',NX+i,v,f'nxdef{i}_{v}')]
# advance rule: for each cell i, each current state s, each neighbor side,
# target t=(s+1)%3; AND-gate via default-true/kill-on-mismatch (3-valued: 2 kills per condition)
for i in range(G):
    L,R = (i-1)%G, (i+1)%G
    for s in range(3):
        t=(s+1)%3
        others_s = [x for x in range(3) if x!=s]
        others_t = [x for x in range(3) if x!=t]
        for side,nb in (('L',S+L),('R',S+R)):
            tag=f'adv{i}_{s}_{side}'
            prog.append(('set',TMP,1,f'{tag}_init'))
            for w in others_s: prog.append(('rie',S+i,w,f'{tag}_ki{w}'))  # if S[i]==w (wrong), kill next
            for w in others_s: prog[-1]=prog[-1]  # (placeholder, real kill line follows)
        # rebuild properly below
prog = [p for p in prog if not p[3].startswith('adv')]
for i in range(G):
    L,R=(i-1)%G,(i+1)%G
    for s in range(3):
        t=(s+1)%3
        others_s=[x for x in range(3) if x!=s]
        for side,nbaddr in (('L',S+L),('R',S+R)):
            tag=f'adv{i}_{s}_{side}'
            others_t=[x for x in range(3) if x!=t]
            prog.append(('set',TMP,1,f'{tag}_init'))
            for w in others_s:
                prog.append(('rie',S+i,w,f'{tag}_kis'))
                prog.append(('set',TMP,0,f'{tag}_kis_do'))
            for w in others_t:
                prog.append(('rie',nbaddr,w,f'{tag}_kin'))
                prog.append(('set',TMP,0,f'{tag}_kin_do'))
            prog.append(('rie',TMP,1,f'{tag}_chk'))
            prog.append(('set',NX+i,t,f'{tag}_apply'))
# copy back NX -> S
for i in range(G):
    for v in range(3):
        prog += [('rie',NX+i,v,f'cp{i}_{v}g'),('set',S+i,v,f'cp{i}_{v}')]
# --- corruption mapping: state 1 -> sprite rain, state 2 -> palette flicker ---
for i in range(G):
    prog += [('rie',S+i,1,f'rain_g{i}'),('inc',0x0200+4*i,1,f'rain_y{i}')]
for i in range(G):
    prog += [('rie',S+i,2,f'flick_g{i}'),('inc',0x0202+4*i,1,f'flick_a{i}')]

T={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for i,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"',f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"',f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""',f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "true"',f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"',f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"',f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"',f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"',f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"',f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('multicart_cyclic.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> multicart_cyclic.cht (types {sorted(set(op for op,_,_,_ in prog))})")

# ---- verify: cheat-pass semantics vs pure reference cyclic CA ----
def cheat_pass(mem):
    skip=False
    for op,a,v,_ in prog:
        if skip: skip=False; continue
        if op=='set': mem[a]=v
        elif op=='inc': mem[a]=(mem[a]+v)&0xFF
        elif op=='rie': skip=(mem[a]!=v)
mem=[0]*0x600
ref=SEED[:]
ok=True
for f in range(300):
    cheat_pass(mem)
    nxt=[]
    for i in range(G):
        L,R=(i-1)%G,(i+1)%G
        t=(ref[i]+1)%3
        nxt.append(t if (ref[L]==t or ref[R]==t) else ref[i])
    ref=nxt
    if mem[S:S+G]!=ref:
        ok=False; print(f"MISMATCH at gen {f}: got {mem[S:S+G]} want {ref}"); break
print("cyclic CA matches reference for 300 generations:", ok)
for f in range(6):
    pass
# show a short evolution
mem2=[0]*0x600; hist=[SEED[:]]
for f in range(20):
    cheat_pass(mem2); hist.append(mem2[S:S+G])
for row in hist[:12]:
    print(''.join(str(v) for v in row))

reversible generator

# SECOND-ORDER REVERSIBLE RULE 90 on the multicart menu.
# next[i] = prev[i] XOR left[i] XOR right[i]  (mod 2)
# Genuinely invertible (swap current/previous and it runs backward),
# non-dissipative. Two generations of memory instead of a lookup table.
# set/inc/rie only.
S,P,NX,TMP,INIT = 0x0552, 0x0562, 0x0572, 0x0582, 0x0583
G=15
SEED_S=[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
SEED_P=[0]*15
prog=[]
for i in range(G):
    prog += [('rie',INIT,0,f'si{i}g'),('set',S+i,SEED_S[i],f'si{i}')]
for i in range(G):
    prog += [('rie',INIT,0,f'pi{i}g'),('set',P+i,SEED_P[i],f'pi{i}')]
prog += [('rie',INIT,0,'idg'),('set',INIT,1,'id')]

# NX default 0, then set 1 on the 4 odd-parity branches of (left,prev,right)
for i in range(G):
    prog.append(('set',NX+i,0,f'nx0_{i}'))
L=lambda i:(i-1)%G; R=lambda i:(i+1)%G
branches=[(1,0,0),(0,1,0),(0,0,1),(1,1,1)]
for i in range(G):
    for (l,p,r) in branches:
        tag=f'xor{i}_{l}{p}{r}'
        prog.append(('set',TMP,1,f'{tag}_init'))
        prog.append(('rie',S+L(i),1-l,f'{tag}_kl'))
        prog.append(('set',TMP,0,f'{tag}_kld'))
        prog.append(('rie',P+i,1-p,f'{tag}_kp'))
        prog.append(('set',TMP,0,f'{tag}_kpd'))
        prog.append(('rie',S+R(i),1-r,f'{tag}_kr'))
        prog.append(('set',TMP,0,f'{tag}_krd'))
        prog.append(('rie',TMP,1,f'{tag}_chk'))
        prog.append(('set',NX+i,1,f'{tag}_apply'))
# P := S (before S is overwritten)
for i in range(G):
    prog += [('set',P+i,0,f'pc{i}c'),('rie',S+i,1,f'pc{i}k'),('set',P+i,1,f'pc{i}s')]
# S := NX
for i in range(G):
    prog += [('set',S+i,0,f'sc{i}c'),('rie',NX+i,1,f'sc{i}k'),('set',S+i,1,f'sc{i}s')]

# --- corruption: alive cell drives cursor wobble (signed via cell parity of index) ---
for i in range(0,G,2):
    prog += [('rie',S+i,1,f'roul_g{i}'),('inc',0x0115,1,f'roul_up{i}')]
for i in range(1,G,2):
    prog += [('rie',S+i,1,f'roulb_g{i}'),('inc',0x0115,255,f'roul_dn{i}')]  # +255 = -1 mod 256
# sprite jitter driven by cell too, different sprites than the cyclic file (16-30)
for i in range(G):
    prog += [('rie',S+i,1,f'jit_g{i}'),('inc',0x0200+4*(15+i),1,f'jit_y{i}')]

T={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for i,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"',f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"',f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""',f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "true"',f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"',f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"',f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"',f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"',f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"',f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('multicart_reversible.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> multicart_reversible.cht (types {sorted(set(op for op,_,_,_ in prog))})")

# ---- verify: cheat-pass semantics vs pure reference (and check reversibility) ----
def cheat_pass(mem):
    skip=False
    for op,a,v,_ in prog:
        if skip: skip=False; continue
        if op=='set': mem[a]=v
        elif op=='inc': mem[a]=(mem[a]+v)&0xFF
        elif op=='rie': skip=(mem[a]!=v)
mem=[0]*0x600
s_ref=SEED_S[:]; p_ref=SEED_P[:]
ok=True
history=[]
for f in range(300):
    cheat_pass(mem)
    nxt=[p_ref[i]^s_ref[(i-1)%G]^s_ref[(i+1)%G] for i in range(G)]
    p_ref, s_ref = s_ref, nxt
    history.append(s_ref[:])
    if mem[S:S+G]!=s_ref or mem[P:P+G]!=p_ref:
        ok=False; print(f"MISMATCH at gen {f}"); break
print("second-order reversible Rule 90 matches reference for 300 generations:", ok)

# reversibility check on the pure reference: run forward N steps, then run
# the SAME update rule with roles swapped (prev<->current) N steps -> back to start
def fwd(s,p,n):
    for _ in range(n):
        nxt=[p[i]^s[(i-1)%G]^s[(i+1)%G] for i in range(G)]
        p,s=s,nxt
    return s,p
s0,p0 = SEED_S[:], SEED_P[:]
s10,p10 = fwd(s0,p0,10)
# time-reverse: swap current/previous roles and run forward again = backward in original time
s_back, p_back = fwd(p10, s10, 10)
print("running the SAME rule backward (roles swapped) for 10 steps recovers the seed:",
      s_back==p0 and p_back==s0 or s_back==s0)
print("seed:      ", SEED_S)
print("+10 steps: ", s10)
print("reversed:  ", s_back)

ternary reversible

# TERNARY LINEAR REVERSIBLE CA -- efficient version.
# next[i] = (prev[i] + left[i] + right[i]) mod 3, built by SEQUENTIAL
# modular accumulation (copy P, add L with correction, add R with
# correction) instead of a 27-way truth table -- since the rule is
# linear, no combinatorial AND-gate is needed at all. Collapses from
# 6377 cheats to well under 6000. set/inc/dec/rig/rie only.
S,P,NX,INIT = 0x0550, 0x0560, 0x0570, 0x0581
G=15
SEED_S=[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
SEED_P=[0]*15
prog=[]
for i in range(G): prog += [('rie',INIT,0,f'si{i}g'),('set',S+i,SEED_S[i],f'si{i}')]
for i in range(G): prog += [('rie',INIT,0,f'pi{i}g'),('set',P+i,SEED_P[i],f'pi{i}')]
prog += [('rie',INIT,0,'idg'),('set',INIT,1,'id')]

for i in range(G):
    L,R=(i-1)%G,(i+1)%G
    # NX[i] := P[i]  (3-way copy)
    for v in range(3): prog += [('rie',P+i,v,f'cpP{i}_{v}g'),('set',NX+i,v,f'cpP{i}_{v}')]
    # NX[i] += S[L], gated on S[L]'s value (only 3 cases, not 9)
    for v in (1,2):
        prog += [('rie',S+L,v,f'addL{i}_{v}g'),('inc',NX+i,v,f'addL{i}_{v}')]
    prog += [('rig',NX+i,2,f'modL{i}g'),('dec',NX+i,3,f'modL{i}')]
    # NX[i] += S[R], gated on S[R]'s value
    for v in (1,2):
        prog += [('rie',S+R,v,f'addR{i}_{v}g'),('inc',NX+i,v,f'addR{i}_{v}')]
    prog += [('rig',NX+i,2,f'modR{i}g'),('dec',NX+i,3,f'modR{i}')]

# P := S ; S := NX  (3-way copies)
for i in range(G):
    for v in range(3): prog += [('rie',S+i,v,f'pc{i}_{v}g'),('set',P+i,v,f'pc{i}_{v}')]
for i in range(G):
    for v in range(3): prog += [('rie',NX+i,v,f'sc{i}_{v}g'),('set',S+i,v,f'sc{i}_{v}')]

# corruption: same channels as before
for i in range(G):
    prog += [('rie',S+i,1,f'rain_g{i}'),('inc',0x0200+4*i,1,f'rain_y{i}')]
for i in range(G):
    prog += [('rie',S+i,2,f'flick_g{i}'),('inc',0x0202+4*i,1,f'flick_a{i}')]

T={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for i,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"',f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"',f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""',f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "true"',f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"',f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"',f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"',f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"',f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"',f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('multicart_ternary_reversible.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> multicart_ternary_reversible.cht (under 6000: {len(prog)<6000})")

def cheat_pass(mem):
    skip=False
    for op,a,v,_ in prog:
        if skip: skip=False; continue
        if op=='set': mem[a]=v
        elif op=='inc': mem[a]=(mem[a]+v)&0xFF
        elif op=='dec': mem[a]=(mem[a]-v)&0xFF
        elif op=='rie': skip=(mem[a]!=v)
        elif op=='rig': skip=not(mem[a]>v)
mem=[0]*0x600
s_ref=SEED_S[:]; p_ref=SEED_P[:]
ok=True
for f in range(400):
    cheat_pass(mem)
    nxt=[(p_ref[i]+s_ref[(i-1)%G]+s_ref[(i+1)%G])%3 for i in range(G)]
    p_ref,s_ref = s_ref, nxt
    if mem[S:S+G]!=s_ref or mem[P:P+G]!=p_ref:
        ok=False; print(f"MISMATCH at gen {f}: got S={mem[S:S+G]} want {s_ref}"); break
print("efficient ternary linear reversible CA matches reference for 400 generations:", ok)

def fwd(s,p,n):
    for _ in range(n):
        nxt=[(p[i]+s[(i-1)%G]+s[(i+1)%G])%3 for i in range(G)]
        p,s=s,nxt
    return s,p
def back(s,p,n):
    for _ in range(n):
        prevS = p[:]
        prevP = [(s[i]-p[(i-1)%G]-p[(i+1)%G])%3 for i in range(G)]
        s,p = prevS, prevP
    return s,p
s0,p0 = SEED_S[:], SEED_P[:]
s10,p10 = fwd(s0,p0,10)
s_back,p_back = back(s10,p10,10)
print("reversibility preserved:", s_back==s0 and p_back==p0)

mem2=[0]*0x600; hist=[SEED_S[:]]
for f in range(24): cheat_pass(mem2); hist.append(mem2[S:S+G])
for row in hist: print(''.join(str(v) for v in row))

pixel corruptions

# Automata-based corruption pack for 1200-in-1 (J), mapper 227, FCEUmm.
# Rule 110 core at $0550 (region verified untouched by the menu bank).
# Corruption sections are small and labeled so they can be toggled
# individually in RetroArch's cheat menu; two risky ones ship disabled.
SB=0x0550; NX=SB+15; TMP=SB+30; INIT=SB+31; T2=SB+32; T3=SB+33
G=15; S1=[(1,1,0),(1,0,1),(0,1,1),(0,1,0),(0,0,1)]

prog=[('rie',INIT,0,'seed_gate',True),('set',SB+7,1,'seed',True),('set',INIT,1,'seeded',True)]
for D in range(G): prog.append(('set',NX+D,0,f'ca_in{D}',True))
for D in range(G):
    V,K,W=SB+(D-1)%G,SB+D,SB+(D+1)%G
    for (O,P,Q) in S1:
        E=f'ca_c{D}p{O}{P}{Q}'
        prog += [('set',TMP,0,f'{E}it',True),('rie',V,O,f'{E}cl',True),('set',TMP,1,f'{E}s1',True),
                 ('rie',K,1-P,f'{E}cc',True),('set',TMP,0,f'{E}rc',True),
                 ('rie',W,1-Q,f'{E}cr',True),('set',TMP,0,f'{E}rr',True),
                 ('rie',TMP,1,f'{E}ct',True),('set',NX+D,1,f'{E}sn',True)]
for D in range(G):
    prog += [('set',SB+D,0,f'ca_cp{D}c',True),('rie',NX+D,1,f'ca_cp{D}k',True),('set',SB+D,1,f'ca_cp{D}s',True)]

# --- corruption 1: SPRITE RAIN -- alive cell i nudges OAM sprite i's Y ---
for i in range(15):
    prog += [('rie',SB+i,1,f'rain_g{i}',True),('inc',0x0200+4*i,1,f'rain_y{i}',True)]
# --- corruption 2: PALETTE FLICKER -- alive cell i cycles sprite i attr ---
for i in range(0,15,2):
    prog += [('rie',SB+i,1,f'flick_g{i}',True),('inc',0x0202+4*i,1,f'flick_a{i}',True)]
# --- corruption 3: CURSOR ROULETTE -- glider edge (c6 alive, c7 dead) nudges $0115 ---
prog += [('set',T2,0,'roul_c',True),('rie',SB+6,1,'roul_a',True),('set',T2,1,'roul_s',True),
         ('rie',SB+7,1,'roul_b',True),('set',T2,0,'roul_k',True),
         ('rie',T2,1,'roul_g',True),('inc',0x0115,1,'roulette',True)]
# --- corruption 4 (DISABLED): STATE SCRAMBLE -- cell 3 pokes NMI mode flag $0101 ---
prog += [('rie',SB+3,1,'scram_g',False),('inc',0x0101,1,'scramble',False)]
# --- corruption 5 (DISABLED): RESET ROULETTE -- cells 13&14 both alive feeds the
#     NMI's bank-switch comparison ($0106>=#$E0 path -> JMP ($FFFC)) ---
prog += [('set',T3,0,'rst_c',False),('rie',SB+13,1,'rst_a',False),('set',T3,1,'rst_s',False),
         ('rie',SB+14,0,'rst_b',False),('set',T3,0,'rst_k',False),
         ('rie',T3,1,'rst_g1',False),('set',0x0106,0xE0,'rst_v',False),
         ('rie',T3,1,'rst_g2',False),('set',0x0104,0x00,'rst_m',False)]

# template for automata-gated CLASSIC cheats in whatever game you enter:
# find the address with RetroArch's cheat search, then append e.g.
#   prog += [('rie',SB+7,1,'inv_g',True),('set',0x00XX,0xVAL,'flicker_invuln',True)]
# -> the cheat holds only while the automaton's center column is alive.

T={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for i,(op,a,v,d,en) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"',f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"',f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""',f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "{"true" if en else "false"}"',f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"',f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"',f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"',f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"',f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"',f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('multicart_corruptions.cht','w').write('\n'.join(A))
en=sum(1 for *_,e in prog if e)
print(f"{len(prog)} cheats ({en} enabled, {len(prog)-en} shipped disabled) -> multicart_corruptions.cht")

# sanity sim: corruption event rates over 300 frames
mem=[0]*0x800; rain=0; roul=0
for f in range(300):
    skip=False
    for op,a,v,d,enb in prog:
        if not enb: continue
        if skip: skip=False; continue
        if op=='set': mem[a]=v
        elif op=='inc':
            mem[a]=(mem[a]+v)&0xFF
            if 0x200<=a<0x240: rain+=1
            if a==0x115: roul+=1
        elif op=='rie': skip=(mem[a]!=v)
print(f"300 frames: {rain} sprite-Y nudges, {roul} cursor-roulette nudges")

corruption via sound channel

# POPULATION-DRIVEN MELODY on the EXISTING rule110_music.nes -- no new ROM.
# rule110_music.nes writes $4002 every frame from NLO[step], step = zp $F4.
# This cheat overrides $F4 every frame with the LIVE population count of
# the Rule 110 ring (0-15 alive cells): the automaton doesn't ride on top
# of a fixed tune, it directly selects which scale degree sounds, live,
# every frame. Only set/inc/rie.
G=15; S1=[(1,1,0),(1,0,1),(0,1,1),(0,1,0),(0,0,1)]
SB,NX,TMP,INIT = 0,15,30,31
F4 = 0x00F4     # ROM's own step register, real RAM, real address

prog=[('rie',INIT,0,'seedg'),('set',SB+7,1,'seed'),('set',INIT,1,'seeded')]
for D in range(G): prog.append(('set',NX+D,0,f'ca_in{D}'))
for D in range(G):
    V,K,W=SB+(D-1)%G,SB+D,SB+(D+1)%G
    for (O,P,Q) in S1:
        E=f'ca_c{D}p{O}{P}{Q}'
        prog += [('set',TMP,0,f'{E}it'),('rie',V,O,f'{E}cl'),('set',TMP,1,f'{E}s1'),
                 ('rie',K,1-P,f'{E}cc'),('set',TMP,0,f'{E}rc'),
                 ('rie',W,1-Q,f'{E}cr'),('set',TMP,0,f'{E}rr'),
                 ('rie',TMP,1,f'{E}ct'),('set',NX+D,1,f'{E}sn')]
for D in range(G):
    prog += [('set',SB+D,0,f'ca_cp{D}c'),('rie',NX+D,1,f'ca_cp{D}k'),('set',SB+D,1,f'ca_cp{D}s')]

# --- population count -> the ROM's own step register, every frame ---
prog.append(('set',F4,0,'pop_zero'))
for i in range(G):
    prog += [('rie',SB+i,1,f'pop_g{i}'),('inc',F4,1,f'pop_a{i}')]

T={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for i,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"',f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"',f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""',f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "true"',f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"',f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"',f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"',f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"',f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"',f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('rule110_population_melody.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> rule110_population_melody.cht (types {sorted(set(op for op,_,_,_ in prog))})")

Dafne corruptor

# The Dafne PLANE on the stock rule110.nes: the history scroll draws
#   bit(f, j) = threshold( (seed[f%5, j%5] + (j//5 + 7*(f//5)) * M[f%5, j%5]) mod 27 )
# i.e. screen row = plane row, 15 columns = block columns C=0,1,2 (a crop).
#
# State:  S[r][j] at 32+15r+j  (r=0..4, j=0..14): row r of current band
#         phi=110 (row shown this frame), INIT=111, w=112 (temp)
# Frame:  [init once] -> display row phi -> phi++ -> if phi==5: band += 7M, phi=0

Mx = [[0,4,8,3,7],[4,8,3,4,5],[8,3,7,5,3],[3,4,5,3,1],[7,5,3,1,0]]
def L2n(c): return 26 if c=='0' else ord(c)-65
SEED = [[L2n(c) for c in row] for row in ["DAFNE","ABRDN","FR0RF","NDRBA","ENFAD"]]
SB, PHI, INIT, W = 32, 110, 111, 112
adr = lambda r,j: SB + 15*r + j

prog = []
# --- init (gated INIT==0): S[r][j] = seed[r][p] + c*M[r][p] mod 27, phi=0 ---
for r in range(5):
    for j in range(15):
        c,p = j//5, j%5
        v = (SEED[r][p] + c*Mx[r][p]) % 27
        prog += [('rie',INIT,0,f'i_g{r}_{j}'), ('set',adr(r,j),v,f'i_v{r}_{j}')]
prog += [('rie',INIT,0,'i_phig'), ('set',PHI,0,'i_phi'), ('set',INIT,1,'i_done')]
# --- display row phi: D[j] = (S[phi][j] > 13) ---
for r in range(5):
    for j in range(15):
        prog += [('set',W,0,f'd_w0_{r}_{j}'),
                 ('rig',adr(r,j),13,f'd_thr_{r}_{j}'), ('set',W,1,f'd_w1_{r}_{j}'),
                 ('rne',PHI,r,f'd_kill_{r}_{j}'), ('set',W,0,f'd_wk_{r}_{j}'),
                 ('rie',PHI,r,f'd_clrg_{r}_{j}'), ('set',j,0,f'd_clr_{r}_{j}'),
                 ('rie',W,1,f'd_setg_{r}_{j}'), ('set',j,1,f'd_set_{r}_{j}')]
# --- advance phase; on wrap, advance band ---
prog += [('inc',PHI,1,'p_inc')]
for r in range(5):
    for j in range(15):
        m = Mx[r][j%5]
        if m == 0: continue
        step = (7*m) % 27
        prog += [('rie',PHI,5,f'b_g{r}_{j}'), ('inc',adr(r,j),step,f'b_a{r}_{j}'),
                 ('rig',adr(r,j),26,f'b_m{r}_{j}'), ('dec',adr(r,j),27,f'b_s{r}_{j}')]
prog += [('rie',PHI,5,'p_wrapg'), ('set',PHI,0,'p_wrap')]

T = {'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A = []
for i,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{i}_address = "{a}"', f'cheat{i}_address_bit_position = "0"',
          f'cheat{i}_big_endian = "false"', f'cheat{i}_cheat_type = "{T[op]}"',
          f'cheat{i}_code = ""', f'cheat{i}_desc = "{d}"',
          f'cheat{i}_enable = "true"', f'cheat{i}_handler = "1"',
          f'cheat{i}_memory_search_size = "3"',
          f'cheat{i}_repeat_add_to_address = "1"', f'cheat{i}_repeat_add_to_value = "0"',
          f'cheat{i}_repeat_count = "1"', f'cheat{i}_rumble_port = "0"',
          f'cheat{i}_rumble_primary_duration = "0"', f'cheat{i}_rumble_primary_strength = "0"',
          f'cheat{i}_rumble_secondary_duration = "0"', f'cheat{i}_rumble_secondary_strength = "0"',
          f'cheat{i}_rumble_type = "0"', f'cheat{i}_rumble_value = "0"',
          f'cheat{i}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('daffine.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> daffine.cht")

# ---------------- verify against the closed form ----------------
def cheat_pass(mem):
    skip=False
    for op,a,v,_ in prog:
        if skip: skip=False; continue
        if op=='set': mem[a]=v
        elif op=='inc': mem[a]=(mem[a]+v)&0xFF
        elif op=='dec': mem[a]=(mem[a]-v)&0xFF
        elif op=='rie': skip=(mem[a]!=v)
        elif op=='rne': skip=(mem[a]==v)
        elif op=='rig': skip=not(mem[a]>v)

def closed_form(f, j):
    r,p,c,R = f%5, j%5, j//5, f//5
    return (SEED[r][p] + (c + 7*R)*Mx[r][p]) % 27

mem=[0]*128; mem[7]=1     # ROM boot state
FR=400
rows=[]
ok=True
for f in range(FR):
    cheat_pass(mem)
    rows.append(mem[0:15])
    exp=[1 if closed_form(f,j)>13 else 0 for j in range(15)]
    if rows[-1]!=exp:
        ok=False; print(f"MISMATCH at plane row {f}: got {rows[-1]} want {exp}"); break
print(f"all {FR} plane rows match the closed form exactly: {ok}")
print("vertical period of the plane texture: 135 rows (5*27):",
      rows[:135]==rows[135:270])
print("\nfirst 25 plane rows (5 bands) as drawn:")
for f in range(25):
    print(('  R%d ' % (f//5) if f%5==0 else '     ') +
          ''.join('#' if b else '.' for b in rows[f]))

mod40 corruption

# HYBRID TERNARY / MOD-40 ORBIT -- pure display, no sound, no register pokes.
# Runs on the EXISTING rule110.nes (untouched display ROM).
#
# T[i] in {0,1,2}: cyclic rock-paper-scissors CA (excitable-medium rule --
#   cell advances to the next state if a neighbor already holds it).
# O[i] in Z_40 (mod 40, honoring your 39-symbol alphabet + sentinel-39 quirk):
#   each frame O[i] += WEIGHT[T[i]]  (T gates a DIFFERENT step speed per phase --
#   the "hybrid" part: ternary automaton driving an unrelated modulus).
#   Hitting the sentinel value 39 snaps O[i] back to 0 next step (echoing
#   takewhile's stop-at-39 behavior in your pixel decoder).
# Display: threshold(O[i] > 19).
G=15
S1=[(1,1,0),(1,0,1),(0,1,1),(0,1,0),(0,0,1)]         # cyclic CA advance set
T,NXT,TMP = 40,55,70
O,OTMP,INIT = 71,86,87
WEIGHT = [2,5,9]        # per-ternary-phase step, drawn from the spirit of S

SEED_T=[0,0,1,1,2,0,0,0,1,2,2,0,1,0,2]
prog=[]
for i in range(G): prog += [('rie',INIT,0,f'ti{i}g'),('set',T+i,SEED_T[i],f'ti{i}')]
prog += [('rie',INIT,0,'oinitg'),('set',INIT,1,'oinit')]  # O starts at 0 via the ROM's own RAM clear at boot; no cheat needed

# --- ternary CA: default copy-through, then advance-on-neighbor (AND-gate, 3-valued) ---
for i in range(G):
    for v in range(3): prog += [('rie',T+i,v,f'nxdef{i}_{v}g'),('set',NXT+i,v,f'nxdef{i}_{v}')]
for i in range(G):
    L,R=(i-1)%G,(i+1)%G
    for s in range(3):
        t=(s+1)%3
        others_s=[x for x in range(3) if x!=s]
        others_t=[x for x in range(3) if x!=t]
        for side,nbaddr in (('L',T+L),('R',T+R)):
            tag=f'adv{i}_{s}_{side}'
            prog.append(('set',TMP,1,f'{tag}_init'))
            for w in others_s:
                prog.append(('rie',T+i,w,f'{tag}_kis'))
                prog.append(('set',TMP,0,f'{tag}_kis_do'))
            for w in others_t:
                prog.append(('rie',nbaddr,w,f'{tag}_kin'))
                prog.append(('set',TMP,0,f'{tag}_kin_do'))
            prog.append(('rie',TMP,1,f'{tag}_chk'))
            prog.append(('set',NXT+i,t,f'{tag}_apply'))
for i in range(G):
    for v in range(3): prog += [('rie',NXT+i,v,f'cp{i}_{v}g'),('set',T+i,v,f'cp{i}_{v}')]

# --- mod-40 orbit: O[i] += WEIGHT[T[i]] mod 40, sentinel reset at 39 ---
for i in range(G):
    for phase,w in enumerate(WEIGHT):
        prog += [('rie',T+i,phase,f'ostep{i}_{phase}g'),('inc',O+i,w,f'ostep{i}_{phase}')]
    prog += [('rig',O+i,39,f'owrap{i}g'),('dec',O+i,40,f'owrap{i}')]  # true modulus: >39 -> subtract 40
    prog += [('rie',O+i,39,f'osent{i}g'),('set',O+i,0,f'osent{i}')]     # ==39 sentinel -> snap to 0

# --- display: threshold(O[i] > 19) ---
for i in range(G):
    prog += [('set',i,0,f'dc{i}'),('rig',O+i,19,f'dt{i}'),('set',i,1,f'ds{i}')]

T_={'set':'1','inc':'2','dec':'3','rie':'4','rne':'5','ril':'6','rig':'7'}
A=[]
for k,(op,a,v,d) in enumerate(prog):
    A += [f'cheat{k}_address = "{a}"',f'cheat{k}_address_bit_position = "0"',
          f'cheat{k}_big_endian = "false"',f'cheat{k}_cheat_type = "{T_[op]}"',
          f'cheat{k}_code = ""',f'cheat{k}_desc = "{d}"',
          f'cheat{k}_enable = "true"',f'cheat{k}_handler = "1"',
          f'cheat{k}_memory_search_size = "3"',
          f'cheat{k}_repeat_add_to_address = "1"',f'cheat{k}_repeat_add_to_value = "0"',
          f'cheat{k}_repeat_count = "1"',f'cheat{k}_rumble_port = "0"',
          f'cheat{k}_rumble_primary_duration = "0"',f'cheat{k}_rumble_primary_strength = "0"',
          f'cheat{k}_rumble_secondary_duration = "0"',f'cheat{k}_rumble_secondary_strength = "0"',
          f'cheat{k}_rumble_type = "0"',f'cheat{k}_rumble_value = "0"',
          f'cheat{k}_value = "{v}"']
A.append(f'cheats = "{len(prog)}"')
open('rule110_ternary40.cht','w').write('\n'.join(A))
print(f"{len(prog)} cheats -> rule110_ternary40.cht (types {sorted(set(op for op,_,_,_ in prog))})")

having a few ways to go can be helpful to unbreak states etc

Interpreters/Implementations

  • RetroArch, obviously.
  • Libretro, same thing.

Known Issues

If you have more than 6,000 cheats, the rest do not work, as they leak into other parts of the settings.

Categories (if this were in mainspace)

Category:Low-level