{"id":1017,"date":"2009-10-09T16:00:05","date_gmt":"2009-10-09T23:00:05","guid":{"rendered":"https:\/\/www.reenigne.org\/blog\/?p=1017"},"modified":"2009-10-09T16:57:23","modified_gmt":"2009-10-09T23:57:23","slug":"ntsc-hacking","status":"publish","type":"post","link":"https:\/\/www.reenigne.org\/blog\/ntsc-hacking\/","title":{"rendered":"NTSC hacking"},"content":{"rendered":"<p>Recently I've been playing about doing NTSC decoding in software, trying to build an software TV\/monitor for emulation purposes. I originally wanted to do the decoding of sampled composite signals to RGB and the horizontal scaling in a single step (precomputing a finite impulse response filter which does it all). However, I have come to realize that while this would yield the fastest code, it's not sufficiently flexible for what I want to do.<\/p>\n<p>Specifically, in the signals I want to decode, the horizontal sync pulses can happen at any point (within a certain range) which means that the relationship between samples and horizontal pixel positions is not fixed in advance. This means that it's better to do the decoding (at least composite to YIQ if not all the way to RGB) at a fixed frequency and then rescale the result to pixels in real time (possibly using linear or cubic rescaling).<\/p>\n<p>Having determined this, I looked to see what other NTSC software implementations do. <a href=\"http:\/\/www.fly.net\/~ant\/libs\/ntsc.html\">Blargg's NES filter<\/a> rescales at a ratio of 3:7 at the same time as it decodes, then it's up to the calling code to rescale this to the right width. <a href=\"http:\/\/linux.die.net\/man\/1\/xanalogtv\">xanalogtv<\/a> converts composite to YIQ at 4 samples per color carrier cycle, uses linear rescaling on the YIQ samples and then converts the result to RGB. The resulting pixels may be doubled or tripled to get to the right width. This also allows for nice effects such as \"blooming\" (widening brighter lines).<\/p>\n<p>My current simulator is <a href=\"http:\/\/www.reenigne.org\/misc\/crtsim.zip\">here<\/a> and the source is <a href=\"http:\/\/www.reenigne.org\/misc\/crtsim_src.zip\">here<\/a> (Windows only for now - sorry). This uses similar techniques to xanalogtv, but the rescaling is done by the GPU, in RGB space. The scanline effects are a bit more accurate (all the scanlines appear to be the same width, no matter what size the window is), and a phosphor mask is displayed. Most reasonably modern machines should be able to display the images at full speed (60Hz). If your machine is too slow or your monitor doesn't run at 60Hz there may be some odd effects (most LCD panels run at 60Hz). I believe this is the only software CRT simulator that correctly renders both interlaced and non-interlaced signals, and has physically correct phase-locked-loop line frequency behavior. If I can figure out how to add a pixel shader for light bloom, I should be able to get images as good as <a href=\"http:\/\/nfgworld.com\/mb\/post\/1720\">these<\/a> (except with arbitrary scaling in real time).<\/p>\n<p>One other rough edge in this version is that the horizontal sync pulse is currently only found to the nearest sample. This means that the phase locked loop isn't very tunable, and will cause problems for PAL signals (where the horizontal sync position is at a different subsample offset on every line). That should be quite easy to fix, though.<\/p>\n<p>This simulator is going to form the basis for my <a href=\"https:\/\/www.reenigne.org\/blog\/demo-machine\/\">demo machine<\/a> emulator. The emulator itself is trivial - in fact I have already written it. But I haven't tried it out yet because I have no program to run on it. First I have to write an assembler for it. I might tweak the instruction set a bit somewhat in doing so, so I don't want to release the emulator just yet. Watch this space!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I've been playing about doing NTSC decoding in software, trying to build an software TV\/monitor for emulation purposes. I originally wanted to do the decoding of sampled composite signals to RGB and the horizontal scaling in a single step (precomputing a finite impulse response filter which does it all). However, I have come to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38],"tags":[],"class_list":["post-1017","post","type-post","status-publish","format-standard","hentry","category-emulation"],"_links":{"self":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1017","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/comments?post=1017"}],"version-history":[{"count":3,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1017\/revisions"}],"predecessor-version":[{"id":1073,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1017\/revisions\/1073"}],"wp:attachment":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/media?parent=1017"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/categories?post=1017"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/tags?post=1017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}