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