NES 2.0 header for cc65
From NESdev Wiki
Jump to navigationJump to search
A cc65 compatible header file for generating a NES 2.0 with macros
The original ASM version for ca65 is available NES 2.0 header for ca65
/** * NES 2.0 header generator for cc65 (nes2header.h) * * USAGE: Generates a header for the NES2 format with C macros * * NES2_HEADER(segmentname) - Creates a NES header in the segment provided by segment name. * The values can be configured using all the of the options described below. * * EXAMPLE: * * #define NES2_MAPPER 4 // REQUIRED: Choose mapper number 4 * #define NES2_PRG 65536 // REQUIRED: With 64kb PRG ROM * #define NES2_BRAM 8192 // And 8kb PRG RAM * #define NES2_MIRROR 'V' // Vertical screen mirroring * #define NES2_TV 'N' // For NTSC tvs * NES2_HEADER("INESHDR"); // and now build the header into the segment named `INESHDR` * * DESCRIPTION: The following values can be defined to set the corresponding NES 2.0 header fields * * - NES2_MAPPER, NES2_SUBMAPPER * Sets the mapper (board class) ID. For example, MMC3 is usually * mapper 4, but TLSROM is 118 and TQROM is 119. Some mappers have * variants. * NO DEFAULT (Required) * Example: (sets the mapper to MMC3 with submapper variant 1 used by StarTropics) * #define NES2_MAPPER 4 * #define NES2_SUBMAPPER 1 * * - NES2_PRG * Sets the PRG ROM size to sz bytes. Must be multiple of 16384; * should be a power of 2. * NO DEFAULT (Required) * Example: #define NES2_PRG 131072 * * - NES2_CHR * Sets the CHR ROM size to sz bytes. Must be multiple of 8192; * should be a power of 2. * Default value: 0 * Example: #define NES2_CHR 32768 * * - NES2_WRAM * Sets the (not battery-backed) work RAM size in bytes. * Default is 0. * Example: #define NES2_WRAM 8192 * * - NES2_BRAM * Sets the battery-backed work RAM size in bytes. * Default is 0. * Example: #define NES2_BRAM 8192 * * - NES2_CHRBRAM * Sets the battery-backed CHR RAM size in bytes. * Default is 0. * Example: #define NES2_CHRBRAM 8192 * * - NES2_MIRROR * Sets the mirroring to one of these values: * 'H' (horizontal mirroring, vertical arrangement) * 'V' (vertical mirroring, horizontal arrangement) * '4' (four-screen VRAM) * '8' (four-screen and vertical bits on, primarily for mapper 218) * Default is 'H' * Example: #define NES2_MIRROR 'V' * * - NES2_TV * Sets the ROM's intended TV system: * 'N' for NTSC NES/FC/PC10 * 'P' for PAL NES * '1' for dual compatible, preferring NTSC * '2' for dual compatible, preferring PAL NES * Default is 'N' * Example: #define NES2_TV '1' * * Original ca65 macro Copyright 2016 Damian Yerrick * cc65 macro Copyright 2022 James Rowe * Copying and distribution of this file, with or without * modification, are permitted in any medium without royalty provided * the copyright notice and this notice are preserved in all source * code copies. This file is offered as-is, without any warranty. * */ #ifndef NES2HEADER_P_H #define NES2HEADER_P_H #ifndef NES2_SUBMAPPER #define NES2_SUBMAPPER 0x0 #endif //NES2_SUBMAPPER // Define the bytes for the mapper fields #define _NES2_MAPPER6 (((NES2_MAPPER) & 0x0F) << 4) #define _NES2_MAPPER7 ((NES2_MAPPER) & 0xF0) #define _NES2_MAPPER8 (((NES2_MAPPER) >> 8) | ((NES2_SUBMAPPER) << 4)) #define _NES2_PRG_SIZE (((NES2_PRG) / 16384) & 0xFF) #define _NES2_PRG_SIZE_HI ((((NES2_PRG) / 16384) >> 8) & 0xFF) // Define the defaults for the constants #ifdef NES2_CHR #define _NES2_CHR_SIZE (((NES2_CHR) / 8192) & 0xFF) #define _NES2_CHR_SIZE_HI ((((NES2_CHR) / 8192) >> 8) & 0xFF) #else #define _NES2_CHR_SIZE 0 #define _NES2_CHR_SIZE_HI 0 #endif //NES2_CHR_SIZE // Helper function: Computes ceil(log2(sz / 64)) Used for NES2 RAM sizes. #define _NES2_ERROR -1 #define _NES2_LOGSIZE(sz) \ (((sz) < 1L) ? 0 : \ ((sz) <= 128L) ? 1 : \ ((sz) <= 256L) ? 2 : \ ((sz) <= 512L) ? 3 : \ ((sz) <= 1024L) ? 4 : \ ((sz) <= 2048L) ? 5 : \ ((sz) <= 4096L) ? 6 : \ ((sz) <= 8192L) ? 7 : \ ((sz) <= 16384L) ? 8 : \ ((sz) <= 32768L) ? 9 : \ ((sz) <= 65536L) ? 10 : \ ((sz) <= 131072L) ? 11 : \ ((sz) <= 262144L) ? 12 : \ ((sz) <= 524288L) ? 13 : \ ((sz) <= 1048576L)? 14 : _NES2_ERROR) #define _NES2_MIRROR_HELPER(x) \ (((x) == 'h' || (x) == 'H') ? 0 : \ ((x) == 'v' || (x) == 'V') ? 1 : \ ((x) == '4') ? 8 : \ ((x) == '8') ? 9 : _NES2_ERROR) #ifdef NES2_MIRROR #define _NES2_MIRROR_OUT (_NES2_MIRROR_HELPER(NES2_MIRROR)) #else #define _NES2_MIRROR_OUT (_NES2_MIRROR_HELPER('H')) #endif //NES2_MIRROR #ifdef NES2_WRAM #define _NES2_WRAM_SIZE (_NES2_LOGSIZE(NES2_WRAM)) #else #define _NES2_WRAM_SIZE (_NES2_LOGSIZE(0)) #endif //NES2_WRAM #ifdef NES2_BRAM #define _NES2_BRAM_SIZE (_NES2_LOGSIZE(NES2_BRAM)) #else #define _NES2_BRAM_SIZE (_NES2_LOGSIZE(0)) #endif //NES2_BRAM #ifdef NES2_CHRBRAM #define _NES2_CHRBRAM_SIZE (_NES2_LOGSIZE(NES2_CHRBRAM)) #else #define _NES2_CHRBRAM_SIZE (_NES2_LOGSIZE(0)) #endif //NES2_CHRBRAM #ifdef NES2_CHRRAM #define _NES2_CHRRAM_SIZE (_NES2_LOGSIZE(NES2_CHRRAM)) #else // CHRRAM size depends on if the mapper has CHRBRAM or CHR already defined #define _NES2_CHRRAM_SIZE \ ((((_NES2_CHR_SIZE) + _NES2_CHR_SIZE_HI + (_NES2_CHRBRAM_SIZE)) > 0) ? 0 : _NES2_LOGSIZE(32768L)) #endif // Battery bit is set if we have either BRAM or CHRBRAM #define _NES2_BATTERY_BIT \ ((((_NES2_BRAM_SIZE) + (_NES2_CHRBRAM_SIZE)) > 0) ? 0x02 : 0x0) #define _NES2_TV_HELPER(x) \ (((x) == 'n' || (x) == 'N') ? 0 : \ ((x) == 'p' || (x) == 'P') ? 1 : \ ((x) == '1') ? 2 : \ ((x) == '2') ? 3 : -1) #ifdef NES2_TV #define _NES2_TV_SYSTEM _NES2_TV_HELPER(NES2_TV) #else #define _NES2_TV_SYSTEM _NES2_TV_HELPER('N') #endif //NES2_TV #define _NES2_STRINGIFY(a) #a #define NES2_HEADER(segment) \ _Pragma( _NES2_STRINGIFY( rodata-name ## ( ## push, ## segment ## ) ## ) ); \ _Static_assert (_NES2_WRAM_SIZE != _NES2_ERROR, "WRAM size must be 0 to 1048576"); \ _Static_assert (_NES2_BRAM_SIZE != _NES2_ERROR, "BRAM size must be 0 to 1048576"); \ _Static_assert (_NES2_CHRBRAM_SIZE != _NES2_ERROR, "CHRBRAM size must be 0 to 1048576"); \ _Static_assert (_NES2_MIRROR_OUT != _NES2_ERROR, "Mirroring mode must be 'H', 'V', '4', or '8'"); \ _Static_assert (_NES2_TV_SYSTEM != _NES2_ERROR, "TV system must be 'N', 'P', '1', or '2'"); \ const unsigned char nes2header[16] = {\ 'N', 'E', 'S', 0x1A,\ _NES2_PRG_SIZE, _NES2_CHR_SIZE,\ _NES2_MAPPER6 | _NES2_MIRROR_OUT | _NES2_BATTERY_BIT, \ _NES2_MAPPER7 | 0x08, \ _NES2_MAPPER8, \ (_NES2_CHR_SIZE_HI << 4) | _NES2_PRG_SIZE_HI, \ (_NES2_BRAM_SIZE << 4) | _NES2_WRAM_SIZE, \ (_NES2_CHRBRAM_SIZE << 4) | _NES2_CHRRAM_SIZE, \ _NES2_TV_SYSTEM, 0, 0, 0 \ };\ _Pragma("rodata-name(pop)"); #endif //NES2HEADER_P_H
Linker script requirement
Your linker configuration file will need to have a segment for the header in the first ROM memory area. This segment can have any name, but should have 16 bytes of size.
MEMORY { HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; # Other memory area definitions appropriate for your board } SEGMENTS { INESHDR: load = HEADER, type = ro, align = $10; # Other segment definitions appropriate for your board }
Examples
A CNROM board with horizontal mirroring (V pad) for NTSC systems:
#include "nes2header.h" #define NES2_MAPPER 3 #define NES2_PRG 32768 #define NES2_CHR 32768 #define NES2_MIRROR 'H' #define NES2_TV 'N' NES2_HEADER("INESHDR");
An SLROM board with 128 KiB PRG ROM, 128 KiB CHR ROM, 8 KiB battery-backed WRAM, and PAL-preferred but dual-compatible program:
#include "nes2header.inc" #define NES2_MAPPER 1 #define NES2_PRG 131072 #define NES2_CHR 131072 #define NES2_BRAM 8192 #define NES2_TV 'P','N' NES2_HEADER("INESHDR");
An MMC3 board with 512 KiB PRG ROM, 32 KiB CHR RAM, and NTSC-only program:
#include "nes2header.inc" #define NES2_MAPPER 4 #define NES2_PRG 524288 #define NES2_CHRRAM 32768 #define NES2_TV 'N' NES2_HEADER("INESHDR");