Jump to content
  • entries
    41
  • comments
    373
  • views
    63,851

2600 compression


batari

2,287 views

Now that Gingerbread Man is done, I am already starting to think about the next project, or more accurately, working on my previous project (Superbug.) Also, a few people tried out a demo cart of the game at NWCGE and it was generally well-received.

 

The biggest problem with the game, I think, is that the track data takes a lot of space. Right now, each 128x128 track takes up 2k, and the supporting data, like powerup locations and the code to read the track data fills out enough additional space that only one track can fit per bank. With 3 banks already nearly full of program code, I'd only have 5 tracks in a 32k game, which would get boring quickly.

 

The arcade game uses a 256x256 random track, made from pieces of pre-defined curves and straightaways, and a few pieces containing sand, an oil slick, and another bug parked on the side of the road. (I also made a MAME infinite time and invincibility cheats to help examine how the game works.) It probably would be possible to make my game work this way and it would solve the space issues.

 

I'd prefer the bitmapped tracks, the only way these will work with a good number of tracks is to use a 64k or larger board (e.g. EF bankswitching) and/or some sort of compression. A compression method would need to be fast, maybe even faster than reading bitmapped data. The decompressor shouldn't be cumbersome in size or cycles, it should compress at least 50%, preferably more, and should be fully capable of random access of compressed data without huge loops. Unfortunately, I know of only one compression scheme that can be adapted to have all of these properties - RLE.

 

My best guess is RLE would either need to have a fixed number of "runs" in a line, say 8 (currently 16 bytes per line are needed) so data could be accessed randomly and the only traversal is a check of the numbers in the set of "runs." This would unfortunately have a large number of unused bytes.

 

Or, put in a table of addresses that contain the start of each line, though this would need 256 bytes in itself for a 128-high bitmap. Still might reduce to <1k though.

 

Well, if not compression, I'd be willing to invest in larger boards, though I'd probably prefer something based on 0840 to simplify the bankswitching logic to use two TTL chips and a 32-pin EPROM (128K-512k.) Supercat had this to say about it:

Even a lowly 16V8 would have no problem supporting a 64-bank scheme with 08xx hotspots. A 22V10 could push it up to 256 banks. That would be a megabyte worth of code. The same result could be achieved using two common TTL chips (a 74HC00 and a 74HC373, or any of many other combinations). The advantage of 0840 is that it doesn't require programming a PLD before assembly.

Also mentioned was adding an EEPROM to a board, which is also a good idea for a number of reasons, but I'm not sure would be fast enough to handle the 4-way scrolling in Superbug.

41 Comments


Recommended Comments



RLE could might work sort of okay for tracks up to 256 wide if you keep separate pointers for the top and bottom of the visible window. Each RLE 'item' would be two bytes (left and right side of the 'extent'). The first item for each row would have the two bytes reversed. If you know where the top row is stored and where the bottom row is stored, you could search through memory reasonably well to find the next/previous row. But using two bytes per RLE item wold probably limit your savings. Further, making horizontal motion fast might be a challenge, though it you had a few pixels of buffer outside the screen you could probably manage. Of course, memory is very tight so that may be easier said than done.

 

Also mentioned was adding an EEPROM to a board, which is also a good idea for a number of reasons, but I'm not sure would be fast enough to handle the 4-way scrolling in Superbug.

 

If you use a 24FC-series EPROM with a suitable PLD, I would expect that you could read data at about 50 cycles/byte. That would certainly be fast enough if you had two copies of the maze stored in EEPROM--one in 'row major' format and the other in 'column-major'. If there's just one copy of the maze data, things could be trickier especially if you need to work in limited RAM.

Link to comment
RLE could might work sort of okay for tracks up to 256 wide if you keep separate pointers for the top and bottom of the visible window. Each RLE 'item' would be two bytes (left and right side of the 'extent'). The first item for each row would have the two bytes reversed. If you know where the top row is stored and where the bottom row is stored, you could search through memory reasonably well to find the next/previous row. But using two bytes per RLE item wold probably limit your savings. Further, making horizontal motion fast might be a challenge, though it you had a few pixels of buffer outside the screen you could probably manage. Of course, memory is very tight so that may be easier said than done.

I see what you're saying - The track rendering doesn't really need random-access except for the initial drawing of the screen, and everything else is sequential. So a table of pointers isn't strictly needed if we just search a few bytes up or down through the table. That does require 4 bytes of RAM for the pointers. So essentially we'd be trading 4 bytes of RAM for 256 bytes of ROM (or 512 for a 256x256 track.) Hmm.

 

256x256 tracks would be nice. I'll do some initial tests of compression ratios for that size, with and without a table. If I can fit two 256x256 tracks in a bank, preferably without needing more RAM, I'd be happy with that, as not only would the tracks be 4x the size, there would be 10 of them in 32k.

 

If anyone wants to whip up some sample 256x256 monochrome bitmaps with varying complexity, I'll write a simple compression utility and see how well they compress. (See an earlier blog entry for a 128x128 track I posted to get an idea about what a suitable road width might be.) The track was drawn with Windows Paint in under 10 minutes and I just used a standard "brush" to map out the road, though I don't remember which one.

Also mentioned was adding an EEPROM to a board, which is also a good idea for a number of reasons, but I'm not sure would be fast enough to handle the 4-way scrolling in Superbug.

 

If you use a 24FC-series EPROM with a suitable PLD, I would expect that you could read data at about 50 cycles/byte. That would certainly be fast enough if you had two copies of the maze stored in EEPROM--one in 'row major' format and the other in 'column-major'. If there's just one copy of the maze data, things could be trickier especially if you need to work in limited RAM.

What sizes are available in the 24FC-series?

Link to comment
If anyone wants to whip up some sample 256x256 monochrome bitmaps with varying complexity, I'll write a simple compression utility and see how well they compress.

Here's a quick 'n' dirty one.

 

test_track_256.png

 

Before I do any others, I'm curious to see how well this works in terms of corner radius, road width, and such.

 

Incidentally, I came up with a real easy way to make these in Photoshop. I can post a how-to at some point, if you'd like. Actually, it'd be pretty easy to "import" tracks from other games (Pole Position II, for example). Anything that has an overhead map.

Link to comment
More fun. This time - switchbacks!

 

The former would compress to about 512 bytes total. The latter would be over 4K.

Instead of standard RLE (which seems to need the "run" and the "value"), I'm just encoding the "run" and I can encode the first pixel on/off in a single bit somewhere.

 

So my program, assuming it's correct, reads a 256x256 Windows monochrome .BMP file - it skips the header and just reads when it a run switches state, and it assumes that the first bit will be encoded somewhere else.

 

This is what I have:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
 int i,x,y,bit,currval,testval;
 int bytecount=0;
 char c;
 for (i=0;i<62;++i) c=getchar();  // skip BMP header
 for (y=0;y<256;++y)
 {
for (x=0;x<32;++x)
{
  c=getchar();
  if (!x) currval=(int)c>>7;
  for (bit=0;bit<8;++bit);
  {
	testval=(int)c>>7;
	if (testval!=currval)
	{
	  bytecount++;
	  currval=testval;
	}
	c<<=1;
  }
}
 }
 printf("\n%d\n",bytecount);
}

The results from the sample bitmaps are: 2059, 3804, 304, and 4766. The compression ratios are better than I expected, but probably aren't good enough to fit two typical 256x256 tracks in a single bank.

 

EDIT: fixed bug in program, but the fix didn't change the results :lol:

Link to comment

Rotating #2 by 90 degrees squeezes it down to 3108 bytes - making it feasible to put in a bank.

 

Regardless, RLE-compressed 256x256 tracks aren't going to work with just 32k, because I'm still probably limited to 5 tracks, even if they are bigger. I could probably do more than 10 128x128 tracks but now I'm liking the idea of 256x256.

 

So we're back to bigger carts, but before I go there, I'll try to get a demo worked up that uses an RLE implementation.

Link to comment

Instead of coding the whole track, why not generate is on the fly? Like River Raid does?

 

Add some parameters which allow different track characteristics (long straights, many curves, etc.) and you get an almost unlimited number of tracks within minimal space.

 

Then you either select a couple of them and only have to store the parameters and random seed. And/or add an option menu, where the user can select those parameters and start a random track.

 

If you like predefined tracks, why not only storing the track deltas? Give it a start point and describe the track from that on (e.g. left and right border, various angles and lengths).

Link to comment

I haven't played the binary that much but the 128x128 tracks have seemed pretty big to me, I wonder if a 256x256 track would be too big?

 

Since the visible "window" is 16x11, 128x128 translates to 8x12 screens worth of track.

 

I think some playtesting would be a good idea before you commit to 256x256.

 

And also - is it possible to have races be multiple laps instead of a single circuit?

 

Or are we deviating too far from a straight port now...?

Link to comment
I haven't played the binary that much but the 128x128 tracks have seemed pretty big to me, I wonder if a 256x256 track would be too big?

 

Since the visible "window" is 16x11, 128x128 translates to 8x12 screens worth of track.

I think it would be best to support 256x256 if possible. Bigger tracks would be a bigger selling point for the game. Besides, you can always make the track area within the bitmap smaller, and presumably it would compress down to a much smaller size. So this gives the option of having a lot more variations in track sizes. Variation is a good thing.

 

Also, and this is a guess, I would suspect you could probably make a 256x256 bitmap, but have it include four separate 128x128 tracks. The program would just need to know which part of the bitmap was being used for a given track selection.

 

test_track_256_multi.png

Link to comment
I think it would be best to support 256x256 if possible. Bigger tracks would be a bigger selling point for the game. Besides, you can always make the track area within the bitmap smaller, and presumably it would compress down to a much smaller size. So this gives the option of having a lot more variations in track sizes. Variation is a good thing.

Good points, and I would agree with you, except for the ROM limitations issue; it may be a better game with 10 128x128 tracks than with 4 256x256 tracks.

Link to comment
Instead of coding the whole track, why not generate is on the fly? Like River Raid does?

 

Did you consider that Superbug is a top-down perspective?

 

Add some parameters which allow different track characteristics (long straights, many curves, etc.) and you get an almost unlimited number of tracks within minimal space.

 

I mean since you're not mentioning details like making closed tracks :lol:

 

If you like predefined tracks, why not only storing the track deltas? Give it a start point and describe the track from that on (e.g. left and right border, various angles and lengths).

 

That should work, kinda like drawing the Star Fire logo :)

Link to comment
Good points, and I would agree with you, except for the ROM limitations issue; it may be a better game with 10 128x128 tracks than with 4 256x256 tracks.

But... if you could have four 256x256 bitmaps with up to 4 128x128 tracks each, you could have a combination of the two. Like eight 128x128 "normal" tracks (2 bitmaps) and two 256x256 "super-sized" ones (the other 2 bitmaps). You could even have one of the bitmaps be a series of mini-tracks that are even smaller...

 

test_track_256_multi2.png

Link to comment

Generating tracks on the fly is exactly what the arcade game does. It works, but the tracks it creates are not as flexible as a bitmap would be. That, and the powerup placement probably wouldn't be ideal. I'm not saying I won't do this, as random tracks might be neat, but I wouldn't make it the only way one could generate a track.

 

A straight port is definitely not what I had in mind, I mean, the real game is sorta fun but I want to add some gameplay elements to make it more fun. I think I'm being faithful enough to the original, while changing some of the less desirable characteristics (like the annoyingly long, seizure-inducing crashes, and amazingly slow acceleration.) I've also thought about a 2-player split screen option, though it's basically going to require flicker.

 

I will try out those multiple-track bitmaps and see how well they compress. That may be a deciding factor. If they work, the small tracks could be called racetracks, i.e. intended for multiple laps, while any large tracks could be classified as rally courses or something like that. A reasonable number of tracks would still fit if I did it this way.

Link to comment

I would think that if you could have a column or two on each side, and a row or two on the top and bottom, that weren't displayed, you might be able to manage some much better compression using a 'deltas' approach. Store the outlines of the track as a sequence of "steps" (e.g. straight, straight, right, straight, left, straight, straight, etc.) One approach would be to use eight directions but assume that there would never be any 90 degree bends; every two bits would choose from: "one forward", "one forward then left", "one forward then right", or "two forward". You would need a drawing routine which would ignore any points that were outside of the screen bitmap.

 

Even with an optimized drawing routine, you wouldn't be able to draw the entire track in a reasonable amount of time. To allow for that, you would probably need to divide the track into squares; you'd have a table which listed, for each square, which portions of the track should be drawn while the vehicle was within that square. For example, if the squares were 16x16, your table might say to draw track data starting at $1523, at coordinate 12,56, facing upward, and draw 23 pixels; use data starting at $1915, at coordinate 15,83, facing upward, and draw 28 pixels. Using larger squares would decrease the storage required for the index, but would increase the amount of drawing that one had to do for each refresh.

 

The amount of storage required for the track would be quite reasonable--especially if the track was fairly sparse. The question would be whether one could draw stuff fast enough that one could get by without an overly dense index.

 

Incidentally, one advantage of this approach would be that one could have tracks that overlap themselves (e.g. figure-eights) provided that the overlapping portion scrolled completely off-screen between visits. There's still a lot of trickiness reqiuired for stuff, though.

Link to comment

To draw the walls in SuperBug, do something like:

 

	   ; X and Y holds current X and Y positions

   ; Part 1 (moving upward)

AllDone_Up:
	rts
Up_1x:
	asl	 shifter
	bcs	 GoUp
GoUp2:
	dey
	jsr	 putpixel
GoUp:
	dey
	jsr	 putpixel
	asl	 shifter
	bne	 LoopUp

FetchUp:
	jsr	 fetch_shifter  ; Fetch byte from appropriate bank, set carry, 
						   ; rotate left, and store in shifter
	beq	 AllDone_Up
LoopUp:
	bcs	 Up_1x
Up_0x:
	asl	 shifter
	bcs	 GoUpLeft
	bcc	 GoUpRight

   ; Part 2 (moving up and to the left)

AllDone_UpLeft:
	rts
UpLeft_1x:
	asl	 shifter
	bcs	 GoUpLeft
GoLeftUp2:
	dex
	dey
	jsr	 putpixel
GoUpLeft:
	dex
	dey
	jsr	 putpixel
	asl	 shifter
	bne	 LoopUpLeft

FetchUpLeft:
	jsr	 fetch_shifter  ; Fetch byte from appropriate bank, set carry, 
						   ; rotate left, and store in shifter
						   ; Return zero if I've fetched enough
	beq	 AllDone_UpLeft
LoopUpLeft:
	bcs	 UpLeft_1x
UpLeft_0x:
	asl	 shifter
	bcs	 GoLeft
	bcc	 GoUp

   ; Ignoring parts 3-8 (other directions) but they're similar.

putpixel:
	bit	 INTIM		  ; Is our time almost expired?
	bmi	 pp_nokernel
	stx	 draw_x
	sty	 draw_y
	jsr	 KERNEL
	ldy	 draw_y
	ldx	 draw_x

pp_nokernel:
	cpy	 #12			; Number of rows
	bcs	 kernel_exit
	cpx	 #32			; Number of columns
	bcs	 kernel_exit
	lda	 ptrs_list,x
	sta	 write_ptr
	lda	 (write_ptr),y
	ora	 mask_list,x
	sta	 (write_ptr),y
	rts

 

There's one part of the code for each of eight directions (only two parts shown). If two bits in the shape table are "11", it should move straight one pixel. If "10", two pixels. If "01", turn left by one direction and move straight one pixel. If "00", turn right by one direction and go straight one pixel.

 

If the "KERNEL" call decides to scroll the screen, it should adjust "draw_x" and "draw_y" appropriately.

Link to comment
Instead of coding the whole track, why not generate is on the fly? Like River Raid does?

Did you consider that Superbug is a top-down perspective?

Just like River Raid, where is the problem? :lol:

Link to comment
Instead of coding the whole track, why not generate is on the fly? Like River Raid does?

Did you consider that Superbug is a top-down perspective?

Just like River Raid, where is the problem? :lol:

 

River Raid is on rails, not freely scrollable 8-ways.

Link to comment
For example, if the squares were 16x16

That gives me another idea:

 

How about defining a lot of small tiles in detail? Each tile consists out of a part of a track (straight, narrow bend, wide bend, etc.), and can be rotated in 90° steps.

 

Then you construct your track by using those tiles.

Link to comment
River Raid is on rails, not freely scrollable 8-ways.

Yes, that makes it a bit easier. But IMO 2D scrolling would still allow a similar, though a bit more sophisticated apporach.

Link to comment
River Raid is on rails, not freely scrollable 8-ways.

Yes, that makes it a bit easier. But IMO 2D scrolling would still allow a similar, though a bit more sophisticated apporach.

 

I don't see how you want to unpack a random closeup window of the track based on a seeded algorithm, unless you're talking fractals or so?

Link to comment

I do have one row on the bottom that isn't displayed. However, I don't know where I'd find the RAM to allow for all of those extra rows and columns (I'd need 15 bytes by my estimate.)

 

I do like the deltas idea presented here as a compression method and I think I've got a handle on your sample code. I think it's trying to draw the whole screen at once? I like that idea if it can be made fast enough (not sure I like the idea of skipping frames, as that would slow down the game.)

 

The game as it is now does not draw the whole screen at once - just the parts that need to scroll in, and the rest of the screen is scrolled L/R or U/D, even both in the same frame if needed, and it was made to run without skipping frames by unrolling loops.

 

If deltas could also work on just the screen edges w/unrolled scrolling loops, that might work.

 

The question is whether deltas can represent all of the above tracks? Can they do things like hairpin turns?

Link to comment

Figure-eights would indeed be nice. Sprintmaster has them, if memory serves.

 

Any interest in using sprites for the power-ups? I know it would cause flicker, but only when they were in-line with the bug. Careful placement could minimize that. As it is, I have a hard time telling what the different ones are since they tend to come into view so fast.

 

Another option, would be to use the area next to the timer to display the sprite for the next power-up that's on the track, and just use a single missile/ball icon for all power-ups within the track.

 

Another thought... a "novice" mode, where an arrow would be displayed next to the score, showing which way the next turn would go. (Sort of like a navigator in rally games.)

 

Finally (for now :) )I probably mentioned this elsewhere, but some other racing game options would be nice. Being able to choose the color of your car, and maybe unlocking bonus cars, and a tournament (or career) mode - using the AtariVox for saves - where you have to beat certain times (or reach other goals, like not crashing, etc.), on several or all of the tracks, for a cumulative score. That stuff always add a lot of replay value to racing games - at least for me. :lol:

Link to comment
For example, if the squares were 16x16

That gives me another idea:

 

How about defining a lot of small tiles in detail? Each tile consists out of a part of a track (straight, narrow bend, wide bend, etc.), and can be rotated in 90° steps.

 

Then you construct your track by using those tiles.

Yup, that's how the arcade game works. I should have said "tiles" because that's what they are.

 

The actual game uses a 6502. Maybe disassembly would tell us more.

 

EDIT: It uses a 6800. I don't know why I thought 6502?

Link to comment
Figure-eights would indeed be nice. Sprintmaster has them, if memory serves.

 

Any interest in using sprites for the power-ups? I know it would cause flicker, but only when they were in-line with the bug. Careful placement could minimize that. As it is, I have a hard time telling what the different ones are since they tend to come into view so fast.

I want to avoid flicker unless absolutely necessary.
Another option, would be to use the area next to the timer to display the sprite for the next power-up that's on the track, and just use a single missile/ball icon for all power-ups within the track.

I like this idea.

Another thought... a "novice" mode, where an arrow would be displayed next to the score, showing which way the next turn would go. (Sort of like a navigator in rally games.)

That would work (not necessarily by the timer, however, but possibly in a HUD of some kind just above the score, which could also include the powerup thingy and maybe some other game info.)

 

Finally (for now :) )I probably mentioned this elsewhere, but some other racing game options would be nice. Being able to choose the color of your car, and maybe unlocking bonus cars, and a tournament (or career) mode - using the AtariVox for saves - where you have to beat certain times (or reach other goals, like not crashing, etc.), on several or all of the tracks, for a cumulative score. That stuff always add a lot of replay value to racing games - at least for me. :lol:

All possible, depending on how much RAM I can find.

 

Advancing to other tracks only upon meeting criteria, like beating time and/or not crashing works, but what other goals can you think of?

Link to comment

Guest
Add a comment...

×   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...