NSF2: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(i will update this very soon, but leaving a note just to make it clear the current article isn't up to date)
(WIP draft, still need to describe IRQ and non-returning INIT)
Line 2: Line 2:
[[Category:File formats]]
[[Category:File formats]]


'''The proposal below is currently out of date. This Wiki will be updated shortly, but for current information please see:
'''NSF2''' is an extension of the [[NSF]] file format, first publicly implemented in 2019, with the following additions:
[https://forums.nesdev.org/viewtopic.php?f=6&t=17704&p=224172#p224172 this forum thread]'''


* 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 [[APU DMC|DMC]] and [[APU Frame Counter|Frame Counter]] IRQ devices.
* An alternative non-returning INIT paradigm for playback.


'''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.
== File Format ==
This header is the same as the original [[NSF#Header Overview|NSF header]], but with the following additions:


It is currently an unfinished proposal.
offset  # of bytes  Function
----------------------------
$005    1  BYTE    Version number, equal to 2
$07C    1  BYTE    [[#Flags|NSF2 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


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


* IRQ support
Non-mandatory metadata is available even in the original [[NSF|version 1 NSF]] by using these bytes to indicate a data length,
* "no return" init addresses
but older players will assume that this metadata is part of the program ROM.
* information block
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.


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


{| class="datatable"
== IRQ Support ==
! offset || length || type || description
Still being written, see reference below.
|-
| $005 || 1 || BYTE || Version number: $02 (was $01)
|-
| $07C || 1 || BYTE || NSF 2.0 feature enables (was $00)
* bit 0-3: (planned:  additional expansion audio bits.)
* bit 4: if set, enable IRQ features
* bit 5: if set, allow a non-returning init address
* bit 6: if set, allow play calling to be disabled
* bit 7: if set, a metadata block follows the NSF data
|-
| $07D || 3 || WORD || Length of NSF data block in bytes, LSB first, up to $100000 (was $000000)
|}


== IRQ support ==
== Non-Returning INIT ==
Still being written, see reference below.


NSF 2 files are allowed to use three IRQ sources:
== Suppressed PLAY ==
* [[APU DMC]] IRQ
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.
* [[APU Frame Counter]] IRQ
* A new cycle-counting interval timer at $401B-$401D, handled much as a mapper IRQ
A program using IRQ MUST write to $4017 to enable the interval timer IRQ and to reset the frame IRQ.


The interval timer has three readable and writable ports:
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.
* $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.
* (These are set to $401B-$401D as to not interfere with $4018-$401A if someone has modified their RP2a0xG to enable [[CPU Test Mode]].)


In NSF 2 files using IRQ, $FFFE and $FFFF (the IRQ vector) are treated as readable and writable.
This bit does not by itself imply NMI-driven PLAY, even though the non-returning INIT does.
A program using IRQ MUST write to these locations before enabling IRQ.
Writes to $FFFE and $FFFF do not affect the NSF data; they are separate bytes of RAM.
 
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).
 
This would allow a real NES to play 2.0 NSFs using say a PowerPak or similar.
 
The timer would decrement at the CPU clock rate, whether it be NTSC or PAL rate (so 1.79MHz or 1.66MHz or so).
 
The counter is a modulus N counter and has the following behaviour:
 
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.
 
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.
 
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).
 
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.
 
Code might look like this:
<pre>
 
timervalue:  .equ 01fffh      ;desired # of cpu cycles minus 1
 
starttimer: LDA #000h
              STA 0401dh        ;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 0401bh        ;low byte of timer value
              LDA #>timervalue
              STA 0401ch        ;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 0401dh        ;turn the timer on
              RTS
 
stoptimer: SEI                    ;turn off IRQs
                LDA #000h
                STA 0401dh        ;turn timer off
                RTS
 
irqvector:  <perform our interrupt code here>
                ....
                ....
             
                LDA 0401dh        ;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 0401dh        ;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.
 
== Non-returning INIT ==
 
An NSF with a non-returning INIT takes more or less complete control of the CPU.
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 ==
== 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.


The optional metadata block allows an NSF to carry the following additional metadata:
It is identical to the format described at [[NSFe]], but with the following differences:
* Longer title, author, and copyright fields
* There is no 'NSFE' fourCC at the start of the NSF2 metadata. The first byte begins the first contained chunk header.
* UTF-8 encoding for wider language support
* Chunks that are already part of the NSF format must not be included. 'INFO', 'DATA', 'BANK' and 'NSF2' chunks should not be used.
* 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.
Files MUST also contain data in the original title, author, and copyright fields for compatibility with pre-NSF 2 tools.
 
Kev's proposal uses a somewhat TIFF-like chunked format for metadata
{| 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)
* $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.
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 'RATE' and 'regn' chunks, while partially redundant, may be included to provide additional Dendy region playback information. The redundant parts should match with the NSF header.


* composer, track $FF, jimbob
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.
* 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.
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 format should be easy for a hardware player to parse.
This mandatory indication allows future expansion to the format without having to redefine the NSF header.
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 ==
* [//forums.nesdev.org/viewtopic.php?f=6&t=7304 NSF 2.0 forum discussion] - original proposal
* [//forums.nesdev.org/viewtopic.php?f=6&t=7304 NSF 2.0 forum discussion] - original proposal
* [//forums.nesdev.org/viewtopic.php?f=6&t=17704 2018 forum discussion] - updated proposal
* [//forums.nesdev.org/viewtopic.php?f=6&t=17704 2018 forum discussion] - updated proposal

Revision as of 04:35, 3 March 2019


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.

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

Still being written, see reference below.

Non-Returning INIT

Still being written, see reference below.

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 redundant parts should match with the NSF header.

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.

Reference