It is currently Mon Oct 23, 2017 9:49 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: NSF files
PostPosted: Thu Aug 13, 2015 8:29 am 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
The wiki does not mention how to determine when a song ends. Any suggested mechanisms to detect this?

Also, it's kind of strange that the player needs to repeatedly jump to the play address at a fixed interval as opposed to the song loop actually being written in 6502 code, especially considering that the play routine maybe designed not to return within the interval. It's easy to set the processor's PC to an address, but it sounds like I need a way to externally trigger a JSR and then some way to detect a return to that virtual JSR. Ugh.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Thu Aug 13, 2015 8:38 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19118
Location: NE Indiana, USA (NTSC)
zeroone wrote:
The wiki does not mention how to determine when a song ends. Any suggested mechanisms to detect this?

The same way you detect in any linear bounded automaton: hash the RAM every frame, and if it's looped, you'll get a repeat. This won't work quite as precisely for music engines that use a non-integer row length, such as my own. But the most reliable way is to sit down, listen to it, and add NSF 2 annotations.

Quote:
Also, it's kind of strange that the player needs to repeatedly jump to the play address at a fixed interval as opposed to the song loop actually being written in 6502 code, especially considering that the play routine maybe designed not to return within the interval.

That's because the vast majority of play routines used in games return in less than 20,000 cycles, giving the player UI a chance to read the controller and update the screen.

Quote:
It's easy to set the processor's PC to an address, but it sounds like I need a way to externally trigger a JSR and then some way to detect a return to that virtual JSR.

How virtual? Just put some player code in memory that no NSF mapper uses for readable registers. Or do like a couple Atari 2600 games did: watch $01Fx accesses and bank switch that way. Both can be done in hardware.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Thu Aug 13, 2015 8:42 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5734
Location: Canada
The usual thing to do is have something that automatically goes to the next track after a period of silence, or detecting a loop (by comparing a log of register writes).

Alternatively, use the NSFe format instead, which can have track times: http://wiki.nesdev.com/w/index.php/NSFe

(Tepples suggested "NSF2 annotations" but the NSF2 proposal is unfinished and doesn't have a specification for track times yet; also nobody's really implemented it yet or made NSF2 files.)

As for how to call PLAY at a regular interval, you need to create some sort of timer either externally or from existing stuff. You could call PLAY from an NMI handler (doesn't support variable speeds, but pretty much all NSFs are at the NMI rate anyway). You could use the APU frame counter instead of the NMI (may fail on NSFs that use the frame counter; a lot of Nintendo NSFs do this, like Zelda). You could build a timer with an IRQ and call PLAY from an IRQ handler. You could build a timer with a state that can be polled in a loop and call PLAY from there (PowerPak does this). If it's an emulator, you have a lot of options (e.g. treating time outside the PLAY routine as "halted" you can save CPU by not having to emulate a wait loop).


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 6:59 am 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
Thanks rainwarrior and tepples. Your suggestions were very helpful.

Most of the tracks in NSF files loop—it is game music after all. Consequentially, many of the NSF players that I experimented with were not designed to detect the end of the songs, requiring the user to advance the track manually instead. But, as you guys described, detecting a period of silence is probably the only practical solution unless it's an NSFe file containing track durations.

Triggering a JSR for the INIT and PLAY routines from outside of code was not that difficult, but I did encounter one unexpected thing. Below is the beginning of the PLAY routine of the Super Mario Bros. NSF file:

Code:
F2D0:AD 70 07  LDA $0770 = #$01
F2D3:D0 04     BNE $F2D9
F2D5:8D 15 40  STA $4015 = #$FF
F2D8:60        RTS -----------------------------------------


The code assumes that the Z flag is initially cleared. However, the INIT routine actually leaves the Z flag set. I solved this by clearing all flags prior to triggering JSR except for the I flag (the JSR is a surrogate for NMI). I'm not sure if this is the proper solution.

Interestingly, FCEUX actually injects a bit of code to loop the PLAY routine:

Code:
00:8000:8D F4 3F  STA $3FF4 = #$00
00:8003:A2 FF     LDX #$FF
00:8005:9A        TXS
00:8006:AD F0 3F  LDA $3FF0 = #$00
00:8009:F0 09     BEQ $8014
00:800B:AD F1 3F  LDA $3FF1 = #$00
00:800E:AE F3 3F  LDX $3FF3 = #$00
00:8011:20 00 00  JSR $0000
00:8014:A9 00     LDA #$00
00:8016:AA        TAX
00:8017:A8        TAY
00:8018:20 00 00  JSR $0000
00:801B:8D F5 EF  STA $EFF5 = #$FF
00:801E:90 FE     BCC $801E
00:8020:8D F3 3F  STA $3FF3 = #$00
00:8023:18        CLC
00:8024:90 FE     BCC $8024


It's unfortunate that the NSF spec did not require something like that already in place. Starting and stopping tracks could have been controlled by treating certain memory locations as registers.

Is it important to respect the playback rate in the NSF file? If the file contains ripped music, why would it need to be played back at a non-NTSC/PAL rate? NSFe didn't even bother to support custom playback rates.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 10:10 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5734
Location: Canada
NSFe deliberately removed the playback rate feature because the original spec had suggested 60.0hz instead of the real NES rate of 60.1Hz. As such, basically every NTSC NSF rip inaccurately specifies 60.0Hz. Disch thought it would be better to remove the field entirely than obey the existing field that was normally slightly inaccurate.

I've never seen a game rip that required a custom playback rate. Many NSF players don't even support this feature. (I think I saw one game rip with a 30hz rate once, but I think it should have been ripped with a small piece of code to just run every second frame at 60hz instead.) More recently people making new NSF music have been using it for musical effects, as well as other things like enabling multiple expansions at once, etc. Whether you want to support that kind of stuff is your own decision.

The PLAY routine should not rely on the setup of A,X,Y or flags. Anything that does so is an invalid rip, and the ripper should have addressed this with some code modification.

However, I don't know what you mean about SMB. The first instruction in the PLAY routine is LDA $0770, which sets the Z flag. It's not relying on the prior state of Z at all?

I'm also unclear what you mean about FCEUX interjecting code? If I use the debugger to step in, execution begins at INIT, or PLAY, and there's never an opportunity to see any code run that's not from the NSF itself. What are you describing with that code block? Where did it come from?


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 10:27 am 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
rainwarrior wrote:
NSFe deliberately removed the playback rate feature because the original spec had suggested 60.0hz instead of the real NES rate of 60.1Hz. As such, basically every NTSC NSF rip inaccurately specifies 60.0Hz. Disch thought it would be better to remove the field entirely than obey the existing field that was normally slightly inaccurate.

I've never seen a game rip that required a custom playback rate. Many NSF players don't even support this feature. (I think I saw one game rip with a 30hz rate once, but I think it should have been ripped with a small piece of code to just run every second frame at 60hz instead.) More recently people making new NSF music have been using it for musical effects, as well as other things like enabling multiple expansions at once, etc. Whether you want to support that kind of stuff is your own decision.


Thanks. I guess I'll make this an optional feature that defaults to the actual NTSC/PAL rates unless the user demands otherwise.

zeroone wrote:
However, I don't know what you mean about SMB. The first instruction in the PLAY routine is LDA $0770, which sets the Z flag. It's not relying on the prior state of Z at all?


That's a good point. I forgot that LDA does that. I'll review my issues with SMB.

zeroone wrote:
I'm also unclear what you mean about FCEUX interjecting code? If I use the debugger to step in, execution begins at INIT, or PLAY, and there's never an opportunity to see any code run that's not from the NSF itself. What are you describing with that code block? Where did it come from?


The source code of nsf.cpp found in the fceux-2.2.2.src package contains a comment at the top and a block of hex:

Code:
//mbg 7/31/06 todo - no reason this couldnt be assembled on the fly from actual asm source code. thatd be less obscure.
//here it is disassembled, for reference
/*
00:8000:8D F4 3F  STA $3FF4 = #$00
00:8003:A2 FF     LDX #$FF
00:8005:9A        TXS
00:8006:AD F0 3F  LDA $3FF0 = #$00
00:8009:F0 09     BEQ $8014
00:800B:AD F1 3F  LDA $3FF1 = #$00
00:800E:AE F3 3F  LDX $3FF3 = #$00
00:8011:20 00 00  JSR $0000
00:8014:A9 00     LDA #$00
00:8016:AA        TAX
00:8017:A8        TAY
00:8018:20 00 00  JSR $0000
00:801B:8D F5 EF  STA $EFF5 = #$FF
00:801E:90 FE     BCC $801E
00:8020:8D F3 3F  STA $3FF3 = #$00
00:8023:18        CLC
00:8024:90 FE     BCC $8024
*/
static uint8 NSFROM[0x30+6]=
{
   /* 0x00 - NMI */
   0x8D,0xF4,0x3F,       /* Stop play routine NMIs. */
   0xA2,0xFF,0x9A,       /* Initialize the stack pointer. */
   0xAD,0xF0,0x3F,       /* See if we need to init. */
   0xF0,0x09,            /* If 0, go to play routine playing. */

   0xAD,0xF1,0x3F,       /* Confirm and load A      */
   0xAE,0xF3,0x3F,       /* Load X with PAL/NTSC byte */

   0x20,0x00,0x00,       /* JSR to init routine     */

   0xA9,0x00,
   0xAA,
   0xA8,
   0x20,0x00,0x00,       /* JSR to play routine  */
   0x8D,0xF5,0x3F,        /* Start play routine NMIs. */
   0x90,0xFE,             /* Loopie time. */

   /* 0x20 */
   0x8D,0xF3,0x3F,        /* Init init NMIs */
   0x18,
   0x90,0xFE        /* Loopie time. */
};


I didn't study the source very much. I just happened to notice that at the top and I assumed that is what they did.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 10:53 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19118
Location: NE Indiana, USA (NTSC)
zeroone wrote:
I guess I'll make this an optional feature that defaults to the actual NTSC/PAL rates unless the user demands otherwise.

Better yet, make it automatic: default to the standard rate (16639 μs for NTSC or 19997 μs for PAL) if it's within 1% of the rate in the file.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 11:04 am 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
tepples wrote:
Better yet, make it automatic: default to the standard rate (16639 μs for NTSC or 19997 μs for PAL) if it's within 1% of the rate in the file.


That sounds like a good option. Thanks.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 2:44 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5734
Location: Canada
Interesting. I'm curious how that NSFROM code is actually applied in FCEUX, since it's function is completely hidden from the debugger somehow, if it's being used. In NSFPlay, there is just code that externally controls the CPU instead; I figured most emulating players would do the same.

Hardware players do have to do it with code, of course. There is source code for PowerPak's NSF player available, and I had to create something similar myself for the 2A03 Puritans album cartridge. The TNS-HFC series of NSF playing cartridges do not support the speed parameter (they appear to be NMI-driven), though they also don't support bankswitching the high bank (i.e. the vectors are written there at load, there is no circuitry to replace them on the fly like with the PowerPak).

There isn't really a problem running NSFs at the 60.0Hz rate; all the game rips sound fine this way, just the tempo is off in a musically insignificant way. The only way I've ever seen this come up is when someone is trying to synchronize an NSF with a video. You'll be 1 second out of synch after 10 minutes. Though, usually the problem goes the other way, i.e. the nonstandard NES rate goes out of synch with non-NES video, in which case the user probably needs the NSF to play at 60.0 Hz anyway (or possibly even the standard NTSC 59.94 Hz).

Like, it's one of those things that's technically very slightly inaccurate, but doesn't normally have any significant consequences.

My own feeling is that automatically "correcting" this is not really the way to go. In the places where it's going to make a difference the user really needs complete and explicit control over the synchronization. In the majority case it won't matter at all, and in the minority where it does matter an automatic override will make it difficult for the user to diagnose and correct their problem.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 4:06 pm 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
rainwarrior wrote:
However, I don't know what you mean about SMB. The first instruction in the PLAY routine is LDA $0770, which sets the Z flag. It's not relying on the prior state of Z at all?


Further testing confirmed that I was mistaken about the Z flag requirement. But, it seems reasonable to set the I flag before the JSR to make it act more like NMI.

rainwarrior wrote:
My own feeling is that automatically "correcting" this is not really the way to go. In the places where it's going to make a difference the user really needs complete and explicit control over the synchronization. In the majority case it won't matter at all, and in the minority where it does matter an automatic override will make it difficult for the user to diagnose and correct their problem.


Good points. Food for thought.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Tue Aug 18, 2015 8:32 pm 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3471
Location: Indianapolis
rainwarrior wrote:
(I think I saw one game rip with a 30hz rate once, but I think it should have been ripped with a small piece of code to just run every second frame at 60hz instead.)

That game rip might have been 720°, I remember running into that when I made my NSF player.

And if anyone ever wondered, I'm the idiot that's responsible for most of the NSF rips that had the playback rate MSB/LSB swapped. Boy, was I surprised when I first tried an NSF player that actually used that value.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Wed Aug 19, 2015 6:16 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3944
Many homebrew NSFs override the playback rate.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Wed Aug 19, 2015 11:01 am 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
Dwedit wrote:
Many homebrew NSFs override the playback rate.


Per the discussion on another thread, the constants used in the elliptic filter for sample decimation were derived based on the CPU frequencies for NTSC and PAL. And, unfortunately, those constants were derived using Octave as opposed to within the code at runtime. It might be possible to generate a table of constants for a wide range of frequencies and interpolate for an approximation of the constants required for the specified frequency. This will require a bit of research.


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Wed Aug 19, 2015 11:04 am 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6297
Location: Seattle
NSF playback rate ≠ Sample rate...


Top
 Profile  
 
 Post subject: Re: NSF files
PostPosted: Wed Aug 19, 2015 1:22 pm 
Online
User avatar

Joined: Mon Dec 29, 2014 1:46 pm
Posts: 712
Location: New York, NY
lidnariq wrote:
NSF playback rate ≠ Sample rate...


True. But, my emulator is sampling at the CPU frequency. And, the playback rate modifies that frequency. It would have to compensate by decimating or interpolating back to the standard NTSC/PAL frequency before filtering.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group