NSF2: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Allowing for extra expansion audio enable bits.)
m (whitespace)
 
(25 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:


The goals are:
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|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 support
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


== New header fields ==
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.


{| class="datatable"
If the file version is 1, any flags in byte $7C should be ignored.
! offset || length || type || description
|-
| $005 || 1 || BYTE || Version number: $02 (was $01)
|-
| $07C || 1 || BYTE || (Available for extra expansion audio enable bits.)
|-
| $07D || 1 || BYTE || NSF 2.0 feature enables (was $00)
* bit 0: if set, enable IRQ features
* bit 1: if set, allow a non-returning init address
* bit 2: if set, allow play calling to be disabled
* bit 3-6: still 0
* bit 7: if set, a metadata block follows the NSF data
|-
| $07E || 3 || WORD || Length of NSF data block in bytes, LSB first, up to $100000 (was $000000)
|}


== IRQ support ==
== 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]].


NSF 2 files are allowed to use three IRQ sources:
Using this bit implies a vector overlay provided by the player at $FFFA-$FFFF.
* [[APU DMC]] IRQ
The NMI and Reset vectors at $FFFA and $FFFC are reserved for the player implementation (see: [[#Non-Returning INIT]])
* [[APU Frame Counter]] IRQ
but the IRQ vector at $FFFE-$FFFF will be provided as RAM.
* A new cycle-counting interval timer at $401B-$401D, handled much as a mapper IRQ
This is an overlay on top of any underlying NSF data,
A program using IRQ MUST write to $4017 to enable the interval timer IRQ and to reset the frame IRQ.
so the NSF program must explicitly write a vector to $FFFE before enabling interrupts.


The interval timer has three readable and writable ports:
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>
* $401B: Lower 8 bits of the 16-bit timer reload value
* $401C: Upper 8 bits of the 16-bit timer reload value
* $401D: Control register. Clearing bit 0 stops the timer and holds it in reset; setting it to 1 starts it.


In NSF 2 files using IRQ, $FFFE and $FFFF (the IRQ vector) are treated as readable and writable.
The first time INIT is called, the IRQ inhibit flag will be set (SEI), and the IRQ timer device will be set to inactive.
A program using IRQ MUST write to these locations before enabling IRQ.
The NSF program will be responsible for enabling interrupts (CLI).
Writes to $FFFE and $FFFF do not affect the NSF data; they are separate bytes of RAM.
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 [[#Non-Returning INIT|below]]).


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).
=== IRQ Timer ===
A cycle counting timer device will be supplied with three readable and writable registers:


This would allow a real NES to play 2.0 NSFs using say a PowerPak or similar.
$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.


The timer would decrement at the CPU clock rate, whether it be NTSC or PAL rate (so 1.79MHz or 1.66MHz or so).
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.


The counter is a modulus N counter and has the following behaviour:
If inactive it will instead reload the counter on every cycle.


When the counter is off (whenever 401d bit 0 is clear) it is constantly being reloaded with the values in 401b/401c, and the counter IRQ flag is cleared.
The IRQ line will be asserted whenever the IRQ flag is set as the counter underflows, until it is acknowledged by reading $401D.


When the counter is on (401d bit 0 is set), it will decrement once per CPU cycle. When it hits 0, it is reloaded from 401b/401c, the IRQ flag is set and an IRQ is asserted.
Bit 7 read from $401D might be used to distinguish between different IRQ sources, if this is required.


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).
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.


To clear the IRQ flag, you read 401d. I should probably put the IRQ flag at bit 7 (read only) to allow easy testing of IRQ source.
The automatic reload allows an IRQ to repeat at a dependable interval of cycles, minimizing jitter.


Code might look like this:
== Non-Returning INIT ==
<pre>
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.


timervalue:  .equ 01fffh      ;desired # of cpu cycles minus 1
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.


starttimer: LDA #000h
The NMI wrapper will be responsible for saving and restoring A,X,Y.
              STA 0401dh        ;reset and shut off timer in case it was on
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.


              LDA #<irqvector
After PLAY returns, the NMI wrapper may also want to do additional things like check user input or update its UI.
              STA 0fffeh          ;store interrupt vector low of our handler
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.
              LDA #>irqvector
              STA 0ffffh            ;store interrupt vector high


              LDA #<timervalue
As with the [[#IRQ Support|IRQ feature]], this also requires a vector memory overlay at $FFFA-FFFF.
              STA 0401bh        ;low byte of timer value
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.
              LDA #>timervalue
              STA 0401ch        ;high byte of timer value


              LDA #0c0h
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.)
              STA 04017h        ;turn off frame IRQs
              LDA 04015h        ;ack any pending DPCM IRQ if, it exists
              CLI                    ;enable IRQs


              LDA #01h
              STA 0401dh        ;turn the timer on
              RTS


stoptimer: SEI                    ;turn off IRQs
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.
                LDA #000h
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>.
                STA 0401dh        ;turn timer off
                RTS


irqvector:  <perform our interrupt code here>
== 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.
                ....
             
                LDA 0401dh        ;reading 401d resets IRQ flag
                RTI                    ;return from interrupt


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.


alternatively, if you wish to determine WHAT caused the IRQ (if you're using more than one source) you'd do something like this...
This bit does not by itself imply NMI-driven PLAY, even though the non-returning INIT does.


irqvector:  BIT 0401dh        ;bit 7 indicates we have a timer IRQ waiting
== Metadata ==
                BPL +
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.
                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.


== Non-returning INIT ==
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.


An NSF with a non-returning INIT takes more or less complete control of the CPU.
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.
In effect, the INIT address becomes the reset vector, and the PLAY address becomes the NMI vector.
If the IRQ feature is also in use, IRQ has its vector at $FFFE-$FFFF as above.
 
== Metadata ==


The optional metadata block allows an NSF to carry the following additional metadata:
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.
* Longer title, author, and copyright fields
* UTF-8 encoding for wider language support
* Separate field for ripper
* Lengths of tracks
* Possibility of separate track, author, and copyright fields


The metadata is placed at the end so that pre-NSF 2 players will correctly ignore it by placing it at the end of the NSF data.
The mandatory bit is intended for cases where extra information may be needed for correct emulation. Examples:
Files MUST also contain data in the original title, author, and copyright fields for compatibility with pre-NSF 2 tools.
* 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.


Kev's proposal uses a somewhat TIFF-like chunked format for metadata
This mandatory indication allows future expansion to the format without having to redefine the NSF header.
{| class="datatable"
! offset || length || type || description
|-
| 0 || 1 || BYTE || record type
|-
| 1 || 2 || WORD || record length
|-
| 3 || 1 || BYTE || track number to which this record applies, or $FF for all tracks
|-
| 4 || N || variant || record data
|-
| N + 4 || 1 || BYTE || next record type...
|}
Records would be one after another, and a record of 4 $00 bytes would signify the end of the data.


* $00: last record (length 0)
Metadata should end with an NEND chunk.
* $01: title (1 line)
* $02: composer (1 line)
* $03: copyright (1 line)
* $04: ripper (1 line)
* $05: Length (in units to be determined)
* $06: Type (background music, jingle, or sound effect; values to be determined)
* $07: Additional description (UTF-8, for example: "Entry 7, cover of 'Breaking the Law' by Judas Priest")


All strings are UTF-8; players should indicate an error for characters that they cannot display.
== Players ==
For example, a hardware player supporting only ASCII might replace any run of bytes with the high bit set with �.


To reduce duplication of data, track $FF is a wildcard indicator, which populates fields that don't have a more specific value for a particular track:
The following implementations of NSF2 exist:
* [https://github.com/bbbradsmith/nsfplay/releases NSFPlay] 2.4 beta 9 (2019-3-1)


* composer, track $FF, jimbob
== Notes ==
* 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 set the composer of tracks 0, 1, and 3 to jimbob.
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.


This format should be easy for a hardware player to parse.
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.
If you prefer to enter metadata as JSON, XML, or the like, create a tool that parses your metadata and stuffs it at the end of the NSF.


== 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