Page 2 of 3
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Mon Jun 01, 2020 10:15 pm
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
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Tue Jun 02, 2020 12:52 am
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Tue Jun 02, 2020 2:09 pm
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
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Tue Jun 02, 2020 11:42 pm
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).
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Jun 03, 2020 12:02 am
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Jun 03, 2020 12:13 am
by Seven.FFF
* Oops, I said it in another Next thread somewhere else on this forum, not earlier in this thread. Soz.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Thu Jul 30, 2020 7:40 pm
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!
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Thu Jul 30, 2020 11:43 pm
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Fri Jul 31, 2020 1:00 am
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Sun Sep 06, 2020 9:43 pm
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
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Mon Sep 07, 2020 1:57 am
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...
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?
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Mon Sep 07, 2020 11:10 am
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).
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 7:51 pm
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 8:12 pm
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 9:59 pm
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 10:08 pm
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.
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 10:24 pm
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
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Sep 09, 2020 10:31 pm
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
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Mar 27, 2024 2:55 pm
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?
Re: The Definitive Programmer’s Guide to Using the Floating Bus Trick on the ZX Spectrum
Posted: Wed Mar 27, 2024 4:48 pm
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.