So, I thought we had talked about this before, and it seemed easy, but I'm scratching my head. All direction scrolling, without showing tiles change, nor attribute table errors...
I was thinking vertical mirroring, would make it easy to scroll left and right without problem. And, leaving BG rendering off for the top 16 pixels would solve up/down scrolling issues. It might be very hard to time this correctly...(NROM) since sprite zero hit won't work with BG off.
Any other suggestions?
Horizontal mirroring with sprites hiding the right and pixels turned off on the left?
For a long time I did exactly what you described, but instead of the sprite 0 hit I used a sprite overflow to time the blanking at the top of the screen. Conveniently enough, the 9 sprites also masked any sprites that happened to go up there. The down side is that, besides losing 9 sprites, you have to waste 8 or 16 scanlines worth of CPU time, depending on the height of your sprites.
To avoid headaches once and for all, I finally switched to a 4-screen layout (i.e. no mirroring). If the cartridge uses CHR-RAM, 4-screen comes for free because of the RAM sizes available today.
Sprite Overflow.
4-screen VRAM.
Both good suggestions. Thank you. That's what I was looking for.
What mapper do you suggest to use with 4-screen?
Looks like standard MMC3 can do 4-screen and it seems like a good mapper to me. CHR-RAM, PRG-RAM, nametable arrangement control (or 4-screen), scanline IRQ and a good amount of PRG and CHR space.
Quote:
What mapper do you suggest to use with 4-screen?
If you can live without scanline irqs,
GTROM is great. Powerful for its home tinkerer friendly price per board.
Any mapper can do 4-screen, but some mappers may require more complicated logic on the cartridge than others. On discrete logic mappers with CHR-RAM, the use of 4-screen is trivial: one wire on the cartridge tells the NES to keep its internal VRAM permanently off so the VRAM in the cartridge (which must be at least 16KB) is used not only for attribute tables, but also for name tables.
GTROM is cool and takes things a step further, doubling the CHR-RAM and NT capacity through bankswitching.
I see. That's good as long as the 4-screen modifications doesn't conflict with any behaviour defined in iNES or NES 2.0 standard, so that it doesn't work properly in emulators or flashcarts.
While GTROM is interesting it seems it only comes in 72-pin. I have no NES and I'd avoid using an adapter if possible.
To get back on topic, if 4-screen can't be used for whatever reason, what other options are there besides sprite overflow?
Some of its features are probably not implemented in emulation. 7.5kB extra ram accessible via PPU address space, self modification (since it's flash), the two LEDs come to mind. I assume the LEDs were included as an easy way to say "don't shut me off while i'm saving your precious game/modifying myself!"
Many years ago, someone (tepples?) observed that "XOR" nametable layout (▞) should make almost-seamless scrolling possible ...
We're pretty certain that no commercial games used it—preventing glitches just wasn't a high enough priority—but the hardware needed is trivial, and any mapper with the ability to arbitrarily configure nametables could be used to develop for it.
I find it interesting to observe that besides Tengen's Gauntlet and Irem's Napoleon Senki (and Rad Racer 2, which doesn't seem to use it for actual four-way scrolling), the only other games that use 4-screen VRAM (and that I'm aware of) are Sachen games, namely Jurassic Boy 2, Rocman X and Zhōngguó Dàhēng. Zhōngguó Dàhēng actually uses six nametables --- four on cartridge RAM for the game board, and the two in the console for the status bar.
NewRisingSun wrote:
I find it interesting to observe that besides Tengen's Gauntlet and Irem's Napoleon Senki (and Rad Racer 2, which doesn't seem to use it for actual four-way scrolling), the only other games that use 4-screen VRAM (and that I'm aware of) are Sachen games, namely Jurassic Boy 2, Rocman X and Zhōngguó Dàhēng. Zhōngguó Dàhēng actually uses six nametables --- four on cartridge RAM for the game board, and the two in the console for the status bar.
Well, right now it's no cheaper to get 8k than 64k SRAM, hence why it's basically a "for free" feature if you have CHR-RAM. Those Sachen games you mentioned all came well after the official commercial era of NES games had ended. I don't know what RAM costs were like in the late 90s but there's probably a point in there where it was just really cost effective to have extra nametable RAM + CHR-RAM.
FrankenGraphics wrote:
Some of its features are probably not implemented in emulation. 7.5kB extra ram accessible via PPU address space, self modification (since it's flash), the two LEDs come to mind. I assume the LEDs were included as an easy way to say "don't shut me off while i'm saving your precious game/modifying myself!"
I implemented if in FCEUX about a year ago. Aside from the LEDs, I think it's actually a "complete" implementation. It even does the flashing.
The "extra ram via PPU" thing isn't its own special feature, it's just a side effect of the nametable banking. $3000-3F00 isn't used by rendering, but the SRAM is just mapped flatly across it anyway in case you want to use it.
(It was just after the last stable release of FCEUX, unfortunately, but there's an interim build available on the site that would have it.)
rainwarrior wrote:
Well, right now it's no cheaper to get 8k than 64k SRAM, hence why it's basically a "for free" feature if you have CHR-RAM.
Unless you're already banking the CHR RAM to allow more than 4K for background tiles and 1K for player and boss sprite cels without slowing down background tile animations.
Haunted: Halloween '85 uses the same board as
Lizard, which provides 8K CHR RAM to allow 256 background tiles at once and double-buffered 256-byte sprite cels that take two frames to load. Its sequel
The Curse of Possum Hollow, by contrast, uses separate 4K CHR RAM banks for the playfield, the parallax strips, and the subtitle font, and sprite cels are arranged into 1K sheets each loaded over the course of eight frames. Most of this CHR trickery could have been done with the TQROM setup, but an 8K RAM and 64K ROM is probably more expensive.
It's tricky to use video memory at $3000-$3EFF for game logic because you have to wait for vblank, which may already be chock full of tile and nametable transfers
to video memory, and you have to know a frame in advance what memory you're going to need to read.
Pokun wrote:
What mapper do you suggest to use with 4-screen?
To be honnest the most elegant mapper which does it is Napoleon Senki's mapper. It does it in a very elegant way using 2kb of CHR-ROM for animated tiles, and using the remaining 6kb for CHR-RAM and leaving 2 extra KB for 2 name tables, combined with the CIRAM for the other 2. It's amazing. Unfortunately this mapper was wasted on a horrible game.
As for your original post:
Quote:
So, I thought we had talked about this before, and it seemed easy, but I'm scratching my head. All direction scrolling, without showing tiles change, nor attribute table errors...
Outside of 4-screen mirroring which I consider a bit cheating, there is
only one way to go really for FULLY clean scrolling with no attribute clash, and no sprite popping : Use vertical mirroring with 16 scanlines at the top hidden AND use $2001 to hid the left 8 pixels. If you use 8x8 sprites, you can hide the top and bottom 8 scanlines instead of the top 16, but it'd be hard to achieve without an IRQs mapper. Hiding the top 16 lines however is very much doable without an IRQ mapper. It requires a constant-timed NMI routine but I don't think it's even close to impossible to achieve
PS: Technically you could do it with "only" 15 hidden lines, 16 aren't needed, but you get the idea.
Bregalad wrote:
Hiding the top 16 lines however is very much doable without an IRQ mapper. It requires a constant-timed NMI routine
It doesn't require a constant-timed NMI routine. If you can guarantee a sprite 0 hit or a sprite overflow (which is what I use to mask sprites at the top of the screen as opposed to keeping rendering completely off and getting the alternate dot crawl pattern) every frame, you can wait for one of these flags to be cleared in order to detect the end of vblank after you're done with your non-contant-timed NMI handler. Then you wait 16 scanlines with timed code (you can do some contant-timed tasks here if you want), and finally turn background rendering on.
Ok, I got a 99% funcional all-direction (top down) engine working. (99% because I see some missing tiles in the corners if you're going diagonally).
No attribute tables yet. No collisions with BG yet. Had to rewrite how neslib handles scrolling, and rewrite the nmi routine.
I'm going to have to drop this project and get back to it...I have so many things to work on right now.
Edit, I'm using 4 screen mode, btw.
tokumaru wrote:
It doesn't require a constant-timed NMI routine. If you can guarantee a sprite 0 hit or a sprite overflow (which is what I use to mask sprites at the top of the screen as opposed to keeping rendering completely off and getting the alternate dot crawl pattern) every frame, you can wait for one of these flags to be cleared in order to detect the end of vblank after you're done with your non-contant-timed NMI handler. Then you wait 16 scanlines with timed code (you can do some contant-timed tasks here if you want), and finally turn background rendering on.
You are indeed right that a constant-timed NMI routine is not needed. For some reason I tought sprite zero hit was mainly for synchronizing at the HIT point, but I forgot that it also allows to synchronize at the end of VBlank, even if the hit point is not even used at all !
Techniques for blanking the first 8 or 16 lines (needed if you do not want any sprite pop-up !) can range from disabling BG by $2001 and having 8 high priority sprites there, to disable rendering completely, or leave everything enabled but use blank CHR-ROM pages for everything.
Also I forgot to mention that IF you're using 8x8 sprites, you could also use horizontal mirroring, and hide the left and right 8 pxiels (requires 30 sprites !!) and hide only the top 8 lines. Since it requires so many sprites, it's not the greatest idea, but it's an alternate way to get completely glitch-free scrolling without sprite poping nor attribute clases. I still belive vertical mirroring to be more convenient.
Quote:
Edit, I'm using 4 screen mode, btw.
Booo cheating !
Just kidding, actually hardware wise it makes a lot of sense to solve the problem this way, but it isn't the 80's way of solving the problem.
I don't understand how you can mask the topmost 8 or 16 scanlines of BG characters using just 8 high priority sprites? Sprites are just 8 dot wide and BG characters are not affected by the sprite overflow.
dougeff wrote:
(99% because I see some missing tiles in the corners if you're going diagonally).
This usually happens because of rows overwriting columns and/or vice versa. When you move diagonally, the column and the row that need to be updated overlap at the corner of the screen, so if you do things in the wrong order, you may corrupt that corner.
Quote:
No attribute tables yet.
I guess you were a bit too optimistic with that 99% figure then... attributes are easily the worst part of an 8-way scroller, specially if the map is built with 256x256-pixel blocks instead of 256x240 ones. I think you mentioned using 256x240, so your case is not so bad, but you still have to split and combine attribute bytes vertically when updating rows, which's kind of annoying.
Quote:
Edit, I'm using 4 screen mode, btw.
Oh, so you may not need to do any serious attribute juggling after all.
Bregalad wrote:
Techniques for blanking the first 8 or 16 lines (needed if you do not want any sprite pop-up !) can range from disabling BG by $2001 and having 8 high priority sprites there, to disable rendering completely, or leave everything enabled but use blank CHR-ROM pages for everything.
Yeah, that's right. Jurassic Park uses the blank (actually black) patterns approach. This is better because it doesn't waste any sprites, but the other method is not so bad IMO.
Quote:
Also I forgot to mention that IF you're using 8x8 sprites, you could also use horizontal mirroring, and hide the left and right 8 pxiels (requires 30 sprites !!) and hide only the top 8 lines. Since it requires so many sprites, it's not the greatest idea, but it's an alternate way to get completely glitch-free scrolling without sprite poping nor attribute clases. I still belive vertical mirroring to be more convenient.
Felix the Cat and Alfred Chicken use sprites to hide the rightmost column of the background, but I'm pretty sure they use 8x16 sprites. That's still pretty wasteful though, because not only you have to waste 15 sprites (compared to only 9 with the other approach), but you also effectively reduce the number of sprites per scanline to 7, as if 8 wasn't already bad enough.
Quote:
hardware wise it makes a lot of sense to solve the problem this way
That was my conclusion. No need to be masochistic about something just so you can be 100% authentic to some specific time period, if you can just go with a very simple solution that has no real drawbacks.
Pokun wrote:
I don't understand how you can mask the topmost 8 or 16 scanlines of BG characters using just 8 high priority sprites? Sprites are just 8 dot wide and BG characters are not affected by the sprite overflow.
The high priority sprites are for hiding sprites only (and you need 9 if you want to trigger the overflow flag)... the background is disabled normally using $2001, which's why you need timed code to enable the it back at the correct time.
Oh now I see. I misunderstood Bergalad's message, he said turn off BG AND hide sprites using sprite overflow.
OK so by only turning off the BG it doesn't count as forced blanking because sprites are still on, just that stray sprites that go up there are hidden by the overflow.
Exactly. There are two advantages in using a sprite overflow instead of disabling rendering completely:
1- By keeping rendering on, you avoid the alternate dot crawl pattern in NTSC, which can look weird;
2- You can use the sprite overflow flag to sync up with the start of the frame, eliminating the need for a constant-timed NMI handler, which is all but trivial to code.
3- You finally get to use that buggy sprite overflow flag, although probably not what it was designed for!
BTW how exactly do you check for the flag? Do you poll $2002 in the beginning of your main loop right after a vblank or something?
If you want to detect the end of vblank, you have to poll $2002 in the vblank handler, after all PPU operations are done, waiting for the overflow flag to be *cleared*.
All raster effects timed from the start of the frame should be done in the NMI handler, because you want them to happen every frame, even if the game logic lags, otherwise the image will glitch every time the game slows down.
And, more importantly:
4 - You can scroll using $2005/$2000 normally and don't have to touch $2006.
On the other hand, an advantage of using real forced blanking for the first 16 lines would be to have extended time for VRAM updates.
Using sprite overflow flag might be elegant, but does it really work ? I mean all I heard about this flag is that it's buggy and it's usage should be avoided.
Yeah I thought that as well, how can you rely on it if it's inconsistent? But according to the
wiki: "games can intentionally place 9 or more sprites in a scanline to trigger the overflow flag consistently, as long as no previous scanlines have exactly 8 sprites." It sounds like its behaviour is consistent on the very first overflow. And if you use it on the first 8 scanlines there should be no risk for unexpected behaviour.
tokumaru wrote:
If you want to detect the end of vblank, you have to poll $2002 in the vblank handler, after all PPU operations are done, waiting for the overflow flag to be *cleared*.
All raster effects timed from the start of the frame should be done in the NMI handler, because you want them to happen every frame, even if the game logic lags, otherwise the image will glitch every time the game slows down.
I see, one disadvantage of this is that you are potentially loosing some time for your logic to run in waiting for the flag I guess. But slowdown is better than graphical glitches.
Yeah, sprite overflow is buggy, but 9 high priority sprites before anything else does guarantee that the flag is set consistently.
Pokun wrote:
I see, one disadvantage of this is that you are potentially loosing some time for your logic to run in waiting for the flag I guess. But slowdown is better than graphical glitches.
Yeah, you waste some time waiting for the end of vblank, and some more while timing the border itself, but you may at least run constant-timed tasks during the second wait... Maybe a
constant-cycle music engine?
Here's a video of my top-down, all direction game engine (like Crystalis). Will put on the blog soon. Got collisions and attribute tables working. Coding this was a huge pain in the ass (or back, since it's actually my back and eyes that hurt)...about a dozen bugs that I fixed, probably more that I missed.
https://youtu.be/gqVa3RHxMjEAll the 'under the hood' code is in asm, but the main code is in C (cc65). I am buffering nametable writes every frame, lots of them. 130 scanlines of filling buffers, and there's no music.
EDIT - and right up against the end of v-blank every frame, like 10-20 instructions before the pre-render scanline. So...basically, no PPU updates or palette changes of any kind, unless I shut off my own PPU updates for a frame.
dougeff wrote:
130 scanlines of filling buffers, and there's no music.
Quote:
and right up against the end of v-blank every frame, like 10-20 instructions before the pre-render scanline.
Those could be serious problems for a fast-paced game, but might be OK for something slower, like an RPG.
This is great, but why not first make a single left-right scrolling, and another simple scroll up-down?
The scroll advantage 4 nametables is very interesting, but I think before reaching this easier to explain first serious lesson right?
[Re, 1 direction scrolling]
Maybe next time. I'm very busy these days.
Also, you can use the all-direction engine to just go horizontally (keep Y at zero, or in the 2 nametable tall range)...or vertically (keep X zero, ot in the 2 nametable wide range)...with slight modification.
And stop calling the "fill the ppu buffer" for the opposite direction, and change the mirroring in the .cfg file, if you want.
Yes, I was testing things yesterday.
I managed to run from left to right and from right to left by setting the mirror to horizontal in.cgf, and in lesson28.h the MAX_SCROLLING_Y to 0. This enabled the screen to lock up and down.
However, when I move, there comes a moment on the screen number 4-5 that begin to appear glicht graphics and gives problems.
In addition, it really seems that the nametables are loaded two by two vertically, and although BGTABLE only left, for example, 9 nametables, these were loaded from top to bottom.
I do not know if I understand the last one I've written. I am sorry
Hmm, maybe there's some bugs in the code.
I will look at it ...some day.
EDIT - I didn't have any problems, when I changed #define ROOMS_HIGH 1
(essentially the same as MAX_SCROLL_Y 0)
I didn't change the .cfg mirroring. I don't think that should be the problem either. Or, maybe that is the problem...yes...it does load 2 rooms tall worth of tiles when you scroll left and right... You would need to go to DrawBG.s and in _CSV2NT_COL_FILL: at line 280 it should just do...
lda #$ff
sta _Col_Buffer+62
jmp Col_Ready
to skip the second half of the 'Draw a column' function.
And, shouldn't it be vertical mirroring? To do horizontal scrolling? It's too early in the morning, I need some coffee.
EDIT 2 - still see some graphic errors. Looks like there is bugs in the code.
EDIT 3 - I think the problem then, is the CSV2NT_ROW_FILL function is being called, and it shouldn't be. I commented out refereces in lesson28.c on lines 292 and 301, and edited CSV2NT_FULL to do 1 fewer row. Seems to work.
dougeff wrote:
Hmm, maybe there's some bugs in the code.
I will look at it ...some day.
EDIT - I didn't have any problems, when I changed #define ROOMS_HIGH 1
(essentially the same as MAX_SCROLL_Y 0)
I didn't change the .cfg mirroring. I don't think that should be the problem either. Or, maybe that is the problem...yes...it does load 2 rooms tall worth of tiles when you scroll left and right... You would need to go to DrawBG.s and in _CSV2NT_COL_FILL: at line 280 it should just do...
lda #$ff
sta _Col_Buffer+62
jmp Col_Ready
to skip the second half of the 'Draw a column' function.
And, shouldn't it be vertical mirroring? To do horizontal scrolling? It's too early in the morning, I need some coffee.
EDIT 2 - still see some graphic errors. Looks like there is bugs in the code.
EDIT 3 - I think the problem then, is the CSV2NT_ROW_FILL function is being called, and it shouldn't be. I commented out refereces in lesson28.c on lines 292 and 301, and edited CSV2NT_FULL to do 1 fewer row. Seems to work.
Mmm, a little hard
My config:
lesson28.h
#define ROOMS_HIGH 1
#define MAX_SCROLL_Y 0
.cgf
NES_MIRRORING: type = weak, value = 1;
lesson28.c
Commented lines and remains so, but this is not whether it is right, because then nothing moves.
Code:
/*
if (direction == GoRight){
tempX = masterX + 0x100;
tempY = masterY;
CSV2NT_COL_FILL();
}
else {
tempX = masterX - 0x10;
tempY = masterY;
CSV2NT_COL_FILL();
}
*/
DrawBG.s
Add these lines at position 280
Code:
;62
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;next column, nametable down one
inc _tempY+1
lda #$ff
sta _Col_Buffer+62
jmp Col_Ready
This still does not work correctly what is supposed to be corrected in CSV2NT_FULL?
EDIT:
In addition, change the BGtable to make a single row and I think that is where there really a problem. As they have nothing to load down errors occur.
Code:
const unsigned char * const BG_TABLE[] = {
BG00, BG01, BG02, BG03, BG04, BG05, BG06, BG07
};
EDIT 2:
It seems that there is a limit when creating METATILES for BACKGROUND
How can increase?
I'm glad someone can make sense of my code. I've been too busy to write a proper blog page. Or comment the code.
I'm definitely going to change the code, ...and make a H scroll only and a V scroll only version of the same.
I regret to report that the engine 4 nametable scroll does not work properly in PAL NES version.
https://www.youtube.com/watch?v=Qc0p-Gv7LzI
Ok.
I'm assuming you are testing on a cartridge that can do 4 screen mirroring?
dougeff wrote:
Ok.
I'm assuming you are testing on a cartridge that can do 4 screen mirroring?
No. I'm testing on a flashcard but now that you mention it will test a PCB compatible with 4 nametable and I said something.
FYI, I spotted some attribute problems (miscolored tiles) when trying that Star Wars demo.
Ok, I just made several attempts on different plates. A PCB Second Dimension is and the other is Retro-Stage.
Previously, I tried an ordinary rom to verify that both plates were functioning properly. In this case, I used the Super Mario Bros. rom and everything went well.
Then I tried SW-RotJ rom. I soldered V and H. In both cases the effects were the same. Can these plates are incompatible with 4 screen?
https://www.youtube.com/watch?v=Zj8wDVlfi3YAs you can see, the result is different. Besides making a wrong scroll, blink several metatiles across the screen.
None of these boards support 4-screen name table layout. If you're soldering H and V tabs, that means you're using mirroring, and 4-screen is precisely the lack of mirroring.
tokumaru wrote:
None of these boards support 4-screen name table layout. If you're soldering H and V tabs, that means you're using mirroring, and 4-screen is precisely the lack of mirroring.
Indeed, you're right. Even unsoldering H and V still did not work, therefore, these plates are not compatible with screen 4 mirror.
However, I tested with GTROM / Cheapocabras and if it works smoothly:
https://www.youtube.com/watch?v=MgiqSY10zzA