Boriel Basic Forum
Scrolling Tiles - Printable Version

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



Scrolling Tiles - slenkar - 2013-05-21

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 - 2013-05-22

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 - 2013-05-23

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 - 2013-05-23

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 - 2013-05-23

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 - 2013-05-23

yes that seems more sensible


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

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 - 2013-05-23

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 - 2013-05-23

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 - 2013-05-28

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 - 2013-05-28

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: