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.
User avatar
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

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

Post by TomD »

Very impressed with this, great for my new project where I needed to clear the sprites at at the bottom of the screen as I pushed the sprite drawing to the limit. I normally either redraw the entire screen from a back buffer or clear after a halt before plot if fast enough. That wasn't working for this so I started with an arbitrary wait loop which was tricky to get right if the number of sprites fluctuated. This seems to have solved that.

Any chance you could share your code to check if 48k/128k or +2A/+3 and how you modify the code for each?

At the moment I'm using the method you put on WoS

ld a,(2899) ;+2A/+3 ROMs return 126
cp 126
jr z,its_a_plus

and I just flip between the two versions of the fl_bus code.

Thanks in advance.

TomD
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

TomD wrote: Mon Jun 01, 2020 10:15 pm Any chance you could share your code to check if 48k/128k or +2A/+3 and how you modify the code for each?

At the moment I'm using the method you put on WoS

ld a,(2899) ;+2A/+3 ROMs return 126
cp 126
jr z,its_a_plus

and I just flip between the two versions of the fl_bus code.
The above is exactly what I use in my real-world code for machine detection. The vanilla code is written for the +2A/+3, so the its_a_plus label just jumps forward without modifying anything. If the condition is not met, then I modify the floating bus sync code:

Code: Select all

		ld hl,v_sync+2
		ld (hl),$2b		;opcode for the DEC HL instruction [6]
		ld (v_sync_sw+1),hl	;redirect the JP NZ,** to DEC HL
		ld a,$40		;change top half of port addr (must be in the $40–$7f range)
		ld (port1+1),a
		ld a,$ff		;change bottom half of port addr
		ld (port2+1),a
The original code (before modification) looks like this:

Code: Select all

port1		ld de,$xx0f		;ATTR color (xx) into D, top half of port to read into E
v_sync		ld a,($5800)		;[13]point to contended memory and fetch a "blanking" attr
					;or DEC HL [6] for the 48K/128K/+2
		ld a,e			;[4]top half of port into A
port2		in a,($fd)		;[11]read port formed by A (MSB) and FDh (LSB) into A
		cp d			;[4]test for ATTR color
v_sync_sw	jp nz,v_sync		;[10]

After modification, the code will look like this:

Code: Select all

port1		ld de,$xx40		;ATTR color (xx) into D, top half of port to read into E
v_sync		ld a,($2b00)		;this makes no sense, but we’re only interested in the third byte,
					;which is the opcode for the DEC HL instruction
		ld a,e			;[4]top half of port into A
port2		in a,($ff)		;[11]read port formed by A (MSB) and FFh (LSB) into A
		cp d			;[4]test for ATTR color
v_sync_sw	jp nz,v_sync+2		;[10]note the changed jump address

Once the CPU reaches the JP instruction, it’ll read the above code differently, namely:

Code: Select all

SMC_v_sync	dec hl			;[6]the padding instruction necessary for syncing
		ld a,e			;[4]top half of port into A
port2		in a,($ff)		;[11]read port formed by A (MSB) and FFh (LSB) into A
		cp d			;[4]test for ATTR color
v_sync_sw	jp nz,SMC_v_sync	;[10]jumping to the last byte of the “nonsensical” LD A,($2B00) instruction

Hope this makes sense.
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
TomD
Manic Miner
Posts: 374
Joined: Tue Nov 13, 2018 9:47 am
Location: Leeds UK
Contact:

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

Post by TomD »

Ast A. Moore wrote: Tue Jun 02, 2020 12:52 am
Hope this makes sense.
Many thanks, I went with the following so it does the check on the fly rather than SMC, guess not great if you change the ROM in the middle of the program but I'm not planning on doing that :-)

One thing I did notice is the +2/+3 routine doesn't work on the Next (using Next/+3 mode). Works fine if you select 128k or Usr 0 mode though so not a major issue.

TomD

Code: Select all

; ==========================================+==================================
; floating bus wait routines (35b)
; ------------------------------------------+----------------------------------
; _fl_bus is for Sinclair 48/128k & Amstrad +2 (taken from sidewize)
; _fl2_bus is for Amstrad +2A/B & +3 (by Ast A. Moore)
; ==========================================+==================================
_fl_bus:
	ld a,($0b53)			; 13t - +2A/+3 ROMs return 126
	cp $7e				; 7t
	jr z,_fl2_bus			; 12/7t
	ld bc,$40ff			; 10t - port
	ld e,$18			; 7t - attr to compare to
_fl_bus100:
	ld a,r				; 4t
	in a,(c)			; 12t 
	cp e				; 4t - is it >=check attr
	jp nc,_fl_bus100		; 10t - loop till <
	ret				; 10t
_fl2_bus:
	ld de,$180f 			; 10t - attr into d, MSB of port addr into e 
_fl2_bus100:
	ld a,($5800) 			; 13t - point to contended memory and fetch a "blanking" attr 
	ld a,e 				; 4t - MSB of port addr into a 
	in a,($fd) 			; 11t - read port $0ffd into a 
	cp d 				; 4t - is it >=check attr
	jp nc,_fl2_bus100 		; 10t - loop till <
	ret				; 10t	
Retro enthusiast and author of Flynn's Adventure in Bombland, The Order of Mazes & Maze Death Rally-X. Check them out at http://tomdalby.com
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

TomD wrote: Tue Jun 02, 2020 2:09 pm One thing I did notice is the +2/+3 routine doesn't work on the Next (using Next/+3 mode). Works fine if you select 128k or Usr 0 mode though so not a major issue.
I’m not familiar with the Next well enough to comment, but keep in mind that on a real +2A/+3, the trick only works if paging hasn’t been explicitly disabled (i.e. it won’t work in 48K BASIC mode).
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
Seven.FFF
Manic Miner
Posts: 744
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

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

Post by Seven.FFF »

Next/+3 mode runs with +3 timings and the NextZXOS ROMs, which are significantly different from the +3 ROMs, given that they’re entirely rewritten with new NextBASIC functionality for new machine features. This tape loading mode is meant for loading new Next programs rather than for compatibility. So reading an arbitrary ROM byte in this mode isn’t a particularly good way to detect +3 timings.

Some of the other modes are intended for compatibility: 48K mode runs with 48K timings and the original 48K ROMs, with Next And 128K features disabled; 128K mode runs with 128K timings Bd the original 128K ROMs, with Next features disabled. Pentagon mode runs with Pentagon timings, and Pentagon/Profi paging. USR0 mode is not really a compatibility mode, it runs in +3 timings with Next features after invoking USR0.

It happens that there isn’t a dedicated +3 compatibility mode yet, because there is insufficient space to hold all 64K of alternate +3ROMs in the memory map. Alternate ROMs are a relatively new feature that avoided the need to endlessly patch the NextZXOS ROMs for compatibility with a succession of individual games. There was only 32K available to give to the scheme though, so it hasn’t yet been implemented for +2A/+3. This could change later after some other things have been reorganised.

As I said earlier in the thread, don’t get too hung up on expecting every program to work in every mode. The modes are pragmatic tools to let every program run in at least one common configuration, with some extra advanced options and manually applied disables to further tweak the environment where needed.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 744
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

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

Post by Seven.FFF »

* Oops, I said it in another Next thread somewhere else on this forum, not earlier in this thread. Soz.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

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

Post by ketmar »

the emulation it is not completely right (i think), but ZXEmuT now can run Yankee in +2A/+3 mode! ;-)

thank you for your hard work, and for detailed explanations!
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

ketmar wrote: Thu Jul 30, 2020 7:40 pm the emulation it is not completely right (i think), but ZXEmuT now can run Yankee in +2A/+3 mode! ;-)

thank you for your hard work, and for detailed explanations!
Glad you found my little writeup useful! When I was helping Mark Woodmass to perfect the timings in SpecEmu, I made a special version of my test for him. Feel free to use it, tweak your emulator, and compare the results with this 50 fps video of my +2A running it. You can advance the video frame by frame and make sure the vertical shifts in the black gaps of the yellow border alight with the correct vertical white bars. Mark was able to nail it with the help of my test.
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
ketmar
Manic Miner
Posts: 697
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

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

Post by ketmar »

thank you even more! ;-)

for now it looks like my contention pattern for +2A is totally broken (no wonder, ZXEmuT couldn't properly run +2A/+3 at all until yesterday ;-), and Yankee works due to very specific timings. your test will definitely help to fix it all.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

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

Post by presh »

OK, so I had this working fine until I started messing around with interrupts - with IM2 enabled, the game locks up and the debugger shows it's stuck in the "floating_bus_loop" (see below).

At first I thought it might be because I hadn't DI / EI 'd either side of the code, but even after adding them it still locks up.

Any idea why this is happening?

(My interrupt code just cycles the border colour through 0-7 at the moment - a simple test routine, so that I can see it's being called successfully. Removing the bit of code which changes the border doesn't fix things either!)

Code: Select all

  DI

  LD D, 8     ; attr: PAPER 1, INK 0
  LD E, $40   ; MSB of port address ($40FF)
  
floating_bus_loop:

  DEC HL      ;[6]padding instruction
  LD A,E      ;[4]MSB of port addr into A
  IN A,($FF)  ;[11]read port 0x40FF into A
  CP D        ;[4]is it D (i.e. INK 1, PAPER 1, BRIGHT 0; FLASH 0)?
  JP NZ, floating_bus_loop	;[10]no? keep trying
	
  ; Found it!
  EI
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

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

Post by presh »

Still mega stumped by this!

I took a backup before I started messing with the interrupts, so I rolled back to that working version.

Ended up in the same situation - interrupt routine runs fine, but gets stuck in the floating bus loop and never "catches" the target value... :cry:

Added DI/EI either side - no effect.

Removed the border colour changing part of the interrupt routine again (in case my OUT (254), colour was messing with things) - no effect.

Same behaviour in Spectaculator & ZX Spin (emulating a +2 throughout development) so not convinced it's an emulator "feature" (also as it's the old "tried & tested" rather than the new +2A/+3 method) :)

Are there any caveats regarding interrupts, interrupt modes, etc I've overlooked?
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

Hmm. Could you post the entire code (well, the relevant portions) so I could test it? The only reason for it not to be working I can think of off the top of my head is that it’s sitting somewhere in contended memory (including the respective RAM banks).
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.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

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

Post by presh »

Sure, my interrupt is set up at the very beginning like so:

Code: Select all

; Set up interrupts 
    DI
    LD A, $65   ; Interrupt table at page $6500
    LD I, A     ; Set the interrupt register to that page
    IM 2        ; Set the interrupt mode
    EI          ; Enable interrupts
The interrupt itself is:

Code: Select all

    ORG $6666   ; 26214
    
INTERRUPT:

    DI
    
    PUSH AF
    PUSH BC
    PUSH DE
    PUSH HL
    EXX
    PUSH AF
    PUSH BC
    PUSH DE
    PUSH HL
    EXX 
    PUSH IX
    PUSH IY
    
    ; --- Main interrupt routine --- ;
    
    ; Update border colour
    LD HL, intrpt_test
    INC (HL)
    LD A, (HL)
    AND 7
    OUT (254), A
    
    ; --- End of main interrupt routine --- ;
    
    POP IY
    POP IX
    EXX
    POP HL
    POP DE
    POP BC
    POP AF
    EXX
    POP HL
    POP DE
    POP BC
    POP AF
    
    EI
    
    RET


intrpt_test:
    DB 0
Interrupt table is 257 bytes of $66. Interrupt runs fine and produces the desired headache-inducing border effect.

The floating bus loop is at $B187 and was working right up until I added the above bits of code in.
Nienn Heskil
Microbot
Posts: 132
Joined: Tue Jun 09, 2020 6:14 am
Contact:

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

Post by Nienn Heskil »

presh wrote: Sun Sep 06, 2020 9:43 pm

Code: Select all

  LD D, 8     ; attr: PAPER 1, INK 0
  <...>
  CP D        ;[4]is it D (i.e. INK 1, PAPER 1, BRIGHT 0; FLASH 0)?
If it's not a typo, chances are you're testing for a different attr value rather than the one you put onscreen.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

Nienn Heskil wrote: Wed Sep 09, 2020 8:12 pm
presh wrote: Sun Sep 06, 2020 9:43 pm

Code: Select all

  LD D, 8     ; attr: PAPER 1, INK 0
  <...>
  CP D        ;[4]is it D (i.e. INK 1, PAPER 1, BRIGHT 0; FLASH 0)?
If it's not a typo, chances are you're testing for a different attr value rather than the one you put onscreen.
Either that, or the tested area is not wide enough for the loop to catch it. Make sure you have enough consecutive cells with the desired attributes. Depending on which machine you run this on (48K/128K), the loop will take a different amount of time to sync up. To be on the safe side, I recommend setting the same attribute value for an entire row.
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
Rick Dangerous
Posts: 2641
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 »

presh wrote: Mon Sep 07, 2020 1:57 am Are there any caveats regarding interrupts, interrupt modes, etc I've overlooked?
Yes, your interrupt vector is pointing to the contended RAM area ($40–$7f). This will trigger the ULA snow effect. You should always make sure it’s outside that range. The ISR itself can sit anywhere in RAM, but the interrupt vector table must reside in non-contended RAM.
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.
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

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

Post by presh »

Nienn Heskil wrote: Wed Sep 09, 2020 8:12 pm
presh wrote: Sun Sep 06, 2020 9:43 pm

Code: Select all

  LD D, 8     ; attr: PAPER 1, INK 0
  <...>
  CP D        ;[4]is it D (i.e. INK 1, PAPER 1, BRIGHT 0; FLASH 0)?
If it's not a typo, chances are you're testing for a different attr value rather than the one you put onscreen.
Haha, yes. Those are [mention]Ast A. Moore[/mention]'s original comments which I hadn't updated! Well spotted :)
presh
Manic Miner
Posts: 237
Joined: Tue Feb 25, 2020 8:52 pm
Location: York, UK

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

Post by presh »

Ast A. Moore wrote: Wed Sep 09, 2020 10:08 pm
presh wrote: Mon Sep 07, 2020 1:57 am Are there any caveats regarding interrupts, interrupt modes, etc I've overlooked?
Yes, your interrupt vector is pointing to the contended RAM area ($40–$7f). This will trigger the ULA snow effect. You should always make sure it’s outside that range. The ISR itself can sit anywhere in RAM, but the interrupt vector table must reside in non-contended RAM.
Ah! Another one of those "quirks" I've heard about but never encountered and thus forgotten. Thanks for the explanation. I shall attempt to find room up there, but it's pretty... RAMmed :lol: :roll:
DoctorRad
Drutt
Posts: 27
Joined: Mon Mar 18, 2024 4:40 pm

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

Post by DoctorRad »

Ast A. Moore wrote: Wed Sep 09, 2020 9:59 pmEither that, or the tested area is not wide enough for the loop to catch it. Make sure you have enough consecutive cells with the desired attributes. Depending on which machine you run this on (48K/128K), the loop will take a different amount of time to sync up. To be on the safe side, I recommend setting the same attribute value for an entire row.
Having read through this thread and your webpage am I right in thinking that your code loop will catch one of the attribute bytes on the relevant row, but you can't be sure which one? And is it also the case that you're not 100% sure why this 35 t-state loop only catches attributes?
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2641
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 »

DoctorRad wrote: Wed Mar 27, 2024 2:55 pm Having read through this thread and your webpage am I right in thinking that your code loop will catch one of the attribute bytes on the relevant row, but you can't be sure which one? And is it also the case that you're not 100% sure why this 35 t-state loop only catches attributes?
True, you can’t be absolutely sure which attribute cell the loop will catch, but if I remember correctly, an entire row is certainly overkill. However, it would be difficult to set up a test that would reveal the exact pattern definitively. Moreover, I suspect that the exact timing of the triggering will depend on the length of the instructions preceding the sync loop and will thus be pretty much unpredictable.

As for why the loop takes the number of T states it does (note that it’ll differ on different Spectrum models), you’re right, I can’t fully explain it. I ran a few dozen tests with different padding instructions and the example outlined in my writeup is just the quickest one that works. It’s reasonable to assume that using another padding instruction (or, indeed, instructions) will let you be more precise in picking and choosing the exact attribute location at the expense of causing a significant delay (for example, you may be limited to just a single attribute per two or more rows). I’d argue it is possible to create a very sophisticated timing pattern for several consecutive loops (each with its own timing) and get very precise indeed. However practical that would be, I am not certain.
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.
Post Reply