|
|
| (79 intermediate revisions by the same user not shown) |
| Line 1: |
Line 1: |
| − | {{WARNING|This page is still in the proposal stage and should not yet be considered an official specification.}}
| + | #REDICT [[NEO mapper/Extension]] |
| − | | |
| − | 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.
| |
| − | | |
| − | {|
| |
| − | |-
| |
| − | |
| |
| − | {| class="wikitable"
| |
| − | |+ ADDRESS
| |
| − | |-
| |
| − | ! 15 || 14 || 13 || 12 || 11 || 10 || 9 || 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0
| |
| − | |-
| |
| − | |style="background-color:#BBB;" colspan="2"| Page
| |
| − | |style="background-color:#FAA;"| 0
| |
| − | |style="background-color:#FAA;"| 0
| |
| − | |style="background-color:#FAA;"| 0
| |
| − | |style="background-color:#AAF;" colspan="3"| Bank
| |
| − | |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
| |
| − | |}
| |
| − | |
| |
| − | {| class="wikitable"
| |
| − | |+ DATA
| |
| − | |-
| |
| − | ! 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0
| |
| − | |-
| |
| − | |style="background-color:#AAF;" colspan="8"| Parameter
| |
| − | |}
| |
| − | |}
| |
| − | | |
| − | The implementation of functions 0 and 1 is mandatory. All others are optional. Function 1 provides a list of the functions supported by the cartridge.
| |
| − | | |
| − | === Functions ===
| |
| − | ==== 0x00: ROM mapper (mandatory) ====
| |
| − | Activate access to the cartridge ROM mapper through the given bank (default mode)
| |
| − | | |
| − | Parameter:
| |
| − | {| class="wikitable"
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |}
| |
| − | | |
| − | ==== 0x01: Cartridge information (mandatory) ====
| |
| − | Activate read access to the cartridge information data through the given bank (256 bytes mirrored)
| |
| − | | |
| − | Parameter:
| |
| − | {| class="wikitable"
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |}
| |
| − | | |
| − | {| class="wikitable"
| |
| − | !rowspan="2"| Offset !!rowspan="2"| Size !!rowspan="2"| Description !!colspan="8"| Content
| |
| − | |-
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | | 0 || 3 || Signature ||colspan="8"| "NEO"
| |
| − | |-
| |
| − | | 3 || 1 || Version ||colspan="4"| Major ||colspan="4"| Minor
| |
| − | |-
| |
| − | | 4 || 1
| |
| − | | Flag
| |
| − | * S: Bank size (0=8KB, 1=16KB)
| |
| − | | 0 || 0 || 0 || 0 || 0 || 0 || 0 || S
| |
| − | |-
| |
| − | | 5 || 1 || ''Reserved'' ||colspan="8"| 0
| |
| − | |-
| |
| − | | 6 || 2 || Number of segments ||colspan="8"| 4~16384
| |
| − | |-
| |
| − | | 8 || 8 || ''Reserved'' ||colspan="8"| 0
| |
| − | |-
| |
| − | | 16 || 1 || Function 0 support (ROM) ||colspan="8"| 1 (mandatory)
| |
| − | |-
| |
| − | | 17 || 1 || Function 1 support (Info) ||colspan="8"| 1 (mandatory)
| |
| − | |-
| |
| − | | 18 || 1 || Function 2 support ||colspan="8"|
| |
| − | |-
| |
| − | | 19 || 1 || Function 3 support (RAM) ||colspan="8"|
| |
| − | |-
| |
| − | | 20 || 1 || Function 4 support (SRAM) ||colspan="8"|
| |
| − | |-
| |
| − | | 21 || 1 || Function 5 support ||colspan="8"|
| |
| − | |-
| |
| − | | 22 || 1 || Function 6 support ||colspan="8"|
| |
| − | |-
| |
| − | | 23 || 1 || Function 7 support (SCC) ||colspan="8"|
| |
| − | |-
| |
| − | | 24 || 1 || Function 8 support (SPC700) ||colspan="8"|
| |
| − | |-
| |
| − | | 25 || 1 || Function 9 support ||colspan="8"|
| |
| − | |-
| |
| − | | 26 || 1 || Function 10 support ||colspan="8"|
| |
| − | |-
| |
| − | | 27 || 1 || Function 11 support ||colspan="8"|
| |
| − | |-
| |
| − | | 28 || 1 || Function 12 support ||colspan="8"|
| |
| − | |-
| |
| − | | 29 || 1 || Function 13 support ||colspan="8"|
| |
| − | |-
| |
| − | | 30 || 1 || Function 14 support ||colspan="8"|
| |
| − | |-
| |
| − | | 31 || 1 || Function 15 support ||colspan="8"|
| |
| − | |-
| |
| − | | 32 || 224 || ''Reserved'' ||colspan="8"| 0
| |
| − | |}
| |
| − | | |
| − | ==== 0x02: ''Reserved'' ====
| |
| − | | |
| − | ==== 0x03: RAM ====
| |
| − | Activate read/write access to RAM through the given bank
| |
| − | | |
| − | Parameter:
| |
| − | {| class="wikitable"
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | |style="background-color:#AAF;" colspan="8"| RAM segment
| |
| − | |}
| |
| − | | |
| − | ==== 0x04: SRAM/FRAM ====
| |
| − | Activate read/write access to SRAM/FRAM through the given bank (8 KB mirrored on NEO-16)
| |
| − | | |
| − | Parameter:
| |
| − | {| class="wikitable"
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | |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"| 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
| |
| − | | |
| − | {| class="wikitable"
| |
| − | ! 8 || 7 || 6 || 5 || 4 || 3 || 2 || 1
| |
| − | |-
| |
| − | |style="background-color:#AAF;"| C
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 0
| |
| − | |style="background-color:#BBB;"| 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:
| |
| − | <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
| |
| − | 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;
| |
| − | }
| |
| − | </syntaxhighlight>
| |