-
Content Count
785 -
Joined
-
Last visited
Posts posted by ZackAttack
-
-
I think as long as there are no power or AV cables going to the cartridge it qualifies as a 2600 game. I don't think it really matters how much of the processing is offloaded onto an in cart CPU as long as the TIA is used to generate the video and audio. To me the TIA is what defines the 2600.
-
1
-
-
After watching the video I can see that I was mistaking the magic sequence for a crash. Sorry for the confusion.
..It doesn't hurt too bad to derail a thread where the only comments are by people ,who don't even like the genre of the game in question, belittling and badgering it for minor cosmetic devices when it is obviously one of the best looking 2600 games of all time (*TOOOOOOOT of one's own horn!!) (*patta pat pat on ones own shoulder)!
I love my own game and play it a LOT!
It sounds like you're more interested in having people praise your game than having honest feedback that may allow you to improve the game further. For the record, I don't care for all the screen flashing either. In fact it's the main reason I thought the game was crashing. I'm glad you love your game and I hope many others will enjoy it also.
Basic without DPC+ really limits the visual effects. Have you considered trying to write the next game in assembly to make it look even better than this one?
-
If I have some time later I'll put it on the harmony and see if I can capture it on my 7800. It would be interesting to see a short video of the game by someone who doesn't completely suck at it like I do.
-
Try holding UP and the button for a while right after the game starts. It crashes pretty bad. (I'd screenshot it, but you have to see it in real time to really appreciate how bad it is)
Try holding DOWN and the button for a while right after the game starts. Is that normal?
I'm not a fan of all the flickering and flashing either.
I do like the animation when an enemy is defated. It's cool how they shatter into pieces.
-
Maybe you could include the flipper animation state in your collision detection and only deflect the ball at full speed if the flipper is in its upswing. You could probably do that without anymore variables.
Pseudocode:
if collision(Ball, LeftFlipper) AND LeftFlipperState == MovingUp then BallDirection = UpFast
-
While playing I noticed that the ball will occasionally pass through objects when it's traveling straight up. I think it even passed through the top of the screen once. The easiest way to see this is to just hold up and watch the ball bounce around for a bit.
If you can find a way to improve the ball and flipper physics, this will be a really awesome game. Keep up the good work!
-
Reading around this thread it seems you are using a ball sprite to smooth out the pf scrolling? Seems neat.
This was the first thing I had tried but I quickly found that it wouldn't work because it takes too long to update all 3 PF registers twice per scanline. Instead I will be changing the background color at precise times and smoothing out the color change with one of the graphic objects. The platform in the last video was just the P1 object, but once I get around to implementing hills and loops you'll start to see where changing the background color mid scanline is needed.
Here's what Sonic might look like in full resolution (as are the sprites in this engine):
Nice! This will look awesome in the next demo video.
-
When they gunmen are facing right the bullets come out of their left shoulder instead of the gun barrel. You should change the starting position of missiles and balls when they face right.
-
Possible, yes. Easy, probably not.
I think the biggest challenge would be handling the possibility that all 4 ghosts and both pacmen can end up on the same scanline. In which case you can flicker each sprite every third frame. If the AI for the ghosts is clever enough it might be possible to limit the occurrences of horizontal overlap.
-
You forgot to change the caparison for setting d=1. You should also add a check for d=0 before setting d to 1 or 2. Otherwise the missle will change directions mid flight.
-
Looking good! I'd like to suggest the flippers be improved so the ball reacts differently when they're moving and stationary. Right now the game practically plays itself if you just hold up. Though even that is fun to watch. I got to 155k before I got tired of holding up

-
1
-
-
I don't see the != operator in the bb documentation. Have you tried using <> instead?
-
Yes, I'm referring to this. Just use the cycle count variable to index into the array.
if modFiveLookup[cycleCount]!=0 then goto FifthCycle
-
Off the top of my head I can think of 3 options:
1) Use BCD for the variable that holds the cycle count. Then AND it with %00001111 and check if it is equal to 0 or 5. This option will use a few more CPU cycles than the other two, but is good if you absolutely must use mod 5 and don't have a lot of ROM space available.
2) Create a 45 byte lookup table with a 1 stored in every 5th byte and 0 in the rest. Then simply index into the lookup table with the cycle count variable and compare that value with 1. This option is good if you must use mod 5 and have more ROM space than CPU cycles.
3) Make it mod 4 or 8 (or any power of 2). Then you can simply AND the count variable with n-1 to obtain mod n quickly. This is the best option to use if you can tolerate using a value that is a power of 2. Ex: cycleCount AND %00000011 is equivalent to cycleCount % 4
-
My estimate is that it would cost $15-$20 USD to construct a 1MB cartridge. Is that reasonable?
I was thinking about this some more. Using $0?3e and $0?3f provides 32 locations which can be written to for bank switching. Bank sizes from 256bytes to 4K could be supported at the same time. 16-256 banks, 8-512 banks, 4-1K banks, 2-2K banks, and 1-4K bank only requires 31 of the 32 locations. This leaves one location left over to use for a meta-bank switching scheme. Though even I will admit that going past 1MB is absurd.
003e - Select 256 byte bank located at 1000-10ff
...
0f3e - Select 256 byte bank located at 1f00-1fff
003f - Select 512 byte bank located at 1000-11ff
...
073f - Select 512 byte bank located at 1e00-1fff
083f - Select 1K byte bank located at 1000-13ff
...
0b3f - Select 1K byte bank located at 1c00-1fff
0c3f - Select 2K byte bank located at 1000-17ff
0d3f - Select 2K byte bank located at 1800-1fff
0e3f - Select 4K byte bank located at 1000-1fff
0f3f - Select meta-bank. When 1MB just isn't enough

-
I wasn't planning on support for color themes. Now that you mention it, I think it would be a good idea. Maybe I'll expand the level data so the color scheme can be controlled per tile instead of per level. That would produce a more flexible level design.Are you going to use self modifying code? Else immediate loads prevent e.g. level color themes. Or require more redundancy.
You're right. That should work nicelyI think using Y for tracking the kernel lines is possible. Since you cannot simply count it down to 0 (which would save 5 cycles), you can still compare it with a RAM variable. That saves you 2 cycles.
The demo chains the addresses of the 8 kernels and the overscan routine in memory and then uses RTS to transfer control from one to the next. I think this will end up being improved in the finished game.Why do you account the 6 RTS cycles for the kernel? Also the branch out of the loop usually takes only 2 cycles because it is not taken. Only in case the jump is taken you have an extra cycle. I suppose new kernel setup code will follow in most cases, so that single cycle may or may not matter.
I don't need a ski boat with a 315hp V8, but I still bought oneBut you don't expect to really need 1MB or so, right?
Realistically 1MB is overkill and I could make a nice game with much less space. It's just too tempting to not do it.
It's the price you pay for greatness... Seriously though, thanks for all the great advice. Now how do I convince you to do all my code reviews from now on?That's a valid point. But you miss a part of the special Atari 2600 challenges then.

-
The Y register is used by the DRAW_PLAYER macro, so if the Y register is used anywhere else in the kernel it must be saved in memory and then restored after. In this example it shouldn't be an issue since X can be used to index into the HMP1/HMM1 lookup table and to detect when the bottom of the hill has been reached.
The reads for the COLUBK could probably be immediate to save a couple cycles there.
Supporting vertical scrolling also needs to be taken into account. If the hill is scrolled off the bottom of the screen it must terminate early and jump to the overscan routine. That means those remaining 16 cycles will mostly be accounted for.
- Decrement the variable that tracks how many lines remain to be rendered by this kernel (5)
- Branch out of kernel loop (3)
- RTS to overscan (6)
Also, if the kernel performs any operation that isn't deterministic a STA WYSNC will be required to sync back up.
Regarding the question of why so much memory is required. The fast macro depends on padding each sprite out to a full page. The timing of COLUBK writes will require at least 6 kernels to scroll horizontally across the entire screen. Additional code is required when the kernel is scrolled off the top of the screen because the objects must be positioned correctly for the first visible line. Lastly, it takes a lot of time to utilize the rom space as efficiently as possible. So it seems more effective to me to just waste space everywhere and get the game working sooner. It's a lot easier to increase the size of the rom than to find more free time to work on a hobby. Oh, and arranging everything in consistent sized chunks should make the tools easier to build too.
-
So effectively you are looking for a kernel which displays multiple sprites most efficiently, right?
During a scanline there is more than enough CPU time to display two multi-colored sprites (even during repositioning one sprite). It only becomes critical when the playfield gets involved.
I suppose I must be missing something.
The basic idea is that P0 is always the playable character and everything else is used to create the background/level/enemies. P1, M1, BALL, and COLUBK are primarily used to draw the background in order to support smooth scrolling in all directions. I think a good example is a hill. It needs to have an upward slope on the left side, a section of level ground on the top, and a downward slope on the right. Here is a simple diagram to help explain. The top portion shows what the player will see. The bottom portion shows the locations of M1 and BALL in dark green on the left and right sides respectively. The light green and brown areas are drawn by changing COLUBK at the correct times. The wider the top of the hill is the more PF register would be needed to fill in that top light green section. Trying to update that many PF registers in a single line would be too time consuming. So to me it seems easier to just change the background color at the right time and use the objects to hide the coarseness of the background color changing.
-
That demo sure looks cool.
But from what I can tell, you only use the sprites? One for the player and the other one (reused) for the platforms?
So I suppose you have plans for using more objects and the playfield too?
Thanks. The plan is to use more objects as needed. The sky, platforms, grass, and dirt are all placed according to level data. So they could easily be moved around in a level editor. It's probably not that noticeable in the demo, but the platforms are all different distances vertically. For technical reasons the horizontal position must be snapped to a 32 pixel boundary. The playfield probably won't get much use because it can't be scrolled smoothly. It's main use will probably be filing in some gaps on the edge of the screen.
I actually found the idea of having several small slots for ROM banks pretty interesting. Yes, there's quite a bit of overhead, but with a scheme like this you won't be switching banks so frequently, since you'll be able to piece together all the routines and data needed for each part of the game. You can load only the graphics you'll actually use, only the part of the level that's currently on screen, the AI routines for objects that are currently active, and so on. I don't know about the technical feasibility of such a scheme, but the idea is definitely interesting.
It should actually reduce some overhead for the graphics portion at least. In the demo I'm using the RTS trick that Roland suggested to string together the 8 sub kernels. This consumes 18 bytes of ram and the SP register. If I make each sub kernel use the same entry point location within the bank I should be able to just JMP directly to the next sub kernel instead of using the RTS trick. There is also some savings in the setup code because the bank switch requires a single byte store instead of storing a two byte return address on the stack.
From my own experience I would say the opposite is true. You can only overcome the problem with massive redundancy (code and data), because usually there is quite a lot of code and data which is reused. So normally you would need access to multiple small banks at the same time. And then you have to bank switch a lot more than with larger banks.
But that's all theory. Probably ZackAttack should just start developing a game based on his specs. If it works, fine. And if not, he can still adjust the bank switching.
I've already started moving from the prototype to developing an actual game based. The goal is to have better animations and more complex background tiles by March.
-
I did see that DPC+ has the windowed mode which is essentially the same thing as what I'm doing.
The decision not to use DPC+ is really just my own personal preference. I wanted my first project to be limited to assembly and bank switching only. That said, I have absolutely nothing against DPC+ or the harmony cart. It's just not what I want to do for this specific project.
It is a lot of wasted space, but trading huge amounts of rom space for a few cycles in the kernels is part of my overall strategy to pulling this thing off.
I'm not sure what you mean by branching between kernels. But since it involves branching it is most likely out of the question.
I had considered limiting the horizontal position of P0 to the left side of the screen and then updating P0 at the end of each scanline. The biggest problem with that is it gets in the way of strobing RESP registers on the right side of the screen. So keeping it as small as possible and putting it in HBlank seemed like the best choice.
I posted a video of the engine prototype in action here. This was taken when it was still a 4k image. I could post the bin if anyone wanted it. The video already shows everything it can do.
-
If I get you right, you want to use 256 bytes for each sprite at each possible vertical position. Correct?
So one single sprite with e.g. 4 animations and 150 vertical positions would require ~154k for the pattern, plus about 1/4th for color data. So in total that would be about 192k. Probably a bit less, but still...
No, vertical position is determined by the value of Y when the rendering of the frame starts (the top SubKernel begins execution). It's 256 bytes for each sprite. The color data is only as large as the sprite. It doesn't matter what gets stored in COLUP0 when GRP0 is set to $00.
So one single sprite with 4 animations and 256 vertical positions (scrolling off top and bottom) would require 1K of graphics data and up to 256 bytes of color data depending on the height of the sprite.
-
My motivation for creating a new scheme is that I want to have the ability to swap banks as small as 256 bytes. This is the result of putting together a prototype of a graphics engine that supports 8way scrolling with 1 pixels scrolling resolution in all directions and single line kernels. The prototype fits in 4k, but that required a lot of creativity with how everything was placed in the ROM image.
P0 is only drawn once per frame, but must be able to be moved around and support a different color for each row of graphic data. My solution in the prototype was to set the Y register just before scanning started according the desired P0 location and then embed the following macro in every kernel.
DRAW_PLAYER_CYCLE_COUNT = 22 MAC DRAW_PLAYER LDA (PlayerColor),y ;5 Palette and Graphic data must be stored at page start (xx00) to keep this at 5 cycles always STA COLUP0 ;3 LDA (PlayerGraphic),y ;5 Ditto AND pPlayerSpriteMask,y ;4 STA GRP0 ;3 INY ;2 ENDM
By putting all the P0 graphic data at the same location on the 6507 address bus I can replace the indirect addressing with absolute and do away with the mask. So having a bank size of 256 bytes allows for this macro to be used instead.
DRAW_PLAYER_CYCLE_COUNT = 16 MAC DRAW_PLAYER LDA PlayerColor,y ;4 Palette and Graphic data must be stored at page start (xx00) to keep this at 5 cycles always STA COLUP0 ;3 LDA PlayerGraphic,y ;4 Ditto STA GRP0 ;3 INY ;2 ENDM
This macro works by exploiting the fact that Y can't be incremented past 255. It allows for the illusion of having a contiguous section of memory with 192 bytes of padding on each side of the graphics data. 192 + 64 + 192 is clearly more than 256, but the wrap around allows the same padding to be used on both sides of the graphics data. So if you wanted the P0 sprite to be visible on scanline 10 you would set Y to 245 just before the frame starts. This causes 0 to be stored in GRP0 for the first 10 lines while Y is 245-255. On the 11th line Y is incremented to 0 thanks to the wrap around and now the actual graphics data stored at the beginning of the page is copied to GRP0. After Y is incremented past the graphics data it will once again point to the 0's in the rest of the page which effectively disables P0 again after it is drawn.
A neat side effect of this approach is that all sprite graphic data is stored right side up.
-
Page crossing is a major problem when I have an absolute indexed instruction in a kernel. The absolute address must point to the beginning of a page or the instruction will not have a deterministic execution time.
I could merge the first 4 banks into a single larger bank giving 1K sized banks. One of the 256 byte banks could be RAM allowing up to 32KB of RAM.
That would allow the 1MB to be divided up as follows:
4 banks merged into a single 1K bank providing room for up to 256KB of code
1 bank of RAM up to 32KB
8 banks will be used to store up to 256 subkernels. Each subkernel will be duplicated and relocated into each of the 8 banks. This will allow up to 8 subkernels to be used in a single frame in any combination.
1 bank will hold up to 256 P0 sprites. Each page will hold one sprite that is up to 64 bytes. The remainder of the page will need to be padded with $00.
1 bank will hold the corresponding color data for the P0 sprites. There will be some extra space available to use in these pages for code or data.
1 bank will hold level data
Obviously this will have a lot of wasted space, but I think it's necessary to allow the kernels to meet such demanding timing constraints.
-
So there probably won't be much interest in this then. Maybe that will change once a game gets released on it.
I guess I should have mentioned that the existing schemes won't work for me because I am designing my engine to be split up into 256 byte chunks. All the existing schemes use banks that are much larger from what I can tell. I figure if everything is built to fit into a 256 byte bank that is always page aligned, there won't be any concern about throwing the timing off by crossing a page boundary. An added bonus is that is should allow me to shave a few cycles off my P0 rendering macro. It'll be so nice to gain a few cycles during H-blank.
I made some modifications to stella and my codebase to support the 1MB scheme I outlined above. It seems to work ok in the emulator. Dasm made it really easy to generate a 1MB file. I'll try it out on the encore once I can find the details on how to add new banking schemes to it. So far I haven't had any luck with that.


The Gizzle Wap and The Strange Red Tree (Final Version)
in batari Basic
Posted
Great job. The graphics look awesome. Have you noticed that the random fruit drops sometimes get placed off the side of the level in the PF0 region? Bitwise ANDing with $7F (127 decimal) and ADDing $10 (16 decimal) should allow you to quickly put your random X coordinate in the desired range of 16-143. Hope that helps.