Last active
October 15, 2024 05:24
-
-
Save nucular/1d1588bf787d4e869ba6 to your computer and use it in GitHub Desktop.
BytePusher Nyan Cat
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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
awesome!