I am currently programming my demo and I have a 32 line software scroll working.
However it's still quite flickery despite waiting for the frame flyback and optimizing the scroll and sprite code a reasonable amount (i.e. I haven't done anything mad yet like use loads of LDIs or self modifying sprite code etc).
So, do I just suck or, should I use double buffering to solve this flicker? And how do I double buffer - I can't find example code anywhere...?
Also, I'm assuming you can't double buffer with overscan 32kb screens, so did coders using overscan just make their code really tight?
Généraly , demomakers avoid software scroller , they try to use hardware to do scroll with shifted buffers.
Idem for overscan , the screen space is generaly build using one or more split.
What is the position of your scroller , width of your screen (in bytes) ?
Quote from: redbox on 22:47, 14 January 10So, do I just suck or, should I use double buffering to solve this flicker? And how do I double buffer - I can't find example code anywhere...?
that depends how is written your code, you can post it here.
Double buffer is done using reg 12 & 13 of the CRTC ( http://www.grimware.org/doku.php/documentations/devices/crtc (http://www.grimware.org/doku.php/documentations/devices/crtc)) , bits 4 & 5 of reg 12 are used to define screen page : %00 = 0 = , %01 = 16 = #4000 , %10 = 32 = #8000 & %11 = 48 = #C000.
After, i am not demomaker but yes the code must be fast.Don't forget you have only around 20000µs per frame and if you make a soft scroll using ldir for a 48 chars screen on 32 lines you will use : 6(µs for ldir)*96(width in bytes)*32(lines)=18432 !
With a 96x32 bytes scroll (3072 bytes), the best you can do to move all the bytes is 4.08 nop per byte. (12544 nop) (but you need to have to make some little mad things.. ;D).
12544 nop are 196 scan line of the 312 lines (total number of lines managed by crtc on a standard 50 Hz screen)
If the scroll is at the bottom of the screen and your code runs at beginning of the frame (the crtc is not displaying the data you're modifying), you do not need to have a double buffered screen.
If you need that your soft scroll runs at 50Hz and you have a main code less critical (all the other things you need to do in your program), you have to 'install' the code of your scroll in the first interruption of the frame. By the way, the main code will use time available when interruption is not busy with the scroll.
If the main code is also displaying such things like sprites, but less critical than scroll, you can use double buffering only with your sprites, but you need to use crtc splitting technics to have your software scroll at the bottom on one buffer and just manage a double buffer on the "sprites" screen.
But if can do a crtc split screen, the road is not so long to create an "hardware scroll", really better for the z80a health.... ;D
Quote from: redbox on 22:47, 14 January 10
I am currently programming my demo and I have a 32 line software scroll working.
However it's still quite flickery despite waiting for the frame flyback and optimizing the scroll and sprite code a reasonable amount (i.e. I haven't done anything mad yet like use loads of LDIs or self modifying sprite code etc).
So, do I just suck or, should I use double buffering to solve this flicker? And how do I double buffer - I can't find example code anywhere...?
Also, I'm assuming you can't double buffer with overscan 32kb screens, so did coders using overscan just make their code really tight?
org &4000
swap equ (&30 xor &10)
di
ld hl,&c9fb
ld (&0038),hl
ei
;; set initial scr base
ld bc,&bc0c
out (c),c
inc b
ld a,&30
out (c),a
dec b
inc c
out (c),c
inc b
ld a,0
out (c),a
;; wait for vsync loop
l1: ld b,&f5
l2: in a,(c)
rra
jr nc,l2
;; write scr base to reg 12
ld bc,&bc0c
out (c),c
inc b
ld a,(scr_base)
out (c),c
halt
halt
halt
ld a,(scr_base)
xor swap
ld (scr_base),a
jp l1
scr_base:
defb &30
The code does this:
1. it sends the "screen base" which is just the 8-bits you normally send to reg 12, and sends it to reg 12. This is ok, because I am not scrolling and reg 13 will stay at 0.
2. then it delays with halts (just to show the effect.. without these the code will execute quickly within the vsync and loop back and vsync will still be active put your code here). then it updates the scr_base and swaps it.
3. one screen at &c000-&ffff, other at &4000-&7fff
To get your draw screen you can do something like this:
ld a,(scr_base)
xor swap
add a,a
add a,a
;; A = &c0 or &40
This all works assuming you don't scroll. If you're scrolling it is a little bit more.. but really not a lot.
Using software or hardware scrolling is normally a choice between amount of ram available and amount of time you want to use.
hardware scrolling is quick.. but if your doing splitting the screen then it can start to "eat" up memory, especially with double buffering.
Some code examples are here:
http://www.kjthacker.f2s.com/source.html
Quote from: fano on 07:21, 15 January 10
Don't forget you have only around 20000µs per frame and if you make a soft scroll using ldir for a 48 chars screen on 32 lines you will use : 6(µs for ldir)*96(width in bytes)*32(lines)=18432 !
I thought the timing was tight, but I didn't realise it was that tight. I really must get my head around calculating how long routines take like you have...!
Quote from: Longshot on 10:44, 15 January 10
But if can do a crtc split screen, the road is not so long to create an "hardware scroll", really better for the z80a health.... ;D
Thanks for your explanations. I'm starting to think I really should look at using a hardware scroll if I want anything else to happen in the demo as I still have the music, rasters, maybe a small animation etc to go. I was looking at using a split screen (MODE 0 for the picture and MODE 1 for the scroll) anyway so maybe this will help me choose the hardware route anyway...?!
Quote from: arnoldemu on 10:52, 15 January 10
The code does this:
1. it sends the "screen base" which is just the 8-bits you normally send to reg 12, and sends it to reg 12. This is ok, because I am not scrolling and reg 13 will stay at 0.
2. then it delays with halts (just to show the effect.. without these the code will execute quickly within the vsync and loop back and vsync will still be active put your code here). then it updates the scr_base and swaps it.
3. one screen at &c000-&ffff, other at &4000-&7fff
Thanks for the example (I've been reading a lot of code from your website!) and I'd like to give it a try. Please could explain the
swap equ (&30 xor &10) line as I don't understand it and it doesn't assemble in WinAPE?
Here is the code I have written so far for the scroll (I have also attached a DSK image so you can see it in action with the sprite font I've generated). I used a general scroll routine and a general sprite routine so they can be made faster because the scroll does 8 lines called 4 times at the moment (instead of 32 at once) and the sprite routine (which I have started to modify because the sprite is always 1 byte wide and 32 pixels high) always prints the sprite in the same place on the screen:
org &8000
loop: ld b,&F5 ;wait for frame flyback
ml1: in a,(c) ;same as &BD19 without using firmware
rra
jr nc,ml1
ld de,&C690 ;first line to scroll
call scroll ;scroll it
ld de,&C6E0 ;second line
call scroll
ld de,&C730 ;third line
call scroll
ld de,&C780 ;and finally the fourth line
call scroll
ld hl,(sprloc) ;address of sprite data to print
ld de,&C6DF ;screen address to print it at
call sprite ;print the sprite on screen
ld a,(sprcnt) ;load a with sprite part counter
dec a ;decrease it by 1
jr nz,nxtaddr ;jump if not zero to get next address for sprite
ld a,8 ;else, reset the
ld (sprcnt),a ;sprite part counter
ld hl,(pointer) ;get current text pointer
ld a,(hl) ;get character from pointer
or a ;is it 0?
jr nz,notend ;jump if not 0 to notend
ld hl,message ;else reset message
ld a,(hl) ;and get character
notend: push hl ;preserve text pointer
sub 31 ;subtract 31 to convert ascii to sprite number
ld b,a ;load b with sprite number for findloc counter
ld ix,sprtab ;load ix with sprite address table
findloc:ld l,(ix) ;load hl with address from sprite address table
inc ix
ld h,(ix)
inc ix
djnz findloc ;decrease b and go to next in table if not 0
ld (sprloc),hl ;put new sprite location into sprloc
pop hl ;restore text pointer
inc hl ;increase text pointer to the next position,
ld (pointer),hl ;and store it as the pointer
jr loop ;loop back
nxtaddr:ld (sprcnt),a ;store decreased sprite part counter
ld hl,(sprloc) ;get current sprite part location
ld de,&20 ;&20 for next sprite part
add hl,de ;add it to sprite part location
ld (sprloc),hl ;store new sprite part location
jr loop ;loop back
scroll: ld a,8 ;8 pixel lines to scroll
scrloop:ld h,d ;set HL to be the
ld l,e ;same as DE
inc hl ;and increase DE by 1
ld bc,79 ;scroll 79 bytes
ldir ;move the memory
ld bc,&7B1 ;add &800 to get to next line
ex de,hl ;minus the 79
add hl,bc ;added by LDIR
ex de,hl
dec a ;decrease the pixel line counter
jr nz,scrloop ;if not zero, do the next line
ret ;else exit
sprite: ld b,32 ;height in pixels
ld c,1 ;width in bytes
sprloop:push bc ;preserve the dimensions
ld b,0 ;BC now equals the width in bytes
ldi ;copy the byte
dec e ;ldi increases de, so decrease it again
pop bc ;restore the dimensions
ex de,hl ;to move DE onto the next screen line,
call nxtline ;we use the routine to move HL onto the
ex de,hl ;the next line, but swap DE and HL either side
djnz sprloop ;if not all lines done, loop back
ret
nxtline:ld a,8
add a,h ;add 8 to H (the high byte)
ld h,a
ret nc ;return if no overflow
push de ;otherwise preserve DE
ld de,&C050
add hl,de ;add &C000+&50 to HL
pop de ;and get DE back again
ret
pointer:defw message ;current text pointer value
message:defm "HELLO CPCWIKI PROGRAMMERS! + ",0
.sprcnt db 8 ;sprite part counter (each sprite 8 bytes wide)
.sprloc dw &4000 ;sprite part location
.sprtab dw &4000 ;sprite address table
dw &4100
dw &4200
dw &4300
dw &4400
dw &4500
dw &4600
dw &4700
dw &4800
dw &4900
dw &4A00
dw &4B00
dw &4C00
dw &4D00
dw &4E00
dw &4F00
dw &5000
dw &5100
dw &5200
dw &5300
dw &5400
dw &5500
dw &5600
dw &5700
dw &5800
dw &5900
dw &5A00
dw &5B00
dw &5C00
dw &5D00
dw &5E00
dw &5F00
dw &6000
dw &6100
dw &6200
dw &6300
dw &6400
dw &6500
dw &6600
dw &6700
dw &6800
dw &6900
dw &6A00
dw &6B00
dw &6C00
dw &6D00
dw &6E00
dw &6F00
dw &7000
dw &7100
dw &7200
dw &7300
dw &7400
dw &7500
dw &7600
dw &7700
dw &7800
dw &7900
dw &7A00
The swap equ (&30 xor &10) should be changed to swap equ &20 if it isnt compiling. You want to swap between &30 and &10 each frame, which you get by xoring the current value with &20. This is the number you get by xoring the two values you want to alternate between.
Something I do to work out what my code is doing is change the colour of the border while it runs. I've added a couple of colour changes to the border in your code so that for the last quarter of your scroll code, you can see how long it takes, and when it is occurring. It's the blue part of the screen. If you modify somewhere on the screen without double buffering, you need to make sure you dont do it at the same point in the frame as that part of the screen is updated, if you want to avoid it being visible as flicker or tearing.
org &8000
loop: ld b,&F5 ;wait for frame flyback
ml1: in a,(c) ;same as &BD19 without using firmware
rra
jr nc,ml1
ld de,&C690 ;first line to scroll
call scroll ;scroll it
ld de,&C6E0 ;second line
call scroll
ld de,&C730 ;third line
call scroll
; change border colour to blue
ld bc,&7F10
out (c),c
ld c,&55
out (c),c
ld de,&C780 ;and finally the fourth line
call scroll
; change border colour to black
ld bc,&7F10
out (c),c
ld c,&54
out (c),c
ld hl,(sprloc) ;address of sprite data to print
ld de,&C6DF ;screen address to print it at
call sprite ;print the sprite on screen
ld a,(sprcnt) ;load a with sprite part counter
dec a ;decrease it by 1
jr nz,nxtaddr ;jump if not zero to get next address for sprite
ld a,8 ;else, reset the
ld (sprcnt),a ;sprite part counter
ld hl,(pointer) ;get current text pointer
ld a,(hl) ;get character from pointer
or a ;is it 0?
jr nz,notend ;jump if not 0 to notend
ld hl,message ;else reset message
ld a,(hl) ;and get character
notend: push hl ;preserve text pointer
sub 31 ;subtract 31 to convert ascii to sprite number
ld b,a ;load b with sprite number for findloc counter
ld ix,sprtab ;load ix with sprite address table
findloc:ld l,(ix) ;load hl with address from sprite address table
inc ix
ld h,(ix)
inc ix
djnz findloc ;decrease b and go to next in table if not 0
ld (sprloc),hl ;put new sprite location into sprloc
pop hl ;restore text pointer
inc hl ;increase text pointer to the next position,
ld (pointer),hl ;and store it as the pointer
jr loop ;loop back
nxtaddr:ld (sprcnt),a ;store decreased sprite part counter
ld hl,(sprloc) ;get current sprite part location
ld de,&20 ;&20 for next sprite part
add hl,de ;add it to sprite part location
ld (sprloc),hl ;store new sprite part location
jr loop ;loop back
scroll: ld a,8 ;8 pixel lines to scroll
scrloop:ld h,d ;set HL to be the
ld l,e ;same as DE
inc hl ;and increase DE by 1
ld bc,79 ;scroll 79 bytes
ldir ;move the memory
ld bc,&7B1 ;add &800 to get to next line
ex de,hl ;minus the 79
add hl,bc ;added by LDIR
ex de,hl
dec a ;decrease the pixel line counter
jr nz,scrloop ;if not zero, do the next line
ret ;else exit
sprite: ld b,32 ;height in pixels
ld c,1 ;width in bytes
sprloop:push bc ;preserve the dimensions
ld b,0 ;BC now equals the width in bytes
ldi ;copy the byte
dec e ;ldi increases de, so decrease it again
pop bc ;restore the dimensions
ex de,hl ;to move DE onto the next screen line,
call nxtline ;we use the routine to move HL onto the
ex de,hl ;the next line, but swap DE and HL either side
djnz sprloop ;if not all lines done, loop back
ret
nxtline:ld a,8
add a,h ;add 8 to H (the high byte)
ld h,a
ret nc ;return if no overflow
push de ;otherwise preserve DE
ld de,&C050
add hl,de ;add &C000+&50 to HL
pop de ;and get DE back again
ret
pointer:defw message ;current text pointer value
message:defm "HELLO CPCWIKI PROGRAMMERS! + ",0
.sprcnt db 8 ;sprite part counter (each sprite 8 bytes wide)
.sprloc dw &4000 ;sprite part location
.sprtab dw &4000 ;sprite address table
dw &4100
dw &4200
dw &4300
dw &4400
dw &4500
dw &4600
dw &4700
dw &4800
dw &4900
dw &4A00
dw &4B00
dw &4C00
dw &4D00
dw &4E00
dw &4F00
dw &5000
dw &5100
dw &5200
dw &5300
dw &5400
dw &5500
dw &5600
dw &5700
dw &5800
dw &5900
dw &5A00
dw &5B00
dw &5C00
dw &5D00
dw &5E00
dw &5F00
dw &6000
dw &6100
dw &6200
dw &6300
dw &6400
dw &6500
dw &6600
dw &6700
dw &6800
dw &6900
dw &6A00
dw &6B00
dw &6C00
dw &6D00
dw &6E00
dw &6F00
dw &7000
dw &7100
dw &7200
dw &7300
dw &7400
dw &7500
dw &7600
dw &7700
dw &7800
dw &7900
dw &7A00
Quote from: Axelay on 13:01, 15 January 10
The swap equ (&30 xor &10) should be changed to swap equ &20 if it isnt compiling. You want to swap between &30 and &10 each frame, which you get by xoring the current value with &20. This is the number you get by xoring the two values you want to alternate between.
Had another thought; as my sprite data is 16kb I will start having to squeeze stuff in if I do use double buffering, unless I compress the sprites which is more overhead...! I will work on moving it about a bit and give it another go.
Quote from: Axelay on 13:01, 15 January 10
Something I do to work out what my code is doing is change the colour of the border while it runs. I've added a couple of colour changes to the border in your code so that for the last quarter of your scroll code, you can see how long it takes, and when it is occurring. It's the blue part of the screen. If you modify somewhere on the screen without double buffering, you need to make sure you dont do it at the same point in the frame as that part of the screen is updated, if you want to avoid it being visible as flicker or tearing.
Thanks Axelay, that's a great tip for helping to work out the timings. I see from your example that 1) the scroll is taking up most of the frame and 2) the last part of the scroll is happening at the same point on the screen as the scroll. I tried it on my real CPC for the first time and it's not as jerky as I thought though, but there is still some tearing.
Quote from: Axelay on 13:01, 15 January 10
The swap equ (&30 xor &10) should be changed to swap equ &20 if it isnt compiling. You want to swap between &30 and &10 each frame, which you get by xoring the current value with &20. This is the number you get by xoring the two values you want to alternate between.
I use Pasmo for my coding and it supports this syntax.
But essentially:
&30 is the crtc base for &c000-&ffff, &10 is the crtc base for &4000-&7ffff.
If you want to swap between two numbers you can take the number and XOR it. Then take that and the XOR will put it back.
swap is a constant value which is the result of &30 xor &10 and is the number used to change between them.
Quote from: Axelay on 13:01, 15 January 10
The swap equ (&30 xor &10) should be changed to swap equ &20 if it isnt compiling.
WinAPE simply doesn't like the brackets (it's a Maxam thing). It will work with
swap equ &30 xor &10
Ok, I've got around to trying to properly implement double buffering (a new project requires it).
However, if you want to move a sprite (normal classic CPC sprite that is) AND use double buffering, what's the best way to do it?
For example, to move a sprite from left to right I have come up with the following loop:
- get screen base (&4000 or &C000)
- get screen location and add screen base (e.g. &0010 + &C010 = &C010)
- decrease screen location by 1, delete sprite, increase it by two and draw new sprite
- store increased screen location
- swap screen base
- loop and do it again
This would work in theory (well it did on a bit of paper) but you get into all sorts of problems when reaching boundries (what if sprite address at start of routine is &C000?) and I am thinking there must be a better way of doing this...?! ::)
Quote from: redbox on 20:58, 07 October 10
Ok, I've got around to trying to properly implement double buffering (a new project requires it).
However, if you want to move a sprite (normal classic CPC sprite that is) AND use double buffering, what's the best way to do it?
For example, to move a sprite from left to right I have come up with the following loop:
- get screen base (&4000 or &C000)
- get screen location and add screen base (e.g. &0010 + &C010 = &C010)
- decrease screen location by 1, delete sprite, increase it by two and draw new sprite
- store increased screen location
- swap screen base
- loop and do it again
This would work in theory (well it did on a bit of paper) but you get into all sorts of problems when reaching boundries (what if sprite address at start of routine is &C000?) and I am thinking there must be a better way of doing this...?! ::)
I store the current x byte and y line position of the sprite on the screen and then convert this into screen address before drawing.
I also calculate the width and height in tiles, since this is the way I then delete it (by redrawing complete tiles it covers).
I also remember the position on the current frame and previous frame. When I then swap the screens I use this info to know which are to delete.
Does this help?
So the loop becomes:
wait for vsync
swap screen (to show one I just created)
now I delete from old frame using positions I stored
update current x,y to move sprite
draw it in this new position
store this position
and loop
Also, say I update the score. When i then exchange the screens to make this new change visible, I must copy the data from the displayed back to the other screen so that this remains correct and updated. So as well as remembering which tiles to redraw to erase the sprites in their position on this screen, I also remember which regions to copy back to keep this screen up to date. Things can get a little more complex when scrolling is involved, but generally, it boils down to using x,y coordinates and then computing the screen address based on the scroll, and when you delete tiles you do so based on an old scroll position, then update the scroll itself I think.
Quote from: arnoldemu on 21:29, 07 October 10
I also remember the position on the current frame and previous frame.
This is the same as "decrease screen location by 1, delete sprite, increase it by two and draw new sprite" in my loop because the sample I gave is just increasing x co-ordinate by 1 each time.
So I think our loops are the same except you are storing the previous position and I am calculating it?
My code to move the sprite from left to right (increasing x co-ordinate) would be something along the lines of:
swap equ &20
di
ld hl,&c9fb ;install interupt disabler
ld (&0038),hl
ei
ld bc,&bc0c ;set initial scr base
out (c),c
inc b
ld a,(scr_base)
out (c),a
dec b
inc c
out (c),c
inc b
ld a,0
out (c),a
loop: ld b,&f5 ;wait for v-sync
ml2: in a,(c)
rra
jr nc,ml2
ld bc,&bc0c ;write scr base to reg 12
out (c),c
inc b
ld a,(scr_base)
out (c),a
ld a,(scr_base) ;swap scr base
xor swap
ld (scr_base),a
ld a,(spr1_direct) ;check for sprite direction
cp 1
call z,increase_x
jp loop
increase_x: ld a,(spr1_scraddr) ;load x co-ordinate screen address
dec a ;decrease it by 1
ld l,a ;load it into low byte of HL
ld a,(scr_base) ;get screen base
add a,a ;turn it into high byte of screen address
add a,a ;A = &C0 or &40
ld h,a ;load it into high byte of HL
ld d,a ;load it also into high byte of DE
ld e,&40 ;load low byte with width of screen (32x32)
ld bc,&800+&00 ;load B with next line calc and C with most common byte
call spr_erase ;erase old sprite
ld a,(spr1_scraddr) ;load screen address x co-ordinate
inc a ;increase it by 1
ld (spr1_scraddr),a ;and save it again for next time
ld l,a
ld a,(scr_base)
add a,a
add a,a ;A = &C0 or &40
ld h,a
ld d,a
ld e,&40
ld bc,&800+&C0
call spr1_draw ;draw new sprite
ret
scr_base: defb &30
spr1_scraddr: defw &10
spr1_direct: defb 1
It appears to work but is flickery as hell, which kind of defeats the object :(
Quote from: redbox on 22:24, 07 October 10
This is the same as "decrease screen location by 1, delete sprite, increase it by two and draw new sprite" in my loop because the sample I gave is just increasing x co-ordinate by 1 each time.
So I think our loops are the same except you are storing the previous position and I am calculating it?
My code to move the sprite from left to right (increasing x co-ordinate) would be something along the lines of:
swap equ &20
di
ld hl,&c9fb ;install interupt disabler
ld (&0038),hl
ei
ld bc,&bc0c ;set initial scr base
out (c),c
inc b
ld a,(scr_base)
out (c),a
dec b
inc c
out (c),c
inc b
ld a,0
out (c),a
loop: ld b,&f5 ;wait for v-sync
ml2: in a,(c)
rra
jr nc,ml2
ld bc,&bc0c ;write scr base to reg 12
out (c),c
inc b
ld a,(scr_base)
out (c),a
ld a,(scr_base) ;swap scr base
xor swap
ld (scr_base),a
ld a,(spr1_direct) ;check for sprite direction
cp 1
call z,increase_x
jp loop
increase_x: ld a,(spr1_scraddr) ;load x co-ordinate screen address
dec a ;decrease it by 1
ld l,a ;load it into low byte of HL
ld a,(scr_base) ;get screen base
add a,a ;turn it into high byte of screen address
add a,a ;A = &C0 or &40
ld h,a ;load it into high byte of HL
ld d,a ;load it also into high byte of DE
ld e,&40 ;load low byte with width of screen (32x32)
ld bc,&800+&00 ;load B with next line calc and C with most common byte
call spr_erase ;erase old sprite
ld a,(spr1_scraddr) ;load screen address x co-ordinate
inc a ;increase it by 1
ld (spr1_scraddr),a ;and save it again for next time
ld l,a
ld a,(scr_base)
add a,a
add a,a ;A = &C0 or &40
ld h,a
ld d,a
ld e,&40
ld bc,&800+&C0
call spr1_draw ;draw new sprite
ret
scr_base: defb &30
spr1_scraddr: defw &10
spr1_direct: defb 1
It appears to work but is flickery as hell, which kind of defeats the object :(
if it flickers then you are doing something wrong I think.
I never get flicker with double buffers.
I have 2 values that I swap. The "visible screen base" and the "drawing screen base".
I see them here in your code, but I don't see where you swap the drawing screen base, only the visible.
Quote from: arnoldemu on 09:42, 08 October 10
I have 2 values that I swap. The "visible screen base" and the "drawing screen base".
I see them here in your code, but I don't see where you swap the drawing screen base, only the visible.
This is taken care of in my loop as follows:
swap equ &20
.....
loop: ld b,&f5 ;wait for v-sync
ml2: in a,(c)
rra
jr nc,ml2
ld bc,&bc0c ;write scr base to reg 12
out (c),c
inc b
ld a,(scr_base)
out (c),a
ld a,(scr_base) ;swap scr base
xor swap
ld (scr_base),a
call increase_x
.....
jp loop
increase_x:
......
ld a,(scr_base) ;get screen base
add a,a ;turn it into high byte of screen address
add a,a ;A = &C0 or &40
ld h,a ;load it into high byte of HL
......
ret
scr_base: defb &30
So at first instance, the screen address is set to &30 (&C000) which becomes the visible screen. This is then swapped to &10 (&4000 - being the non-visable screen) and stored. The sprite routine then uses this &4000 to draw to and when finished the loop starts again. Visible screen address is set to &4000 and value is swapped to &C000 (non-visible screen) and sprite printing starts and so on.
But I still can't work out why it flickers...! >:(
Quote from: redbox on 15:11, 08 October 10
But I still can't work out why it flickers...! >:(
Okay, I've worked it out now and it was to do with my sprite printing routine.
The sprite printing was working on the assumption that the screen address was always &C000, so when &800 was added for the next pixel line it was using a JR NC,nextline working on the assumption that if this overflows I can add &C050 to get to the next character line.
Obviously, when this is being done with a screen base of &4000, the adding of &800 never overflows, so the next character line being calculated was outside of video memory. So only one version of this part of the sprite was being displayed every other frame (on the &C000 base) - hence the flicker.
The code was this:
ld hl,&C000 ; screen address
ld b,&800 ; next pixel line down
ld de,&C050 ; next character line down
spr1_line1:
..... print sprite line 1 .....
ld a,h ; calculate next line down
add b
ld h,a
jr nc,spr1_line2
add hl,de
spr_line2:
..... print sprite line 2 .....
Glad to have found it but means my sprite routine will be quite a bit slower now ::) Anyone got any ideas as to what I can use instead (reg C is not used in above example but is reserved for other duties)...?
This works for me :)
HL can be in range &4000-&7fff, OR &c000-&ffff and it still works :)
ld a,h
add a,8
ld h,a
and &38
ret nz
ld a,l
add a,&50
ld l,a
ld a,h
adc a,&c0
ld h,a
ret
Did you have a look at the sprite code example I posted a while ago?
http://cpcwiki.eu/forum/index.php/topic,668.msg11563.html#msg11563 (http://cpcwiki.eu/forum/index.php/topic,668.msg11563.html#msg11563)
That used a method of generating a table of addresses rather than calculating them as needed which can be faster, but only if the circumstances suit, and it eats memory. I think as I demonstrated it, it's actually slower than calculating them because to aid clarity I didnt use unrolled loops.
In any case, something else I've been trying lately is using sprites locked to even address lines (considering y=0 is top screen line), because then the next odd line is always a set 3,h away (or whichever register is appropriate). If that suits your requirements you could save some time as then you would have (I think) a sprite routine that looks like this:
ld hl,&C000 ; screen address
ld b,&800 ; next pixel line down
ld de,&C050 ; next character line down
spr1_line1:
..... print sprite line 1 .....
set 3,h ; calculate next line down
spr_line2:
..... print sprite line 2 .....
call next_address_line ; calculate next line down
spr1_line3:
..... print sprite line 3 .....
set 3,h ; calculate next line down
spr_line4:
..... print sprite line 4 .....
call next_address_line ; calculate next line down
; and so on
Quote from: arnoldemu on 14:39, 09 October 10
This works for me :)
Thanks, that's great and will give it a whirl in my routine.
Quote from: Axelay on 05:00, 10 October 10
Did you have a look at the sprite code example I posted a while ago?
Yes I did see this and had it on my todo list to look into further - looks very interesting as appears to provide a solution to some of the problems I've been having. Now all I have to do is work out how to reduce the overhead of copying background, printing sprite and then restoring background ???
Quote from: Axelay on 05:00, 10 October 10
In any case, something else I've been trying lately is using sprites locked to even address lines (considering y=0 is top screen line), because then the next odd line is always a set 3,h away (or whichever register is appropriate).
That's quite an optimisation, especially if you use it with fast sprites. It would especially make up for the overhead of calculating next line down on double buffered screens (because you can't just detect overflow on lower screen bases as discussed above).
If you are using the Plus and paging in the ASIC (&4000 to &7FFF) does this mean you can't use this area for double buffering...?
You can use it, but obviously you need to page the ASIC out whilst reading/writing to the screen buffer there.