NSF2: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Imported spec from http://forums.nesdev.com/viewtopic.php?f=6&t=7304 ; hopefully someone can make it readable)
m (whitespace)
 
(28 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Audio]]
[[Category:Audio]]
[[Category:File formats]]
[[Category:File formats]]
'''NSF2''' is an extension of the [[NSF]] file format, first publicly implemented in 2019, with the following additions:


'''NSF 2''' is a proposed extension to the [[NSF]] file format. It is intended to be backward compatible with the original NSF, provide IRQ timer functionality, and allow track times and other metadata to be contained.
* Backward compatibility with the original NSF format where the new features are optional.
* Extensible [[#Metadata|metadata]] through incorporation of the [[NSFe]] format, of both optional and mandatory varieties.
* A programmable [[#IRQ Timer|timer IRQ]], as well as explicit access to the existing [[APU DMC|DMC]] and [[APU Frame Counter|Frame Counter]] IRQ devices.
* An alternative [[#Non-Returning INIT|non-returning INIT]] paradigm for playback.
* All strings contained in NSF2 should be encoded in UTF-8 format.


It is currently an unfinished proposal. Currently Nintendulator partially supports it. See link below for reference.
== File Format ==
This header is the same as the original [[NSF#Header Overview|NSF header]], but with the following additions:


<pre>
offset  # of bytes  Function
The goals are:
----------------------------
$005    1  BYTE    Version number, equal to 2
$07C    1  BYTE    NSF2 feature flags
                bits 0-3: reserved, must be 0
                bit 4: if set, this NSF may use the [[#IRQ Support|IRQ support]] features
                bit 5: if set, the [[#Non-Returning INIT|non-returning INIT]] playback feature will be used
                bit 6: if set, the [[#Suppressed PLAY|PLAY subroutine will not be used]]
                bit 7: if set, the appended [[#Metadata|NSFe metadata]] may contain a mandatory chunk required for playback
$07D    3  BYTES  24-bit length of the NSF program data, allowing [[#Metadata|metadata]] to follow the data


* IRQ supprt
If a non-zero value appears in $7D-7F, NSFe [[#Metadata|metadata]] may be appended to the file immediately following the end of the data.
* "no return" init addresses
* information block


Non-mandatory metadata is available even in the original [[NSF|version 1 NSF]] by using these bytes to indicate a data length,
but older players will assume that this metadata is part of the program ROM.
Since NSF playback is mostly deterministic, it's reasonable to ensure that misinterpreted appended data isn't used by the NSF
and won't harm playback. For this reason if the metadata is optional information (e.g. track names),
and no other NSF2 features are required, it may be preferable to leave the version as 1.


For IRQ support, I figured allowing the use of frame IRQs and DPCM IRQs just like on the NES, and then a 16 bit IRQ timer which would be connected to the CPU in the usual way (via its IRQ input).
If the file version is 1, any flags in byte $7C should be ignored.


This would allow a real NES to play 2.0 NSFs using say a powerpak or similar.
== IRQ Support ==
This bit explicitly allows use of IRQs by the NSF program.
It provides its own programmable cycle timed IRQ,
but also allows the use of APU IRQs generated by the [[APU DMC|DMC]] or [[APU Frame Counter|Frame Counter]].


So my proposal for IRQ hardware:
Using this bit implies a vector overlay provided by the player at $FFFA-$FFFF.
The NMI and Reset vectors at $FFFA and $FFFC are reserved for the player implementation (see: [[#Non-Returning INIT]])
but the IRQ vector at $FFFE-$FFFF will be provided as RAM.
This is an overlay on top of any underlying NSF data,
so the NSF program must explicitly write a vector to $FFFE before enabling interrupts.


* a 16 bit IRQ timer that sits at 0x4018-0x401a
For convenience, before INIT the host system should initialize the IRQ vector RAM with the starting contents of $FFFE-$FFFF. <small>(Added 2022-12-4)</small>
    * 4018 = lower 8 bits of the 16 bit timer reload value
    * 4019 = upper 8 bits of the 16 bit timer reload value
    * 401a = control register.  
        * bit 0 - 0 = timer off, reset.  1 = timer is running
    * all registers are readable and writable.  on a real NES, these
      addresses are fully readable/writable to the cartridge.


* allowing the use of DPCM and frame IRQs.  
The first time INIT is called, the IRQ inhibit flag will be set (SEI), and the IRQ timer device will be set to inactive.
    * these will work like on a real NES. you MUST write to 0x4017
The NSF program will be responsible for enabling interrupts (CLI).
      to enable the external (timer) IRQ, and to reset the frame IRQ,
The NSF player will never change the IRQ flag directly, though if used in combination with the
      just like a regular NES.
non-returning INIT feature the PLAY routine will have an implied SEI via being called from an NMI (see [[#Non-Returning INIT|below]]).


* IRQ vector.
=== IRQ Timer ===
    * write the vector to FFFE and FFFF.  These two locations hold the
A cycle counting timer device will be supplied with three readable and writable registers:
      IRQ vector like usual, but are writable.  When read by  the CPU,
      these two locations must return the two bytes written there.
    * when written, you DO NOT write to the underlying NSF data bank.
    * in effect, FFFE/FFFF become two bytes of RAM which are separate
      from the rest of NSF space.


And the proposal for no return on the init address:
$401B R/W - Low 8 bits of counter reload.
$401C R/W - High 8 bits of counter reload.
$401D W - Activate with bit 0 set, deactivate with bit 0 clear.
$401D R - Acknowledges IRQ. Bit 7 returns IRQ flag before clearing it. Bit 0 returns active status.


    * Allow for the init address to never return.
If active, every cycle the counter will be decremented. When the counter goes below 0 it will enable its IRQ flag, and be reloaded with the value given at $401B/C.


    this basically means:
If inactive it will instead reload the counter on every cycle.
    * init becomes the reset vector
    * play becomes the NMI vector
    * and IRQ has its vector at FFFE/FFFF


The IRQ line will be asserted whenever the IRQ flag is set as the counter underflows, until it is acknowledged by reading $401D.


Bit 7 read from $401D might be used to distinguish between different IRQ sources, if this is required.


The period of this timer with a reload value of '''N''' will repeat every '''N+1 cycles'''. A reload value of 0 means it will repeat every 1 cycle.


The automatic reload allows an IRQ to repeat at a dependable interval of cycles, minimizing jitter.


Proposed header changes:
== Non-Returning INIT ==
This bit changes the method of playback to a paradigm where INIT is allowed to run indefinitely, and PLAY will interrupt it as an NMI:
* The INIT routine will be called twice.
* The first call of INIT will be as [[NSF#Initializing a tune|NSF]], but additionally the ''Y'' register will contain $80. This call must return.
* NMI is be enabled.
* A second call of INIT, with the same 'A' and 'X' as the first call, but ''Y'' will now contain $81. This call does not have to return.
* PLAY will be called by an NMI wrapper, interrupting the still running INIT function.


The NMI wrapper is implementation-defined, and part of the player. PLAY will not be called directly by the NMI vector, and must end with an RTS, not an RTI.


0005    1  BYTE    Version number (currently 01h)
The NMI wrapper will be responsible for saving and restoring A,X,Y.
When PLAY is called, there is an implied SEI by the enclosing NMI, which should be considered if also using the [[#IRQ Support|IRQ feature]].
The NMI wrapper should disable and re-enable the NMI signal to prevent re-entry if PLAY runs long.


this will be bumped to 02h for version 2.0
After PLAY returns, the NMI wrapper may also want to do additional things like check user input or update its UI.
An ideal NSF2 player should keep this wrapper as minimal as possible, but for wider compatibility the NSF program should not rely on any specific timing for this.


As with the [[#IRQ Support|IRQ feature]], this also requires a vector memory overlay at $FFFA-FFFF.
This replaces any data from the NSF program at that location, and the specific vectors for $FFFA (NMI) and $FFFC (Reset) are reserved to be provided by the player.


007c    4  ----    4 extra bytes for expansion (must be 00h)
The second INIT is allowed to return, in which case the player should fall back to its own infinite loop. (INIT will not be called a third time.)


these will be used as follows:


007c    1  BYTE  NSF 2.0 feature enables
A Y value other than $80 or $81 can be interpreted as a player that doesn't support this feature, which may be used to create a fallback for compatibility.
      bit:
No existing pre-NSF2 players are known to have used these values for Y on INIT<ref>[//forums.nesdev.org/viewtopic.php?p=235576#p235576 NSF player Y INIT survey]</ref>.
      0    - when set, enables the IRQ features. when clear, disables them
      1    - when set, allows for a non-returning init address.
      2    - when set, allows play calling to be disabled
      3-6 - maintain 0
      7    - an extended info block follows nsf data. (see below)


007d    3  WORD  length of NSF data block, in bytes. LSB first (little endian)
== Suppressed PLAY ==
If this bit is set, the PLAY routine will never be called. This is mostly intended for use with the non-returning INIT feature, allowing it to continue uninterrupted.


Hardware player implementations may reserve the need to still execute brief NMI interruptions during non-returning INIT, even if suppressed, to allow its player program to remain responsive to user interaction. Ideally, however, the non-returning INIT should not be interrupted at all if PLAY is disabled.


Extended data block:
This bit does not by itself imply NMI-driven PLAY, even though the non-returning INIT does.


  * an extended block of data that is optional to include.
== Metadata ==
  * it has the following features:
Optional metadata may be appended immediately following the data. It appears at a file offset of the 24-bit data length at $7D-7F (little-endian) plus $80 for the length of the NSF header.
      * stores a unicode? title up to N characters long
      * stores the same for copyright, author, and ripper
      * allow for separate author/copyright/title on each track?
      * lengths of tracks
      * any other possible ancillary data?
  * the reason for placing it at the end, is so that 1.0 players can still
      use these NSFs.  they will append the extra data into NSF space, and
      it should not affect the playback if it doesn't use any of the other
      features (IRQs, non-return init addresses)
    * You MUST still populate the original author/copyright/title strings
      in the original header for backwards compatibility.


It is identical to the format described at [[NSFe]], but with the following differences:
* There is no 'NSFE' fourCC at the start of the NSF2 metadata. The first byte begins the first contained chunk header.
* Chunks that are already part of the NSF format must not be included. 'INFO', 'DATA', 'BANK' and 'NSF2' chunks should not be used.


So that's basically it. I think it adds all the features that can be added and still function properly on an NES with a powerpak or similar player cartridge.
The 'RATE' and 'regn' chunks, while partially redundant, may be included to provide additional Dendy region playback information. The fields in the NSF header can supply a backward compatible fallback for this case if the metadata is not parsed.


I'm open to suggestions or feedback on it. If people like it, I will formalize it and update the existing NSF document, and modify my FPGA synth to conform to the document for testing.
If bit 7 of header byte $7C is set, parsing the metadata becomes ''mandatory''. This means that the appended metadata contains a mandatory chunk, indicated by a fourCC of capital letters, that must be parsed and understood by the player to be able to play back the file.


</pre>
The mandatory bit is intended for cases where extra information may be needed for correct emulation. Examples:
== IRQ timer ==
* A 'VRC7' chunk can be used to substitute VRC7 with the related but not compatible YM2413 chip.
* A chip with embedded sample data could be provided in an appropriate chunk.


The timer would decrement at the CPU clock rate, whether it be NTSC or PAL rate (so 1.79MHz or 1.66MHz or so).
This mandatory indication allows future expansion to the format without having to redefine the NSF header.


The counter is a modulus N counter and has the following behaviour:
Metadata should end with an NEND chunk.


When the counter is off (whenever 401a bit 0 is clear) it is constantly being reloaded with the values in 4018/4019, and the counter IRQ flag is cleared.
== Players ==


When the counter is on (401a bit 0 is set), it will decrement once per CPU cycle. When it hits 0, it is reloaded from 4018/4019, the IRQ flag is set and an IRQ is asserted.
The following implementations of NSF2 exist:
* [https://github.com/bbbradsmith/nsfplay/releases NSFPlay] 2.4 beta 9 (2019-3-1)


This means an IRQ will be generated every N+1 clock cycles, where N is the value loaded into the counter. (it is N+1 because 0 is counted too).
== Notes ==


To clear the IRQ flag, you read 401a. I should probably put the IRQ flag at bit 7 (read only) to allow easy testing of IRQ source.
Though the ideal NSF2 player will not be encumbered by these problems,
the are a few suggestions for NSF programs for increased compatibility
with potential hardware players that might not be able to provide all features:
* For non-returning INIT use a standard PLAY rate specified in the header. Hardware players may not be able to adjust their NMI timing.
* If using IRQ or non-returning INIT, do not bankswitch $F000. This allows the vector overlay to be applied directly to the NSF data on load instead of requiring an extra decoder.
* Some NSF players already have some IRQ support. Placing the IRQ vector at $FFFE as well as writing to it can be compatible with both NSF1+IRQ and NSF2.
* The older NSF1 had no specification for string encoding. UTF-8 is the standard for NSF2, but for backward compatibility only ASCII should be used for the fields in the header. An NSFe 'auth' chunk can be used to override the header's title/author/copyright with UTF-8 strings.


Code might look like this:
The vector overlay is not implied by the header version 2, it will only be used if either the IRQ or non-returning INIT features are specified in byte $7C.
<pre>
 
timervalue:  .equ 01fffh      ;desired # of cpu cycles minus 1
 
starttimer: LDA #000h
              STA 0401ah        ;reset and shut off timer in case it was on
 
              LDA #<irqvector
              STA 0fffeh          ;store interrupt vector low of our handler
              LDA #>irqvector
              STA 0ffffh            ;store interrupt vector high
 
              LDA #<timervalue
              STA 04018h        ;low byte of timer value
              LDA #>timervalue
              STA 04019h        ;high byte of timer value
 
              LDA #0c0h
              STA 04017h        ;turn off frame IRQs
              LDA 04015h        ;ack any pending DPCM IRQ if, it exists
              CLI                    ;enable IRQs
 
              LDA #01h
              STA 0401ah        ;turn the timer on
              RTS
 
stoptimer: SEI                    ;turn off IRQs
                LDA #000h
                STA 0401ah        ;turn timer off
                RTS
 
irqvector:  <perform our interrupt code here>
                ....
                ....
             
                LDA 0401ah        ;reading 401a resets IRQ flag
                RTI                    ;return from interrupt
 
 
alternatively, if you wish to determine WHAT caused the IRQ (if you're using more than one source) you'd do something like this...
 
irqvector:  BIT 0401ah        ;bit 7 indicates we have a timer IRQ waiting
                BPL +
                JSR timer          ;if bit 7 was set, call timer subroutine
              + BIT 04015h
                BPL +
                PHP                  ;save flags if we have DPCM int.
                JSR dpcm          ;if bit 7 was set, call DPCM sub
                PLP
              + BVC +
                JSR frame          ;bit 6 of 4015 = frame IRQ
              + RTI                    ;exit interrupt
 
timer:        <do stuff here>
                RTS
 
dpcm:        <do stuff here>
                RTS
 
frame:        <do stuff here>
                  RTS
 
</pre>
 
There's a lot of ways to skin this cat, but this is one particular method.  The idea is to read the status regs to figure out which source caused the interrupt, then run code to service it, then go back and check the other sources just in case one of them also needs servicing.
 
Like the APU period timers and the MMC3 scanline timer, the NSF2 cycle timer includes both the 0 and the reload value in its sequence of states.
When it hits 0, the NEXT clock performs a reload.
 
For example, when the reload value is $0010, the counter would have a period of 17 CPU cycles:
 
10, 10, 10, [Enable now] 0F, 0E, 0D, 0C, ..., 02, 01, 00, 10*, 0F, 0E, 0D, 0C, ...
 
where * denotes the IRQ flag becoming true.
 
== Track information ==
<pre>
my approach would be to have records that contain track # and then information fields... something like this:
 
record:
 
 
offset, # of bytes, type, description
----------------------------------------
0 1 BYTE record type
1 2 WORD record length
3 1 BYTE track #
4 N --- record data
 
Records would be one after another, and a record of 4 00h bytes would signify the end of the data.
 
the type would be something like:
 
0 - last record
1 - title
2 - composer
3 - copyright
 
etc.
 
Track # 0ffh could be reserved, and used as a wildcard indicator,
allowing for things like this:
 
composer, track 0ffh, jimbob
title, track 0, my first song
title, track 1, my second song
title, track 2, a song by someone else
composer, track 2, billy
title, track 3, another song
 
this would populate all the "composer" entries on the tracks which do not have a specific composer (i.e. everything but track 2 in the above example).
 
This would allow removal of most duplicated material and allow for individual track fields to be populated with different info if required.
 
Also, it'd be pretty trivial to process this type of data on a real NES or other hardware player.
 
Possible fields could be (with tentative ID's):
 
01 Title followed by ASCII or unicode? data. A max length should be specified.
02 Composer (Same as above)
03 Copyright (Same as above)
04 Ripper (Same as above)
05 length (in seconds? milliseconds? NMI counts?)
06 type (maybe? i.e. SFX, BGM, title tune, etc)
07 ancillary data (i.e. compo entry #? "this is a cover of xyz")
 
Any other fields that would be useful? As usual, all fields are optional, and you only use the ones you need/want.
 
This would make it pretty easy to read via a real NES, vs. some feel-good text format. As such, it'd probably be a decent idea to only allow ASCII in the fields since an NES cannot read unicode too easily. (Also, a simple converter could be written to "compile" a desired text format into the binary format for stuffing onto the end of the NSF.)
 
As for a pure text format, I ran into this issue with .SAP files. I had to write a somewhat annoying complicated parser for these files, because they have a human-generated header with the binary data just appended on. Because of this, you have to account for all sorts of weird cases; tabs, spaces, CRLF, LF only, etc.
 
It's kind of a nightmare to handle in 6502 asm.
</pre>
 
The data would be encoded in UTF-8, with a warning if the player cannot display a particular character.


== Reference ==
== Reference ==
* [http://forums.nesdev.org/viewtopic.php?f=6&t=7304 NSF 2.0 forum discussion]
* [https://github.com/bbbradsmith/nes-audio-tests nes-audio-tests] - Test NSF files for NSF2 features
* [https://gist.github.com/bbbradsmith/4bc17ae16b10a9be03e80addfbea5009 nsfe_to_nsf2.py] - Tool for converting NSFe files to NSF + Metadata
* [https://gist.github.com/bbbradsmith/e225068e750fdb32e59a191a4f6a7e45 nsf2_strip.py] - Tool for stripping NSFe metadata from NSF
* [//forums.nesdev.org/viewtopic.php?f=6&t=17704 2018 forum discussion] - rainwarrior's 2018 NSF2 proposal
* [//forums.nesdev.org/viewtopic.php?f=6&t=7304 NSF 2.0 forum discussion] - Kevtris' 2010 NSF2 proposal
<References />

Latest revision as of 00:14, 13 February 2023

NSF2 is an extension of the NSF file format, first publicly implemented in 2019, with the following additions:

  • Backward compatibility with the original NSF format where the new features are optional.
  • Extensible metadata through incorporation of the NSFe format, of both optional and mandatory varieties.
  • A programmable timer IRQ, as well as explicit access to the existing DMC and Frame Counter IRQ devices.
  • An alternative non-returning INIT paradigm for playback.
  • All strings contained in NSF2 should be encoded in UTF-8 format.

File Format

This header is the same as the original NSF header, but with the following additions:

offset  # of bytes   Function
----------------------------
$005    1   BYTE    Version number, equal to 2
$07C    1   BYTE    NSF2 feature flags
                bits 0-3: reserved, must be 0
                bit 4: if set, this NSF may use the IRQ support features
                bit 5: if set, the non-returning INIT playback feature will be used
                bit 6: if set, the PLAY subroutine will not be used
                bit 7: if set, the appended NSFe metadata may contain a mandatory chunk required for playback
$07D    3   BYTES   24-bit length of the NSF program data, allowing metadata to follow the data

If a non-zero value appears in $7D-7F, NSFe metadata may be appended to the file immediately following the end of the data.

Non-mandatory metadata is available even in the original version 1 NSF by using these bytes to indicate a data length, but older players will assume that this metadata is part of the program ROM. Since NSF playback is mostly deterministic, it's reasonable to ensure that misinterpreted appended data isn't used by the NSF and won't harm playback. For this reason if the metadata is optional information (e.g. track names), and no other NSF2 features are required, it may be preferable to leave the version as 1.

If the file version is 1, any flags in byte $7C should be ignored.

IRQ Support

This bit explicitly allows use of IRQs by the NSF program. It provides its own programmable cycle timed IRQ, but also allows the use of APU IRQs generated by the DMC or Frame Counter.

Using this bit implies a vector overlay provided by the player at $FFFA-$FFFF. The NMI and Reset vectors at $FFFA and $FFFC are reserved for the player implementation (see: #Non-Returning INIT) but the IRQ vector at $FFFE-$FFFF will be provided as RAM. This is an overlay on top of any underlying NSF data, so the NSF program must explicitly write a vector to $FFFE before enabling interrupts.

For convenience, before INIT the host system should initialize the IRQ vector RAM with the starting contents of $FFFE-$FFFF. (Added 2022-12-4)

The first time INIT is called, the IRQ inhibit flag will be set (SEI), and the IRQ timer device will be set to inactive. The NSF program will be responsible for enabling interrupts (CLI). The NSF player will never change the IRQ flag directly, though if used in combination with the non-returning INIT feature the PLAY routine will have an implied SEI via being called from an NMI (see below).

IRQ Timer

A cycle counting timer device will be supplied with three readable and writable registers:

$401B R/W - Low 8 bits of counter reload.
$401C R/W - High 8 bits of counter reload.
$401D W - Activate with bit 0 set, deactivate with bit 0 clear.
$401D R - Acknowledges IRQ. Bit 7 returns IRQ flag before clearing it. Bit 0 returns active status.

If active, every cycle the counter will be decremented. When the counter goes below 0 it will enable its IRQ flag, and be reloaded with the value given at $401B/C.

If inactive it will instead reload the counter on every cycle.

The IRQ line will be asserted whenever the IRQ flag is set as the counter underflows, until it is acknowledged by reading $401D.

Bit 7 read from $401D might be used to distinguish between different IRQ sources, if this is required.

The period of this timer with a reload value of N will repeat every N+1 cycles. A reload value of 0 means it will repeat every 1 cycle.

The automatic reload allows an IRQ to repeat at a dependable interval of cycles, minimizing jitter.

Non-Returning INIT

This bit changes the method of playback to a paradigm where INIT is allowed to run indefinitely, and PLAY will interrupt it as an NMI:

  • The INIT routine will be called twice.
  • The first call of INIT will be as NSF, but additionally the Y register will contain $80. This call must return.
  • NMI is be enabled.
  • A second call of INIT, with the same 'A' and 'X' as the first call, but Y will now contain $81. This call does not have to return.
  • PLAY will be called by an NMI wrapper, interrupting the still running INIT function.

The NMI wrapper is implementation-defined, and part of the player. PLAY will not be called directly by the NMI vector, and must end with an RTS, not an RTI.

The NMI wrapper will be responsible for saving and restoring A,X,Y. When PLAY is called, there is an implied SEI by the enclosing NMI, which should be considered if also using the IRQ feature. The NMI wrapper should disable and re-enable the NMI signal to prevent re-entry if PLAY runs long.

After PLAY returns, the NMI wrapper may also want to do additional things like check user input or update its UI. An ideal NSF2 player should keep this wrapper as minimal as possible, but for wider compatibility the NSF program should not rely on any specific timing for this.

As with the IRQ feature, this also requires a vector memory overlay at $FFFA-FFFF. This replaces any data from the NSF program at that location, and the specific vectors for $FFFA (NMI) and $FFFC (Reset) are reserved to be provided by the player.

The second INIT is allowed to return, in which case the player should fall back to its own infinite loop. (INIT will not be called a third time.)


A Y value other than $80 or $81 can be interpreted as a player that doesn't support this feature, which may be used to create a fallback for compatibility. No existing pre-NSF2 players are known to have used these values for Y on INIT[1].

Suppressed PLAY

If this bit is set, the PLAY routine will never be called. This is mostly intended for use with the non-returning INIT feature, allowing it to continue uninterrupted.

Hardware player implementations may reserve the need to still execute brief NMI interruptions during non-returning INIT, even if suppressed, to allow its player program to remain responsive to user interaction. Ideally, however, the non-returning INIT should not be interrupted at all if PLAY is disabled.

This bit does not by itself imply NMI-driven PLAY, even though the non-returning INIT does.

Metadata

Optional metadata may be appended immediately following the data. It appears at a file offset of the 24-bit data length at $7D-7F (little-endian) plus $80 for the length of the NSF header.

It is identical to the format described at NSFe, but with the following differences:

  • There is no 'NSFE' fourCC at the start of the NSF2 metadata. The first byte begins the first contained chunk header.
  • Chunks that are already part of the NSF format must not be included. 'INFO', 'DATA', 'BANK' and 'NSF2' chunks should not be used.

The 'RATE' and 'regn' chunks, while partially redundant, may be included to provide additional Dendy region playback information. The fields in the NSF header can supply a backward compatible fallback for this case if the metadata is not parsed.

If bit 7 of header byte $7C is set, parsing the metadata becomes mandatory. This means that the appended metadata contains a mandatory chunk, indicated by a fourCC of capital letters, that must be parsed and understood by the player to be able to play back the file.

The mandatory bit is intended for cases where extra information may be needed for correct emulation. Examples:

  • A 'VRC7' chunk can be used to substitute VRC7 with the related but not compatible YM2413 chip.
  • A chip with embedded sample data could be provided in an appropriate chunk.

This mandatory indication allows future expansion to the format without having to redefine the NSF header.

Metadata should end with an NEND chunk.

Players

The following implementations of NSF2 exist:

Notes

Though the ideal NSF2 player will not be encumbered by these problems, the are a few suggestions for NSF programs for increased compatibility with potential hardware players that might not be able to provide all features:

  • For non-returning INIT use a standard PLAY rate specified in the header. Hardware players may not be able to adjust their NMI timing.
  • If using IRQ or non-returning INIT, do not bankswitch $F000. This allows the vector overlay to be applied directly to the NSF data on load instead of requiring an extra decoder.
  • Some NSF players already have some IRQ support. Placing the IRQ vector at $FFFE as well as writing to it can be compatible with both NSF1+IRQ and NSF2.
  • The older NSF1 had no specification for string encoding. UTF-8 is the standard for NSF2, but for backward compatibility only ASCII should be used for the fields in the header. An NSFe 'auth' chunk can be used to override the header's title/author/copyright with UTF-8 strings.

The vector overlay is not implied by the header version 2, it will only be used if either the IRQ or non-returning INIT features are specified in byte $7C.

Reference