Jump to content

Open Club  ·  87 members

AtariVox
IGNORED

Copy AtariVox / SaveKey utility


Karl G

Recommended Posts

I've noticed that for reads if I let time elapse between them (e.g. the visible screen) I need to start over, whereas with writes I can pause and do a new write without any issues.  Not a big deal, but I thought I'd note it in case anyone needs this info in the future.

 

I never noticed it for loads/saves for Penult, since for loads I blank the visible screen for one frame when loading (since it gets loaded right after the title screen anyway it's not jarring) whereas for saves I spread it out over a few frames while continuing to display the visible screen as normal.

Link to comment
Share on other sites

Oh, and here is how I intend the utility to work, in case anyone has any feedback or thing it should work differently.

 

For each new block of 64 bytes, the first byte of the block on the source is checked, and if it is $FF (uninitialized), then that block is skipped.

 

If the source block does not start with $FF, then all 64 bytes are loaded into RAM.

 

 - If overwrite mode is selected (before the copy starts via the select switch), then the same lock on the destination is overwritten with the contents of this RAM, done in a single write once the bytes are transferred to minimize the number of writes

 - If overwrite mode is NOT selected (the default), then the first byte of the destination block is checked, and that block is only overwritten with the contents of the source if the first byte of the destination block is $FF (uninitialized)

Link to comment
Share on other sites

You cannot be 100% sure that a $FF at the beginning of a block always indicates an empty block. E.g. if a game saves high scores per game variation and the first one hasn't been played, the first by will not be initialized. IMO it would be better to check the whole block.

 

49 minutes ago, Karl G said:

I've noticed that for reads if I let time elapse between them (e.g. the visible screen) I need to start over, whereas with writes I can pause and do a new write without any issues.  Not a big deal, but I thought I'd note it in case anyone needs this info in the future.

I suppose that's somewhere mentioned in the documentation. But since almost no one does mass reads or writes, probably no one ever cared.

  • Like 1
Link to comment
Share on other sites

39 minutes ago, Thomas Jentzsch said:

You cannot be 100% sure that a $FF at the beginning of a block always indicates an empty block. E.g. if a game saves high scores per game variation and the first one hasn't been played, the first by will not be initialized. IMO it would be better to check the whole block.

A fair point.  I'm realizing now that it could fail for my own game even, since I am using the unused bytes in the block allocated for Space Game, so the first bytes could be uninitialized if the block is only used for Penult.  I guess I don't need to preserve any of the bytes I read in the destination block, so running out of RAM won't be an issue.  I'll do it that way.

Link to comment
Share on other sites

  • 1 year later...

I never did finish this, but there was some discussion in 2600 Programming about reading the AtariVox/SaveKey from the left port or reading two at once, so I thought I'd at least upload what I have so far for reference.

 

As I mentioned in that topic, if I picked this up again, I might use the QuadTari to switch between two of them in the right port so as not to have to have separate drivers, and not have to deal with swapping a controller out before starting the utility.

 

copyvox.zip

  • Thanks 1
Link to comment
Share on other sites

  • 8 months later...

So - with SaveKey stuff fresh in my mind, I decided to do a clean rewrite of this utility. I seem to be having issues with the read portion from the left SaveKey. Specifically, it reads the first two bites after initializing successfully, but on subsequent calls, it reads all bytes as $FF regardless of the actual data stored at that location.

 

The read function (ReadSaveKey) is largely identical to my working code in my recent SaveKey editor, except for the fact that it is modified to use the functions in the i2c_v2.3left.inc file. So I'm wondering if there might be any remaining issues in the i2c_v2.3left.inc file, or if it's an issue with Stella's ability to use AtariVox/SaveKey from the left port.

 

To test in Stella (as I'm afraid of trashing my actual devices), I set the left device to AtariVox, and the right device to SaveKey since each of these uses a different data file.

 

Does anyone have any ideas as to why the first two bytes would read correctly, but all others would return $FF?

 

copyvox2.zip

Link to comment
Share on other sites

Update: I decided to take the plunge and run it on hardware, after all. I saw the same results as under Stella: only the first two bytes are read correctly, and the rest are read as $FF, as if I had run i2c_stopwritel prematurely. As far as I can tell, the only difference between the first two bytes read and all the others is the passage of time. Anyway, I can at least rule out Stella's AtariVox/SaveKey emulation from the left port as a cause.

Link to comment
Share on other sites

14 hours ago, Karl G said:

So - with SaveKey stuff fresh in my mind, I decided to do a clean rewrite of this utility. I seem to be having issues with the read portion from the left SaveKey. Specifically, it reads the first two bites after initializing successfully, but on subsequent calls, it reads all bytes as $FF regardless of the actual data stored at that location.

 

The read function (ReadSaveKey) is largely identical to my working code in my recent SaveKey editor, except for the fact that it is modified to use the functions in the i2c_v2.3left.inc file. So I'm wondering if there might be any remaining issues in the i2c_v2.3left.inc file, or if it's an issue with Stella's ability to use AtariVox/SaveKey from the left port.

 

To test in Stella (as I'm afraid of trashing my actual devices), I set the left device to AtariVox, and the right device to SaveKey since each of these uses a different data file.

 

Does anyone have any ideas as to why the first two bytes would read correctly, but all others would return $FF?

 

copyvox2.zip 9.36 kB · 0 downloads

LoL. I was just about to offer to test on hardware but you beat me to it.

 

I did see that the comments in the “left” source seem to be incorrect w.r.t. the longer procedures. eg: if I2C_TXBITL takes 30 cycles rather than 22, then i2c_startreadl is really more like 38/39 cycles.

 

I ran into a problem when trying to do back-to-back writes, and had to inject something to wait for ACK.

 

Looking at the offending code (around ll. 516-539) briefly it looks like you're reading two bytes per operation, (per frame, I think?) and maybe the write of the new start address on each pass is not registering because there's no wait-for-ack after the last stop reading? That seems like a real stretch, though, if that's really a whole frame apart.

 

I'm also a little curious about the `clv` at line 517, as I don't recall ever having to do that.

Link to comment
Share on other sites

2 minutes ago, Bruce-Robert Pocock said:

I did see that the comments in the “left” source seem to be incorrect w.r.t. the longer procedures. eg: if I2C_TXBITL takes 30 cycles rather than 22, then i2c_startreadl is really more like 38/39 cycles.

I'm the one who altered the driver to work on the left port (with extensive help from @Thomas Jentzsch), and I didn't update cycle counts in comments when I did so.

 

3 minutes ago, Bruce-Robert Pocock said:

 

I ran into a problem when trying to do back-to-back writes, and had to inject something to wait for ACK.

The issue I'm having in on a read. As for writes, I do wait for the better part of a frame after doing a stopwrite before doing any other operation. Come to think of it, I shouldn't even have to do that since the writes are to the right SaveKey, and the next operation is a read from the left SaveKey.

 

6 minutes ago, Bruce-Robert Pocock said:

Looking at the offending code (around ll. 516-539) briefly it looks like you're reading two bytes per operation, (per frame, I think?) and maybe the write of the new start address on each pass is not registering because there's no wait-for-ack after the last stop reading? That seems like a real stretch, though, if that's really a whole frame apart.

Yes, I'm reading 2 bytes per pass, but 2 times per frame: vertical blank, the unused portion of the visible screen, and overscan. I don't do an i2c_stopreadl until I'm done with the entire block, though.

 

10 minutes ago, Bruce-Robert Pocock said:

I'm also a little curious about the `clv` at line 517, as I don't recall ever having to do that.

This was copied from @Thomas Jentzsch's code in the SaveKey for Dummies topic for the address initialization portion of the SaveKey setup.

 

Link to comment
Share on other sites

Another update. apparently my reads stop working after I run my ProgressBar sub during the visible screen. When I comment it out, everything works fine. I definitely don't see anything in there that could cause this, however, so I'll work on figuring out that part next.

  • Like 2
Link to comment
Share on other sites

3 minutes ago, Karl G said:

Yes, I'm reading 2 bytes per pass, but 2 times per frame: vertical blank, the unused portion of the visible screen, and overscan. I don't do an i2c_stopreadl until I'm done with the entire block, though.

I had thought that you were sending the start address on every call to LoadSaveKey and maybe the EEPROM could get confused between the address writes. I see now on a closer read that you're only doing the address write every 64 bytes, so that's probably not a concern really. (I do do random reads more-or-less back-to-back without a wait-for-ack.)

 

One thing that's probably completely irrelevant is that the data sheet (p.13, § 8.3) says

> Following the final byte transmitted to the master [the console], the master will NOT [emph. orig.] generate an Acknowledge, but will generate a Stop condition.

… but the driver code does always send an ack in your i2c_rxbyte[l] routines. My code (which is closely based on the same original) does the same thing, though, without issues.

 

Sorry to not be more helpful.

3 minutes ago, Karl G said:

This was copied from @Thomas Jentzsch's code in the SaveKey for Dummies topic for the address initialization portion of the SaveKey setup.

I'll have to look that up now :D (now that it's “too late for me” I suppose, but I am curious.)

  • Thanks 1
Link to comment
Share on other sites

Whelp ... I put aside trying to find the cause of this, and went ahead and finished the utility. After all of the changes I made, I can't reproduce the original problem. So ... I guess accidentally fixed by code redesign? It kind of bugs me not knowing what was going on, but perhaps not enough to spend a lot of time debugging something that works now.

 

Anyway, here is the latest version. I'll probably make a topic in the 2600 Programming forum as well.

 

Usage:

 

After launching the utility, plug the source AtariVox/SaveKey into the left port, and the destination AV/SK into the right port, then press Reset. The utility will then attempt to detect the devices, and report the results. If both are detected, you can hit Reset again to begin the copy.

 

The default mode is "No Overwrite", where the utility will not overwrite any blocks on the destination device that are not blank (all $FF value). Hitting Select before starting the copy changes the mode to "Overwrite", which will replace non-empty blocks on the destination device with the contents of the source device.

 

In both modes, to save time and writes, blank blocks from the source device are not written to the destination device.

 

I have tested this in Stella and on hardware successfully, but use with caution as there may still be some bugs that I haven't seen.

 

copyvox.bin

 

  • Like 3
Link to comment
Share on other sites

Okay, so I did decide to go back and figure this out even though I have a working utility now because I wanted to understand what was going on.

 

TL;DR I think the issue was that you need to preserve the overflow flag if you run other code between reads without reinitializing the read everytime.

 

I was getting indeterminate results because I was running my subsequent reads with the overflow flag in an indeterminate state after running other code. If I reinitialized the read every time I read some bytes, and ran the i2c_rxbyte calls back to back afterwards, then this wouldn't be an issue. The overflow flag being set or clear determines whether or not the code sends an acknowledge bit to the device.

 

Now before I call the routine to continue my read, I have this code to restore the state of the overflow flag:

 

    lda ProcessorStatus
    pha
    plp

 

And after I run my routine, I have this code to save the state of the overflow flag:

    php
    pla
    sta ProcessorStatus

 

@Thomas Jentzsch @RevEng, or anyone else who is highly familiar with the savekey driver code, does my reasoning seem sound here?

 

Here's my current source/ROM with drivers:

 

copyvox2.zip

Link to comment
Share on other sites

31 minutes ago, Thomas Jentzsch said:

Yes, the V-flag is used by the i2c include file (not that it is cleared in SetupSaveKey). But usually you do not have to care for it. Probably you have modified the code so that it becomes important.

You are thinking that this issue is something particular to the changes made to the left driver, then? It looks like the driver sends an acknowledge bit if overflow is set when doing an i2c_rxbyte, and then sets overflow explicitly, presumably for the next byte.

 

Maybe I can do a simple test program to read a couple of bytes each frame using only the right driver, and seeing if clearing the overflow between reads stops the reads from working as I am seeing in my utility. That would confirm or eliminate the modified left driver as a factor.

Link to comment
Share on other sites

2 hours ago, Thomas Jentzsch said:

No, I don't think it is the left driver.

 

Have you tried to simply clear the V-flag instead of restoring it?

Yeah, but it needs to be clear for the first byte that is read only (the state it will be in after i2c_startwrite). Subsequent bytes will need overflow to be set, not clear (i2c_rxbyte sets overflow).

 

I think this is a rare corner case that few people see, because in most cases, loading is done all at once at the beginning, and not spread out over several frames with other processing in-between. Also, it won't be an issue if you re-initialize every time you read more bytes.

 

I wrote a small test program just to make sure what I understood about the behavior was correct, and that there was nothing different about how the normal right port driver performs compared to the left.  This loads a block of data at $3000 at startup (the numbers 63 to 0 descending), then loads them into RAM two bytes per frame. It displays the contents of that block of RAM on the screen as colors.

 

Fire button: Reload data

 

Left Difficulty=B: Save and restore processor status to preserve the overflow flag.

testread.thumb.png.60d5091c4540ffdbd8d7d7491435f358.png

 

Left Difficulty=A: Do not restore processor status (overflow is cleared before the load function).

testread_1.thumb.png.9b23cf2186da18d1b92dbb8f77d15243.png

 

 

testread.zip

  • Like 1
Link to comment
Share on other sites

  • Recently Browsing   0 members

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