The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Joefish
Microbot
Posts: 172
Joined: Tue Nov 14, 2017 10:26 am

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Joefish » Tue Apr 17, 2018 5:10 pm

Ast A. Moore wrote:
Tue Apr 17, 2018 4:55 pm
If you’re referring to the +2A/+3 code, then the port itself doesn’t seem to be contended. The LD A,(NNNN) instruction is what affects the timing, as reading from a non-contended address will break the sync.
I was asking about both, so what about the 48K code? Is Ersh right? Doesn't the contention of IN always give you the attribute byte? i.e. it's impossible to catch the pixel byte?
0 x

User avatar
Ast A. Moore
Manic Miner
Posts: 341
Joined: Mon Nov 13, 2017 3:16 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Ast A. Moore » Tue Apr 17, 2018 5:28 pm

Ersh wrote:
Tue Apr 17, 2018 4:46 pm
If the CPU is suspended for 4 T-states while the ULA fetches bitmap, attr, bitmap, attr. When the CPU is released shouldn't every read of the port be that last 'attr'? Clearly that's not the case, just wondering how that works.
No, that’s precisely what happens. On a +2A, the last read value is latched onto the bus. That’s why we need to keep the LD A,(NNNN) in the loop; otherwise we’ll get a bunch of false positives.

One thing to keep in mind, though. Contention only applies to the code running in contended memory (that’s why it’s called that). Code running elsewhere is unaffected and the CPU is running at full speed all the time.
Ersh wrote:
Tue Apr 17, 2018 4:46 pm
Also how is the well-timed loop 'synchronized' to always get the attr? It takes a certain amount of T-states but doesn't it need to start at the right time as well?
That’s a bit of a mystery to me. However, if you move the border-changing code down in the top border are (by increasing the delay), you’ll see that the position of the first blue pixels jitters a bit. So syncing is not cell-perfect. However, with the padding instructions I suggest, you can be rest assured that it’ll only fetch the attribute byte. Fill the bitmap area of the screen with the same byte as the attribute byte you’re expecting, and you’ll see that the sync will only occur in the attribute area.
1 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

User avatar
Ast A. Moore
Manic Miner
Posts: 341
Joined: Mon Nov 13, 2017 3:16 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Ast A. Moore » Tue Apr 17, 2018 5:36 pm

Joefish wrote:
Tue Apr 17, 2018 5:10 pm
Ast A. Moore wrote:
Tue Apr 17, 2018 4:55 pm
If you’re referring to the +2A/+3 code, then the port itself doesn’t seem to be contended. The LD A,(NNNN) instruction is what affects the timing, as reading from a non-contended address will break the sync.
I was asking about both, so what about the 48K code? Is Ersh right? Doesn't the contention of IN always give you the attribute byte? i.e. it's impossible to catch the pixel byte?
I partially answered that already while you were typing your question. Contention is only pertinent to the code running in contended memory, or if you’re addressing contended memory or contended port. It is my understanding that the ports available for this trick on the +2A are non-contended, but contention applies partially to the LD A,(NNNN) instruction.

On a 48K/128K/+2, however, the situation is different. You have more ports to choose from—some contended some not. If you play around with the port address in my sample code, you’ll see the effect immediately.

But yes, it is technically possible to catch the pixel byte. That’s why I used a bitmap pattern of the same byte as the attribute in my initial tests—as a booby trap for myself.

Insert something like the following right before the fl_bus label in my example code and observe the effect:

Code: Select all

	ld bc,6144	
	ld hl,$4000
2$	ld a,9			;pattern fill
	ld (hl),a
	cpi
	jp pe,2$
See? The sync stays still happens only on the attribute line, even though the entire bitmap area of the screen file is filled with the value 9. No false triggering.
1 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

User avatar
Ersh
Berk
Posts: 48
Joined: Mon Nov 13, 2017 1:06 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Ersh » Tue Apr 17, 2018 5:42 pm

Ast A. Moore wrote:
Tue Apr 17, 2018 5:28 pm
One thing to keep in mind, though. Contention only applies to the code running in contended memory (that’s why it’s called that). Code running elsewhere is unaffected and the CPU is running at full speed all the time.
Totally forgot about that! :lol:

Thanks for putting all this together. It'll be fun to explore this in the future.
0 x

Joefish
Microbot
Posts: 172
Joined: Tue Nov 14, 2017 10:26 am

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Joefish » Tue Apr 17, 2018 5:48 pm

Ersh wrote:
Tue Apr 17, 2018 5:42 pm
Ast A. Moore wrote:
Tue Apr 17, 2018 5:28 pm
One thing to keep in mind, though. Contention only applies to the code running in contended memory (that’s why it’s called that). Code running elsewhere is unaffected and the CPU is running at full speed all the time.
Totally forgot about that! :lol:

Thanks for putting all this together. It'll be fun to explore this in the future.
I was just thinking that he should be a politician - 3 replies and still no answer to the original question!!!! :lol:

So in the first (48K) example, the IN address is 0x40FF. Since this is equivalent to a memory address in the video RAM (16639) the ULA treats it as contended, so it's this contention of the IN instruction that keeps the loop synchronised when not in the border, so that it's always looking at either (a) the floating bus byte or (b) the attribute byte.

(If you did want to read the pixel byte, you'd need another contended memory or port operation to synchronise things, then use an uncontended IN port address to catch the pixel byte at the right moment).
0 x

User avatar
Ast A. Moore
Manic Miner
Posts: 341
Joined: Mon Nov 13, 2017 3:16 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Ast A. Moore » Tue Apr 17, 2018 6:10 pm

Joefish wrote:
Tue Apr 17, 2018 5:48 pm
I was just thinking that he should be a politician - 3 replies and still no answer to the original question!!!! :lol:
Please address your complains to my secretary. You will receive an official reply in 7–12 days.
Joefish wrote:
Tue Apr 17, 2018 5:48 pm
So in the first (48K) example, the IN address is 0x40FF.
That’s assuming that port contention pattern is identical to memory contention. I’m not a hundred percent sure of that, though.
Joefish wrote:
Tue Apr 17, 2018 5:48 pm
Since this is equivalent to a memory address in the video RAM (16639) the ULA treats it as contended, so it's this contention of the IN instruction that keeps the loop synchronised when not in the border, so that it's always looking at either (a) the floating bus byte or (b) the attribute byte.

(If you did want to read the pixel byte, you'd need another contended memory or port operation to synchronise things, then use an uncontended IN port address to catch the pixel byte at the right moment).
Quite possible, quite possible. It seems we do need contention in both cases—either port contention (for the IN instruction), or memory contention (for the LD A,(NNNN) instruction). It’s been over a year since my original experiments. I remember trying a gazillion different combinations of loop instructions before nailing it.
1 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

djnzx48
Berk
Posts: 30
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by djnzx48 » Wed Apr 18, 2018 5:58 am

Nice, it all makes a lot more sense now. So the whole reason for the timing loop is to ensure that only the attribute byte is read and not the bitmap byte? And I suppose that you can still check for attribute bytes with even INK values, as long as you don't have any other attributes where the corresponding odd value is used?

It's a shame that the only two emulators capable of using this technique are closed source, and both seem to have dead download links at the moment. I know that if you're using an emulator, you can easily just switch to 128K mode, but it means you can't really make games using the +2A/+3 floating bus unless you've got real hardware to test it on. So it's of limited use at the moment.
0 x

User avatar
Nomad
Manic Miner
Posts: 521
Joined: Thu Dec 28, 2017 12:38 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Nomad » Wed Apr 18, 2018 6:30 am

One thing that should make everyone uncomfortable..

This is just one un-implemented behavior of a core system. How many more behaviors are left unimplemented by emulators on core/primary systems they are presenting as accurate.

Now consider that many of these same emulators have had continuous development for more than a decade and still do not implement behaviors that are known on core/primary systems?
0 x

User avatar
Ast A. Moore
Manic Miner
Posts: 341
Joined: Mon Nov 13, 2017 3:16 pm

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Ast A. Moore » Wed Apr 18, 2018 8:59 am

An update: I added an example of filling the bitmap area of the display file with the same value as the attribute byte we’re using as a sync trigger to clearly demonstrate that it does not break the sync.

djnzx48 wrote:
Wed Apr 18, 2018 5:58 am
Nice, it all makes a lot more sense now. So the whole reason for the timing loop is to ensure that only the attribute byte is read and not the bitmap byte? And I suppose that you can still check for attribute bytes with even INK values, as long as you don't have any other attributes where the corresponding odd value is used?
Yes and yes. Just keep in mind that the returned value will always have Bit 0 set, so black ink will be misread as blue, red as magenta, green as cyan, and yellow as white.
djnzx48 wrote:
Wed Apr 18, 2018 5:58 am
It's a shame that the only two emulators capable of using this technique are closed source, and both seem to have dead download links at the moment.
I don’t believe the links are dead: SpecEmu, Spectramine.
djnzx48 wrote:
Wed Apr 18, 2018 5:58 am
I know that if you're using an emulator, you can easily just switch to 128K mode, but it means you can't really make games using the +2A/+3 floating bus unless you've got real hardware to test it on. So it's of limited use at the moment.
On the contrary. It’s because you can easily switch Spectrum models in an emulator that you can use the floating bus on all models in your code.

My philosophy is very simple and straighforward: I consider myself to be a Spectrum developer. Not a particular-flavor-of-a-software-or-hardware-emulator-or-clone-of-the-Spectrum developer. At the same time, I hold absolutely no grudge against the latter. I’ve brought this issue up a few times already: emulator authors—both software and hardware—each has his own agenda and it may not always be the accuracy of emulation of the original hardware. At the very least, it might not be at the top of the list. Again, there’s nothing wrong with that.

But we as developers can (to an extent) influence their decisions. If enough people request a feature, chances are it’ll be implemented sooner rather than later. By avoiding the use of a feature of the original hardware “because [insert name of emulator] doesn’t support it,” we’ll only create a vicious circle and reinforce the notion that “meh, nobody really uses it anyway.” Conversely, if a particular feature receives widespread use among developers, users themselves will ask for it to be implemented. Even if they don’t know what’s wrong, they’ll say, “Hey, this works on my +2A, but it doesn’t in my emulator! What gives?”

So far, A Yankee in Iraq is the only game that exploits the floating bus trick on the +2A/+3. Yes, it won’t work on most emulators, but, as you pointed out, it takes a second to switch to a different Spectrum model, so most people will simply do that. After all, there are plenty of commercial games from way back when that only work on a particular Spectrum model. But one game alone is never going to convince emulator authors to implement this feature. Mark and weiv did it because they found it challenging, worth their effort, amusing, fun—you name it. I don’t know what motivated them exactly. (It certainly wasn’t the game itself :lol: )

So, petition your local MP. ;) Or contribute to the development of an emulator. Here’s the original ticket for Fuse. It has links to all the pertinent information necessary to test the implementation the floating bus behavior on the +2A/+3, including my original tests that Mark Woodmass and weiv used (my handle on SourceForge is “Nichals Naime”). But more importantly, write your own games that use it.
Nomad wrote:
Wed Apr 18, 2018 6:30 am
This is just one un-implemented behavior of a core system. How many more behaviors are left unimplemented by emulators on core/primary systems they are presenting as accurate.

Now consider that many of these same emulators have had continuous development for more than a decade and still do not implement behaviors that are known on core/primary systems?
This ties in neatly to what I wrote above: I don’t for a second think there’s malice behind any of this. Both emulator development and reverse engineering the Spectrum can be quite daunting. Both are carried out by enthusiasts and usually provided free of charge. At some point, however, their priorities change from “let’s make the most accurate Spectrum emulator” to “hey, that’s good enough for me, let’s add tons of new features.”

As for something esoteric such as the floating bus on the +2A, unless there’s an incentive, it’s never going to be implemented by an emulator author whose priorities have long changed.

There’s also a more trivial reason—people simple lose interest.
1 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

Joefish
Microbot
Posts: 172
Joined: Tue Nov 14, 2017 10:26 am

Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum

Post by Joefish » Wed Apr 18, 2018 10:06 am

There is an alternative, which is to write your code with the right timing built-in to its execution time, and synch it to the interrupt. But you do have to identify the machine you're running on to get the timing right.

There are various approaches to make sure your code always takes the same time to execute. Generally you have to work out your 'worst case' that takes the longest time, then make sure that shorter branches of code are padded out to take the same time as the longer branches. It may seem wasteful, but then if your code can't handle its 'worst case' every time then it's not going to work properly anyway.

So you avoid IF-THEN-CALL-RETURN type structures, and do IF-THEN-JUMP and JUMP back, and if the jump isn't taken, pad for time.
And a sprite routine should draw the maximum number of sprites every time, even if it means dumping some to off-screen (but still contended) memory. The timing doesn't have to be perfect (unless you're trying to do multicolour effects), just close enough with a bit of a safety margin.

Though if you want to start shifting graphics during the bottom border time, you have to time your code right from the top of the TV frame to the bottom of the pixel area, then be sure you can take a break and wait for the interrupt, then continue.

If you're doing a complex A.I. routine, or a sort, you might be relying on it completing well short of the worst case 99% of the time, and just let the odd frame drop or flicker if it does run a particularly bad case. In which case yes, you'd still need a synchronisation trick like this floating bus.
0 x

Post Reply