Hey everyone,
I noticed that in one of my programs there was sometimes sprite flicker that I could not explain.
After some investigations I figured out that I was writing data too fast. My assumptions have always been that 17 T-States should be enough delay, but testing on actual hardware this assumptions turns out to be false. E.g.:
OUT (#98),A ; 12 T-states NOP ; 5 T-states OUT (#98),A ; 17 T-states delay is not enough!
My speculation is that the internal destination VRAM address of the VDP is correctly incremented, but sometimes the byte that is actually written into VRAM somehow gets mutilated.
Been trying to find some 'scientific' information with some clear metrics on this but have not been able to find anything conclusive (e.g.: [[www.msx.org]] VRAM_access_speed or [[map.grauw.nl]] vdp-timings part 1 and 2.)
I have written a test program that runs a couple of test (see below) and ran it on some actual hardware. It seems that 17 T-states delay is only safe when inside V-blank, outside of it it needs to be 18 T-states or more (e.g.: just use OUTI).
Some results:
NMS 8245 (V9938), artifacts occur with 17 T-states if:
- 50Hz, Outside V-blank, not executing a VDP command.
- 50Hz, Outside V-blank, while executing a VDP command.
- 60Hz, Outside V-blank, while executing a VDP command.
NMS 8255 (but was upgraded to MSX 2+ so it has a V9958), artifacts occur with 17 T-states if:
- 50Hz, Outside V-blank, not executing a VDP command.
- 60Hz, Outside V-blank, while executing a VDP command.
These results are bit puzzling, because only some combinations of 50Hz/60Hz and running a HMMM VDP command or not seems to cause glitches.
It could be that my hardware is just very old? (Especially my NMS 8255 has seen a lot of usage over the years.)
Could other people also try this test and see what results they get? :) (would be interesting to see what happens on actual MSX 2+ machines, for example?)
BTW: tried the test on latest openMSX (17.0) and the glitches are not visible there, nor does it warn of too-fast VRAM access, which could be an emulation inaccuracy?
Below you will find the test itself, which is basically a BASIC program with some assembly code to run the timing critical parts.
(also see the attached screenshot for what to look for).
Direct VRAM access timing test:
10 REM Direct VRAM I/O timing tests. 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 810:P$="":FOR I=1 TO 32:READ D:P$=P$+CHR$(D):NEXT I 50 SPRITE$(0)=P$ 60 RESTORE 910: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 1010 210 READ SZ 220 FOR I=1 TO SZ:READ D:POKE &H9000+I-1,D:NEXT I 230 D=BASE(28):POKE &H9000,D AND 255:POKE &H9001,D/256 240 FR=5*60:POKE &H9005,FR AND 255:POKE &H9006,FR/256 250 DEF USR0=&H9007 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 or 19 (T-States delay) 550 CLS 560 PSET(0,30),0:PRINT #1,"TEST: "+TN$+" (run"+STR$(FR)+" frames)" 570 PSET(0,40),0:IF HZ=50 THEN VDP(10)=2:PRINT #1,"50 Hz" ELSE VDP(10)=0:PRINT #1,"60 Hz" 580 POKE &H9002,VB:PSET(0,50),0:IF VB=0 THEN PRINT #1,"Outside V-blank" ELSE PRINT #1,"Inside V-blank" 590 POKE &H9003,CM:PSET(0,60),0:IF CM=0 THEN PRINT #1,"No VDP command" ELSE PRINT #1,"Run VDP command while writing" 600 POKE &H9004,DL:PSET(0,70),0:PRINT #1,"T-States delay:"+STR$(DL) 610 X=0:A$="sprite":GOSUB640 620 X=126:A$="cross":GOSUB640 630 A=USR(0):RETURN 640 PSET(X,150),0:PRINT #1,"^ See":PSET(X,160),0:PRINT #1,"| "+A$:PSET(X,170),0: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 1000 Rem Assembly code 1010 DATA 313 1020 DATA 0,0,0,0,17,1,0,243 1030 DATA 33,159,253,17,26,145,1,5 1040 DATA 0,237,176,33,66,144,17,159 1050 DATA 253,1,3,0,237,176,251,237 1060 DATA 75,5,144,237,91,158,252,42 1070 DATA 158,252,167,237,82,40,248,11 1080 DATA 121,176,32,239,243,33,26,145 1090 DATA 17,159,253,1,5,0,237,176 1100 DATA 251,201,195,75,144,219,153,165 1110 DATA 32,251,201,14,155,62,2,211 1120 DATA 153,62,143,211,153,58,2,144 1130 DATA 167,32,5,46,64,205,69,144 1140 DATA 58,3,144,167,40,49,33,54 1150 DATA 145,126,60,230,15,71,135,135 1160 DATA 135,135,176,119,33,46,145,62 1170 DATA 36,211,153,62,145,211,153,6 1180 DATA 11,237,179,46,1,205,69,144 1190 DATA 33,31,145,62,32,211,153,62 1200 DATA 145,211,153,6,15,237,179,42 1210 DATA 0,144,124,15,15,230,3,211 1220 DATA 153,62,142,211,153,125,211,153 1230 DATA 124,230,63,246,64,211,153,175 1240 DATA 211,153,62,144,211,153,175,211 1250 DATA 154,62,3,211,154,6,8,58 1260 DATA 4,144,254,19,40,43,254,18 1270 DATA 40,17,62,120,211,152,0,211 1280 DATA 152,175,211,152,0,211,152,16 1290 DATA 241,24,38,1,152,32,33,237 1300 DATA 144,237,163,237,163,237,163,237 1310 DATA 163,32,243,24,20,120,120,0 1320 DATA 0,62,120,211,152,35,211,152 1330 DATA 62,0,211,152,43,211,152,16 1340 DATA 240,62,216,211,152,0,175,211 1350 DATA 153,62,143,211,153,175,211,153 1360 DATA 62,144,211,153,175,211,154,175 1370 DATA 211,154,0,0,0,0,0,2 1380 DATA 0,0,0,0,0,0,0,254 1390 DATA 0,8,0,0,0,208,254,0 1400 DATA 0,0,2,0,8,0,0,0 1410 DATA 192
If you run the test, you should look for sprite glitches whereby a sprite is sometimes visible on the left side of the screen (or anywhere really), or sometimes the center sprite shows a cross (although this seems to be quite rare). Either one of these glitches means a sprite's X, Y and/or pattern number has been garbled while writing it to VRAM.
Edit: Hmm, for some reason the image tag doesn't work, try this instead: https://www.msx.pics/image/yH8cD