Save-game Architecture Save-games are built around three structures: 1. 64 24-bit word (192 bytes, 96 16-bit word) flash rows 2. 512 24-bit word flash sectors 3. A slot number (0-7) that identifies a logical save-game slot When save-games are enabled, JLP commandeers the upper 768 words of JLP RAM. If no save-games are used, this RAM remains available to the game. JLP only commandeers this memory in response to save-game commands, and is available for game use the rest of the time. MEMORY MAP $8000 - $9F7F: 7.875K words RAM $8000 - $9C7F: 7.125K words available for games to use as they please $9C80 - $9CDF: Save Game slot 0 (96 words) $9CE0 - $9D3F: Save Game slot 1 (96 words) $9D40 - $9D9F: Save Game slot 2 (96 words) $9DA0 - $9DFF: Save Game slot 3 (96 words) $9E00 - $9E5F: Save Game slot 4 (96 words) $9E60 - $9EBF: Save Game slot 5 (96 words) $9EC0 - $9F1F: Save Game slot 6 (96 words) $9F20 - $9F7F: Save Game slot 7 (96 words) ISSUING COMMANDS The save-game feature in JLP communicates with games via the 768-word windows in RAM shown above. All save-game functions are invoked through a single command register at $9FFF. Programs write a command word to this location, wait for JLP to complete its operation, and then examine the result in RAM. The write must originate from an instruction whose last word is at location $0300. Command words are formatted as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | slot update bitmap | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ The upper 8 bits have a fixed value that JLP uses to verify that this write is a valid command write. The lower 8 bits form a bitmap indicating the operation to perform on each of the 8 slots. Bit 0 corresponds to slot 0, bit 1 to slot 1, etc. -- A 0 in the bitmap indicates a read operation. JLP will read the latest value for each slot whose bitmap is zero and update the image in RAM. -- A 1 in the bitmap indicates a write operation. JLP will record the information in the given slot as the latest value for that slot. Thus, saving and loading are accomplished with the same command word. JLP combines the two operations because a write may require erasing the flash memory pool and JLP needs somewhere to put the "current" values for all 8 slots while it erases. Issuing the save-game command is tricky. Because the related flash operations are slow, JLP will stop responding to accesses while a command is in progress. Therefore, games must NOT use any resources (RAM or ROM) provided by JLP while a command is busy. Thus, games must do the following: 1. Point the stack to a region in System RAM. 2. Download a short bit of code to System RAM to execute the command. 3. Point the interrupt service vector to a routine in System RAM. 4. Issue the command from System RAM. 4. Spin in a busy loop in System RAM until JLP begins responding again. Note that it's not necessary to actually disable interrupts. In fact, from a game-player experience perspective, disabling interrupts is a bad idea, as that will cause the display to blank or flash. To determine that JLP has completed its command, programs should poll location $9FFF until it returns the value $0000. While JLP is busy, location $9FFF should return $FFFF. The following code sequence illustrates how to use JLP's save game feature. It sets up a stub ISR that keeps the screen enabled, and issues the save-game command with the bitmap in R0. It expects to be able to put a 16-word stack at $2F0, and 9 words of code at $300 - $308. Note: I currently plan to have JLP verify that the write to $9FFF occurs from code at location $0300. Given that, please ensure that at least the "MVO@ R0, R1" remains at $0300 if you change the code below. ;; ======================================================================== ;; ;; SG_UPD: Update the save-game slots based on bitmap in R0. ;; ;; ======================================================================== ;; SG_UPD PROC PSHR R5 JSRD R5, @@copy ;; === start of code that will run from RAM MVO@ R0, R1 ; $300: initiate command @@loop: CMP@ R1, R2 ; $301: Check for JLP to return BNEQ @@loop ; $302: JR R5 ; $304: MVII #$20, R1 ; $305: \ MVO@ R1, R1 ; $307: |- simple ISR JR R5 ; $308: / ;; === end of code that will run from RAM @@copy: MVII #$300, R4 REPEAT 9 MVI@ R5, R2 ; \_ Copy code fragment to System RAM MVO@ R2, R4 ; / ENDR MOVR R6, R2 ; save old stack pointer MVII #$2F0, R6 ; new stack pointer PSHR R2 ; save old SP on new stack MVII #$100, R4 ; \ SDBD ; |_ Save old ISR on new stack MVI@ R4, R2 ; | PSHR R2 ; / MVII #$305, R2 ; \ MVO R2, $100 ; |_ Set up new ISR in RAM SWAP R2 ; | MVO R2, $101 ; / MVII #$9FFF, R1 ; \ ANDI #$00FF, R0 ; |- Set up command word XORI #$A500, R0 ; / CLRR R2 ; JLP returns 0 @ $9FFF when ready JSRE R5, $300 ; Invoke the command PULR R2 ; \ MVO R2, $100 ; |_ Restore old ISR from new stack SWAP R2 ; | MVO R2, $101 ; / PULR R6 ; Restore old SP PULR PC ; Return ENDP REDUCING NUMBER OF SLOTS Many games will need fewer than 8 slots, and may wish to reclaim some of the 768 words these slots occupy. To do that, programs can write an "active slot count" to location $9FFE. The number written here indicates the first slot number that's active. Slot numbers equal to or greater than this number will be available. As with location $9FFF, this is protected with a magic constant in bits 4 through 15: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | first slot # | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Writing 0 means all 8 slots are available. Writing 6 means that only slots 6 and 7 will be available. A number larger than 7 effectively disables the save-game feature. NOTE: If you reduce the number of available slots after storing something in a lower numbered slot, you may not be able to retrieve the data in the lower numbered slot in the future if you do any updates to the flash. This is because JLP uses the slot storage to hold the "latest versions" of each slot when it has to erase flash when the flash pool wraps around. If a given update causes the pool to wrap, JLP won't have any place to temporarily store the data for these lower numbered slots. It should be safe to lower the number of available slots temporarily if you don't write any slot updates during that time.