Hello I am working on a game, primarily written in c using CPCTelera. I disable the firmware and use double-buffered screens at 0x8000/0xC000.
I would like the user to be able to save the current game state to disk. Is there an easy way to do this? I know the firmware can be re-instated, which just re-enables the interrupts, but can the jumpblock/variables be restored temporarily? Or alternatively can the rom be enabled directly and the open/save functions be called directly?
Any ideas appreciated.
In Defence I'm saving the current game state like that:
- Ingame I have my double-buffer, like you, at 0x8000/0xC000.
- My application starts at 0x40 so the lower jumpblock is no problem.
- During menus and when a game finishes, I switch my double-buffer off and only use 0xC000
- I reenable the jumptable and then amsdos (and I also try to enable rom6 for paraods or M4 for example)
- I save to disk (or load)
- Reenable my own IRQ-Routine
- Finished
Some not very cleared copy+paste example for sdcc:
(When the game starts, call StoreDriveLetter() for remembering the drive from which the game startet. So you do not blame the |B users ;D )
My save-data is in a struct called sPlayerSaveData. So I save like that:
SaveFile(SaveGameFileName,(char *)playerSaveData,6,sizeof(struct sPlayerSaveData));
////////////////////////////////////////////////////////////////////////
void StoreDriveLetter(void)
{
__asm
;;------------------------------------------------------------------------
;; store the drive number the loader was run from to InitializeAmsdos
ld hl,(#0xbe7d)
ld a,(hl)
ld (_driveLoad+1),a
;ld (_driveSave+1),a
;;------------------------------------------------------------------------
;; store jump restore call to InitializeAmsdos
;ld hl,(#0xbD38)
ld a,(#0xbD38)
ld (_firmJump+1),a
__endasm;
}
////////////////////////////////////////////////////////////////////////
void InitializeAmsdos(void) __naked
{
__asm
;; ** INITIALISE ALTERNATE REGISTER SET FOR FIRMWARE **
exx
ld bc,#0x7f88 ;initialise lower rom and select mode 0
out (c),c ;this routine must be located above &4000
exx
ex af,AF
xor a
ex af,af
;; ** INITIALISE FIRMWARE **
call #0x0044 ;initialise lower jumpblock (&0000-&0040)
;and high kernal jumpblock (&b800-&bae4)
_firmJump: call #0x08bd ;initialise firmware jumpblock entries
;call #0xBD37
;(&bb00- ...)
call #0xbb00 ;initialise keyboard manager
call #0xb909 ;disable lower rom
;;; ** INITIALISE DISK ROM FOR LOADING/SAVING **
;
ld c,#7 ;disk rom
ld de,#0x8000 ;lowest useable byte of memory
ld hl,#0xb0ff ;highest useable byte of memory
call #0xbcce ;initialise disk rom
ld c,#6 ;disk rom
ld de,#0x8000 ;lowest useable byte of memory
ld hl,#0xb0ff ;highest useable byte of memory
call #0xbcce ;initialise disk rom
ld a,#0xc9
ld (#0xbb5a),a ;prevent printing of text characters
;don t get error messages corrupting screen
;; ** INITIALISE ALL ROMS (FOR LOADING/SAVING) **
;ld c,#7 ;disk rom
;ld de,#0x8000 ;lowest useable byte of memory
;ld hl,#0xb0ff ;highest useable byte of memory
;call #0xbccB ;initialise disk rom (KL ROM WALK)
;xor a ;select drive (A)
;ld (#0xac00),a
ld a,#0xff
ld (#0xbe78),a ;turn of disc error messages
;;------------------------------------------------------------------------
;; when AMSDOS is enabled, the drive reverts back to drive 0!
;; This will restore the drive number to the drive the loader was run from
_driveLoad: ld a, #0x00
ld hl,(#0xbe7d)
ld (hl),a
ret
__endasm;
}
////////////////////////////////////////////////////////////////////////
void LoadFile(char *sFileName, char *pLoadAddress, unsigned char nFileNameLen)
{
sFileName;
pLoadAddress;
nFileNameLen;
__asm
di
ld (stackLoad+1),sp ;save stack used by program
ld sp,#0xc000 ;new stack used by firmware and load routines
call _InitializeAmsdos
;; ** LOAD FILE USING FIRMWARE **
;; B = length of the filename in characters
ld b, 8 (IX) ;nFileNameLen
;; HL = address of the start of the filename
LD L, 4 (IX) ;sFileName
LD H, 5 (IX) ;sFileName
;LD HL, #_filename
LD DE, #0
call #0xbc77 ; cas_in_open
ld bc,#0
ld a,#2
;LD DE,#162 ; laenge
;LD E, 9 (IX) ;laenge
;LD D,10 (IX) ;laenge
;; HL = address of the start of the filename
LD L, 6 (IX) ;sFileName
LD H, 7 (IX) ;sFileName
call #0xbc83 ;cas_in_direct
call #0xbc7a ;cas_in_close
di
LD HL,#0X0038
LD (HL),#0XFB ;EI
INC HL
LD (HL),#0XC9 ;RET
EI
stackLoad: ld sp,#0 ;old stack back and...
;ret ;...exit to program!!
__endasm;
}
void SaveFile(char *sFileName, char *pSaveAddress, unsigned char nFileNameLen, unsigned short fileLen)
{
sFileName;
pSaveAddress;
nFileNameLen;
fileLen;
__asm
di
ld (stack+1),sp ;save stack used by program
ld sp,#0xc000 ;new stack used by firmware and load routines
call _InitializeAmsdos
;; ** SAVE FILE USING FIRMWARE **
;; B = length of the filename in characters
ld b, 8 (IX) ;nFileNameLen
;; HL = address of the start of the filename
LD L, 4 (IX) ;sFileName
LD H, 5 (IX) ;sFileName
;LD HL, #_filename
LD DE, #0
call #0xbc8c ; cas_out_open
ld bc,#0
ld a,#2
;LD DE,#162 ; laenge
LD E, 9 (IX) ;laenge
LD D,10 (IX) ;laenge
;; HL = address of the start of the filename
LD L, 6 (IX) ;sFileName
LD H, 7 (IX) ;sFileName
call #0xbc98 ;cas_out_direct
call #0xbc8f ;cas_out_close
ld bc,#0xFA7E ; FLOPPY MOTOR OFF
out (c),c
di
LD HL,#0X0038
LD (HL),#0XFB ;EI
INC HL
LD (HL),#0XC9 ;RET
EI
stack: ld sp,#0 ;old stack back and...
;ret ;...exit to program!!
__endasm;
}
Quote from: Shining on 22:04, 06 October 16
In Defence I'm saving the current game state like that:
Perfect, that's exactly what I was looking for thanks.
I am making a game using cpctelera and I wanted both loading and saving of files uisng the firmware because I want compatibility with AcmeDOS etc.
So I have made some functions.
The main file is loaded by a basic program and called:
5 a=HIMEM
10 memory &3e00
20 load"code.bi0",&3f00
30 call &3f00,a
The problem I had was that the load address is always changing because main() is not at the beginning of the file. I haven't looked into it more but i may need to change the crt to have a jp.
I have attached some files I sent to ronaldo to be included into cpctelera.
This has the load function. I need to fix up and test the save function (I too wanted to use it for save game).
I hope this is useful.
Quote from: arnoldemu on 08:09, 07 October 16
The problem I had was that the load address is always changing because main() is not at the beginning of the file. I haven't looked into it more but i may need to change the crt to have a jp.
With SDCC you need to process the .map file for the binary you're building and look for the address of "_main_init" (that's usually the entry point of crt0, if you're using it; otherwise use _main -- but having a custom crt0 is usually a good idea if you have initialised data).
That's one of the "annoying" bits of SDCC, but AFAIK that's automatically resolved for you by cpctelera.
Quote from: reidrac on 08:26, 07 October 16
With SDCC you need to process the .map file for the binary you're building and look for the address of "_main_init" (that's usually the entry point of crt0, if you're using it; otherwise use _main -- but having a custom crt0 is usually a good idea if you have initialised data).
That's one of the "annoying" bits of SDCC, but AFAIK that's automatically resolved for you by cpctelera.
But I don't want to do that.
I have used SDCC before and setup a custom crt and put a jp _main at the beginning.
Then I could call the load address to start the program. Much simpler :)
Since there was no CPCtelera before. I always used my own CRT0 with the same start-address always. So I'm doing like arnoldemu suggested.
But instead of arnoldemu I'm using a binary-loader (located in my later double-buffer) so that I'm able to load directly to 0x40 and to do stuff like decrunching.... (and bank-switching).
Quote from: Shining on 14:32, 07 October 16
Since there was no CPCtelera before. I always used my own CRT0 with the same start-address always. So I'm doing like arnoldemu suggested.
But instead of arnoldemu I'm using a binary-loader (located in my later double-buffer) so that I'm able to load directly to 0x40 and to do stuff like decrunching.... (and bank-switching).
Yes, I do exactly that!
I don't find that difficult to find the start address of my crt0; once you write that in your Makefile... is done for ever :)
You could tweak the linker scripts to make sure the CRT0 is always the first thing put in your binary. This way the entry point will be at a fixed address as long as the CRT0 doesn't change.
Sorry for taking so long, to reply, guys. These days I'm completely overloaded. Hope to have more time soon to answer all of you. And of course, thank you very much for all contributions I have received. I will reply to all of you, I promise.
Regarding the entry point, I wouldn't use a CRT0 to add a jp: there is no need to loose those 3 bytes having other alternatives. Just write the main() function at the start of the first file of your code and it will have always the same address (the one you choose in the build_config.mk). There are also other alternatives (like setting it manually with absolute memory alocation macros, for instance), but I think this one is the easiest.
I you need examples, I can upload one :).