Welcome, Guest |
You have to register before you can post on our site.
|
Online Users |
There are currently 264 online users. » 0 Member(s) | 260 Guest(s) Applebot, Bing, Google, Yandex
|
Latest Threads |
Strange Happenings
Forum: Bug Reports
Last Post: zedex82
10 hours ago
» Replies: 2
» Views: 123
|
.tap file code not execut...
Forum: Help & Support
Last Post: Zoran
04-28-2025, 10:59 AM
» Replies: 4
» Views: 523
|
Exit from more than one l...
Forum: Wishlist
Last Post: Duefectu
04-23-2025, 10:06 PM
» Replies: 3
» Views: 400
|
put small ASM programs li...
Forum: How-To & Tutorials
Last Post: Zoran
04-18-2025, 02:02 PM
» Replies: 6
» Views: 1,748
|
Creating +3 Menus - Loadi...
Forum: Help & Support
Last Post: merlinkv
04-16-2025, 02:08 PM
» Replies: 6
» Views: 685
|
Randomize not very random...
Forum: Help & Support
Last Post: Zoran
04-08-2025, 10:40 AM
» Replies: 4
» Views: 1,038
|
Scope rules
Forum: Bug Reports
Last Post: Zoran
04-04-2025, 09:46 AM
» Replies: 2
» Views: 424
|
Using constants not allow...
Forum: Bug Reports
Last Post: baltasarq
03-19-2025, 10:00 PM
» Replies: 8
» Views: 1,202
|
404 page not found
Forum: Documentation
Last Post: boriel
03-08-2025, 07:16 PM
» Replies: 5
» Views: 2,971
|
Spectrum keywords codes
Forum: Bug Reports
Last Post: boriel
03-08-2025, 11:00 AM
» Replies: 1
» Views: 483
|
|
|
ZX Assembler Inline Basic |
Posted by: britlion - 03-04-2010, 04: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).
|
|
|
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.
|
|
|
"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 :-)
|
|
|
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'
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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?
|
|
|
|