Skip to content

Instantly share code, notes, and snippets.

@nucular
Last active September 12, 2015 15:02
Show Gist options
  • Save nucular/1d1588bf787d4e869ba6 to your computer and use it in GitHub Desktop.
Save nucular/1d1588bf787d4e869ba6 to your computer and use it in GitHub Desktop.
BytePusher Nyan Cat
"""
Copyright (C) 2014 nucular
This work is free. You can redistribute it and/or modify it under the
terms of the Do What The Fuck You Want To Public License, Version 2,
as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
"""
import os, sys
import random
# ==== CONSTANTS =====
NOTICE = " by nucular, WTFPL License "
PAGE = 256
BANK = PAGE * PAGE
MEMSIZE = PAGE * BANK
SIZE_ADDR = 3
SIZE_INSTR = 3 * SIZE_ADDR
# Pointers
GFX = 0x02
S_RBOW = 19*19
S_STAR = 7*7
W_CAT = 33
H_CAT = 21
S_CAT = W_CAT * H_CAT
CAT1 = (GFX + 1) << 16
CAT2 = CAT1 + S_CAT
CAT3 = CAT2 + S_CAT
CAT4 = CAT3 + S_CAT
W_RBOW = 19
H_RBOW = 19
S_RBOW = W_RBOW * H_RBOW
RBOW1 = CAT4 + S_CAT
RBOW2 = RBOW1 + S_RBOW
W_STAR = 7
H_STAR = 7
S_STAR = W_STAR * H_STAR
STAR1 = RBOW2 + S_RBOW
STAR2 = STAR1 + S_STAR
STAR3 = STAR2 + S_STAR
STAR4 = STAR3 + S_STAR
# ==== UTILITY FUNCTIONS ====
pos = 0
size = 0
def prev(n=1):
return pos - SIZE_INSTR * n
def next(n=1):
return pos + SIZE_INSTR * n
def tell():
assert pos == fp.tell()
return pos
def seek(v):
global fp, pos, size
if v > size:
fp.seek(size)
fp.write("\x00"*(v-size))
size = v
else:
fp.seek(v)
pos = v
assert pos == fp.tell()
# Byte writing
def w1(v):
"""Write one byte"""
global fp, pos, size
fp.write(chr(v & 255))
pos += 1
if pos > size:
size = pos
def w2(v):
"""Write two bytes"""
w1(v>>8)
w1(v)
def w3(v):
"""Write three bytes"""
w1(v>>16)
w2(v)
def op(a, b, c):
"""Write an instruction"""
w3(a)
w3(b)
w3(c)
# Basic instructions
def nop():
"""Do nothing but fill 9 bytes"""
op(0, 0, next())
def jmp(adr):
"""Jump to an adress"""
op(0, 0, adr)
def mov(a, b):
"""Copy a byte from a to b"""
op(a, b, next())
# Simple drawing system
def wspr(adr, data):
"""Write a base64-encoded, zlib-ed sprite statically into the program"""
global size
data = data.decode("base64").decode("zlib")
lp = tell()
seek(adr)
fp.write(data)
if size < adr + len(data):
size = adr + len(data)
seek(lp)
def ldspr(adr, x, y, w, h):
"""Draw a sprite at x,y"""
lp = tell()
i = 0
for yp in xrange(h):
for xp in xrange(w):
if x + xp < 256 and y + yp < 256:
t = GFX<<16 | y + yp<<8 | x + xp
mov(adr + i, t)
i += 1
def wfill(col):
"""Fill the graphics bank statically"""
global size
lp = tell()
seek(GFX << 16)
fp.write(chr(col) * 0x10000)
if size < (GFX << 16) + 0x10000:
size = (GFX << 16) + 0x10000
seek(lp)
# Other
def ldpc(v):
"""Set the program counter"""
jmp(next() + 3)
lp = tell()
w3(v)
mov(lp, 2)
mov(lp + 1, 3)
mov(lp + 2, 4)
def wait():
"""Wait for the next frame"""
# Set the program counter behind an infinite loop
ldpc(next(6) + 3)
lp = tell()
nop()
jmp(lp)
# ==== MAIN FUNCTION ====
def assemble():
"""Write the actual byte code to the program"""
global pos, size
# Zeropage
w2(0) # Keycodes
w3(8 + len(NOTICE)) # Program counter
w1(GFX) # Graphics pointer
w2(0xFEFF) # Sound pointer
fp.write(NOTICE)
pos = pos + len(NOTICE)
size = size + len(NOTICE)
# Generate star positions
stars = []
for i in xrange(10):
x, y = random.randint(0, 250), random.randint(0, 250)
# Avoid placing stars on the rainbow
while y >= 90 and y <= 160:
y = random.randint(0, 250)
# The third value is the frame number
stars.append([x, y, random.randint(0, 3)])
def frame(cat, rbow, xoff):
"""Draw a frame, xoff is the x-offset of the rainbow"""
for x in xrange(0, 100, 17):
ldspr(rbow, x + xoff, 118, W_RBOW, H_RBOW)
ldspr(cat, 104, 118, W_CAT, H_CAT)
for i, v in enumerate(stars):
s = globals()["STAR" + str(v[2] + 1)]
# Repeat the star animation
v[2] = (v[2] + 1) % 4
ldspr(s, v[0], v[1], W_STAR, H_STAR)
# Wait four frames
for i in xrange(4):
wait()
# Fill the graphics bank with the blue background color
wfill(8)
# Main loop
lp = tell()
frame(CAT1, RBOW1, 0)
frame(CAT2, RBOW1, -1)
frame(CAT3, RBOW2, 0)
frame(CAT4, RBOW2, -1)
jmp(lp)
# Statically write the sprite data at the end of the program
wspr(CAT1, SPR_CAT1)
wspr(CAT2, SPR_CAT2)
wspr(CAT3, SPR_CAT3)
wspr(CAT4, SPR_CAT4)
wspr(RBOW1, SPR_RBOW1)
wspr(RBOW2, SPR_RBOW2)
wspr(STAR1, SPR_STAR1)
wspr(STAR2, SPR_STAR2)
wspr(STAR3, SPR_STAR3)
wspr(STAR4, SPR_STAR4)
# ==== SPRITE DATA ====
SPR_CAT1 =\
"eJyFkkEOhCAMRf+yN+A+XsETeSbdujUhLkg40fBbasGZZH4IUvpaW+A4mkQEb0nXYUJ5CxNA/z0q\
iIsiYI5ThxIjcKG4I3J8AS5A4QZopZUaAWwbLANX1l6tiBLathJFFw1oM+qcgVuwLzOoWW6vUh2u\
BixGeIZz8pNYjCjeJYHdqF2rTCkBkcFq1GmDA0RKNJHz84OcO5B41F5jAxjKQWCl0nBZ6F2iH5QC\
LCLueepSgSHo96PRk1gtYzflsaQ/N53DFon4P/oA1McQ/w=="
SPR_CAT2 =\
"eJx9kssNxCAMROdIB+knLaSi1LRcc42E9mCJitYe8zHZ1Y4QH83D2EDOOScVnkpN2QV5Cgtg/jtq\
EreKgBsXG4kA3DekGzPGFzAEGGyApVpVK4DzhEewGYFaMVMwwAgBJ6kCAWgR1AB8ZJgGXAHockDV\
I1yrr+bukWRUaeuXUzZg2+ywkANTY8dBAUMa4EWUMg4ohYAiIjNHBWyrNQUOFQFZz4AXinRQiM8d\
6tCrdn/s+f1rCLDzNfoF9V+VnJ07OAvf7q8+gSke8g=="
SPR_CAT3 =\
"eJxtkssNwyAQROdIB+nHLbgiakquXC2hHJBcUZhZPgvxCJnF89jlF0LVpyk8qHvYtQEou+CBi/7X\
axKXhO4nNREb0I2ZwwM3fAGAMAEu9ZY8gBhhGRhtQDKARIECA44tQzUA68MhCUgO6GoARoa0+pWg\
CwLJVXgbxa6XKssSbH3qLOwlbBM5jwI548UCJ496rLECnMpWgVPyl9Vy2kYRGuCv2+2jHrX8gPj3\
YJZXY6euz9OQfwTPKYps+AOAwicg"
SPR_CAT4 =\
"eJx1klEOhCAMROezN9j7eAVO5Jl2f/k1Ifth4om2MxWsxJ2gBeZRKGpm9jllT+omZk0A9llIwLbR\
/2ZdxCah+1VNxAR048qRgONA3gAgTIBHPaQMYF0RGdibgBoAiR3qCFiWKYMbQERbJAE1AV0BON0z\
1LvvBG0P+6iSwDsoBrxiq/12hDifQjx9iyiitbFBawJK4VWPMzrAWTYHCtwv+WPhrFKFwoqE/LlT\
HX7VtA1jzZ+/Rrn0ehpyRuy1Qr1z+AP+EyfK"
SPR_RBOW1 = "eJzbsgUCOKBgC7HgGBSg8wmCS1CAzicInKAAnU8QiEMBOp8gCIYCdD5BAAtNJD4AovGp0Q=="
SPR_RBOW2 = "eJzj4ICALVCAxCQOHIMCJCZx4BIUIDGJA05QgMQkDohDARKTOBAMBUhM4gA0cDmATADHO6nR"
SPR_STAR1 = "eJzj4OC4zoEOriOR6OIAd1UExQ=="
SPR_STAR2 = "eJzj4OC4zsEBIcAMDjgDhQtXBADIMQgB"
SPR_STAR3 = "eJzj4ICB60jU9esghCYIAQDIMQgB"
SPR_STAR4 = "eJzj4MAOruMQ5wAAOrACWA=="
# ==== ENTRY POINT ====
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: {0} PATH".format(os.path.relpath(sys.argv[0])))
else:
fp = open(sys.argv[1], "wb")
assemble()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment