GAMEX  documentation
created by P. Putnik

 GAMEX - should mean something like 'gaming extra', 'games exitable'. It is intended for old Atari ST and compatible machines. I tested it on diverse Atari ST(E), Mega ST(E) and Falcon.
Purpose is making gameplay and especially saves/restores of game progress (gamestate) more user friendly and compatible with modern storage devices. It is intended mostly for usage with hard disks, but may work with floppies too (except GOS4, 5). Typical savefile is 512KB long - for games working on 512KB machines.
 
Principle of gamestate saving:  user may interrupt game at any position with keypress (usually F9). Then code for storing machine state activates and saves complete CPU, RAM and peripheral chip, video etc. states. It is known system for decades, made for many of old microcomputers. Usually such systems had special HW add-on with some button for interrupt game (or any SW). Gamex is pure SW solution and therefore requires special adapting of games, so they can be interrupted.
  Gamestate saving in form of snapshots is also a way to save game adapter from digging in game code - seeking ingame pos. save/load code, and modifying it for hard disk instead floppy. It may be not simple by some games, so I think that general machinestate save is better solution, less work. Not exactly a replacement for ingame saves, but has some benefits - as faster pos. restoring  and  possibility to save pos. even at places where it is not allowed with game's regular savings.

Restoring gamestates:  it is made with little GUI program. It offers something not much seen: restoring gamestate currently in RAM. Idea for it came from fact that we need extra RAM for many games if we want to perform some saves on hard disk, or even just want to run from hard disk. Reason is that most of games is not cooperative with hard disks (their driver SW), so we need some tricks (and plus RAM) if want to play such games from hard disks, and especially if we want to save positions on hard disks. So we need 1MB RAM for games working with 512KB. Then we can do some RAM swaps:  before game start: lowering RAMtop (Phystop) for TOS on 512KB, then copy low system RAM content in top RAM, store machine state. After it we load game in low RAM, while some overwriting of GEMDOS, AES may happen. But it is no problem for games which not load from disk after start. Then starting game, which must have added code for detecting Exit key press (called GAMEX key) and machine state storing code. So, when user wants to finish playing he press Gamex key, and code activates - it will save machine state in RAM and then low and half RAM will swap content. User will find self in Desktop, just as before launching game. Just there is less free RAM. Of course enough for launching Gamex utility PRG, with which can continue just finished play, or saving permanently current gamestate. Or loading and playing something already saved. There are some options to help later play as inserting notes in savefiles, infos about used HW, TOS version,
The benefits:  user can have stored gamestates (positions, settings) on hard disk of games not intended for hard disk saves. User can in few seconds, after couple mouse clicks find himself in certain positions of games - no need for starting game with launcher and waiting for all starting sequences.

Gamestate file structure:  at beginning there is header which holds all necessary informations, notes. After it comes raw memory content of whole RAM from bottom up to end of normal video RAM location. If there is some data above latest, some extra code is required, but may be solved different...  I try to make it maximally flexible and expandable, so there may happen some extensions in future.

Header entries:


* Ofset    Meaning            Content
   
*     0    signature            PPGX
*     4    AltRamStart    new phystop,   $80000 for 512KB games
*     8    Length of Swap area          usually  $7FCC8
*    12    TOS restoring code pos    usually  $FFD00
*    16    SysSto  pos    TOS state storing       usually   $FFF80
*    20    Game restore  code pos       usually   $7FD00
*    24    GameStat       pos            varies...
*    28    GameJmp      where to jump to continue game - varies
*    32     GamexKey    code         $43  for F9
*    33     GamePosS    code      for case of option "save and continue"
*    34    Special IKBD flag #1   if  $14  then force mouse off by restore   
*    35    STE  DMA  audio flag ...
*    36    Force Timer Data registers - for 4 timers, if not 0 force
*    40    TOS saving loc     -     if 0 will be calculated
*    44    Flag for installing extra files , word
*    46    Falcon cache settings - 1 byte, another byte is validity flag = $CF  .

* So far 48 bytes of header low part defined
* Will be some Mega STE, Falcon setting flags


* Header entries for keeping track on what TOS, HW is snapshot made:
* at pos 128

* First textual mode entries for easy reading:
* 128   Machine     8 bytes  longest entry:  Mega STE
* 136   TOS V       8 bytes again         TOS 2.06
* 144   TOS lang     4 bytes                ITA,0   
* Binary :
* 148    HWdet        10         Binary machine description
    TOS V Major, TOS V Minor, TOS RAM size in 512KB steps (1 byte only), Machine code 1-ST, 2-STE, 3-MSTE, 4-Falcon, 5-TT
    MSTE cache state, Falcon bus state, Language , Real RAM size (ST(E) only) ,  Falcon cache state - but use it from header pos 46, 47 !

* 158 .;...    reserved

    * Above ones are generated by PRG

*  Optional user editable entries:
*  174    Gamex key string  ASCII  10  bytes
*  184    Note  ASCII  64 bytes
*  248    reserved   8  bytes
*  256    End

Gamex handles not loading of game files, it is on launcher. If there is need to load some files outside from launcher - as after loading gamestate, it is on game. But Gamex will do some support by executing necessary program by need or loading, installing some special files as modded GEMDOS variants. For beginning I focus on games which not accessing disks after loading all files at start. Games which work well with hard disks are not much interesting for 'gamexing'. Games loading levels from floppy with direct FDC access need in any case some changes in code. There are diverse options how to avoid floppy access - as RAMDisk, low level hard disk access (gamecache area on drives) or using TOS filesystem access.
Of course we need  no more progress saves on floppy (idea:  may using place where code for it is ! ).

Improving, expanding:   option for saving gamestate without exiting game. It will work basically in same way: will reactivate TOS and then will save state with some specific filename in current DIR, then instead exiting launcher or Util will go back in game in same way as by continuing saved state.


Gamestate storing steps: 
  1. Detecting keypress and branch to exit code (2). Location in games is easy to find with Steem Debugger's monitoring.
  2. Saving CPU registers on stack, Saving stack pointers
  3. Saving video+palette, STE DMA audio if used, PSG, MFP (needs little code for correct read of timer data regs.)
  4. JMP to stage 2 where RAM swap and TOS restoring is. In stage 2 IKBD state will be checked and stored by need.
Restoring TOS:
  1. Disable interrupts, Clear some MFP regs, swap low and upper RAM contents.
  2. Restoring video, stack pointers, MFP, enabling interrupts, sound out as signal and killer of remaining sounds of game
  3. Clear ACIA from possible overload, testing and storing IKBD state
  4. Resetting IKBD and setting mouse relative mode (for AES)
  5. Set screen and program exit call - what leads to Desktop
  6. Note: if instead latest we jump back in launcher we may do there some things: as saving gamestate, loading of new levels etc...
Restoring game:
  1. Basically same as restoring TOS. May happen from RAM or from file. File will be loaded in upper RAM and then flow is same.
  2. But need to restore STE DMA audio if used, PSG and IKBD states from saves too.

 The RAM swap flow

 
Restoring game:  gamereflow.png     Restoring TOS: tosreflow.png

  Storing only used part by TOS of low RAM, without screen, as we don't need it by PRG exit. It is max some 150 KB with TOS 2.06 and hard disk driver installed. Of course, when nothing else is installed.
  There is enough place for modded GEMDOS in upper RAM, upper part. It is about 93KB for vers. 1.

RAM allocation (usage) in case of GOS4, with 1MB RAM in machine :
 
860KB-1M  Space for storing TOS state by launch ( 164KB )
768KB-860KB  GEMDOS 0.15
700KB-768KB  Hard disk driver,buffers - used while playing game
512KB-700KB   used by gamestate restoring, some extra purposes
0-512KB  GEMDOS workspace (25KB), Game

 With 2MB or more in machine there is more space for TOS state storing, and more space for harddisk driver, it's buffers  - then located above 1MB pos.


RAM allocation (usage) in case of GOS5, with 1MB RAM in machine :
 
860KB-1M  Space for storing TOS state by launch ( 164KB )
830KB-860KB GEMDOS workspace
768KB-830KB  Modded, reduced GEMDOS 0.15, only for file I/O
700KB-768KB  Hard disk driver,buffers - used while playing game
512KB-700KB   used by gamestate restoring, some extra purposes
0-512KB   Game

 Because whole upper 512KB will be filled with game content by exiting, we need to reload GEMDOS and hard disk drivers in upper RAM by continuing play from RAM, and of course in case of loading gamestate from file too.

Main steps when launching, playing game with GOS5 :

 HW detection, storing params.  Message, exit if insuff.  RAM. Message, exit if too much low RAM occupied. Lowering Phystop, TPA RAM to 512KB (in case of 512KB games) or to 1MB by 1MB games. Change to low res., move screen buffer down. Saving running TOS, video, MFP params. (for later exit to Desktop).
Display detected HW, ask about cheats (if possible), then may show some cover pic or whatever.
  Loading packed GOS5 executable, depack to dest. Saving low RAM to safe space (for later exit to Desktop).
  Next step is installing of autoboot harddisk driver in high RAM. In case of own driver (PPTOS/DOS) it means only copy to dest. loc. (it's PC relative), setting buffers.  In case of popular drivers as Hddriver need to 'boot' and install it again, to dest. loc.  All it requires some special rutines, changed Trap #1 Malloc, setting proper cookies etc.
 Finally we may load game's starting part, performing some changes there by need, and jump to start.
 When game performs some originally floppy access request, the changed ingame loader activates. It passes required params to GEMDOS file I/O loading rutine. Then storing CPU, MFP state, and set correct values for GEMDOS and execute file I/O.
After it restoring just saved MFP, CPU conditions and jump back to game, with some ret. values by need.
Exiting to Desktop: storing machine state (CPU, PSG, audio DMA if used, MFP, video, Blitter if used ...), low 512KB content and restoring launcher TOS state, then just PRG exit, what means return to Desktop. Gamestate is in upper 512KB RAM (or more by 1MB games).

It was brief description of how it works. First such adapt. was game Creatures, then Armour-Geddon, ... F1 GP ... Turrican 2. 

The positives: multifloppy games may work on machines with only 1MB RAM (together with snapshot making option). Fast disk loadings even with many short accesses (Vroom). Often ingame depackers are culprits for slower level loads. After getting ingame floppy access code, adaptation is usually relative easy.

The negatives:  need to reinstall hard disk driver by launch, by gamestate restoring (not problem with mine drivers). On Falcon limited to max. 512MB partitions because of used TOS 1.04 DOS core.


More details in:  example source .


Problems: It seems that we have couple things which can not detect properly, so can not have 100% correct machine state saved. First one is STE DMA audio - address counter registers are write-only, so we can not know where exactly is audio playback in moment of break. It is not fatal - by continuing game we will just have some noise for short time, usually under 1 second.
Other problem is with reading MFP timer data registers. Reading values accurate is possible with sampling for some time, and taking maximal value. But in case when timer is stopped it works not. I encountered that problem first in game Goldrunner, where Timer A works only by (sampled) voice playback. Workaround: detetmining data values with Steem Debugger for instance and writing them in header. (4 bytes needed for Timers A,B,C,D ). If there is 0 then use detected value. If non-zero use it.

Current look of util:
gx05.png

At bottom are some RAM addresses shown - considering low and high RAM occupied. With Free RAM button may release TOP RAM, without reset.



Credits, used literature:  Atari ST Profibuch, 68000 CPU book, STE Devdocs. Devpac 3. Latest but not least: Steem Debugger - without it many things would be much-much harder.

P. Putnik
Latest update:  Jan 2010.