Log inRegister an accountBrowse CSDbHelp & documentationFacts & StatisticsThe forumsAvailable RSS-feeds on CSDbSupport CSDb Commodore 64 Scene Database
You are not logged in - nap
CSDb User Forums


Forums > C64 Coding > Save-feature on the Easy Flash
2018-02-20 16:30
TWW

Registered: Jul 2009
Posts: 545
Save-feature on the Easy Flash

According to what I have (superficially) have read, the EasyFlash allows you to save data on the fly to the cartridge. I don't know of any other HW which allows this.

I was wondering if this could have been used as a "save game" functionality or is this a bad idea?

Reason I ask is due to a crack project I'm working on (for ages) which is made for the "C64 Game System Cartridge" and I was wondering if I could get rid of the need for any disk IO by porting it to EasyFlash.
2018-02-20 16:48
hedning

Registered: Mar 2009
Posts: 4732
Check the EasyFlash-cracks that have been made. Many of them use the save-option.
2018-02-20 17:12
chatGPZ

Registered: Dec 2001
Posts: 11386
the easyflash contains flash-rom which can be programmed at runtime in-system. so yes, you can use this for implementing save games.

look at the developer docs on the easyflash website, in particular the documentation of the "EAPI", which is what you want to use for this (DONT access the flash directly via I/O registers)
2018-02-20 18:45
TWW

Registered: Jul 2009
Posts: 545
Thanks.
2018-02-22 08:22
enthusi

Registered: May 2004
Posts: 677
You can check here:
http://skoe.de/easyflash/doku.php?id=develdocs
2018-02-22 08:23
enthusi

Registered: May 2004
Posts: 677
Possibly worth to look into this as well (I forgot :)
Prince of Persia Saver 0.1
2018-02-22 08:51
Knight Rider

Registered: Mar 2005
Posts: 131
Quote: According to what I have (superficially) have read, the EasyFlash allows you to save data on the fly to the cartridge. I don't know of any other HW which allows this.

I was wondering if this could have been used as a "save game" functionality or is this a bad idea?

Reason I ask is due to a crack project I'm working on (for ages) which is made for the "C64 Game System Cartridge" and I was wondering if I could get rid of the need for any disk IO by porting it to EasyFlash.


GMOD2 also has flash memory for game state saves. I am not sure if you can buy this as an end consumer though
2018-02-22 09:08
TWW

Registered: Jul 2009
Posts: 545
Thanks.

I am creating an image of the .crt file to get a full grip on the structure of this file (yes I know of the tool which comes with Vice^^) but I am struggling to make it work.

I generate a full MB file (size and bank headers verified with a hex-editor) with the cartridge-header and chip-headers as described in the vice doc's:

Quote:

    Size            1024Kb (64 banks of 2 * 8Kb)
    EXROM           inactive (hi) (1)
    GAME            active   (lo) (0)
    Load address    $8000-$9FFF (ROML), $A000-$BFFF or $E000-$FFFF (ROMH)

          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F       ASCII
          -----------------------------------------------  ----------------
    0000: 43 36 34 20 43 41 52 54 52 49 44 47 45 20 20 20  C64?CARTRIDGE???
    0010: 00 00 00 40 01 00 00 20 01 00 00 00 00 00 00 00  ???@????????????
    0020: 45 61 73 79 46 6C 61 73 68 20 43 61 72 74 72 69  EasyFlash?Cartri
    0030: 64 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00  dge?????????????
    0040: 43 48 49 50 00 00 20 10 00 00 00 01 80 00 20 00  CHIP????????.???
    0050: 00 85 5E FE C3 C2 CD 38 30 93 0D 2B 2B 2B 20 45  ?…^....80.?+++?E

          43 36 34 20 43 41 52 54 52 49 44 47 45 20 20 20
          00 00 00 40 01 00 00 20 01 00 00 00 00 00 00 00
          45 61 73 79 46 6c 61 73 68 20 43 61 72 74 72 69
          64 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00
          43 48 49 50 00 00 20 10 00 00 00 01 80 00 20 00




Firstly, isn't the EX and GAME lines mixed up in the description on top (probably don't matter)? Secondly, isn't the first bank number = 0 and not 1 like here? Thirdly, shouldn't the chip type be Flash (type 2)?

I checked some other releases to confirm the values, and chip type was 2 and bank 0 was, well, 0. So I assume the info above is outdated or something?

My other doubt is the bank numbering scheme. As this can be mapped into two banks ($8000 & $e000) or one ($a000), would the correct bank header numbering scheme with only one bank be address $a000 and bank number 0 to 127?

I have written a framework to generate the cartridge file which looks like this:

    // Cartridge Parameters
    // ====================

    // Cartridge Header Parameters
    // ---------------------------
    .const CartridgeHeaderSize     = $40                         // #$40 is default Double Word (Big endian)
    .const CartridgeVersion        = 1.000                       // High/Low Order Fixed Point Word
    .const CartridgeType           = 32                          // 32 = EasyFlash (Big Endian)
    .const CartridgeEXROM          = 0                           // 0 = Inactive & 1 = Active.
    .const CartridgeGAME           = 1                           // 0 = Inactive & 1 = Active.
    .const CartridgeName           = "EasyFlash Cartridge"       // Maximum 32 bytes.

    // Chip Header Parameters
    // ----------------------
    .const CartridgeChipHeaderSize = $10                         // Size of Bank Header Integer (#$10 is default)
    .const CartridgeChipSize       = $2000                       // Size of Cartridge Banks Integer
    .const CartridgeChipType       = 2                           // 0 = ROM, 1 = RAM, 2 = Flash. Word (Big Endian)
    .var CartridgeChipNumber       = 0                           // ID Number for the Chips
    .const CartridgeLocation       = $a000                       // Cartridge ROM address on the C64 Word (Big Endian)

    // Functions
    // =========

    // Switches endian on a 16 bit argument
    .function _SwitchEndian16(argument) {
        .return floor(argument / $100) + floor([argument/$100-floor(argument / $100)]*256)*256
    }

    // Switches endian on a 32 bit argument
    .function _SwitchEndian32(argument) {
        .return floor(argument / $1000000) + [floor(argument / $10000) - floor(argument / $1000000)*256]*256 + [floor(argument / $100) - floor(argument / $10000)*256]*$10000 + [argument - floor(argument / $100)*256]*$1000000
    }


    // Cartridge Header File
    // =====================

    .pc = $0000 "Cartridge Header"

    .text "C64 CARTRIDGE   "
    .dword _SwitchEndian32(CartridgeHeaderSize)
    .byte CartridgeVersion, round([CartridgeVersion-floor(CartridgeVersion)]*1000)
    .word _SwitchEndian16(CartridgeType)
    .byte CartridgeGAME, CartridgeEXROM
    .fill 6,0
    .encoding "petscii_upper"
    .text CartridgeName
    .encoding "screencode_mixed"
    .fill 32-CartridgeName.size(),0


    // Macro to set chip header for each bank (to be called at each bank)
    // ==================================================================

    .macro ChipHeader() {
        .text "CHIP"
        .dword _SwitchEndian32(CartridgeChipHeaderSize + CartridgeChipSize)
        .word _SwitchEndian16(CartridgeChipType)
        .word _SwitchEndian16(CartridgeChipNumber)
        .eval CartridgeChipNumber++
        .word _SwitchEndian16(CartridgeLocation)
        .word _SwitchEndian16(CartridgeChipSize)
    }

    // Bank #00
    // ========

    :ChipHeader()                       // Write the chip header for Bank #00

Bank_Begin:
    .pseudopc CartridgeLocation {

        .word CartridgeColdStart
        .word CartridgeWarmStart
        .byte $c3, $c2, $cd, $38, $30  // CBM80

    CartridgeColdStart:
    CartridgeWarmStart:
        dec $d020
        jmp *-3
    }
    .fill CartridgeChipSize - [* - Bank_Begin], $00


    // Fill remaining banks with headers and 'null' data
    //==================================================

    .for (var i = 0 ; i < 255 - CartridgeChipNumber ; i++) {
        :ChipHeader()
    !Begin:
        .byte $00
    !End:
        .fill CartridgeChipSize - [!End- - !Begin-], $00
    }



/* THIS PRODUCES:

Cartridge Header ($00000):
    43 36 34 20 43 41 52 54 52 49 44 47 45 20 20 20
    00 00 00 40 01 00 00 20 01 00 00 00 00 00 00 00
    45 61 73 79 46 6c 61 73 68 20 43 61 72 74 72 69
    64 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00

Bank Header 0 ($00040):
    43 48 49 50 00 00 20 10 00 02 00 00 a0 00 20 00

Code Bank 0 ($00050):
    09 a0 09 a0 c3 c2 cd 38 30 ce 20 d0 4c 09 a0 00
    ...

Bank Header 2 ($02050):
    43 48 49 50 00 00 20 10 00 02 00 01 a0 00 20 00
    ...

All the way untill bank 127 ($fe830):
    43 48 49 50 00 00 20 10 00 02 00 7f a0 00 20 00
    ...
*/
2018-02-22 09:12
oziphantom

Registered: Oct 2014
Posts: 490
You need 2 chip packets of 8K each per "Bank", one for the $8000 and one for $a000

Note that the device starts in UltimaxMode so ExROM/Game needs to be set up for Ulitmax.
2018-02-22 09:14
oziphantom

Registered: Oct 2014
Posts: 490
Quote: GMOD2 also has flash memory for game state saves. I am not sure if you can buy this as an end consumer though

EEPROM for saves, as the Flash can only be written by an E rev. So you save to the Serial EEPROM.
2018-02-22 09:20
TWW

Registered: Jul 2009
Posts: 545
Yeah I was reading in the docs here that it boots in Ultimax and that you need to use the $fffc vector to make shit happen. I was hoping to use only one 8k bank at a time but seems you're need to use 2 at start-up and then set it up as you wish after that (fair enough). Will fiddle with it some more.
2018-02-22 09:45
Knight Rider

Registered: Mar 2005
Posts: 131
It looks like you are trying to reinvent the wheel. See here http://codebase64.org/doku.php?id=base:code_sample&s[]=easyflash
2018-02-22 13:53
TWW

Registered: Jul 2009
Posts: 545
In order to understand the wheel you have to reinvent the wheel ;-)

I had seen that framework but it (as far as I can tell) does not build the .crt fram around the binary.
2018-02-22 15:02
Knight Rider

Registered: Mar 2005
Posts: 131
Maybe you are overthinking it ? At the end you need a $FF padded 1024mb file.

Take C64 FIBR as an example. 1st your "standard" code and:

;64TASS
;
; =============================================================================
; 01;0;0000 (LOROM, bank 1)
bankStart_01_0
.logical $8000

Bank01x
.logical $0801
Bank01i
        .binary "fibr.prg",$02
.here
Bank01L = * - Bank01x

.rept $c000-*
  .byte $FF
.next
		
.here

         .fill $f7fff,$ff

* = $fffff
         .byte $ff


Built:

"...\64tass-1.53.1515\64tass" --long-address --no-warn ef.tas -b -o fibr.bin 
"...\WinVICE-3.1-x86\cartconv" -i fibr.bin -t easy -o fibr_ef.crt
2018-02-23 01:56
TWW

Registered: Jul 2009
Posts: 545
Got it working once I setup the binary file correctly. here's the framework in case it could be of use to someone:


/*-----------------------------------------------------------------------------

    EasyFlash Cartridge Framework

-----------------------------------------------------------------------------*/


    // Cartridge Parameters
    // ====================

    // Cartridge Header Parameters
    // ---------------------------
    .const CartridgeHeaderSize     = $40                         // #$40 is default Double Word (Big endian)
    .const CartridgeVersion        = 1.000                       // High/Low Order Fixed Point Word
    .const CartridgeType           = 32                          // 32 = EasyFlash (Big Endian)
    .const CartridgeEXROM          = 0                           // 0 = Inactive & 1 = Active.
    .const CartridgeGAME           = 1                           // 0 = Inactive & 1 = Active.
    .const CartridgeName           = "EasyFlash Cartridge"       // Maximum 32 bytes.

    // Chip Header Parameters
    // ----------------------
    .const CartridgeChipHeaderSize = $10                         // Size of Bank Header Integer (#$10 is default)
    .const CartridgeChipSize       = $2000                       // Size of Cartridge Banks Integer
    .const CartridgeChipType       = 2                           // 0 = ROM, 1 = RAM, 2 = Flash. Word (Big Endian)
    .var CartridgeChipNumber       = 0                           // ID Number for the Chips
    .const CartridgeLocationLo     = $8000                       // Cartridge ROM address on the C64 Word (Big Endian)
    .const CartridgeLocationHi     = $a000                       // Cartridge ROM address on the C64 Word (Big Endian)
    .var CartridgeLocation = CartridgeLocationLo

    // Cartridge Header File
    // =====================

    .pc = $0000 "Cartridge Header"

    .text "C64 CARTRIDGE   "
    .dword _SwitchEndian32(CartridgeHeaderSize)
    .byte CartridgeVersion, round([CartridgeVersion-floor(CartridgeVersion)]*1000)
    .word _SwitchEndian16(CartridgeType)
    .byte CartridgeGAME, CartridgeEXROM
    .fill 6,0
    .encoding "petscii_upper"
    .text CartridgeName
    .encoding "screencode_mixed"
    .fill 32-CartridgeName.size(),0


    // Macros
    // ======

    // Macro to set chip header for each bank (to be called at each bank)
    .macro ChipHeader() {
        .text "CHIP"
        .dword _SwitchEndian32(CartridgeChipHeaderSize + CartridgeChipSize)
        .word _SwitchEndian16(CartridgeChipType)
        .word _SwitchEndian16(CartridgeChipNumber)
        .word _SwitchEndian16(CartridgeLocation)
        .if (CartridgeLocation==CartridgeLocationLo) {
            .eval CartridgeLocation = CartridgeLocationHi
        } else {
            .eval CartridgeLocation = CartridgeLocationLo
            .eval CartridgeChipNumber++
        }
        .word _SwitchEndian16(CartridgeChipSize)
    }


    // Functions
    // =========

    // Switches endian on a 16 bit argument
    .function _SwitchEndian16(argument) {
        .return floor(argument / $100) + floor([argument/$100-floor(argument / $100)]*256)*256
    }

    // Switches endian on a 32 bit argument
    .function _SwitchEndian32(argument) {
        .return floor(argument / $1000000) + [floor(argument / $10000) - floor(argument / $1000000)*256]*256 + [floor(argument / $100) - floor(argument / $10000)*256]*$10000 + [argument - floor(argument / $100)*256]*$1000000
    }



    // LoBank #00 - Asset Manager
    // ==========================

    :ChipHeader()                       // Write the chip header for Bank #00
    .import source "Asset Manager.asm"  // Import Bank #00 which contains all the code to handle the startup and memory management etc.

    // HiBank #00 - EasyFlash Startup Bank
    //====================================

    :ChipHeader()
!Bank_Begin:

    .fill $1800,0
    // EAPI
    .import source "EasyAPI.asm"
    .encoding "petscii_upper"
    .text "EF-Name:KOL@@@@@@@@@@@@@"
    .encoding "screencode_mixed"
    .fill CartridgeChipSize - [* - !Bank_Begin-] - 2, $00
    .word $8000

    // Fill remaining banks with headers and 'null' data
    //==================================================

    .for (var i = 0 ; i < 190 - CartridgeChipNumber ; i++) {
        :ChipHeader()
    !Begin:
        .byte $00
    !End:
        .fill CartridgeChipSize - [!End- - !Begin-], $00
    }

    


Thanks for your input gents.
2018-02-26 11:55
Maxlide

Registered: Apr 2003
Posts: 31
Quoting TWW
(...)

.fill $1800,0
// EAPI
.import source "EasyAPI.asm"
.encoding "petscii_upper"
.text "EF-Name:KOL@@@@@@@@@@@@@"
.encoding "screencode_mixed"
.fill CartridgeChipSize - [* - !Bank_Begin-] - 2, $00
.word $8000

// Fill remaining banks with headers and 'null' data
//==================================================

.for (var i = 0 ; i < 190 - CartridgeChipNumber ; i++) {
:ChipHeader()
!Begin:
.byte $00
!End:
.fill CartridgeChipSize - [!End- - !Begin-], $00
}


[/code]

Thanks for your input gents.

Unused areas should be filled with $FF, not $00.
2018-02-26 13:22
TWW

Registered: Jul 2009
Posts: 545
Indeed ;-) Thanks.
2018-03-02 05:43
Majikeyric

Registered: Sep 2002
Posts: 83
Is 64KB (8 banks) the smallest amount of flash rom to erase (with EAPIEraseSector) in order to save a high score table or I misunderstood something in the docs ?
2018-03-02 07:43
Count Zero

Registered: Jan 2003
Posts: 1932
Indeed, 64kb to erase as minimum. When working somewhat incremental you can always add to those 64kb as a large save bank and only need to erase and re-save whenever the 64kb block is full.

Given that one is using a complete 64kb bank as save bank that is.
2018-03-02 08:18
enthusi

Registered: May 2004
Posts: 677
Yeah, that is sort of the weak spot.
Also they need to be in one chip (obviously), so not spread across high and low.
2018-03-02 10:42
Majikeyric

Registered: Sep 2002
Posts: 83
Thanks for your explanations, incremental writes seem a good idea, I gonna implement that! :)
2018-03-02 12:36
Mr. SID

Registered: Jan 2003
Posts: 424
FWIW, Prince of Persia uses a single 8KB bank for saving (each save data is 3 bytes, iirc), so it just adds the saves incrementally until the bank is full, it doesn't erase data at all.
2018-03-02 14:43
MagerValp

Registered: Dec 2001
Posts: 1078
Ultima IV Remastered uses a pair of 64 kB banks that it alternates between:

https://raw.githubusercontent.com/MagerValp/u4remastered/master..
https://github.com/MagerValp/u4remastered/blob/master/src/efloa..
2018-03-03 08:07
JackAsser

Registered: Jun 2002
Posts: 2014
Quote: FWIW, Prince of Persia uses a single 8KB bank for saving (each save data is 3 bytes, iirc), so it just adds the saves incrementally until the bank is full, it doesn't erase data at all.

Wow, was 3 bytes enough to hold player location, life, weapon, which enemy is dead etc? Or do you just save general progress (max health, level etc) and reset level state completely on load?
2018-03-03 13:56
TWW

Registered: Jul 2009
Posts: 545
DOes someone has the latest binary files for the EAPI? I found the source but figured if someone has the binary I'd save the trouble to convert it / assemble it with an assembler I don't have installed.
2018-03-04 06:35
Knight Rider

Registered: Mar 2005
Posts: 131
I have it on my phone. I will create a release and upload

EasyFlash eAPI
2018-03-04 08:34
TWW

Registered: Jul 2009
Posts: 545
Smashing! thanks.
2018-03-04 12:42
iAN CooG

Registered: May 2002
Posts: 3193
Quote: I have it on my phone. I will create a release and upload

EasyFlash eAPI


add something more on that entry. as it is doesn't make any sense.
EDIT: Ok, no response was given, entry deleted. It's probably part of a commercial product so doesn't belong here.
Don't use CSDb as a download site for passing non-scene files to others.
RefreshSubscribe to this thread:

You need to be logged in to post in the forum.

Search the forum:
Search   for   in  
All times are CET.
Search CSDb
Advanced
Users Online
Flashback
Steve/Laser, Zenith,..
Alakran_64
zscs
Paladin/G★P
Scrap/Genesis Project
Guests online: 102
Top Demos
1 Next Level  (9.7)
2 13:37  (9.7)
3 Mojo  (9.7)
4 Coma Light 13  (9.6)
5 Edge of Disgrace  (9.6)
6 What Is The Matrix 2  (9.6)
7 The Demo Coder  (9.6)
8 Uncensored  (9.6)
9 Comaland 100%  (9.6)
10 Wonderland XIV  (9.6)
Top onefile Demos
1 Layers  (9.6)
2 No Listen  (9.6)
3 Cubic Dream  (9.6)
4 Party Elk 2  (9.6)
5 Copper Booze  (9.6)
6 Dawnfall V1.1  (9.5)
7 Rainbow Connection  (9.5)
8 Onscreen 5k  (9.5)
9 Morph  (9.5)
10 Libertongo  (9.5)
Top Groups
1 Performers  (9.3)
2 Booze Design  (9.3)
3 Oxyron  (9.3)
4 Triad  (9.3)
5 Censor Design  (9.3)
Top Fullscreen Graphicians
1 Joe  (9.7)
2 Sulevi  (9.6)
3 The Sarge  (9.6)
4 Veto  (9.6)
5 Facet  (9.6)

Home - Disclaimer
Copyright © No Name 2001-2024
Page generated in: 0.069 sec.