Jump to content
IGNORED

Panel de Pon - WIP


NaOH

Recommended Posts

I started on a homebrew Panel de Pon port. It uses the 4-colour technique similar to what is laid out here. The game rewrites its own code in real-time by writing 6502 opcodes to the 1kb cartridge ram included in a CV cart. This allows it to hammer the COLUBK register to change colours. Only 4 colours per scanline are effectively possible with this technique, as three of the colours are stored in X,Y, and A and a fourth colour (black) is possible by using the undocumented SAX opcode to write A&X, which is black, to COLUBK (we choose colour A and colour X such that they have no bits in common, thus A&X = 0).

 

ataripon.gif

Here's the cartridge RAM in Stella, marked up to show the instructions:

store_colubk.png

Unfortunately, it's a bit buggy. I am pretty convinced this is due to some unusual behaviour in the cartridge ram. I notice in the source code for Stella that the sta ($abcd),y opcode (which I use to rewrite the code in the ram cart) will "poke" memory at address $ab+(cd+y & 0xFF) before "writing" to memory $(abcd+y). I don't know what "poke" means in this context, as it clearly doesn't mean "write," but evidently it is corrupting memory sometimes. However, write-breakpoints in stella don't seem to detect this form of memory manipulation, so it's hard to debug. I tried adjusting the code so that y is always 0 during this sta ($abcd),y operation, which ought to avoid this corner case, but oddly it didn't seem to help.

 

buggy.gif

 

I probably won't work on this for a while until, as I mostly just wanted to make a proof-of-concept.

Source code on Github. ROM attached.

vcspon-wip.CV

  • Like 6
Link to comment
Share on other sites

22 hours ago, NaOH said:

I am pretty convinced this is due to some unusual behaviour in the cartridge ram. I notice in the source code for Stella that the sta ($abcd),y opcode (which I use to rewrite the code in the ram cart) will "poke" memory at address $ab+(cd+y & 0xFF) before "writing" to memory $(abcd+y). I don't know what "poke" means in this context, as it clearly doesn't mean "write," but evidently it is corrupting memory sometimes.

Indirect indexed writes (e.g. "sta (pointer),y", where "pointer" is a zero page address),  when there's no carry in adding the Y index, perform  an "extra" read access (the value read is discarded) to the destination address one CPU cycle before the actual write. If, instead, adding the index results in a carry (page crossing), that extra read is done to a different address, after adding the Y index to the LSB, but before incrementing the MSB. The next cycle after that, the write is performed to the correct address, with the carry added to the MSB.

 

In case of cartridge ram in a 2600 game (that uses an address bit instead of the R/W signal from the CPU to differentiate a read from a write operation, since that signal is not available on the cart port), a read operation can cause a write if the address is in the "write port" area. The value written is undetermined, since neither the CPU nor the RAM chip are driving the data bus in that case.

 

So, if you write to cartridge ram using indirect indexed addressing mode, and there's no page crossing, there are no consequences: the extra read will indeed corrupt the byte at the destination address, but on the next cycle the correct value will be written.

If instead, there is page crossing, and the address with the same LSB but in the previous page is a "write" address for the cartridge ram, then it will be corrupted.

 

22 hours ago, NaOH said:

However, write-breakpoints in stella don't seem to detect this form of memory manipulation, so it's hard to debug.

Stella has an option to break on such "reads from write port" in the "developer" menu.

That option is enabled by default if you enable "developer mode", while it is normally disabled in "player mode".

 

wrp0.thumb.png.a3409c51505c27a6382e8a1da325341d.png

rwp1.thumb.png.e20b299edf1395ede675d67730c99dd4.png

 


In this example you see Stella reacting to one of such cases:
circled in green is the message indicating what caused the break: "RWP[@ $1500]: $18e7", that is read from write port at address $1500 casued by instruction at $18e7 ("sta (ram_82),y").
The pointer at ram_82 is $15f6, and Y is $a. The resulting address is $1600, and because of the page crossing, there's an access to $1500 that corrupts that byte in ram.

rwp2.thumb.png.a67524d11e793dd34a1120ec33219461.png

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

Cool concept!

 

It's worth mentioning that if you use indexed indirect addressing, i.e., STA (addr, x), you can avoid these problems. Of course this addressing mode is nowhere near as nice to use, but at least there are no longer any dummy "pokes" anywhere.

Link to comment
Share on other sites

On 6/7/2023 at 9:53 AM, alex_79 said:

Stella has an option to break on such "reads from write port" in the "developer" menu.

That option is enabled by default if you enable "developer mode", while it is normally disabled in "player mode".

Fantastic! I had no idea about this feature. This will be very helpful should I continue development. :)

Link to comment
Share on other sites

  • 3 months later...

Thank to @alex_29's advice, I was able to solve the bugs! I wrote a python script to evaluate various inline-defined python assertions in order to ensure that nothing would cross a page boundary. As a simple example, I can add `; [py] 5 + 3 == 8` and python will evaluate it and confirm it's true. (Of course, the real value of this comes from being able to read symbol addresses and verify properties about them.)

I've also now implemented gravity and block removal:

vcspon.gif.712dc7cda3105d22286598e1d029b81c.gif

Next thing to do will be to implement a nice "explosion" effect. I wonder how I'll represent that visually -- maybe color-cycle?

vcspon.CV

Edited by NaOH
  • Like 4
Link to comment
Share on other sites

Okay, I've got all the basic features of the gameplay loop in, and nothing seems to be terribly glitchy. What remains is basically just polish. Out of the $800 bytes of ROM, I'm using about $700 now, there isn't a lot of polish I can do. Basically the choice now is between whether I want to add score or add sound. I'm leaning toward sound. Perhaps there will be room for both. And now it's got buttery-smooth scrolling.

butter.gif.070796d144eda19349f77fe43213bd17.gif
Demo attached.

vcspon.CV

Edited by NaOH
buttery-smooth scrolling
  • Like 2
Link to comment
Share on other sites

ZeroPage Homebrew is playing Panel de Pon on today's ZPH stream LIVE on Twitch, hope you can join us for my 50th birthday stream!

 

Games:

20231003-LetsPlay.thumb.jpg.0c99a66deb8272b0ca1a8677923d431b.jpg

Link to comment
Share on other sites

Okay, I've added levels, difficulties, and basic sound effects (very temporary). I also trimmed down a bunch of code and so despite adding all that in, there are still $100 bytes left of free space for music, TBD.

Set both difficulty switches to B for normal mode; set both to A for the hardest mode. The goal is to reach the highest level possible. You level up after clearing a certain threshold of blocks. (One of the difficulty switches currently lowers this threshold, and I will probably change this behaviour. One of the difficulty switches makes the game faster.)

Attached are two slightly different versions. One is the normal version and one uses flicker to smooth out the blocks. I imagine that flicker will only look good on certain screens -- let me know if anyone has any good or bad experiences with the flicker enabled.

Edit: Updated the grace timer period mechanic a bit, should be a little more intuitive now.

 

vcspon.CV vcspon-flicker.CV

Edited by NaOH
  • Like 4
Link to comment
Share on other sites

After @ZeroPage Homebrew's feedback during their stream, I've made the game pause before resetting so you can see what level you're on. I've also added a numeral at the top of the screen to show the current level. Finally, the difficulty has been tweaked a bit; hopefully it is a tad easier at the start, as requested.

With just $C1 bytes remaining of free space, and fewer and fewer options to shrink the existing code and data, I'm getting a little nervous about music, heeeh...

Edit: I was able to shrink the input routine a whole ton. Turns out it was very inefficient before. Now there are $106 bytes left for music. I think that should be possible, right?

vcspon.CV

Edited by NaOH
  • Like 3
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...