User Tools

Site Tools


base:sprite_multiplexer

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.

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

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.

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.

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.

       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

Quite simple isnt it?

Ok, now lets setup our irq chian, so we can see this bumch of sprites weve just arranged.

       jsr setirq     ;setup irq.

In runtime 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

and so on until max sprites.

      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

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.

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
base/sprite_multiplexer.txt · Last modified: 2015-04-17 04:34 (external edit)