Well hello sir! I was wondering when I'd bump into you here!
Hey there! I've been away from 2600 stuff for a while, but it's still my second favorite system to code for. Glad to see other NESDev'ers showing interest!
I thought I read somewhere a lot of games use 2 scanlines at a time to be able to position more objects. Perhaps that's what I need to do?
Yeah, I guess that if you want anything that looks more complex than the earlier games you have to spread the logic over 2 or more scanlines.
Seems rather nutty to run out of cycles after positioning just TWO things, haha.
You do need a different mentality to code for the 2600. The more you think like a hardware designer, the better. Hardware designers are always cutting corners and packing the most functionality in the smallest amount of time and space. Think of how the NES PPU evaluates sprites: it subtracts the sprite's Y coordinate from the current scanline number, and the result is, if smaller than the sprite height, the line of the sprite that needs to be drawn. That's a very hardware-oriented approach, because it needs less state (no need for individual counters for each sprite) and solves multiple problems in a single operation. The 2600 can draw sprites in a similar way (I believe that the common name of the technique is "SkipDraw", and there are many variations of it).
A more radical approach to positioning sprites vertically is to use masks, but that requires a lot of ROM, so you'd normally only do this if you used bankswitching and had ROM banks dedicated exclusively to graphics. The idea is to have separate pointers to the sprite data and to a mask that blanks out the sprite where you don't want it to show (it's an array of $00s with a "hole" of $FFs the same height as the sprite). Then you can do this in your kernel:
lda (P0Pattern), y
and (P0Mask), y
Then you don't need to do any positioning in real time, so this is pretty fast (no decisions or states to take care of)! you just need to setup the pointers beforehand, so they align properly with Y as the scanline counter.
The size occupied by each of these masks is ScreenHeight * 2 + ObjectHeight, if you plan on being able to place the sprite anywhere in the screen, and you'll need different masks for different object heights.
Another important detail is that kernels don't typically buffer TIA writes, since that's way too slow. It's much better to time your logic so you can write to the registers directly. The TIA has a built-in buffer for player graphics though (you can delay GRP0 and GRP1 updates until the other register is written), and that helps a lot with making all the updates fit in hblank.
EDIT: Why is the forum mangling my code tags? It consistently removes line breaks and screws up the spacing!
Edited by tokumaru, Mon Feb 5, 2018 12:27 PM.