NES Programming Blog

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

PRG bankswitching ?
Maybe with MMC3.

And, I plan to add a DMC music/sfx example.

And, I want to do a much better SMB3/Metroid bi-directional platformer with better physics.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: NES Programming Blog

Post by zeroone »

mikejmoffitt wrote:Your blog made front-page on Hacker News!
link
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

mikejmoffitt wrote:Your blog made front-page on Hacker News!

Do you have any plans on discussing PRG bankswitching techniques from a C context?
Done.

http://nesdoug.com/2016/01/15/24-mmc3-b ... hing-irqs/


Any more requests?
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: NES Programming Blog

Post by thefox »

There are some problems with the example:

- You should save/restore registers in your IRQ handler. But in fact when writing IRQ handlers in C, this is not enough, because the C runtime library's (fairly large) state should also be saved. In your example you got lucky (without "-Oi" switch the program doesn't work). In addition some of the runtime library routines are not reentrant. See http://www.cc65.org/faq.php#IntHandlers

- Your example is placing the runtime library code in one of the switchable banks ("CODE"). If any code is placed in one of the other switchable banks (e.g. "CODE1"), and the compiler emits a call to the runtime library routines, you'll get a crash. It'd be better to place the runtime library stuff in the fixed bank, which you can do by simply calling the fixed segment "CODE". (NOTE: In this case the "-Oi" switch and the simplicity of the example causes there to be no runtime library code emitted in the ROM, but this is not something that can be relied on.)

These are kind of annoying problems, because your program might appear to work fine without taking them into consideration, and then stop working one day when the code is modified. Thus, careful testing is needed to make sure that everything works as it should.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

Thanks for the response. I hadn't considered the C runtime library being in a non-fixed bank. I'll have to change the example code.

So far, I've only made small technical demos with MMC3. So, I haven't run into any errors...that will certainly happen with more complex code.

Oh, and I see from the link you posted...
I do usually suggest to think about writing the interrupt handler completely in assembler
.

I was hoping to write the IRQ code in C, so people could read it more easily. But, I guess it should probably be written in ASM, for stability. Otherwise, I would have to copy all the zeropage C variables, at the start of the IRQ and replace them at the end of the IRQ.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: NES Programming Blog

Post by thefox »

dougeff wrote:I was hoping to write the IRQ code in C, so people could read it more easily. But, I guess it should probably be written in ASM, for stability. Otherwise, I would have to copy all the zeropage C variables, at the start of the IRQ and replace them at the end of the IRQ.
Yeah. An additional problem with IRQ handlers written in C is that you can't rely on the timing of the C code very much.

...

(Random idea: It would be possible to provide a sort of a generic IRQ handler (written in assembly) for the common tasks like setting the scroll and switching the CHR bank. Then one could simply set some variables e.g. scroll and the new CHR bank from C code, and maybe some bitflags to indicate what values were set. Assembly code would then pick up the values and take care of the rest. Might be a reasonable idea for a game engine, but maybe not so good for a tutorial.)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

It occurred to me, that all of my example codes in my tutorial will have problems if the logic runs longer than 1 frame...Here's what they all do...

BIG MAIN LOOP {
-wait for NMI-flag
-then update the PPU
-then set the scroll
-then logic
}

NMI {
-set NMI-flag
}

This runs fine for short games / demos. And, allowed me to write nearly everything in C.

but, if the logic ran too long, it might be outside of the next V-blank by the time it starts updating the PPU and setting the scroll, etc. --misaligning the screen every time lag happens.

How would you guys address this problem? Would you have the PPU update, Sprite update, set scroll stuff in the NMI handler? And maybe music (because laggy music is annoying).

What if I put a test for the NMI-flag at the end of the MAIN() loop, and if it has already been set, reset it to zero, so that it waits for yet another NMI to occur before. The screen will never misalign, but lag would be more frequent (maybe.)
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: NES Programming Blog

Post by rainwarrior »

if "set NMI flag" means you increment a counter, and "wait for NMI flag" means you read the counter and spin until it changes, then there should be no problem with scroll corruption or anything; a long frame should just result in slowdown.

My own recommendation is:

1. Put all PPU updates in NMI handler.
2. Guarantee that the PPU updates can't run longer than vblank.
3. Create a variable that controls whether the PPU update happens (i.e. "update ready"). Item 1 should only run when a new set of updates are ready.
4. Run music in NMI handler too, this means that music is not subject to slowdown.

If you have a scroll split, or other raster effect timings to make, ideally these should also be done in the NMI handler, or in an IRQ that's set up by the NMI handler. Basically the idea is that the NMI has reliable timing (at least until it gets to the music), and the main thread does not. The main thread prepares a frame worth of updates (taking as long as it needs), then uses that variable (item 3) to signal the NMI thread to send it to the PPU.

I would even go as far as to leave the NMI on always, so that music can play smoothly during screen transitions, etc. Because NMI PPU updates only happen on request, you can do stuff like load nametables in the main thread safely. (Also, only ever turn rendering on or off in the NMI handler, so you never get a partial screen.)
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

I added a tutorial on 'Importing a MIDI file to Famitracker'...because I still can't find much information on this subject, I made up my own technique. My method is very specific to the program REAPER that I prefer to use. If anyone has a better method, please let me know - or any specifics on how to do this with any other software, thanks.

http://nesdoug.com/2016/01/24/25-import ... mitracker/
nesdoug.com -- blog/tutorial on programming for the NES
Bavi_H
Posts: 193
Joined: Sun Mar 03, 2013 1:52 am
Location: Texas, USA
Contact:

Re: FamiTracker 0.4.2's MIDI import

Post by Bavi_H »

dougeff wrote:I added a tutorial on 'Importing a MIDI file to Famitracker' [...] If anyone has a better method, please let me know - or any specifics on how to do this with any other software, thanks.
When you record form a keyboard, I agree it's a good idea to quantize the notes. I also agree keeping the MIDI channels monophonic is a requirement to get meaningful results in the FamiTracker import.

I don't use Reaper, so I'm not sure how Reaper's "project tempo" and "MIDI track tempo" interact. Can you tell more about why you set the project tempo to 12 BPM before recording, then later set the project tempo to 100 BPM and the MIDI track tempo to 125 BPM? In my import tests, I didn't need to modify the MIDI file tempo, but I did modify the MIDI file resolution (MIDI ticks per quarter note).

I took a look at FamiTracker 0.4.2's MIDI import source code. There are some flaws and limitations in the MIDI import, so there's never really going to be an easy way to use it. Here are some explanations of the flaws and limitations and alternative ideas I've come up to work around them until a better MIDI import process is found.

Terminology notes

MIDI file ticks and module ticks are different concepts. For clarity, I call them "MIDI ticks" and "module ticks".
  • In a MIDI file, the MIDI tick is the smallest time unit you can use to specify the position of an event. The MIDI file resolution is the number of MIDI ticks per quarter note the MIDI file uses, for example 240 MIDI ticks per quarter note or 960 MIDI ticks per quarter note or other values.

    In a module file, the smallest time unit you can enter notes with is a row, but effects like arpeggios work on smaller units of module ticks. The module Speed value sets the size of a row in module ticks.
Also, MIDI file tempo (quarter notes per minute) and module Tempo (double-dozen module ticks per minute) are different concepts.
  • When the module Speed value is the default 6 and a "beat" is 4 rows, then the module Tempo value happens to be in beats per minute. Otherwise, the module Tempo value is really in double-dozen module ticks per minute, and you have to calculate what module Tempo value to set to get the "real tempo" you want. Additionally, the FamiTracker status bar shows a "real tempo" value in beats per minute by using the "Row highlight, 1st" value in the toolbar as the size of a beat in rows.
FamiTracker 0.4.2 MIDI import flaws, limitations, and workarounds

In the MIDI protocol, there are two alternative ways to specify the end of a note: use a Note Off event or use a Note On event with velocity zero. Unfortunately, FamiTracker's MIDI import incorrectly considers a Note On event with velocity zero as a normal Note On event.
  • If you are using a MIDI editor that specifies the end of notes using a Note On event with velocity zero, here is one way to work around that. Drag the end of each note up to the beginning of the next note, so there are no gaps between the notes. During the import, the end of the each note will be converted as a new note, but then get overwritten by the beginning of the next note.
In every MIDI file track, FamiTracker removes any silent part before the first note. In other words, each track is shifted so its first note begins at row 0.
  • If your MIDI file tracks have first notes that begin at different times, try one of these options:
    • Convert the MIDI file to format 0. This creates a MIDI file with everything in one track.
    • In each track that has a delayed start, put a dummy note so that all tracks have a note at the same starting point. After the import, erase the dummy notes in the module.
FamiTracker converts a note's position from MIDI ticks into module rows by considering each module row as 24 MIDI ticks in size.
  • One way to control the MIDI note size to module row size conversion is to use a MIDI editor that lets you change the MIDI file resolution (MIDI ticks per quarter note). As a typical suggestion, if you want a quarter note to be 4 rows, you would change the MIDI file resolution to 96 MIDI ticks per quarter note.
FamiTracker incorrectly sets the number of frames in the song to 1 less frame than needed.
  • After the import, increase the Frames value by 1 to show the last imported frame.
FamiTracker sets the module Speed and Tempo to strange values.
  • If you set the MIDI file quarter note to be 4 rows, then I recommend changing the Speed to 6 and changing the Tempo to the same tempo value the MIDI file uses (in quarter notes per minute).
FamiTracker creates a strange instrument.
  • Delete the strange instrument and create new instruments for each channel.
FamiTracker ignores Note Off events in the MIDI file.
  • Depending on the final instrument you use, each note will continue sounding up to the start of the next note. If you don't want that, you'll need to manually create note cuts or releases, or use an instrument with a volume that fades out.
MIDI editor Sekaiju

Sekaiju is a MIDI editor for Windows you can use to convert a MIDI file to format 0 and change its resolution.

Sekaiju defaults to Japanese. To change to English:
  • 1. Go to the (S) menu and choose Language.
    2. Set "User Interface" to English and "Text Encoding" to "1252-Western Latin-1 [recommended]". (Or if you know you want to interpret the text in a MIDI file using another encoding, choose the encoding you want.)
    3. Restart Sekaiju.
After you open a MIDI file in Sekaiju, here's how to convert the format and resolution:
  • 1. Go to the File menu and choose Property.
    2. In the SMF Format section, choose SMF Format 0.
    3. In the Time Mode Resolution section, "TPQN Base (recommended)" should already be selected. My default suggestion is to change the Resolution value to 96 Ticks / Quarter Note.
russellsprouts
Posts: 53
Joined: Sun Jan 31, 2016 9:55 pm

Re: NES Programming Blog

Post by russellsprouts »

Thanks for the tutorials, Doug. I'm developing in assembly and on Linux (which requires liberal use of Wine to run a lot of the tools), but your sample code was exactly what I needed to get started. I ended up only using your HelloWorld example as a base for my code, but I've read the whole series and it's been very helpful. Keep up the good work!
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

Can you tell more about why you set the project tempo to 12 BPM before recording, then later set the project tempo to 100 BPM and the MIDI track tempo to 125 BPM?
I'm still working on ways to import MIDI to Famitracker. My first tests, with BPM at 120 in Reaper, import with WAY too many ticks per note. So, I did a few tests, recording with BPM (in Reaper) set to 12 (while I play the notes at 120 BPM) gets it very close to what I want. With 5 ticks per note in Famitracker. Changing the project BPM to 100 and then forcing the MIDI track to 125 (125/100 = 5/4)...imports with 4 ticks per note.

Obviously, this isn't ideal, and needa refined.

The famitracker file will still need LOTS of editing (adding note ends, volume column, etc).

Thanks for the input, Bavi_H.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

@russellsprouts

Thanks for the positive comments. BTW, there's an error in my button pressing code, I'll update as soon as I get a chance (maybe next week). Sorry.
nesdoug.com -- blog/tutorial on programming for the NES
Bavi_H
Posts: 193
Joined: Sun Mar 03, 2013 1:52 am
Location: Texas, USA
Contact:

Re: NES Programming Blog

Post by Bavi_H »

dougeff wrote:My first tests, with BPM at 120 in Reaper, import with WAY too many ticks per note. So, I did a few tests, recording with BPM (in Reaper) set to 12 (while I play the notes at 120 BPM) gets it very close to what I want. With 5 ticks per note in Famitracker. Changing the project BPM to 100 and then forcing the MIDI track to 125 (125/100 = 5/4)...imports with 4 ticks per note.
To make sure we're talking about the same thing, perhaps you mean "rows" instead of "ticks"? (That is, your 1st test yielded many rows per note, 2nd test 5 rows per note, and 3rd test 4 rows per note?) In FamiTracker and other trackers, rows are what you see on the screen -- each line is a row. Ticks are sub-units of a row that you hear when you use arpeggios and other effects commands in the last three character columns of each channel. (In FamiTracker, go to the Help menu and choose "Effect table" to see the effects commands.)

[Note: Everywhere else in my comments here, I'm saying "module ticks" to mean ticks in FamiTracker and "MIDI ticks" to mean ticks in a MIDI file, because these two kinds of ticks do not correspond to each other.]

In your screenshot of Reaper's "MIDI item properties" box, it says "Ticks per quarter note: 960". This suggests Reaper is making a MIDI file with 960 MIDI ticks per quarter note, so that would mean a quarter note in the MIDI file would import into FamiTracker as 40 rows.

I can understand setting the tempo to 12 BPM, pressing the record button, then playing along with a 120 BPM metronome. In that case, you're basically recording notes 1/10 of the size -- 10 "played" quarter notes (96 MIDI ticks each) fit into 1 "Reaper" quarter note (960 MIDI ticks). But I think that should import into FamiTracker as 4 rows per "played" quarter note. So I don't understand why it imported as 5 rows per "played" quarter note for you.

However, instead of playing around with tempos further trying to understand that, it might be easier to change the MIDI ticks per quarter note. I took a look at the Reaper manual, and it looks like you can change MIDI ticks per quarter note as follows:

1. Go to the Options menu and choose Preferences.
2. Go to the category Media and the sub-category MIDI.
3. Change the value for "Ticks per quarter note for new MIDI items".

To make a quarter note in the MIDI file become 4 rows in FamiTracker, set Reaper to use 96 MIDI ticks per quarter note.

If you want a different relationship between MIDI quarter notes and FamiTracker rows, figure out the correct MIDI ticks per quarter note to use based on this fact: during the import, FamiTracker considers each row to be 24 MIDI ticks.
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: NES Programming Blog

Post by dougeff »

Thanks again. I'll give it a try.
nesdoug.com -- blog/tutorial on programming for the NES
Post Reply