diff --git a/HiSprite.py b/HiSprite.py index b67d08c..1077658 100755 --- a/HiSprite.py +++ b/HiSprite.py @@ -122,6 +122,9 @@ class Listing(object): self.flush_stash() return "\n".join(self.lines) + "\n" + def add_listing(self, other): + self.lines.extend(other.lines) + def get_filename(self, basename): return "%s-%s.%s" % (basename, self.slug.lower(), self.assembler.extension) @@ -686,6 +689,157 @@ class ColLookup(Listing): self.byte("$%02x" % ((pixel % screen.numShifts) * 2), screen.numShifts) +class BackingStore(Listing): + # Each entry in the stack includes: + # 2 bytes: address of restore routine + # 1 byte: x coordinate + # 1 byte: y coordinate + # nn: x * y bytes of data, in lists of rows + + def __init__(self, assembler, byte_width, row_height): + Listing.__init__(self, assembler) + self.byte_width = byte_width + self.row_height = row_height + self.save_label = "savebg_%dx%d" % (byte_width, row_height) + self.restore_label = "restorebg_%dx%d" % (byte_width, row_height) + self.space_needed = self.compute_size() + self.create_save() + self.out() + self.create_restore() + self.out() + + def compute_size(self): + return 2 + 1 + 1 + (self.byte_width * self.row_height) + + def create_save(self): + self.label(self.save_label) + + # reserve space in the backing store stack + self.asm("sec") + self.asm("lda bgstore") + self.asm("sbc #%d" % self.space_needed) + self.asm("sta bgstore") + self.asm("lda bgstore+1") + self.asm("sbc #0") + self.asm("sta bgstore+1") + + # save the metadata + self.asm("ldy #0") + self.asm("lda #<%s" % self.restore_label) + self.asm("sta (bgstore),y") + self.asm("iny") + self.asm("lda #>%s" % self.restore_label) + self.asm("sta (bgstore),y") + self.asm("iny") + self.asm("lda PARAM0") + self.asm("sta (bgstore),y") + self.asm("iny") + self.asm("lda PARAM1") + + # Note that we can't clobber PARAM1 like the restore routine can + # because this is called in the sprite drawing routine and these + # values must be retained to draw the sprite in the right place! + self.asm("sta SCRATCH0") + self.asm("sta (bgstore),y") + self.asm("iny") + + loop_label, col_label = self.smc_row_col(self.save_label, "SCRATCH0") + + for c in range(self.byte_width): + self.label(col_label % c) + self.asm("lda $2000,x") + self.asm("sta (bgstore),y") + self.asm("iny") + if c < self.byte_width - 1: + # last loop doesn't need this + self.asm("inx") + + self.asm("inc SCRATCH0") + + self.asm("cpy #%d" % self.space_needed) + self.asm("bcc %s" % loop_label) + + self.asm("rts") + + def smc_row_col(self, label, row_var): + # set up smc for hires column, because the starting column doesn't + # change when moving to the next row + self.asm("ldx PARAM0") + self.asm("lda DIV7_1,x") + smc_label = "%s_smc1" % label + self.asm("sta %s+1" % smc_label) + + loop_label = "%s_line" % label + # save a line, starting from the topmost and working down + self.label(loop_label) + self.asm("ldx %s" % row_var) + + self.asm("lda HGRROWS_H1,x") + col_label = "%s_col%%s" % label + for c in range(self.byte_width): + self.asm("sta %s+2" % (col_label % c)) + self.asm("lda HGRROWS_L,x") + for c in range(self.byte_width): + self.asm("sta %s+1" % (col_label % c)) + + self.label(smc_label) + self.asm("ldx #$ff") + return loop_label, col_label + + def create_restore(self): + # 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. + self.label(self.restore_label) + + # we can clobber the heck out of PARAM1 because we're being called from + # the restore driver and when we return we are just going to load it up + # with the next value anyway. + loop_label, col_label = self.smc_row_col(self.restore_label, "PARAM1") + + for c in range(self.byte_width): + self.asm("lda (bgstore),y") + self.label(col_label % c) + self.asm("sta $2000,x") + self.asm("iny") + if c < self.byte_width - 1: + # last loop doesn't need this + self.asm("inx") + + self.asm("inc PARAM1") + self.asm("cpy #%d" % self.space_needed) + self.asm("bcc %s" % loop_label) + + self.asm("rts") + + +class BackingStoreDriver(Listing): + # Driver to restore the screen using all the saved data. + # The backing store is a stack that grows downward in order to restore the + # chunks in reverse order that they were saved. + # + # variables used: + # bgstore: (lo byte, hi byte) 1 + the first byte of free memory. + # I.e. points just beyond the last byte + # PARAM0: (byte) x coord + # PARAM1: (byte) y coord + # + # everything else is known because the sizes of each erase/restore + # routine are hardcoded because this is a sprite *compiler*. + def __init__(self, assembler, sizes): + Listing.__init__(self, assembler) + self.slug = "backing-store" + for byte_width, row_height in sizes: + code = BackingStore(assembler, byte_width, row_height) + self.add_listing(code) + + + if __name__ == "__main__": disclaimer = ''' ; This file was generated by HiSprite.py, a sprite compiler by Quinn Dunki. @@ -731,13 +885,14 @@ if __name__ == "__main__": for pngfile in options.files: try: - listings.append(Sprite(pngfile, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.processor, options.name)) + sprite_code = Sprite(pngfile, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.processor, options.name) except RuntimeError, e: print "%s: %s" % (pngfile, e) sys.exit(1) except png.Error, e: print "%s: %s" % (pngfile, e) sys.exit(1) + listings.append(sprite_code) if options.output_prefix: r = RowLookup(assembler, screen) luts[r.slug] = r @@ -754,6 +909,9 @@ if __name__ == "__main__": if listings: if options.output_prefix: + if Sprite.backing_store_sizes: + backing_store_code = BackingStoreDriver(assembler, Sprite.backing_store_sizes) + listings.append(backing_store_code) driver = Listing(assembler) for source in listings: genfile = source.write(options.output_prefix, disclaimer) diff --git a/backingstore-3x11.s b/backingstore-3x11.s deleted file mode 100644 index 0db9cd6..0000000 --- a/backingstore-3x11.s +++ /dev/null @@ -1,254 +0,0 @@ -; backing store test, hardcoded for 3x11 apple.png-sized sprite -; -; The backing store memory starts from some high address -; and grows downward in order to facilitate speedier restoring, because -; there will be different sized chunks to restore -; -; -; -; -; needs: -; bgstore: (lo byte, hi byte) 1 + the first byte of free memory. -; I.e. points just beyond the last byte -; PARAM0: (byte) x coord -; PARAM1: (byte) y coord -; -; everything else is known because the sizes of each erase/restore -; routine will be hardcoded. - -; modification of quinn's column sweep - -; memory needed for this chunk of background: -; 2: address of restore routine -; 1: x coordinate -; 1: y coordinate -; 33: number of bytes of background to save -SIZE_3X11 = 2 + 1 + 1 + 3*11 - -savebg_3x11 - ; reserve space in the backing store stack - sec - lda bgstore - sbc #SIZE_3X11 - sta bgstore - lda bgstore+1 - sbc #0 - sta bgstore+1 - - ; save the metadata - ldy #0 - lda #restorebg_3x11 - sta (bgstore),y - iny - lda PARAM0 - sta (bgstore),y - iny - lda PARAM1 - sta SCRATCH0 - sta (bgstore),y - iny - - ; set up smc for hires column, because the starting column doesn't change - ; when moving to the next row - ldx PARAM0 - lda DIV7_1,x - sta savebg_3x11_smc1+1 - -savebg_3x11_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_3x11_col0+2 - sta savebg_3x11_col1+2 - sta savebg_3x11_col2+2 - lda HGRROWS_L,x - sta savebg_3x11_col0+1 - sta savebg_3x11_col1+1 - sta savebg_3x11_col2+1 - -savebg_3x11_smc1 - ldx #$ff - -savebg_3x11_col0 - lda $2000,x - sta (bgstore),y - iny - inx -savebg_3x11_col1 - lda $2000,x - sta (bgstore),y - iny - inx -savebg_3x11_col2 - lda $2000,x - sta (bgstore),y - iny - - inc SCRATCH0 - - cpy #SIZE_3X11 - bcc savebg_3x11_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_3x11 - ldx PARAM1 ; Calculate Y line - - lda HGRROWS_H1,x ; Compute hires row - sta restorebg_3x11_col0+2 - sta restorebg_3x11_col1+2 - sta restorebg_3x11_col2+2 - lda HGRROWS_L,x - sta restorebg_3x11_col0+1 - sta restorebg_3x11_col1+1 - sta restorebg_3x11_col2+1 - - ldx PARAM0 ; Compute hires column - lda DIV7_1,x - tax - - lda (bgstore),y -restorebg_3x11_col0 - sta $2000,x - iny - inx - lda (bgstore),y -restorebg_3x11_col1 - sta $2000,x - iny - inx - lda (bgstore),y -restorebg_3x11_col2 - sta $2000,x - iny - - inc PARAM1 - - cpy #SIZE_3X11 - bcc restorebg_3x11 - rts - - -SIZE_3X8 = 2 + 1 + 1 + 3*8 - -savebg_3X8 - ; reserve space in the backing store stack - sec - lda bgstore - sbc #SIZE_3X8 - sta bgstore - lda bgstore+1 - sbc #0 - sta bgstore+1 - - ; 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/multitest.dsk b/multitest.dsk index 947b04d..fd11bb4 100644 Binary files a/multitest.dsk and b/multitest.dsk differ diff --git a/multitest.s b/multitest.s index b1b556a..1332fd4 100644 --- a/multitest.s +++ b/multitest.s @@ -251,4 +251,3 @@ sprite_diry .include multitest-sprite-driver.s .include backingstore.s -.include backingstore-3x11.s