User Tools

Site Tools


base:sprite_multiplexer

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

base:sprite_multiplexer [2015-04-17 04:34] (current)
Line 1: Line 1:
 +====== Sprite Multiplexer ======
  
 +By Fungus/​Nostalgia.
 +
 +The sources are in Turbo Assembler format.
 +
 +You should be able to have 1 independant color per sprite, and also an independant image for each
 +sprite. You could also add fore/​background priority buffers and add that register to your plot code.
 + 
 +I chose to go for shortness in this routine rather than pure speed. It can be speeded up about
 +20% by unrolling all the plotting loops and using self modifed code instead of the second set 
 +of plotting buffers. d010 and d012 storing and comparing could also be precalculated.
 +
 +Now, the goods.
 +
 +We are using 32 sprites, so we are going to call this variable (maxspr) from here on. 
 +All of our defined tables will be of length(maxspr). The first table we need, is our linked
 +list. Our most important table, as we sort this linked list instead of all our tables.
 +Lets define it. 
 +
 +<​code>​
 +maxspr = $1f
 +sort   = $02
 +
 +       ldx #$00      ;init x with 00
 +loop   ​txa ​          ;​transfer x to a to create index vals
 +       sta sort,​x ​   ;store it in the sort table
 +       ​inx ​          
 +       cpx maxspr+1 ​ ;have we reached 32 yet?
 +       bne loop
 +</​code>​
 +This routine puts numbers $00 to $1f (32) in the linked list table. Now we have our sort table defined.
 +The sort buffers can be in any memory area. I chose zeropage for speed. Now lets set up some sprites
 +and colors in our sprite data buffers. ​
 +
 + each buffer takes (maxspr) number of bytes. ​
 +<​code>​
 +ypos = $c000    ;sprite y position frame buffer
 +ybuf = $22      ;sprite y position raster buffer
 +xpos = $c020    ;sprite x position frame buffer
 +xbuf = $42      ;sprite x position raster buffer
 +xmsb = $c040    ;sprite x msb frame buffer
 +mbuf = $62      ;sprite x msb raster buffer
 +sprc = $c060    ;sprite color frame buffer
 +cbuf = $82      ;sprite color raster buffer
 +sprp = $c080    ;sprite pointer frame buffer
 +pbuf = $a2      ;sprite pointer raster buffer
 +
 +      jsr movspr ​    ;​preset sprite coordinates
 +      jsr anim       ;​animate sprites
 +      jsr colors ​    ;​adjust sprite colors.
 +</​code>​
 + Now that we have set up some sprite to show, lets go ahead and precalculate the first frame
 +to prevent any update bugs, due to excessive sorting of the first frame. This is a special ​
 +sorting algorythm, which I discovered on Cadaver'​s covert bit ops page. It comes from the
 +Imagine games done for Konami and several others. It's nice too see that good old 64
 +programmers came up with the idea of a prediction sort that long ago :) The speed of this
 +sorting technique is unmatched, it uses a prediction, which is really just the previous ​
 +frame'​s sorted index. Y positions generally do no change by a radical amount from frame to
 +frame. This is ideal for Multiplexer sorting, as it takes less than 2000 cycles to sort 32 sprites.
 +Otherwise the routine is a simple swap sorter, which is not very complicated at all :)
 +
 + Here is the routine.
 +<​code>​
 +       ldx #$00         ;init x index to 00
 +loop1  ldy sort+1,​x ​    ;load y index from linked list plus 1
 +loop2  lda ypos,​y ​      ;load a with y position for that index
 +       ldy sort,​x ​      ;load y index from linked list
 +       cmp ypos,​y ​      ;​compare yposition(index+1,​x) to yposition(index,​x)
 +       bcc swap         ;if yposition(index,​x) is less than yposition(index+1,​x) then swap the indexes
 +       ​inx ​             ;if more than, increment index
 +       cpx maxspr+1 ​    ;​checked all ypositions?
 +       bne loop1        ;no
 +       beq end          ;yes
 +swap   
 +       lda sort+1,​x ​    ;swap yposition(index+1,​x) with yposition(index,​x)
 +       sta sort,x
 +       sty sort+1,x
 +       tay
 +       dex
 +       bpl loop2        ;if not first sprite loop to main
 +       ​inx ​             ;correct index
 +       beq loop1        ;restart sort from sprite(index+1,​x)
 +end
 +</​code>​
 +Quite simple isnt it? 
 +
 + Ok, now lets setup our irq chian, so we can see this bumch of sprites weve just arranged.
 +<​code>​
 +       jsr setirq ​    ;​setup irq.
 +</​code>​
 +In runtime code
 +<​code>​
 +main    lda timer     ;wait for signal, that the buffer swap is complete.
 +        sta mloop+1 ​  
 +mloop   lda +      ​
 +        beq mloop
 +
 +        jsr movspr ​   ;move sprites ​
 +        jsr anim      ;animate sprites
 +        jsr color     ;​animate colors
 +cont    jmp main      ;loop
 +
 +setirq
 +      sei            ;set interrupt disable
 +      lda #$1b
 +      sta $d011      ;raster irq to 1st half of screen.
 +      lda #$fb
 +      sta $d012      ;irq to happen at line #$fb
 +      lda #<irq0
 +      sta $fffe      ;hardware irq vector low byte
 +      lda #>irq0
 +      sta $ffff      ;hardware irq vector high byte
 +      lda #$1f
 +      sta $dc0d      ;turn off all types of cia irq/nmi.
 +      sta $dd0d
 +      lda #$01
 +      sta $d01a      ;turn on raster irq.
 +      lda #$35
 +      sta $01        ;no basic or kernal
 +      lda $dc0d      ;​acknowledge any irq that has occured during setup.
 +      lda $dd0d
 +      inc $d019
 +      cli            ;clear interrupt disable
 +      rts            ;return from subroutine
 +
 +
 +irq0
 +      pha            ;use stack instead of zp to prevent bugs.
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019      ;​acknowledge irq
 +      ldx #$03       ;wait a few cycles
 +l1    dex
 +      bpl
 +      inx
 +      stx $d015      ;sprites off = more raster time in top/bottom border
 +
 +slop  ldy sort+1,​x ​  ;main index sort algo
 +slep  lda ypos,y
 +      ldy sort,​x ​    ;this sorter uses the previous frame as a prediction buffer.
 +      cmp ypos,​y ​    ;as y position does not change much from frame to frame.
 +      bcc swap       ;​otherwise,​ it is a simple swap sort.
 +      inx            ;our linked list (sort) is sorted in decending order, according
 +      cpx #​maxspr-1 ​ ;to sprite y positions.
 +      bne slop
 +      beq end
 +swap 
 +      lda sort+1,x
 +      sta sort,x
 +      sty sort+1,x
 +      tay
 +      dex
 +      bpl slep
 +      inx
 +      beq slop
 +end
 +
 +      ldy sort      ;re arrange frame buffers, into the raster buffers.
 +      lda ypos,​y ​   ;this is unrolled for speed.
 +      sta ybuf      ;this allows us to use only 1 index pointer for our sprite plotter.
 +      lda xpos,​y ​   ;it is double buffered, to allow runtime code to calculate the sprite
 +      sta xbuf      ;positions.
 +      lda xmsb,y
 +      sta mbuf
 +      lda sprc,y
 +      sta cbuf
 +      lda sprp,y
 +      sta pbuf
 +
 +      ldy sort+1
 +      lda ypos,y
 +      sta ybuf+1
 +      lda xpos,y
 +      sta xbuf+1
 +      lda xmsb,y
 +      sta mbuf+1
 +      lda sprc,y
 +      sta cbuf+1
 +      lda sprp,y
 +      sta pbuf+1
 +</​code>​
 +and so on until max sprites.
 +<​code>​
 +      ldx #$00     ;find # of used sprites (you can remove sprites by
 +      stx sptr     ;​placing #$ff into the ypos buffer for the corresponding
 +maxc  lda ybuf,​x ​  ;​sprite. It will not be displayed by the raster routine.
 +      cmp #$ff
 +      beq mxs
 +      inx
 +      cpx maxspr
 +      bne maxc
 +maxs  stx cnt      ;max sprites this frame count.
 +      cpx #$07     ;​check if were doing more than 8
 +      bcc maxm     ;if not, we want the plotter to stop after 1 irq.
 +      ldx #$07     
 +maxm  stx mnt
 +
 +      lda #$ff    ;reset sprites to off screen.
 +      sta $d001   ;​prevents bugs.
 +      sta $d003
 +      sta $d005
 +      sta $d007
 +      sta $d009
 +      sta $d00b
 +      sta $d00d
 +      sta $d00f
 +
 +      inc lsbtod ​  ;​buffers are swapped, so we can do the next frame now.
 +
 +      lda #<​irq1 ​  ;irq chain for raster code. prolly want a routine before
 +      sta $fffe    ;this one, to turn the sprites back on ;)
 +      lda #>​irq1 ​  ;i.e. lda #$ff sta $d015
 +      sta $ffff
 +      lda #$28
 +      sta $d012
 +      jmp eirq
 +</​code>​
 +Since the buffers have been reordered into proper decending order, we can use unrolled loops for the sprite
 +plotting. Each plot irq , is for each sprite in the order, 1,​2,​3,​4,​5,​6,​7,​8 respectively.
 +
 +We have a counter (mnt) for 0-7 sprites, as we want to go ahead an plot the first 8 sprites all at
 +once. We also have another counter (cnt) for the maximum number of sprites to display this frame.
 +
 +If (mnt) has not been reached yet, each irq branches to the next, to plot the first 8 sprites.
 +After that, cnt is checked to see if were done plotting sprites. If not, each irq checks the postion of the
 +sprite to see if it has finished displaying, and to see if its time to be done yet, if it is, it branches to the next irq. 
 +If it is not time yet, it calculates the next raster irq position and sets up the next irq.
 +
 +By using the previous sprites position, you can properly chain the irqs down the screen, for much less 
 +"​overlapping"​ bugs.
 +<​code>​
 +irq1
 +      pha           ;save registers
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019     ;​acknowledge irq
 +      ldx sptr      ;get current sprite index
 +hlop1 lda ybuf,​x ​   ;get sprite y position
 +      sta $d001     ;​store sprite y postion.
 +      lda xbuf,​x ​   ;get sprite x position.
 +      sta $d000     ;sta sprite x position.
 +      lda mbuf,​x ​   ;get sprite x position msb
 +      bne no1       ;set msb register
 +      lda $d010
 +      ora #%00000001
 +      bne yes1
 +no1   lda $d010
 +      and #%11111110
 +yes1  sta $d010
 +      lda pbuf,​x ​   ;get sprite image pointer
 +      sta $63f8     ;​store it, double buffered screen.
 +      sta $67f8
 +      lda cbuf,​x ​   ;get sprite color
 +      sta $d027     ;​store sprite color
 +      inx           ;next sprite index
 +      cpx mnt       ;lets go to next plot, if < then 8 yet.
 +      bcc hlop2
 +      cpx cnt       ;no more sprites?
 +      bne ok1
 +      jmp done      ;no more sprites.
 +
 +ok1   stx sptr      ;save sprite index
 +      lda $d003     ;get last position of next sprite
 +      clc
 +      adc #$15      ;add 21 lines
 +      cmp $d012     ;we there yet?
 +      bcc hlop2     ;​yeah,​ so plot next sprite
 +      adc #$02      ;no, so calculate next irq position (+3)
 +      sta $d012     ;set it
 +      lda #<​irq2 ​   ;irq for next sprite.
 +      sta $fffe
 +      lda #>irq2
 +      sta $ffff
 +      jmp eirq
 +
 +irq2
 +      pha           ;and so on
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop2 lda ybuf,x
 +      sta $d003
 +      lda xbuf,x
 +      sta $d002
 +      lda mbuf,x
 +      bne no2
 +      lda $d010
 +      ora #%00000010
 +      bne yes2
 +no2   lda $d010
 +      and #%11111101
 +yes2  sta $d010
 +      lda pbuf,x
 +      sta $63f9
 +      sta $67f9
 +      lda cbuf,x
 +      sta $d028
 +      inx
 +      cpx mnt
 +      bcc hlop3
 +      cpx cnt
 +      bne ok2
 +      jmp done
 +
 +ok2   stx sptr
 +      lda $d005
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop3
 +      adc #$02
 +      sta $d012
 +      lda #<irq3
 +      sta $fffe
 +      lda #>irq3
 +      sta $ffff
 +      jmp eirq
 +
 +irq3
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop3 lda ybuf,x
 +      sta $d005
 +      lda xbuf,x
 +      sta $d004
 +      lda mbuf,x
 +      bne no3
 +      lda $d010
 +      ora #%00000100
 +      bne yes3
 +no3   lda $d010
 +      and #%11111011
 +yes3  sta $d010
 +      lda pbuf,x
 +      sta $63fa
 +      sta $67fa
 +      lda cbuf,x
 +      sta $d029
 +      inx
 +      cpx mnt
 +      bcc hlop4
 +      cpx cnt
 +      bne ok3
 +      jmp done
 +
 +ok3   stx sptr
 +      lda $d007
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop4
 +      adc #$02
 +      sta $d012
 +      lda #<irq4
 +      sta $fffe
 +      lda #>irq4
 +      sta $ffff
 +      jmp eirq
 +
 +irq4  ​
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop4 lda ybuf,x
 +      sta $d007
 +      lda xbuf,x
 +      sta $d006
 +      lda mbuf,x
 +      bne no4
 +      lda $d010
 +      ora #%00001000
 +      bne yes4
 +no4   lda $d010
 +      and #%11110111
 +yes4  sta $d010
 +      lda pbuf,x
 +      sta $63fb
 +      sta $67fb
 +      lda cbuf,x
 +      sta $d02a
 +      inx
 +      cpx mnt
 +      bcc hlop5
 +      cpx cnt
 +      bne ok4
 +      jmp done
 +
 +ok4   stx sptr
 +      lda $d009
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop5
 +      adc #$02
 +      sta $d012
 +      lda #<irq5
 +      sta $fffe
 +      lda #>irq5
 +      sta $ffff
 +      jmp eirq
 +
 +irq5
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop5 lda ypos,x
 +      sta $d009
 +      lda xpos,x
 +      sta $d008
 +      lda mbuf,x
 +      bne no5
 +      lda $d010
 +      ora #%00010000
 +      bne yes5
 +no5   lda $d010
 +      and #%11101111
 +yes5  sta $d010
 +      lda pbuf,x
 +      sta $63fc
 +      sta $67fc
 +      lda cbuf,x
 +      sta $d02b
 +      inx
 +      cpx mnt
 +      bcc hlop6
 +      cpx cnt
 +      bne ok5
 +      jmp done
 +
 +ok5   stx sptr
 +      lda $d00b
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop6
 +      adc #$02
 +      sta $d012
 +      lda #<irq6
 +      sta $fffe
 +      lda #>irq6
 +      sta $ffff
 +      jmp eirq
 +
 +irq6
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop6 lda ybuf,x
 +      sta $d00b
 +      lda xbuf,x
 +      sta $d00a
 +      lda mbuf,x
 +      bne no6
 +      lda $d010
 +      ora #%00100000
 +      bne yes6
 +no6   lda $d010
 +      and #%11011111
 +yes6  sta $d010
 +      lda pbuf,x
 +      sta $63fd
 +      sta $67fd
 +      lda cbuf,x
 +      sta $d02c
 +      inx
 +      cpx mnt
 +      bcc hlop7
 +      cpx cnt
 +      bne ok6
 +      jmp done
 +
 +ok6   stx sptr
 +      lda $d00d
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop7
 +      adc #$02
 +      sta $d012
 +      lda #<irq7
 +      sta $fffe
 +      lda #>irq7
 +      sta $ffff
 +      jmp eirq
 +
 +irq7
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop7 lda ybuf,x
 +      sta $d00d
 +      lda xbuf,x
 +      sta $d00c
 +      lda mbuf,x
 +      bne no7
 +      lda $d010
 +      ora #%01000000
 +      bne yes7
 +no7   lda $d010
 +      and #%10111111
 +yes7  sta $d010
 +      lda pbuf,x
 +      sta $63fe
 +      sta $67fe
 +      lda cbuf,x
 +      sta $d02d
 +      inx
 +      cpx mnt
 +      bcc hlop8
 +      cpx cnt
 +      bne ok7
 +      jmp done
 +
 +ok7   stx sptr
 +      lda $d00f
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop8
 +      adc #$02
 +      sta $d012
 +      lda #<irq8
 +      sta $fffe
 +      lda #>irq8
 +      sta $ffff
 +      jmp eirq
 +
 +irq8 
 +      pha
 +      txa
 +      pha
 +      tya
 +      pha
 +      inc $d019
 +      ldx sptr
 +hlop8 lda ybuf,x
 +      sta $d00f
 +      lda xbuf,x
 +      sta $d00e
 +      lda mbuf,x
 +      bne no8
 +      lda $d010
 +      ora #%10000000
 +      bne yes8
 +no8   lda $d010
 +      and #%01111111
 +yes8  sta $d010
 +      lda pbuf,x
 +      sta $63ff
 +      sta $67ff
 +      lda cbuf,x
 +      sta $d02e
 +      inx
 +      cpx mnt
 +      bcc hlop9
 +      cpx cnt
 +      bne ok8
 +      jmp done
 +
 +ok8   stx sptr
 +      lda $d001
 +      clc
 +      adc #$15
 +      cmp $d012
 +      bcc hlop9
 +      adc #$02
 +      sta $d012
 +      lda #<irq1
 +      sta $fffe
 +      lda #>irq1
 +      sta $ffff
 +      jmp eirq
 +hlop9 jmp hlop1
 +
 +done  lda #<irq0
 +      sta $fffe
 +      lda #>irq0
 +      sta $ffff
 +      lda #$fb
 +      sta $d012
 +eirq        ​
 +      pla
 +      tay
 +      pla
 +      tax
 +      pla
 +      rti
 +
 +END
 +</​code>​
base/sprite_multiplexer.txt ยท Last modified: 2015-04-17 04:34 (external edit)