Help appreciated in constructing a hardware NSF-Player

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Help appreciated in constructing a hardware NSF-Player
by on (#99590)
Hello.
I am making a hardware NSF player, which consists of a NES CPU, RAM and programmable rom (Flash) memory, MMC card reader, LCD display and a CPLD, which generates enable signals for RAM/ROM/LCD, helps to communicate CPU with MMC and so on.

the memory area I designed is as follows:
$0000-$1fff - 8KB RAM
$2000-$2007 - registers for MMC card & LCD
$3000-$3fff - 4KB RAM
$5FF8-$5fff - bankswitching registers
$6000-$7fff - 8KB RAM
$8000-$8fff - 4KB ROM bank0
$9000-$9fff - 4KB ROM bank1
$a000-$afff - 4KB ROM bank2
$b000-$bfff - 4KB ROM bank3
$c000-$cfff - 4KB ROM bank4
$d000-$dfff - 4KB ROM bank5
$e000-$efff - 4KB ROM bank6
$f000-$ffff - 4KB ROM bank7

Also the whole ROM space can be switched in 64kb chunks.
The 50 HZ hardware generated signal (for executing play routine in proper intervals) is connected to the /NMI line of CPU.

When the device is turned on, it is executing the bootloader code (that is available in the ROM - chunk 0).
It it copying the useful procedures to the unused by NSF RAM areas ($3000-3FFF) and begins executing code from there.
Then it switches the ROM to the chunk1 and fills it with the NSF code.

The NMI interrupt vector is pointing to some location in the $3000-$3FFF RAM, where resides the procedure for updating LCD display and executing the play code from NSF.

There aren't any problems with the not bankswitched NSF files - ROM is programmed with its contents, except the $FFFE-$FFFF address, which is programmed to point to the procedure in the $3000-$3FFF RAM.

However, some problems occurs with bankswitched music, because the last rom bank (7), which contains NMI interrupt vector might be switched during playback, so I don't know in which banks' last 2 bytes should I write the NMI addres vector?
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99599)
I also wrote some ideas for a hardware NSF, but using a cartridge, not this, so some of my ideas won't apply.

Must the ROM be written with the .NSF before playing? If not, you need RAM there, not ROM (look at my specification for writing register $4018; this could be used to write-protect the RAM if it is not FDS).

Do you intend to support expansion chips?

I think the specification of NSF says that the init and play routines should not be called until they return, so NMI cannot be used for that unless you have some hardware to disable it; otherwise use IRQ instead (the NSF specification says that programs in .NSF files should not mess with IRQ).
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99600)
There have to be ROM there, because after power-up something must be run from it!

Yes, it is a hardware 50 Hz generator, controlled from the CPU so it can be enabled or disabled by CPU:
Image

No matter if it's NMI or IRQ, but there is still a problem which bank to place the interrupt vectors in.

I am not going to support any chips (even the FDS) at the moment.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99605)
As for finding which bank to put the vectors in, I'd recommend using a 13-input NAND gate (74HC133) to decode $FFF8-$FFFF. You may also need a 1 MHz programmable clock divider that you can load with $4E20 (PAL) or $4100 (NTSC) or a lower value that some high-update-frequency NSFs use.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99606)
For the most part NSFs do not interfere with IRQ, but very recently Delek has decided to start suppressing the IRQ so he can use the frame counter as a timer in his Deflemask program (thread).

So, at this point if it's all the same to you I'd recommend using NMI as your timer instead of IRQ. The other alternative is polling a custom mapper register timer like the powerpak does.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99614)
tepples wrote:
As for finding which bank to put the vectors in, I'd recommend using a 13-input NAND gate (74HC133) to decode $FFF8-$FFFF. You may also need a 1 MHz programmable clock divider that you can load with $4E20 (PAL) or $4100 (NTSC) or a lower value that some high-update-frequency NSFs use.

Tepples, your post cofirm me in the conviction that the NSF format is not prepared for use with hardware players, as because it completely do not support external interrupt.

And bytes at fff8-ffff might not always be considered as interrupt vectors, but might be the song opcodes if rom is not prepared for nmi and it will still not work.

So i will overwrite the FFFA-FFFF of the bank, which number is in the bankswitch table at offset $77 in nsf.

However, if there is $0 at offset $77, i will consider that at there is the last bank number, not $0 and will ovewrite the last bank.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99616)
kryzysiobal: I think you misunderstood about IRQs and NSF. The NSF format was absolutely designed to be playable on hardware.

The NSF itself is not supposed to use interrupts, but the reason for this is that the hardware player may need them. So, your player is allowed to use interrupts in any way it wants, it's the NSF code that isn't.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99620)
rainwarrior wrote:
kryzysiobal: I think you misunderstood about IRQs and NSF. The NSF format was absolutely designed to be playable on hardware.

The NSF itself is not supposed to use interrupts, but the reason for this is that the hardware player may need them. So, your player is allowed to use interrupts in any way it wants, it's the NSF code that isn't.


No, it is not. There is no way to distinguish if a CPU reads from FFFE-FFFF is because of CPU fething the NMI address or because of fetching opcode at this address.

Lets give for example: Hello Kitty no Ohanabatake.nsf and let's look at its $70-$78 offsets:

unsigned char data[8] = {
0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00
};

so $8000-$8FFF is bank 0,
$9000-$9FFF is bank 0,
$AAAA-$AFFF is bank 1,
etc
and $F000-$FFFF is bank 0.
Let's write my NMI routine address at the last 2 bytes - $FFFE and $FFFF. So its the last
two bytes of first 1Kbyte of NSF rom file. However cpu opcodes and data resides here and they will be corrupted if I do so!

Hopefully I can assign any bank number to $F000-$FFFF and the game still work.

But what if the game used all bytes of its all banks, so that nothing could be overwritten?

Allright, Mr tepples suggested nice idea of exchanging those bytes in-the-fly by using address decoding.
But what if the game has at $FFFE-$FFFF part of its procedure?

Then CPU asserting its address lines to $FFFF would want to read opcode to execute, but if I would exchange it into NMI vector in the fly, trash would be executed.

Buf if the NSF specification said that FFFA-FFFF cannot contains any opcodes, it would be nsf-hardware friendly.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99621)
The original NSFs were ripped from actual NES games. NES games couldn't have anything in $FFFA-$FFFD other than the game's NMI and reset vectors. So I guess it'd be safe to assume that $FFFA-$FFFF won't have any opcodes.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99623)
I wouldn't be so sure.
The game I mentioned above (Hello Kitty no Ohanabatake.nsf) uses bankswitching in NSF.
But it is a CNROM game, so no PRG-ROM bankswitching, only CHR. Why the hell it is bankswitched?
The only idea that cames into my head is that the authors of the game put the songs in the CHR-MEMORY and the CPU reads them into its RAM via PPU (PPUDATA addres).

Btw. Why there are 0, 0, 0, 0, 0, 0, 0, 0 in the $70-$78 to indicate no bankswitching? It would be much convenient to put there 0, 1, 2, 3, 4, 5, 6, 7.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99624)
0,0,0,0,0,0,0,0 for no bankswitching also indicates that writing the bankswitch addresses should not bankswitch. (In practise, doesn't amount to anything, since no NSFs I know of write those addresses unnecessarily).

Also, NSFs should not put anything they expect to read in the vector table $FFFA-FFFF. (I think we forgot to mention this in the spec.) Just override it with your own player's NMI / IRQ / Reset handlers. (You don't need to worry about NSFs trying to read these addresses; I don't think there are any.)

Questions about why an NSF is bankswitched, etc. we can't answer for you. The person who made the NSF would have made that decision for one reason or another. NSF rips of games generally have modifications of those games to work as an NSF, so you should expect changes like this. (A lot of NSFs use a bankswitc table to fill in the ROM sparsely, for instance.)
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99627)
rainwarrior wrote:
Also, NSFs should not put anything they expect to read in the vector table $FFFA-FFFF. (I think we forgot to mention this in the spec.) Just override it with your own player's NMI / IRQ / Reset handlers. (You don't need to worry about NSFs trying to read these addresses; I don't think there are any.)
Add it to the specification if it is agreed to do so; I agree this would help.

I believe my hardware NSF design is 100% compliant (although it could be simplified a little bit if $FFFA-$FFFF is not allowed for use by the .NSF; but I did manage to make it work even without that rule), so some of the ideas mentioned there might help. Since your NSF player is not a cartridge, some of the things I mentioned are not relevant to this project, though.
Re: Help appreciated in constructing a hardware NSF-Player
by on (#99629)
I did update the wiki NSF spec. Added it to the caveats section.