Correct way to read the VRAM
Moderator: Moderators
- DarkKodKod
- Posts: 18
- Joined: Wed Sep 19, 2018 8:21 am
- Location: Germany
- Contact:
Correct way to read the VRAM
Hi All
I want to read what is in the nametable (the exact byte under any nametable address). The problem is that I want to read it not during the NMI because is for some check with collisions against the background. Is it really possible to access the VRAM not in the NMI and not have render glitches ?
and if it possible? what would be the method to do so?
I want to read what is in the nametable (the exact byte under any nametable address). The problem is that I want to read it not during the NMI because is for some check with collisions against the background. Is it really possible to access the VRAM not in the NMI and not have render glitches ?
and if it possible? what would be the method to do so?
Re: Correct way to read the VRAM
Keep a cached copy of the relevant portion of VRAM in main RAM. Super Mario Bros. keeps a 32x13-cell sliding window, where each cell represents 16 by 16 pixels. RHDE keeps a 32x24-cell window for the playfield, where each cell represents one 8x8-pixel tile.
Or structure the collision data to support random access. Haunted: Halloween '85 and '86 store each level's collision map in part as a list of rectangular slabs in ROM.
Or structure the collision data to support random access. Haunted: Halloween '85 and '86 store each level's collision map in part as a list of rectangular slabs in ROM.
- DarkKodKod
- Posts: 18
- Joined: Wed Sep 19, 2018 8:21 am
- Location: Germany
- Contact:
Re: Correct way to read the VRAM
I wanted to do it that but because I was using the 8x8 pixels tile, I thought it was a lot of RAM waste but if there is no other way, then I will follow your suggestion
Thanks!
Re: Correct way to read the VRAM
Is your game a scrolling game or a non-scrolling game? Is the playfield logically made of 8x8 pixel, 16x16 pixel, or other size cells? Choosing the best paradigm to fit hardware interaction, the sound driver, and your game state into 2048 bytes of RAM depends on the answers to these questions.
If it helps, here's an executive summary of the memory layout in RHDE:
$0000-$000F Local variables
$0010-$00FF Most commonly accessed variables and entity states
$0100-$01BF Decompression and VRAM transfer buffer
$01C0-$01FF Stack
$0200-$02FF OAM buffer
$0300-$05FF Playfield
$0600-$07FF Entity states; sound driver
If it helps, here's an executive summary of the memory layout in RHDE:
$0000-$000F Local variables
$0010-$00FF Most commonly accessed variables and entity states
$0100-$01BF Decompression and VRAM transfer buffer
$01C0-$01FF Stack
$0200-$02FF OAM buffer
$0300-$05FF Playfield
$0600-$07FF Entity states; sound driver
Re: Correct way to read the VRAM
You seem to be confusing NMI with VBlank. NMI is an intereput, one of the 3 supported by the 6502 CPU (NMI, IRQ, Reset). VBlank is an event happening each frame during which no image is rendered and VRAM can be freely accessed by $2006/7.DarkKodKod wrote: ↑Sat Jul 11, 2020 4:31 am Hi All
Is it really possible to access the VRAM not in the NMI and not have render glitches ?
You cannot read VRAM during rendering/outside of VBlank without causing graphical glitches, that is unless you use forced blanking and then nothing is shown on screen. Whether you use VBlank NMI or not is irrelevant for that matter.
Also, I strongly suggest separating collision data and nametable data written to VRAM. That being said, it's perfectly fine to read data back from VRAM during VBlank and use it for logic during the frame through a buffer system opposite of what is typically done for VRAM updates. The thing is, some people will consider it a waste of time that could be used for VRAM updates - but IMO it's fine to do even if unelegant if that is intended to be used for collision detection.
Last edited by Bregalad on Sat Jul 11, 2020 1:31 pm, edited 1 time in total.
- Controllerhead
- Posts: 314
- Joined: Tue Nov 13, 2018 4:58 am
- Location: $4016
- Contact:
Re: Correct way to read the VRAM
Personally, i keep track of map / camera coordinates and (re)read level data from ROM instead. Reading anything in VRAM is never really a great idea...DarkKodKod wrote: ↑Sat Jul 11, 2020 4:31 am Hi All
I want to read what is in the nametable (the exact byte under any nametable address). The problem is that I want to read it not during the NMI because is for some check with collisions against the background. Is it really possible to access the VRAM not in the NMI and not have render glitches ?
and if it possible? what would be the method to do so?
I don't know what your engine or level data format look like, but, if you want to save RAM at the cost of perhaps burning a few more cycles to reload a piece of your level data, this is probably the way to go.DarkKodKod wrote: ↑Sat Jul 11, 2020 4:40 am I thought it was a lot of RAM waste but if there is no other way, then I will follow your suggestion. Thanks!
Re: Correct way to read the VRAM
Doing collisions against graphical data is a poor programming practice in general. Depending on the hardware, game design or graphical style, you may run into cases where tiles need to be reused but with different collision attributes (e.g. a secret passage that looks like a solid wall, or a top-down view that results in objects being partially blocked, etc.).
On the NES in particular, using graphical data for collision is quite prohibitive, since access to VRAM is limited (as you found out) and mirroring all that data in program RAM would consume a ridiculously large chunk of memory. NES games will often do one of these 3 things instead:
1- buffer a partially decoded map in program RAM: emphasis on "partially", meaning that only the area immediately surrounding the player is available at any given time, and it's not fully decoded all the way down to the tile level, it usually goes at most to the 16x16 block level, from which tiles and collision data can be easily extracted.
2- use extra program RAM on the cartridge to hold entire maps: basically the same thing as above, but with more room to work with. If the levels aren't particularly large, complete maps can be in RAM at a time.
3- keep all maps entirely in ROM: this can be done when maps are stored in a format that allows for random access to the data (i.e. fixed compression ratio). Tile data is extracted from a map when scrolling, and collision data is extracted for physics stuff.
It's a good programming practice to keep a game's model (simulated world) separate from its view (graphical representation), so that you don't have to rebuild the entire game in case you decide to render it differently (e.g. when porting it to another system with incompatible video hardware, or changing the view from cross section to top-down, isometric or first person).
On the NES in particular, using graphical data for collision is quite prohibitive, since access to VRAM is limited (as you found out) and mirroring all that data in program RAM would consume a ridiculously large chunk of memory. NES games will often do one of these 3 things instead:
1- buffer a partially decoded map in program RAM: emphasis on "partially", meaning that only the area immediately surrounding the player is available at any given time, and it's not fully decoded all the way down to the tile level, it usually goes at most to the 16x16 block level, from which tiles and collision data can be easily extracted.
2- use extra program RAM on the cartridge to hold entire maps: basically the same thing as above, but with more room to work with. If the levels aren't particularly large, complete maps can be in RAM at a time.
3- keep all maps entirely in ROM: this can be done when maps are stored in a format that allows for random access to the data (i.e. fixed compression ratio). Tile data is extracted from a map when scrolling, and collision data is extracted for physics stuff.
It's a good programming practice to keep a game's model (simulated world) separate from its view (graphical representation), so that you don't have to rebuild the entire game in case you decide to render it differently (e.g. when porting it to another system with incompatible video hardware, or changing the view from cross section to top-down, isometric or first person).
Re: Correct way to read the VRAM
I fully agree in the case of PC games, mobile games, fifth generation (Saturn, PlayStation, N64) or later console games, and GBA or later handheld games. Before that, it's good practice but not quite as critical to maintain strict input and output abstraction because you have to rebuild everything anyway when porting a game to another machine with a mutually incompatible instruction set or an entirely different video paradigm (like that of Apple II).tokumaru wrote: ↑Sat Jul 11, 2020 10:15 am It's a good programming practice to keep a game's model (simulated world) separate from its view (graphical representation), so that you don't have to rebuild the entire game in case you decide to render it differently (e.g. when porting it to another system with incompatible video hardware, or changing the view from cross section to top-down, isometric or first person).
- Controllerhead
- Posts: 314
- Joined: Tue Nov 13, 2018 4:58 am
- Location: $4016
- Contact:
Re: Correct way to read the VRAM
If you're porting NES games to newer platforms you may consider interpreting the 6502 code... Writing a basic 6502 simulator without all the timing constraints sounds easier than reprogramming an entire game. Input and output would consist of native code, using as much of the new hardware as you'd like. If you have enough sub-pixel precision in your fixed-point coordinates, you can even draw graphics in higher resolutions without sacrificing the pixel-perfect movement.
You're right that total separation between game logic and IO isn't always 100% possible in older systems, but in many cases it is doable. If you can do it without sacrificing anything else, you should definitely go for it.
Re: Correct way to read the VRAM
If the collision check is just walkable vs. non-walkable (and not stuff like spikes who take energy away or swamp that makes you walk slower), then you can store one bit per 8x8 tile. Those are then 120 bytes.DarkKodKod wrote: ↑Sat Jul 11, 2020 4:40 am I wanted to do it that but because I was using the 8x8 pixels tile, I thought it was a lot of RAM waste but if there is no other way, then I will follow your suggestion
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: Correct way to read the VRAM
This is a good suggestion. If you really, really must get collision attributes from tile indices, you can get that information while copying the tiles to VRAM and cache only the collision attributes in a smaller table in RAM. Even if you need more states than walkable vs. non-walkable, using 2 bits or even 4 per tile will still result in significant memory savings compared to storing all 8 bits of each tile index.
- DarkKodKod
- Posts: 18
- Joined: Wed Sep 19, 2018 8:21 am
- Location: Germany
- Contact:
Re: Correct way to read the VRAM
Thanks for all of your advises. It is now clear how should I proceed with this. I have a lot of experience with game development but this is my very first game on the NES and I dont know the good practices yet