'Showing' horizontal lines with 'set adjust' VDP register?

By Sandy Brand

Champion (285)

Sandy Brand's picture

18-04-2022, 00:05

Hey everyone,

I am currently trying to figure out a way to make a stable screen-split in Screen 5 using VDP R18 (the 'set adjust' register).

It seems though that changing VDP R18 is problematic in many ways. Not only do we know it causes artifacts while running VDP commands, it also seems that access to this register is quite sensitive in terms of timing and certain combinations of values can cause strange artifacts.

One of these 'Easter Eggs' I found is that sometimes, on real hardware, a full horizontal line appears with a repeating pattern of pixels.
I took a (crappy) screenshot in Screen 5 of this on a real MSX machine.

This artifact is also visible on Screen 6, but not on Screen 7 or 8.

With some experimentation I have deduced the following conditions for this to happen:

  • Screen 5 or 6 (Graphics mode 4 or 5).
  • On line N we set VDP R18 to value U.
  • On line N + 1 we set VDP R18 to value V.
  • If value V makes the lower line (at N + 1) start earlier (= more leftwards) than the upper line (at N), then the lower line (at N + 1) is displayed entirely as a repeating set of pixels comprised of the third byte of the upper line (at N).

This behavior is very puzzling, especially that the third byte of the previous line is repeated is very odd. None of the other VDP registers seems to have an influence on this: the vertical element in VDP R18 has no effect, VDP R23 works as expected and so does setting the visual page. Similarly the display frame-rate has no effect on this, and sprites seem to work as expected on the screen-split lines themselves.

So, because of how pixels are encoded, the horizontal line repeating pixels will display in Screen 5 pixels X = 4 and X = 5, and in Screen 6 pixels X = 8 through X = 11.

:)

Similarly, I was always under the assumption that writing to registers had no speed limits, only when trying to do direct VRAM access do you need to add some delays.
For VDP R18 however, there are definitely some timing limits. From my tests on real hardware it seems that writing too fast to VDP R18 just as the scan beam has reached the right horizontal blank border causes the lower part of the screen to jitter irregularly.

I tried all combinations and this is what it looks like when put in a table:

Just for completeness, here is the meaning of the lower nibble of VDP R18:

    Left    Center   Right
    7 .. 1    0    15 .. 8

Note: 14 T-states is a direct OUT (C),r. The smallest amount of delay I could add there was a NOP, which would already result in 19 T-states of delay. So wherever you see '19' in this table, this should be interpreted as: a delay somewhere in the range of [15 .. 19] T-states.

This table seems to suggest that we need roughly 1 additional T-state delay per pixel that the upper line is more to the right than the lower line.
We can conclude that the worst-case scenario is a screen-split whereby VDP R18 is modified from 7 (=left-most) to 8 (= right-most). This will require 26 T-states of delay to be stable, as is demonstrated in the code snippet below.

      ; Set VDP R15 -> S2 (status register 2).
      ; Set VDP R17 -> R18 (indirect register access).
      ; C = #9B (VDP port 3).
      ; H = 32 (check HR bit).
      ; E = Value to write into VDP R18.

LOOP: IN   A,(#99)
      AND  H            ; Check HR bit.
      JP   Z,LOOP

      INC  IX           ; 12 T-states (dummy delay).

      OUT  (C),E        ; 14 T-states.

This timing problem seems to be there on Screen 5, 6, 7 and 8.

:)

I would like to ask other people to also try this out on real hardware and confirm my findings?
Below I will copy a simple BASIC program that lets you play with all the parameters.
To make testing things easier I also added a couple of presets that should immediately show some relevant results:

  • Preset 1: Screen 5: shows horizontal line glitch, bottom screen part is stable.
  • Preset 2: Screen 5: no horizontal line glitch, bottom screen part is jittering.
  • Preset 3: Screen 5: no horizontal line glitch, bottom screen part is stable.
  • Preset 4: Screen 6: shows horizontal line glitch, bottom screen part is stable.
  • Preset 5: Screen 7: no horizontal line glitch, bottom screen part is stable

Final note: none of these glitches are reproducible on the latest version of openMSX (version 17.0).

Sorry for the long post :)

10 REM Screen-split VDP R18 interactive editor. V1.0 By Sandy Brand (2022)
20 BG=4:COLOR15,BG,0:SCREEN 5,2:COLOR=RESTORE:OPEN "GRP:" FOR OUTPUT AS #1
30 RESTORE 5000
40 READ SZ
50 FOR I=1 TO SZ:READ D:POKE &HA400+I-1,D:NEXT I
100 X1=8:T1=24:B1=64:REM Position of line 1 (vertical).
110 X2=16:T2=34:B2=54:REM Position of line 2 (vertical).
120 Y3=54:T3=34:B3=64:REM Position of line 3 (horizontal).
130 PT$="":FOR I=1 TO 64:PT$=PT$+CHR$(255):NEXT I:REM Sprite pattern
140 DL=PEEK(&HA400)
190 RF=1:A$="1":GOTO300
200 REM Main loop
210 IF RF>0 THEN GOSUB4300
220 A$=INKEY$:IFA$=""THEN400
300 IF A$="X" THEN XD=1:GOSUB1000:GOTO600 ELSE IF A$="x" THEN XD=-1:GOSUB1000:GOTO600
310 IF A$="U" THEN XD=1:GOSUB1100:GOTO600 ELSE IF A$="u" THEN XD=-1:GOSUB1100:GOTO600
320 IF A$="Y" THEN YD=1:GOSUB1200:GOTO600 ELSE IF A$="y" THEN YD=-1:GOSUB1200:GOTO600
330 IF A$="D" THEN DD=1:GOSUB1400:GOTO600 ELSE IF A$="d" THEN DD=-1:GOSUB1400:GOTO600
340 IF A$="T" THEN XD=1:GOSUB1600:GOTO600 ELSE IF A$="t" THEN XD=-1:GOSUB1600:GOTO600
350 IF A$="B" THEN XD=1:GOSUB1900:GOTO600 ELSE IF A$="b" THEN XD=-1:GOSUB1900:GOTO600
360 IF A$="V" THEN YD=1:GOSUB2100:GOTO600 ELSE IF A$="v" THEN YD=-1:GOSUB2100:GOTO600
370 IF A$="S" THEN DD=1:GOSUB2300:GOTO600 ELSE IF A$="s" THEN DD=-1:GOSUB2300:GOTO600
380 IF A$="P" THEN DD=1:GOSUB3700:GOTO600 ELSE IF A$="p" THEN DD=-1:GOSUB3700:GOTO600
390 IF A$="M" THEN DD=1:GOSUB2400:GOTO600 ELSE IF A$="m" THEN DD=-1:GOSUB2400:GOTO600
400 IF A$="I" THEN DD=1:GOSUB2600:GOTO600 ELSE IF A$="i" THEN DD=-1:GOSUB2600:GOTO600
410 IF A$="R" THEN DD=1:GOSUB4100:GOTO600 ELSE IF A$="r" THEN DD=-1:GOSUB4100:GOTO600
420 IF A$="H" OR A$="h" THEN GOSUB3300:GOTO600
430 IF A$="1" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
440 IF A$="2" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=47:DL=14:TA=7:BA=8:GOSUB3000:GOTO600
450 IF A$="3" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=60:DL=27:TA=7:BA=8:GOSUB3000:GOTO600
460 IF A$="4" THEN GOSUB4200:SC=6:X1=8:X2=11:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
470 IF A$="5" THEN GOSUB4200:SC=7:X1=4:X2=5:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
480 IF A$="q"ORA$="Q" THEN GOSUB3200:END
600 GOTO200
1000 REM Adjust vertical line 1.
1010 REM XD = X delta (can also be negative).
1020 LINE(X1,T1)-(X1,B1),BG:LINE(X1+4,T1)-(X1+50,T1+8),BG,BF:X1=(X1+XD)AND255:GOTO1300
1100 REM Adjust vertical line 2.
1110 REM XD = X delta (can also be negative).
1120 LINE(X2,T2)-(X2,B2),BG:LINE(X2+4,T2)-(X2+50,T2+8),BG,BF:X2=(X2+XD)AND255:GOTO1300
1200 REM Adjust horizontal line 3.
1210 REM YD = Y delta (can also be negative).
1220 LINE(0,Y3)-(80,Y3),BG:LINE(60,Y3+4)-(120,Y3+12),BG,BF:Y3=Y3+YD:IF Y3B3 THEN Y3=T3
1230 GOTO1300
1300 REM Redraw lines (without cleaning).
1310 LINE(0,Y3)-(16,Y3),C3:PSET(60,Y3+4):PRINT#1,"Y/y"+STR$(Y3)
1320 LINE(X2,T2)-(X2,B2),C2:PSET(X2+4,T2):PRINT#1,"U/u"+STR$(X2)
1330 LINE(X1,T1)-(X1,B1),C1:PSET(X1+4,T1):PRINT#1,"X/x"+STR$(X1):RETURN
1400 REM Adjust timing delay between reaching right border and writing VDP R18.
1410 REM DL = Delay
1420 REM DD = Delay delta
1430 DL=DL+DD:IF DL<14 THEN DL=28 ELSE IF DL>28 THEN DL=14 ELSE IF DL>14 AND DL<19 THEN GOTO1430
1440 POKE&HA400,DL
1500 REM Redraw timing delay.
1500 PSET(0,140):PRINT#1,"D/d timing Delay"+STR$(DL)+" T-States":RETURN
1600 REM Change top horizontal adjust.
1610 REM XD = X delta (can also be negative).
1620 D=PEEK(&HA401):DD=XD:GOSUB1700:POKE&HA401,D:GOTO1800
1700 REM Compute adjusted VDP R18 nibble value.
1710 REM D = Old/new VDP R18 nibble value (in the range of [0 .. 15]).
1720 REM DD = Adjust value (can also be negative).
1730 D=D+DD:IF D<0 THEN D=15 ELSE IF D>15 THEN D=0
1740 RETURN
1800 REM Redraw top horizontal adjust
1810 D=PEEK(&HA401):PSET(0,100):PRINT#1,"T/t Top horizontal adjust"+STR$(D)+" ":RETURN
1900 REM Change bottom horizontal adjust.
1910 REM XD = X delta (can also be negative).
1920 D=PEEK(&HA402):DD=XD:GOSUB1700:POKE&HA402,D:GOTO2000
2000 REM Redraw bottom horizontal adjust
2010 D=PEEK(&HA402):PSET(0,110):PRINT#1,"B/b Bottom horizontal adjust"+STR$(D)+" ":RETURN
2100 REM Change vertical adjust
2110 REM YD = Y delta (can also be negative).
2120 D=PEEK(&HA403):DD=YD:GOSUB1700:POKE&HA403,D:GOTO2200
2200 REM Redraw vertical adjust
2210 D=PEEK(&HA403):PSET(0,120):PRINT#1,"V/v Vertical adjust"+STR$(D)+" ":RETURN
2300 REM Change screen.
2310 REM DD = Delta
2340 SC=SC+DD:IF SC<5 THEN SC=8 ELSE IF SC>8 THEN SC=5
2350 GOTO2800
2400 REM Change visualization mode.
2410 REM DD = Delta
2420 D=PEEK(&HA404)+DD:IF D<0 THEN D=1 ELSE IF D>1 THEN D=0
2430 POKE&HA404,D:GOTO2500
2500 REM Redraw visualization mode.
2510 LINE(0,190)-(211,197),BG,BF:D=PEEK(&HA404):PSET(0,190):A$="M/m Mode: ":IF D=0 THEN A$=A$+"Write VDP R18" ELSE A$=A$+"Show timing"
2520 PRINT#1,A$:RETURN
2600 REM Change sprite mode.
2610 REM DD = Delta
2620 SM=SM+DD:IF SM<0 THEN SM=2 ELSE IF SM>2 THEN SM=0
2630 GOSUB2700:RF=1:RETURN
2700 REM Apply sprite mode.
2710 REM SM = Sprite mode: 0 = disabled, 1 = normal, 2 = EC -32 pixel shift.
2720 IF SM=0 THEN VDP(9)=VDP(9)OR2 ELSE VDP(9)=VDP(9)AND253
2730 IF SM=2 THEN D=128 ELSE D=0
2740 SC$="":FOR I=0 TO 15:SC$=SC$+CHR$(I OR 8 OR D):NEXT I:COLOR SPRITE$(0)=SC$:RETURN
2800 REM Set screen and redraw all
2810 REM SC = Screen index [5 .. 8].
2820 IF SC=8 THEN C1=224:C2=28:C3=252 ELSE IF SC=6 THEN C1=1:C2=2:C3=3 ELSE C1=2:C2=8:C3=10
2830 GOSUB3900:GOSUB3200:SCREEN SC,2:AT=BASE((SC-5)*5 + 28):POKE &HA405,AT-(INT(AT/256) * 256):POKE &HA406,INT(AT/256):RF=1:GOSUB3100:RETURN
3000 REM Apply current preset and redraw all.
3010 REM X1 = X position line 1.
3020 REM X2 = X position line 2.
3030 REM Y3 = Y position line 3.
3040 REM SC = Screen, SM = Sprite mode, DL = Delay,PI = Page index, VV = VDP(24)
3050 REM TA = Top horizontal adjust, BA = Bottom horizontal adjust, VA = Vertical adjust, VM = Visualization mode.
3080 POKE&HA400,DL:POKE&HA401,TA:POKE&HA402,BA:POKE&HA403,VA:POKE&HA404,VM:VDP(24)=VV
3090 GOSUB2800:GOSUB2700:GOSUB3500:GOSUB3400:GOTO2800
3100 REM Enable split screen.
3110 DEF USR0=&HA40B:A=USR0(0):RETURN
3200 REM Disable split screen.
3210 DEF USR0=&HA40D:A=USR0(0):RETURN
3300 REM Toggle refresh rate.
3310 IF HZ=50 THEN HZ=60 ELSE HZ=50
3320 GOSUB3400:RF=1:RETURN
3400 REM Apply refresh rate.
3410 REM HZ = 50 OR 60.
3420 IF HZ=50 THEN VDP(10)=VDP(10)OR2 ELSE VDP(10)=VDP(10)AND253
3430 RETURN
3500 REM Set page index (without automatic redraw).
3510 REM PI = Page index
3520 GOSUB3900
3530 FOR I=0 TO A:SET PAGE I,I:CLS:NEXT I:SET PAGE PI,PI:GOSUB3600:RETURN
3600 REM Setup sprites.
3610 SPRITE$(0)=PT$:PUT SPRITE 0,(64,40),15,0:COLOR SPRITE$(0)=SC$:IF SC=5 OR SC=6 THEN A=PI*&H080 ELSE A=PI*&H100
3620 POKE&HA408,A-(INT(A/256)*256):POKE&HA409,INT(A/256):RETURN
3700 REM Adjust page index and redraw.
3710 REM DD = Delta
3720 GOSUB3800:PI=PI+DD:IF PI<0 THEN PI=A ELSE IF PI>A THEN PI=0
3730 GOSUB3500:RF=1:RETURN
3800 REM Get maximum page index for current screen.
3810 REM SC = Screen index.
3820 REM Out: A = max screen index.
3830 A=1:IF SC=5 OR SC=6 THEN A=3
3840 RETURN
3900 REM Clamp page index.
3910 REM PI = Page index.
3920 GOSUB3800:IF PI>A THEN PI=A
3930 RETURN
4000 REM Redraw VDP R23.
4010 LINE(0,170)-(211,177),BG,BF:PSET(0,170):PRINT#1,"R/r VDP R23 = "+STR$(VDP(24))+"  ":RETURN
4100 REM Adjust VDP R23 (and redraw).
4110 REM DD = Delta
4120 D=VDP(24)+DD:IF D<0 THEN D=255 ELSE IF D>255 THEN D=0
4130 VDP(24)=D:GOTO4000
4200 REM Reset preset.
4210 SC=5:PI=0:SM=0:HZ=50:X1=0:X2=1:Y3=B3:DL=14:TA=0:BA=0:VA=0:VM=0:VV=0:RETURN
4300 REM Redraw all
4310 RF=0:SET PAGE PI,PI:CLS:GOSUB3600:GOSUB3500
4320 GOSUB1300:GOSUB1500:GOSUB1800:GOSUB2000:GOSUB2200:GOSUB2500
4330 PSET(0,150):PRINT#1,"S/s Screen"+STR$(SC)
4340 PSET(0,160):PRINT#1,"P/p Page"+STR$(PI):IF SM=0 THEN A$="disabled" ELSE IF SM=1 THEN A$="normal" ELSE A$="(EC) Early Clock"
4350 PSET(0,130):PRINT#1,"I/i sprItes "+A$:IF HZ=50 THEN A$="50 (PAL)" ELSE A$="60 (NTSC)"
4360 PSET(0,180):PRINT#1,"H/h Hz: "+A$
4370 PSET(0,200):PRINT#1,"Q/q Quit":PSET(0,76):PRINT#1,"Uppercase = +1 , lowercase = -1":PSET(0,90):PRINT#1,"1/2/3/4/5 presets"
4380 GOSUB3100:GOSUB4000:GOSUB4000:RETURN
5000 REM Assembly code
5010 DATA 539
5020 DATA 14,0,0,0,0,0,0,0
5030 DATA 0,0,48,195,45,164,33,26
5040 DATA 166,126,167,200,54,0,243,58
5050 DATA 241,255,211,153,62,146,211,153
5060 DATA 33,57,165,17,159,253,1,5
5070 DATA 0,237,176,251,201,33,26,166
5080 DATA 126,167,192,54,255,243,33,159
5090 DATA 253,17,57,165,1,5,0,237
5100 DATA 176,33,78,164,17,159,253,1
5110 DATA 3,0,237,176,251,201,195,81
5120 DATA 164,62,2,211,153,62,143,211
5130 DATA 153,0,219,153,15,218,49,165
5140 DATA 205,186,165,58,0,164,33,62
5150 DATA 165,254,14,40,66,33,67,165
5160 DATA 254,19,40,59,33,72,165,254
5170 DATA 20,40,52,33,77,165,254,21
5180 DATA 40,45,33,82,165,254,22,40
5190 DATA 38,33,87,165,254,23,40,31
5200 DATA 33,92,165,254,24,40,24,33
5210 DATA 97,165,254,25,40,17,33,102
5220 DATA 165,254,26,40,10,33,107,165
5230 DATA 254,27,40,3,33,112,165,17
5240 DATA 16,165,1,5,0,237,176,58
5250 DATA 3,164,135,135,135,135,95,58
5260 DATA 1,164,179,211,153,62,146,211
5270 DATA 153,58,4,164,167,32,16,62
5280 DATA 18,211,153,62,145,211,153,58
5290 DATA 2,164,179,14,155,24,11,175
5300 DATA 211,153,62,144,211,153,62,35
5310 DATA 14,154,103,46,32,229,6,1
5320 DATA 197,46,64,205,124,165,58,10
5330 DATA 164,167,40,12,61,71,46,32
5340 DATA 205,118,165,205,124,165,16,248
5350 DATA 193,225,219,153,165,202,10,165
5360 DATA 0,0,0,0,0,58,4,164
5370 DATA 167,40,22,62,0,211,154,46
5380 DATA 32,205,118,165,175,211,153,62
5390 DATA 144,211,153,175,211,154,0,211
5400 DATA 154,0,175,211,153,62,143,211
5410 DATA 153,0,0,0,0,0,237,97
5420 DATA 0,0,0,0,237,97,0,0
5430 DATA 200,237,97,0,0,19,237,97
5440 DATA 0,0,62,255,237,97,0,16
5450 DATA 0,237,97,0,203,71,237,97
5460 DATA 0,17,0,0,237,97,221,35
5470 DATA 237,97,0,24,0,237,97,0
5480 DATA 50,117,165,237,97,0,219,153
5490 DATA 165,40,251,201,219,153,165,32
5500 DATA 251,201,167,24,1,55,213,22
5510 DATA 0,203,26,203,58,135,135,95
5520 DATA 124,7,7,230,3,179,211,153
5530 DATA 62,142,211,153,125,211,153,124
5540 DATA 230,63,178,211,153,209,201,205
5550 DATA 130,165,235,14,152,237,178,201
5560 DATA 205,133,165,235,14,152,237,179
5570 DATA 201,0,175,42,5,164,237,91
5580 DATA 7,164,25,58,9,164,206,0
5590 DATA 245,229,17,27,166,6,4,205
5600 DATA 167,165,58,185,165,230,1,79
5610 DATA 33,27,166,6,1,126,203,65
5620 DATA 32,14,214,1,56,4,254,16
5630 DATA 48,20,62,16,203,249,24,14
5640 DATA 198,1,56,6,254,64,40,2
5650 DATA 56,4,62,64,203,249,119,35
5660 DATA 52,52,35,35,16,215,203,121
5670 DATA 40,6,33,185,165,126,47,119
5680 DATA 225,241,17,27,166,6,4,195
5690 DATA 176,165,0
Login or register to post comments

By Sandy Brand

Champion (285)

Sandy Brand's picture

18-04-2022, 00:19

Oops, forgot to mention I was able to reproduce these glitches on the following machines:

  • Philips NMS 8245 (original, so it has a V9938).
  • Philips NMS 8255 (converted to MSX 2+, so it has a V9958).

By ARTRAG

Enlighted (6865)

ARTRAG's picture

18-04-2022, 11:00

Interesting...
I'm focused on screen 8 ATM so I didn't notice the problem

By sd_snatcher

Prophet (3557)

sd_snatcher's picture

18-04-2022, 22:22

@Sandy Brand

Doesn't the tidy screen split with a blank line method solve this issue?

By Sandy Brand

Champion (285)

Sandy Brand's picture

18-04-2022, 23:37

@sd_snatcher
Yes, for splitting the screen with a playfield and HUD that will work Smile

But, I am trying to see if it is possible to do proper 'mid-screen' screen-splits whereby both parts of the playfield can move independently.

From these findings this seems only possible by having 2 black lines in between them (1 line to make sure the 3rd 3 is 'black' and then the next line where the first line will be repeated due to the glitch).

Or, theoretically we could avoid the glitch by making sure the upper part of the screen to stays more leftwards than the lower part. This could work if the upper part scrolls at 16 pixels while the lower part only scrolls at 8 pixels, for example.

By Grauw

Ascended (10623)

Grauw's picture

18-04-2022, 23:49

I’ve also observed the VDP line counter getting confused by r#18 changes in this test.

However, my running theory is that this has to do with at which moment in the line the register is changed. Not with how quickly the register is changed after the previous register change (or the next). The delays you mentioned, I expect if you add more delay the effect will return.

By Sandy Brand

Champion (285)

Sandy Brand's picture

19-04-2022, 12:11

@Grauw
The test you mention is in combination with VDP commands, right?

My new test has no VDP commands running (well, some of them are generated by the menu that is run in BASIC, but the screen-split is skipped while VDP commands are running, so these should have no effect).
Also, in my test I am not writing to any registers, the code is just reading the status register to wait for the HR bit to become raised, only then writes to register R18.

I tried to visualize the timing of when writing into the register by changing the palette color instead, but it is rather inconclusive.

If it would be helpful, I can add more delays and see if there is an upper bound at which the 'jitter' effect is there again?

By Grauw

Ascended (10623)

Grauw's picture

19-04-2022, 15:06

Yeah the test I linked to was about testing the VDP commands corruption, but you can also observe the vertical height changing there, which is indeed unrelated to command execution.

Finding the precise moment will be difficult, but at least it should be possible to confirm whether it is a matter of timing respective to the horizontal scan, or related to register access speed.

By Sandy Brand

Champion (285)

Sandy Brand's picture

23-04-2022, 16:02

I have made some improvements to the testing program. It is now possible to introduce up to 388 T-states of delay before writing to register R18 (which would basically make it 'skip' an entire screen line). I have also made improvements in the timing visualization.

10 REM Screen-split VDP R18 interactive editor. V1.02 By Sandy Brand (2022)
20 BG=4:COLOR15,BG,0:SCREEN 5,2:COLOR=RESTORE:OPEN "GRP:" FOR OUTPUT AS #1
30 RESTORE 5000
40 READ SZ
50 FOR I=1 TO SZ:READ D:POKE &HA800+I-1,D:NEXT I
100 X1=8:T1=24:B1=64:REM Position of line 1 (vertical).
110 X2=16:T2=34:B2=54:REM Position of line 2 (vertical).
120 Y3=54:T3=34:B3=64:REM Position of line 3 (horizontal).
130 PT$="":FOR I=1 TO 64:PT$=PT$+CHR$(255):NEXT I:REM Sprite pattern
140 DL=PEEK(&HA80A)+(PEEK(&HA80B)*256)
190 RF=1:A$="1":GOTO300
200 REM Main loop
210 IF RF>0 THEN GOSUB4300
220 A$=INKEY$:IFA$=""THEN600
300 IF A$="X" THEN XD=1:GOSUB1000:GOTO600 ELSE IF A$="x" THEN XD=-1:GOSUB1000:GOTO600
310 IF A$="U" THEN XD=1:GOSUB1100:GOTO600 ELSE IF A$="u" THEN XD=-1:GOSUB1100:GOTO600
320 IF A$="Y" THEN YD=1:GOSUB1200:GOTO600 ELSE IF A$="y" THEN YD=-1:GOSUB1200:GOTO600
330 IF A$="D" THEN DD=1:GOSUB1400:GOTO600 ELSE IF A$="d" THEN DD=-1:GOSUB1400:GOTO600
340 IF A$="T" THEN XD=1:GOSUB1600:GOTO600 ELSE IF A$="t" THEN XD=-1:GOSUB1600:GOTO600
350 IF A$="B" THEN XD=1:GOSUB1900:GOTO600 ELSE IF A$="b" THEN XD=-1:GOSUB1900:GOTO600
360 IF A$="V" THEN YD=1:GOSUB2100:GOTO600 ELSE IF A$="v" THEN YD=-1:GOSUB2100:GOTO600
370 IF A$="S" THEN DD=1:GOSUB2300:GOTO600 ELSE IF A$="s" THEN DD=-1:GOSUB2300:GOTO600
380 IF A$="P" THEN DD=1:GOSUB3700:GOTO600 ELSE IF A$="p" THEN DD=-1:GOSUB3700:GOTO600
390 IF A$="M" THEN DD=1:GOSUB2400:GOTO600 ELSE IF A$="m" THEN DD=-1:GOSUB2400:GOTO600
400 IF A$="I" THEN DD=1:GOSUB2600:GOTO600 ELSE IF A$="i" THEN DD=-1:GOSUB2600:GOTO600
410 IF A$="R" THEN DD=1:GOSUB4100:GOTO600 ELSE IF A$="r" THEN DD=-1:GOSUB4100:GOTO600
420 IF A$="H" OR A$="h" THEN GOSUB3300:GOTO600
430 IF A$="1" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
440 IF A$="2" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=47:DL=14:TA=7:BA=8:GOSUB3000:GOTO600
450 IF A$="3" THEN GOSUB4200:SC=5:X1=4:X2=5:Y3=60:DL=27:TA=7:BA=8:GOSUB3000:GOTO600
460 IF A$="4" THEN GOSUB4200:SC=6:X1=8:X2=11:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
470 IF A$="5" THEN GOSUB4200:SC=7:X1=4:X2=5:Y3=47:DL=27:TA=8:BA=7:GOSUB3000:GOTO600
480 IF A$="Z"orA$="z" THEN DL=14:GOSUB1440:GOTO600
490 IF A$="q"ORA$="Q" THEN GOSUB3200:END
600 GOTO200
1000 REM Adjust vertical line 1.
1010 REM XD = X delta (can also be negative).
1020 LINE(X1,T1)-(X1,B1),BG:LINE(X1+4,T1)-(X1+50,T1+8),BG,BF:X1=(X1+XD)AND255:GOTO1300
1100 REM Adjust vertical line 2.
1110 REM XD = X delta (can also be negative).
1120 LINE(X2,T2)-(X2,B2),BG:LINE(X2+4,T2)-(X2+50,T2+8),BG,BF:X2=(X2+XD)AND255:GOTO1300
1200 REM Adjust horizontal line 3.
1210 REM YD = Y delta (can also be negative).
1220 LINE(0,Y3)-(80,Y3),BG:LINE(60,Y3+4)-(120,Y3+12),BG,BF:Y3=Y3+YD:IF Y3B3 THEN Y3=T3
1230 GOTO1300
1300 REM Redraw lines (without cleaning).
1310 LINE(0,Y3)-(16,Y3),C3:PSET(60,Y3+4):PRINT#1,"Y/y"+STR$(Y3)
1320 LINE(X2,T2)-(X2,B2),C2:PSET(X2+4,T2):PRINT#1,"U/u"+STR$(X2)
1330 LINE(X1,T1)-(X1,B1),C1:PSET(X1+4,T1):PRINT#1,"X/x"+STR$(X1):RETURN
1400 REM Adjust timing delay between reaching right border and writing VDP R18.
1410 REM DL = Delay
1420 REM DD = Delay delta
1430 DL=DL+DD
1440 A=PEEK(&HA80C)+(PEEK(&HA80D)*256):IF DL<14 THEN DL=A ELSE IF DL>A THEN DL=14 ELSE IF DL>14 AND DL<19 THEN GOTO1430
1450 GOSUB4400
1500 REM Redraw timing delay.
1500 PSET(0,140):PRINT#1,"D/d timing Delay"+STR$(DL)+" T-States ":RETURN
1600 REM Change top horizontal adjust.
1610 REM XD = X delta (can also be negative).
1620 D=PEEK(&HA801):DD=XD:GOSUB1700:POKE&HA801,D:GOTO1800
1700 REM Compute adjusted VDP R18 nibble value.
1710 REM D = Old/new VDP R18 nibble value (in the range of [0 .. 15]).
1720 REM DD = Adjust value (can also be negative).
1730 D=D+DD:IF D<0 THEN D=15 ELSE IF D>15 THEN D=0
1740 RETURN
1800 REM Redraw top horizontal adjust
1810 D=PEEK(&HA801):PSET(0,100):PRINT#1,"T/t Top horizontal adjust"+STR$(D)+" ":RETURN
1900 REM Change bottom horizontal adjust.
1910 REM XD = X delta (can also be negative).
1920 D=PEEK(&HA802):DD=XD:GOSUB1700:POKE&HA802,D:GOTO2000
2000 REM Redraw bottom horizontal adjust
2010 D=PEEK(&HA802):PSET(0,110):PRINT#1,"B/b Bottom horizontal adjust"+STR$(D)+" ":RETURN
2100 REM Change vertical adjust
2110 REM YD = Y delta (can also be negative).
2120 D=PEEK(&HA803):DD=YD:GOSUB1700:POKE&HA803,D:GOTO2200
2200 REM Redraw vertical adjust
2210 D=PEEK(&HA803):PSET(0,120):PRINT#1,"V/v Vertical adjust"+STR$(D)+" ":RETURN
2300 REM Change screen.
2310 REM DD = Delta
2340 SC=SC+DD:IF SC<5 THEN SC=8 ELSE IF SC>8 THEN SC=5
2350 GOTO2800
2400 REM Change visualization mode.
2410 REM DD = Delta
2420 D=PEEK(&HA804)+DD:IF D<0 THEN D=1 ELSE IF D>1 THEN D=0
2430 IF D=0 THEN BG=4 ELSE BG=0
2440 POKE&HA804,D:RF=1:RETURN
2600 REM Change sprite mode.
2610 REM DD = Delta
2620 SM=SM+DD:IF SM<0 THEN SM=2 ELSE IF SM>2 THEN SM=0
2630 GOSUB2700:RF=1:RETURN
2700 REM Apply sprite mode.
2710 REM SM = Sprite mode: 0 = disabled, 1 = normal, 2 = EC -32 pixel shift.
2720 IF SM=0 THEN VDP(9)=VDP(9)OR2 ELSE VDP(9)=VDP(9)AND253
2730 IF SM=2 THEN D=128 ELSE D=0
2740 SC$="":FOR I=0 TO 15:SC$=SC$+CHR$(I OR 8 OR D):NEXT I:COLOR SPRITE$(0)=SC$:RETURN
2800 REM Set screen and redraw all
2810 REM SC = Screen index [5 .. 8].
2820 IF SC=8 THEN C1=224:C2=28:C3=252 ELSE IF SC=6 THEN C1=1:C2=2:C3=3 ELSE C1=2:C2=8:C3=10
2830 GOSUB3900:GOSUB3200:SCREEN SC,2:AT=BASE((SC-5)*5 + 28):POKE &HA805,AT-(INT(AT/256) * 256):POKE &HA806,INT(AT/256):RF=1:GOSUB3100:RETURN
3000 REM Apply current preset and redraw all.
3010 REM X1 = X position line 1.
3020 REM X2 = X position line 2.
3030 REM Y3 = Y position line 3.
3040 REM SC = Screen, SM = Sprite mode, DL = Delay,PI = Page index, VV = VDP(24)
3050 REM TA = Top horizontal adjust, BA = Bottom horizontal adjust, VA = Vertical adjust, VM = Visualization mode.
3080 GOSUB4400:POKE&HA801,TA:POKE&HA802,BA:POKE&HA803,VA:POKE&HA804,VM:VDP(24)=VV
3090 GOSUB2800:GOSUB2700:GOSUB3500:GOSUB3400:GOTO2800
3100 REM Enable split screen.
3110 DEF USR0=&HA80E:A=USR0(0):RETURN
3200 REM Disable split screen.
3210 DEF USR0=&HA811:A=USR0(0):RETURN
3300 REM Toggle refresh rate.
3310 IF HZ=50 THEN HZ=60 ELSE HZ=50
3320 GOSUB3400:RF=1:RETURN
3400 REM Apply refresh rate.
3410 REM HZ = 50 OR 60.
3420 IF HZ=50 THEN VDP(10)=VDP(10)OR2 ELSE VDP(10)=VDP(10)AND253
3430 RETURN
3500 REM Set page index (without automatic redraw).
3510 REM PI = Page index
3520 GOSUB3900
3530 FOR I=0 TO A:SET PAGE I,I:GOSUB4500:NEXT I:SET PAGE PI,PI:GOSUB3600:RETURN
3600 REM Setup sprites.
3610 SPRITE$(0)=PT$:PUT SPRITE 0,(64,40),15,0:COLOR SPRITE$(0)=SC$:IF SC=5 OR SC=6 THEN A=PI*&H080 ELSE A=PI*&H100
3620 POKE&HA808,A-(INT(A/256)*256):POKE&HA809,INT(A/256):RETURN
3700 REM Adjust page index and redraw.
3710 REM DD = Delta
3720 GOSUB3800:PI=PI+DD:IF PI<0 THEN PI=A ELSE IF PI>A THEN PI=0
3730 GOSUB3500:RF=1:RETURN
3800 REM Get maximum page index for current screen.
3810 REM SC = Screen index.
3820 REM Out: A = max screen index.
3830 A=1:IF SC=5 OR SC=6 THEN A=3
3840 RETURN
3900 REM Clamp page index.
3910 REM PI = Page index.
3920 GOSUB3800:IF PI>A THEN PI=A
3930 RETURN
4000 REM Redraw VDP R23.
4010 LINE(0,170)-(211,177),BG,BF:PSET(0,170):PRINT#1,"R/r VDP R23 = "+STR$(VDP(24))+"  ":RETURN
4100 REM Adjust VDP R23 (and redraw).
4110 REM DD = Delta
4120 D=VDP(24)+DD:IF D<0 THEN D=255 ELSE IF D>255 THEN D=0
4130 VDP(24)=D:GOTO4000
4200 REM Reset preset.
4210 SC=5:PI=0:SM=0:HZ=50:X1=0:X2=1:Y3=B3:DL=14:TA=0:BA=0:VA=0:VM=0:VV=0:RETURN
4300 REM Redraw all
4310 COLOR15,BG,0:RF=0:SET PAGE PI,PI:GOSUB4500
4320 GOSUB3600:GOSUB3500:GOSUB1300:GOSUB1500:GOSUB1800:GOSUB2000:GOSUB2200
4330 PSET(0,150):PRINT#1,"S/s Screen"+STR$(SC)
4340 PSET(0,160):PRINT#1,"P/p Page"+STR$(PI):IF SM=0 THEN A$="disabled" ELSE IF SM=1 THEN A$="normal" ELSE A$="(EC) Early Clock"
4350 PSET(0,130):PRINT#1,"I/i sprItes "+A$:IF HZ=50 THEN A$="50 (PAL)" ELSE A$="60 (NTSC)"
4360 PSET(0,180):PRINT#1,"H/h Hz: "+A$:D=PEEK(&HA804):A$="M/m Mode: ":IF D=0 THEN A$=A$+"Write VDP R18" ELSE A$=A$+"Show timing"
4370 PSET(0,190):PRINT#1,A$
4380 PSET(0,200):PRINT#1,"Q/q Quit":PSET(0,76):PRINT#1,"Uppercase = +1 , lowercase = -1":PSET(0,90):PRINT#1,"1/2/3/4/5 presets"
4390 GOSUB3100:GOSUB4000:GOSUB4000:RETURN
4400 POKE&HA80A,DL AND 255:POKE&HA80B,DL/256:RETURN
4500 CLS:IF PEEK(&HA804)>0 THEN LINE(0,0)-(0,211),4:LINE(255,0)-(255,211),4
4510 RETURN
5000 REM Assembly code
5010 DATA 658
5020 DATA 48,0,0,0,0,0,0,0
5030 DATA 0,0,14,0,132,1,195,48
5040 DATA 168,33,145,170,126,167,200,54
5050 DATA 0,243,58,241,255,211,153,62
5060 DATA 146,211,153,33,197,169,17,159
5070 DATA 253,1,5,0,237,176,251,201
5080 DATA 33,145,170,126,167,192,54,255
5090 DATA 243,33,159,253,17,197,169,1
5100 DATA 5,0,237,176,33,81,168,17
5110 DATA 159,253,1,3,0,237,176,251
5120 DATA 201,195,84,168,62,2,211,153
5130 DATA 62,143,211,153,0,219,153,15
5140 DATA 218,189,169,205,49,170,33,126
5150 DATA 169,54,0,17,127,169,1,62
5160 DATA 0,237,176,6,45,58,10,168
5170 DATA 42,10,168,124,167,32,100,125
5180 DATA 17,217,169,14,0,254,14,40
5190 DATA 101,17,217,169,14,1,254,19
5200 DATA 40,92,17,218,169,14,1,254
5210 DATA 20,40,83,17,219,169,14,1
5220 DATA 254,21,40,74,17,220,169,14
5230 DATA 2,254,22,40,65,17,222,169
5240 DATA 14,2,254,23,40,56,17,224
5250 DATA 169,14,2,254,24,40,47,17
5260 DATA 226,169,14,3,254,25,40,38
5270 DATA 17,229,169,14,2,254,26,40
5280 DATA 29,17,231,169,14,2,254,27
5290 DATA 40,20,17,233,169,14,3,254
5300 DATA 28,40,11,17,248,255,25,16
5310 DATA 146,17,217,169,14,0,33,126
5320 DATA 169,62,45,144,40,6,71,54
5330 DATA 126,35,16,251,235,121,167,40
5340 DATA 4,6,0,237,176,33,202,169
5350 DATA 1,2,0,237,176,58,4,168
5360 DATA 167,40,8,33,204,169,1,13
5370 DATA 0,237,176,58,3,168,135,135
5380 DATA 135,135,95,58,1,168,179,211
5390 DATA 153,62,146,211,153,58,4,168
5400 DATA 167,32,16,62,18,211,153,62
5410 DATA 145,211,153,58,2,168,179,14
5420 DATA 155,24,11,175,211,153,62,144
5430 DATA 211,153,62,0,14,154,103,46
5440 DATA 32,229,6,1,197,46,64,205
5450 DATA 243,169,58,0,168,167,40,12
5460 DATA 61,71,46,32,205,237,169,205
5470 DATA 243,169,16,248,58,4,168,167
5480 DATA 40,4,62,35,211,154,193,225
5490 DATA 219,153,165,202,120,169,0,0
5500 DATA 0,0,0,0,0,0,0,0
5510 DATA 0,0,0,0,0,0,0,0
5520 DATA 0,0,0,0,0,0,0,0
5530 DATA 0,0,0,0,0,0,0,0
5540 DATA 0,0,0,0,0,0,0,0
5550 DATA 0,0,0,0,0,0,0,0
5560 DATA 0,0,0,0,0,0,0,0
5570 DATA 0,0,0,0,0,0,175,211
5580 DATA 153,62,143,211,153,0,0,0
5590 DATA 0,0,237,97,175,211,153,62
5600 DATA 144,211,153,175,211,154,0,211
5610 DATA 154,0,200,19,62,255,16,0
5620 DATA 203,71,17,0,0,221,35,24
5630 DATA 0,50,236,169,0,219,153,165
5640 DATA 40,251,201,219,153,165,32,251
5650 DATA 201,167,24,1,55,213,22,0
5660 DATA 203,26,203,58,135,135,95,124
5670 DATA 7,7,230,3,179,211,153,62
5680 DATA 142,211,153,125,211,153,124,230
5690 DATA 63,178,211,153,209,201,205,249
5700 DATA 169,235,14,152,237,178,201,205
5710 DATA 252,169,235,14,152,237,179,201
5720 DATA 0,175,42,5,168,237,91,7
5730 DATA 168,25,58,9,168,206,0,245
5740 DATA 229,17,146,170,6,4,205,30
5750 DATA 170,58,48,170,230,1,79,33
5760 DATA 146,170,6,1,126,203,65,32
5770 DATA 14,214,1,56,4,254,16,48
5780 DATA 20,62,16,203,249,24,14,198
5790 DATA 1,56,6,254,64,40,2,56
5800 DATA 4,62,64,203,249,119,35,52
5810 DATA 52,35,35,16,215,203,121,40
5820 DATA 6,33,48,170,126,47,119,225
5830 DATA 241,17,146,170,6,4,195,39
5840 DATA 170,0

By Sandy Brand

Champion (285)

Sandy Brand's picture

23-04-2022, 16:20

Grauw wrote:

However, my running theory is that this has to do with at which moment in the line the register is changed. Not with how quickly the register is changed after the previous register change (or the next). The delays you mentioned, I expect if you add more delay the effect will return.

Grauw wrote:

Finding the precise moment will be difficult, but at least it should be possible to confirm whether it is a matter of timing respective to the horizontal scan, or related to register access speed.

So, I have done more testing and I have not been able to get the jitter to come back by adding more delays.
It could be, as you said, because the timing of it could be very precise. However, looking at the table I previously posted: for the worst-case scenario (switching VDP R18 from 7 (left-most) to 8 (right-most), any delay between 14 up to 25 T-states will induce the jitter effect. So it is maybe fair to assume that trying to trigger the jitter again with higher delays should not be impossible, if it were there?

I have also improved the timing visualization a bit. From this it would seem that, even with the shortest delay, you cannot write into the VDP R18 register fast enough to get it done in the horizontal blank on the right side of the screen by just timing it on bit HR in status register 2?

From my table, it shows that the highest delay is needed for when VDP R18 is switched from 7 (left-most) to 8 (right-most). And this delay seems to roughly bring writing into VDR R18 just at the boundary of where the horizontal blank on the left side of the screen ends and where normal writing of screen pixels starts.

So my working theory for now, is that the jitters are there whenever you change VDP R18 while the scanning beam is still close to the very leftmost side of horizontal blank on the left?