NSF 2.0 Featureset

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

NSF 2.0 Featureset

Post by kevtris »

ack. it ate my formatting! edited.

Code: Select all

Well, I was talking with gil_ on IRC and we were discussing how a potential NSF 2.0 would be approached.

The goals are:

* IRQ supprt
* "no return" init addresses
* information block


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.

So my proposal for IRQ hardware:

* a 16 bit IRQ timer that sits at 0x4018-0x401a
    * 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.  
    * these will work like on a real NES. you MUST write to 0x4017
       to enable the external (timer) IRQ, and to reset the frame IRQ,
      just like a regular NES.

* IRQ vector.
    * write the vector to FFFE and FFFF.  These two locations hold the 
       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:

    * Allow for the init address to never return. 

    this basically means:
    * init becomes the reset vector 
    * play becomes the NMI vector
    * and IRQ has its vector at FFFE/FFFF





Proposed header changes:


0005    1   BYTE    Version number (currently 01h)

this will be bumped to 02h for version 2.0


007c    4   ----    4 extra bytes for expansion (must be 00h)

these will be used as follows:

007c    1  BYTE   NSF 2.0 feature enables
       bit:
       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)


Extended data block:

   * an extended block of data that is optional to include.
   * it has the following features:
       * 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.


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.

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.


/* this is a comment */
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: NSF 2.0 Featureset

Post by Hamtaro126 »

kevtris wrote:Well, I was talking with gil_ on IRC and we were discussing how a potential NSF 2.0 would be approached.

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.

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.

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.
Good idea, I approve of this, Just wait for more to approve,

EDIT: Fixed my formatting too,
EDIT2: What do I say about my post level - It's Over 300!!!
AKA SmilyMZX/AtariHacker.
lidnariq
Posts: 11430
Joined: Sun Apr 13, 2008 11:12 am

Re: NSF 2.0 Featureset

Post by lidnariq »

kevtris wrote:* a 16 bit IRQ timer that sits at 0x4018-0x401a
Clocked by the standard 1.8MHz (1.6MHz on pal) cpu instruction clock, I assume?
User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Re: NSF 2.0 Featureset

Post by kevtris »

lidnariq wrote:
kevtris wrote:* a 16 bit IRQ timer that sits at 0x4018-0x401a
Clocked by the standard 1.8MHz (1.6MHz on pal) cpu instruction clock, I assume?
That's correct. It 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 401a bit 0 is clear) it is constantly being reloaded with the values in 4018/4019, and the counter IRQ flag is cleared.

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.

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 401a. I should probably put the IRQ flag at bit 7 (read only) to allow easy testing of IRQ source.

Thusly, your code would look something like this to use the IRQ timer:

Code: Select all


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

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.


/* this is a comment */
Lord Nightmare
Posts: 131
Joined: Wed Apr 05, 2006 10:12 am
Location: PA, USA
Contact:

Post by Lord Nightmare »

Technicality: does it reload when it HITS 0 (on 1->0 transition, so 0 is only in timer for an irerelevantly small time period before reload) or does it reload on underflow FROM 0 (0->"-1" transition)?

LN
"When life gives you zombies... *CHA-CHIK!* ...you make zombie-ade!"
User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Post by kevtris »

Lord Nightmare wrote:Technicality: does it reload when it HITS 0 (on 1->0 transition, so 0 is only in timer for an irerelevantly small time period before reload) or does it reload on underflow FROM 0 (0->"-1" transition)?

LN
when it hits 0, the NEXT clock performs a reload. so, the counter would count like so:

if the reload is 10h...

(reset)
10
10
10
(counter enabled)
0f
0e
0d
0c
...
02
01
00
10
0f
0e
0d
0c

when the counter is reloaded, the IRQ flag would be set also.

02 0
01 0
00 0
10 1

the second number being the IRQ flag.
/* this is a comment */
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

Actually what would be nice in the info field is to follow what MML format does, by having title, composer, and programer (sic) fields for each track.
User avatar
Dwedit
Posts: 4922
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

I'd like to see individual track information saved into the file, including an indication of whether a track is a song, jingle or sound effect. Then with your magical NSF player plugin for winamp, you can enqueue only songs and not sound effects.

Also, maybe an "Initial ram image and sound registers" feature, so you don't need to make your own 6502 code. It would make it more like SPC files.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
B00daW
Posts: 586
Joined: Thu Jan 03, 2008 1:48 pm

Post by B00daW »

Dwedit is onto something...

Some standardization n the optional info block for players somewhat in the lines of markup language would be pretty useful. Of course we would want to conserve as much space as possible for hardware players.

Take for example Track info:

(Open by < and close by >)

<AUzan-zan-zawa-veia>AU<CPzan-zan-zawa-veia 2010>CP<TR3dirt hole:145Table jump:505Tfour word:430T>TR

This would decode as the author being "zan-zan-zawa-veia", the copyright being "zan-zan-zawa-veia 2010", and there being 3 tracks, named "dirt hole," "able jump," "four word," and their respective times, last two characters being seconds, and two preceding being minutes. (I doubt that many tracks would be more than an 59 minutes and 59 seconds, right? Even so, someone may do it just to see if they can. A byte for hours might be desirable. It wouldn't be impossible for someone to create a nine hour long 2a0x track with the slowest possible time and refresh, but then they are just sadistic. Just thinking ahead...) At the end of the track length info would be the type of track: T for track, J for jingle, X for effect, D for stand-alone DPCM data with pointer, and P for stand-alone PCM data with pointer.

<RS(RAM/REG state dump here)>RS

The above information tries to make an attempt at conserving space and allowing for the standard desirable categorization of information. Moving forward, the markup code for the info block shouldn't need more than 2 characters per attribute.

As for some additional information, it might be nice to know what sound engine is being used in the NSF. For example: FamiTracker vXXX, PornoTracker vXXX, NerdTracker vXXX, SuperNSF vXXX, it2nsf vXXX, etc...

<SEFamiTracker vXXX>SE

... I believe this can be expanded to infinity ...and beyond.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Interesting. First I thought "How would you escape : and > characters in titles?" and then I realized that one might choose JSON instead of an ad hoc markup language, and lengths in seconds instead of minutes and seconds. This is more verbose, but at least less so than XML, and it's self-describing and probably already has a parser in your favorite language.

Code: Select all

{
"title":"didder","engine":"FamiTracker vXXX",
"author":"zan-zan-zawa-veia","copr":"zan-zan-zawa-veia 2010",
"tracks":[
  {"n":"dirt hole","t":105,"d":"bgm"},
  {"n":"able jump","t":305,"d":"bgm"},
  {"n":"four word","t":270,"d":"bgm"}
]}
User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Post by kevtris »

tepples wrote:Interesting. First I thought "How would you escape : and > characters in titles?" and then I realized that one might choose JSON instead of an ad hoc markup language, and lengths in seconds instead of minutes and seconds. This is more verbose, but at least less so than XML, and it's self-describing and probably already has a parser in your favorite language.

Code: Select all

{
"title":"didder","engine":"FamiTracker vXXX",
"author":"zan-zan-zawa-veia","copr":"zan-zan-zawa-veia 2010",
"tracks":[
  {"n":"dirt hole","t":105,"d":"bgm"},
  {"n":"able jump","t":305,"d":"bgm"},
  {"n":"four word","t":270,"d":"bgm"}
]}

I was thinking something a lot simpler- this should ideally be readable on an NES and parsing some kind of higher level thing would be a pain to do I think.

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.
/* this is a comment */
User avatar
B00daW
Posts: 586
Joined: Thu Jan 03, 2008 1:48 pm

Post by B00daW »

The Ripper field could be interchangeable for Engine. Since it's unlikely that the engine will be known if it is ripped from a commercial game. The engine information would be useful for modern composed NSFs.
kevtris wrote: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")
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

If you want the header readable on the NES, then how are you going to handle Unicode UTF-8 character encoding? A player running on an NES would have to include a font covering the whole Unicode BMP including CJK, a layout engine including stacked accents, bidirectional reordering, and contextual glyph replacements, just in case an NSF has Arabic song titles.

"Engine" is known for any game whose music data format has been described on romhacking.net. For example, a lot of Capcom games use substantially the same engine.
User avatar
clueless
Posts: 496
Joined: Sun Sep 07, 2008 7:27 am
Location: Seatlle, WA, USA

Post by clueless »

Kevtris, your approach seems very similar to how a TIFF image encodes image meta-data in "tiff tags". I like the idea that the info is easily extracted with 6502/NES software.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Post by thefox »

kevtris wrote: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.)
Standardizing it as UTF-8 is probably the best choice, since it's backwards compatible with ASCII.
tepples wrote:If you want the header readable on the NES, then how are you going to handle Unicode UTF-8 character encoding? A player running on an NES would have to include a font covering the whole Unicode BMP including CJK, a layout engine including stacked accents, bidirectional reordering, and contextual glyph replacements, just in case an NSF has Arabic song titles.
Yeah if you really need to nitpick. Most players would be fine with displaying only ASCII characters though and maybe display a message if non-ASCII characters are detected.

I would also go with a binary format for the meta-data.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Post Reply