Difference between revisions of "NEO mapper/Extension2"
From MSX Game Library
(→Principles) |
(→Programmable processor and emulation) |
||
| Line 130: | Line 130: | ||
NEO-16 mapper: | NEO-16 mapper: | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| − | + | #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 | // Extended function prototypes | ||
| − | typedef void (* | + | 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); | ||
| + | |||
| + | // Supported extended functions | ||
| + | void InitMapper(); | ||
| + | void selectMapper(uint8 bank, uint8 data); | ||
| + | void selectInfo(uint8 bank, uint8 data); | ||
| + | void selectRAM(uint8 bank, uint8 data); | ||
| + | void selectSRAM(uint8 bank, uint8 data); | ||
| + | |||
// Extended functions table | // Extended functions table | ||
| − | + | const funcSelect_t funcSelectTable[FUNC_NUM] = { | |
| − | funcWrite_t funcWriteTable[ | + | selectMapper, |
| − | funcRead_t funcReadTable[ | + | selectInfo, |
| + | NULL, | ||
| + | selectRAM, | ||
| + | selectSRAM, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | NULL, | ||
| + | }; | ||
| + | funcWrite_t funcWriteTable[BANK_NUM]; | ||
| + | funcRead_t funcReadTable[BANK_NUM]; | ||
| + | |||
| + | //============================================================================= | ||
| + | // NEO-16 Interface | ||
| + | //============================================================================= | ||
// Handle device initialization | // Handle device initialization | ||
void NEO16_Init() | void NEO16_Init() | ||
{ | { | ||
| − | + | InitMapper(); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
} | } | ||
// Handle write access | // Handle write access | ||
| − | void NEO16_Write(uint16 address, uint8 | + | 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 | + | if (bank < BANK_NUM) |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
{ | { | ||
| − | + | funcWriteTable[bank](address, data); | |
| − | |||
| − | |||
} | } | ||
| − | else // | + | 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 | |
} | } | ||
} | } | ||
| Line 185: | Line 202: | ||
{ | { | ||
uint8 bank = address >> 14; // Get the corresponding bank number | uint8 bank = address >> 14; // Get the corresponding bank number | ||
| − | if (bank | + | if (bank < BANK_NUM) |
| − | return | + | return funcReadTable[bank](address); |
| − | if ( | + | return 0xFF; // skip |
| − | return funcReadTable[bank](address); | + | } |
| − | + | ||
| − | return | + | //============================================================================= |
| + | // 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); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | //============================================================================= | ||
| + | // 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; | ||
| + | } | ||
| + | |||
| + | //============================================================================= | ||
| + | // 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; | ||
| + | } | ||
| + | |||
| + | //============================================================================= | ||
| + | // 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; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 23:56, 3 October 2026
Proposal for an extension to the NEO mapper format.
Contents
Specifications
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.
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.
|
| ||||||||||||||||||||||||||||||||||||||||||||||||
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 |
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);
// Supported extended functions
void InitMapper();
void selectMapper(uint8 bank, uint8 data);
void selectInfo(uint8 bank, uint8 data);
void selectRAM(uint8 bank, uint8 data);
void selectSRAM(uint8 bank, uint8 data);
// Extended functions table
const funcSelect_t funcSelectTable[FUNC_NUM] = {
selectMapper,
selectInfo,
NULL,
selectRAM,
selectSRAM,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
funcWrite_t funcWriteTable[BANK_NUM];
funcRead_t funcReadTable[BANK_NUM];
//=============================================================================
// NEO-16 Interface
//=============================================================================
// Handle device initialization
void NEO16_Init()
{
InitMapper();
}
// 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
}
//=============================================================================
// 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);
}
}
//=============================================================================
// 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;
}
//=============================================================================
// 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;
}
//=============================================================================
// 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;
}
Extension example
Here is a pseudo code in C to handle a SRAM extension for NEO mapper:
uint8 sramSeg; // Current SRAM segment [0:15]
uint8 sramBank; // Current bank where SRAM is selected (0xFF if not selected)
uint8* sramData; // SRAM data (hardware specific)
// Write to SRAM
void SRAM_Write(uint16 address, uint8 value)
{
sramData[(sramSeg * 0x2000) + (address & 0x1FFF)] = value; //
}
// Read from SRAM
uint8 SRAM_Read(uint16 address)
{
return sramData[(sramSeg * 0x2000) + (address & 0x1FFF)];
}
// Enable/disable SRAM feature
void SRAM_Enable(bool enable, uint8 param)
{
if (sramBank != 0xFF) // Disable previous SRAM registration
{
if (funcWriteTable[sramBank] == SRAM_Write)
funcWriteTable[sramBank] = NULL;
if (funcReadTable[sramBank] == SRAM_Read)
funcReadTable[sramBank] = NULL;
sramBank = 0xFF;
}
if (enable) // Enable a SRAM segment in the given bank
{
uint8 bank = param & 0x07; // Get bank number
if (bank > 2)
return; // skip (for NEO-16)
sramSeg = (param >> 4) & 0x0F; // SRAM segment number
sramBank = bank;
funcWriteTable[sramBank] = SRAM_Write; // Register SRAM callbacks
funcReadTable[sramBank] = SRAM_Read;
}
}
// Initialize SRAM feature
void SRAM_Init()
{
sramBank = 0xFF;
funcEnableTable[0x04] = SRAM_Enable; // Register to function 0x04 (SRAM/FRAM feature)
}
Then, you just need to call SRAM_Init() from NEO16_Init() to register the extended feature.