Thomas Jentzsch Posted March 6, 2016 Share Posted March 6, 2016 (edited) 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. SaveKey & AtariVox memory allocation list i2c_v2.2.inc i2c_v2.3.inc Edited November 2, 2016 by Thomas Jentzsch 15 Quote Link to comment Share on other sites More sharing options...
Lumi Posted March 6, 2016 Share Posted March 6, 2016 (edited) This is brilliant! Thank you so much! Edited March 7, 2016 by Lumi Quote Link to comment Share on other sites More sharing options...
gauauu Posted March 6, 2016 Share Posted March 6, 2016 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. Quote Link to comment Share on other sites More sharing options...
Wickeycolumbus Posted March 6, 2016 Share Posted March 6, 2016 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. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted March 6, 2016 Author Share Posted March 6, 2016 Currently ~30 homebrew games support the SaveKey (see link in my first post). Quote Link to comment Share on other sites More sharing options...
alex_79 Posted March 6, 2016 Share Posted March 6, 2016 Thank you for sharing that! Quote Link to comment Share on other sites More sharing options...
Lumi Posted March 7, 2016 Share Posted March 7, 2016 (edited) By the way, just so nobody makes my mistake, ReadSaveKey will not return anything if it encounters $FF at any point. I didn't realize that initially and was confused. Edited March 7, 2016 by Lumi Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted March 7, 2016 Author Share Posted March 7, 2016 I thought the comment would be sufficient, no? Quote Link to comment Share on other sites More sharing options...
Lumi Posted March 7, 2016 Share Posted March 7, 2016 (edited) Yes, I just didn't see that part initially. I brought it up just in case someone else misses it or doesn't read it. Edited March 7, 2016 by Lumi Quote Link to comment Share on other sites More sharing options...
enthusi Posted March 8, 2016 Share Posted March 8, 2016 A little too late for me However, I used your code from Threes as you know. Thanks alot in general, this is not that well documented. For the record, reading 2 bytes takes roughly 26 PAL lines including the Setup. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted March 8, 2016 Author Share Posted March 8, 2016 For the record, reading 2 bytes takes roughly 26 PAL lines including the Setup....and 26 NTSC lines too! Quote Link to comment Share on other sites More sharing options...
Jinroh Posted March 8, 2016 Share Posted March 8, 2016 Awesome! Thanks so much for sharing Thomas! I was thinking about poking around the SaveKey so this was a very nice bit of code to read. Quote Link to comment Share on other sites More sharing options...
gauauu Posted March 15, 2016 Share Posted March 15, 2016 (edited) ;------------------------------------------------------------------------------ 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 March 15, 2016 by gauauu Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted March 15, 2016 Share Posted March 15, 2016 ; 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. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted March 15, 2016 Author Share Posted March 15, 2016 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. Quote Link to comment Share on other sites More sharing options...
gauauu Posted April 22, 2016 Share Posted April 22, 2016 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) Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 22, 2016 Author Share Posted April 22, 2016 Yup. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted November 2, 2016 Author Share Posted November 2, 2016 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. 2 Quote Link to comment Share on other sites More sharing options...
Dionoid Posted September 17, 2018 Share Posted September 17, 2018 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' ? Quote Link to comment Share on other sites More sharing options...
Dionoid Posted September 17, 2018 Share Posted September 17, 2018 (edited) 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 September 19, 2018 by Dionoid Quote Link to comment Share on other sites More sharing options...
iesposta Posted October 7, 2018 Share Posted October 7, 2018 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. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted October 7, 2018 Author Share Posted October 7, 2018 Although I haven't used my allocated space yet, you can write and test your game by saving to the "scratchpad" area, correct?Yes. Or any area you like to overwrite. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted November 10, 2018 Author Share Posted November 10, 2018 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. Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 10, 2018 Share Posted December 10, 2018 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. Quote Link to comment Share on other sites More sharing options...
+Karl G Posted September 26, 2019 Share Posted September 26, 2019 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? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.