diff --git a/HiSprite.py b/HiSprite.py index eb3c0d2..27c73a4 100755 --- a/HiSprite.py +++ b/HiSprite.py @@ -53,6 +53,8 @@ def slugify(s): class AssemblerSyntax(object): + extension = "s" + def asm(self, text): return "\t%s" % text @@ -74,6 +76,9 @@ class AssemblerSyntax(object): def origin(self, text): return self.asm("*= %s" % text) + def include(self, text): + return self.asm(".include \"%s\"" % text) + def binary_constant(self, value): try: # already a string @@ -97,6 +102,8 @@ class Mac65(AssemblerSyntax): class CC65(AssemblerSyntax): + extension = "s" + def label(self, text): return "%s:" % text @@ -108,11 +115,23 @@ class Listing(object): self.current = None self.desired_count = 1 self.stash_list = [] + self.slug = "sprite-driver" def __str__(self): self.flush_stash() return "\n".join(self.lines) + "\n" + def get_filename(self, basename): + return "%s-%s.%s" % (basename, self.slug.lower(), self.assembler.extension) + + def write(self, basename, disclaimer): + filename = self.get_filename(basename) + print("Writing to %s" % filename) + with open(filename, "w") as fh: + fh.write(disclaimer + "\n\n") + fh.write(str(self)) + return filename + def out(self, line): self.flush_stash() self.lines.append(line) @@ -135,6 +154,9 @@ class Listing(object): def addr(self, text): self.out(self.assembler.address(text)) + def include(self, text): + self.out(self.assembler.include(text)) + def flush_stash(self): if self.current is not None and len(self.stash_list) > 0: self.lines.append(self.current(", ".join(self.stash_list))) @@ -166,6 +188,8 @@ class Listing(object): class Sprite(Listing): + backing_store_sizes = set() + def __init__(self, pngfile, assembler, screen, xdraw=False, use_mask=False, backing_store=False, processor="any", name=""): Listing.__init__(self, assembler) self.screen = screen @@ -179,7 +203,7 @@ class Sprite(Listing): self.processor = processor if not name: name = os.path.splitext(pngfile)[0] - self.niceName = slugify(name) + self.slug = slugify(name) self.width = pngdata[0] self.height = pngdata[1] self.pixelData = list(pngdata[2]) @@ -189,7 +213,7 @@ class Sprite(Listing): def jumpTable(self): # Prologue - self.label("%s" % self.niceName) + self.label("%s" % self.slug) self.comment("%d bytes per row" % self.screen.byteWidth(self.width)) if self.processor == "any": @@ -234,13 +258,13 @@ class Sprite(Listing): self.asm("ldy PARAM0") self.asm("ldx MOD%d_%d,y" % (self.screen.numShifts, self.screen.bitsPerPixel)) - self.asm("jmp (%s_JMP,x)\n" % (self.niceName)) + self.asm("jmp (%s_JMP,x)\n" % (self.slug)) offset_suffix = "" # Bit-shift jump table for 65C02 - self.label("%s_JMP" % (self.niceName)) + self.label("%s_JMP" % (self.slug)) for shift in range(self.screen.numShifts): - self.addr("%s_SHIFT%d" % (self.niceName, shift)) + self.addr("%s_SHIFT%d" % (self.slug, shift)) def jump6502(self): self.save_axy_6502() @@ -248,16 +272,16 @@ class Sprite(Listing): self.asm("ldx MOD%d_%d,y" % (self.screen.numShifts, self.screen.bitsPerPixel)) # Fast jump table routine; faster and smaller than self-modifying code - self.asm("lda %s_JMP+1,x" % (self.niceName)) + self.asm("lda %s_JMP+1,x" % (self.slug)) self.asm("pha") - self.asm("lda %s_JMP,x" % (self.niceName)) + self.asm("lda %s_JMP,x" % (self.slug)) self.asm("pha") self.asm("rts\n") # Bit-shift jump table for generic 6502 - self.label("%s_JMP" % (self.niceName)) + self.label("%s_JMP" % (self.slug)) for shift in range(self.screen.numShifts): - self.addr("%s_SHIFT%d-1" % (self.niceName,shift)) + self.addr("%s_SHIFT%d-1" % (self.slug,shift)) def blitShift(self, shift): # Blitting functions @@ -267,7 +291,7 @@ class Sprite(Listing): # SAVE_AXY + RESTORE_AXY + rts + sprite jump table cycleCount = 9 + 12 + 6 + 3 + 4 + 6 - self.label("%s_SHIFT%d" % (self.niceName,shift)) + self.label("%s_SHIFT%d" % (self.slug,shift)) colorStreams = self.screen.byteStreamsFromPixels(shift, self) maskStreams = self.screen.byteStreamsFromPixels(shift, self, True) @@ -278,6 +302,7 @@ class Sprite(Listing): if self.backing_store: byteWidth = len(colorStreams[0]) self.asm("jsr savebg_%dx%d" % (byteWidth, self.height)) + self.backing_store_sizes.add((byteWidth, self.height)) self.asm("ldx PARAM1") cycleCount += 3 @@ -594,6 +619,7 @@ class HGRBW(HGR): class RowLookup(Listing): def __init__(self, assembler, screen): Listing.__init__(self, assembler) + self.slug = "hgrrows" self.generate_y(screen) def generate_y(self, screen): @@ -615,6 +641,7 @@ class RowLookup(Listing): class ColLookup(Listing): def __init__(self, assembler, screen): Listing.__init__(self, assembler) + self.slug = "hgrcols-%dx%d" % (screen.numShifts, screen.bitsPerPixel) self.generate_x(screen) def generate_x(self, screen): @@ -648,6 +675,7 @@ 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("-n", "--name", default="", help="Name for generated assembly function (default: based on image filename)") + parser.add_argument("-o", "--output-prefix", default="", help="Base name to create a set of output files. If not supplied, all code will be sent to stdout.") 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() @@ -670,6 +698,7 @@ if __name__ == "__main__": sys.exit(1) listings = [] + luts = {} for pngfile in options.files: try: @@ -680,6 +709,13 @@ if __name__ == "__main__": except png.Error, e: print "%s: %s" % (pngfile, e) sys.exit(1) + if options.output_prefix: + r = RowLookup(assembler, screen) + luts[r.slug] = r + c = ColLookup(assembler, screen) + luts[c.slug] = c + + listings.extend([luts[k] for k in sorted(luts.keys())]) if options.rows: listings.append(RowLookup(assembler, screen)) @@ -688,7 +724,14 @@ if __name__ == "__main__": listings.append(ColLookup(assembler, screen)) if listings: - print disclaimer + if options.output_prefix: + driver = Listing(assembler) + for source in listings: + genfile = source.write(options.output_prefix, disclaimer) + driver.include(genfile) + driver.write(options.output_prefix, disclaimer) + else: + print disclaimer - for section in listings: - print section + for section in listings: + print section diff --git a/Makefile.rob b/Makefile.rob index 0940bdc..a1d3d72 100644 --- a/Makefile.rob +++ b/Makefile.rob @@ -28,7 +28,10 @@ colortest.dsk: HiSprite.py colortest.s bwsprite.s atasm -ocolortest.xex colortest.s -Lcolortest.var -gcolortest.lst atrcopy colortest.dsk boot -b colortest.xex --brun 6000 -f -multitest.dsk: HiSprite.py multitest.s colorsprite.s bwsprite.s +multitest-sprite-driver.s: HiSprite.py $(BWSPRITE) + python HiSprite.py -a mac65 -p 6502 -s hgrbw -m -b -o multitest $(BWSPRITE) $(COLORSPRITE) + +multitest.dsk: HiSprite.py multitest.s multitest-sprite-driver.s atasm -omultitest.xex multitest.s -Lmultitest.var -gmultitest.lst atrcopy multitest.dsk boot -b multitest.xex --brun 6000 -f @@ -36,4 +39,4 @@ clean: rm -f rowlookup.s collookupbw.s collookupcolor.s rm -f bwtest.dsk bwtest.xex bwtest.var bwtest.lst rm -f colortest.dsk colortest.xex colortest.var colortest.lst - rm -f multitest.dsk multitest.xex multitest.var multitest.lst + rm -f multitest.dsk multitest.xex multitest.var multitest.lst multitest-sprite-driver.s multitest-bwsprite.s multitest-hgrcols-7x1.s multitest-hgrrows.s diff --git a/backingstore-3x11.s b/backingstore-3x11.s index ef0a137..7dbacbc 100644 --- a/backingstore-3x11.s +++ b/backingstore-3x11.s @@ -26,6 +26,7 @@ SIZE_3X11 = 2 + 1 + 1 + 3*11 savebg_3x11 + ; reserve space in the backing store stack sec lda bgstore sbc #SIZE_3X11 @@ -33,8 +34,9 @@ savebg_3x11 lda bgstore+1 sbc #0 sta bgstore+1 - ldy #0 + ; save the metadata + ldy #0 lda #restorebg_3X8 + sta (bgstore),y + iny + lda PARAM0 + sta (bgstore),y + iny + lda PARAM1 + sta SCRATCH0 + sta (bgstore),y + iny + +savebg_3X8_line + ; save a line, starting from the topmost and working down + ldx SCRATCH0 ; Calculate Y line + + lda HGRROWS_H1,x ; Compute hires row + sta savebg_3X8_col0+2 + sta savebg_3X8_col1+2 + sta savebg_3X8_col2+2 + lda HGRROWS_L,x + sta savebg_3X8_col0+1 + sta savebg_3X8_col1+1 + sta savebg_3X8_col2+1 + + ldx PARAM0 ; Compute hires column + lda DIV7_1,x + tax + +savebg_3X8_col0 + lda $2000,x + sta (bgstore),y + iny + inx +savebg_3X8_col1 + lda $2000,x + sta (bgstore),y + iny + inx +savebg_3X8_col2 + lda $2000,x + sta (bgstore),y + iny + + inc SCRATCH0 + + cpy #SIZE_3X8 + bcc savebg_3X8_line + + rts + +; bgstore will be pointing right to the data to be blitted back to the screen, +; which is 4 bytes into the bgstore array. Everything before the data will have +; already been pulled off by the driver in order to figure out which restore +; routine to call. Y will be 4 upon entry, and PARAM0 and PARAM1 will be +; filled with the x & y values. +; +; also, no need to save registers because this is being called from a driver +; that will do all of that. +restorebg_3X8 + ldx PARAM1 ; Calculate Y line + + lda HGRROWS_H1,x ; Compute hires row + sta restorebg_3X8_col0+2 + sta restorebg_3X8_col1+2 + sta restorebg_3X8_col2+2 + lda HGRROWS_L,x + sta restorebg_3X8_col0+1 + sta restorebg_3X8_col1+1 + sta restorebg_3X8_col2+1 + + ldx PARAM0 ; Compute hires column + lda DIV7_1,x + tax + + lda (bgstore),y +restorebg_3X8_col0 + sta $2000,x + iny + inx + lda (bgstore),y +restorebg_3X8_col1 + sta $2000,x + iny + inx + lda (bgstore),y +restorebg_3X8_col2 + sta $2000,x + iny + + inc PARAM1 + + cpy #SIZE_3X8 + bcc restorebg_3X8 + rts diff --git a/backingstore.s b/backingstore.s index a23c127..a558136 100644 --- a/backingstore.s +++ b/backingstore.s @@ -48,8 +48,8 @@ restorebg_jsr jsr $ffff clc - lda bgstore - adc #SIZE_3X11 + tya ; y contains the number of bytes processed + adc bgstore sta bgstore lda bgstore+1 adc #0 diff --git a/multitest.dsk b/multitest.dsk index 8d654b4..211df60 100644 Binary files a/multitest.dsk and b/multitest.dsk differ diff --git a/multitest.s b/multitest.s index 72e01b2..6304668 100644 --- a/multitest.s +++ b/multitest.s @@ -21,10 +21,9 @@ SCRATCH0 = $19 SCRATCH1 = $1a SPRITEPTR_L = $1b SPRITEPTR_H = $1c +BGSTORE = $fa BGTOP = $c0 ; page number of first byte beyond top of backing store stack -bgstore = $80 -bgline = $82 ; constants MAXPOSX = 250 @@ -204,10 +203,10 @@ sprite_active .byte 1, 1, 1, 1, 1, 1, 1, 1, $ff ; 1 = active, 0 = skip, $ff = end of list sprite_l - .byte BWSPRITE, >BWSPRITE, >BWSPRITE, >BWSPRITE, >BWSPRITE, >BWSPRITE, >BWSPRITE, >BWSPRITE + .byte >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >MOLDY_BURGER, >MOLDY_BURGER sprite_x .byte 80, 164, 33, 245, 4, 9, 255, 18 @@ -229,10 +228,6 @@ sprite_diry - .include colorsprite.s - .include bwsprite.s - .include rowlookup.s - .include collookupbw.s - .include collookupcolor.s - .include backingstore.s - .include backingstore-3x11.s +.include multitest-sprite-driver.s +.include backingstore.s +.include backingstore-3x11.s