| |
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. |
|
| |
hedning
Registered: Mar 2009 Posts: 4732 |
Check the EasyFlash-cracks that have been made. Many of them use the save-option. |
| |
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) |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Thanks. |
| |
enthusi
Registered: May 2004 Posts: 677 |
You can check here:
http://skoe.de/easyflash/doku.php?id=develdocs |
| |
enthusi
Registered: May 2004 Posts: 677 |
Possibly worth to look into this as well (I forgot :)
Prince of Persia Saver 0.1 |
| |
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 |
| |
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
...
*/
|
| |
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. |
| |
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. |
| |
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. |
| |
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 |
| |
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. |
| |
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
|
| |
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. |
| |
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. |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Indeed ;-) Thanks. |
| |
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 ? |
| |
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. |
| |
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. |
| |
Majikeyric
Registered: Sep 2002 Posts: 83 |
Thanks for your explanations, incremental writes seem a good idea, I gonna implement that! :) |
| |
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. |
| |
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.. |
| |
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? |
| |
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. |
| |
Knight Rider
Registered: Mar 2005 Posts: 131 |
I have it on my phone. I will create a release and upload
EasyFlash eAPI |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Smashing! thanks. |
| |
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. |