Forum
Scrolling Tiles - Printable Version

+- Forum (https://www.boriel.com/forum)
+-- Forum: Compilers and Computer Languages (https://www.boriel.com/forum/forumdisplay.php?fid=12)
+--- Forum: ZX Basic Compiler (https://www.boriel.com/forum/forumdisplay.php?fid=11)
+---- Forum: How-To & Tutorials (https://www.boriel.com/forum/forumdisplay.php?fid=13)
+---- Thread: Scrolling Tiles (/showthread.php?tid=547)



Scrolling Tiles - slenkar - 05-21-2013

I managed to get it done using ZXBASIC
Code:
DIM mapdata(31) AS UBYTE => {1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2}
DIM brick(7) AS UBYTE =>{249,249,0,159,159,0,249,249}
DIM crystal(7) AS UBYTE =>{16,40,84,40,84,40,16,0}

Dim CurrentGraphic as ubyte=1
Dim NextGraphic as Ubyte
Dim PreviousGraphic as ubyte

Dim address as UInteger
Dim LineStartAddress as UInteger
addr=16384
LineStartAddress=addr
Dim PreviousAddress as UInteger

Dim LandscapeEightIter as Ubyte
Dim LandscapeIter as ubyte
Dim LineNumber as UByte

for x=0 to 31
Print AT 0,x;"["
next

for iterations=0 to 258 'scroll left 258 times
address=16384

for y=0 to 7
LineStartAddress=address


for x=0 to 31
CurrentGraphic = PEEK(address)
'pause 5


if x>0 'the leftmost byte has no graphics to its left
if CurrentGraphic Band 128=128 'if this graphic has a dot on the left
PreviousGraphic=PreviousGraphic Bor 1 'Add a dot to the right of the previous graphic byte
POKE PreviousAddress,PreviousGraphic
end if
end if

PreviousAddress=address 'save the address of the byte on the left
CurrentGraphic=CurrentGraphic SHL 1 'shift dors to left
PreviousGraphic=CurrentGraphic 'save graphic

if x<31
POKE address,CurrentGraphic
end if

if x=31

if mapdata(LandscapeIter)=1
NextGraphic=brick(LineNumber)
end if

if mapdata(LandscapeIter)=2
NextGraphic=crystal(LineNumber)
end if

if mapdata(LandscapeIter)=0
NextGraphic=0
end if



if LandscapeEightIter=0
if NextGraphic Band 1=1
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=1
if NextGraphic Band 2=2
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=2
if NextGraphic Band 4=4
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=3
if NextGraphic Band 8=8
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=4
if NextGraphic Band 16=16
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=5
if NextGraphic Band 32=32
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=6
if NextGraphic Band 64=64
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

if LandscapeEightIter=7
if NextGraphic Band 128=128
CurrentGraphic=CurrentGraphic BOR 1
end if
end if

POKE address,CurrentGraphic

end if

address=address+1 'move one square right
next
address=LineStartAddress
address=address+256 'move one line down
LineNumber=LineNumber+1

next
LineNumber=0

LandscapeEightIter=LandscapeEightIter+1
if LandscapeEightIter=8
LandscapeEightIter=0
LandscapeIter=LandscapeIter+1
end if

next

Print "done"
Print STR(178 Band 128)
STOP

but I could never get it done using assembly:
Code:
function FASTCALL drawthis() as ubyte
asm


;LD HL,2000
;LD (delayiter),HL
LD HL,16384

;drawvert:
;LD A,31
;LD L,A
;LD (screenwidthiter),A

;drawblokes:

;LD A,(graphic)
;DEC A
;LD (graphic),A
;LD (HL),A






;LD A,(screenwidthiter)
;LD L,A
;DEC A
;LD (screenwidthiter),A
;LD B,A
;INC B
;INC B
;INC B
;djnz drawblokes

;LD A,(screenheightiter)
;LD L,A
;INC H
;DEC A
;LD (screenheightiter),A
;LD B,A

;INC B
;djnz drawvert




LD A,31
LD (screenwidthiter),A
LD A,7
LD (screenheightiter),A

LD HL,60000
LD (delayiter),HL


loop:
LD BC,20000

LD A,7
LD (screenheightiter),A

LD HL,16384
LD (screenaddress),HL

screenvert:
;LD BC,6000
;wait:
;DEC BC
;LD A,B
;Or C
;jp nz,wait

LD (screenaddress),HL

;load landscape number into a
LD HL,data
LD BC,(landscapeiter)
ADD HL,BC
LD A,(HL)
LD (__LABEL__printablevar),A


;load graphic into B
CP 0
jp z,thisisempty
CP 1
jp z,thisisbrick
CP 2
jp z,thisiscrystal

jp thisisbrick

thisisempty:
LD B,0

jp donegraphic
    
thisisbrick:

LD HL,brick

LD DE,(screenheightiter)
ADD HL,DE
LD B,(HL)

jp donegraphic

thisiscrystal:
LD HL,crystal
LD DE,(screenheightiter)
ADD HL,DE
LD B,(HL)
jp donegraphic

donegraphic:

LD A,0
LD (rightbit),A

LD A,(landscapeiter)


CP 0
jp z,bit0
CP 1
jp z,bit1
CP 2
jp z,bit2
CP 3
jp z,bit3
CP 4
jp z,bit4
CP 5
jp z,bit5
CP 6
jp z,bit6
CP 7
jp z,bit7

bit0:
BIT 0,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit1:
BIT 1,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit2:
BIT 2,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit3:
BIT 3,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit4:
BIT 4,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit5:
BIT 5,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit6:
BIT 6,B
jp nz,setbitfirst
jp z,dontsetbitfirst
bit7:
BIT 7,B
jp z,dontsetbitfirst
jp nz,setbitfirst

setbitfirst:
LD A,1
LD (rightbit),A

dontsetbitfirst:


LD HL,(screenaddress)

LD A,31
LD L,A
LD (screenwidthiter),A
LD (screenaddress),HL

LD A,0
LD (leftbit),A
BIT 7,(HL)
jp nz,dontsetbit
LD A,1
LD (leftbit),A
dontsetbit:


LD A,(HL)
SLA A
LD (HL),A


LD A,(rightbit)
CP 0
jp z, dontsetrightbit
LD A,(HL)
OR 1
LD (HL),A
dontsetrightbit:

LD A,(leftbit)
LD (rightbit),A

LD A,30
LD (screenwidthiter),A
LD L,A
LD (screenaddress),HL


screenhori:


LD A,0
LD (leftbit),A
BIT 7,(HL)
jp nz,dontsetbit3
LD A,1
LD (leftbit),A
dontsetbit3:

LD B,(HL)
SLA B
LD A,(rightbit)
CP 0
jp nz,dontsetblockbit2
LD A,B
OR 1
LD B,A
dontsetblockbit2:
LD (HL),B


LD A,(screenwidthiter)
LD L,A
DEC A
LD (screenwidthiter),A

LD A,(leftbit)
LD (rightbit),A

LD A,(screenwidthiter)
CP 0
jp nz,screenhori



LD A,(screenheightiter)

INC H
DEC A



LD (screenaddress),HL
LD (screenheightiter),A
CP 0
jp nz,screenvert


LD A,(__LABEL__landscapeeightiter)
INC A
LD (__LABEL__landscapeeightiter),A


  

LD A,(__LABEL__landscapeeightiter)
CP 8
jp nz,dontresetblock
LD A,0
LD (__LABEL__landscapeeightiter),A
LD A,(landscapeiter)
INC A
LD (landscapeiter),A
dontresetblock:


;jp loop



end asm
end function

Dim x as Integer

for x=0 to 31
Print AT 0,x;STR(x)
next

for x=0 to 30
drawthis()
Print AT x,0;STR(PEEK(@printablevar))+"-"

next

Print "done"

STOP:
printablevar:
ASM
defb 255
END ASM
landscapeeightiter:
ASM
defb 0
blockiter:
defw 8
delayiter:
defw 60000
screenwidthiter:
defb 31
screenheightiter:
defb 8
scrolliter:
defw 10000
graphic:
defb 255
screenaddress:
defw 0
landscapeiter:
defb 0
data:
defb 1,1,0,1,1,1,0,1,1,2,2,2,2,0,1,0,1,0,2,2,2,2
brick:
defb 249,249,0,159,159,0,249,249
crystal:
defb 16,40,84,40,84,40,16,0
rightbit:
defb 0
leftbit:
defb 0

DISPNUM:
     call 11563      ; We'll push the BC register onto the calculator stack
     call 11747      ; and then output that number to the screen by calling this routine
     ret
end asm



Re: Scrolling Tiles - LCD - 05-22-2013

Looks good, and very smooth...
By the way:
Code:
if NextGraphic Band 64=64
can be shortened:
Code:
if NextGraphic & 64



Re: Scrolling Tiles - boriel - 05-22-2013

LCD Wrote:Looks good, and very smooth...
It does indeed! :!:
The idea behind ZX Basic is you don't have to go down to ASM if possible :roll:
Quote:By the way:
Code:
if NextGraphic Band 64=64
can be shortened:
Code:
if NextGraphic & 64
That's right. In fact this rule can be applied to the other lines also
(band 32 = 32, band 16 = 16 etc).
Slenkar, the rule is
Code:
IF x band 2^n = 2^n THEN
can be replaced by
Code:
IF x bAND 2^n THEN
Not sure if the compiler already optimizes that with -O3. Will check it... :|


Re: Scrolling Tiles - slenkar - 05-22-2013

oh ok thanks

I havent checked this out with 03 yet, i wonder if its faster

I put the bricks on the bottom of the screen and colored them,
and put a few GOTO's in there to make it more efficient.
If there are any other optimizations feel free to share
Code:
'#include<mj/fourspriter.bas>
DIM mapdata(63) AS UBYTE => {1,0,1,1,1,1,1,1,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2}
DIM brick(7) AS UBYTE =>{249,249,0,159,159,0,249,249}
DIM crystal(7) AS UBYTE =>{16,40,84,40,84,40,16,0}
INK 2
PAPER 0

'MJfspActivateSprite(1)
'MJfspSetGfxAddress (@brick)
'MJfspSetGfxSprite (1,1,2,3,4)

'MJfspInitSprites ()
'MJfspMoveSprite (1,1,1)
Dim CurrentGraphic as ubyte=1
Dim NextGraphic as Ubyte
Dim PreviousGraphic as ubyte

Dim address as UInteger
Dim LineStartAddress as UInteger
addr=16384
LineStartAddress=addr
Dim PreviousAddress as UInteger

Dim LandscapeEightIter as Ubyte
Dim LandscapeIter as ubyte
Dim LineNumber as UByte

for x=0 to 31
for y=0 to 23
Print AT y,x;" "
next
next


Dim startaddress as UInteger

ASM

LD HL,(__LABEL__adr)

LD A,2
SLA A
SLA A
SLA A

OR H
LD H,A

LD A,7
SLA A
SLA A
SLA A
SLA A
SLA A

OR L
LD L,A

LD (__LABEL__adr),HL
END ASM

Print AT 1,0;STR(PEEK(UINTEGER,@adr))

for iterations=0 to 480 'scroll left 480 times
address=PEEK(UInteger,@adr)


for y=0 to 7
LineStartAddress=address


for x=0 to 31
CurrentGraphic = PEEK(address)
'pause 5


if x>0 'the leftmost byte has no graphics to its left
if CurrentGraphic Band 128=128 'if this graphic has a dot on the left
PreviousGraphic=PreviousGraphic Bor 1 'Add a dot to the right of the previous graphic byte
POKE PreviousAddress,PreviousGraphic
end if
end if

PreviousAddress=address 'save the address of the byte on the left
CurrentGraphic=CurrentGraphic SHL 1 'shift dors to left
PreviousGraphic=CurrentGraphic 'save graphic

POKE address,CurrentGraphic

if x=31

if mapdata(LandscapeIter)=1
NextGraphic=brick(LineNumber)
GOTO 50
end if

if mapdata(LandscapeIter)=2
NextGraphic=crystal(LineNumber)
GOTO 50
end if

if mapdata(LandscapeIter)=0
NextGraphic=0

end if


50

if LandscapeEightIter=0
if NextGraphic Band 1
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=1
if NextGraphic Band 2
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=2
if NextGraphic Band 4
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=3
if NextGraphic Band 8=8
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=4
if NextGraphic Band 16
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=5
if NextGraphic Band 32
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=6
if NextGraphic Band 64
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if

if LandscapeEightIter=7
if NextGraphic Band 128
CurrentGraphic=CurrentGraphic BOR 1
GOTO 100
end if
end if


100 POKE address,CurrentGraphic

end if

address=address+1 'move one square right
next
address=LineStartAddress
address=address+256 'move one line down
LineNumber=LineNumber+1

next
LineNumber=0

LandscapeEightIter=LandscapeEightIter+1
if LandscapeEightIter=8
LandscapeEightIter=0
LandscapeIter=LandscapeIter+1
end if


'MJfspUpdateSprites()
next



'Print "done"
'Print STR(178 Band 128)
STOP
adr:
ASM
defw 16384
END ASM
poker:
ASM
defb 0
END ASM



Re: Scrolling Tiles - britlion - 05-23-2013

I think your gotos could be more descriptive, if you have to use them. Structured programming, of course, never uses them - though I am guilty of doing so Smile How about instead of goto 100 you used goto endofblock ?


What you're trying to do, though is escape multiple if's right?

So you began with

if test=1 then
do thing 1
end if

if test=2 then
do thing 2
end if

if test=3 then
do thing 3
end if

And elected to speed that up with:

if test=1 then
do thing 1
goto endofblock
end if

I believe? So that it doesn't do all the other tests?

In that case, you need to look at If / then / Else concepts, I think:

IF test=1 then
do thing 1
ELSEIF test=2 then
do thing 2
ELSEIF test=3 then
do thing 3
ELSE
do thing "not found"
END IF

It's one if, and it only tests until it finds a match, because ELSE doesn't run if the IF is true. If test=1 then it does thing 1, and gets out right there. if test=2 - it checks to see if test=1, finds it isn't so checks to see if test=2, finds it is, does thing 2 and goes to the END IF.

Does that help?


Re: Scrolling Tiles - slenkar - 05-23-2013

yes that seems more sensible


Re: Scrolling Tiles - slenkar - 05-23-2013

faster version, uses 9 bit rotation
this one takes 7 seconds to scroll 800 pixels
the previous one takes 23 seconds
Code:
DIM mapdata(63) AS UBYTE => {1,0,1,1,1,1,1,1,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2,1,0,2,0,2,1,1,2}
DIM brick(7) AS UBYTE =>{249,249,0,159,159,0,249,249}
DIM crystal(7) AS UBYTE =>{16,40,84,40,84,40,16,0}
Dim LandscapeBANDS(7) AS UBYTE=>{1,2,4,8,16,32,64,128}
INK 2
PAPER 0



Dim LandscapeEightIter as Ubyte
Dim LandscapeIter as ubyte
Dim LineNumber as UByte
DIm NextGraphic as UByte
Dim address as UBYTE
Dim x as UINTEGER

for x=0 to 31
for y=0 to 23
Print AT y,x;"A"
next
next


Dim startaddress as UInteger

Function FASTCALL drawthis() as UBYTE
ASM
;LD A,255
;LD (__LABEL__iters),A
;iterloop:
LD A,7
LD (__LABEL__heightiter),A
LD HL,(__LABEL__startadr)
heightloop:
LD B,L
Ld A,(HL)
RLA
LD (HL),A
widthloop:
DEC L
Ld A,(HL)
RLA
LD (HL),A
djnz widthloop

LD A,31
LD L,A


INC H

LD A,(__LABEL__heightiter)
DEC A
LD (__LABEL__heightiter),A
CP 0
JP NZ,heightloop


;LD A,(__LABEL__iters)
;DEC A
;LD (__LABEL__iters),A
;CP 0
;jp nz,iterloop
END ASM
End Function




for x=0 to 800
drawthis()

'address=PEEK (UINTEGER,@adr)
'address=address+31


'CurrentGraphic=PEEK(UINTEGER,address)
'CurrentGraphic Or 1
'P''OKE UINTEGER address,CurrentGraphic

for LineNumber=0 to 7

CurrentGraphic=PEEK(UINTEGER,address)
if mapdata(LandscapeIter)=1
NextGraphic=brick(LineNumber)
GOTO 50
end if

if mapdata(LandscapeIter)=2
NextGraphic=crystal(LineNumber)
GOTO 50
end if

if mapdata(LandscapeIter)=0
NextGraphic=0
end if


50
'if LineNumber=0
if NextGraphic Band LandscapeBANDS(LandscapeEightIter)
POKE @bit0+LineNumber,1
else
POKE @bit0+LineNumber,0
end if
'GOTO 100
'end if


100
next

LandscapeEightIter=LandscapeEightIter+1
if LandscapeEightIter=8
LandscapeEightIter=0
LandscapeIter=LandscapeIter+1
end if

ASM
LD HL,(__LABEL__startadr)
LD A,31
LD L,A
LD A,(__LABEL__bit0)
CP 0
JP Z,dontset0
LD A,(HL)
OR 1
LD (HL),A
dontset0:

INC H
LD A,(__LABEL__bit1)
CP 0
JP Z,dontset1
LD A,(HL)
OR 1
LD (HL),A
dontset1:

INC H
LD A,(__LABEL__bit2)
CP 0
JP Z,dontset2
LD A,(HL)
OR 1
LD (HL),A
dontset2:

INC H
LD A,(__LABEL__bit3)
CP 0
JP Z,dontset3
LD A,(HL)
OR 1
LD (HL),A
dontset3:

INC H
LD A,(__LABEL__bit4)
CP 0
JP Z,dontset4
LD A,(HL)
OR 1
LD (HL),A
dontset4:
INC H
LD A,(__LABEL__bit5)
CP 0
JP Z,dontset5
LD A,(HL)
OR 1
LD (HL),A
dontset5:

INC H
LD A,(__LABEL__bit6)
CP 0
JP Z,dontset6
LD A,(HL)
OR 1
LD (HL),A
dontset6:

INC H
LD A,(__LABEL__bit7)
CP 0
JP Z,dontset7
LD A,(HL)
OR 1
LD (HL),A
dontset7:

END ASM



next
'Print "done"
'Print STR(178 Band 128)
STOP
adr:
ASM
defw 16384
END ASM

startadr:
ASM
defw 16384
END ASM
currentgraphic:
ASM
defb 0
END ASM
linestartadr:
ASM
defb 0
END ASM
bit0:
ASM
defb 0
END ASM

bit1:
ASM
defb 0
END ASM
bit2:
ASM
defb 0
END ASM
bit3:
ASM
defb 0
END ASM
bit4:
ASM
defb 0
END ASM
bit5:
ASM
defb 0
END ASM
bit6:
ASM
defb 0
END ASM
bit7:
ASM
defb 0
END ASM
heightiter:
ASM
defb 7
END ASM

iters:
ASM
defb 255
END ASM



Re: Scrolling Tiles - boriel - 05-23-2013

slenkar Wrote:faster version, uses 9 bit rotation
this one takes 7 seconds to scroll 800 pixels
the previous one takes 23 seconds
awesome! Confusedhock:
But you finally used ASM Cry
(just kidding) :mrgreen: .
I see how you overcome the limitation using external labels from within ASM.
I'm working on a system to access BASIC variables from ASM in an easy way. The compiler is currently *broken*. I finally started the hard refactoring (almost from scratch) I mentioned some month ago. Please, be patient.


Re: Scrolling Tiles - slenkar - 05-23-2013

There is still quite a bit of basic in there, Big Grin

Im gonna combine it with a sprite library and see if it looks ok


Re: Scrolling Tiles - britlion - 05-28-2013

Some ideas Smile

Is
Code:
RL (HL)

At 15 T states

A better choice than:

Code:
LD A,(HL)
RLA
LD (HL),A

At 7+4+7=18 T states perhaps? (Also 1 byte shorter, and doesn't mess up your A register, for what it's worth - which is usually the biggest reason it's useful).


Similarly:
Code:
LD A,31
LD L,A
LD A,(__LABEL__bit0)

You are faffing with A, where:

Code:
LD L,31
LD A,(__LABEL__bit0)

is more direct, surely?


I suspect that:
Code:
LD A,(HL)
OR 1
LD (HL),A

Again faffs with the A register (for 7+4+7 T states), where

Code:
SET 0, (HL)
Is 15 T states and doesn't faff with the A register.


Code:
LD A,(__LABEL__bit0)
CP 0
JP Z,dontset0

CP 0 works, but is two bytes. You can trigger the flags with something like AND A - which is a single byte opcode, and faster.

Code:
LD A,(__LABEL__bit0)
AND A
JP Z,dontset0

Finally, I get the feeling that using a bunch of bytes in memory for the bit counter could be optimized by using bits of a byte for the bit information. But I'm way too distracted to try to refactor that right now Wink So I'll stick to simple tweak suggestions.


Al that said. Wow. You're using assembler, and it's working like a charm! Well done! This isn't easy stuff Wink


Re: Scrolling Tiles - slenkar - 05-28-2013

thanks I put all those suggestions in the code and they worked
testing bits of a byte in a register would be faster than loading 8 bytes and testing each :oops: