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

View File

@ -56,13 +56,47 @@ def slugify(s):
return 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): class Listing(object):
disclaimer = ''' disclaimer = '''
; This file was generated by HiSprite.py, a sprite compiler by Quinn Dunki. ; 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. ; 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] self.lines = [self.disclaimer]
def __str__(self): def __str__(self):
@ -71,10 +105,31 @@ class Listing(object):
def out(self, line): def out(self, line):
self.lines.append(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): class Sprite(Listing):
def __init__(self, pngfile, xdraw=False): def __init__(self, pngfile, assembler, xdraw=False, processor="any"):
Listing.__init__(self) Listing.__init__(self, assembler)
reader = png.Reader(pngfile) reader = png.Reader(pngfile)
try: try:
@ -83,6 +138,7 @@ class Sprite(Listing):
raise RuntimeError raise RuntimeError
self.xdraw = xdraw self.xdraw = xdraw
self.processor = processor
self.niceName = slugify(os.path.splitext(pngfile)[0]) self.niceName = slugify(os.path.splitext(pngfile)[0])
self.width = pngdata[0] self.width = pngdata[0]
self.height = pngdata[1] self.height = pngdata[1]
@ -98,32 +154,46 @@ class Sprite(Listing):
def jumpTable(self): def jumpTable(self):
# Prologue # Prologue
self.out("%s: ;%d bytes per row" % (self.niceName, self.byteWidth)) self.label("%s" % self.niceName)
self.out("\tSAVE_AXY") self.comment("%d bytes per row" % self.byteWidth)
self.out("\tldy PARAM0") self.asm("SAVE_AXY")
self.out("\tldx MOD7_2,y") self.asm("ldy PARAM0")
self.out(".ifpC02") self.asm("ldx MOD7_2,y")
self.out("\tjmp (%s_JMP,x)\n" % (self.niceName))
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 = "" offset_suffix = ""
# Bit-shift jump table for 65C02 # Bit-shift jump table for 65C02
self.out("%s_JMP:" % (self.niceName) ) self.label("%s_JMP" % (self.niceName))
for shift in range(self.numShifts): 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 # Fast jump table routine; faster and smaller than self-modifying code
self.out("\tlda %s_JMP+1,x" % (self.niceName)) self.asm("lda %s_JMP+1,x" % (self.niceName))
self.out("\tpha") self.asm("pha")
self.out("\tlda %s_JMP,x" % (self.niceName)) self.asm("lda %s_JMP,x" % (self.niceName))
self.out("\tpha") self.asm("pha")
self.out("\trts\n") self.asm("rts\n")
# Bit-shift jump table for generic 6502 # 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): for shift in range(self.numShifts):
self.out("\t.addr %s_SHIFT%d-1" % (self.niceName,shift)) self.addr("%s_SHIFT%d-1" % (self.niceName,shift))
self.out(".endif")
def blitShift(self, shift): def blitShift(self, shift):
# Blitting functions # Blitting functions
@ -133,8 +203,8 @@ class Sprite(Listing):
# SAVE_AXY + RESTORE_AXY + rts + sprite jump table # SAVE_AXY + RESTORE_AXY + rts + sprite jump table
cycleCount = 9 + 12 + 6 + 3 + 4 + 6 cycleCount = 9 + 12 + 6 + 3 + 4 + 6
self.out("%s_SHIFT%d:" % (self.niceName,shift)) self.label("%s_SHIFT%d" % (self.niceName,shift))
self.out("\tldx PARAM1") self.asm("ldx PARAM1")
cycleCount += 3 cycleCount += 3
rowStartCode,extraCycles = self.rowStartCalculatorCode(); rowStartCode,extraCycles = self.rowStartCalculatorCode();
self.out(rowStartCode) self.out(rowStartCode)
@ -347,18 +417,45 @@ def pixelColor(pixelData,row,col):
class HorizontalLookup(Listing): class HorizontalLookup(Listing):
def __init__(self): def __init__(self, assembler):
Listing.__init__(self) Listing.__init__(self, assembler)
self.generate_hgr()
self.generate_tables() self.generate_tables()
def generate_tables(self): def generate_hgr(self):
self.out("DIV7_2:") offsets = []
for pixel in range(140): for y in range(192):
self.out("\t.byte $%02x" % ((pixel / 7)*2)) # 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): 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__": if __name__ == "__main__":
@ -366,15 +463,26 @@ if __name__ == "__main__":
parser.add_argument("-v", "--verbose", default=0, action="count") 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("-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("-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!") 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() 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: if options.tables:
print HorizontalLookup() print HorizontalLookup(syntax)
exit(0) exit(0)
for pngfile in options.files: for pngfile in options.files:
try: try:
print Sprite(pngfile, options.xdraw) print Sprite(pngfile, syntax, options.xdraw, options.processor[0])
except RuntimeError: except RuntimeError, e:
parser.usage() print e
parser.print_help()