Difference between revisions of "NEO mapper/Extension2"

From MSX Game Library

< NEO mapper

(→‎0x07: SCC/SCC+)
 
(10 intermediate revisions by the same user not shown)
Line 7: Line 7:
 
=== Principles ===
 
=== Principles ===
  
−
Any write operation to the NEO mapper with address bits 10-12 set to 0 is considered as access to extended features. In that case, the mapper bank register is not modified.
+
Any write operation to the NEO mapper with address bits 11-13 set to 0 is considered as access to extended features. In that case, the mapper bank register is not modified.
  
 
All other write operations should be considered normal access to the mapper's bank register.
 
All other write operations should be considered normal access to the mapper's bank register.
  
−
The address contains an 8-bit function ID, and a 3-bit destination bank value for function to be applied to a given mapper's bank.
+
The address contains an 4-bit function ID, and a 3-bit destination bank value for function that applied to a given mapper's bank.
 +
 
 +
The data can be use by the function as a parameter.
  
 
{|
 
{|
Line 26: Line 28:
 
|style="background-color:#FAA;"| 0
 
|style="background-color:#FAA;"| 0
 
|style="background-color:#AAF;" colspan="3"| Bank
 
|style="background-color:#AAF;" colspan="3"| Bank
−
|style="background-color:#AAF;" colspan="8"| Function
+
|style="background-color:#BBB;"| 0
 +
|style="background-color:#BBB;"| 0
 +
|style="background-color:#BBB;"| 0
 +
|style="background-color:#BBB;"| 0
 +
|style="background-color:#AAF;" colspan="4"| Function
 
|}
 
|}
 
|
 
|
Line 101: Line 107:
 
==== 0x05: ''Reserved'' ====
 
==== 0x05: ''Reserved'' ====
  
−
==== 0x07: ''Reserved'' ====
+
==== 0x06: ''Reserved'' ====
  
 
==== 0x07: SCC/SCC+ ====
 
==== 0x07: SCC/SCC+ ====
Line 119: Line 125:
 
|style="background-color:#BBB;"| 0
 
|style="background-color:#BBB;"| 0
 
|}
 
|}
 +
* C: 0=SCC, 1=SCC+
  
 
== Appendix ==
 
== Appendix ==
Line 126: Line 133:
 
NEO-16 mapper:
 
NEO-16 mapper:
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
−
const uint8* romData; // ROM data
+
#define BANK_NUM 3
−
uint16 bankValue[3]; // Bank switching register value
+
#define BANK_SIZE (16 * 1024)
 +
#define ROM_SEG_NUM 1024
 +
#define ROM_SEG_MASK (ROM_SEG_NUM - 1)
 +
#define ROM_SIZE (ROM_SEG_NUM * BANK_SIZE)
 +
#define FUNC_NUM 16
  
 
// Extended function prototypes
 
// Extended function prototypes
−
typedef void (*funcEnable_t)(bool, uint8);
+
typedef void (*funcSelect_t)(uint8, uint8);
 
typedef void (*funcWrite_t)(uint16, uint8);
 
typedef void (*funcWrite_t)(uint16, uint8);
 
typedef uint8 (*funcRead_t)(uint16);
 
typedef uint8 (*funcRead_t)(uint16);
−
// Extended functions table
+
 
−
funcEnable_t funcEnableTable[32];
+
// Extended functions tables
−
funcWrite_t funcWriteTable[3];
+
funcSelect_t funcSelectTable[FUNC_NUM];
−
funcRead_t funcReadTable[3];
+
funcWrite_t funcWriteTable[BANK_NUM];
 +
funcRead_t funcReadTable[BANK_NUM];
 +
 
 +
// Supported extended function initialization
 +
void initMapper();
 +
void initInfo();
 +
void initRAM();
 +
void initSRAM();
 +
 
 +
//=============================================================================
 +
// NEO-16 Interface
 +
//=============================================================================
  
 
// Handle device initialization
 
// Handle device initialization
 
void NEO16_Init()
 
void NEO16_Init()
 
{
 
{
−
for (uint8 i = 0; i < 3; i++)
+
// Mandatory functions
−
{
+
initMapper();
−
bankValue[i] = 0x0000;
+
initInfo();
−
funcWriteTable[i] = NULL;
+
// Optional functions
−
funcReadTable[i] = NULL;
+
initRAM();
−
}
+
initSRAM();
−
for (uint8 i = 0; i < 32; i++)
 
−
funcEnableTable[i] = NULL;
 
 
}
 
}
  
 
// Handle write access
 
// Handle write access
−
void NEO16_Write(uint16 address, uint8 value)
+
void NEO16_Write(uint16 address, uint8 data)
 
{
 
{
 
uint8 bank = ((address >> 12) & 0x03) - 1; // Get the corresponding bank number
 
uint8 bank = ((address >> 12) & 0x03) - 1; // Get the corresponding bank number
−
if (bank > 2)
+
if (bank < BANK_NUM)
−
return; // skip
 
−
 
 
−
if (funcWriteTable[bank] != NULL) // Check for bank write callback
 
−
{
 
−
funcWriteTable[bank](address, value);
 
−
}
 
−
else if ((address & 0x0001) && (value & 0x80)) // Handle extended function
 
 
{
 
{
−
uint8 funcId = value & 0x3F; // Get function ID
+
funcWriteTable[bank](address, data);
−
if (funcEnableTable[funcId] != NULL)
 
−
funcEnableTable[funcId](value & 0x40, (address >> 1) & 0xFF); // Execute function specific code
 
 
}
 
}
−
else // Write to the given bank register
+
else // Extended function
 
{
 
{
−
if (address & 1) // Set bank register MSB
+
uint8 funcId = address & 0xFF; // Get function ID
−
bankValue[bank] = ((value & 0x0F) << 8) | (bankValue[bank] & 0x00FF);
+
bank = (address >> 8) & 0x07; // Get destination bank
−
else // Set bank register LSB
+
if ((bank < BANK_NUM) && (funcSelectTable[funcId] != NULL)) // Is function supported?
−
bankValue[bank] = (bankValue[bank] & 0xFF00) | (value);
+
funcSelectTable[funcId](bank, data); // Initialize function specific code
 
}
 
}
 
}
 
}
Line 181: Line 192:
 
{
 
{
 
uint8 bank = address >> 14; // Get the corresponding bank number
 
uint8 bank = address >> 14; // Get the corresponding bank number
−
if (bank > 2)
+
if (bank < BANK_NUM)
−
return 0xFF; // skip
+
return funcReadTable[bank](address);
  
−
if (funcReadTable[bank] != NULL) // Check for bank read callback
+
return 0xFF; // skip
−
return funcReadTable[bank](address);
 
−
else
 
−
return romData[(bankValue[bank]) << 14 + (address & 0x3FFF)];
 
 
}
 
}
−
</syntaxhighlight>
 
  
−
=== Extension example ===
+
//=============================================================================
−
Here is a pseudo code in C to handle a SRAM extension for NEO mapper:
+
// Function 0x00 - ROM Mapper
 +
//=============================================================================
 +
 
 +
// ROM data
 +
extern const uint8* romData;
 +
 
 +
// Bank switching register value
 +
uint16 romSegment[BANK_NUM];
  
−
<syntaxhighlight lang="c">
+
void writeMapper(uint16 address, uint8 data)
−
uint8 sramSeg; // Current SRAM segment [0:15]
+
{
−
uint8 sramBank; // Current bank where SRAM is selected (0xFF if not selected)
+
if (address & 1) // Set bank register MSB
−
uint8* sramData; // SRAM data (hardware specific)
+
romSegment[bank] = ((data & 0x0F) << 8) | (romSegment[bank] & 0x00FF);
 +
else // Set bank register LSB
 +
romSegment[bank] = (romSegment[bank] & 0xFF00) | (data);
 +
}
  
−
// Write to SRAM
+
uint8 readMapper(uint16 address)
−
void SRAM_Write(uint16 address, uint8 value)
 
 
{
 
{
−
sramData[(sramSeg * 0x2000) + (address & 0x1FFF)] = value; //
+
return romData[(romSegment[bank]) << 14 + (address & 0x3FFF)];
 
}
 
}
  
−
// Read from SRAM
+
void selectMapper(uint8 bank, uint8 data)
−
uint8 SRAM_Read(uint16 address)
 
 
{
 
{
−
return sramData[(sramSeg * 0x2000) + (address & 0x1FFF)];
+
funcWriteTable[bank] = writeMapper;
 +
funcReadTable[bank] = readMapper;
 
}
 
}
  
−
// Enable/disable SRAM feature
+
void initMapper()
−
void SRAM_Enable(bool enable, uint8 param)
 
 
{
 
{
−
if (sramBank != 0xFF) // Disable previous SRAM registration
+
for (uint8 i = 0; i < BANK_NUM; i++)
 
{
 
{
−
if (funcWriteTable[sramBank] == SRAM_Write)
+
romSegment[i] = 0x0000;
−
funcWriteTable[sramBank] = NULL;
+
selectMapper(i, 0);
−
if (funcReadTable[sramBank] == SRAM_Read)
 
−
funcReadTable[sramBank] = NULL;
 
−
sramBank = 0xFF;
 
 
}
 
}
 +
funcSelectTable[0] = selectMapper;
 +
}
 +
 +
//=============================================================================
 +
// Function 0x01 - Cartridge Information
 +
//=============================================================================
 +
 +
struct info
 +
{
 +
char Sign[3];
 +
uint8 Version;
 +
uint8 Flag;
 +
uint8 _reserved1;
 +
uint16 SegmentNum;
 +
uint8 _reserved2[8];
 +
uint8 Functions[FUNC_NUM]; // Must be different from 0 for supported functions
 +
};
  
−
if (enable) // Enable a SRAM segment in the given bank
+
const struct infoData =
 +
{
 +
{ 'N', 'E', 'O' },
 +
0x13, // Version 1.3
 +
0x01, // NEO-16
 +
0,
 +
ROM_SEG_NUM, // 1204 segments (16 MB)
 +
{ 0, 0, 0, 0, 0, 0, 0, 0 },
 
{
 
{
−
uint8 bank = param & 0x07; // Get bank number
+
1, // Mapper
−
if (bank > 2)
+
1, // Info
−
return; // skip (for NEO-16)
+
0,
 +
32, // RAM
 +
32, // SRAM/FRAM
 +
0,
 +
0,
 +
0, // SCC
 +
0,
 +
0,
 +
0,
 +
0,
 +
0,
 +
0,
 +
0,
 +
0,
 +
},
 +
};
 +
 
 +
void writeInfo(uint16 address, uint8 data) {}
 +
 
 +
uint8 readInfo(uint16 address)
 +
{
 +
uint8 index = address & 0xFF; // mirrored every 256 bytes
 +
if (index > sizeof(info))
 +
return 0;
 +
return ((const uint8*)&infoData)[index];
 +
}
 +
 
 +
void selectInfo(uint8 bank, uint8 data)
 +
{
 +
funcWriteTable[bank] = writeInfo;
 +
funcReadTable[bank] = readInfo;
 +
}
 +
 
 +
void initInfo()
 +
{
 +
funcSelectTable[1] = selectInfo;
 +
}
 +
 
 +
//=============================================================================
 +
// Function 0x03 - RAM
 +
//=============================================================================
 +
 
 +
#define RAM_SEG_NUM 32
 +
#define RAM_SEG_MASK (RAM_SEG_NUM - 1)
 +
 
 +
// RAM data
 +
uint8 ramData[16 * 1024 * RAM_SEG_NUM]; // 32 * 16 KB = 512 KB
 +
 
 +
// Bank switching register value
 +
uint8 ramSegment[BANK_NUM];
 +
 
 +
void writeRAM(uint16 address, uint8 data)
 +
{
 +
ramData[(ramSegment[bank]) << 14 + (address & 0x3FFF)] = data;
 +
}
 +
 
 +
uint8 readRAM(uint16 address)
 +
{
 +
return ramData[(ramSegment[bank]) << 14 + (address & 0x3FFF)];
 +
}
 +
 
 +
void selectRAM(uint8 bank, uint8 data)
 +
{
 +
ramSegment[bank] = data & RAM_SEG_MASK;
 +
funcWriteTable[bank] = writeRAM;
 +
funcReadTable[bank] = readRAM;
 +
}
 +
 
 +
void initRAM()
 +
{
 +
for (uint8 i = 0; i < BANK_NUM; i++)
 +
ramSegment = 0;
 +
funcSelectTable[3] = selectRAM;
 +
}
 +
 
 +
//=============================================================================
 +
// Function 0x04 - SRAM
 +
//=============================================================================
 +
 
 +
#define SRAM_SEG_NUM 16
 +
#define SRAM_SEG_MASK (SRAM_SEG_NUM - 1)
 +
 
 +
// SRAM data
 +
uint8 sramData[8 * 1024 * SRAM_SEG_NUM]; // 16 * 8 KB = 128 KB
 +
 
 +
// Bank switching register value
 +
uint8 sramSegment[BANK_NUM];
 +
 
 +
void writeSRAM(uint16 address, uint8 data)
 +
{
 +
sramData[(sramSegment[bank]) << 13 + (address & 0x1FFF)] = data;
 +
}
 +
 
 +
uint8 readSRAM(uint16 address)
 +
{
 +
return sramData[(sramSegment[bank]) << 13 + (address & 0x1FFF)];
 +
}
  
−
sramSeg = (param >> 4) & 0x0F; // SRAM segment number
+
void selectSRAM(uint8 bank, uint8 data)
−
sramBank = bank;
+
{
−
funcWriteTable[sramBank] = SRAM_Write; // Register SRAM callbacks
+
sramSegment[bank] = data & SRAM_SEG_MASK;
−
funcReadTable[sramBank] = SRAM_Read;
+
funcWriteTable[bank] = writeSRAM;
−
}
+
funcReadTable[bank] = readSRAM;
 
}
 
}
  
−
// Initialize SRAM feature
+
void initSRAM()
−
void SRAM_Init()
 
 
{
 
{
−
sramBank = 0xFF;
+
for (uint8 i = 0; i < BANK_NUM; i++)
−
funcEnableTable[0x04] = SRAM_Enable; // Register to function 0x04 (SRAM/FRAM feature)
+
sramSegment = 0;
 +
funcSelectTable[4] = selectSRAM;
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
−
 
−
Then, you just need to call <tt>SRAM_Init()</tt> from <tt>NEO16_Init()</tt> to register the extended feature.
 

Latest revision as of 00:40, 21 October 2026

⚠️ This page is still in the proposal stage and should not yet be considered an official specification.

Proposal for an extension to the NEO mapper format.

Specifications

Principles

Any write operation to the NEO mapper with address bits 11-13 set to 0 is considered as access to extended features. In that case, the mapper bank register is not modified.

All other write operations should be considered normal access to the mapper's bank register.

The address contains an 4-bit function ID, and a 3-bit destination bank value for function that applied to a given mapper's bank.

The data can be use by the function as a parameter.

ADDRESS
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Page 0 0 0 Bank 0 0 0 0 Function
DATA
7 6 5 4 3 2 1 0
Parameter

Functions

0x00: ROM mapper

Activate access to the cartridge ROM mapper through the given bank (default mode)

Parameter:

8 7 6 5 4 3 2 1
0 0 0 0 0 0 0 0

0x01: Cartridge information

Activate read access to the cartridge information data through the given bank (8 KB mirrored on NEO-16)

Parameter:

8 7 6 5 4 3 2 1
0 0 0 0 0 0 0 0

0x02: Reserved

0x03: RAM

Activate read/write access to RAM through the given bank

Parameter:

8 7 6 5 4 3 2 1
RAM segment

0x04: SRAM/FRAM

Activate read/write access to SRAM/FRAM through the given bank (8 KB mirrored on NEO-16)

Parameter:

8 7 6 5 4 3 2 1
0 0 0 0 Segment

0x05: Reserved

0x06: Reserved

0x07: SCC/SCC+

Activate SCC to be accessed through memory area from 9800h to 9FFFh (4 KB mirrored) Any bank number is valid, but bank #4 is recommanded for NEO-8 and #2 for NEO-16 to use standard address for

8 7 6 5 4 3 2 1
C 0 0 0 0 0 0 0
  • C: 0=SCC, 1=SCC+

Appendix

Programmable processor and emulation

Here is a pseudo code in C to handle NEO mappers extension for programmable processor based cartridges or for emulators:

NEO-16 mapper:

#define BANK_NUM				3
#define BANK_SIZE				(16 * 1024)
#define ROM_SEG_NUM				1024
#define ROM_SEG_MASK			(ROM_SEG_NUM - 1)
#define ROM_SIZE				(ROM_SEG_NUM * BANK_SIZE)
#define FUNC_NUM				16

// Extended function prototypes
typedef void (*funcSelect_t)(uint8, uint8);
typedef void (*funcWrite_t)(uint16, uint8);
typedef uint8 (*funcRead_t)(uint16);

// Extended functions tables
funcSelect_t funcSelectTable[FUNC_NUM];
funcWrite_t funcWriteTable[BANK_NUM];
funcRead_t funcReadTable[BANK_NUM];

// Supported extended function initialization
void initMapper();
void initInfo();
void initRAM();
void initSRAM();

//=============================================================================
// NEO-16 Interface
//=============================================================================

// Handle device initialization
void NEO16_Init()
{
	// Mandatory functions
	initMapper();
	initInfo();
	// Optional functions
	initRAM();
	initSRAM();
}

// Handle write access
void NEO16_Write(uint16 address, uint8 data)
{
	uint8 bank = ((address >> 12) & 0x03) - 1; // Get the corresponding bank number
	if (bank < BANK_NUM)
	{
		funcWriteTable[bank](address, data);
	}
	else // Extended function
	{
		uint8 funcId = address & 0xFF; // Get function ID
		bank = (address >> 8) & 0x07; // Get destination bank
		if ((bank < BANK_NUM) && (funcSelectTable[funcId] != NULL)) // Is function supported?
			funcSelectTable[funcId](bank, data); // Initialize function specific code
	}
}

// Handle read access
uint8 NEO16_Read(uint16 address)
{
	uint8 bank = address >> 14; // Get the corresponding bank number
	if (bank < BANK_NUM)
		return funcReadTable[bank](address);

	return 0xFF; // skip
}

//=============================================================================
// Function 0x00 - ROM Mapper
//=============================================================================

// ROM data
extern const uint8* romData; 

// Bank switching register value
uint16 romSegment[BANK_NUM];

void writeMapper(uint16 address, uint8 data)
{
	if (address & 1) // Set bank register MSB
		romSegment[bank] = ((data & 0x0F) << 8) | (romSegment[bank] & 0x00FF);
	else // Set bank register LSB
		romSegment[bank] = (romSegment[bank] & 0xFF00) | (data);
}

uint8 readMapper(uint16 address)
{
	return romData[(romSegment[bank]) << 14 + (address & 0x3FFF)];
}

void selectMapper(uint8 bank, uint8 data)
{
	funcWriteTable[bank] = writeMapper;
	funcReadTable[bank] = readMapper;		
}

void initMapper()
{
	for (uint8 i = 0; i < BANK_NUM; i++)
	{
		romSegment[i] = 0x0000;
		selectMapper(i, 0);		
	}
	funcSelectTable[0] = selectMapper;
}
	
//=============================================================================
// Function 0x01 - Cartridge Information
//=============================================================================

struct info
{
	char	Sign[3];
	uint8	Version;
	uint8	Flag;
	uint8	_reserved1;
	uint16	SegmentNum;
	uint8	_reserved2[8];
	uint8	Functions[FUNC_NUM]; // Must be different from 0 for supported functions 
};

const struct infoData = 
{
	{ 'N', 'E', 'O' },
	0x13, // Version 1.3
	0x01, // NEO-16
	0,
	ROM_SEG_NUM, // 1204 segments (16 MB)
	{ 0, 0, 0, 0, 0, 0, 0, 0 },
	{
		1, // Mapper
		1, // Info
		0,
		32,	// RAM
		32, // SRAM/FRAM
		0,
		0,
		0, // SCC
		0,
		0,
		0,
		0,		
		0,
		0,
		0,
		0,
	},
};

void writeInfo(uint16 address, uint8 data) {}

uint8 readInfo(uint16 address)
{
	uint8 index = address & 0xFF; // mirrored every 256 bytes
	if (index > sizeof(info))
		return 0;
	return ((const uint8*)&infoData)[index]; 
}

void selectInfo(uint8 bank, uint8 data)
{
	funcWriteTable[bank] = writeInfo;
	funcReadTable[bank] = readInfo;		
}

void initInfo()
{
	funcSelectTable[1] = selectInfo;
}

//=============================================================================
// Function 0x03 - RAM
//=============================================================================

#define RAM_SEG_NUM				32
#define RAM_SEG_MASK			(RAM_SEG_NUM - 1)

// RAM data
uint8 ramData[16 * 1024 * RAM_SEG_NUM]; // 32 * 16 KB = 512 KB

// Bank switching register value
uint8 ramSegment[BANK_NUM];

void writeRAM(uint16 address, uint8 data)
{
	ramData[(ramSegment[bank]) << 14 + (address & 0x3FFF)] = data;
}

uint8 readRAM(uint16 address)
{
	return ramData[(ramSegment[bank]) << 14 + (address & 0x3FFF)];
}

void selectRAM(uint8 bank, uint8 data)
{
	ramSegment[bank] = data & RAM_SEG_MASK;
	funcWriteTable[bank] = writeRAM;
	funcReadTable[bank] = readRAM;		
}

void initRAM()
{
	for (uint8 i = 0; i < BANK_NUM; i++)
		ramSegment = 0;
	funcSelectTable[3] = selectRAM;
}

//=============================================================================
// Function 0x04 - SRAM
//=============================================================================

#define SRAM_SEG_NUM			16
#define SRAM_SEG_MASK			(SRAM_SEG_NUM - 1)

// SRAM data
uint8 sramData[8 * 1024 * SRAM_SEG_NUM]; // 16 * 8 KB = 128 KB

// Bank switching register value
uint8 sramSegment[BANK_NUM];

void writeSRAM(uint16 address, uint8 data)
{
	sramData[(sramSegment[bank]) << 13 + (address & 0x1FFF)] = data;
}

uint8 readSRAM(uint16 address)
{
	return sramData[(sramSegment[bank]) << 13 + (address & 0x1FFF)];
}

void selectSRAM(uint8 bank, uint8 data)
{
	sramSegment[bank] = data & SRAM_SEG_MASK;
	funcWriteTable[bank] = writeSRAM;
	funcReadTable[bank] = readSRAM;		
}

void initSRAM()
{
	for (uint8 i = 0; i < BANK_NUM; i++)
		sramSegment = 0;
	funcSelectTable[4] = selectSRAM;
}