I've tried on a real Sony HB-F1XDmk2:
...
I didn't observe the left square in any of the tests.
Thanks for trying it on real hardware!
Interesting that your results are slightly different, but also indicate that the frame-rate is a factor here.
Because seeing a cross would mean that the sprite pattern index somehow got corrupted when writing it into VRAM.
The good news is though that we now have some evidence that suggests that 18 T-States is the actual minimum required delay to be safe for worst-case scenarios.
If you change the visible screen content during the refresh there will be always glitches. Maybe is that the issue?
In the test, the sprite attributes are written to VRAM either during the VBlank or shortly after it when the scan-line is still at the top of the screen. The sprites themselves are located at Y = 120, that should be more than enough for not causing any issues by the time the VDP starts drawing them there on the screen.
@Snake: I made a new version of the test that actually reads back VRAM to check if it has been 'corrupted' (something I should have done as part of the first version actually ) and I can confirm that this is indeed the case.
Writing with just 17 T-states delay results in the following VRAM corruptions on real hardware:
NMS 8255 (Upgraded to MSX 2+ so it has a V9958):
- Test 1B: ~10 frames corrupted.
- Test 1D: 1 frame corrupted (quite rare to see this one actually).
- Test 2D: ~25 frames corrupted.
NMS 8245 (V9938):
- Test 1B: ~8 frames corrupted
- Test 2D: ~25 frames corrupted.
Now the puzzling thing is that it always seems to be just 1 byte that is corrupted out of the 8 * 4 = 32 bytes that are written (8 sprite attributes).
This would indicate that the internal VRAM address is correctly incremented, it is just that the byte that is written into VRAM just gets mangled somehow.
Here is the improved test:
10 REM Direct VRAM I/O timing tests V1.01. By Sandy Brand (2022) 20 COLOR15,4,0:SCREEN 5,2:SET PAGE 0,0:COLOR=RESTORE:OPEN "GRP:" FOR OUTPUT AS #1 40 RESTORE 800:P$="":FOR I=1 TO 32:READ D:P$=P$+CHR$(D):NEXT I 50 SPRITE$(0)=P$ 60 RESTORE 900:P$="":FOR I=1 TO 32:READ D:P$=P$+CHR$(D):NEXT I 70 FOR I=1 TO 63:SPRITE$(I)=P$:NEXT I 100 FOR I=0 TO 7:PUT SPRITE I,(120,120),15-I,0:NEXT I 200 RESTORE 2010 210 READ SZ 220 FOR I=1 TO SZ:READ D:POKE &HA000+I-1,D:NEXT I 230 D=BASE(28):POKE &HA000,D AND 255:POKE &HA001,D/256 240 FR=5*60:POKE &HA005,FR AND 255:POKE &HA006,FR/256 250 DEF USR0=&HA00F 300 HZ=50 310 TN$="1A":VB=1:CM=0:DL=17:GOSUB 500 320 TN$="1B":VB=0:CM=0:DL=17:GOSUB 500 330 TN$="1C":VB=1:CM=1:DL=17:GOSUB 500 340 TN$="1D":VB=0:CM=1:DL=17:GOSUB 500 350 TN$="1E":VB=0:CM=1:DL=18:GOSUB 500 360 TN$="1F":VB=0:CM=1:DL=19:GOSUB 500 400 HZ=60 410 TN$="2A":VB=1:CM=0:DL=17:GOSUB 500 420 TN$="2B":VB=0:CM=0:DL=17:GOSUB 500 430 TN$="2C":VB=1:CM=1:DL=17:GOSUB 500 440 TN$="2D":VB=0:CM=1:DL=17:GOSUB 500 450 TN$="2E":VB=0:CM=1:DL=18:GOSUB 500 460 TN$="2F":VB=0:CM=1:DL=19:GOSUB 500 490 COLOR15,0,0:END 500 REM TN%: Test name 510 REM HZ: 50 Hz (PAL) or 60 Hz (NTSC). 520 REM VB: 0 = outside Vblank, 1 = inside VBlank. 530 REM CM: 0 = no VDP command, 1 = run VDP command while writing. 540 REM DL: 17, 18 or 19 (T-States delay). 550 CLS:VDP(9)=VDP(9)AND253 560 PSET(0,30):PRINT #1,"TEST: "+TN$+" (run"+STR$(FR)+" frames)" 570 PSET(0,40):IF HZ=50 THEN VDP(10)=VDP(10)OR2:PRINT #1,"50 Hz" ELSE VDP(10)=VDP(10)AND253:PRINT #1,"60 Hz" 580 POKE &HA002,VB:PSET(0,50):IF VB=0 THEN PRINT #1,"Outside V-blank" ELSE PRINT #1,"Inside V-blank" 590 POKE &HA003,CM:PSET(0,60):IF CM=0 THEN PRINT #1,"No VDP command" ELSE PRINT #1,"Run VDP command while writing" 600 POKE &HA004,DL:PSET(0,70):PRINT #1,"T-States delay:"+STR$(DL) 610 X=0:A$="sprite":GOSUB750 620 X=126:A$="cross":GOSUB750 630 A=USR(0):VDP(9)=VDP(9)OR2:CF=PEEK(&HA007)+PEEK(&HA008)*256:CT=PEEK(&HA009)+PEEK(&HA00A)*256:CL=PEEK(&HA00B)+PEEK(&HA00C)*256:CU=PEEK(&HA00D)+PEEK(&HA00E)*256 640 X=10:Y=100:LINE(0,Y)-(255,211),4,BF:IF CF=0 THEN A$="No VRAM corruptions.":GOSUB700:GOTO 660 ELSE A$="VRAM corruptions detected!":GOSUB700 650 A$="Corrupted frames:"+STR$(CF)+" of "+STR$(FR):GOSUB700:A$="Min corrupted bytes:"+STR$(CL):GOSUB700:A$="Max corrupted bytes:"+STR$(CU):GOSUB700 660 I=TIME:WT=4*50:REM Show results and wait. 670 IF I=TIME THEN GOTO670 ELSE I=TIME:WT=WT-1:IF WT>0 THEN GOTO670 ELSE RETURN 700 REM Print at (X,Y) and do new-line. 710 REM X = Left position. 720 REM Y = Top position. 730 REM A$ = Text. 740 PSET(X,Y):PRINT #1,A$:Y=Y+8:RETURN 750 REM Print hint. 760 PSET(X,150):PRINT #1,"^ See":PSET(X,160):PRINT #1,"| "+A$:PSET(X,170):PRINT #1,"| here?":RETURN 800 REM Sprite pattern = Square 810 DATA 255,128,128,128,128,128,128,128,128,128,128,128,128,128,128,255 820 DATA 255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,255 900 REM Sprite pattern = Cross 910 DATA 128,64,32,16,8,4,2,1,1,2,4,8,16,32,64,128 920 DATA 1,2,4,8,16,32,64,128,128,64,32,16,8,4,2,1 2000 Rem Assembly code 2010 DATA 432 2020 DATA 0,0,0,0,17,1,0,0 2030 DATA 0,0,0,0,0,0,0,243 2040 DATA 33,159,253,17,114,161,1,5 2050 DATA 0,237,176,33,89,160,17,159 2060 DATA 253,1,3,0,237,176,33,0 2070 DATA 0,34,7,160,34,9,160,34 2080 DATA 11,160,34,13,160,251,237,75 2090 DATA 5,160,237,91,158,252,42,158 2100 DATA 252,167,237,82,40,248,11,121 2110 DATA 176,32,239,243,33,114,161,17 2120 DATA 159,253,1,5,0,237,176,251 2130 DATA 201,195,98,160,219,153,165,32 2140 DATA 251,201,14,155,62,2,211,153 2150 DATA 62,143,211,153,58,2,160,167 2160 DATA 32,5,46,64,205,92,160,58 2170 DATA 3,160,167,40,49,33,173,161 2180 DATA 126,60,230,15,71,135,135,135 2190 DATA 135,176,119,33,165,161,62,36 2200 DATA 211,153,62,145,211,153,6,11 2210 DATA 237,179,46,1,205,92,160,33 2220 DATA 150,161,62,32,211,153,62,145 2230 DATA 211,153,6,15,237,179,30,64 2240 DATA 205,126,161,175,211,153,62,144 2250 DATA 211,153,175,211,154,62,3,211 2260 DATA 154,6,8,58,4,160,254,19 2270 DATA 40,43,254,18,40,17,62,120 2280 DATA 211,152,0,211,152,175,211,152 2290 DATA 0,211,152,16,241,24,38,1 2300 DATA 152,32,33,241,160,237,163,237 2310 DATA 163,237,163,237,163,32,243,24 2320 DATA 20,120,120,0,0,62,120,211 2330 DATA 152,35,211,152,62,0,211,152 2340 DATA 43,211,152,16,240,62,216,211 2350 DATA 152,0,175,211,153,62,143,211 2360 DATA 153,175,211,153,62,144,211,153 2370 DATA 175,211,154,175,211,154,30,0 2380 DATA 205,126,161,1,152,32,33,176 2390 DATA 161,237,162,32,252,33,176,161 2400 DATA 1,0,8,17,241,160,205,119 2410 DATA 161,205,119,161,205,119,161,205 2420 DATA 119,161,16,239,175,185,40,42 2430 DATA 71,42,9,160,9,34,9,160 2440 DATA 42,7,160,35,34,7,160,42 2450 DATA 11,160,125,180,40,4,237,66 2460 DATA 56,4,237,67,11,160,42,13 2470 DATA 160,167,237,66,48,4,237,67 2480 DATA 13,160,0,0,0,0,0,26 2490 DATA 190,35,19,200,12,201,42,0 2500 DATA 160,124,15,15,230,3,211,153 2510 DATA 62,142,211,153,125,211,153,124 2520 DATA 230,63,179,211,153,201,2,0 2530 DATA 0,0,0,0,0,0,254,0 2540 DATA 8,0,0,0,208,254,0,0 2550 DATA 0,2,0,8,0,0,0,192
Has anyone had any luck verifying this glitch on other actual hardware?
Has anyone had any luck verifying this glitch on other actual hardware?
I'm running it now:
1A, 1B, 1C: no corruption
1D: 7/300 frames corrupted. Min and max corrupted bytes: 1, 1
1E,1F: no corruption
2A, 2B, 2C, 2D, 2E, 2F: no corruption
It's a Sony HB-F1XDmk2.
@mcolom: Thanks for testing again!
Interesting to see that the results are slightly different, indicating that the timing is rather delicate.
Still, they are still in-line with what we have observed so far: 18 T-states seems to be the minimum required delay for safe VRAM access under worst-case scenarios.