The following warnings occurred:
Warning [2] Undefined array key 0 - Line: 1677 - File: showthread.php PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/showthread.php 1677 errorHandler->error_callback
/showthread.php 916 buildtree




Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Fourspriter: Alternate Version
#1
I've made quite a few changes to the actual FourSpriter code. I have made it significantly shorter, and not a small amount faster in execution, using tricks like LDI to copy directly from (HL) to (DE) as opposed to copying into and out of the A register. I removed at least one redundant section of code that was identical with another one - redirecting calls took care of that. There are also places that use bafflingly long code to do something simple. For example:

Code:
ld hl, 23676 ;10 ld a, (hl) ; 7 ld e, a ; 4 ld hl, 23675 ;10 ld a, (hl) ; 7 ld l, a ; 4 ld a, e ; 4 ld h, a ; 4=50

Is used to get the address of the UDG into the HL register. It takes 50 T states to do this, whereas:
Code:
ld HL,(23675)
Does it in one instruction, and 16 T states.

I know I'm not really that good with Z80 code....but stuff like that leaves me scratching my head.


Anyway, It still seems to catch the ray trace at the top of the screen between erase and draw (which means that for the first frame after updating sprites, you can't see them on the top 24 lines (or 3 characters). It was 26, but I managed to speed it up enough T states to creep back 2 lines Smile

My advice, unless I (or someone else) can work a way around this is to use this area for status bars - or at least avoid running sprites into it.

I tried to make it more user friendly than the original - now all Sprite actions can be controlled fairly easily with calls to routines and functions. Most of these are used internally, and the chances are all a user will need are the routines to initialize, color, move and redraw moved sprites.

Usage:
Code:
fspInit (sprite#, graphicsA,graphicsB,graphicsC,graphicsD,x,y) Sets sprite sprite # to use the four UDG's listed from 0-20. This also turns on an inactive sprite. fspDisable(Sprite#) Shuts down a sprite (internally by setting its first graphic to 99). fspCoord(sprite#,x,y) Sets the new co-ordinates for a sprite. fspAttrs(sprite#, AttrA,AttrB,AttrC,AttrD) Sets a sprite to have attributes A,B,C,D. Note: This is the advanced use, that allows different bits of the sprite to be different colours. fspAttrByte(ink,paper,bright,flash) as uByte returns an attribute byte equivalent to the input ink,paper,bright and flash. This function is used by the following subroutine: fspAttr (sprite#,ink,paper,bright,flash) Sets ALL four attributes of a sprite to the input values for ink,paper,bright and flash. This is expected to be the common usage. Note: Color words are #defined, so you can use fspAttr(0,WHITE,BLACK,FALSE,FALSE) to set a sprite to white on black, no bright, no flash. fspRedraw() Back buffer -> screen (erasing sprites) screen -> back buffer (save background) sprites -> screen (srawing sprites) current location -> last location (update location) In essence it moves the sprites to their new coordinates It effectively, internally uses the following routines, that the user may not need to call. They are made available, however, in case the programmer needs more direct control of sprite activity. fspErase() Erases all the sprites and replaces them with their background data. fspBufferBackgroundAndDraw() Copies screen to buffer and redraws the sprites onto the screen. fspUpdate() Updates co-ordinates


The actual code. Note that Apenao's example program is already attached at the end.

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) IF n>3 OR (udgA>20 AND udgA<>99) or udgB>20 or udgC>20 or udgD>20 then return : END IF : REM Bounds checking - so a bad function call doesn't poke all over memory. 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. ;; Pergreñado por na_th_an ;; Uses UDGs (it reads the address from the system variables). ;; It moves up to 4 sprites, preserving the backgrounds. datap: ; 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 7,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 ;; General use coordinates xpos: defb 0 ypos: defb 0 cxpos: defb 0 cypos: defb 0 ;; 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 docopy: borra_char: call get_scr_address ; Dirección (xpos, ypos) en BC ld l, c ld h, b ; Dirección (xpos, ypos) en 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 ;; Esta función devuelve en bc la dirección de memoria ;; de la posición (xpos, ypos) en carácteres. Extraído de código ;; por BloodBaz. get_scr_address: push de ld a, (xpos) and 31 ld c, a ld a, (ypos) ld d, a and 24 add a, 64 ld b, a ld a, d and 7 rrca rrca rrca or c ld c, a pop de ret ;; Esta función devuelve en bc la dirección de memoria ;; de la posición (xpos, ypos) dentro del archivo de atributos. ;; Adaptado de código por Jonathan Cauldwell. ;; 010110YY YYYXXXXX get_attr_address: ld a, (ypos) ; Cogemos y rrca rrca rrca ; La multiplicamos por 32 ld c, a ; nos lo guardamos en c and 3 ; ponemos una mascarita 00000011 add a, 88 ; 88 * 256 = 22528, aquí empieza el tema ld b, a ; Hecho el bite superior. ld a, c ; Nos volvemos a traer y * 32 and 224 ; Mascarita 11100000 ld c, a ; Lo volvemos a poner en c ld a, (xpos) ; Cogemos x add a, c ; Le sumamos lo que teníamos antes. ld c, a ; Listo. Ya tenemos en BC la dirección. ret spare: defb 85,85,85,85,85,85,85,85 ;; Routine to save the background to the buffer ;borra_sprites: ; pointer = datap ; for (i = 0; i < 4; i ++) { ; CX = *(pointer + 6) ; CY = *(pointer + 7) ; scr_address = get_scr_address (CX, CY); ; for (j = 0; j < 8; j ++) { ; *scr_address = *(pointer + 8 + j); ; scr_address += 256; ; } ; scr_address = get_scr_address (CX+1, CY); ; for (j = 0; j < 8; j ++) { ; *scr_address = *(pointer + 16 + j); ; scr_address += 256; ; } ; scr_address = get_scr_address (CX, CY+1); ; for (j = 0; j < 8; j ++) { ; *scr_address = *(pointer + 24 + j); ; scr_address += 256; ; } ; scr_address = get_scr_address (CX+1, CY+1); ; for (j = 0; j < 8; j ++) { ; *scr_address = *(pointer + 32 + j); ; scr_address += 256; ; } ; pointer += 40; ; } fspEraseAsm: ld de, datap ; ld b, 4 ; i4chars2: push bc ;; ld a, (de) cp 99 jr z, nxt2 ;; A LD BC,6 ; 10 EX DE,HL ; 4 ADD HL,BC ; 11 ; EX DE,HL ; 4 ;inc de ;inc de ;inc de ;inc de ;inc de ;inc de ; Obtenemos xpos = CX ld DE, xpos ;ld a, (de) ;ld (hl), a LDI ;inc de ; Obtenemos ypos = CY ld DE, ypos ;ld a, (de) ;ld (hl), a ;inc de LDI EX DE,HL ;; B ; Pintamos el primer char call borra_char ; xpos++ ld hl, xpos inc (hl) ;; C ; Pintamos el segundo char call borra_char ; xpos -- ld hl, xpos ;ld a, (hl) ;dec a ;ld (hl), a dec (hl) ; ypos ++ ld hl, ypos inc (hl) ;; D ; Pintamos el tercer char call borra_char ; xpos++ ld hl, xpos inc (hl) ;; E ; Pintamos el cuarto char call borra_char ;; attr inc de inc de inc de inc de ;; ; xpos -- ld hl, xpos dec (hl) ; ypos -- ld hl, ypos dec (hl) ; call get_attr_address ; call copyattrs ; nxt2: pop bc djnz i4chars2 ret copia_char: call get_scr_address ; ld l, c ld h, b ; 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 fspBufferAndDrawAsm: init_sprites: ; pointer = datap ; for (i = 0; i < 4; i ++) { ; X = *(pointer + 4); // A ; Y = *(pointer + 5); ; scr_address = get_scr_address (X, Y); // B ; for (j = 0; j < 8; j ++) { ; *(pointer + 8 + j) = *scraddress; ; scraddress += 256; ; } ; scr_address = get_scr_address (X+1, Y); // C ; for (j = 0; j < 8; j ++) { ; *(pointer + 16 + j) = *scraddress; ; scraddress += 256; ; } ; scr_address = get_scr_address (X, Y+1); // D ; for (j = 0; j < 8; j ++) { ; *(pointer + 24 + j) = *scraddress; ; scraddress += 256; ; } ; scr_address = get_scr_address (X+1, Y+1); // E ; for (j = 0; j < 8; j ++) { ; *(pointer + 32 + j) = *scraddress; ; scraddress += 256; ; } ; pointer = pointer + 40; ; } ld de, datap ; ld b, 4 ; i4chars: push bc ;; ld a, (de) cp 99 jr z, nxt1 ;; A EX DE,HL LD BC,4 ADD HL,BC ;inc de ;inc de ;inc de ;inc de ; ld DE, xpos ;ld a, (de) ;ld (hl), a ;inc de LDI ; ld DE, ypos ;ld a, (de) ;ld (hl), a ;inc de LDI EX DE,HL inc de inc de ;; B ; call copia_char ; xpos++ ld hl, xpos inc (hl) ;; C ; call copia_char ; xpos -- ld hl, xpos dec (hl) ; ypos ++ ld hl, ypos inc (hl) ;; D ; call copia_char ; xpos++ ld hl, xpos inc (hl) ;; E ; call copia_char ;; Now we point to the ATTR buffer,adding 4: inc de inc de inc de inc de ;; ; xpos -- ld hl, xpos dec (hl) ; ypos -- ld hl, ypos dec (hl) ; call get_attr_address ; ld l, c ld h, b ld bc, 32 ;ld a, (hl) ;ld (de), a ; Primer carácter ;inc hl ;inc de LDI ; bc was loaded with 32 instead of 31 to account for the BC-- this instruction has. ;ld a, (hl) ;ld (de), a ; Segundo carácter ;inc de LDI add hl, bc ; we can get away with this because BC++ as well as hl-- ;ld a, (hl) ;ld (de), a ; Tercer carácter ;inc hl ;inc de LDI ;ld a, (hl) ;ld (de), a ; Cuarto carácter ;inc de LDI ; Fin del bucle nxt1: pop bc djnz i4chars ; ret - No! 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: draw_sprites: ld de, datap ; ld b, 4 ; i4chars3: push bc ld a, (de) cp 99 jr z, nxt3 ;; copiar aquí "de" a un buffer call charsabuff ; Obtenemos xpos ld hl, xpos EX DE,HL ;ld a, (de) ;ld (hl), a ;inc de LDI ; Obtenemos ypos ;ld a, (de) ld DE, ypos ;ld (hl), a ;inc de LDI inc HL ; de inc HL ; de push HL ; de ld hl, bufchars call getde call docopy ; xpos++ ld hl, xpos inc (hl) ; Dirección del gráfico ld hl, bufchars+1 ;inc hl call getde ; Pintamos el segundo char call docopy ; xpos-- ld hl, xpos ld a, (hl) dec a ld (hl), a ; ypos ++ ld hl, ypos inc (hl) ; Dirección del gráfico ld hl, bufchars+2 ;inc hl ;inc hl call getde ; Pintamos el tercer char call docopy ; xpos ++ ld hl, xpos inc (hl) ; Dirección del gráfico ld hl, bufchars+3 ;inc hl ;inc hl ;inc hl call getde ; Pintamos el cuarto char call docopy pop de ; de = de + 32 ld bc, 32 ld h, d ld l, e add hl, bc ld d, h ld e, l ;; attr ; xpos -- ld hl, xpos ;ld a, (hl) ;dec a ;ld (hl), a dec (hl) ; ypos -- ld hl, ypos ;ld a, (hl) ;dec a ;ld (hl), a dec(hl) ; call get_attr_address ; call copyattrs inc de inc de inc de inc de ; nxt3: pop bc djnz i4chars3 ret ;; Esta función calcula la posición en memoria del UDG apuntado por ;; el número que está en la dirección hl. getde: ld a, (hl) ;ld d, a ;ld hl, 23676 ;ld a, (hl) ;ld e, a ;ld hl, 23675 ;ld a, (hl) ;ld l, a ;ld a, e ;ld h, a ld HL,(23675) ;; HL = *(23675) + 256 * *(23676) ;ld a, d rlca rlca rlca ; a = N * 8 ld d, 0 ld e, a add hl, de ;ld d, h ;ld e, l EX DE,HL ret bufchars: defb 0,0,0, 0 ; charsabuff: ld hl, bufchars EX DE,HL LDI LDI LDI LDI EX DE,HL ret copyattrs: ld l, c ld h, b ld bc, 31 ld a, (de) ld (hl), a ; Primer carácter inc hl inc de ld a, (de) ld (hl), a ; Segundo carácter add hl, bc inc de ld a, (de) ld (hl), a ; Tercer carácter inc hl inc de ld a, (de) ld (hl), a ; Cuarto carácter inc de ; Ahora de apunta a datap+48, o sea, al siguiente sprite. ret fspUpdateAsm: update_coordinates: ld de, datap ; Apuntamos a la zona de datos ld hl, datap ; idem ld b, 4 ; 4 iteraciones i4chars4: push bc ;; Para cada sprite hay que hacer: ;; *(datap + 6) = *(datap + 4) ;; *(datap + 7) = *(datap + 5) inc de inc de inc de inc de ld a, (de) ; a = X inc hl inc hl inc hl inc hl inc hl inc hl ld (hl), a ; CX = a inc de ld a, (de) ; a = Y inc hl ld (hl), a ; CY = a inc hl ;; hl = hl + 40 ld bc, 40 add hl, bc ;; de = hl ld d, h ld e, l pop bc djnz i4chars4 ret END ASM END SUB SUB fspDisable (n as UByte) IF n>3 then return : END IF 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) IF n>3 then return : END IF 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) IF n>3 then return : END IF 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 if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then return 0: END IF: Rem bounds check 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) if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then return : END IF: Rem bounds check 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) memcopy (0,16384,4092) LET gx=10:LET gy=10:let dx=3:let dy=3:let tx=6:let ty=6:let cx=18:let cy=18 let dmx=1:let dmy=1:let tmx=-1:let tmy=-1:let cmx=1:let cmy=-1 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)<>0 and gx>0 THEN LET gx=gx-1: END IF IF MULTIKEYS(KEYP)<>0 and gx<30 THEN LET gx=gx+1:END IF IF MULTIKEYS(KEYQ)<>0 and gy>0 THEN LET gy=gy-1 :END IF IF MULTIKEYS(KEYA)<>0 and gy<22 THEN LET gy=gy+1: END IF let dx=dx+dmx:if dx=0 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


Messages In This Thread

Forum Jump:


Users browsing this thread: 1 Guest(s)