The following warnings occurred:
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval
Warning [2] Undefined array key "attachments" - Line: 20 - File: portal.php(680) : eval()'d code PHP 8.2.31 (Linux)
File Line Function
/inc/class_error.php 157 errorHandler->error
/portal.php(680) : eval()'d code 20 errorHandler->error_callback
/portal.php 680 eval




Welcome, Guest
You have to register before you can post on our site.

Username
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 277
» Latest member: Elemento26
» Forum threads: 1,087
» Forum posts: 6,490

Full Statistics

Online Users
There are currently 124 online users.
» 0 Member(s) | 122 Guest(s)
Bing, Google

Latest Threads
GuSprites
Forum: Help & Support
Last Post: zedex82
2026-06-19, 06:39 PM
» Replies: 2
» Views: 355
New video Couse / Nuevo c...
Forum: News
Last Post: Duefectu
2026-04-29, 11:02 PM
» Replies: 0
» Views: 2,697
location of heap manageme...
Forum: Help & Support
Last Post: boriel
2026-03-07, 12:13 AM
» Replies: 1
» Views: 745
non-paged supervisor code...
Forum: Help & Support
Last Post: sdo303
2026-02-20, 06:38 PM
» Replies: 8
» Views: 1,847
How to open fuse as an ex...
Forum: How-To & Tutorials
Last Post: Duefectu
2026-02-09, 01:52 PM
» Replies: 3
» Views: 1,742
Old zxbasic game errors
Forum: Help & Support
Last Post: boriel
2025-11-09, 11:52 AM
» Replies: 7
» Views: 2,537
Error: Undefined GLOBAL l...
Forum: Help & Support
Last Post: ardentcrest
2025-11-04, 05:46 PM
» Replies: 3
» Views: 1,395
A Fast(er) Plot Routine f...
Forum: How-To & Tutorials
Last Post: tubz74
2025-10-30, 03:16 PM
» Replies: 2
» Views: 1,478
Hall of Fame - Include fo...
Forum: How-To & Tutorials
Last Post: tubz74
2025-10-28, 03:48 PM
» Replies: 0
» Views: 819
[SOLVED] Array layout bug...
Forum: Bug Reports
Last Post: Zoran
2025-10-25, 05:48 PM
» Replies: 2
» Views: 1,539

 
  Incorrect Line numbers (*solved*)
Posted by: britlion - 2010-03-07, 08:16 AM - Forum: Bug Reports - Replies (6)

I'm getting an error in the code "illegal character _ at line xxx"

And there isn't one in line xxx.

I suspect that this line number comes from the assembler, and it's a line number based on the code stripped of comments and white space perhaps?

I've noticed that it's sometimes hard to pin down the correct line number for errors before.

Print this item

  SVN
Posted by: britlion - 2010-03-06, 07:55 PM - Forum: ZX Basic Compiler - Replies (6)

I've never used it at all; but I hear that it's a nice code control system.

How does one get going then?

Do you use windows, or linux?

Print this item

  Fourspriter: Alternate Version
Posted by: britlion - 2010-03-06, 06:29 AM - Forum: How-To & Tutorials - Replies (47)

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

Print this item

  Stack Bug in ZXB 1.2.5-r1489 ?
Posted by: britlion - 2010-03-05, 05:07 PM - Forum: Bug Reports - Replies (19)

I was updating fourspriter to use SUBs as opposed to gotos. Probably slower, but a little cleaner on the interface to the user.

Anyway, as it stands the main program:

Calls initialize for the first sprite
which calls a dummy "data" routine (which is really just for asm. The first line is return. It only calls it to make sure it's not peephole optimized out)
Comes back to main
calls coords for the first sprite
comes back to main
calls initialize for the second sprite
which calls the dummy data routine again......

and then it goes wrong. It never comes back from this data routine this second time, even though it should return immediately. In fact, it restarts the main program again. Over and over - and then after a few cycles, crashes.


I confirmed the code does the same thing with 1.23 and 1.22; and I didn't think they had the stack bugs, so it could be something I'm doing wrong, I suppose. I can't really see what could go wrong with "return" being the only statement, though.

The code is a bit complex, and is unfinished; but hopefully you can see where I'm going. It has testing PRINTs in it. Just copy and paste and see if it behaves the same way for you.

Code:
SUB fspInitialize (n as uByte, a as uByte, b as uByte, c as uByte, d 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) PRINT AT 0,0;"Initialize" : PAUSE 1: PAUSE 0 fspData() : REM call the data sub to make sure it isn't optimized out of the compile. That call just returns. PRINT AT 2,0;"FSPData came back to Initialize" : PAUSE 1: PAUSE 0 IF n>3 OR a>20 or b>20 or c>20 or d>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) POKE targetAddr,a targetAddr=targetAddr+1 POKE targetAddr,b targetAddr=targetAddr+1 POKE targetAddr,c targetAddr=targetAddr+1 POKE targetAddr,d PRINT AT 3,0;"We're at the end of Initialize" PAUSE 1:PAUSE 0 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,fspAttrByte,fspAttrByte,fspAttrByte,fspAttrByte) END SUB SUB fspData() fspDataStart: RETURN : REM if called we should return immediately. This sub is for data and ASM only. REM This routine contains Fourspriter data and code that is called by other routines. In effect, it's a dummy subroutine. REM It is called by fspInitialize in order to ensure it is included in the build. 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 0,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 0,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 0,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 0,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: 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 END ASM END SUB SUB fspErase() asm ;; 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; ; } ld de, datap ; ld b, 4 ; i4chars2: push bc ;; ld a, (de) cp 99 jr z, nxt2 ;; A inc de inc de inc de inc de inc de inc de ; Obtenemos xpos = CX ld a, (de) ld hl, xpos ld (hl), a inc de ; Obtenemos ypos = CY ld a, (de) ld hl, ypos ld (hl), a inc de ;; 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 ; 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 ld a, (hl) dec a ld (hl), a ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; call copyattrs ; nxt2: pop bc djnz i4chars2 jp fspErase_end borra_char: call get_scr_address ; ld l, c ld h, b ; 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 fspErase_end: end asm END SUB SUB fspBufferBackground() asm 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 inc de inc de inc de inc de ; ld a, (de) ld hl, xpos ld (hl), a inc de ; ld a, (de) ld hl, ypos ld (hl), a inc de inc de inc de ;; B ; call copia_char ; xpos++ ld hl, xpos inc (hl) ;; C ; call copia_char ; xpos -- ld hl, xpos ld a, (hl) dec a ld (hl), a ; 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 ld a, (hl) dec a ld (hl), a ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; ld l, c ld h, b ld bc, 31 ld a, (hl) ld (de), a ; Primer carácter inc hl inc de ld a, (hl) ld (de), a ; Segundo carácter add hl, bc inc de ld a, (hl) ld (de), a ; Tercer carácter inc hl inc de ld a, (hl) ld (de), a ; Cuarto carácter inc de ; Fin del bucle nxt1: pop bc djnz i4chars ;ret jp fspBufferBackground_end 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 ; Subroutines don't need to return themselves. fspBufferBackground_end: ;; Routine to move the sprites: end asm END SUB SUB fspRedraw() asm halt end asm ';call borra_sprites ; Erase the sprites fspErase() ';call init_sprites ; Save background fspBufferBackground() ';call draw_sprites ; print sprites fspDraw() ';call update_coordinates ; update coordinates :) fspUpdate() ';ret END SUB SUB fspDraw() asm ;; Print sprites routine ;; UDGs are labeled from 0 to 21. The addres of the UDG is: ;; *(23675) + 256 * *(23676) + 8 * N 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 a, (de) ld hl, xpos ld (hl), a inc de ; Obtenemos ypos ld a, (de) ld hl, ypos ld (hl), a inc de inc de inc de push de ld hl, bufchars call getde call docopy ; xpos++ ld hl, xpos inc (hl) ; Dirección del gráfico ld hl, bufchars 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 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 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 ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; call copyattrs inc de inc de inc de inc de ; nxt3: pop bc djnz i4chars3 ;ret jp fspDraw_end ;; 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 ;; 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 ret bufchars: defb 0,0,0, 0 ; charsabuff: ld hl, bufchars ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc 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 fspDraw_end: END ASM END SUB SUB fspUpdate() asm 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 #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) PRINT AT 5,0;"We came back from Initialize the first time" : PAUSE 1: PAUSE 0 fspCoord (0,gy,gx) PRINT AT 6,0;"We came back from fspCoord the first time": PAUSE 1: PAUSE 0 CLS fspInitialize (1,0,1,2,3) PRINT AT 7,0;"We came back from Initialize the second time" : PAUSE 1: PAUSE 0 fspCoord (1,dy,dx) fspInitialize (2,0,1,2,3) fspCoord (2,ty,tx) fspInitialize (3,0,1,2,3) fspCoord (3,cy,cx) fspAttrs (0,31,31,31,31) fspAttrs (1,12,12,12,12) fspAttrs (2,26,26,26,26) fspAttrs (3,6,6,6,6) DO 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 pause 2 fspCoord (0,gx,gy) fspCoord (1,dx,dy) fspCoord (2,tx,ty) fspCoord (3,cx,cy) fspRedraw() loop

Print this item

  ZX Assembler Inline Basic
Posted by: britlion - 2010-03-04, 05:51 AM - Forum: How-To & Tutorials - No Replies

ZXB allows you to do stuff that's pretty crazy.

I had a fancy cls routine - that didn't actually clear the screen, just ran a attribute bar over the screen, and then of course you do a CLS from basic afterwards. The effect is the same, but it looks cool.

It wouldn't compile right - the call to the ZX ROM sound routine kept corrupting things. Probably messing with IX and IY registers in naughty ways.

Then it occurred to me.... just compile a beep into the middle. :-)

Look:

Code:
sub clearscreen() asm ld hl, 22528 ; 28000 33 0 88 10ts ld de, 32 ; 28003 17 32 0 10ts l_6d66: push de ; 28006 213 11ts push hl ; 28007 229 11ts ld de, 32 ; 28008 17 32 0 10ts ld b, 24 ; 28011 6 22 7ts l_6d6d: ld (hl), 24 ; Pick your leading bar attribute colour here. add hl, de ; 28015 25 11ts djnz l_6d6d ; 28016 16 251 8/13ts push de ; save our registers before dropping to compiled code. end asm beep .05,-20 asm pop de ; recover our register pop hl ; (we didn't need to save HL, because we're resetting it) push hl ; 28032 229 11ts ld b, 24 ; 28033 6 22 7ts l_6d83: ld (hl), 63 ; Pick you final attribute here add hl, de ; 28037 25 11ts djnz l_6d83 ; 28038 16 251 8/13ts pop hl ; 28040 225 10ts pop de ; 28041 209 10ts dec de ; 28042 27 6ts inc hl ; 28043 35 6ts ld a, d ; 28044 122 4ts or e ; 28045 179 4ts jr nz, l_6d66 ; 28046 32 214 7/12ts end asm cls : rem actually wipe the bits off the screen - we just ran an attribute bar before! end sub CLS Print "hello world" pause 1 pause 0 clearscreen() print "Hello!"

How beautiful is that?
So long as I stack the registers I need (in this case DE), I can find them again when I come back. In a way, it's inline basic in the middle of assembler :-)

Anyway, that's my silliness for the day. Also: This makes for a nice clear screen subroutine. Change the bar colour and the final colour where indicated (and remember, ink and paper should be the same, so it does 'clear' it by hiding any characters on the screen until the CLS.

It's also pretty easy to change the sound (or remove it altogether - but you need something there to slow it down, or you don't see the effect. A busy loop or a pause would work too).

Print this item

  INT (fixed) doesn't work (*solved*)
Posted by: britlion - 2010-03-02, 06:19 PM - Forum: Bug Reports - Replies (3)

Version zxb 1.2.5-r1489

Code:
DIM a as fixed LET a=3.5 print a print INT(a)

Returns
3.5
229376

I think it's getting bit converted to a long. If I do cast(fixed,int(a))) I get back 3.5

Print this item

  Fourspriter: Sprite Engine from the Mojon Twins
Posted by: apenao - 2010-03-01, 10:10 PM - Forum: How-To & Tutorials - Replies (11)

As I promissed, here I am with an adaptation for the Mojon Twins "Fourspriter". Well, in fact I promissed just the opposite (skip this one and post the other sprite routine I'm working in), but I think this one is more suited to a basic enviroment and the other one is just great for a crap game (see my game Colour Clash of the Titans in the CSSCGC 2010 thread within the WoS games forum) but not so good if you plan to do a polished game.

The engine itself comes in assembler, and all you have to do is POKE the right adresses to configure and move your sprites (up to 4). All I have done is adding 3 subroutines to make easier the poking part, and 4 labes so you can call the different subroutines of the engine via gosub.

Here goes the code you have to include in your library folder (I called it fourspriter.bas):

Code:
SUB fsprite (n as uByte, a as uByte, b as uByte, c as uByte, d as uByte) 'Initialite Sprite: n (sprite number 0-3);a,b,c,d (UDG number 0-20,99 means the sprite won't be printed) POKE @fourspriter+48*n,a POKE @fourspriter+1+48*n,b POKE @fourspriter+2+48*n,c POKE @fourspriter+3+48*n,d END sub SUB fspritecoord (n as uByte, x as uByte, y as uByte) 'Set sprite coords: n (sprite number);x,y (vertical,horizontal coords) POKE @fourspriter+4+48*n,x POKE @fourspriter+5+48*n,y POKE @fourspriter+6+48*n,x POKE @fourspriter+7+48*n,y END SUB SUB fspriteattr (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) POKE @fourspriter+40+48*n,attra POKE @fourspriter+41+48*n,attrb POKE @fourspriter+42+48*n,attrc POKE @fourspriter+43+48*n,attrd END SUB fourspriter: 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 40 bytes long. ;; Sprite 1 udgs1: defb 0, 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 ; Gfx buffer. attrs1: defb 7, 7, 7, 7 ; Sprite ATTRs buffatrs1: defb 0,0,0, 0 ; Attr's buffer ;; Sprite 2 udgs2: defb 0, 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 0, 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 0, 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 ;; ;; Routine to save the background to the buffer end asm initsprites: asm 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 inc de inc de inc de inc de ; ld a, (de) ld hl, xpos ld (hl), a inc de ; ld a, (de) ld hl, ypos ld (hl), a inc de inc de inc de ;; B ; call copia_char ; xpos++ ld hl, xpos inc (hl) ;; C ; call copia_char ; xpos -- ld hl, xpos ld a, (hl) dec a ld (hl), a ; 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 ld a, (hl) dec a ld (hl), a ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; ld l, c ld h, b ld bc, 31 ld a, (hl) ld (de), a ; Primer carácter inc hl inc de ld a, (hl) ld (de), a ; Segundo carácter add hl, bc inc de ld a, (hl) ld (de), a ; Tercer carácter inc hl inc de ld a, (hl) ld (de), a ; Cuarto carácter inc de ; Fin del bucle nxt1: pop bc djnz i4chars 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 ;; Routine to move the sprites: end asm updatesprite: asm update_sprites: halt call borra_sprites ; Erase the sprites call init_sprites ; Save background call draw_sprites ; print sprites call update_coordinates ; update coordinates :) ret ;; Routine to erase the sprites: end asm borrasprites: asm 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; ; } ld de, datap ; ld b, 4 ; i4chars2: push bc ;; ld a, (de) cp 99 jr z, nxt2 ;; A inc de inc de inc de inc de inc de inc de ; Obtenemos xpos = CX ld a, (de) ld hl, xpos ld (hl), a inc de ; Obtenemos ypos = CY ld a, (de) ld hl, ypos ld (hl), a inc de ;; 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 ; 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 ld a, (hl) dec a ld (hl), a ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; call copyattrs ; nxt2: pop bc djnz i4chars2 ret borra_char: call get_scr_address ; ld l, c ld h, b ; 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 ;; Print sprites routine ;; UDGs are labeled from 0 to 21. The addres of the UDG is: ;; *(23675) + 256 * *(23676) + 8 * N bufchars: defb 0,0,0, 0 ; end asm drawsprites: asm 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 a, (de) ld hl, xpos ld (hl), a inc de ; Obtenemos ypos ld a, (de) ld hl, ypos ld (hl), a inc de inc de inc de push de ;; ld hl, bufchars call getde ;; call docopy ; xpos++ ld hl, xpos inc (hl) ; Dirección del gráfico ld hl, bufchars 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 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 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 ; ypos -- ld hl, ypos ld a, (hl) dec a ld (hl), a ; call get_attr_address ; call copyattrs inc de inc de inc de inc de ; nxt3: pop bc djnz i4chars3 ret charsabuff: ld hl, bufchars ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a inc de inc 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 end asm updatecoordinates: asm 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 ;; 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 ;; 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 ret docopy: 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 end asm

The subroutines are the following:

1) Define your sprites:

fsprite (n,a,b,c,d) ; where n is the sprite number (0 to 3) and a-d are the UDGs number that compose the sprite (0-21)

2) Set the atributes for each of the sprite's UDG:

fspriteattr (n,a,b,c,d) ; n=sprite number ; a-d=attr of each UDG

3) Set the initial coords for your spries:
fspritecoord (n,y,x) ; n= sprite number ; y=vertical coord ; x=horizontal coord

The labels to be called via gosub are:

borrasprites ; this will erase the sprites, restoring the background.

initsprites ; this will select wether a sprite will be printed or not. If you don't want a sprite to be printed, you have to poke 99 into the first value of the sprite udg (i.e. fsprite (1,99,0,0,0) won't print sprite 1)

drawsprites ; this will draw the sprites on the screen

updatecoordinates ; this will update the current coordinates of each sprite, while storing the old ones so you can restore the background.

Print this item

  "Register" command
Posted by: britlion - 2010-02-27, 05:47 AM - Forum: Wishlist - No Replies

I was thinking that it might be incredibly useful, for inline assembly to be able to set what's on the registers as you hit z80 asm.

And a command to do that, that works in a similar way to a function call, might be a good way to do it and avoid a lot of the overhead of a function call for very small jobs.

How about a register and an unregister command?

Register - takes a variable and puts it in the current registers
unregister - takes registers and puts them into a variable.

So you could do something like:

DIM a as long
register a
asm
LD D,0
end asm
unregister a

To kill off the top byte of a long, if that was your whimsy.

Not sure if other people would find this useful, but I could imagine ways it could be useful.

How about:
dim a as fixed
dim b as uinteger
(do things)
register a
unregister b

to put the decimal part of a into b if you wanted to deal with the integer part and the decimal part separately. You could even shunt it back later...

Anyway, just a crazy thought from me, as per usual :-)

Print this item

  MOD doesn't work with type FIXED (*solved*)
Posted by: britlion - 2010-02-25, 07:18 AM - Forum: Bug Reports - Replies (2)

I did wonder if this was a bug or not, since it might be odd to modulus a floating point number.

But mod works perfectly with type FLOAT, so logically it should also work well with type FIXED. Fixed division works, and the compiler seems to know where the code for mod should be - at label _MODF16.

Error: Undefined label '__MODF16'

Print this item

  Improved bAND, bOR, bXOR
Posted by: britlion - 2010-02-25, 04:33 AM - Forum: How-To & Tutorials - Replies (4)

Here are more general purpose functions for AND, OR and NOT. They work with Byte, Integer or Long types (the function is long, but the cast works upscale).

A binary NOT is tougher - what bits do you invert? If you send me 8, and I invert 32 is that what you wanted?
(of course, you could bAND the result into the last 8 or 16 bits)

These functions are very similar, of course.

Binary AND:

Code:
Function bAND(a as uLong, b as uLong) as uLong asm POP BC ; something EXX POP HL ; return address EXX POP HL ; aL POP DE ; aH POP BC ; bL LD A,B AND H LD H,A LD A,C AND L LD L,A POP BC ;bH LD A,B AND D LD D,A LD A,C AND E LD E,A EXX PUSH HL ; put return address back EXX end asm END function

Binary OR:
Code:
Function bOR(c as uLong, d as uLong) as uLong asm POP BC ; something EXX POP HL ; return address EXX POP HL ; aL POP DE ; aH POP BC ; bL LD A,B OR H LD H,A LD A,C OR L LD L,A POP BC ;bH LD A,B OR D LD D,A LD A,C OR E LD E,A EXX PUSH HL ; put return address back EXX end asm END function

Binary XOR:
Code:
Function bXOR(e as uLong, f as uLong) as uLong asm POP BC ; something EXX POP HL ; return address EXX POP HL ; aL POP DE ; aH POP BC ; bL LD A,B XOR H LD H,A LD A,C XOR L LD L,A POP BC ;bH LD A,B XOR D LD D,A LD A,C XOR E LD E,A EXX PUSH HL ; put return address back EXX end asm END function

Here's the 32 bit NOT function. This is the one that's tricky. If you want a NOT in 8 bits, do bAND(255,bNOT32(num)) - in other words, AND it to cut the length you want; or you are going to be confused at the result being a LOT bigger than you wanted.

8 bit result: bAND with 255
16 bit result: bAND with 65535

If you want the function, it's probably obvious to you. You could also use bNOT16 and bNOT8 listed in the previous thread - and if you always want 16 or 8 bit values, that's the smaller and faster route.

32 Bit Binary NOT:
Code:
FUNCTION FASTCALL bNOT32(g as uLong) as uLong asm LD A,D CPL LD D,A LD A,E CPL LD E,A LD A,H CPL LD H,A LD A,L CPL LD L,A end asm END FUNCTION

Print this item