mirror of
https://github.com/robmcmullen/asmgen.git
synced 2025-05-12 17:43:28 +00:00
Rename to asmgen; added WIP for RLE compression
This commit is contained in:
parent
8ab52b1722
commit
4f8cf05853
30
Makefile
30
Makefile
@ -3,36 +3,36 @@ BWSPRITE = apple-sprite9x11.png
|
||||
|
||||
all: cpbg.dsk fonttest.dsk
|
||||
|
||||
rowlookup.s: quicksprite.py
|
||||
python quicksprite.py -a mac65 -p 6502 -r > rowlookup.s
|
||||
rowlookup.s: asmgen.py
|
||||
python asmgen.py -a mac65 -p 6502 -r > rowlookup.s
|
||||
|
||||
collookupbw.s: quicksprite.py
|
||||
python quicksprite.py -a mac65 -p 6502 -s hgrbw -c > collookupbw.s
|
||||
collookupbw.s: asmgen.py
|
||||
python asmgen.py -a mac65 -p 6502 -s hgrbw -c > collookupbw.s
|
||||
|
||||
collookupcolor.s: quicksprite.py
|
||||
python quicksprite.py -a mac65 -p 6502 -c > collookupcolor.s
|
||||
collookupcolor.s: asmgen.py
|
||||
python asmgen.py -a mac65 -p 6502 -c > collookupcolor.s
|
||||
|
||||
bwsprite.s: quicksprite.py collookupbw.s rowlookup.s $(BWSPRITE)
|
||||
python quicksprite.py -a mac65 -p 6502 -s hgrbw $(BWSPRITE) -n bwsprite -m -b > bwsprite.s
|
||||
bwsprite.s: asmgen.py collookupbw.s rowlookup.s $(BWSPRITE)
|
||||
python asmgen.py -a mac65 -p 6502 -s hgrbw $(BWSPRITE) -n bwsprite -m -b > bwsprite.s
|
||||
|
||||
colorsprite.s: quicksprite.py collookupcolor.s rowlookup.s $(COLORSPRITE)
|
||||
python quicksprite.py -a mac65 -p 6502 -s hgrcolor $(COLORSPRITE) -n colorsprite -m > colorsprite.s
|
||||
colorsprite.s: asmgen.py collookupcolor.s rowlookup.s $(COLORSPRITE)
|
||||
python asmgen.py -a mac65 -p 6502 -s hgrcolor $(COLORSPRITE) -n colorsprite -m > colorsprite.s
|
||||
|
||||
bwtest.dsk: quicksprite.py bwtest.s bwsprite.s
|
||||
bwtest.dsk: asmgen.py bwtest.s bwsprite.s
|
||||
atasm -obwtest.xex bwtest.s -Lbwtest.var -gbwtest.lst
|
||||
atrcopy bwtest.dsk boot -b bwtest.xex --brun 6000 -f
|
||||
|
||||
colortest.dsk: quicksprite.py colortest.s bwsprite.s
|
||||
colortest.dsk: asmgen.py colortest.s bwsprite.s
|
||||
atasm -ocolortest.xex colortest.s -Lcolortest.var -gcolortest.lst
|
||||
atrcopy colortest.dsk boot -b colortest.xex --brun 6000 -f
|
||||
|
||||
cpbg-sprite-driver.s: quicksprite.py $(BWSPRITE)
|
||||
python quicksprite.py -a mac65 -p 6502 -s hgrbw -m -k -d -g -f fatfont128.dat -o cpbg $(BWSPRITE) $(COLORSPRITE)
|
||||
cpbg-sprite-driver.s: asmgen.py $(BWSPRITE)
|
||||
python asmgen.py -a mac65 -p 6502 -s hgrbw -m -k -d -g -f fatfont128.dat -o cpbg $(BWSPRITE) $(COLORSPRITE)
|
||||
|
||||
cpbg.xex: cpbg.s cpbg-sprite-driver.s
|
||||
atasm -mae -ocpbg.xex cpbg.s -Lcpbg.var -gcpbg.lst
|
||||
|
||||
cpbg.dsk: quicksprite.py cpbg.xex
|
||||
cpbg.dsk: asmgen.py cpbg.xex
|
||||
atrcopy cpbg.dsk boot -b cpbg.xex --brun 6000 -f
|
||||
|
||||
fonttest.dsk: fonttest.s fatfont.s
|
||||
|
17
README.rst
17
README.rst
@ -1,6 +1,6 @@
|
||||
|
||||
===========
|
||||
Quicksprite
|
||||
AsmGen
|
||||
===========
|
||||
|
||||
|
||||
@ -8,8 +8,8 @@ Quicksprite
|
||||
Abstract
|
||||
========
|
||||
|
||||
Quicksprite - sprite compiler and related utilities for Apple ][ hi-res and
|
||||
Atari 8-bit software sprites
|
||||
AsmGen - code generator for sprites, fonts, and images for Apple ][ hi-res and
|
||||
Atari 8-bit computers
|
||||
|
||||
This program creates 6502 (or 65c02) code that draws sprites using unrolled
|
||||
loops for each shifted shape. By removing data shifts, image lookups, and loops
|
||||
@ -34,10 +34,17 @@ Prerequisites
|
||||
* pypng
|
||||
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
The sample font is modified from Michael Pohoreski's excellent tutorial on
|
||||
Apple II fonts: https://github.com/Michaelangel007/apple2_hgr_font_tutorial
|
||||
|
||||
|
||||
Disclaimer
|
||||
==========
|
||||
|
||||
Quicksprite, the 6502 sprite compiler sponsored by the Player/Missile Podcast
|
||||
AsmGen, the 6502 code generator sponsored by the Player/Missile Podcast
|
||||
Copyright (c) 2017 Rob McMullen (feedback@playermissile.com)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -58,7 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Generated Code License
|
||||
----------------------
|
||||
|
||||
While the code for quicksprite itself is licensed under the GPLv3, the code it
|
||||
While the code for AsmGen itself is licensed under the GPLv3, the code it
|
||||
produces is licensed under the the Creative Commons Attribution 4.0
|
||||
International (CC BY 4.0), so you are free to use the generated code for
|
||||
commercial or non-commercial purposes.
|
||||
|
@ -11,6 +11,8 @@ import png # package name is "pypng" on pypi.python.org
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
|
||||
def slugify(s):
|
||||
"""Simplifies ugly strings into something that can be used as an assembler
|
||||
label.
|
||||
@ -1242,16 +1244,130 @@ class FastScroll(Listing):
|
||||
self.out()
|
||||
|
||||
|
||||
class RLE(Listing):
|
||||
def __init__(self, assembler, data):
|
||||
Listing.__init__(self, assembler)
|
||||
self.raw = np.asarray(data, dtype=np.uint8)
|
||||
rle = self.calc_rle()
|
||||
#c1 = self.compress_pcx(rle)
|
||||
#c2 = self.compress_high_bit_swap(rle)
|
||||
c3 = self.compress_run_copy(rle)
|
||||
|
||||
def calc_rle(self):
|
||||
""" run length encoding. Partial credit to R rle function.
|
||||
Multi datatype arrays catered for including non Numpy
|
||||
returns: tuple (runlengths, startpositions, values) """
|
||||
ia = self.raw
|
||||
n = len(ia)
|
||||
if n == 0:
|
||||
return ([], [], [])
|
||||
else:
|
||||
y = np.array(ia[1:] != ia[:-1]) # pairwise unequal (string safe)
|
||||
i = np.append(np.where(y), n - 1) # must include last element posi
|
||||
z = np.diff(np.append(-1, i)) # run lengths
|
||||
p = np.cumsum(np.append(0, z))[:-1] # positions
|
||||
return(z, p, ia[i])
|
||||
|
||||
def compress_pcx(self, rle):
|
||||
run_lengths, pos, values = rle
|
||||
|
||||
# Max size will be the same size as the original data. If compressed is
|
||||
# greater than original, abort
|
||||
compressed = np.empty(len(self.raw), dtype=np.uint8)
|
||||
|
||||
compressed_size = 0
|
||||
for i in range(len(run_lengths)):
|
||||
p, r, v = pos[i], run_lengths[i], values[i]
|
||||
if v < 0x80:
|
||||
num_tokens = 1
|
||||
else:
|
||||
num_tokens = 2
|
||||
print("%d: run %d of %d (%d)" % (p, r, v, num_tokens))
|
||||
compressed_size += num_tokens
|
||||
print("compressed size: %d" % compressed_size)
|
||||
return compressed_size
|
||||
|
||||
def compress_high_bit_swap(self, rle):
|
||||
run_lengths, pos, values = rle
|
||||
|
||||
# Max size will be the same size as the original data. If compressed is
|
||||
# greater than original, abort
|
||||
compressed = np.empty(len(self.raw), dtype=np.uint8)
|
||||
|
||||
compressed_size = 0
|
||||
high_bit_set = False
|
||||
for i in range(len(run_lengths)):
|
||||
p, r, v = pos[i], run_lengths[i], values[i]
|
||||
changed = high_bit_set
|
||||
if r == 1:
|
||||
if v < 0x80 and not high_bit_set:
|
||||
num_tokens = 1
|
||||
elif v > 0x80 and high_bit_set:
|
||||
num_tokens = 1
|
||||
elif v == 0x80:
|
||||
num_tokens = 2
|
||||
else:
|
||||
high_bit_set = not high_bit_set
|
||||
num_tokens = 2
|
||||
else:
|
||||
num_tokens = 2
|
||||
print("%d: run %d of %d (%d)%s" % (p, r, v, num_tokens, "" if changed == high_bit_set else " high bit = %s" % high_bit_set))
|
||||
compressed_size += num_tokens
|
||||
print("compressed size: %d" % compressed_size)
|
||||
return compressed_size
|
||||
|
||||
def compress_run_copy(self, rle):
|
||||
run_lengths, pos, values = rle
|
||||
|
||||
# Max size will be the same size as the original data. If compressed is
|
||||
# greater than original, abort
|
||||
compressed = np.empty(len(self.raw), dtype=np.uint8)
|
||||
|
||||
compressed_size = 0
|
||||
copy_start = -1
|
||||
for i in range(len(run_lengths)):
|
||||
p, r, v = pos[i], run_lengths[i], values[i]
|
||||
if r < 3:
|
||||
if copy_start < 0:
|
||||
copy_start = p
|
||||
elif copy_start - p + r > 127:
|
||||
num = p - copy_start
|
||||
compressed_size += num + 1
|
||||
print("%d: copy %d (%d)" % (copy_start, num, num + 1))
|
||||
copy_start = p
|
||||
else:
|
||||
if copy_start >= 0:
|
||||
num = p - copy_start
|
||||
compressed_size += num + 1
|
||||
print("%d: copy %d (%d)" % (copy_start, num, num + 1))
|
||||
copy_start = -1
|
||||
while r > 2:
|
||||
num = min(r, 128)
|
||||
print("%d: run %d of %d (2)" % (p, num, v))
|
||||
p += num
|
||||
r -= num
|
||||
compressed_size += 2
|
||||
if r > 0:
|
||||
copy_start = p
|
||||
if copy_start >= 0:
|
||||
num = p - copy_start
|
||||
compressed_size += num + 1
|
||||
print("%d: copy %d (%d)" % (copy_start, num, num + 1))
|
||||
print("compressed size: %d" % compressed_size)
|
||||
return compressed_size
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
disclaimer = '''; AUTOGENERATED FILE; DO NOT EDIT!
|
||||
;
|
||||
; This file was generated by quicksprite.py, a sprite compiler sponsored by
|
||||
; the Player/Missile Podcast (based on HiSprite by Quinn Dunki).
|
||||
; This file was generated by asmgen.py, a 6502 code generator sponsored by
|
||||
; the Player/Missile Podcast. (The sprite compiler is based on HiSprite by
|
||||
; Quinn Dunki).
|
||||
;
|
||||
; The code produced by quicksprite is licensed under the Creative Commons
|
||||
; Attribution 4.0 International (CC BY 4.0), so you are free to use the code
|
||||
; in this file for commercial or non-commercial purposes. (The code generator
|
||||
; itself is licensed under the GPLv3.)
|
||||
; The code produced by asmgen is licensed under the Creative Commons
|
||||
; Attribution 4.0 International (CC BY 4.0), so you are free to use the code in
|
||||
; this file for any purpose. (The code generator itself is licensed under the
|
||||
; GPLv3.)
|
||||
'''
|
||||
|
||||
parser = argparse.ArgumentParser(description="Sprite compiler for 65C02/6502 to generate assembly code to render all shifts of the given sprite, optionally with exclusive-or drawing (if background will be non-black). Generated code has conditional compilation directives for the CC65 assembler to allow the same file to be compiled for either architecture.")
|
||||
@ -1265,8 +1381,9 @@ if __name__ == "__main__":
|
||||
parser.add_argument("-p", "--processor", default="any", choices=["any","6502", "65C02"], help="Processor type (default: %(default)s)")
|
||||
parser.add_argument("-s", "--screen", default="hgrcolor", choices=["hgrcolor","hgrbw"], help="Screen format (default: %(default)s)")
|
||||
parser.add_argument("-i", "--image", default="line", choices=["line", "color","bw"], help="Screen format used for full page image conversion (default: %(default)s)")
|
||||
parser.add_argument("-l", "--scroll", default=1, type=int, help="Unrolled loop to scroll screen (default: %(default)s)")
|
||||
parser.add_argument("-l", "--scroll", default=0, type=int, help="Unrolled loop to scroll screen (default: %(default)s)")
|
||||
parser.add_argument("--merge", type=int, nargs="*", help="Merge two HGR images, switching images at the scan line")
|
||||
parser.add_argument("--rle", action="store_true", default=False, help="Create run-length-encoded version of data (assumed to be an image)")
|
||||
parser.add_argument("-n", "--name", default="", help="Name for generated assembly function (default: based on image filename)")
|
||||
parser.add_argument("-k", "--clobber", action="store_true", default=False, help="don't save the registers on the stack")
|
||||
parser.add_argument("-d", "--double-buffer", action="store_true", default=False, help="add code blit to either page (default: page 1 only)")
|
||||
@ -1310,31 +1427,37 @@ if __name__ == "__main__":
|
||||
sys.exit(0)
|
||||
|
||||
for pngfile in options.files:
|
||||
try:
|
||||
reader = png.Reader(pngfile)
|
||||
pngdata = reader.asRGB8()
|
||||
except RuntimeError, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
except png.Error, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
if pngfile.lower().endswith(".png"):
|
||||
try:
|
||||
reader = png.Reader(pngfile)
|
||||
pngdata = reader.asRGB8()
|
||||
except RuntimeError, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
except png.Error, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
|
||||
name = options.name if options.name else os.path.splitext(pngfile)[0]
|
||||
slug = slugify(name)
|
||||
name = options.name if options.name else os.path.splitext(pngfile)[0]
|
||||
slug = slugify(name)
|
||||
|
||||
w, h = pngdata[0:2]
|
||||
if w == 280 and h == 192:
|
||||
# Full screen conversion!
|
||||
Image(pngdata, name, options.image.lower())
|
||||
w, h = pngdata[0:2]
|
||||
if w == 280 and h == 192:
|
||||
# Full screen conversion!
|
||||
Image(pngdata, name, options.image.lower())
|
||||
else:
|
||||
sprite_code = Sprite(slug, pngdata, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.double_buffer, options.damage, options.processor)
|
||||
listings.append(sprite_code)
|
||||
if options.output_prefix:
|
||||
r = RowLookup(assembler, screen)
|
||||
luts[r.slug] = r
|
||||
c = ColLookup(assembler, screen)
|
||||
luts[c.slug] = c
|
||||
else:
|
||||
sprite_code = Sprite(slug, pngdata, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.double_buffer, options.damage, options.processor)
|
||||
listings.append(sprite_code)
|
||||
if options.output_prefix:
|
||||
r = RowLookup(assembler, screen)
|
||||
luts[r.slug] = r
|
||||
c = ColLookup(assembler, screen)
|
||||
luts[c.slug] = c
|
||||
data = np.fromfile(pngfile, dtype=np.uint8)
|
||||
if options.rle:
|
||||
listings.append(RLE(assembler, data))
|
||||
|
||||
|
||||
listings.extend([luts[k] for k in sorted(luts.keys())])
|
||||
|
8
setup.py
8
setup.py
@ -8,15 +8,15 @@ except ImportError:
|
||||
with open("README.rst", "r") as fp:
|
||||
long_description = fp.read()
|
||||
|
||||
scripts = ["quicksprite.py"]
|
||||
scripts = ["asmgen.py"]
|
||||
|
||||
setup(name="quicksprite",
|
||||
setup(name="asmgen",
|
||||
version="1.0",
|
||||
author="Rob McMullen",
|
||||
author_email="feedback@playermissile.com",
|
||||
url="https://github.com/robmcmullen/quicksprite",
|
||||
url="https://github.com/robmcmullen/asmgen",
|
||||
scripts=scripts,
|
||||
description="Sprite compiler for Apple ][ and Atari 8-bit",
|
||||
description="6502 code generator for Apple ][ and Atari 8-bit",
|
||||
long_description=long_description,
|
||||
license="GPL",
|
||||
classifiers=[
|
||||
|
Loading…
x
Reference in New Issue
Block a user