Writing a NSF player
Moderator: Moderators
Re: Writing a NSF player
Do NSF players usually run on single thread or do they run on multiple? If they run on one, should the player really take much processing power at all?
Avatar is pixel art of Noah Prime from Astral Chain
Re: Writing a NSF player
The only computationally intensive thing is resampling the original audio stream at 1.8MHz down to whatever is supported by the sound card, and that only if you synthesize audio that way in the first place.
Re: Writing a NSF player
Unless some error occurred in my emulation code, FamiTracker's NSF files seem to use some illegal opcodes. I have currently set it so that when encountering one, a message is printed to console and the player is halted. The specific opcode is LAX, zpg ($A7). Since I have to implement these, how do the status flags behave on the ones that are usable? Some are mentioned to affect no flags, though. At least for LAX, both A and X will have the same value, so it probably doesn't matter which register you check the flags with, unless the status flags behave differently for some reason
Avatar is pixel art of Noah Prime from Astral Chain
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Writing a NSF player
Regular Famitracker does not use illegal opcodes AFAIK, so that could indicate another issue, though I couldn't say whether the 0CC fork uses them.
I think I learned most of what I needed to know about the illegal opcodes from this document, and by inspecting source code of a few emulators:
http://www.oxyron.de/html/opcodes02.html
I think I learned most of what I needed to know about the illegal opcodes from this document, and by inspecting source code of a few emulators:
http://www.oxyron.de/html/opcodes02.html
Re: Writing a NSF player
The original cause of the issue was JMP incrementing PC after it was set to the target value. Next problem seems to be either with JSR or RTS. I'm not sure which one it is though.
Just in case there is some obvious mistake that I just can't see here is how I have implemented the said instructions: https://pastebin.com/GpFKfKQf
I'm not sure how easy it is to read, but I added some comments to hopefully make it a bit easier to understand.
(EDIT: cv. and sr. are references to other files)
Just in case there is some obvious mistake that I just can't see here is how I have implemented the said instructions: https://pastebin.com/GpFKfKQf
I'm not sure how easy it is to read, but I added some comments to hopefully make it a bit easier to understand.
(EDIT: cv. and sr. are references to other files)
Avatar is pixel art of Noah Prime from Astral Chain
Re: Writing a NSF player
JSR is supposed to add 2 to PC before pushing it, and RTS is supposed to add 1 after pulling PC. Some NSFs rely on this as a means of jumping through a jump table.
Re: Writing a NSF player
I did a few tests some time ago before implementing these instructions, but just to be sure:
-JSR pushes the high byte to stack first and then the low byte
-RTS pulls the low byte first and then the high byte
-JSR pushes the high byte to stack first and then the low byte
-RTS pulls the low byte first and then the high byte
Avatar is pixel art of Noah Prime from Astral Chain
Re: Writing a NSF player
I took a look at bankswitching and I don't understand the meaning of the "padding bytes" that you get from load_addr AND #$0FFF. Can someone explain this a bit further?
Avatar is pixel art of Noah Prime from Astral Chain
Re: Writing a NSF player
An NSF that doesn't use bankswitching can be loaded directly into NES memory based on the load address, and one that does needs to be loaded as a (potentially larger) ROM that gets mapped into NES memory.
If I understand it correctly (I'll need to implement this myself soon), basically you're building a ROM out of 4kB banks. AND #$0FFF keeps the padding within that 4kB size, those upper 4 bits of the load address are ignored. The NSF load address becomes the beginning load address for your ROM. Like this:
NSF header says load address is $8456 (or $A456, $F456 would all be the same)
[ROM at 0x000000] bank 0
[padding up until 0x000456]
[load NSF data from here on]
If I understand it correctly (I'll need to implement this myself soon), basically you're building a ROM out of 4kB banks. AND #$0FFF keeps the padding within that 4kB size, those upper 4 bits of the load address are ignored. The NSF load address becomes the beginning load address for your ROM. Like this:
NSF header says load address is $8456 (or $A456, $F456 would all be the same)
[ROM at 0x000000] bank 0
[padding up until 0x000456]
[load NSF data from here on]