Page 2 of 3

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 12:05 pm
by Ralf
Hi Giorgio! Welcome to the forums!

This discussion about floating bus is certainly very interesting.

I would have one advice to beginner programmers however. Don't mess with with floating bus ;) It's a rather advanced trick. Joffa games were generally a piece of quite sophisticated code.

A "normal", typical game did it this way:

1) It keeps a screen buffer somewhere in memory
2) It draws to this buffer as fast as it can but usually longer than one frame
3) When it's ready it copies the buffer to screen by series of LDI commands without any synchronisation with screen refresh

Copying buffer to screen may alone take more than one frame so some artifacts on screen may happen. And the final framerate was around 12.5 fls or so.

It all may sound very rough and crude but in practice it worked and the result was acceptable.

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 1:06 pm
by Nomad
Did Joffa Smith ever talk about or write on how he wrote his games?

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 1:14 pm
by Ralf
Did Joffa Smith ever talk about or write on how he wrote his games?
He made some comments here and there but not very detailed.
In my opinion he was generally a very modest man. He would probably say you that all his tricks were nothing special and not worth to get excited about.

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 1:37 pm
by Nomad
Kind of like an anti Ian Weatherburn then (personality wise). :lol:

One of the most frustrating things trying to find out about N.O.M.A.D was the main developer had left nothing behind about how he wrote the game. It was weird that for a guy that was so sure of his own genius that he didn't talk about his process more before he became An Hero after Canvas folded.

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 2:51 pm
by Ast A. Moore

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 4:48 pm
by druellan
Ralf wrote: Tue Apr 17, 2018 1:14 pm He made some comments here and there but not very detailed.
In my opinion he was generally a very modest man. He would probably say you that all his tricks were nothing special and not worth to get excited about.
Was Steve Wetherill also able to figure out the trick for SideWize?

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 5:15 pm
by Joefish
Joffa did give some loose notes on how he did his scrolling, but they were such that you'd only really understand it if you tried to program it yourself. I had a few private exchanges with him and here are the basics that I gleaned. I didn't actually get anything from him about the floating bus, so I don't know when or where he used it. Much of what I understood was simply down to timing of the program code.

His general approach was for a 25fps update rate, which worked as follows:

1. Wait for an interrupt.
2. Do some pre-processing of various stuff. This is a delay whilst the raster reaches the first line of the screen, so could be using the floating bus. This may involve sorting the sprites into vertical order.
3. Redraw the scrolling level scenery line-by-line from the top down, just behind the raster. Any sprites are erased by this scenery wipe. This is inevitably slower than the raster so by the time it finishes, the raster will be well into the lower scoreboard, if not the lower border or even the next TV frame.
4. Draw the sprites as fast as possible, hopefully before the raster catches up with their positions - hence the vertical sorting earlier. Fully expect the next frame interrupt to occur while this is going on, if it hasn't already.
5. Make sound effects. Rig the system so the next interrupt breaks out of the sound routine and loops back to 2.

Sprites were generally only placeable at 2-pixel positions (4 pre-shifted frames horizontally, data stored in a left-right/right-left zig-zag form with masks and drawn two rows at a time). There were enough black-attribute columns either side for the screen to mask off sprites that went outside the scenery area.

For games, specifically:

Green Beret -
Used an 8-character high buffered strip that was pixel-scrolled horizontally, filled in at the edge from scenery data, and copied to the screen. Don't know if the scroll and copy were done simultaneously or if the scroll was done, then POP+PUSH used to copy it. The top third of the screen is simply erased. The rockets are made to protrude slightly higher into the empty sky by adding sprites to their tips.

Hysteria
Like Green Beret, used an 8-character high strip pixel-scrolled horizontally and copied to the screen. But some of the extra space was filled in by parallax patterns at the bottom of the screen. These are simply 16-bit values repeatedly PUSHed across the screen. Note how the sprites that are supposed to be sat on the background judder slightly as they move in 2-pixel resolution compared to the scenery's 1-pixel movement. Note also you can grab items off the background, though you're actually running along the strip of ground in front that's moving in 2-pixel steps.

Cobra
This one is more complicated - there is no background buffer; the scenery is redrawn from scratch each frame. There are a very limited number of scenic blocks, but it's actually quicker than a scroll+copy. Note also that the scenery now scrolls in 2-pixel steps.
The pre-shifted scenery blocks are loaded into the registers AF, BC, DE and HL then the scenery is drawn by a series of PUSHes, PUSHing the relevant pattern onto the screen at that point. It's wasteful as a scrolling pattern may be half-in and half-out of a character position. So the patterns for solid platforms might be:

AF = empty space
BC = start of platform
DE = continuous platform
HL = end of platform

But before this happens, the program must first re-write the list of PUSH instructions to match the level data. The PUSH list for each row of scenery blocks is re-used to draw all 16 pixel rows. It only has to POP four registers to fetch the scenery patterns for an entire pixel row, so it's quicker that using POP+PUSH to copy a row of data (just as many PUSHes, but fewer POPs).

The scroll can also draw things one-character over, so it only needs scenery pre-shifted to 4 positions (to cover each pattern moving across 8 pixels, not 16). The blacked-off borders either side of the screen are uneven; there's an extra character on one side to hide the scenery redraw moving about, as well as any sprite overlap.

It can have a different set of patterns on each row of the screen, and because of the character-position adjustment it can have either:
- one pattern that can be repeated to any length (e.g. platforms which need 3 pre-shifted patterns to start, repeat and end)
- or 3 separate patterns of a narrow upright object (e.g. pipes up to 10 pixels wide, which always just fit within one 16 pixel wide pre-shifted block)
- or one platform that must start and end within 24 pixels and one narrow upright object.

Finally, there's a faster parallax bit at the bottom of the screen drawn by just PUSHing a 16-bit wide pattern right across the screen. This is programmed to occur twice per game cycle. The result is that this part scrolls at 50fps, making everything seem a bit smoother.

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 6:59 pm
by Ast A. Moore
Joefish wrote: Tue Apr 17, 2018 5:15 pm I didn't actually get anything from him about the floating bus, so I don't know when or where he used it.
I think he only used it in Cobra and Terra Cresta. Both use the same code:

Code: Select all

	ld bc,$3f28	;Cobra
1$	ld a,c
	in a,($ff)
	cp b
	jr nc,1$
	
	ld c,$28	;Terra Cresta
	ld b,$3f
1$	ld a,c
	in a,($ff)
	cp b
	jr nc,1$
druellan wrote: Tue Apr 17, 2018 4:48 pm
Ralf wrote: Tue Apr 17, 2018 1:14 pm He made some comments here and there but not very detailed.
In my opinion he was generally a very modest man. He would probably say you that all his tricks were nothing special and not worth to get excited about.
Was Steve Wetherill also able to figure out the trick for SideWize?
Yes, he was. Sidewize was never re-released, though. So it hangs on a +2A. Steve’s code is as follows:

Code: Select all

	ld bc,$40ff
	ld e,$40	;BRIGHT BLACK INK on BLACK PAPER
sync	ld a,r		;padding instruction
	in a,(c)
	cp e
	jp nz,sync
Curiously enough, in his blog post he admits that he doesn’t remember wha the LD A,R instruction does and speculates that it’s “some self-modifying code to conditionally branch on the state of the Z80 parity flag.” :D

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 7:07 pm
by Joefish
I didn't fully appreciate that Sidewize / Crosswize run at 50fps.
That's a lot of scenery and sprites being redrawn. I can see that Sidewize uses a block of scenery that repeats several times across the screen, and I know Crosswize multi-loads, so it's probably not being super-efficient with memory, but nevertheless 50fps is quite an achievement!

Re: Double buffering on the Spectrum

Posted: Tue Apr 17, 2018 7:17 pm
by Ast A. Moore
Joefish wrote: Tue Apr 17, 2018 7:07 pm I didn't fully appreciate that Sidewize / Crosswize run at 50fps.
That's a lot of scenery and sprites being redrawn. I can see that Sidewize uses a block of scenery that repeats several times across the screen, and I know Crosswize multi-loads, so it's probably not being super-efficient with memory, but nevertheless 50fps is quite an achievement!
Yes, Sidewize was pretty impressive from a technical standpoint. Incidentally, I only learned about it a couple of years ago. It'd somehow slipped under my radar back in the day.

What I miss the most in Spectrum games today are ducks. Joffa really knew how to cook ’em. :lol:

Re: Double buffering on the Spectrum

Posted: Wed Apr 18, 2018 12:35 pm
by druellan
Ast A. Moore wrote: Tue Apr 17, 2018 6:59 pm Curiously enough, in his blog post
Seems he was using a bright 1 black attribute as delimiter. That's clever:

Image
I didn't fully appreciate that Sidewize / Crosswize run at 50fps.
Me either, but when I've learned about EmuZWin's real FPS indicator, I started to test games, and I loaded Sidewize because I can, and I got instantly blown again about how well it performs.

Image

I now notice the flickering, EmuZWin seems not to be doing a great job emulating this.

Re: Double buffering on the Spectrum

Posted: Fri Jun 14, 2019 12:07 am
by robpearmain
Joefish wrote: Tue Apr 17, 2018 5:15 pm


Cobra
This one is more complicated - there is no background buffer; the scenery is redrawn from scratch each frame. There are a very limited number of scenic blocks, but it's actually quicker than a scroll+copy. Note also that the scenery now scrolls in 2-pixel steps.
The pre-shifted scenery blocks are loaded into the registers AF, BC, DE and HL then the scenery is drawn by a series of PUSHes, PUSHing the relevant pattern onto the screen at that point. It's wasteful as a scrolling pattern may be half-in and half-out of a character position. So the patterns for solid platforms might be:

AF = empty space
BC = start of platform
DE = continuous platform
HL = end of platform

But before this happens, the program must first re-write the list of PUSH instructions to match the level data. The PUSH list for each row of scenery blocks is re-used to draw all 16 pixel rows. It only has to POP four registers to fetch the scenery patterns for an entire pixel row, so it's quicker that using POP+PUSH to copy a row of data (just as many PUSHes, but fewer POPs).

The scroll can also draw things one-character over, so it only needs scenery pre-shifted to 4 positions (to cover each pattern moving across 8 pixels, not 16). The blacked-off borders either side of the screen are uneven; there's an extra character on one side to hide the scenery redraw moving about, as well as any sprite overlap.

It can have a different set of patterns on each row of the screen, and because of the character-position adjustment it can have either:
- one pattern that can be repeated to any length (e.g. platforms which need 3 pre-shifted patterns to start, repeat and end)
- or 3 separate patterns of a narrow upright object (e.g. pipes up to 10 pixels wide, which always just fit within one 16 pixel wide pre-shifted block)
- or one platform that must start and end within 24 pixels and one narrow upright object.

Finally, there's a faster parallax bit at the bottom of the screen drawn by just PUSHing a 16-bit wide pattern right across the screen. This is programmed to occur twice per game cycle. The result is that this part scrolls at 50fps, making everything seem a bit smoother.
This is great information, what about when a row changed graphics, how did this work (see image attached)

Image

Re: Double buffering on the Spectrum

Posted: Fri Jun 14, 2019 1:21 am
by catmeows
robpearmain wrote: Fri Jun 14, 2019 12:07 am

This is great information, what about when a row changed graphics, how did this work (see image attached)

Image
By loading values in the middle of line.

Code: Select all

	push bc			 ; 89d2 c5 $0bts
	push af			 ; 89d3 f5 $0bts
	push de			 ; 89d4 d5 $0bts
	push bc			 ; 89d5 c5 $0bts
	push hl			 ; 89d6 e5 $0bts
	push hl			 ; 89d7 e5 $0bts
	push hl			 ; 89d8 e5 $0bts
	ld ($8bf0), sp		 ; 89d9 ed 73 f0 8b $14ts
	ld sp, ix		 ; 89dd dd f9 $0ats
	pop bc			 ; 89df c1 $0ats
	pop de			 ; 89e0 d1 $0ats
	pop af			 ; 89e1 f1 $0ats
	ld sp, ($8bf0)		 ; 89e2 ed 7b f0 8b $14ts
	push af			 ; 89e6 f5 $0bts
	push de			 ; 89e7 d5 $0bts
	push de			 ; 89e8 d5 $0bts
	push de			 ; 89e9 d5 $0bts
	push de			 ; 89ea d5 $0bts
	push de			 ; 89eb d5 $0bts
	push de			 ; 89ec d5 $0bts
	jp $9ced		 ; 89ed c3 ed 9c $0at
	

Re: Double buffering on the Spectrum

Posted: Fri Jun 14, 2019 11:27 am
by Joefish
Could it really do that?
OK, my 50Hz scrolling demo couldn't, but then there was no 'slack time' in it. I tried to keep mine running at a constant speed, whereas with all its sprites, Cobra varies a lot in execution time. Mine used the alternate registers for an alternate set of platforms, but could only swap them between rows, not columns. I just assumed Cobra did the same.
I did develop some code that used the alternate BC/DE/HL registers (or just as easily AF'/IX/IY) for an extra platform type within a row, but it takes longer to execute.

Re: Double buffering on the Spectrum

Posted: Fri Jun 14, 2019 4:00 pm
by g0blinish
maybe not related to topic, but I used another tricks for AY Mehademo 3

http://g0blinish.ucoz.ru/forblog2/mudademo.rar
(sources inside,compile with sjasm)

Re: Double buffering on the Spectrum

Posted: Wed Jun 17, 2020 5:04 am
by ketmar
sorry for necroposting, but i happened to stumble upon Joffa's name here, and simply couldn't stop myself. i reversed Firefly engine around 10 years ago (man, i'm old now!), extracted map blitting parts from it, and even made a simple editor. i intended to use that in my game, but never wrote it. it works mostly like Cobra, using low part of the screen for the status bar, and for floating bus vsyncing. the map is 32x32 tiles of 16x16 pixels, with wraparound.

if anybody's interested, i can try to reverse-engineer my code (i lost most of my notes and such, and my asm source is not really well commented ;-), and publish it here. actually, the code is quite easy to follow (i think ;-), so i may throw in more comments, and turn it into some kind of simple example/tutorial. i think Joffa would be happy to see others learning from his tricks...

Re: Double buffering on the Spectrum

Posted: Wed Jun 17, 2020 9:39 am
by Ast A. Moore
ketmar wrote: Wed Jun 17, 2020 5:04 am i reversed Firefly engine . . . . it works mostly like Cobra, using low part of the screen for the status bar, and for floating bus vsyncing
Curious. I haven’t noticed any floating bus polling in Firefly. Besides, the game runs just fine on a +2A/+3. Do you think you could share the relevant part of Firefly’s code with us?

Re: Double buffering on the Spectrum

Posted: Wed Jun 17, 2020 2:38 pm
by ketmar
yeah, that may be added by me to have some more time to do things, it is hard to tell now. as i said, i lost most of my notes and oridinal disassemblies, there's only the "final" extracted engine left. if i'll find my original notes, i'll double-check. the blitter itself doesn't really need that, you can simply "chase the beam", of course.

but tbh, i don't keep hight hopes here. i think i used the mix of IDA and my own tools, and i don't have neither IDA, nor those tools anymore.

yeah, judging from git history (which is very short ;-), floating bus vsync was added by me. so sorry for misinformation. still, "builder" and "blitter" code is almost unmodified (and it seems i introduced small bug there...).

when i'll find some more time, i will properly comment it, and will make a post. i think it will be interesting to see how exactly Joffa did his magic. and if i'll find any my old notes, i'll add them.

p.s.: ah, another thing i remember about Firefly is that it is using two types of compression for maps. i don't remember exact algos, but i think that one was your usual RLE, and another one was kind of LZ coding (but somewhat twisted). or maybe just RLE with another counter size.


p.p.s.: yeah, Firefly is one of my favorite Speccy games. i remember seeing its fantastic 8-way smooth scrolling for the first time and dropped my jaw. so dissecting Firefly was a natural choice. ;-)

Re: Double buffering on the Spectrum

Posted: Wed Jun 17, 2020 2:53 pm
by Ast A. Moore
Firefly is by far my favorite Joffa’s game. Pretty brilliant concept and implementation.

Re: Double buffering on the Spectrum

Posted: Wed Jun 17, 2020 3:09 pm
by ketmar
Ast A. Moore wrote: Wed Jun 17, 2020 2:53 pm Firefly is by far my favorite Joffa’s game. Pretty brilliant concept and implementation.
yeah, i absolutely agree. and it aged very well: it still looks fantastic, and insanely addictive. i never really liked Cobra or Green Beret (excellent technical implementation, but the gameplay is not my cup of tea). but i must confess that i am still playing Firefly from time to time, and it is still as good as it was the first time.

randomized world map is a brilliant touch too: it always looks like there are more unexplored maps there, even if you know that there are not so many of them. and each game feels like a fresh one.

eh, i can keep praizing Firefly the whole day... ;-)

Re: Double buffering on the Spectrum

Posted: Fri Jun 19, 2020 10:53 am
by ketmar
i almost reversed my old code (and put alot more comments in it). there's still one bug left (i have to double-check if it is mine, or i did something the original code wasn't designed for). i'll fix it (i hope ;-), and then i will try to write a post about it all, with code samples and some explanations.

Re: Double buffering on the Spectrum

Posted: Fri Jun 19, 2020 6:46 pm
by MonkZy
ketmar wrote: Fri Jun 19, 2020 10:53 am i will try to write a post about it all, with code samples and some explanations.
I look forward to this.

Re: Double buffering on the Spectrum

Posted: Fri Jul 03, 2020 9:44 am
by ketmar
sorry for bumping, but i want to say that i'm still working on that post/article. it is somewhat hard to split my time between various things, and i want to explain the code and the idea behind it instead of simply dumping it on you, so it require some more time. basically, i'm going through the code line-by-line, adding explanations where i think they're needed, and creating a small sample game to show how one can connect all the pieces together to get something interesting.

it looks like in becomes more a kind of Yet Another Game Programming Tutorial, this time for people who know the asm, and want to do some advanced stuff. dunno if the world needs another one of those, but at least creating it is fun.

Re: Double buffering on the Spectrum

Posted: Tue Dec 21, 2021 8:08 pm
by Art
I try to speed up my game, which draws to a memory buffer and then it copies it to the screen. It buffers the entire 6912 screen bytes, so I can use a simple ldir, but I also try the push/pop way. I wrote the following routine and it works, but the screen is a bit corrupted - two columns are doubled on the screen and if I move in the game, the screen is more and more corrupted. I admit that I don't really understand how exactly the stack works (I read some explanations, but it's still not clear). Is anything wrong with my routine and should it be faster than ldir?

Code: Select all

         di                  ; disable interrupt
         ld (stack),sp       ; save stack address
         ld ix,buffer        ; start of buffer
         ld iy,16384+15      ; start of screen + 15
         ld a,216            ; loop counter, 216*32=6912
scrcopy  ld i,a              ; counter to i

         ld sp,ix            ; stack is on ix in buffer
         pop af              ; 16 bytes from buffer to registers
         pop bc
         pop de
         pop hl
         exx
         pop af
         pop bc
         pop de
         pop hl
         exx
         ld sp,iy            ; stack is on iy in screen
         exx
         push hl             ; 16 bytes from registers to screen in reverse order
         push de
         push bc
         push af
         exx
         push hl
         push de
         push bc
         push af
         ld bc,16
         add ix,bc           ; increase buffer address by 16
         add iy,bc           ; increase screen address by 16
         
         ld sp,ix            ; stack is on ix in buffer
         pop af              ; 16 bytes from buffer to registers
         pop bc
         pop de
         pop hl
         exx
         pop af
         pop bc
         pop de
         pop hl
         exx
         ld sp,iy            ; stack is on iy in screen
         exx
         push hl             ; 16 bytes from registers to screen in reverse order
         push de
         push bc
         push af
         exx
         push hl
         push de
         push bc
         push af
         ld bc,16
         add ix,bc           ; increase buffer address by 16
         add iy,bc           ; increase screen address by 16
         
         ld a,i              ; counter to a
         dec a               ; decrease counter by 1
         jr nz,scrcopy       ; if counter is not 0, copy next 32 bytes
         ld sp,(stack)       ; restore stack address
         ei                  ; enable interrupt
         ret

Re: Double buffering on the Spectrum

Posted: Tue Dec 21, 2021 8:56 pm
by catmeows
Hi, Art.
The EXX instruction swaps BC, DE, HL pairs only. AF can swapped by special instruction EX AF,AF'.
What you do is that you load AF register twice from two different locations without swapping and that is why screen get corrupted.