Jump to content
IGNORED

SaveKey for Dummies!


Recommended Posts

Hi,
I found no easy to use code for loading and storing on a SaveKey or AtariVox. Therefore I am presenting my code here.

All it takes are:

  • add the attached include file to your code's directory
  • define the three variables/constants at the beginning of the code
  • call WriteSaveKey for storing your score etc.
  • call ReadSaveKey for reading what you have stored

Notes:

  • if you have multiple scores to store (e.g. different game modes), you have to modify the address used in SetupSaveKey.
  • the X register is not used in any i2c subroutine.
  • after calling WriteSaveKey you must wait at least 5ms (~80 scan lines) before accessing the SaveKey again.
  • update i2c v2.3 attached, fixes unwanted noise issues when using an AtariVox
skBuffer    = <score RAM>       ; define the RAM address you want to store
SK_BYTES    = <n>               ; define how many bytes your want to store
SAVEKEY_ADR = $xxxx             ; ask Albert for a free slot! 
 
    include "i2c_v2.3.inc"      ; a highly optimized (for space) version   
    i2c_subs                    ; this makes the i2c macros of the include file known to the code 

;-------------------------------------------------------------------------------
WriteSaveKey SUBROUTINE         ; total cycles = 1923 (for 3 bytes)
;-------------------------------------------------------------------------------
; setup SaveKey:
    jsr     SetupSaveKey        ; 6+927
    bcc     .noSKfound          ; 2/3

; write high score:
    ldx     #SK_BYTES-1         ; 2 = 937   
.loopWriteSK
    lda     skBuffer,x          ; 4
    jsr     i2c_txbyte          ;6+296      transmit to EEPROM
    dex                         ; 2
    bpl     .loopWriteSK        ; 2/3=932

; stop write:
    jsr     i2c_stopwrite       ; 6+42=48   terminate write and commit to memory
.noSKfound
    rts                         ; 6

;-------------------------------------------------------------------------------
ReadSaveKey SUBROUTINE          ; total cycles = 2440 (for 3 bytes)
;-------------------------------------------------------------------------------
; setup SaveKey:
    jsr     SetupSaveKey        ;6+927
    bcc     .noSKfound          ; 2/3

; start read:
    jsr     i2c_stopwrite       ;6+42       end of "fake" write
    jsr     i2c_startread       ;6+284      Start signal and $a1 command byte

; read high score:
    ldx     #SK_BYTES-1         ; 2 = 1275
.loopReadSK
    jsr     i2c_rxbyte          ;6+333      read byte from EEPROM
    cmp     #$ff                ; 2         EEPROM slot empty? (we are assuming $ff for uninitialized space)
    bne     .skipEmptySK        ; 2/3        no, skip clear
    lda     #0                  ; 2         clear EEPROM slot
.skipEmptySK
    sta     skBuffer,x          ; 4
    dex                         ; 2
    bpl     .loopReadSK         ; 2/3=1061

; stop read:
    jsr     i2c_stopread        ;6+92=98    terminate read
.noSKfound
    rts                         ; 6

;------------------------------------------------------------------------------
SetupSaveKey SUBROUTINE         ; = 927
;------------------------------------------------------------------------------
; detect SaveKey:
    jsr     i2c_startwrite      ;6+312
    bne     .exitSK             ; 2/3

; setup address:
    clv                         ; 2
    lda     #>SAVEKEY_ADR       ; 2         upper byte of address
    jsr     i2c_txbyte          ;6+296
    lda     #<SAVEKEY_ADR       ; 2         lower byte offset
    jmp     i2c_txbyte          ;3+296      returns C==1

.exitSK
    clc
    rts

; 176 bytes in total (less if you inline the subroutines)

I hope this answers all questions. Else let me know. icon_smile.gif

SaveKey & AtariVox memory allocation list

i2c_v2.2.inc

i2c_v2.3.inc

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

Cool! Thanks for sharing that, Thomas.

 

Cool, I was looking for documentation about this last night and couldn't find any.

How popular are these SaveKeys? I'm trying to decide if it's something I want to support in my game.

 

Somewhat popular from what I can tell. They are integrated into the AtariVox, and those are supported by a number of homebrews. If you have room for it, you may as well add the extra feature, to give people a reason to own one.

Link to comment
Share on other sites

;------------------------------------------------------------------------------
SetupSaveKey SUBROUTINE         ; = 833
;------------------------------------------------------------------------------
; detect SaveKey:
    jsr     i2c_startwrite      ;6+280
    bne     .exitSK             ; 2/3

; setup address:
    clv                         ; 2
    lda     #>SAVEKEY_ADR       ; 2         upper byte of address
    jsr     i2c_txbyte          ;6+264
    lda     #<SAVEKEY_ADR       ; 2         lower byte offset
    jmp     i2c_txbyte          ;3+264      returns C==1   ; <======================THIS LINE?

.exitSK
    clc
    rts

 

Is that jmp instruction just before .exitSK really a jmp? Or is that supposed to be a jsr?

 

Edit: Oh, I get it, after staring at it long enough. Since i2c_txbyte will do the rts for you, you can use its rts to return from SetupSaveKey. Nevermind!

Edited by gauauu
Link to comment
Share on other sites

; setup address:
    clv                         ; 2
    lda     #>SAVEKEY_ADR       ; 2         upper byte of address
    jsr     i2c_txbyte          ;6+264
    lda     #<SAVEKEY_ADR       ; 2         lower byte offset
    jmp     i2c_txbyte          ;3+264      returns C==1

.exitSK
    clc
    rts

; 174 bytes in total (less if you inline the subroutines)

There is a small cycle optimization here. The first instruction of of i2c_txbyte is EOR #$FF. Since you are loading constants this can be applied ahead of time and that instruction skipped over saving 4 cycles. You might be able to save more too if the carry is known to be set or cleared.

Link to comment
Share on other sites

Yes, there is room for optimization, cycles and ROM (e.g. inline and share SetupSaveKey, merging .noSKFound/.exitSK and the calls for i2c_stopwrite/read...). But I wanted to keep the code as simple and understandable as possible.

 

The amount of cycles which can be saved is very small (maybe 1%). But if anyone needs to find a few extra bytes ROM, I can help optimizing.

Link to comment
Share on other sites

  • 1 month later...

Is the best way to reserve a slot still to fill out the main contact form on AtariAge?

 

(Just making sure, I sent something there and hadn't heard back in awhile. I'm happy to wait longer if it everyone's just busy, but didn't want to be waiting forever if I'm doing it wrong)

Link to comment
Share on other sites

  • 6 months later...

After 8 years, we found a small issue in the i2c_v2.2.inc, which caused some noise when using an AtariVox. Therefore I attached an updated version 2.3 above.

 

Note: This version requires 2 more bytes ROM and is slightly slower when writing a byte to SaveKey/AtariVox. The timing has been adjusted accordingly.

  • Like 2
Link to comment
Share on other sites

  • 1 year later...

This library is very easy to integrate. Thanks, Thomas!

 

Because storing a 3-byte highscore takes 25 scanlines, I had to turn the screen to black during a single frame at the end of a highscore game. Otherwise that frame would need more than 262 scanlines.

Are there any best practices on when/where to call 'jsr WriteSaveKey' ?

Link to comment
Share on other sites

Wait... I might just be able to split-up the writing of the 3 highscore bytes into 3 successive frames.

 

[Edit]

Splitting worked for me! Writing of a single byte to the SaveKey takes 1292 cycles, for which I have just enough time during overscan.

Note that writing a single byte at a time is not optimal, because the transmit-start and transmit-end in i2c uses most of the cycles. But with 2600 programming sometimes you just need to do what works :-)

Edited by Dionoid
Link to comment
Share on other sites

  • 3 weeks later...

Although I haven't used my allocated space yet, you can write and test your game by saving to the "scratchpad" area, correct?

 

In batari Basic, is very simple to: include anything.asm

and any needed variables/constants are defined or reused from the ones available depending on which "kernel" is used, and defined in the batari Basic way at the start of your code.

Link to comment
Share on other sites

  • 1 month later...

Wait... I might just be able to split-up the writing of the 3 highscore bytes into 3 successive frames.

 

[Edit]

Splitting worked for me! Writing of a single byte to the SaveKey takes 1292 cycles, for which I have just enough time during overscan.

Note that writing a single byte at a time is not optimal, because the transmit-start and transmit-end in i2c uses most of the cycles. But with 2600 programming sometimes you just need to do what works :-)

I am no expert of flash memory wear, but IMO it is always a good idea to minimize flash writes.

 

Therefore I am not sure if yours is the optimal way, because that way you are flashing the whole 64 bytes block three times instead of only once. But probably that is still more than fine.

Link to comment
Share on other sites

  • 5 weeks later...

I wouldn't sweat more writes being traded off for stable and attractive frames.

 

The spec sheet for the 24LC256 EEPROM says it's good for more than a million write/erase cycles. Even if every developer used 3 writes per save, that still leaves over three hundred thousand saves per vox.

Link to comment
Share on other sites

  • 9 months later...

I'm thinking of modifying this API for my own purposes, but I have a question. If I were to do the SetupSaveKey in OS and e.g. write 3 or 4 bytes via i2c_txbyte, could I then pause to finish up my OS timer, do VSYNC, and then go back to writing e.g. 7 more bytes in VB before finalizing the write before the visible screen? Would the delay between calls to i2c_txbyte make any difference, or can I avoid having to call the SetupSaveKey and i2c_stopwrite twice for these two time segments?

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