Jump to content
IGNORED

A Two-Player Competitive and Co-Op "Tetris" for the Atari 2600 VCS


AkashicRecord

Recommended Posts

I don't know if you have been thinking of NTSC/PAL compatibility, but it might be good to implement that now instead of later. All you really need to change between the two is the length of vblank and overscan, plus the speed of the game. Without adjusting for speed, PAL games run at 5/6 speed, which is very noticeably different. Since you've been working on game speed, I thought I would suggest it.

I haven't implemented speed into my NyanCat project yet, but I plan on using the typical method of having an 8-bit "fractional" counter that changes your object's position when the counter rolls over. This way, you could add values that are multiples of 5 in NTSC mode, and multiples of 6 in PAL mode, and both modes would play at exactly the same speed.

Thanks for bringing that one up, because I did think about it very briefly when I was selecting palette colors. I think I can find a nice balance between the two, and I was leaning towards 50Hz as the standard difficulty speed, anyway. PAL support is definitely on the early list. It looks like the majority of it will only require different spinlock timer values and different color values (along with your list too, of course.)

Link to comment
Share on other sites

I don't know if you have been thinking of NTSC/PAL compatibility, but it might be good to implement that now instead of later. All you really need to change between the two is the length of vblank and overscan, plus the speed of the game. Without adjusting for speed, PAL games run at 5/6 speed, which is very noticeably different. Since you've been working on game speed, I thought I would suggest it.

I haven't implemented speed into my NyanCat project yet, but I plan on using the typical method of having an 8-bit "fractional" counter that changes your object's position when the counter rolls over. This way, you could add values that are multiples of 5 in NTSC mode, and multiples of 6 in PAL mode, and both modes would play at exactly the same speed.

All you need to swap between ntsc/pal mode is a palette swap. Many homebrews use the color/bw switchfor this feature. Most pal gamers prefer 60hz refresh rate when feasible.
  • Like 2
Link to comment
Share on other sites

Thanks for bringing that one up, because I did think about it very briefly when I was selecting palette colors. I think I can find a nice balance between the two, and I was leaning towards 50Hz as the standard difficulty speed, anyway. PAL support is definitely on the early list. It looks like the majority of it will only require different spinlock timer values and different color values (along with your list too, of course.)

Don't rely on framerate to control difficulty. While most pal tvs can sync to 60hz, most ntsc tube tvs "roll" on 50hz. The few that can sync at 50hz generally crop the bottom of the image. Game needs to be locked to 262 scanlines if outputting ntsc color mode, and I suggest keeping 262 scanlines and only changing the color palette if you want pal. To get pal50, you will have to adjust scanline count to 312 and tweak all of the in game timing accordingly.

 

Starting difficulty is moot. All Tetris variants start slow and gain speed as levels progress, so let the player decide the starting difficulty.

  • Like 1
Link to comment
Share on other sites

I won't get too hung up on timing, but I should have clarified that I wanted to basically have the game "clock" ticking at the same rate on both 60 and 50Hz systems so that there really won't be any noticeable difference in game play or speed between regions.

 

Right now I'm moving forward on to strategies on representing the games playfield data and manipulating its bitfields.

Link to comment
Share on other sites

I wouldn't worry much about SECAM, but if you want to support it, I would just make sure that black is the only color with a luminance value of 0, and that the score/menu colors contrast with the background in the SECAM palette. That way, if the game is run on SECAM hardware, it will at least be playable.

  • Like 1
Link to comment
Share on other sites

So today I started working more on the playfield and ran into some interesting issues. One of them was somewhat expected, especially since I really like the aesthetics of a reflected playfield...then problem is, with an Atari 2600 game, the CPU and the television interface are in a certain lock-step with each other, so to say that timing is critical is a huge understatement.

 

The issue isn't so much of a problem though, because I'm (mostly) ignoring writes and changes PF0, so that buys me a write to each player...so...problem mostly solved...but the key issue is in timing.

 

To create an asymmetric playfield, you essentially have to cheat. The system wasn't really designed to do this, but it can be achieved by exploiting a CPU and TIA timing issue. I need to write an asymmetric playfield which is also reflected. This reflection point is the PF2 register... In order to do this, I need to write a change to the PF2 register at the exact CPU (and TIA color) clock cycle when it is possible to do so.

 

This is only on one cycle.

Edited by AkashicRecord
  • Like 2
Link to comment
Share on other sites

The way that I imagine that this works (for a reflected playfield) is that the TIA reads the PF0, PF1, and PF2 registers in one direction, and then it immediately sweeps back to read them in the reverse order. [Please correct me if I am wrong. I haven't read a developer's manual.]

 

This causes a severe contention for the PF2 register at a point directly in the middle of the screen. There is a more "standard" mode that accesses PF0, PF1, and PF2 more sequentially, and also buys you more time across the scanline to make changes to PF2. I don't want to use that mode unless I have to, so I have to work this kernel to write to PF2 (when it needs to) at an exactly perfectly-timed CPU clock cycle. No more, no less.

Edited by AkashicRecord
Link to comment
Share on other sites

Here's one thing about VCS programming that is vastly different than what most anyone will ever come across:

 

You essentially have to look at your game's rendered image and find areas between the pixels in which you can execute your code.

 

Not all code will need to use the actual scanline because you get some free time (if you exhaust VBLANK) during each horizontal retrace period...you can kind of think of each scanline as the lines of a lined notebook, and the margins on the left are the retrace period. You can execute code within the margin notes, but if you want to fold the paper, you have to do it at one particular location...

Edited by AkashicRecord
Link to comment
Share on other sites

The way that I imagine that this works (for a reflected playfield) is that the TIA reads the PF0, PF1, and PF2 registers in one direction, and then it immediately sweeps back to read them in the reverse order. [Please correct me if I am wrong. I haven't read a developer's manual.]

 

This causes a severe contention for the PF2 register at a point directly in the middle of the screen. There is a more "standard" mode that accesses PF0, PF1, and PF2 more sequentially, and also buys you more time across the scanline to make changes to PF2. I don't want to use that mode unless I have to, so I have to work this kernel to write to PF2 (when it needs to) at an exactly perfectly-timed CPU clock cycle. No more, no less.

No, they are read in the same order for the Right side of the screen, PF0, PF1, PF2.

 

The timing is quite tight.

 

Image linked from Random Terrains tutorial by Andrew Davie

http://www.randomterrain.com/atari-2600-memories-tutorial-andrew-davie-17.html

 

2600_timing_02.png

  • Like 2
Link to comment
Share on other sites

That timing chart is very useful. The exact cycle you are looking for is cycle 48, meaning that your write to PF2 finishes on cycle 48. This is technically a little too early, but the TIA has the feature that it will finish drawing a full 4-cycle-width block of the playfield before drawing the new updated graphics.

 

Whenever I want to calculate where the beam will be on the screen at a particular time, I use this formula:

(CPU_CYCLE * 3) - 68 = COLOR_CLOCK (pixel location)

 

For example, on cycle 48, the beam is at "pixel 76" because (48 * 3) - 68 = 76. (Remember there are 160 "pixels", or color clocks.) You could then divide by 4 to get the playfield block if you wanted.

 

If you want the opposite information, finding out which cycle represents a particular color clock:

(COLOR_CLOCK + 68) / 3 = CPU_CYCLE

Edited by JeremiahK
  • Like 2
Link to comment
Share on other sites

I have a small 10" LCD TV that displays a fuzzy and b/w picture if I boot an NTSC or PAL60 cart on a PAL VCS. OK, a composite mod mends this, but as I am slow at soldering, this is a very tedious solution.

Not going to necrobump an old post. Many modern HDTV adc chips are actually PAL and NTSC compatible and determine the standard not by detecting the colorburst frequency, but the scanline count. My Atari diaplays in full color over NTSC analog channel 2, however if the scaline count exceeds so many scanlines, it reverts to grayscale with nasty razor thin jailbars that sync with the colorburst frequency.

 

This tells me the TV expects NTSC encoding if the framerate is closer to 60z, and PAL encoding if the framerate is closer to 50Hz. Air Raid (290 scanlines) goes grayscale on my new TCL 4k TV, where it went full color on my old Sanyo. Sorry I don't have a real PAL video source to test with.

 

So PAL60 (detected by framerate as NTSC) may not run correctly on newer international display chips even if it worked on older flat panel TVs.

  • Like 3
Link to comment
Share on other sites

Sorry for another technical post (I'll have more media later.)

Code as data, data as code... This is something that jumped out at me as I was working on the game's playfield data representation.

Normally, I'm not a big fan of premature optimization, but it might be some sort of secret Holy Grail thing for the VCS...

I noticed that the PF1 (and PF2) bitmap that I was using to draw the 2 player wells (#$E0) is the opcode for the immediate addressing mode of the CPX instruction (ComPare X.) What's even better is that this bitmap for a "default clear" line state only represents only half of the well. Throwing in the other (clear) half puts "E0" into two side-by-side RAM locations, which boils down to the following instruction:


CPX #$E0

This is comparing the X register to the immediate value "E0" which is almost exactly what I want to do to check if half of a game row is clear....

 

This recursive nature of the CPX opcode definition almost allows direct execution of my data with no change. If I load the next half needing to be checked into the X register, and with a few more clever instructions, I may be able to use this method to check the next segment after that, and so on, and so forth. I can potentially use a byte after the first failure of the clearing logic to flag where the "dirty" lines start and then use this for the lion's share of the "line clear" stuff. Combine this method with dynamically self-modifying code and we can have very tight and optimized wizard shit going on...

This method may even compress my playfield data somewhat, saving a bit of RAM, since most lines (unless you suck) should be in the clear state. ;) Since this recursively self-executing data-code will reside in RAM (as the playfield data normally would anyway), then only the lines, or the number of lines, that actually have a "dirty" flag will need to exist as a bitmap in RAM (up to a maximum of 80 bytes) and having this line clearing check "kernel" down to only a handful of bytes will help a lot for CPU usage as well.

 

I shouldn't be particularly starved of CPU in the VBLANK and OVERSCAN areas, but everything helps. I can focus more ROM and CPU on good music then. :D

Edited by AkashicRecord
Link to comment
Share on other sites

...which leads me to a question:

 

Is there any reason to shoot for a 2K ROM instead of 4K?

 

I'm afraid I don't know a lot about costs for particular cartridge formats (should it be possible for me to actually do this without being sued.)

Edited by AkashicRecord
Link to comment
Share on other sites

There is really no reason to go 2K over 4K, except for bragging rights. It has been said that one hasn't truly coded for the VCS until they have done a full game in 2K, since that was the original constraints of the system. But Eproms are cheaper now than they were in 1977...

  • Like 1
Link to comment
Share on other sites

There is really no reason to go 2K over 4K, except for bragging rights. It has been said that one hasn't truly coded for the VCS until they have done a full game in 2K, since that was the original constraints of the system. But Eproms are cheaper now than they were in 1977...

 

Yeah, it's funny how cheap things like that are now. Almost 20 years ago, I strangely ended up with many hundreds of EPROMS, for free, but I had nothing to do with them. (I even think they were the same ones found in the VCS cartridges, but I could be mistaken.) I eventually threw them away at some point for some stupid reason. I guess making "old" games never crossed my mind as something that would be even be possible...or desired.

 

I can see 3D printing cartridge case halves as being a thing, if it isn't already...

 

Edit: it seems that it is...

 

IMG_4975_display_large_preview_featured.

IMG_4984_display_large_preview_featured.

IMG_4987_display_large_preview_featured.

 

 

 

Edited by AkashicRecord
  • Like 1
Link to comment
Share on other sites

No, they are read in the same order for the Right side of the screen, PF0, PF1, PF2.

 

Really? I was referring to a reflected, or mirrored playfield which should go in the order of:

 

PF0 PF1 PF2 PF2 PF1 PF0

 

So I thought that was why there was this cycle 48 contention for PF2 in this mode, because you have to write to PF2 at exactly the same time that it is being read (and essentially locked) for the first time...and that you only have 1 specific cycle in which this can be accomplished, because it is immediately read again for a second time.

 

In a sense, it almost seems that you shouldn't even have a cycle to do this, but somehow you do...

 

I don't need to get hung up on the absolute specifics, but it does help to understand why certain things are the way they are, especially if you have a hunch.

Edited by AkashicRecord
Link to comment
Share on other sites

Here's another .bin, but the appearance won't exactly show what is actually happening under the hood... You'll.probably have to use Stella's debugger to see what is going on towards scanline 257.

 

This is the first test where a small number of bytes are manipulated in RAM as part of a seed for a dynamic self-executing mini-kernel.

 

Right now, this doesn't actually perform anything "dynamic," but it does jump to some code that is written to RAM which performs a spurious operation (CPX #$E0) and then jumps back to code which rebuilds itself (i.e.; the RAM jump that was just returned from.)

 

post-66218-0-02970100-1540096336.png

 

ramjump-test.bin

Edited by AkashicRecord
Link to comment
Share on other sites

So while this self-executing data from RAM idea is very nice and elegant, I'm going to simplify things first and press on with the more naive method(s) for checking for clear lines, etc. There's no sense in complicating the prototyping process too much, too soon...

 

As far as the playfield is concerned, I still think it is very nice to have the data representation in the exact form that the PF1 and PF2 registers will require, so that is going to be a large focus today. The majority of the game logic is just a few simple bitwise operators and bit tests (for the most part). Translating the game field coordinates into scanlines (and horizontal positioning code) is probably the most complicated and complex issue...

 

I'm also looking again at the design of the playfield. The shaded blue 2-player image a few pages back still looks the best to me. I'll probably put up a simple .bin later that draws that. Somehow I b0rked the assembly file for that one days ago, so I have to rewrite it. :P

Link to comment
Share on other sites

I love the gradient shading. It looks like, If Atari had made a version of Tetris back in the day...

 

I'm going to reattach an image from my early playfield tests a few pages back. This is close to what I'm aiming for (with a progression of colors), but the image has some glitches due to bugs. I'll try to get a bin that cycles through some colors in this fashion:

 

post-66218-0-43189200-1540131724.png

Link to comment
Share on other sites

Oh, here's an old idea that I was toying with as I was testing the vertical animation strategies, but I didn't bother to share it.

 

I had an idea to have special pieces (especially in some 2P modes) that do different things. One idea I had was to cycle the colors on the special pieces kind of like this:

 

colorcycle-test.bin

 

It's not perfect, but I kind of liked the simplicity of the effect. I could also have unfinished lines in the game that randomly light up and color cycle for some amount of time.

Link to comment
Share on other sites

I love the gradient shading. It looks like, If Atari had made a version of Tetris back in the day...

 

Atari was actually behind the Tengen Tetris that was banned and pulled from the market over licensing issues with Nintendo. That's my primary inspiration for this one, but it will be quite different.

  • Like 1
Link to comment
Share on other sites

I need to start thinking about pseudo-random number generation at some point, so no better time than now. I can also use it in debugging and testing.

 

A random number (modulo 7) was my standard method for choosing the next game piece, but all computers can only generate pseudo-random numbers...the pattern of numbers will repeat after some time.

 

Obviously, an 8-bit number is out of the question. Using 16 bits will repeat the game piece sequence after ~65,000 pieces (for the crude RNGs)...which I think isn't terrible, but it isn't optimal. (In some of my older games I used the Mersenne Twister algorithm.)

Edited by AkashicRecord
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...