1

Hi to all,
in the last days I have played around with the Neo Geo memory card because I would like to save the players best highscores and the last completed stage in Hypernoid on the memory card.
Except of Razoola's "Memory Card Manager", I could not find any further examples of getting access to memory card functions of the Neo Geo.
But thanks to the Neo Geo Programmers Guide http://www.hardmvs.com/manuals/NeoGeoProgrammersGuide.pdf and the Neogeodev Wiki https://wiki.neogeodev.org/index.php?title=CARD
I was able to find sufficient information to start coding a small demo program with the NeoBitz C dev kit + HPMAN'S DATlib.

Currently, the program can do the following:
- check if there is a memory card inserted or not by reading at address 0x800000 (65535/0xFFFF = not inserted, 65280/0xFF00 = SNK card, 65358/0xFF4E = NeoSaveMasta card, 00000/0x0000 = 3rd party SRAM card )
- format the card for the use in Neo Geo systems (Button A)
- save one DWORD from system RAM into the card RAM (Button B)
- load one DWORD from card RAM into the system RAM (Button C)

In the following is a video of the program in action. It saves 0x41424344 (MESS-OUT Type 1 code for "ABCD") to the first 4 bytes of "Page Unit 0" of the card, which will be displayed as "game title" in the UniBios Memory Card Manager:



And here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <task.h>
#include <input.h>
#include <DATlib.h>
#include <math.h>
#include <video.h>
#include "externs.h"
#include "fixData.h"

typedef struct bkp_ram_info {WORD debug_dips; BYTE stuff[254];} bkp_ram_info;	// 256 bytes backup block
bkp_ram_info bkp_data;

BYTE p1,p2,ps;

DWORD save_input;
DWORD save_output;
DWORD *pointer_to_save_adress;
DWORD *pointer_to_load_adress;

void memory_card()
{
	volMEMWORD(0x401ffe)=0x3456; // BG color
	LSPCmode=0x900;

	initGfx();
	clearFixLayer();
	clearSprites(1, 381);

	palJobPut( 1, hypernoid_font_Palettes.palCount, hypernoid_font_Palettes.data); // fix layer palette

	SCClose();

	pointer_to_save_adress = &save_input;
	pointer_to_load_adress = &save_output;

	save_input = 0x41424344;

	do{
		waitVBlank();

		p1=volMEMBYTE(P1_CURRENT);
		ps=volMEMBYTE(PS_CURRENT);

		if(p1&JOY_A)
		{
			volMEMBYTE(0x10FDC4)=0x00;	// CARD_COMMAND 0 = format card
			__asm__ ("jsr 0xC00468 \n");	// call CARD routine
			break;				// exit loop
		}

		if(p1&JOY_B)
		{
			volMEMDWORD(0x10FDC8)=pointer_to_save_adress; 	// BIOS_CARD_START must contain an adress to system RAM data
			volMEMWORD(0x10FDCC)=0x0004; 			// BIOS_CARD_SIZE must contain the size of the save (normally 64 bytes)
			volMEMWORD(0x10FDCE)=0x7777; 			// BIOS_CARD_FCB, contains Game NGH number
			volMEMWORD(0x10FDD0)=0x0000; 			// BIOS_CARD_SUB must contain the game save number (16 max per game)

			volMEMBYTE(0x10FDC4)=0x03;   			// CARD_COMMAND 3 = save data from system RAM into card RAM
			__asm__ ("jsr 0xC00468 \n"); 			// call CARD routine
			break;						// exit loop
		}

		if(p1&JOY_C)
		{
			volMEMDWORD(0x10FDC8)=pointer_to_load_adress; 	// BIOS_CARD_START must contain an address where the save will be loaded
			volMEMWORD(0x10FDCC)=0x0004; 			// BIOS_CARD_SIZE must contain the size of the save (normally 64 bytes)
			volMEMWORD(0x10FDCE)=0x7777; 			// BIOS_CARD_FCB, contains Game NGH number
			volMEMWORD(0x10FDD0)=0x0000; 			// BIOS_CARD_SUB must contain the game save number (16 max per game)

			volMEMBYTE(0x10FDC4)=0x02;   			// CARD_COMMAND 2 = load previously saved data from card RAM into system RAM
			__asm__ ("jsr 0xC00468 \n"); 			// call CARD routine
			break;						// exit loop
		}

		fixPrintf(27, 3,1,3,"VBL: %05d",	DAT_frameCounter);
		fixPrintf( 3, 3,1,3,"MEMORY CARD ACCESS");
		fixPrintf( 3, 4,1,3,"----------------------------------");

		if(volMEMWORD(0x800000)==65535)
		{
			fixPrintf( 3, 6,1,3,"CARD INSERTED: NO ");
		}else{
			fixPrintf( 3, 6,1,3,"CARD INSERTED: YES ");
		}

		fixPrintf( 3, 7,1,3,"CARD_COMMAND: %04d", volMEMBYTE(0x10FDC4));
		fixPrintf( 3, 8,1,3,"CARD_ANSWER : %04d", volMEMBYTE(0x10FDC6));
		fixPrintf( 3, 9,1,3,"CARD_ERROR:   %04d", volMEMBYTE(0xC0046E));

		fixPrintf( 3,12,1,3,"save_input:   %08d", save_input);
		fixPrintf( 3,13,1,3,"save_output:  %08d", save_output);

		fixPrintf( 3,14,1,3,"adress save pointer:  %08d", pointer_to_save_adress); // 01048646 = 0x100046
		fixPrintf( 3,15,1,3,"adress load pointer:  %08d", pointer_to_load_adress); // 01048656 = 0x100050

		fixPrintf( 3,17,1,3,"CARD_START:   %08d", volMEMDWORD(0x10FDC8));
		fixPrintf( 3,18,1,3,"CARD_SIZE:    %04d", volMEMWORD(0x10FDCC));
		fixPrintf( 3,19,1,3,"CARD_FCB:     %04d", volMEMWORD(0x10FDCE));
		fixPrintf( 3,20,1,3,"CARD_SUB:     %04d", volMEMWORD(0x10FDD0));

		fixPrintf( 3,22,1,3,"DIRECTORY_START:   %08d", volMEMDWORD(0x10FD04));
		fixPrintf( 3,23,1,3,"CARD_BLOCK_NUMBER: %04d", volMEMWORD(0x10FD08));
		fixPrintf( 3,24,1,3,"CARD_FAT_START:    %08d", volMEMDWORD(0x10FD0A));
		fixPrintf( 3,25,1,3,"CARD_FAT_SIZE:     %04d", volMEMWORD(0x10FD0E));
		fixPrintf( 3,26,1,3,"CARD_DATA_START:   %08d", volMEMDWORD(0x10FD10));
		fixPrintf( 3,27,1,3,"CARD_TYPE:         %04d", volMEMWORD(0x10FD14));
		fixPrintf( 3,28,1,3,"CARD_SIZE:         %04d", volMEMWORD(0x10FD16));

		fixPrintf(24, 6,1,3,"CA0:%09d", volMEMWORD(0x800000)); // 0x800000 to 0xBFFFFF memory card adress range
		fixPrintf(24, 7,1,3,"CA2:%09d", volMEMWORD(0x800002));
		fixPrintf(24, 8,1,3,"CA3:%09d", volMEMWORD(0x800004));
		fixPrintf(24, 9,1,3,"CA4:%09d", volMEMWORD(0x800FFE));

		SCClose();

	}while(!(p1&JOY_D));
}


int main(void)
{
	while(1)
	{
		memory_card();
	}
}


I am not really sure if I have done this in a proper way because I got two compiler warnings: "assignment makes integer from pointer without a cast" for the lines:

volMEMDWORD(0x10FDC8)=pointer_to_save_adress;
volMEMDWORD(0x10FDC8)=pointer_to_load_adress;

Also, I would like to save/load more than one DWORD. According to the Neo Geo Programmers Guide there are 16 "page units" with 64 bytes each available for one game.
I seems that a save/load has to done in a single step for all the data of the respective "page unit". Which means it looks like that there is no adding of new data possible.
A new save will always overwrite the existing data of the page unit.

In which way would you think multiple DWORDs could be saved/loaded in one step?
Maybe as a string?
Or inside of a struct similar to the "bkp_ram_info"?

2

typedef struct save_format { char title[20]; byte stuff[10]; //etc... } save_format; save_format SAVE_FILE; //... volMEMDWORD(BIOS_CARD_START)=(int)&SAVE_FILE;
That should work as expected, if save format is >64 bytes pass &SAVE_FILE+64 for page 1, &SAVE_FILE+128 for page 2 etc...

3

Hi HPMAN,

thank you a lot for the code - the compiler warnings are gone and now the program can save three blocks of demo data. sun
The save and load process has effect on all three save blocks of the card and there is no need to fill each block individually.
I have tested the program on my 2-slot MV2F board with the original SNK memory card, the NeoSaveMasta card and with a few 3rd party SRAM cards without any problems.
Already existing data saves from other games have fortunately not been corrupted with my save.

Here is a video of the current version:



And here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <task.h>
#include <input.h>
#include <DATlib.h>
#include <math.h>
#include <video.h>
#include <string.h>
#include "externs.h"
#include "fixData.h"

typedef struct bkp_ram_info{
	WORD debug_dips;
	BYTE stuff[254]; 	// 256 bytes backup block
} bkp_ram_info;
bkp_ram_info bkp_data;

BYTE p1,p2,ps;

typedef struct save_format{
	char title[20];		// 1st block game title, stage number
	BYTE data[44];		// 1st block 44 bytes data
	BYTE data2[64];		// 2nd block 64 bytes data
	BYTE data3[64];		// 3rd block 64 bytes data
}save_format;

save_format SAVE_FILE;

typedef struct load_format{
	char title[20];
	BYTE data[44];
	BYTE data2[64];
	BYTE data3[64];
}load_format;

load_format LOAD_FILE;

// memory card loop ///////////////////////////////////////////

void memory_card()
{
	short i;

	volMEMWORD(0x401ffe)=0x3456; // BG color
	LSPCmode=0x900;

	initGfx();
	clearFixLayer();
	clearSprites(1, 381);

	palJobPut( 1, hypernoid_font_Palettes.palCount, hypernoid_font_Palettes.data); // fix layer palette

	SCClose();

	// fill save area with demo data

	strcpy(SAVE_FILE.title, "DEMO-GAME-STAGE-01--");	// 1st block - 20 bytes char array

	for(i=0; i<44; i++)
	{
		SAVE_FILE.data[i]=1; 	// 1st block - 44 bytes array
	}

	for(i=0; i<64; i++)
	{
		SAVE_FILE.data2[i]=2; // 2nd block - 64 bytes array
		SAVE_FILE.data3[i]=3; // 3th block - 64 bytes array
	}

	do{
		waitVBlank();

		p1=volMEMBYTE(P1_CURRENT);
		ps=volMEMBYTE(PS_CURRENT);

		if(p1&JOY_A)
		{
			volMEMBYTE(0x10FDC4)=0x00;   // CARD_COMMAND 0 = format card
			__asm__ ("jsr 0xC00468 \n"); // call CARD routine
			break;
		}

		if(p1&JOY_B)
		{
			volMEMDWORD(0x10FDC8)=(int)&SAVE_FILE; 	// BIOS_CARD_START must contain an adress to system RAM data
			volMEMWORD(0x10FDCC)=0x0192; 			// BIOS_CARD_SIZE must contain the size of the save (normally 64 bytes)
			volMEMWORD(0x10FDCE)=0x7777; 			// BIOS_CARD_FCB, contains Game NGH number
			volMEMWORD(0x10FDD0)=0x0000; 			// BIOS_CARD_SUB must contain the game save number (16 max per game)

			volMEMBYTE(0x10FDC4)=0x03;   			// CARD_COMMAND 3 = save data from system RAM into card RAM
			__asm__ ("jsr 0xC00468 \n"); 			// call CARD routine
			break;									// exit loop
		}

		if(p1&JOY_C)
		{
			volMEMDWORD(0x10FDC8)=(int)&LOAD_FILE; 	// BIOS_CARD_START must contain an address where the save will be loaded
			volMEMWORD(0x10FDCC)=0x0192; 			// BIOS_CARD_SIZE must contain the size of the save (normally 64 bytes)
			volMEMWORD(0x10FDCE)=0x7777; 			// BIOS_CARD_FCB, contains Game NGH number
			volMEMWORD(0x10FDD0)=0x0000; 			// BIOS_CARD_SUB must contain the game save number (16 max per game)

			volMEMBYTE(0x10FDC4)=0x02;   			// CARD_COMMAND 2 = load saved data from card RAM into system RAM
			__asm__ ("jsr 0xC00468 \n"); 			// call CARD routine
			break;									// exit loop
		}

		fixPrintf(27, 3,1,3,"VBL: %05d",	DAT_frameCounter);
		fixPrintf( 3, 3,1,3,"MEMORY CARD ACCESS");
		fixPrintf( 3, 4,1,3,"----------------------------------");

		if(volMEMWORD(0x800000)==65535)
		{
			fixPrintf( 3, 6,1,3,"CARD_INSERTED:  NO");
		}else{
			fixPrintf( 3, 6,1,3,"CARD_INSERTED: YES ");
		}

		fixPrintf( 3, 7,1,3,"CARD_COMMAND: %04d", volMEMBYTE(0x10FDC4));
		fixPrintf( 3, 8,1,3,"CARD_ANSWER : %04d", volMEMBYTE(0x10FDC6));


		fixPrintf(22, 6,1,3,"CARD_ADD: %05d", volMEMWORD(0x800000)); // 0x800000 to 0xBFFFFF memory card adress range
		fixPrintf(22, 7,1,3,"CARD_ERROR: %03d", volMEMBYTE(0xC0046E));
		fixPrintf(22, 8,1,3,"CARD_SIZE: %04d", volMEMWORD(0x10FDCC));

		fixPrintf( 3,10,1,3,"DATA SAVE AREA:");
		fixPrintf( 3,20,1,3,"DATA LOAD AREA:");

		// data save
		fixPrintf( 3,12,1,3,"%c%c%c%c%c%c%c%c%c%c", SAVE_FILE.title[0],  SAVE_FILE.title[1],  SAVE_FILE.title[2],  SAVE_FILE.title[3],  SAVE_FILE.title[4],  SAVE_FILE.title[5],  SAVE_FILE.title[6],  SAVE_FILE.title[7],  SAVE_FILE.title[8],  SAVE_FILE.title[9]);
		fixPrintf( 3,13,1,3,"%c%c%c%c%c%c%c%c%c%c", SAVE_FILE.title[10], SAVE_FILE.title[11], SAVE_FILE.title[12], SAVE_FILE.title[13], SAVE_FILE.title[14], SAVE_FILE.title[15], SAVE_FILE.title[16], SAVE_FILE.title[17], SAVE_FILE.title[18], SAVE_FILE.title[19]);
		fixPrintf( 3,14,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data[0],  SAVE_FILE.data[1],  SAVE_FILE.data[2],  SAVE_FILE.data[3],  SAVE_FILE.data[4],  SAVE_FILE.data[5],  SAVE_FILE.data[6],  SAVE_FILE.data[7],  SAVE_FILE.data[8],  SAVE_FILE.data[9]);
		fixPrintf( 3,15,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data[10], SAVE_FILE.data[11], SAVE_FILE.data[12], SAVE_FILE.data[13], SAVE_FILE.data[14], SAVE_FILE.data[15], SAVE_FILE.data[16], SAVE_FILE.data[17], SAVE_FILE.data[18], SAVE_FILE.data[19]);
		fixPrintf( 3,16,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data[20], SAVE_FILE.data[21], SAVE_FILE.data[22], SAVE_FILE.data[23], SAVE_FILE.data[24], SAVE_FILE.data[25], SAVE_FILE.data[26], SAVE_FILE.data[27], SAVE_FILE.data[28], SAVE_FILE.data[29]);
		fixPrintf( 3,17,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data[30], SAVE_FILE.data[31], SAVE_FILE.data[32], SAVE_FILE.data[33], SAVE_FILE.data[34], SAVE_FILE.data[35], SAVE_FILE.data[36], SAVE_FILE.data[37], SAVE_FILE.data[38], SAVE_FILE.data[39]);
		fixPrintf( 3,18,1,3,"%d%d%d%d",             SAVE_FILE.data[40], SAVE_FILE.data[41], SAVE_FILE.data[42], SAVE_FILE.data[43]);

		fixPrintf(15,12,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[0],  SAVE_FILE.data2[1],  SAVE_FILE.data2[2],  SAVE_FILE.data2[3],  SAVE_FILE.data2[4],  SAVE_FILE.data2[5],  SAVE_FILE.data2[6],  SAVE_FILE.data2[7],  SAVE_FILE.data2[8],  SAVE_FILE.data2[9]);
		fixPrintf(15,13,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[10], SAVE_FILE.data2[11], SAVE_FILE.data2[12], SAVE_FILE.data2[13], SAVE_FILE.data2[14], SAVE_FILE.data2[15], SAVE_FILE.data2[16], SAVE_FILE.data2[17], SAVE_FILE.data2[18], SAVE_FILE.data2[19]);
		fixPrintf(15,14,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[20], SAVE_FILE.data2[21], SAVE_FILE.data2[22], SAVE_FILE.data2[23], SAVE_FILE.data2[24], SAVE_FILE.data2[25], SAVE_FILE.data2[26], SAVE_FILE.data2[27], SAVE_FILE.data2[28], SAVE_FILE.data2[29]);
		fixPrintf(15,15,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[30], SAVE_FILE.data2[31], SAVE_FILE.data2[32], SAVE_FILE.data2[33], SAVE_FILE.data2[34], SAVE_FILE.data2[35], SAVE_FILE.data2[36], SAVE_FILE.data2[37], SAVE_FILE.data2[38], SAVE_FILE.data2[39]);
		fixPrintf(15,16,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[40], SAVE_FILE.data2[41], SAVE_FILE.data2[42], SAVE_FILE.data2[43], SAVE_FILE.data2[44], SAVE_FILE.data2[45], SAVE_FILE.data2[46], SAVE_FILE.data2[47], SAVE_FILE.data2[48], SAVE_FILE.data2[49]);
		fixPrintf(15,17,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data2[50], SAVE_FILE.data2[51], SAVE_FILE.data2[52], SAVE_FILE.data2[53], SAVE_FILE.data2[54], SAVE_FILE.data2[55], SAVE_FILE.data2[56], SAVE_FILE.data2[57], SAVE_FILE.data2[58], SAVE_FILE.data2[59]);
		fixPrintf(15,18,1,3,"%d%d%d%d",             SAVE_FILE.data2[60], SAVE_FILE.data2[61], SAVE_FILE.data2[62], SAVE_FILE.data2[63]);

		fixPrintf(27,12,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[0],  SAVE_FILE.data3[1],  SAVE_FILE.data3[2],  SAVE_FILE.data3[3],  SAVE_FILE.data3[4],  SAVE_FILE.data3[5],  SAVE_FILE.data3[6],  SAVE_FILE.data3[7],  SAVE_FILE.data3[8],  SAVE_FILE.data3[9]);
		fixPrintf(27,13,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[10], SAVE_FILE.data3[11], SAVE_FILE.data3[12], SAVE_FILE.data3[13], SAVE_FILE.data3[14], SAVE_FILE.data3[15], SAVE_FILE.data3[16], SAVE_FILE.data3[17], SAVE_FILE.data3[18], SAVE_FILE.data3[19]);
		fixPrintf(27,14,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[20], SAVE_FILE.data3[21], SAVE_FILE.data3[22], SAVE_FILE.data3[23], SAVE_FILE.data3[24], SAVE_FILE.data3[25], SAVE_FILE.data3[26], SAVE_FILE.data3[27], SAVE_FILE.data3[28], SAVE_FILE.data3[29]);
		fixPrintf(27,15,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[30], SAVE_FILE.data3[31], SAVE_FILE.data3[32], SAVE_FILE.data3[33], SAVE_FILE.data3[34], SAVE_FILE.data3[35], SAVE_FILE.data3[36], SAVE_FILE.data3[37], SAVE_FILE.data3[38], SAVE_FILE.data3[39]);
		fixPrintf(27,16,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[40], SAVE_FILE.data3[41], SAVE_FILE.data3[42], SAVE_FILE.data3[43], SAVE_FILE.data3[44], SAVE_FILE.data3[45], SAVE_FILE.data3[46], SAVE_FILE.data3[47], SAVE_FILE.data3[48], SAVE_FILE.data3[49]);
		fixPrintf(27,17,1,3,"%d%d%d%d%d%d%d%d%d%d", SAVE_FILE.data3[50], SAVE_FILE.data3[51], SAVE_FILE.data3[52], SAVE_FILE.data3[53], SAVE_FILE.data3[54], SAVE_FILE.data3[55], SAVE_FILE.data3[56], SAVE_FILE.data3[57], SAVE_FILE.data3[58], SAVE_FILE.data3[59]);
		fixPrintf(27,18,1,3,"%d%d%d%d",             SAVE_FILE.data3[60], SAVE_FILE.data3[61], SAVE_FILE.data3[62], SAVE_FILE.data3[63]);

		// data load
		fixPrintf( 3,22,1,3,"%c%c%c%c%c%c%c%c%c%c", LOAD_FILE.title[0],  LOAD_FILE.title[1],  LOAD_FILE.title[2],  LOAD_FILE.title[3],  LOAD_FILE.title[4],  LOAD_FILE.title[5],  LOAD_FILE.title[6],  LOAD_FILE.title[7],  LOAD_FILE.title[8],  LOAD_FILE.title[9]);
		fixPrintf( 3,23,1,3,"%c%c%c%c%c%c%c%c%c%c", LOAD_FILE.title[10], LOAD_FILE.title[11], LOAD_FILE.title[12], LOAD_FILE.title[13], LOAD_FILE.title[14], LOAD_FILE.title[15], LOAD_FILE.title[16], LOAD_FILE.title[17], LOAD_FILE.title[18], LOAD_FILE.title[19]);
		fixPrintf( 3,24,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data[0],  LOAD_FILE.data[1],  LOAD_FILE.data[2],  LOAD_FILE.data[3],  LOAD_FILE.data[4],  LOAD_FILE.data[5],  LOAD_FILE.data[6],  LOAD_FILE.data[7],  LOAD_FILE.data[8], LOAD_FILE.data[9]);
		fixPrintf( 3,25,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data[10], LOAD_FILE.data[11], LOAD_FILE.data[12], LOAD_FILE.data[13], LOAD_FILE.data[14], LOAD_FILE.data[15], LOAD_FILE.data[16], LOAD_FILE.data[17], LOAD_FILE.data[18], LOAD_FILE.data[19]);
		fixPrintf( 3,26,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data[20], LOAD_FILE.data[21], LOAD_FILE.data[22], LOAD_FILE.data[23], LOAD_FILE.data[24], LOAD_FILE.data[25], LOAD_FILE.data[26], LOAD_FILE.data[27], LOAD_FILE.data[28], LOAD_FILE.data[29]);
		fixPrintf( 3,27,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data[30], LOAD_FILE.data[31], LOAD_FILE.data[32], LOAD_FILE.data[33], LOAD_FILE.data[34], LOAD_FILE.data[35], LOAD_FILE.data[36], LOAD_FILE.data[37], LOAD_FILE.data[38], LOAD_FILE.data[39]);
		fixPrintf( 3,28,1,3,"%d%d%d%d",             LOAD_FILE.data[40], LOAD_FILE.data[41], LOAD_FILE.data[42], LOAD_FILE.data[43]);

		fixPrintf(15,22,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[0],  LOAD_FILE.data2[1],  LOAD_FILE.data2[2],  LOAD_FILE.data2[3],  LOAD_FILE.data2[4],  LOAD_FILE.data2[5],  LOAD_FILE.data2[6],  LOAD_FILE.data2[7],  LOAD_FILE.data2[8],  LOAD_FILE.data2[9]);
		fixPrintf(15,23,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[10], LOAD_FILE.data2[11], LOAD_FILE.data2[12], LOAD_FILE.data2[13], LOAD_FILE.data2[14], LOAD_FILE.data2[15], LOAD_FILE.data2[16], LOAD_FILE.data2[17], LOAD_FILE.data2[18], LOAD_FILE.data2[19]);
		fixPrintf(15,24,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[20], LOAD_FILE.data2[21], LOAD_FILE.data2[22], LOAD_FILE.data2[23], LOAD_FILE.data2[24], LOAD_FILE.data2[25], LOAD_FILE.data2[26], LOAD_FILE.data2[27], LOAD_FILE.data2[28], LOAD_FILE.data2[29]);
		fixPrintf(15,25,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[30], LOAD_FILE.data2[31], LOAD_FILE.data2[32], LOAD_FILE.data2[33], LOAD_FILE.data2[34], LOAD_FILE.data2[35], LOAD_FILE.data2[36], LOAD_FILE.data2[37], LOAD_FILE.data2[38], LOAD_FILE.data2[39]);
		fixPrintf(15,26,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[40], LOAD_FILE.data2[41], LOAD_FILE.data2[42], LOAD_FILE.data2[43], LOAD_FILE.data2[44], LOAD_FILE.data2[45], LOAD_FILE.data2[46], LOAD_FILE.data2[47], LOAD_FILE.data2[48], LOAD_FILE.data2[49]);
		fixPrintf(15,27,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data2[50], LOAD_FILE.data2[51], LOAD_FILE.data2[52], LOAD_FILE.data2[53], LOAD_FILE.data2[54], LOAD_FILE.data2[55], LOAD_FILE.data2[56], LOAD_FILE.data2[57], LOAD_FILE.data2[58], LOAD_FILE.data2[59]);
		fixPrintf(15,28,1,3,"%d%d%d%d",             LOAD_FILE.data2[60], LOAD_FILE.data2[61], LOAD_FILE.data2[62], LOAD_FILE.data2[63]);

		fixPrintf(27,22,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[0],  LOAD_FILE.data3[1],  LOAD_FILE.data3[2],  LOAD_FILE.data3[3],  LOAD_FILE.data3[4],  LOAD_FILE.data3[5],  LOAD_FILE.data3[6],  LOAD_FILE.data3[7],  LOAD_FILE.data3[8],  LOAD_FILE.data3[9]);
		fixPrintf(27,23,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[10], LOAD_FILE.data3[11], LOAD_FILE.data3[12], LOAD_FILE.data3[13], LOAD_FILE.data3[14], LOAD_FILE.data3[15], LOAD_FILE.data3[16], LOAD_FILE.data3[17], LOAD_FILE.data3[18], LOAD_FILE.data3[19]);
		fixPrintf(27,24,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[20], LOAD_FILE.data3[21], LOAD_FILE.data3[22], LOAD_FILE.data3[23], LOAD_FILE.data3[24], LOAD_FILE.data3[25], LOAD_FILE.data3[26], LOAD_FILE.data3[27], LOAD_FILE.data3[28], LOAD_FILE.data3[29]);
		fixPrintf(27,25,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[30], LOAD_FILE.data3[31], LOAD_FILE.data3[32], LOAD_FILE.data3[33], LOAD_FILE.data3[34], LOAD_FILE.data3[35], LOAD_FILE.data3[36], LOAD_FILE.data3[37], LOAD_FILE.data3[38], LOAD_FILE.data3[39]);
		fixPrintf(27,26,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[40], LOAD_FILE.data3[41], LOAD_FILE.data3[42], LOAD_FILE.data3[43], LOAD_FILE.data3[44], LOAD_FILE.data3[45], LOAD_FILE.data3[46], LOAD_FILE.data3[47], LOAD_FILE.data3[48], LOAD_FILE.data3[49]);
		fixPrintf(27,27,1,3,"%d%d%d%d%d%d%d%d%d%d", LOAD_FILE.data3[50], LOAD_FILE.data3[51], LOAD_FILE.data3[52], LOAD_FILE.data3[53], LOAD_FILE.data3[54], LOAD_FILE.data3[55], LOAD_FILE.data3[56], LOAD_FILE.data3[57], LOAD_FILE.data3[58], LOAD_FILE.data3[59]);
		fixPrintf(27,28,1,3,"%d%d%d%d",             LOAD_FILE.data3[60], LOAD_FILE.data3[61], LOAD_FILE.data3[62], LOAD_FILE.data3[63]);

		SCClose();

	}while(!(p1&JOY_D));
}

int main(void)
{
	while(1)
	{
		memory_card();
	}
}

4

The CARD command isn't register safe according to the documentation, so you probably need to save/restore thoses.
__asm__ ("movem.l %d0-%d7/%a0-%a6,-(%sp) \n \ jsr 0xc00468 \n \ movem.l (%sp)+, %d0-%d7/%a0-%a6 \n");

Also hi-score savings is typically a MVS prerogative, avoiding to spread scores across systems with a card.

5

Hi HPMAN,
great, thank you for checking my code again and for posting this improvement top
HPMAN (./4) :
Also hi-score savings is typically a MVS prerogative, avoiding to spread scores across systems with a card.

Yes, I thought about this problem too and my plan is to save high-scores only on AES systems (to have something similar like the MVS backup ram).

6

Hi,

in the last days I have integrated the memory card load/save routine to my Hypernoid game but, unfortunately, I have found a strange problem gol
If I save data to the memory card after the game loop, the sound effects do not work when the game loop is re-loaded again (but the background music is playing).

What could be the reason for this? Maybe a register which holds the sound data gets overwritten by the memory card save routine?

7

You should only be saving to memory card during or at the end of the game over sequence. The bios takes care of everything after you set a few registers. Loading should happen at game start but if you want memory card saving or high score saving only then you should load during the game init sequence.

8

Hi Razoola,
thank you for your suggestion, I have tried to do the "save to card"-routine only in game over state but, unfortunately, the sound problem is still there.
By the way, if the sound effects are disabled by this issue only a system hard reset helps - if I do only a soft reset with UniBios the sound effects are still not working.

It seems that if I call the "save to card"-routine after the game loop was running at least one time something strange happens, if I call the "save to card"-routine directly
after a system start or a hard reset (before the game loop was loaded the first time) it works and I don't have that sound effects problem.

Also, the save data file on the card (if saved after the game loop) seems to be "corrupted" and loading this "corrupted" file after a new system start/hard reset will also disable all sound effects, even if I don't try to save.
Loading a "not corrupted" save data file from card (which has been saved before the game loop) works without that sound effects problem.

Here are some examples what happens:

System Start > Demo Mode > "Saving Data before Game Mode" > Title Mode > "Loading Data which was saved before Game Mode" > Game Mode = works (sound effects are not disabled)
System Start > Demo Mode > Title Mode > "Loading Data which was saved before Game Mode" > Game Mode = works (sound effects are not disabled)

System Start > Demo Mode > Title Mode > Game Mode > Game Over Mode > "Saving Data after Game Mode" > Demo Mode > Title Mode > Game Mode = not working (sound effects are disabled)
System Start > Demo Mode > Title Mode > "Loading Data which was saved after Game Mode" > Game Mode = not working (sound effects are disabled)

fou

9

Are sound codes sent to the Z80?

I don't see how the bios could interfere with the sound driver, there's just really that one byte register to play around with.
How is your sound managed? that's more likely your code glitches into something interfering with this grin

Tried different bios versions?

10

HPMAN, thank you for pointing me into this direction - you were right, I have found an uninitialized variable inside a struct for a
function I have written a long time ago to avoid "overloading" of the six sound effect channels. This variable was filled with random data
each time if a card call was made and this was the reason for the issue. Now this variable gets initialized with "0" upon game loop start
and the issue is gone tongue

11

Finally, memory card loading and saving works in Hypernoid - thanks a lot sun - without your help I would had never get this to work.
I have decided to give MVS/CMVS owners the option to save also the score of the last completed stage. To avoid highscore spamming
this option is disabled by default and can be enabled via soft dip settings if wanted.

12

Sorry for the delay in getting back to you, I have had family visiting. Glad you have the situation sorted. I guess the unibios soft reset issue with sound is also now fixed?

13

Yes, fortunately it is fixed now - the issue was caused by an uninitialized variable in my sound manager code.

14

Now, please prompt me, what I have to do for saving of some more certain - for example, the XP gained after victory. I just decided to add an initial game data saving system to the Shaman King project.
avatar