Problem with rotating procedure

xorxor
edited July 2006 in Development
Hi folks!

I wrote a proc in z80 asm, based on:

x' = x * cos (z) - y * sin (z)
y' = x * sin (z) + y * cos (z)

I used a precalculated table of sin and cos data. I suspect a circle on a screen, but it is not exactly an oval. Anyone can help me? It's not easy to write any of mult and div procs on 8 bit processors :) I'm newbie in z80 programming, and cannot find an error in proc.

;============================================================================
;x.asm 2006 (c) by Xor
;============================================================================
org $8000
;============================================================================
start: call rotator
jp start
;============================================================================
;mult21 - mnozenie liczby 16- przez 8-bitowa
;mnozna: de, mnoznik: a
;wynik: a i hl
;============================================================================
mult21: ld hl,0
ld b,8
nxbit: add hl,hl
rla
jr nc,fin21
add hl,de
adc a,0
fin21: djnz nxbit
ret
;============================================================================
;mult22 - mnozenie liczb dwubajtowych
;argumenty: hl i de
;wynik: hl i de
;============================================================================
mult22: ld c,h
ld a,l
call mult21
push hl
ld h,a
ld a,c
ld c,h
call mult21
pop de
ld b,c
ld c,d
add hl,bc
adc a,0
ld d,l
ld l,h
ld h,a
ret
;============================================================================
;rotatez
;============================================================================
rotatez: ld bc,(anglez) ;xc=cos(anglez)*x
ld hl,cosx
add hl,bc
ld e,(hl)
inc hl
ld d,(hl)
ld hl,(x)
call mult22
ld (xc),de

ld bc,(anglez) ;ys=sin(anglez)*y
ld hl,sinx
add hl,bc
ld e,(hl)
inc hl
ld d,(hl)
ld hl,(y)
call mult22
ld (ys),de

ld bc,(anglez) ;xs=sin(anglez)*x
ld hl,sinx
add hl,bc
ld e,(hl)
inc hl
ld d,(hl)
ld hl,(x)
call mult22
ld (xs),de

ld bc,(anglez) ;yc=cos(anglez)*y
ld hl,cosx
add hl,bc
ld e,(hl)
inc hl
ld d,(hl)
ld hl,(y)
call mult22
ld (yc),de
;
ld de,(xc) ;xr=xc-ys
ld bc,(ys)

ld a,e
sub c
ld e,a
ld a,d
sbc a,b
ld d,a

ld (xr),de ;xr=x*cos(z)-y*sin(z)
;
ld de,(xs) ;yr=xs+yc
ld bc,(yc)

ld a,e
add a,c
ld e,a
ld a,d
adc a,b
ld d,a

ld (yr),de ;yr=y*sin(z)+y*cos(z)
;
ld a,128
ld hl,(xr)
add a,h
ld e,a

ld a,96
ld hl,(yr)
add a,h
ld l,a

call putpx1
;
ret
;============================================================================
rotuj: call rotatez
ret
;============================================================================
rotator: ld hl,(anglez) ;zwieksz anglez o 1 i sprawdz
inc hl ;czy jest wieksze o 359
inc hl
ld a,h ;jesli tak - wyzeruj
cp 3
jr nz,r1
ld a,l
cp $d0
jr nz,r1
ld hl,0
r1: ld (anglez),hl
call rotuj
ret
;============================================================================
;dane
;============================================================================
anglez dw 0
x dw 20
y dw 20
xc dw 0
ys dw 0
xs dw 0
yc dw 0
xr dw 0
yr dw 0
xr2 dw 0
yr2 dw 0
;============================================================================

;============================================================================
; putpx - rysowanie punktu na ekranie
; in: e - x, l - y
;============================================================================
putpx1: ld h,taby1x ;7
ld b,(hl) ;7
inc h ;4
ld c,(hl) ;7

ld d,tabx1x ;7
ld a,(de) ;7
add a,c ;4
ld c,a ;4

inc d ;3
ld a,(de) ;7

ld h,b ;4
ld l,c ;4

or (hl) ;7
ld (hl),a ;7
;total = 80
ret
;
putpx0: ld h,taby1x ;7
ld b,(hl) ;7
inc h ;4
ld c,(hl) ;7
ld d,tabx1x ;7
ld a,(de) ;7
add a,c ;4
ld c,a ;4
inc d
inc d ;3
ld a,(de) ;7
ld h,b ;4
ld l,c ;4
and (hl) ;7
ld (hl),a ;7
;total = 80
ret
;============================================================================
include data.inc
;============================================================================
end start


;============================================================================
;data.inc 2006 (c) by Xor
;============================================================================
org $b000
taby1x equ $b0
taby1 db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47
db $40,$41,$42,$43,$44,$45,$46,$47

db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f
db $48,$49,$4a,$4b,$4c,$4d,$4e,$4f

db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57
db $50,$51,$52,$53,$54,$55,$56,$57

org $b100
taby2x equ $b1
taby2 db $00,$00,$00,$00,$00,$00,$00,$00
db $20,$20,$20,$20,$20,$20,$20,$20
db $40,$40,$40,$40,$40,$40,$40,$40
db $60,$60,$60,$60,$60,$60,$60,$60
db $80,$80,$80,$80,$80,$80,$80,$80
db $a0,$a0,$a0,$a0,$a0,$a0,$a0,$a0
db $c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0
db $e0,$e0,$e0,$e0,$e0,$e0,$e0,$e0

db $00,$00,$00,$00,$00,$00,$00,$00
db $20,$20,$20,$20,$20,$20,$20,$20
db $40,$40,$40,$40,$40,$40,$40,$40
db $60,$60,$60,$60,$60,$60,$60,$60
db $80,$80,$80,$80,$80,$80,$80,$80
db $a0,$a0,$a0,$a0,$a0,$a0,$a0,$a0
db $c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0
db $e0,$e0,$e0,$e0,$e0,$e0,$e0,$e0

db $00,$00,$00,$00,$00,$00,$00,$00
db $20,$20,$20,$20,$20,$20,$20,$20
db $40,$40,$40,$40,$40,$40,$40,$40
db $60,$60,$60,$60,$60,$60,$60,$60
db $80,$80,$80,$80,$80,$80,$80,$80
db $a0,$a0,$a0,$a0,$a0,$a0,$a0,$a0
db $c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0
db $e0,$e0,$e0,$e0,$e0,$e0,$e0,$e0

org $b200
tabx1x equ $b2
tabx1 db 0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1
db 2,2,2,2,2,2,2,2
db 3,3,3,3,3,3,3,3
db 4,4,4,4,4,4,4,4
db 5,5,5,5,5,5,5,5
db 6,6,6,6,6,6,6,6
db 7,7,7,7,7,7,7,7
db 8,8,8,8,8,8,8,8
db 9,9,9,9,9,9,9,9
db 10,10,10,10,10,10,10,10
db 11,11,11,11,11,11,11,11
db 12,12,12,12,12,12,12,12
db 13,13,13,13,13,13,13,13
db 14,14,14,14,14,14,14,14
db 15,15,15,15,15,15,15,15
db 16,16,16,16,16,16,16,16
db 17,17,17,17,17,17,17,17
db 18,18,18,18,18,18,18,18
db 19,19,19,19,19,19,19,19
db 20,20,20,20,20,20,20,20
db 21,21,21,21,21,21,21,21
db 22,22,22,22,22,22,22,22
db 23,23,23,23,23,23,23,23
db 24,24,24,24,24,24,24,24
db 25,25,25,25,25,25,25,25
db 26,26,26,26,26,26,26,26
db 27,27,27,27,27,27,27,27
db 28,28,28,28,28,28,28,28
db 29,29,29,29,29,29,29,29
db 30,30,30,30,30,30,30,30
db 31,31,31,31,31,31,31,31
db 32,32,32,32,32,32,32,32

org $b300
tabx2x equ $b3
tabx2 db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1
db 128,64,32,16,8,4,2,1

org $b400
tabx3x equ $b4
tabx3 db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254
db 127,191,223,293,247,251,253,254

org $b500
sinx: dw 0, 4, 8, 13, 17, 22, 26, 31, 35, 40
dw 44, 48, 53, 57, 61, 66, 70, 74, 79, 83
dw 87, 91, 95,100,104,108,112,116,120,124
dw 127,131,135,139,143,146,150,154,157,161
dw 164,167,171,174,177,181,184,187,190,193
dw 196,198,201,204,207,209,212,214,217,219
dw 221,223,226,228,230,232,233,235,237,238
dw 240,242,243,244,246,247,248,249,250,251
dw 252,252,253,254,254,255,255,255,255,255
dw 256,255,255,255,255,255,254,254,253,252
dw 252,251,250,249,248,247,246,244,243,242
dw 240,238,237,235,233,232,230,228,226,223
dw 221,219,217,214,212,209,207,204,201,198
dw 196,193,190,187,184,181,177,174,171,167
dw 164,161,157,154,150,146,143,139,135,131
dw 127,124,120,116,112,108,104,100,95,91
dw 87,83,79,74,70,66,61,57,53,48
dw 44,40,35,31,26,22,17,13,8,4
dw 0,-4,-8,-13,-17,-22,-26,-31,-35,-40
dw -44,-48,-53,-57,-61,-66,-70,-74,-79,-83
dw -87,-91,-95,-100,-104,-108,-112,-116,-120,-124
dw -127,-131,-135,-139,-143,-146,-150,-154,-157,-161
dw -164,-167,-171,-174,-177,-181,-184,-187,-190,-193
dw -196,-198,-201,-204,-207,-209,-212,-214,-217,-219
dw -221,-223,-226,-228,-230,-232,-233,-235,-237,-238
dw -240,-242,-243,-244,-246,-247,-248,-249,-250,-251
dw -252,-252,-253,-254,-254,-255,-255,-255,-255,-255
dw -256,-255,-255,-255,-255,-255,-254,-254,-253,-252
dw -252,-251,-250,-249,-248,-247,-246,-244,-243,-242
dw -240,-238,-237,-235,-233,-232,-230,-228,-226,-223
dw -221,-219,-217,-214,-212,-209,-207,-204,-201,-198
dw -196,-193,-190,-187,-184,-181,-177,-174,-171,-167
dw -164,-161,-157,-154,-150,-146,-143,-139,-135,-131
dw -128,-124,-120,-116,-112,-108,-104,-100,-95,-91
dw -87,-83,-79,-74,-70,-66,-61,-57,-53,-48
dw -44,-40,-35,-31,-26,-22,-17,-13,-8,-4

org $b600
cosx: dw 256,255,255,255,255,255,254,254,253,252
dw 252,251,250,249,248,247,246,244,243,242
dw 240,238,237,235,233,232,230,228,226,223
dw 221,219,217,214,212,209,207,204,201,198
dw 196,193,190,187,184,181,177,174,171,167
dw 164,161,157,154,150,146,143,139,135,131
dw 128,124,120,116,112,108,104,100,95,91
dw 87,83,79,74,70,66,61,57,53,48
dw 44,40,35,31,26,22,17,13,8,4
dw 0,-4,-8,-13,-17,-22,-26,-31,-35,-40
dw -44,-48,-53,-57,-61,-66,-70,-74,-79,-83
dw -87,-91,-95,-100,-104,-108,-112,-116,-120,-124
dw -127,-131,-135,-139,-143,-146,-150,-154,-157,-161
dw -164,-167,-171,-174,-177,-181,-184,-187,-190,-193
dw -196,-198,-201,-204,-207,-209,-212,-214,-217,-219
dw -221,-223,-226,-228,-230,-232,-233,-235,-237,-238
dw -240,-242,-243,-244,-246,-247,-248,-249,-250,-251
dw -252,-252,-253,-254,-254,-255,-255,-255,-255,-255
dw -256,-255,-255,-255,-255,-255,-254,-254,-253,-252
dw -252,-251,-250,-249,-248,-247,-246,-244,-243,-242
dw -240,-238,-237,-235,-233,-232,-230,-228,-226,-223
dw -221,-219,-217,-214,-212,-209,-207,-204,-201,-198
dw -196,-193,-190,-187,-184,-181,-177,-174,-171,-167
dw -164,-161,-157,-154,-150,-146,-143,-139,-135,-131
dw -128,-124,-120,-116,-112,-108,-104,-100,-95,-91
dw -87,-83,-79,-74,-70,-66,-61,-57,-53,-48
dw -44,-40,-35,-31,-26,-22,-17,-13,-8,-4
dw 0,4,8,13,17,22,26,31,35,40
dw 44,48,53,57,61,66,70,74,79,83
dw 87,91,95,100,104,108,112,116,120,124
dw 128,131,135,139,143,146,150,154,157,161
dw 164,167,171,174,177,181,184,187,190,193
dw 196,198,201,204,207,209,212,214,217,219
dw 221,223,226,228,230,232,233,235,237,238
dw 240,242,243,244,246,247,248,249,250,251
dw 252,252,253,254,254,255,255,255,255,255
;============================================================================

I'm using PASMO cross-assembler and TAP with basic loader output.
Post edited by xor on

Comments

  • edited July 2006
    xor wrote:
    I used a precalculated table of sin and cos data. I suspect a circle on a screen, but it is not exactly an oval. Anyone can help me?

    It looks ok to me. A circle won't look like a circle because the aspect ratio of the screen is not 1:1; ie pixels in the Y direction are bigger than pixels in the X direction. If you want a circle to look like a circle you'll have to scale one of the coordinates before plotting (and before the translation). You could try multiplying the y coordinate by 3/4 before adding 96 to it, for example.

    Or is it the other way around?? Well you get the gist..
  • xorxor
    edited July 2006
    yes, you're right. but just look at this:

    http://www.k-ow.net/~xor/zx/x_tap.png

    or

    http://www.k-ow.net/~xor/zx/x.tap

    it seems to be a broken oval :( i think that problem is with adds and subs. somewhere there is SIGN missing. (maybe?)

    PS. another error is:

    ld a,h ;jesli tak - wyzeruj
    cp 3

    should be:

    cp 2

    becouse 360*2 in hex is 2d0h, not 3d0h :)
  • JmkJmk
    edited July 2006
    Move the cosx table to $B800 to leave enough room for the 720-byte sinx table and suddenly a circle appears...
  • xorxor
    edited July 2006
    Yes! Of course! THANKS!

    Now another question - any sugestions to make this procedure faster? Maybe more precalculations?
  • edited July 2006
    If you want to draw a circle quickly, you're generally going to be much better off using a difference-of-squares method rather than sin and cos tables.

    There are some listings in C on this page, that should give you a few hints:

    http://www.ddj.com/184409279
  • edited July 2006
    The ultimate in efficient circle drawing routines is probably item 149 of HAKMEM...
    x = x - y>>n /* for some constant n */
    y = y + x>>n
    
    Only really useful in special cases (you don't get a lot of choice about the radius), but too good to go without a mention :)
  • xorxor
    edited July 2006
    THANKS!!! But i'm planning to write more 3d graphics, so i need a rotating by an axis. I draw a circle only for test purposes :)

    Thanks for links, very usefull!
  • edited July 2006
    Here are some notes (based on on heavy googling and some diassembling of zx vector games):
    -probably best internet resource about math on Z80: baze.au.com/misc/z80bits.html
    -some useful thing can be found on www.ticalc.org (it is archive of software for TEXAS INSTRUMENT calculators, early models have Z80 and many software/games are provided with source). especcially this http://www.ticalc.org/archives/files/fileinfo/346/34642.html and this http://www.ticalc.org/archives/files/fileinfo/234/23429.html are worth to see
    -basically, speccy is a bit slow to draw a complex 3D scene so it is ever about making compromises. some things which comes to my mind.
    *nice example of fast game is Dark star. Math is done in signed 16bit, game writes pixel directly to screen (avoiding copy routine which takes ages), uses very simple models, see way how planets are drawed
    *as multiplication is pretty slow it is worth to do it with less bits than your 24bit
    (imagine you have huge world so you need 16 or 24 bits to work with objects. you can define a reference point for every object in a game and solve visibility with 24bit math. once you decide that object is visible you have to pick up points of objects, do rotation of all points around object centre in 8bit signed numbers and add result to refence point. in this way you avoid many 16bit ADD and RL instructions. for example BUSY, author of some nice demos uses signed 9bit and logarithm based multiplication in his demo BLAVA (some sources of his demos can be seen at sysel.webz.cz. (the page is in czech and comments in slowak.)
    *BUSY again: "How many T takes the fastest plot routine ? 100T ? 90T? 85T ? Nope! 7T: ld (hl), a ..." Of course, this is an exaggeration but BUSY want to say that something like this
    ;draw routine
    compute next line point
    call put_pixel
    do again until end of line reached
    
    is stupid.
    fastest, table driven, put pixel routine takes about 90T. but when you are drawing a line, you already have an adress. the next point will be just one pixel left or one pixel below. such computations can be done much faster than
    computing adress and bit from scratch (see the link at ticalc, the fastest rutine described there takes 61T per pixel in the worst case. i estimate that games like ELITE or STARSTRIKE draws 1500-3000 pixel per frame so this routine can save about one frame).

    ...hope this helps. F.
  • edited July 2006
    As fikee mentions, using logarithms to perform multiplications is a great idea.

    Another thing that probably won't contribute a lot of speedup is that rather than using a 360 degree circle, it is common to divide the circle into units of 256. Table lookup will be a little bit quicker as will cosine and sine COMPUTATIONS (you do table lookup for that so this doesn't matter).

    Your multiplies can be done faster, again using table lookup, but I'd also investigate the logarithm idea.

    And, as fikee says, only compute the positions of endpoints and use smart line drawing that modify screen addresses directly for each pixel plotted.

    Fikee's post is probably a lot more helpful :-) I just wanted to mention the 256-degree thing really.
  • edited July 2006
    Xor:
    As for fast math... there are many ways of doing multiplication and each has its pros and cons. The general rule is - the more speed, the more memory requirements. For example, you could use several Speccy 128k pages to store the result of X * sin(Y) where Y (0..255) maps to higher byte and X maps to lower byte of the lookup address. Ideally, the X * Y should be performed as:

    ld h,X
    ld l,Y
    ld a,(hl) ; of course, it's not that easy in reality ;)

    Anyway, the logarithmic/exponential multiplication is indeed a good idea based on equation: a * b = exp(log(a) + log(b)). We need a lookup table holding logarithms and another one holding e^x. But in the case of rotations we can actually use two different logarithm tables: a * sin(b) = exp(log(a) + log(sin(b))).

    Based on experience, you need at least 10 bits of precision to store logarithms to be able to perform reasonably accurate 8.8 fixed point calculation. Anyway, the signed X * sin(Y) takes just 77 cycles (if I remember correctly), which is really fast (I don't recall any faster method except the one mentioned earlier). X * cos(Y) is the same as X * sin(Y + 64). Ages ago, I wrote an article for the Russian scene magazine Scenergy (forgive the horrible English): http://baze.au.com/misc/expmul.txt

    Hope that helps. Btw... vector calculations themselves take significantly less time than drawing points / lines / solid triangles. So, while it's interesting to optimise math, I'd first take a look at the "fillrate-intensive" tasks.
Sign In or Register to comment.