Page 1 of 7

NTSC NES Composite Video Emulator

Posted: Tue Jan 10, 2006 3:33 pm
by blargg
I've finally completed a reasonably fast implementation of NewRisingSun's NTSC NES composite video emulator algorithm. The code converts lines of NES pixels, stored as palette indicies (0 to 0x3F), to 15-bit RGB pixels, with a 2.667 horizontal stretch factor. It runs about 50 frames per second when using the full 256x240 PPU source resolution on my 400 MHz PowerMac, so should run at full-speed on more recent machines. It's written in ANSI C with an LGPL license. The archive includes a demo that uses SDL to load an included raw NES image and display it with hue and saturation controlled by the mouse.

ntsc_emu.zip

Here are some screenshots, with even and odd fields combined as they would be when viewing them in an emulator, and with 50% scanline doubling:

Image

Image

zelda.png

castlevania.png

I'll try to post a Win32 build of my emulator with this installed, or you can post a build of yours with this integrated. :)

Re: NTSC NES Composite Video Emulator

Posted: Tue Jan 10, 2006 4:38 pm
by pragma
blargg wrote:Here are some screenshots, with even and odd fields combined as they would be when viewing them in an emulator, and with 50% scanline doubling:
Nice. I love how nasty the edges look on the different color fields; just like the real thing. If you didn't mention that you were using an emulator, I'd almost swear you used some form of video capture instead. :)

Posted: Tue Jan 10, 2006 6:09 pm
by Memblers
Looks really good.

Posted: Tue Jan 10, 2006 8:40 pm
by hap
It works over here and looks great :D

CPU usage (athlon xp 2ghz):
- me converting 15 bit colourspace to 32 bit: 17% ;p
- NTSC emulation: it depends on what's happening on the screen, usage of anything from 30% up to a lot.
examples:
- Super Mario Bros: fine
- Tiger Heli: slow down on titlescreen, better in gameplay
- Super Mario Bros 3: very slow

Also, compilation didn't work at first, because of "//" comments (using mingw/gcc, compiling with -ansi).

a test build: <<link removed>>
Note that it's a development build, so some things are broken a bit, eg. file handling, open a file by dragging/dropping it into the window. And it won't work if you have your desktop colour depth at anything but 32bit. As for default controls, open and exit it and see the .ini file.

*edit* oh, the number at the bottom left in the dosbox is the profiler; the average counts it takes to do ntsc emulation for 1 frame (not including colourspace conversion)

*edit2* ... better take those speed fluctuations with a spoonful of salt, I just tried again (that is, 2 hours later without having touched it in the meantime), and NTSC emulation speed is fairly consistent.. the profiler or timing system is probably broken somewhere.

Posted: Tue Jan 10, 2006 11:10 pm
by Marty
Fine over here too! :D

Image

Posted: Wed Jan 11, 2006 9:16 am
by kode54
http://kode54.foobar2000.org/QuickNES.zip

Just think, the emulation is quick enough for the NTSC simulation to be usable! At least on my Athlon64 3200+. Software scaling to 680x480 that doesn't involve NTSC simulation coming when I feel like it.

Posted: Wed Jan 11, 2006 9:41 am
by Zepper
Full speed here... Celeron D 2,66GHz. ;)
Man, you should include a full screen mode...

Posted: Wed Jan 11, 2006 10:06 am
by blargg
Here is an update:

ntsc_emu2.zip

- Changed output to 32-bit aRGB format. Is there any need for 16-bit RGB output on modern machines? Is it any faster? I guess I'll have to duplicate some code at some point so I can provide 16- and 32-bit output.

- Added scanline doubling at 50% intensity (can be turned off)

- Added option to use lower-quality unidirectional filtering (slightly faster)

- Changed ISO C/C++-style comments to ANSI C style

There are two quality issues with the current algorithm. The image could be a little sharper (working on this). Also, signal clamping causes some dark colors to have alternating bright and dark pixels. NewRisingSun is in the best position to resolve this, since I still don't have a good understanding of NES video output.

One feature I'd like to add is built-in mixing of even and odd fields (as I've done for the screenshots here), but by reworking my tables rather than brute-force generation of two frames and mixing them. Without this, the fields must be displayed at a very constant rate or flicker will be noticeable. This is difficult on modern monitors with refresh rates not an integral multiple of 60 Hz. Maybe it's not as much an issue on LCDs with their slower response time.

Posted: Wed Jan 11, 2006 12:37 pm
by tepples
blargg wrote:I guess I'll have to duplicate some code at some point so I can provide 16- and 32-bit output.
You won't have to duplicate too much if you create a generic algorithm using a C++ template and then instantiate it for 16-bit and 32-bit output.
- Added option to use lower-quality unidirectional filtering (slightly faster)
Analog filters are unidirectional. Most designs are either near-linear-phase (Bessel) or minimum-phase.
One feature I'd like to add is built-in mixing of even and odd fields
In other words, some sort of 3D comb filter, right?

Posted: Wed Jan 11, 2006 1:21 pm
by blargg
You won't have to duplicate too much if you create a generic algorithm using a C++ template and then instantiate it for 16-bit and 32-bit output.
But then I'd annoy the "C++ sucks" people (ANSI C is pure tedium to code in, but I wanted it to be usable by C-only folks). Plus I accumulate two 16-bit pixels into a 32-bit integer before writing, so it'd get littered with lots of conditionals (compile-time constant, of course, but still visually cluttered).

I'll work something out once the actual algorithm matches the NES more closely. Right now I'm seeing how usable my PC's audio digitizer is for examining the video signal (somewhat promising). :)
Analog filters are unidirectional. Most designs are either near-linear-phase (Bessel) or minimum-phase.
I figured as much. If I could use a unidirectional IIR filter I could speed things up a bit. If I could find a usable second-order filter, I could even merge it with the NES pixel generation loop, perhaps making the whole affair twice as fast. I guess I didn't realize that you could get an IIR to give close to linear phase (I've mostly dealt with FIR filtering); I'll read up on that.

And yeah, what I was talking about would pretty much be an inter-frame comb filter.

EDIT: I found a nifty online filter constructor.

Re: NTSC NES Composite Video Emulator

Posted: Wed Jan 11, 2006 1:50 pm
by Disch_
blargg wrote:The code converts lines of NES pixels, stored as palette indicies (0 to 0x3F), to 15-bit RGB pixels
How does this work with color emphasis? Or is that not yet supported?

(yeah it's me, Disch -- too lazy to keep logging in -- gotta figure out why it logs me out every 2 minutes)

NES Turing Test

Posted: Wed Jan 11, 2006 2:53 pm
by Jagasian
Looks great. True emulation also recreates the flaws. I'm loving this. When is soming going to emulate a faulty 72-pin connector? I'm am only partly joking, really. The Atari 2600 scene emulates what is called "frying", and it might be fun to be able to select how perfectly the emulated NES's connector works... I can't be the only person who enjoyed "glitching out" their NES games as a kid by playing a game that is having technical issues due to a bad connection, yet the game still runs.
FRYING
A term coined by Russ Perry Jr. and Dave Perry. "Frying" refers to drawing out strange effects in a game system by rapidly switching the power on and off. One way to do this is to attempt to set the power switch exactly between on and off.
I don't want to get too far off topic, but I just wanted to say that this "warts and all" emulation is the right direction for NES emulation. I eagerly await the day when "NES Turing Test" competitions are held, where emulators are lined up against real NES systems, to see which emulators can fool the most people into thinking that the controller and TV are connected to a real NES as opposed to a PC emulating a NES. Of course the actual system being played would be hidden, so that the only discriminating factors would be A/V output, controller response, gameplay, etc and not just being able to look at the system behind the curtain.

Posted: Wed Jan 11, 2006 3:10 pm
by hap
*edit* kode54: QuickNES crashes here with an illegal instruction. Does it require SSE2 and/or SSE3 ?
Disch: it doesn't support colour emphasis.

You're saying (in readme.txt) the monochrome mode hasn't been implemented, but there's no need to do that. It's just the normal palette, masked with $F0, that can normally be done in the ppu emulator. (I do pixel=(pixel&mono_mask)|emphasis, a 9 bit number)

Could you add an option to disable darkened/black scanlines completely ? To disable it now, I just commented out *2 in [out = (char*) out + out_pitch * 2;] in ntsc_emu_blit.

Have you noticed it's slower when black is used a lot ? eg. Super Mario Bros 3 end of level, or Super Mario Bros 1 level 1-2.
As for the speed fluctuations in my previous post, that was due to compilation with sse and 3dnow enabled.. strange; normally that should be faster, but in this case, eg. Super Mario Bros 1, level 1-1 is faster than normal compilation, but level 1-2 is very very very slow.

Posted: Wed Jan 11, 2006 3:20 pm
by kode54
Yum, I almost forgot about denormal numbers.

Posted: Wed Jan 11, 2006 4:55 pm
by blargg
Jagasian, fun idea regarding bad connection emulation, and I like the concept of a NES Turing Test.

Good point about monochrome, hap. Noted in readme.
Could you add an option to disable darkened/black scanlines completely?
It'd be best to collaborate on a more stable interface in general (once we've identified needed features). For now I suggest modifying the code to suit your use and either posting or sending me a private message of your changes. At this point I don't have a good idea of the requirements.

Kode54, good page about denormals. I added a small bias (1.0 / 65536) to the luminance values in the table, which will hopefully address this issue. Apparently this doesn't affect PowerPC much since I saw no speed change when the screen was almost all black.

I looked into a Bessel function and got something working fairly well. I can now get full-sized output running on my machine at full-speed with quality nearly the same as the original. I was able to choose a cutoff frequency that resulted in one of the coefficients being 1.0, making it more efficient. This one is slightly more blurry than before, but fairly close. There are still more implementation choices to explore regarding filtering.

Here is a source code update:

ntsc_emu3.zip

I'm kind of rushing because I'd like to get outside before it gets dark.