First findings (for reference):



TODO: Write test cases combining different BPLMOD values with different scrolling values (single playfield mode first).
The BPLMOD changes is likely to do the scaling in Y direction (meaning if you set a negative modulo it will "repeat" the same line again)
My guess (without knowing at all how the code works) is that this is blitter related and how they scale in x
More findings:
The BPLMOD changes is likely to do the scaling in Y direction
Yes. This makes it unlikely that the BPLxMOD related code is the culprit. Vertical scaling seems to be fine.
My guess (without knowing at all how the code works) is that this is blitter related and how they scale in x
This might well be. I've extracted the type of the Blitter operations that are performed during the faulty scene:
ABCD Minterm Direction
1001 $F0 Ascending + Descending
1011 $E2 Ascending
0011 $0A Ascending
Sizes: (1,480), (1,483), (2,480), (1,320), (2,320)
Next step: Write test cases that run the Blitter with these settings.
For reference: Comparison with UAE's DMA debugger:

It matches what we see in vAmiga.
This sounded familiar. Similar glitch happened on UAE when "old" (or previous) BDAT was not cleared when blitter was started.
@dirkwhoffmann do you know if what Toni writes above may be the issue here?
Similar glitch happened on UAE when "old" (or previous) BDAT was not cleared when blitter was started.
Does this mean, BDAT (aka "B new" in the diagram below) is cleared whenever the Blitter is startet?

No, both "A old" and "B old" gets cleared when blitter starts. Without it blitter would be quite useless when shift is non-zero: first data that comes out from shifter would also contain some random garbage from last blit.
btw, there is also undocumented B-shift feature: if BLTBDAT is written when blitter is not running (probably same happens when it is running but thats too undefined currently anyway..), value will be shifted (using current shift value) immediately to B HOLD (which is then used as a static data for following blit(s) if B-channel is disabled). After shift B Old equals B New.
A-channel does not do this because A-channel has masking which needs to be applied every word.
Hi Toni, thanks a lot for this Information! This might very well be the issue here.
Just had a look at the code. The clearance of "A old" and "B old" is already there:
void
Blitter::beginSlowCopyBlit()
{
// Only call this function in copy blit mode
assert(!bltconLINE());
// Set width and height counters
resetXCounter();
resetYCounter();
// Reset registers
aold = 0;
bold = 0;
I'll have a look at the undocumented B-shift feature...
I just reviewed my current code to see how the undocumented B-shift feature can be implemented in vAmiga.
Background: My Blitter is driven by small micro-programs such as this one:
// B: A0 C0 -- A1 C1 D0 A2 C2 D1 | -- D2
{
{
{ // Full execution, no fill
&Blitter::exec <FETCH_A | HOLD_D>,
&Blitter::exec <FETCH_C | HOLD_A | HOLD_B>,
&Blitter::exec <WRITE_D | REPEAT>,
&Blitter::exec <HOLD_D>,
&Blitter::exec <WRITE_D | BLTDONE>,
&Blitter::exec <BLTDONE>
},
In this program, micro-instruction HOLD_B is executed in each iteration. This instruction updates „B hold“ with the output of the Barrel shifter (which is always driven by the current scroll value). I think I need to do the following:
HOLD_B from all micro-programs that have the B channel disabled.HOLD_B does whenever BLTDATB is written.Before I apply the code changes, I’ll try to write a suitable test-case for this Blitter feature…
Finally 😎. The culprit was the "undocumented B-shift feature".

TODOs:
Thanks to all who have helped tracking this down. I would have searched forever I had to do it alone.
Great work! Should try #437 and see if it's fixed as well :)
see if it's fixed as well :)
Just checked. This is something else 🙈:

oh no :)
Code has been cleaned up and the FastBlitter can run the demo, too (v0.9.15).
Most helpful comment
No, both "A old" and "B old" gets cleared when blitter starts. Without it blitter would be quite useless when shift is non-zero: first data that comes out from shifter would also contain some random garbage from last blit.
btw, there is also undocumented B-shift feature: if BLTBDAT is written when blitter is not running (probably same happens when it is running but thats too undefined currently anyway..), value will be shifted (using current shift value) immediately to B HOLD (which is then used as a static data for following blit(s) if B-channel is disabled). After shift B Old equals B New.
A-channel does not do this because A-channel has masking which needs to be applied every word.