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

Username
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 258
» Latest member: manuelzo75
» Forum threads: 1,074
» Forum posts: 6,434

Full Statistics

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

Latest Threads
.tap file code not execut...
Forum: Help & Support
Last Post: Zoran
04-28-2025, 10:59 AM
» Replies: 4
» Views: 194
Exit from more than one l...
Forum: Wishlist
Last Post: Duefectu
04-23-2025, 10:06 PM
» Replies: 3
» Views: 262
put small ASM programs li...
Forum: How-To & Tutorials
Last Post: Zoran
04-18-2025, 02:02 PM
» Replies: 6
» Views: 1,526
Creating +3 Menus - Loadi...
Forum: Help & Support
Last Post: merlinkv
04-16-2025, 02:08 PM
» Replies: 6
» Views: 517
Randomize not very random...
Forum: Help & Support
Last Post: Zoran
04-08-2025, 10:40 AM
» Replies: 4
» Views: 419
Scope rules
Forum: Bug Reports
Last Post: Zoran
04-04-2025, 09:46 AM
» Replies: 2
» Views: 290
Using constants not allow...
Forum: Bug Reports
Last Post: baltasarq
03-19-2025, 10:00 PM
» Replies: 8
» Views: 1,016
404 page not found
Forum: Documentation
Last Post: boriel
03-08-2025, 07:16 PM
» Replies: 5
» Views: 2,855
Spectrum keywords codes
Forum: Bug Reports
Last Post: boriel
03-08-2025, 11:00 AM
» Replies: 1
» Views: 398
ZXodus][Engine
Forum: ZX Basic Compiler
Last Post: boriel
02-19-2025, 11:43 PM
» Replies: 69
» Views: 213,491

 
  INT (fixed) doesn't work (*solved*)
Posted by: britlion - 03-02-2010, 05: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 - 03-01-2010, 09: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 - 02-27-2010, 04: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 - 02-25-2010, 06: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 - 02-25-2010, 03: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

  8 digit hexadecimal numbers (*solved*)
Posted by: britlion - 02-25-2010, 12:35 AM - Forum: Bug Reports - Replies (2)

Print $1234567 compiles.

Print $12345678 does not.

Since this happily fits into a LONG type, just like the first one, it looks like a buglet.

Print this item

  Bit Shifts
Posted by: britlion - 02-22-2010, 11:39 PM - Forum: How-To & Tutorials - Replies (11)

C provides bit shift functions >> and << which aren't available in Sinclair or ZX basic. Since I happened to have a need for such a function, I decided to drop them up here. Again, this is something that's easy on the CPU.

They work with Long Unsigned integers - and will work fine with smaller values, or you can use them as a template for 16 or 8 bit versions in assembler.

Code:
FUNCTION bitL(num as uLong) as uLong
  REM we get DEHL as uLong
  asm
   SLA  L
   RL  H
   RL  E
   RL  D
  END asm
END FUNCTION

Code:
FUNCTION bitR(num as uLong) as uLong
  REM we get DEHL as uLong
  asm
   SRA  D
   RES 7,D ; makes a zero shift into D instead of what was there originally. If you want this to work with signed numbers, you could always change it to res 6,d.
   RR  E
   RR  H
   RR  L
  END asm
END FUNCTION

Print this item

  Faster Trigonometry
Posted by: britlion - 02-20-2010, 03:39 AM - Forum: How-To & Tutorials - Replies (3)

Sometimes we're willing to lose accuracy for speed. ZX BASIC uses the spectrum's ROM routines for many of its math functions, and as a result of them being general purpose routines that use 40 bit numbers, well, they can be a bit slow sometimes.

See my article on square roots, for a good example.

Here is a routine to produce SIN(angle) - where angle is in degrees. It uses a lookup table to calculate sin values, and it's a very quick and dirty method, in that it only actually knows 32 angles, and those to only 8 bit precision. It does linear interpolation between these known angles, however, so that does improve things somewhat.

I was actually surprised how precise it was - it's good for at least 2 decimal places, probably 3 as a rule of thumb. The average error is 0.002 That's probably good enough for games that need to calculate angles. It's about 4-5 times faster than the SIN(FLOAT) function, and not even written in native assembler.

If you need better accuracy, it would be fairly easy to change the method to use a bigger table - perhaps 2 bytes per entry, even.

Remember to work out COS and TAN can also use this function - COS is SIN(90+x) and TAN is SIN(x)/COS(x). It should be easy to write COSINE and TANGENT functions to do the adjustments and call the SINE function.

(And this one I didn't copy. It's all mine! Bugs and all. And now it's free for anyone to use.)

Code:
FUNCTION SINE(num as FIXED) as FIXED
DIM quad as byte
DIM est1,dif as uByte

while num>360
  num=num-360
end while

IF num>180 then
    quad=-1
    num=num-180
ELSE
    quad=1
END IF

IF num>90 then num=180-num: end if


num=((num*31)/90)
dif=num : rem Cast to byte loses decimal
num=num-dif : rem so this is just the decimal bit


est1=PEEK (@sinetable+dif)
dif=PEEK (@sinetable+dif+1)-est1 : REM this is just the difference to the next up number.

num=est1+(num*dif): REM base +interpolate to the next value.

return (num/255)*quad


sinetable:
asm
DEFB 000,013,026,038,051,064,076,088
DEFB 100,112,123,134,145,156,166,175
DEFB 184,193,201,209,216,223,229,234
DEFB 239,243,247,250,252,254,255,255
end asm
END FUNCTION

Print this item

  Sometimes it just goes crazy
Posted by: britlion - 02-20-2010, 03:03 AM - Forum: Bug Reports - Replies (1)

I was having trouble with a variable not being where I expected. In the end, I have this code:

Code:
FUNCTION SINE(num as FIXED) as FIXED

{skipped some code]

PRINT at 0,0;((num*32)/90)
PRINT INT ((num*32)/90)

So num is a fixed, and the skipped code makes it 0<=num<=90

The routine prints out for those two:
0.35554504......
23301

Now, I'm thinking that INT(0.35) is 0. The compiler landed about 23,000 too high on that one.

Any idea why?

Print this item

  Compiler Speed Trials
Posted by: britlion - 02-18-2010, 04:20 PM - Forum: ZX Basic Compiler - Replies (76)

I know that ZX Basic is amazing, but I was wondering how it stood up to other basic compilers that were around for use on the ZX Spectrum. We know that Hisoft basic was pretty fast, for example, and LCD mentioned another compiler the other day that was pretty amazing too.

Let me borrow from an article in Crash Magazine: http://www.crashonline.org.uk/19/compilers.htm

In this article, Simon Goodwin talks about several compilers. Hisoft Basic isn't one of them - it wasn't out yet. He doesn't list the benchmarks, either; but they can be interpolated from this:

Code:
Benchmark BM1 : A null-action FOR, REPEAT or DO loop, executed
                1000 times.

Benchmark BM2 : A  null-action explicitly-coded loop  executed
                1000 times.

Benchmark BM3 : BM2 plus A=K/K*K+K-K in the loop.

Benchmark BM4 : BM2 plus A=K/2*3+4-5 in the loop.

Benchmark BM5 : BM4  plus  a branch to null-action  subroutine
                from inside the loop.

Benchmark BM6 : BM5  plus  an array declaration  M(5),  and  a
                null-action  FOR  loop (of 1-5)  also  in  the
                loop.

Benchmark BM7 : BM6 plus M(L)=A in this 1-5 loop.

Benchmark BM8 : A  square  function,   log  function  and  sin
                function  in  an  explicitly-coded  FOR  loop,
                repeated 100 times.

Benchmark BM9 : Prime  numbers in the range 1-1000 are printed
                to the screen,  calculated in an outer loop of
                1000 and an inner loop of 500,  with no tricks
                at  all.  This  is  a very  bad  prime  number
                routine  indeed,  but a very useful basis  for
                inter-machine,    interpreter   and   compiler
                comparisons.

Simon didn't use Benchmark 9, and I can see why - it's not clearly specified. BM1 to BM8 are pretty clear, however.

My own personal testing with Sinclair Basic gave very slightly differing results. In all cases, my programs were very slightly faster than the timings Goodwin gave in the magazine article. Perhaps he specified things a little differently, perhaps he was using a stopwatch in hand, and human error was the result. Perhaps it was a different version of the ZX Spectrum used. I got the computer to time the programs using the 50 frames per second interrupt timer. For very fast running programs I increased the number of loops by a factor 10 or 100 and estimated back down.

The compilers goodwin tested were:

A Mehmood's "Compiler".
MCODER
Softek's FP and IS
And a little cheekily, Zip 1.5. He wrote that himself, I believe.

The first two rows are for Sinclair Basic. The first being Simon Goodwin's numbers, the second being my own. All times are in seconds, smaller is better.

Code:
BM1      BM2      BM3      BM4      BM5      BM6      BM7      BM8                    BMDRAW
    Sinclair           4.46     8.46     21.56    19.82    25.34    60.82    87.44    23.30                   80.18
    Boriel's ZX BASIC  0.038    0.032     0.30     0.15     0.16     0.328    2.20    24.0

   ZX Basic 1.26-r1603 -O3                                                    0.94    20.78 (17.14 with fSin)
   ZX Basic 1.2.8-s682 -O3                                                    0.88    20.56 (16.94 with fSin)  21.14
   ZX Basic 1.2.8-s758 -O3                                                    0.90    20.76 (17.10 with fSin)  21.32

    HiSoft FP          0.82     1.34      7.26     7.30     7.32    12.52    14.40    21.9
    HS Integer         0.042    0.67      0.08     0.088    0.334    0.50    10.76

    Mehmood            *        0.065     9.0      4.2      4.2     *        *        *
    ZIP 1 .5           0.031    0.064     0.194    0.108    0.115    0.29     0.46    *
    TOBOS              0.58     0.82      2.02     1.76     2.34     6.68     8.72    0.746
    SOFTEK FP          1.75     2.1       8.7      9.4      9.4     19.7     24.0     22.5
    SOFTEK IS          0.058    0.076     0.57     0.98     0.99     1.32    *        *
    MCODER2            0.043    0.097     0.62     0.90     0.92     1.17     1.47    *
The actual code used is listed below. It's possible to Extrapolate what BM1-6 are, because they simply add code to end up with BM7. Bm 8's main loop is listed separately.

Code:
REM BM7
FUNCTION t() as uLong
asm
    LD DE,(23674)
    LD D,0
    LD HL,(23672)
end asm
end function

goto start

subroutine:
return

start:
DIM time,i as uInteger
DIM k,var,j as uByte
let time =t()
LET k=5
LET i=0

label:
LET i=i+1
LET var=k/2*3+4-5
gosub subroutine
DIM M(5) as uInteger
FOR j=0 to 4
LET M(j)=i
NEXT j
IF i<1000 then GOTO label: END IF

print (CAST (FLOAT,t())-time)/50

BM 8 replaces most of the code with:
Code:
REM BM8
DIM i,j as ubyte
j=2
FOR i=1 to 100
result=j^2
result=ln(j)
result=sin(j)
next i
This is changed from using constants to prevent constant folding optimizations.
RESULTS and DISCUSSION

First up, passing all the benchmarks and more, clearly Boriel's work is by far the most flexible and comprehensive compiler available. It blows the spots off everything else in terms of WHAT it can compile, and all credit to him for creating it. It is excellent!

In terms of performance, it's pretty amazing, too. It's the second fastest of all the compilers listed here. Only ZIP goes faster, generally. BM7 is a little disappointing, in that the produced code seems to be slower than both MCODER 2 and Zip by a quite significant margin. Perhaps some examination of array handling code could improve this. With version 1.25 beta, sadly, I couldn't use -O3 as an option - the programs all failed to compiler with this option enabled, so I couldn't see if peephole optimization would make a difference. It's worth noting that most On Spectrum compilers refused to deal with floating point numbers. In this roundup, only Softek FP could do it, and that barely faster than Basic. Boriel's compiler blew me away with the FP result, frankly. I had to check to see if it was doing it correctly, it was so amazing! There might be some sneaky optimization happening, but printing the numbers as it created them did seem to work fine. (Note: It WAS cheating. It was putting in constants at compile time. A clever option, but not what we were aiming to test. This number has been changed)

Fixed Hisoft Basic Numbers. These corrected numbers do in fact show it produces some of the fastest code available, sometimes beaten by ZIP 1.5. It far outmatches what ZIP can do, however, in that it deals with FP as well as integer - and it seems to do both faster than the competition. Of course ZX BASIC basic excels at being FP and Integer aware as well.

Added in Tobos. It's fully FP, so tends to be slow where integer math could improve things. But look at BM8!

ZX BASIC In short: Solid and well optimized. Seems to be slow in BM7 (array handling). Very clever use of constant insertion to produce good BM8 speed value of 0.1 but now times are corrected because that was cheating a little!
[Edit] - Array handling speed has been dramatically increased with later versions. Boriel has stated that he will be looking into further array optimizations similar to Hisoft Basic methods - so we can hope for another doubling of speed, perhaps! Confusedhock:

Print this item