From 3afe35febe1215f018a163b5a760f9859dd9d49a Mon Sep 17 00:00:00 2001 From: Rob McMullen Date: Tue, 20 Jun 2017 19:55:47 -0700 Subject: [PATCH] Added processor and assembler syntax options * added complete lookup table generation --- HiSprite.py | 176 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 34 deletions(-) diff --git a/HiSprite.py b/HiSprite.py index 080f99e..2ad35fa 100755 --- a/HiSprite.py +++ b/HiSprite.py @@ -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()