Jump to content
IGNORED

Using overscan to store data?


deepthaw

Recommended Posts

Guy on reddit made this claim:

 

Fun Fact: The Atari 2600 had so little memory that some creative devs used TV scanlines above the viewing area (usually for factory testing) as memory registers, giving them extra space to store bits as black or white pixels.

 

And is using some links that talk about using VBLANK and HBLANK to run code as support.

 

Is this a thing? 2600 has no frame buffer, no VRAM, and hence no way to even know what was drawn to a scanline once its drawn, right?

Link to comment
Share on other sites

Not possible, the TIA registers are either read only or write only, so no way to save anything.

 

I think there are some bits unused in PIA 6532. Besides that you have only the 128 bytes of RAM to save things.

 

From the very moment the guy said "storing things in scanlines" he is nuts, no way something from TV returns to Atari or any other console.

 

Edit: Point us to the reddit thread so we can fall over the guy :P

  • Like 1
Link to comment
Share on other sites

Nobody did that back in the day, nor does anybody do it today.

 

In theory you could use the display objects to save the state of a few bits heading out of the visible screen, and pull the bits back later using other objects and the collision registers. In practice, it's enough of a pain to not make it worth the trade-off in ROM and cycles.

  • Like 3
Link to comment
Share on other sites

I tried explaining it, but he insists everybody else is wrong and furthermore - everybody who disagrees is all the same person who's been stalking him and he's reporting us all to the mods?

 

I think he has it confused with storing data in the vram of other computers/consoles (which people apparently did) and doesn't realize that the references to scanlines in VBLANK and HBLANK and overscan are being used as shorthand for a number of processor cycles.

Link to comment
Share on other sites

In theory you could use the display objects to save the state of a few bits heading out of the visible screen, and pull the bits back later using other objects and the collision registers. In practice, it's enough of a pain to not make it worth the trade-off in ROM and cycles.

Not even in theory. As explained above, the TIA registers are either read or write only and there is no screen buffer. So you cannot store anything in the display objects.

 

The only option would be to read set TIA object bits by checking them bit by bit through collision registers. But that's extremely inefficient and AFAIK has never ever been used.

  • Like 1
Link to comment
Share on other sites

Not even in theory. As explained above, the TIA registers are either read or write only and there is no screen buffer. So you cannot store anything in the display objects.

 

The only option would be to read set TIA object bits by checking them bit by bit through collision registers. But that's extremely inefficient and AFAIK has never ever been used.

Uh, yeah. That's why I mentioned the collision registers specifically, when I said "in theory".

  • Like 2
Link to comment
Share on other sites

Obviously the other objects are what you have to collide with to set the collision flags.

Despite this being the dumbest thing you could probably do with the VCS, you could actually sacrifice some objects to gain a few more bits of memory. At least it sounds feasible in theory.

Position the ball to whatever value you'd like to retain (0-159), disable the ball during field rendering and enable it in overscan, use PF and/or P0 to find the current position of the ball. Don't forget collisions are only detected when vblank is disabled. So you'd need to color everything black during the detection phase to avoid visible artifacts.

Of course in practice you will run out of the 5 objects before you run out of the 128 bytes of RAM, that guy clearly has no grasp of reality, and there almost is certainly no game that has ever or will ever attempt to do this. But you could if you wanted to :grin:

 

Actual Fun Fact: Coloring everything black and using collision registers to readback the value of a TIA register is used by the bus stuffing driver to detect which data bits can be successfully bus stuffed or not.

  • Like 4
Link to comment
Share on other sites

Obviously the other objects are what you have to collide with to set the collision flags.

 

Despite this being the dumbest thing you could probably do with the VCS, you could actually sacrifice some objects to gain a few more bits of memory. At least it sounds feasible in theory.

 

Position the ball to whatever value you'd like to retain (0-159), disable the ball during field rendering and enable it in overscan, use PF and/or P0 to find the current position of the ball. Don't forget collisions are only detected when vblank is disabled. So you'd need to color everything black during the detection phase to avoid visible artifacts.

 

Of course in practice you will run out of the 5 objects before you run out of the 128 bytes of RAM, that guy clearly has no grasp of reality, and there almost is certainly no game that has ever or will ever attempt to do this. But you could if you wanted to :grin:

 

Actual Fun Fact: Coloring everything black and using collision registers to readback the value of a TIA register is used by the bus stuffing driver to detect which data bits can be successfully bus stuffed or not.

 

You've described the referenced display objects which include the partial frame buffer (20 bits) and the 5 sprite buffers.

 

We can read the display objects back with the collision registers as you explained but the "other objects" cannot be considered the display objects without a circular reference.

 

Agree the reddit posters idea was valid and also unlikely, though any game that uses the collision registers (99.9%) is reading the write-only video RAM register buffers (not really write only).

Link to comment
Share on other sites

In theory you could use the display objects to save the state of a few bits heading out of the visible screen, and pull the bits back later using other objects and the collision registers. In practice, it's enough of a pain to not make it worth the trade-off in ROM and cycles.

I either did this or was going to do this in one of my hacks. I can't find or remember the rom though. It was a weird situation where I just needed a few bits more of ram. This was long ago...

  • Like 1
Link to comment
Share on other sites

I either did this or was going to do this in one of my hacks. I can't find or remember the rom though. It was a weird situation where I just needed a few bits more of ram. This was long ago...

I might have to eat my words that "nobody does it today" :D

 

This topic has me thinking that we should have a thread somewhere dedicated to weird storage locations. I recall that you cleverly repurposed the 6507 interrupt flag for the star-map display state, with your Starmaster 2-button hack. So long as you stay away from adc/sbc you could do the same with the decimal mode flag. (that would make more sense on the NES, though I doubt you'd ever be scrounging for bits there) There's always the RIOT port bits. I'm pretty sure you could repurpose the capacitor charge-up as a flag, but without a resistor/paddle you wouldn't be able to unset it.

 

I'm sure there are lots of other entertaining ways one might scrounge for bits that I haven't encountered or considered.

  • Like 2
Link to comment
Share on other sites

Thinking about it... If you store data in the PFx registers, you get 20bit of storage. If you read it out using a comb consisting of all five objects, spaced 16 pixels, you should be able to read out all 20 bits in five lines (one line positioning + setup, four lines readout with late HMOVEs). However, you lose about 350 bytes of ROM for the readout code, and you cannot use playfield or ball while data is stored :P

Edited by DirtyHairy
  • Like 6
Link to comment
Share on other sites

I wonder about the maximum storage in TIA:

  • 20 bits for playfield
  • 2*2*8 = 32 bits from GRP0/1 (including shadow registers)
  • 2*2 = 4 bits bits from ENAM0/1 (including shadow registers)
  • 2*1 = 2 bits from ENABL (including shadow registers)
  • 5*4 = 20 bits from HM registers
  • 5*7.x = ~37 bits from object positions
  • 4 bits from CTRLPF (I don't think it is possible to check the score bit)
  • 2*5 = 10 bits from NUSIZ0/1
  • 2 bits from RESPM0/1
  • 2 bits from REFP0/1
  • maybe 1 bit from VSYNC
  • maybe 3 bits from VBLANK
  • ...anything I missed?

That's 137 bits in total. Minus the bits required for the object to check collisions (e.g. BL). So maybe 125 bits or 15.x bytes.

 

Eventually we should start a challenge who can store the most bits and read them most efficient. :)

Edited by Thomas Jentzsch
  • Like 8
Link to comment
Share on other sites

I wonder about the maximum storage in TIA:

  • 20 bits for playfield
  • 2*2*8 = 32 bits from GRP0/1 (including shadow registers)
  • 2*2 = 4 bits bits from ENAM0/1 (including shadow registers)
  • 2*1 = 2 bits from ENABL (including shadow registers)
  • 5*4 = 20 bits from HM registers
  • 5*7.x = ~37 bits from object positions
  • 4 bits from CTRLPF (I don't think it is possible to check the score bit)
  • 2*5 = 10 bits from NUSIZ0/1
  • 2 bits from RESPM0/1
  • 2 bits from REFP0/1
  • maybe 1 bit from VSYNC
  • maybe 3 bits from VBLANK
  • ...anything I missed?

That's 137 bits in total. Minus the bits required for the object to check collisions (e.g. BL). So maybe 125 bits or 15.x bytes.

 

Eventually we should start a challenge who can store the most bits and read them most efficient. :)

You should also include what is available outside TIA. Off the top of my head with the proper setting and kernel implementation these should all be available:

 

- Stack Pointer (1 byte)

- INTIM (1 byte)

- TIMINT (1 bit)

- Status Register (4 useful bits - the interupt flag, overflow flag, decimal mode, and carry flag)

- SWCHB (the 3 unused bits - D5, D4, and D2)

- The upper 3 bits of the high address of the Program Counter

 

RevEng's idea of dumping the latches is also a good one that I never considered.

 

I did not include SWCHA as those bits can be pulled down by a control. However thinking about it now I did use the right port for storage in all of my trackball hacks. With SWCHB I have tried it and it works although someone pointed out it is shortening those bits to ground so I went away from it.

 

One of the most useful ways to preserve bits is to use the high three bits of any pointer address as they are all mirrors. I've used that in some of my hacks when ram was limited, but my first goto's are always the stack pointer and timer as they give a whole byte each and are usually easy to implement.

  • Like 3
Link to comment
Share on other sites

I have actually thought about the possibility of using the collision registers as "RAM".

I once wrote a 6-digit score display that pulled graphics from the stack, and the loop would break when the stack wrapped around to the collision registers. In order for it to work, I had to position objects to collide correctly to set the bits. It was a major pain in the neck, and it was only to save 1 byte of RAM. I am glad to say I have since found a much better solution.

But, yes, you could totally write a complicated routine that would waste many scanline of overscan to use maybe 1 of the collision registers as a 6-bit chunk of "RAM". Although now that I think about it, as soon as the kernel was drawn, it would corrupt your value. So it would only be able to be used during overscan / vblank.

Which is all to say that there is a reason nobody has ever actually done this before.

Link to comment
Share on other sites

I have actually thought about the possibility of using the collision registers as "RAM".

I once wrote a 6-digit score display that pulled graphics from the stack, and the loop would break when the stack wrapped around to the collision registers. In order for it to work, I had to position objects to collide correctly to set the bits. It was a major pain in the neck, and it was only to save 1 byte of RAM. I am glad to say I have since found a much better solution.

But, yes, you could totally write a complicated routine that would waste many scanline of overscan to use maybe 1 of the collision registers as a 6-bit chunk of "RAM". Although now that I think about it, as soon as the kernel was drawn, it would corrupt your value. So it would only be able to be used during overscan / vblank.

Which is all to say that there is a reason nobody has ever actually done this before.

 

Definitely awkward for traditional Atari games as you described but not necessarily so for 32-bit ARM games, the bus stuffing driver uses the collision registers to read the bits back for processing.

 

It's innovative because no one has done it before for practical application, which necessarily means folks who are advanced in the field will make fun instead of considering the new idea like you saw initially in this thread and on reddit. That happens a lot with innovation, even in peer review. Part of the phenomena is that many folks learn without thinking for themselves, only copying other peoples ideas.

 

The VCS has incredibly open ended architecture, so new discoveries are still possible including modern discoveries with broad application.

Link to comment
Share on other sites

 

Eventually we should start a challenge who can store the most bits and read them most efficient. :)

 

This reminds me of a technique I used on the NES. I used fractions of a bit to store values. For example, I'd store the level (values 0-8 ) in 3.17 bits, and the # lives (0-4) in 2.33 bits. Thus, only 5.5 bits required to store the two. I was pretty pleased with this, and actually wrote to Don Knuth about it. About 6 months later I got a personal message from him, through his secretary's email... telling me it was a known technique but very pleasing.

 

Normally to store a value 0-4 you'd need 3 bits (giving values 0-7, but values 5 and 6 unused), and for 0-8 you'd need 4 bits (giving values 0-15, with 9-15 unused). That's a total of 4+3 = 7 bits needed for the two value ranges. In this example, only 5.5 bits needed... I'm saving one whole bit... but actually 1.5 bits if we're counting fractional bits, which I am :) For this two-range example it's not much saving but when you start having 10 or so value ranges, you can actually pack a whole lot more into very few bits. Fewer than you'd think! With this very simple example, if I had 7 bits not only can I store the original values 0-4 and 0-8 but I can also put a YES/NO flag in there using 1 of those 1.5 spare fractional bits.

 

I'm sure someone will figure out how I did this. I'm pretty sure I've explained it before.

Edited by Andrew Davie
  • Like 5
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...