Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - [HOW-TO] detect VDP corruption with Emulicious

Reply to topic
Author Message
  • Joined: 01 Jul 2016
  • Posts: 55
Reply with quote
[HOW-TO] detect VDP corruption with Emulicious
Post Posted: Tue Apr 04, 2017 9:53 pm
Introduction
To avoid VDP corruption, I was looking for a way to detect VDP write at the "wrong" times.

While i was fighting with one case on my GG port, I tried every tool I have to help me...and so I finally came to Emulicious' RAM Watch

I don't know if it's already known or shared, I didn't find a post on it and noone gave me this hint when I was looking for help.

So I'll share it with you, in case 1 dev, one day, need it

The problem
Based on Charles SMS VDP doc, we know SMS display follow this behavior
NTSC, 256x192 -------------
Lines  Description

192    Active display
24     Bottom border
3      Bottom blanking
3      Vertical blanking
13     Top blanking
27     Top border


or, if you don't mind,
NTSC:
000 - 191   Active display  (192 lines in total)
192 - 262   Borders and blanking (70 VDP safe lines in total)

(thanks hang-on)

VDP corruptions occurs if we write within the 000-191 range.
To avoid this, you need to be sure every VDP write is done as soon as possible after vblank (so at 192+)

But how to detect if you write on the wrong range ? (apart if VDP corruption is huge)
With Emulicious ! (should I say "of course"?)

How ?
I use 3 global vars (global so they exists on the symbol files Emulicious loads with the ROM) : loopStartCounter, vdpEndCounter and loopEndCounter

I set loopStartCounter to VCounter (see $7e) at the start of my game loop, called after a waitVBlank
i then set vdpEndCounter to VCounter after all my VDP writes, before my game logic
And finally I set loopEndCounter at the end of my game loop

Build and load your ROM on Emulicious.
Open the debugger
Open the RAM watch window

On defaut graph, set X to @loopStartCounter (or @$<adr_loopStartCounter>) with type Unsigned 1 byte
Click "+"
On this new graph, set X to @vdpEndCounter (or @$<adr_vdpEndCounter>) the same way

Resume the game and watch the graphs

on the first one, you should see almost perfect line at .... 192 (yes, first line of the Vblank)
the second one is the most interesting, because it would give you were your VDP writes end.
To be safe, it should be 262 or less..sorry...255 or less
Don't forget the magic trick of the VDP to keep the VCounter 8bit :
(still from Charles doc's)
V counter values
00-DA, D5-FF



On the screenshot attached, the first graph is vdpWriteEnd
I'm in serious problem because it's at 137 and even more every time i add a sprite.
I clearly need to move some logic from the start of my game loop after all my VDP writes, if I want to avoid VDP corruption.


Conclusion
I hope it would help some of you.
I made this post because it's all I can do to give you back all the help you already gave me....and because Emulicious is full of awesome features a few seems to really know.

Let me know if it was a good idea, if I even find something else ;)


Please note I use Emulicious because I work on my Mac and my laptop..and because Meka doesn't work on my XP-dev laptop,.
Maybe something similar exists on Meka...be free to add a comment


  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3828
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Wed Apr 05, 2017 8:49 am
There's one small mistake in your post
KanedaFr wrote
VDP corruptions occurs if we write within the 000-191 range.


No, VDP corruption can only occur if both these conditions are true:
- we are in active display (line 0-191 with display ON)
- we don't wait at least 26 CPU cycles between writes to the VDP data port

otherwise it's completely fine to update VRAM contents during active screen, and you shouldn't have any corruption if you're using devkitSMS 'regular' functions (those which aren't UNSAFE)
  View user's profile Send private message Visit poster's website
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Thu Apr 06, 2017 8:31 pm
That's a quite creative use of the RAM Watch feature. I like it! :)
An alternative would be to set a breakpoint at the end of the game loop with a condition checking if the current scanline is out of the range you expect (e.g. "scanline < 192"). The breakpoint will then always suspend there when the condition is true. If you want to get a feeling for how often it happens you can also disable the check in the Suspend checkbox and keep an eye on the Breakpoint console as the game runs. But obviously if you're more of a visual type of guy you will prefer your solution over this one. :)

sverx wrote
There's one small mistake in your post
KanedaFr wrote
VDP corruptions occurs if we write within the 000-191 range.


No, VDP corruption can only occur if both these conditions are true:
- we are in active display (line 0-191 with display ON)
- we don't wait at least 26 CPU cycles between writes to the VDP data port

otherwise it's completely fine to update VRAM contents during active screen, and you shouldn't have any corruption if you're using devkitSMS 'regular' functions (those which aren't UNSAFE)

This is the first time I see you saying that VDP corruption is not related to writing to the VDP too fast. And you're right about it being unrelated. But you're not completely right about it being completely fine to update VRAM contents during active screen as in his case this is what causes the VDP corruption. What happens is that during his update of VRAM contents, his line interrupt occurs and in there he changes the scroll register. By writing to the scroll register he trashes the current VDP address and when he returns from the interrupt the tile loading code continues loading tiles (to the trashed address now) causing the corruption he describes. The "Break on inconsistent state after interrupt" breaks in such a situation and can help to identify when this is happening.
  View user's profile Send private message Visit poster's website
  • Joined: 01 Jul 2016
  • Posts: 55
Reply with quote
Post Posted: Thu Apr 06, 2017 10:48 pm
Calindro wrote
That's a quite creative use of the RAM Watch feature. I like it! :)
An alternative would be to set a breakpoint at the end of the game loop with a condition checking if the current scanline is out of the range you expect (e.g. "scanline < 192"). The breakpoint will then always suspend there when the condition is true. If you want to get a feeling for how often it happens you can also disable the check in the Suspend checkbox and keep an eye on the Breakpoint console as the game runs. But obviously if you're more of a visual type of guy you will prefer your solution over this one. :).


Thanks
At first, I looked on the Expression view to the 3 vars i created.
I also read about the checkbox suspend but I was sure a visual view would be awesome, so I tried the RaM watch.
And this way to see var's value over time is just awesome: I was, for exemple, able to 'see' that adding an enemy sprite increased the game loop of about 10 scanlines...

I'm still trying to understand what the "Y" is for but...perhaps later, looking to fix another bug ;)
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3828
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Fri Apr 07, 2017 8:00 am
Calindro wrote
What happens is that during his update of VRAM contents, his line interrupt occurs and in there he changes the scroll register. By writing to the scroll register he trashes the current VDP address and when he returns from the interrupt the tile loading code continues loading tiles (to the trashed address now) causing the corruption he describes. The "Break on inconsistent state after interrupt" breaks in such a situation and can help to identify when this is happening.


Wait - are you saying that if I update some VDP register then I should write again a VRAM address to the control port before pushing any data to the data port? That's completely new to me, and it's something that may surely cause a lot of headaches! But it's very weird that writing to a VDP register should change the VRAM pointer register - but indeed not impossible.

So Kaneda's only option is to make sure that he doesn't update VDP registers while updating VRAM, or the other way around (make sure there are no VRAM updates when he needs line IRQ to update VDP registers)
  View user's profile Send private message Visit poster's website
  • Joined: 31 Oct 2007
  • Posts: 853
  • Location: Estonia, Rapla city
Reply with quote
Post Posted: Fri Apr 07, 2017 11:49 am
Writing to a VDP register will reset VRAM address, same behaviour happens on TMS and MD VDPs too.
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Sat Apr 08, 2017 6:51 am
Resets it to what? Does it apply the register number/value pair to the address latch?
  View user's profile Send private message Visit poster's website
  • Joined: 01 Jan 2014
  • Posts: 331
Reply with quote
Post Posted: Sat Apr 08, 2017 7:27 am
edit: actually i can't remember specifics but its something like that maxim.
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14745
  • Location: London
Reply with quote
Post Posted: Sat Apr 08, 2017 8:04 pm
Actually Charles' doc says it plainly:

Quote
the address and code register are updated like normal when a VDP register write is done


Basically, register writes are to the same port as address writes, and the processing is the same regarding setting the address - just the high two bits determine what happens next (read and increment VRAM, select VRAM and wait for write, set register from address, or select CRAM and wait for write).

For games keeping their VRAM work inside VBlank, it's not an issue; but raster FX just can't be interleaved with slow writes (or reads), unless you disable interrupts and set the address every time, which will mess with the line interrupt timing.
  View user's profile Send private message Visit poster's website
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Mon Apr 10, 2017 7:54 pm
KanedaFr wrote
Calindro wrote
That's a quite creative use of the RAM Watch feature. I like it! :)
An alternative would be to set a breakpoint at the end of the game loop with a condition checking if the current scanline is out of the range you expect (e.g. "scanline < 192"). The breakpoint will then always suspend there when the condition is true. If you want to get a feeling for how often it happens you can also disable the check in the Suspend checkbox and keep an eye on the Breakpoint console as the game runs. But obviously if you're more of a visual type of guy you will prefer your solution over this one. :).


Thanks
At first, I looked on the Expression view to the 3 vars i created.
I also read about the checkbox suspend but I was sure a visual view would be awesome, so I tried the RaM watch.
And this way to see var's value over time is just awesome: I was, for exemple, able to 'see' that adding an enemy sprite increased the game loop of about 10 scanlines...

I'm still trying to understand what the "Y" is for but...perhaps later, looking to fix another bug ;)

Yeah, that's what the RAM Watch is there for: Watch how the values in RAM change.

A plot is made up of (X, Y) pairs which define data points. In the X field you can enter the data source for the X coordinates, in the Y field you can enter the data source for the Y coordinates. Leaving one of these fields empty causes the plotter to use the values 0 to 511 for the respective coordinate.
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!