Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
FourSpriter MK III
#1
Okay, reduced the flicker quite a bit more.

Sadly, because it's erase all four sprites and then redraws all sprites, different sprites start to flicker at different points.

If using 4 sprites:
Sprite 0 starts to flicker at above (pixel) line 10
And every other sprite at about 8 lines further down, per sprite.

If using 3 sprites
Sprites 0 is stable
Sprite 1 flickers above row 6
Sprite 3 starts to flicker at above line 16.

If using 2 sprites
Everything is stable.

I assume this is why the mojon's demonstrations only use 2 sprites...

I can't see any more speed optimizations (and right now it probably completes a redraw cycle in less than half the time of the original). There are probably some left, and no doubt Boriel will spot them immediately because he's a genius. However, right now I'm looking at changing /how/ it works. Things like not redrawing a sprite if it hasn't moved, and rather than erase all sprites and then draw all sprites, perhaps go for an erase-buffer-draw routine that does it on a per-sprite basis; limiting the time in which flicker can hit to areas of the screen.

The only alternative to that is to use a back-buffer screen; and that's a LOT of memory.

So right now, it's perfectly useable, if you think about where your sprites are going to go on the screen. The more sprites you have, the more careful you have to be - and keep the later numbered sprites away from the top of the screen!

So, here's how it stands right now (with a tweaked version of Apenao's demo program):

Code:
#DEFINE BLACK 0
#DEFINE BLUE 1
#DEFINE RED 2
#DEFINE MAGENTA 3
#DEFINE GREEN 4
#DEFINE CYAN 5
#DEFINE YELLOW 6
#DEFINE WHITE 7
#DEFINE TRANSPARENT 8
#DEFINE CONTRAST 9
#DEFINE TRUE 1
#DEFINE FALSE 0

SUB fspInitialize (n as uByte, udgA as uByte, udgB as uByte, udgC as uByte, udgD as uByte,x as uByte,y as uByte)  'Initialize Sprite: n (sprite number 0-3);a,b,c,d (UDG number 0-20,99 means the sprite won't be printed)
#ifndef FINAL_CODE
IF n>3 OR (udgA>20 AND udgA<>99) or udgB>20 or udgC>20 or udgD>20 then Print "Out Of Bounds Call in ";"fspInitialize" : stop : END IF : REM Bounds checking - so a bad function call doesn't poke all over memory.
#endif
DIM targetAddr as uInteger
targetAddr=@fspDataStart+(48*n)
fspErase()
POKE targetAddr,udgA
targetAddr=targetAddr+1
POKE targetAddr,udgB
targetAddr=targetAddr+1
POKE targetAddr,udgC
targetAddr=targetAddr+1
POKE targetAddr,udgD
targetAddr=targetAddr+1
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
targetAddr=targetAddr+1
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
fspBufferAndDraw()
RETURN

fspDataStart:
REM This Section contains Fourspriter data and code that is called by other routines.

ASM
;; 16x16 Sprites moving a character each frame
;; Copyleft 2009 The Mojon Twins.
;; Updated Copyleft 2010 Paul Fisher
;; Uses UDGs (it reads the address from the system variables).
;; It moves up to 4 sprites, preserving the backgrounds.

;; Sprite control is done writing a certain number
;; in the Sprite's first UDG
;; If the first UDG is 99, it won't be printed

; Note that the sprites are initialized to disabled.

fspDataStartAsm:                        ; each block is 48 bytes long. [This comment originally said 40 bytes, and it's clearly 48]
;; Sprite 1
udgs1:      defb   99,0,0,0      ; Four UDGs of the first sprite.
x_pos1:     defb   0            ; X position in chars.
y_pos1:     defb   0            ; Y position in chars.
cx_pos1:    defb   0            ; Previous X position in chars.
cy_pos1:    defb   0            ; Previous Y position in chars.
buffer1:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0         ; Background Gfx buffer.
attrs1:     defb   6,7,7,7      ; Sprite ATTRs
buffatrs1:  defb   0,0,0,0      ; Background Attr's buffer

;; Sprite 2
udgs2:      defb   99,0,0,0
x_pos2:     defb   0
y_pos2:     defb   0
cx_pos2:    defb   0
cy_pos2:    defb   0
buffer2:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs2:     defb   7,7,7,7
buffatrs2:  defb   0,0,0,0

;; Sprite 3
udgs3:      defb   99,0,0,0
x_pos3:     defb   0
y_pos3:     defb   0
cx_pos3:    defb   0
cy_pos3:    defb   0
buffer3:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs3:     defb   7,7,7,7
buffatrs3:  defb   0,0,0,0

;; Sprite 4
udgs4:      defb   99,0,0,0
x_pos4:     defb   0
y_pos4:     defb   0
cx_pos4:    defb   0
cy_pos4:    defb   0
buffer4:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs4:     defb   7,7,7,7
buffatrs4:  defb   0,0,0,0

fspCollisionDetect:   defb   0,0,0 ; Sprite 0 with 1,2,3
                      defb   0,0   ; Sprite 1 with 2,3
                      defb   0     ; sprite 2 with 3
fspCopyToScreen:
        
         call   get_scr_address   ; Returns (xpos, ypos) in HL
        
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
        
         ret  

fspCopyFromScreen:
         call   get_scr_address
        
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
        
         ret  
        
; This function returns the address into HL of the screen address
; b,c in character grid notation.
; Original code was extracted by BloodBaz

get_scr_address:
         push    de
         ld      a,b ; b=x
         and     31
         ld      l,a
         ld      a,c ; c=y
         ld      d,a
         and     24
         add     a,64
         ld      h,a
         ld      a,d
         and     7
         rrca
         rrca
         rrca
         or      l
         ld      l,a
         pop     de
         ret
              
              
;; This function returns the memory address of the Character Position
;; b,c in the attribute screen memory.
;; Adapted from code by Jonathan Cauldwell.

get_attr_address:
         ld      a,c        ;ypos
         rrca
         rrca
         rrca               ; Multiply by 32
         ld      l,a        ; Pass to L
         and     3          ; Mask with 00000011
         add     a,88       ; 88 * 256 = 22528 - start of attributes.
         ld      h,a        ; Put it in the High Byte
         ld      a,l        ; We get y value *32
         and     224        ; Mask with 11100000
         ld      l,a        ; Put it in L
         ld      a,b        ; xpos
         add     a,l        ; Add it to the Low byte
         ld      l,a        ; Put it back in L, and we're done. HL=Address.
         ret

;; Routine to save the background to the buffer
fspEraseAsm:

         ld      hl, fspDataStartAsm
         EXX
         ld      b,   4
        
i4chars2:
         EXX
        
         ;;
         ld      a,   (hl)
         cp      99
         jr      z,   nxt2

         ;; A
         LD BC,6
         ;EX DE,HL ; HL now points at the start sprite data instead
         ADD HL,BC  ; HL moved on to point to old x-y positions.
        
         LD b,(HL) ; b=x
         INC HL
        
         LD c,(HL) ;c=y
         INC HL
        
         EX DE,HL ; Now it's DE that points into the data buffer.
      
         ; Draw first background character
         call   fspCopyToScreen
        
         ;; B
         ; xpos++
         inc b
        
         ; Draw second background Character
         call   fspCopyToScreen
  
         ;; C
         ; xpos --
         dec b
         ; ypos ++
         inc c
         ; draw Third background Character
         call    fspCopyToScreen

         ;; D
         ; xpos++
         inc b
         ; Draw Fourth background Character
         call   fspCopyToScreen
        
         ;; move to attr
         inc      de
         inc      de
         inc      de
         inc      de
        
         ;Set our position to top left corner again
         ; xpos --
         dec b
         ; ypos --
         dec c
        
         call   get_attr_address
         EX DE,HL
        
         ;copyattrs
         ld      bc, 32
        
         ;First
         LDI
         ;Second
         LDI
        
         ; DE=DE+BC
         EX DE,HL
         add     hl,bc
         EX DE,HL
        
         ;Third
         LDI
                  
         ;Fourth
         LDI

nxt2:    EXX
         djnz    i4chars2        
      
         ret



fspBufferAndDrawAsm:

         ld     hl, fspDataStartAsm  
         EXX
         ld     b,   4
i4chars:
         ;push   bc
         EXX
         ;Active Sprite?
         ld      a,   (hl)
         cp      99
         jr      z,   nxt1

         ;; A
         LD BC,4
         ADD HL,BC ; move past the udg part
        
         ;xpos
         ld b,(hl)
         inc hl
        
         ;ypos
         ld c,(hl)
         inc hl
                  
         inc    hl   ; move past the old co-ordinates
         inc    hl
         EX de,hl
         call   fspCopyFromScreen
        
         ;; B
         ; xpos++
         ;ld      hl, xpos
         ;inc      (hl)
         inc b
         call    fspCopyFromScreen
        
         ;; C
         ; xpos --
         ;ld      hl, xpos
         ;dec (hl)
         dec b
         ; ypos ++
         ;ld      hl, ypos
         ;inc      (hl)
         inc c
         call    fspCopyFromScreen
        
         ;; D
         ; xpos++
         ;ld      hl, xpos
         ;inc      (hl)
         inc b
         call   fspCopyFromScreen
        

         ;; Now we point to the ATTR buffer,adding 4:
         inc      de
         inc      de
         inc      de
         inc      de
        
         ;Put ourselves back at the top left corner.
         ; xpos --
         dec b
         ; ypos --
         dec c      
        
        
         call   get_attr_address
         ld      bc, 32
        
         ;First Character
         LDI  ; bc was loaded with 32 instead of 31 to account for the BC-- this instruction has.
        
         ;Second Character
         LDI
         add      hl,   bc ; we can get away with this because BC++ as well as hl--
        
         ;Third Character
         LDI
                                
         ;Fourth Character
         LDI
        
         EX DE,HL ; back to an HL pointer.

nxt1:    EXX
         djnz    i4chars
         ; Go straight into Painting new sprites for speed.

        

;; Print sprites routine
;; UDGs are labeled from  0 to 21. The addres of the UDG is:
;; *(23675) + 256 * *(23676) + 8 * N
fspDrawAsm:
         ld      hl, fspDataStartAsm      ;
         EXX
         ld      b,   4         ;
i4chars3:  
         ;push   bc
         EXX
         ld      a,   (hl)
         cp      99
         jr      z,   nxt3
        
         ;; Copy pointer "HL" to alt reg
         push HL
         exx
         pop HL
         exx
                  
         ld bc,4
         add hl,bc
        
         ; Get xpos
         LD b,(hl) ;x
         inc hl
        
         ; Get ypos
         ld c,(hl)   ;y
         inc hl
        
         push   HL ;  de  Save pointer.
        
         EXX
         LD A,(HL)
         INC HL
         EXX
        
         call   fspGetUdgAddr
         ;Draw the first character
         call   fspCopyToScreen
         ; xpos++
         inc b
        
         ; Address of the graphics
         EXX
         LD A,(HL)
         INC HL
         EXX
         call   fspGetUdgAddr
        
         ; Draw the second Character
         call   fspCopyToScreen
        
         ; xpos--
         dec b
        
         ; ypos ++
         inc c
        
         ; Address of Graphics
         EXX
         LD A,(HL)
         INC HL
         EXX
         call   fspGetUdgAddr
        
         ; Draw the third character
         call   fspCopyToScreen
        
         ; xpos ++
         inc b
        
         ; Address of next Graphic
         EXX
         LD A,(HL)
         INC HL
         EXX
         call    fspGetUdgAddr
        
         ; Draw the fourth Character
         call   fspCopyToScreen
                    
         pop      hl   ; Recover hl to point at the start of the pixel buffer
        
         ; hl = hl + 32
         ld a,l
         add a,34
         ld l,a
         jp nc, fspNoIncH
         inc h    ; hl now points to the attr data.
fspNoIncH:        
         EX DE,HL
        
         ;Reset position to top left.
         ; xpos --
         dec b
         ; ypos --
         dec c
        
         ;; attr
         call   get_attr_address
         EX DE,HL
         ;copyattrs
         ld      bc, 32
        
         ;First
         LDI
         ;Second
         LDI
        
         ; DE=DE+BC
         EX DE,HL
         add     hl,bc
         EX DE,HL
        
         ;Third
         LDI
                  
         ;Fourth
         LDI
        
         LD BC,4
         ADD HL,BC ; Move pointer to start of next block
        
nxt3:    EXX
         djnz    i4chars3
         ret


        
fspGetUdgAddr:  
         ; finds the address of the UDG # in A
         ld HL,(23675)          ;HL points to the UDG space
         rlca
         rlca
         rlca   ; a = N * 8
         ld      d, 0
         ld      e, a
         add      hl, de
         EX DE,HL
         ret
                  
        
fspUpdateAsm:          ; This name will be used from ASM
        ; For each Sprite:
         ;; *(data + 6) = *(data + 4)
         ;; *(data + 7) = *(data + 5)

         ld     hl, fspDataStartAsm+4     ; Points to sprite 1
         ld     de, fspDataStartAsm+6            
         ldi
         ldi

         ld     hl, fspDataStartAsm+4+48     ; Points to sprite 2
         ld     de, fspDataStartAsm+6+48                  
         ldi
         ldi

         ld     hl, fspDataStartAsm+4+48+48     ; Points to sprite 3
         ld     de, fspDataStartAsm+6+48+48                
         ldi
         ldi

         ld     hl, fspDataStartAsm+4+48+48+48     ; Points to sprite 4
         ld     de, fspDataStartAsm+6+48+48+48                    
         ldi
         ldi
         ret
END ASM
END SUB

SUB fspDisable (n as UByte)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspDisable" : stop: END IF
#endif
fspErase()
POKE @fspDataStart+48*n,99
end SUB

SUB fspCoord (n as uByte, x as uByte, y as uByte)   'Set sprite coords: n (sprite number);x,y (vertical,horizontal coords)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspCoord" : stop: END IF
#endif
DIM targetAddr as uInteger
targetAddr=@fspDataStart+4+48*n
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
rem targetAddr=targetAddr+1
rem POKE targetAddr,x
rem targetAddr=targetAddr+1
rem POKE targetAddr,y
END SUB

SUB fspAttrs (n as uByte, attra as uByte, attrb as uByte, attrc as uByte, attrd as uByte) 'Set sprite attrs: n (sprite number);a,b,c,d (UDG attrs)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspAttrs" : stop: END IF
#endif

DIM targetAddr as uInteger
targetAddr=@fspDataStart+40+48*n
POKE targetAddr,attra
targetAddr=targetAddr+1
POKE targetAddr,attrb
targetAddr=targetAddr+1
POKE targetAddr,attrc
targetAddr=targetAddr+1
POKE targetAddr,attrd
END SUB

FUNCTION fspAttrByte(fspInk as uByte,fspPaper as uByte,fspBright as uByte, fspFlash as uByte) as uByte
#ifndef FINAL_CODE
    if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then Print "Out Of Bounds Call in ";"fspAttrByte" : stop: END IF
#endif
    return (fspFlash shl 7) + (fspBright shl 6) + (fspPaper shl 3) + fspInk
END FUNCTION

SUB fspAttr(n as uByte,fspInk as uByte,fspPaper as uByte,fspBright as uByte, fspFlash as uByte)
#ifndef FINAL_CODE
    if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then Print "Out Of Bounds Call in ";"fspAttr" : stop: END IF
#endif
    DIM attrByte as uByte
    attrByte=fspAttrByte(fspInk,fspPaper,fspBright,fspFlash)
    fspAttrs(n,attrByte,attrByte,attrByte,attrByte)
END SUB    

SUB FASTCALL fspErase()
  asm
  call fspEraseAsm
  end asm
END SUB


SUB FASTCALL fspBufferAndDraw()
asm
call fspBufferAndDrawAsm
end asm
END SUB

SUB FASTCALL fspUpdate()
asm
call fspUpdateAsm
END ASM
END SUB


SUB fspRedraw()
asm
   halt
   ;REM Erase the sprites
   call fspEraseAsm
   ;REM Save background and
   ;REM print sprites
   call fspBufferAndDrawAsm
   ;REM update coordinates
   call fspUpdateAsm
  
end asm
END SUB


REM below this is Apenao's Demo Program:




#include <sinclair.bas>
#include <memcopy.bas>
#include <keys.bas>


100 DIM gentle (0 to 3,0 to 7) AS uByte => {  { 15, 15, 15, 15, 15, 15, 13, 15} , _
     { 240, 144, 208, 208, 240, 240, 176, 240} , _
     { 15, 14, 63, 0, 0, 12, 26, 30} , _
     { 176, 112, 252, 0, 0, 48, 104, 120}}

110 POKE Uinteger 23675, @gentle(0,0)

    CLS
    for n = 0 to 15
    print paper (n mod 7); ink ((n+3) mod 8); "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"
    next n
    
    let cx=1:let cy=1: let cmx=1:let cmy=1
    let dx=7:let dy=5: let dmx=1:let dmy=-1
    let tx=10:let ty=7: let tmx=-1:let tmy=1
    LET gx=15:LET gy=11
    
    fspInitialize (0,0,1,2,3,gx,gy) : REM (sprite number, udgA, udgB,udgC,udgD, initial X co-ordinate, initial y-coordinate)                                                      
    fspInitialize (1,0,1,2,3,dx,dy)
    fspInitialize (2,0,1,2,3,tx,ty)
    fspInitialize (3,0,1,2,3,cx,cy)
    
    fspAttr(0,WHITE,MAGENTA,FALSE,FALSE) : REM (sprite number, ink, paper, bright, flash)
    fspAttr(1,YELLOW,BLACK,FALSE,FALSE)
    fspAttr(2,GREEN,BLUE,TRUE,FALSE)
    fspAttr(3,RED,WHITE,FALSE,FALSE)
    
DO
    pause 2
    IF MULTIKEYS(KEYO) and gx>0 THEN LET gx=gx-1: END IF
    IF MULTIKEYS(KEYP) and gx<30 THEN LET gx=gx+1:END IF
    IF MULTIKEYS(KEYQ) and gy>0 THEN LET gy=gy-1 :END IF
    IF MULTIKEYS(KEYA) and gy<22 THEN LET gy=gy+1: END IF


    let dx=dx+dmx:if dx=1 or dx=30 then let dmx=-dmx : END IF
    let dy=dy+dmy:if dy=0 or dy=22 then let dmy=-dmy: END IF
    
    let tx=tx+tmx:if tx=0 or tx=30 then let tmx=-tmx: END IF
    let ty=ty+tmy:if ty=0 or ty=22 then let tmy=-tmy: END IF
    
    let cx=cx+cmx:if cx=0 or cx=30 then let cmx=-cmx: END IF
    let cy=cy+cmy:if cy=0 or cy=22 then let cmy=-cmy: END IF

    fspCoord (0,gx,gy)
    fspCoord (1,dx,dy)
    fspCoord (2,tx,ty)
    fspCoord (3,cx,cy)

    fspRedraw()

loop
Reply
#2
Great work again Britlion.
Don't worry about it flikering, I think it's more than good for its intended purpose (easy sprite control from basic). And you already did a lot on optimization.
Reply
#3
It's not actually that bad, if the user thinks about where sprites are going on the screen (and uses lower numbered sprites for anything that can go near the top) - or limits the number of sprites used, of course Big Grin
Reply
#4
Thanks BritLion / Boriel / the others involved in this project... just started tinkering with this and it's all awesome... Smile
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)