Added processor and assembler syntax options

* added complete lookup table generation
This commit is contained in:
Rob McMullen 2017-06-20 19:55:47 -07:00
parent d02300573d
commit 3afe35febe
1 changed files with 142 additions and 34 deletions

View File

@ -56,13 +56,47 @@ def slugify(s):
return s
class Syntax(object):
def asm(self, text):
return "\t%s" % text
def comment(self, text):
return "\t; %s" % text
def label(self, text):
return text
def byte(self, text):
return self.asm(".byte %s" % text)
def word(self, text):
return self.asm(".word %s" % text)
def address(self, text):
return self.asm(".addr %s" % text)
def origin(self, text):
return self.asm("*= %s" % text)
class Mac65(Syntax):
def address(self, text):
return self.asm(".word %s" % text)
class CC65(Syntax):
def label(self, text):
return "%s:" % text
class Listing(object):
disclaimer = '''
; This file was generated by HiSprite.py, a sprite compiler by Quinn Dunki.
; If you feel the need to modify this file, you are probably doing it wrong.
'''
def __init__(self):
def __init__(self, assembler):
self.assembler = assembler
self.lines = [self.disclaimer]
def __str__(self):
@ -71,10 +105,31 @@ class Listing(object):
def out(self, line):
self.lines.append(line)
def out_append_last(self, line):
self.lines[-1] += line
def label(self, text):
self.out(self.assembler.label(text))
def comment(self, text):
self.out_append_last(self.assembler.comment(text))
def asm(self, text):
self.out(self.assembler.asm(text))
def addr(self, text):
self.out(self.assembler.address(text))
def byte(self, text):
self.out(self.assembler.byte(text))
def word(self, text):
self.out(self.assembler.word(text))
class Sprite(Listing):
def __init__(self, pngfile, xdraw=False):
Listing.__init__(self)
def __init__(self, pngfile, assembler, xdraw=False, processor="any"):
Listing.__init__(self, assembler)
reader = png.Reader(pngfile)
try:
@ -83,6 +138,7 @@ class Sprite(Listing):
raise RuntimeError
self.xdraw = xdraw
self.processor = processor
self.niceName = slugify(os.path.splitext(pngfile)[0])
self.width = pngdata[0]
self.height = pngdata[1]
@ -98,32 +154,46 @@ class Sprite(Listing):
def jumpTable(self):
# Prologue
self.out("%s: ;%d bytes per row" % (self.niceName, self.byteWidth))
self.out("\tSAVE_AXY")
self.out("\tldy PARAM0")
self.out("\tldx MOD7_2,y")
self.out(".ifpC02")
self.out("\tjmp (%s_JMP,x)\n" % (self.niceName))
self.label("%s" % self.niceName)
self.comment("%d bytes per row" % self.byteWidth)
self.asm("SAVE_AXY")
self.asm("ldy PARAM0")
self.asm("ldx MOD7_2,y")
if self.processor == "any":
self.out(".ifpC02")
self.jump65C02()
self.out(".else")
self.jump6502()
self.out(".endif")
elif self.processor == "65C02":
self.jump65C02()
elif self.processor == "6502":
self.jump6502()
else:
raise RuntimeError("Processor %s not supported" % self.processor)
def jump65C02(self):
self.asm("jmp (%s_JMP,x)\n" % (self.niceName))
offset_suffix = ""
# Bit-shift jump table for 65C02
self.out("%s_JMP:" % (self.niceName) )
self.label("%s_JMP" % (self.niceName))
for shift in range(self.numShifts):
self.out("\t.addr %s_SHIFT%d" % (self.niceName, shift))
self.addr("%s_SHIFT%d" % (self.niceName, shift))
self.out(".else")
def jump6502(self):
# Fast jump table routine; faster and smaller than self-modifying code
self.out("\tlda %s_JMP+1,x" % (self.niceName))
self.out("\tpha")
self.out("\tlda %s_JMP,x" % (self.niceName))
self.out("\tpha")
self.out("\trts\n")
self.asm("lda %s_JMP+1,x" % (self.niceName))
self.asm("pha")
self.asm("lda %s_JMP,x" % (self.niceName))
self.asm("pha")
self.asm("rts\n")
# Bit-shift jump table for generic 6502
self.out("%s_JMP:" % (self.niceName))
self.label("%s_JMP" % (self.niceName))
for shift in range(self.numShifts):
self.out("\t.addr %s_SHIFT%d-1" % (self.niceName,shift))
self.out(".endif")
self.addr("%s_SHIFT%d-1" % (self.niceName,shift))
def blitShift(self, shift):
# Blitting functions
@ -133,8 +203,8 @@ class Sprite(Listing):
# SAVE_AXY + RESTORE_AXY + rts + sprite jump table
cycleCount = 9 + 12 + 6 + 3 + 4 + 6
self.out("%s_SHIFT%d:" % (self.niceName,shift))
self.out("\tldx PARAM1")
self.label("%s_SHIFT%d" % (self.niceName,shift))
self.asm("ldx PARAM1")
cycleCount += 3
rowStartCode,extraCycles = self.rowStartCalculatorCode();
self.out(rowStartCode)
@ -347,18 +417,45 @@ def pixelColor(pixelData,row,col):
class HorizontalLookup(Listing):
def __init__(self):
Listing.__init__(self)
def __init__(self, assembler):
Listing.__init__(self, assembler)
self.generate_hgr()
self.generate_tables()
def generate_tables(self):
self.out("DIV7_2:")
for pixel in range(140):
self.out("\t.byte $%02x" % ((pixel / 7)*2))
def generate_hgr(self):
offsets = []
for y in range(192):
# From Apple Graphics and Arcade Game Design
a = y // 64
d = y - (64 * a)
b = d // 8
c = d - 8 * b
offsets.append((1024 * c) + (128 * b) + (40 * a))
self.out("\n\nMOD7_2:")
self.label("HGRROWS_H1")
for y in range(192):
addr = 0x2000 + offsets[y]
self.byte("$%02x" % (addr // 256))
self.label("HGRROWS_H2")
for y in range(192):
addr = 0x4000 + offsets[y]
self.byte("$%02x" % (addr // 256))
self.label("HGRROWS_L")
for y in range(192):
addr = offsets[y]
self.byte("$%02x" % (addr & 0xff))
def generate_tables(self):
self.label("DIV7_2")
for pixel in range(140):
self.out("\t.byte $%02x" % ((pixel % 7)*2))
self.byte("$%02x" % ((pixel / 7)*2))
self.out("\n")
self.label("MOD7_2")
for pixel in range(140):
self.byte("$%02x" % ((pixel % 7)*2))
if __name__ == "__main__":
@ -366,15 +463,26 @@ if __name__ == "__main__":
parser.add_argument("-v", "--verbose", default=0, action="count")
parser.add_argument("-t", "--tables", action="store_true", default=False, help="output only lookup tables for horizontal sprite shifts (division and modulus 7)")
parser.add_argument("-x", "--xdraw", action="store_true", default=False, help="use XOR for sprite drawing")
parser.add_argument("-s", "--syntax", default="cc65", nargs=1, choices=["cc65","mac65"], help="Assembler syntax (default: %(default)s)")
parser.add_argument("-p", "--processor", default="any", nargs=1, choices=["any","6502", "65C02"], help="Processor type (default: %(default)s)")
parser.add_argument("files", metavar="IMAGE", nargs="*", help="a PNG image [or a list of them]. PNG files must not have an alpha channel!")
options, extra_args = parser.parse_known_args()
if options.syntax[0].lower() == "cc65":
syntax = CC65()
elif options.syntax[0].lower() == "mac65":
syntax = Mac65()
else:
print("Unknown assembler %s" % options.syntax)
parser.print_help()
if options.tables:
print HorizontalLookup()
print HorizontalLookup(syntax)
exit(0)
for pngfile in options.files:
try:
print Sprite(pngfile, options.xdraw)
except RuntimeError:
parser.usage()
print Sprite(pngfile, syntax, options.xdraw, options.processor[0])
except RuntimeError, e:
print e
parser.print_help()