From 88ac9a14bc0560f4d5273a19dcd52c4828285615 Mon Sep 17 00:00:00 2001 From: Persune Date: Tue, 10 Jan 2023 20:19:28 +0800 Subject: [PATCH 01/36] Add NTSC-CRT library from https://github.com/LMP88959/NTSC-CRT --- Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 7 +- Core/LMP88959NtscFilter.cpp | 213 +++++++ Core/LMP88959NtscFilter.h | 30 + Utilities/Utilities.vcxproj | 2 + Utilities/Utilities.vcxproj.filters | 4 + Utilities/crt.c | 906 ++++++++++++++++++++++++++++ Utilities/crt.h | 160 +++++ 8 files changed, 1321 insertions(+), 3 deletions(-) create mode 100644 Core/LMP88959NtscFilter.cpp create mode 100644 Core/LMP88959NtscFilter.h create mode 100644 Utilities/crt.c create mode 100644 Utilities/crt.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 3c3eba030..1d3f6869f 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -589,6 +589,7 @@ + @@ -1122,6 +1123,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index d82dc5571..1736d7af3 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -874,9 +874,6 @@ Nes\Mappers\Unnamed - - Nes\Mappers\Unnamed - Nes\Mappers\Unnamed @@ -1853,6 +1850,7 @@ Nes\Mappers\EPSG + @@ -2164,5 +2162,8 @@ Nes\Mappers\EPSG + + Misc + \ No newline at end of file diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp new file mode 100644 index 000000000..56cc944f6 --- /dev/null +++ b/Core/LMP88959NtscFilter.cpp @@ -0,0 +1,213 @@ +// NTSC filter based on EMMIR's composite modem +// https://github.com/LMP88959/NTSC-CRT +#include "stdafx.h" +#include "LMP88959NtscFilter.h" +#include "PPU.h" +#include "EmulationSettings.h" +#include "Console.h" + +LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoFilter(console) +{ + memset(_palette, 0, sizeof(_palette)); + memset(&_ntscData, 0, sizeof(_ntscData)); + _ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240](); +} + +FrameInfo LMP88959NtscFilter::GetFrameInfo() +{ + OverscanDimensions overscan = GetOverscan(); + uint32_t overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0; + uint32_t overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0; + + if(_keepVerticalRes) { + return { + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight), + (PPU::ScreenHeight - overscan.Top - overscan.Bottom), + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), + PPU::ScreenHeight, + 4 + }; + } else { + return { + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight, + (PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2, + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), + PPU::ScreenHeight * 2, + 4 + }; + } +} + +void LMP88959NtscFilter::OnBeforeApplyFilter() +{ + bool paletteChanged = false; + uint32_t* palette = _console->GetSettings()->GetRgbPalette(); + for(int i = 0, len = _console->GetSettings()->IsFullColorPalette() ? 512 : 64; i < len; i++) { + uint8_t r = (palette[i] >> 16) & 0xFF; + uint8_t g = (palette[i] >> 8) & 0xFF; + uint8_t b = palette[i] & 0xFF; + + if(_palette[i * 3] != r || _palette[i * 3 + 1] != g || _palette[i * 3 + 2] != b) { + paletteChanged = true; + + _palette[i * 3] = (palette[i] >> 16) & 0xFF; + _palette[i * 3 + 1] = (palette[i] >> 8) & 0xFF; + _palette[i * 3 + 2] = palette[i] & 0xFF; + } + } + + _ntscBorder = (_console->GetModel() == NesModel::NTSC); + + PictureSettings pictureSettings = _console->GetSettings()->GetPictureSettings(); + NtscFilterSettings ntscSettings = _console->GetSettings()->GetNtscFilterSettings(); + + _keepVerticalRes = ntscSettings.KeepVerticalResolution; + if (ntscSettings.UseExternalPalette != _useExternalPalette) { + paletteChanged = true; + _useExternalPalette = ntscSettings.UseExternalPalette; + } + + if( + paletteChanged || + _ntscSetup.hue != pictureSettings.Hue || + _ntscSetup.saturation != pictureSettings.Saturation || + _ntscSetup.brightness != pictureSettings.Brightness || + _ntscSetup.contrast != pictureSettings.Contrast || + _ntscSetup.artifacts != ntscSettings.Artifacts || + _ntscSetup.bleed != ntscSettings.Bleed || + _ntscSetup.fringing != ntscSettings.Fringing || + _ntscSetup.gamma != ntscSettings.Gamma || + (_ntscSetup.merge_fields == 1) != ntscSettings.MergeFields || + _ntscSetup.resolution != ntscSettings.Resolution || + _ntscSetup.sharpness != ntscSettings.Sharpness + ) { + _ntscSetup.hue = pictureSettings.Hue; + _ntscSetup.saturation = pictureSettings.Saturation; + _ntscSetup.brightness = pictureSettings.Brightness; + _ntscSetup.contrast = pictureSettings.Contrast; + + _ntscSetup.artifacts = ntscSettings.Artifacts; + _ntscSetup.bleed = ntscSettings.Bleed; + _ntscSetup.fringing = ntscSettings.Fringing; + _ntscSetup.gamma = ntscSettings.Gamma; + _ntscSetup.merge_fields = (int)ntscSettings.MergeFields; + _ntscSetup.resolution = ntscSettings.Resolution; + _ntscSetup.sharpness = ntscSettings.Sharpness; + + float decodermatrix[6] = { + (float)ntscSettings.DecodeMatrixIR, + (float)ntscSettings.DecodeMatrixQR, + (float)ntscSettings.DecodeMatrixIG, + (float)ntscSettings.DecodeMatrixQG, + (float)ntscSettings.DecodeMatrixIB, + (float)ntscSettings.DecodeMatrixQB + }; + + _ntscSetup.decoder_matrix = decodermatrix; + + if (_useExternalPalette) { + if (_console->GetSettings()->IsFullColorPalette()) { + _ntscSetup.base_palette = nullptr; + _ntscSetup.palette = _palette; + } + else { + _ntscSetup.base_palette = _palette; + _ntscSetup.palette = nullptr; + } + } + else { + _ntscSetup.base_palette = nullptr; + _ntscSetup.palette = nullptr; + } + + nes_ntsc_init(&_ntscData, &_ntscSetup); + } +} + +void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) +{ + uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; + for (int i = 0; i < 240; i++) { + nes_ntsc_blit(&_ntscData, + // input += in_row_width; + ppuOutputBuffer + PPU::ScreenWidth * i, + _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, + PPU::ScreenWidth, + phase, + PPU::ScreenWidth, + 1, + // rgb_out = (char*) rgb_out + out_pitch; + reinterpret_cast(_ntscBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); + + phase = (phase + 1) % 3; + } + GenerateArgbFrame(_ntscBuffer); +} + +void LMP88959NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer) +{ + uint32_t* outputBuffer = GetOutputBuffer(); + OverscanDimensions overscan = GetOverscan(); + int overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0; + int overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0; + int rowWidth = NES_NTSC_OUT_WIDTH(PPU::ScreenWidth); + int rowWidthOverscan = rowWidth - overscanLeft - overscanRight; + + if(_keepVerticalRes) { + ntscBuffer += rowWidth * overscan.Top + overscanLeft; + for(uint32_t i = 0, len = overscan.GetScreenHeight(); i < len; i++) { + memcpy(outputBuffer, ntscBuffer, rowWidthOverscan * sizeof(uint32_t)); + outputBuffer += rowWidthOverscan; + ntscBuffer += rowWidth; + } + } else { + double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity; + bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend; + + for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) { + uint32_t const* in = ntscBuffer + y * rowWidth; + uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan; + + if(verticalBlend || scanlineIntensity < 1.0) { + for(int x = 0; x < rowWidthOverscan; x++) { + uint32_t prev = in[overscanLeft]; + uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0; + + *out = 0xFF000000 | prev; + + /* mix 24-bit rgb without losing low bits */ + uint32_t mixed; + if(verticalBlend) { + mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1; + } else { + mixed = prev; + } + + if(scanlineIntensity < 1.0) { + uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF; + r = (uint8_t)(r * scanlineIntensity); + g = (uint8_t)(g * scanlineIntensity); + b = (uint8_t)(b * scanlineIntensity); + *(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b; + } else { + *(out + rowWidthOverscan) = 0xFF000000 | mixed; + } + in++; + out++; + } + } else { + for(int i = 0; i < rowWidthOverscan; i++) { + out[i] = 0xFF000000 | in[overscanLeft+i]; + } + memcpy(out + rowWidthOverscan, out, rowWidthOverscan * sizeof(uint32_t)); + } + } + } +} + +LMP88959NtscFilter::~LMP88959NtscFilter() +{ + delete[] _ntscBuffer; +} \ No newline at end of file diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h new file mode 100644 index 000000000..94e5e280a --- /dev/null +++ b/Core/LMP88959NtscFilter.h @@ -0,0 +1,30 @@ +#pragma once +#include "stdafx.h" +#include "BaseVideoFilter.h" +#include "../Utilities/crt.h" + +class Console; + +class LMP88959NtscFilter : public BaseVideoFilter +{ +private: + nes_ntsc_setup_t _ntscSetup; + nes_ntsc_t _ntscData; + bool _keepVerticalRes = false; + bool _useExternalPalette = true; + uint8_t _palette[512 * 3]; + uint32_t* _ntscBuffer; + bool _ntscBorder = true; + + void GenerateArgbFrame(uint32_t *outputBuffer); + +protected: + void OnBeforeApplyFilter(); + +public: + LMP88959NtscFilter(shared_ptr console); + virtual ~LMP88959NtscFilter(); + + virtual void ApplyFilter(uint16_t *ppuOutputBuffer); + virtual FrameInfo GetFrameInfo(); +}; \ No newline at end of file diff --git a/Utilities/Utilities.vcxproj b/Utilities/Utilities.vcxproj index 6ffe1968e..810d65815 100644 --- a/Utilities/Utilities.vcxproj +++ b/Utilities/Utilities.vcxproj @@ -427,6 +427,7 @@ + @@ -478,6 +479,7 @@ + diff --git a/Utilities/Utilities.vcxproj.filters b/Utilities/Utilities.vcxproj.filters index 4d7726656..d279373b6 100644 --- a/Utilities/Utilities.vcxproj.filters +++ b/Utilities/Utilities.vcxproj.filters @@ -175,6 +175,9 @@ Misc + + Misc + @@ -303,5 +306,6 @@ Avi + \ No newline at end of file diff --git a/Utilities/crt.c b/Utilities/crt.c new file mode 100644 index 000000000..83a263c77 --- /dev/null +++ b/Utilities/crt.c @@ -0,0 +1,906 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#include "crt.h" + +#include +#include + +/* + * FULL HORIZONTAL LINE SIGNAL (~63500 ns) + * |---------------------------------------------------------------------------| + * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) + * |-------------------||------------------------------------------------------| + * + * + * WITHIN HBLANK PERIOD: + * + * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) + * |--------------||---------------||------------||-------------||-------------| + * BLANK SYNC BLANK BLANK BLANK + * + */ +#define LINE_BEG 0 +#define FP_ns 1500 /* front porch */ +#define SYNC_ns 4700 /* sync tip */ +#define BW_ns 600 /* breezeway */ +#define CB_ns 2500 /* color burst */ +#define BP_ns 1600 /* back porch */ +#define AV_ns 52600 /* active video */ +#define HB_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns) /* h blank */ +/* line duration should be ~63500 ns */ +#define LINE_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns + AV_ns) + +/* convert nanosecond offset to its corresponding point on the sampled line */ +#define ns2pos(ns) ((ns) * CRT_HRES / LINE_ns) +/* starting points for all the different pulses */ +#define FP_BEG ns2pos(0) +#define SYNC_BEG ns2pos(FP_ns) +#define BW_BEG ns2pos(FP_ns + SYNC_ns) +#define CB_BEG ns2pos(FP_ns + SYNC_ns + BW_ns) +#define BP_BEG ns2pos(FP_ns + SYNC_ns + BW_ns + CB_ns) +#define AV_BEG ns2pos(HB_ns) +#define AV_LEN ns2pos(AV_ns) + +/* somewhere between 7 and 12 cycles */ +#define CB_CYCLES 10 + +/* frequencies for bandlimiting */ +#define L_FREQ 1431818 /* full line */ +#define Y_FREQ 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ +#define I_FREQ 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ +#define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ + +/* IRE units (100 = 1.0V, -40 = 0.0V) */ +#define WHITE_LEVEL 100 +#define BURST_LEVEL 20 +#define BLACK_LEVEL 7 +#define BLANK_LEVEL 0 +#define SYNC_LEVEL -40 + +#if (CRT_CHROMA_PATTERN == 1) +/* 227.5 subcarrier cycles per line means every other line has reversed phase */ +#define CC_PHASE(ln) (((ln) & 1) ? -1 : 1) +#else +#define CC_PHASE(ln) (1) +#endif + +/* ensure negative values for x get properly modulo'd */ +#define POSMOD(x, n) (((x) % (n) + (n)) % (n)) + +/*****************************************************************************/ +/***************************** FIXED POINT MATH ******************************/ +/*****************************************************************************/ + +#define T14_2PI 16384 +#define T14_MASK (T14_2PI - 1) +#define T14_PI (T14_2PI / 2) + +static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ + 0x0000, + 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, + 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, + 0x7f60 +}; + +static int +sintabil8(int n) +{ + int f, i, a, b; + + /* looks scary but if you don't change T14_2PI + * it won't cause out of bounds memory reads + */ + f = n >> 0 & 0xff; + i = n >> 8 & 0xff; + a = sigpsin15[i]; + b = sigpsin15[i + 1]; + return (a + ((b - a) * f >> 8)); +} + +/* 14-bit interpolated sine/cosine */ +extern void +crt_sincos14(int *s, int *c, int n) +{ + int h; + + n &= T14_MASK; + h = n & ((T14_2PI >> 1) - 1); + + if (h > ((T14_2PI >> 2) - 1)) { + *c = -sintabil8(h - (T14_2PI >> 2)); + *s = sintabil8((T14_2PI >> 1) - h); + } else { + *c = sintabil8((T14_2PI >> 2) - h); + *s = sintabil8(h); + } + if (n > ((T14_2PI >> 1) - 1)) { + *c = -*c; + *s = -*s; + } +} + +#define EXP_P 11 +#define EXP_ONE (1 << EXP_P) +#define EXP_MASK (EXP_ONE - 1) +#define EXP_PI 6434 +#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P) +#define EXP_DIV(x, y) (((x) << EXP_P) / (y)) + +static int e11[] = { + EXP_ONE, + 5567, /* e */ + 15133, /* e^2 */ + 41135, /* e^3 */ + 111817 /* e^4 */ +}; + +/* fixed point e^x */ +static int +expx(int n) +{ + int neg, idx, res; + int nxt, acc, del; + int i; + + if (n == 0) { + return EXP_ONE; + } + neg = n < 0; + if (neg) { + n = -n; + } + idx = n >> EXP_P; + res = EXP_ONE; + for (i = 0; i < idx / 4; i++) { + res = EXP_MUL(res, e11[4]); + } + idx &= 3; + if (idx > 0) { + res = EXP_MUL(res, e11[idx]); + } + + n &= EXP_MASK; + nxt = EXP_ONE; + acc = 0; + del = 1; + for (i = 1; i < 17; i++) { + acc += nxt / del; + nxt = EXP_MUL(nxt, n); + del *= i; + if (del > nxt || nxt <= 0 || del <= 0) { + break; + } + } + res = EXP_MUL(res, acc); + + if (neg) { + res = EXP_DIV(EXP_ONE, res); + } + return res; +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +#define HISTLEN 3 +#define HISTOLD (HISTLEN - 1) /* oldest entry */ +#define HISTNEW 0 /* newest entry */ + +#define EQ_P 16 /* if changed, the gains will need to be adjusted */ +#define EQ_R (1 << (EQ_P - 1)) /* rounding */ +/* three band equalizer */ +static struct EQF { + int lf, hf; /* fractions */ + int g[3]; /* gains */ + int fL[4]; + int fH[4]; + int h[HISTLEN]; /* history */ +} eqY, eqI, eqQ; + +/* f_lo - low cutoff frequency + * f_hi - high cutoff frequency + * rate - sampling rate + * g_lo, g_mid, g_hi - gains + */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + int sn, cs; + + memset(f, 0, sizeof(struct EQF)); + + f->g[0] = g_lo; + f->g[1] = g_mid; + f->g[2] = g_hi; + + crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); + if (EQ_P >= 15) { + f->lf = 2 * (sn << (EQ_P - 15)); + } else { + f->lf = 2 * (sn >> (15 - EQ_P)); + } + crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); + if (EQ_P >= 15) { + f->hf = 2 * (sn << (EQ_P - 15)); + } else { + f->hf = 2 * (sn >> (15 - EQ_P)); + } +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->fL, 0, sizeof(f->fL)); + memset(f->fH, 0, sizeof(f->fH)); + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i, r[3]; + + f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; + f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; + + for (i = 1; i < 4; i++) { + f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; + f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; + } + + r[0] = f->fL[3]; + r[1] = f->fH[3] - f->fL[3]; + r[2] = f->h[HISTOLD] - f->fH[3]; + + for (i = 0; i < 3; i++) { + r[i] = (r[i] * f->g[i]) >> EQ_P; + } + + for (i = HISTOLD; i > 0; i--) { + f->h[i] = f->h[i - 1]; + } + f->h[HISTNEW] = s; + + return (r[0] + r[1] + r[2]); +} + +/* infinite impulse response low pass filter for bandlimiting YIQ */ +static struct IIRLP { + int c; + int h; /* history */ +} iirY, iirI, iirQ; + +/* freq - total bandwidth + * limit - max frequency + */ +static void +init_iir(struct IIRLP *f, int freq, int limit) +{ + int rate; /* cycles/pixel rate */ + + memset(f, 0, sizeof(struct IIRLP)); + rate = (freq << 9) / limit; + f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); +} + +static void +reset_iir(struct IIRLP *f) +{ + f->h = 0; +} + +/* hi-pass for debugging */ +#define HIPASS 0 + +static int +iirf(struct IIRLP *f, int s) +{ + f->h += EXP_MUL(s - f->h, f->c); +#if HIPASS + return s - f->h; +#else + return f->h; +#endif +} + +/*****************************************************************************/ +/***************************** PUBLIC FUNCTIONS ******************************/ +/*****************************************************************************/ + +extern void +crt_resize(struct CRT *v, int w, int h, int *out) +{ + v->outw = w; + v->outh = h; + v->out = out; +} + +extern void +crt_reset(struct CRT *v) +{ + v->saturation = 18; + v->brightness = 0; + v->contrast = 179; + v->black_point = 0; + v->white_point = 100; + v->hsync = 0; + v->vsync = 0; +} + +extern void +crt_init(struct CRT *v, int w, int h, int *out) +{ + memset(v, 0, sizeof(struct CRT)); + crt_resize(v, w, h, out); + crt_reset(v); + + /* kilohertz to line sample conversion */ +#define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) + + /* band gains are pre-scaled as 16-bit fixed point + * if you change the EQ_P define, you'll need to update these gains too + */ + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); + + init_iir(&iirY, L_FREQ, Y_FREQ); + init_iir(&iirI, L_FREQ, I_FREQ); + init_iir(&iirQ, L_FREQ, Q_FREQ); +} + +extern void +crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = ((CRT_LINES * 64500) >> 16); + int n; +#if CRT_DO_BLOOM + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > ((AV_LEN * 55500) >> 16)) { + destw = ((AV_LEN * 55500) >> 16); + } + if (desth > ((CRT_LINES * 63500) >> 16)) { + desth = ((CRT_LINES * 63500) >> 16); + } + } else { + destw = (AV_LEN * 55500) >> 16; + desth = (CRT_LINES * 63500) >> 16; + } +#else + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > AV_LEN) { + destw = AV_LEN; + } + if (desth > ((CRT_LINES * 64500) >> 16)) { + desth = ((CRT_LINES * 64500) >> 16); + } + } +#endif + + xo = AV_BEG + 4 + (AV_LEN - destw) / 2; + yo = CRT_TOP + 4 + (CRT_LINES - desth) / 2; + + s->field &= 1; + + /* align signal */ + xo = (xo & ~3); + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + if (n <= 3 || (n >= 7 && n <= 9)) { + /* equalizing pulses - small blips of sync, mostly blank */ + while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else if (n >= 4 && n <= 6) { + int even[4] = { 46, 50, 96, 100 }; + int odd[4] = { 4, 50, 96, 100 }; + int *offs = even; + if (s->field == 1) { + offs = odd; + } + /* vertical sync pulse - small blips of blank, mostly sync */ + while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else { + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + if (n < CRT_TOP) { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + if (s->as_color) { + int cb; + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + cb = s->cc[(t + 0) & 3]; + line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; + } + } + } + } + + for (y = 0; y < desth; y++) { + int field_offset; + int syA, syB; + + field_offset = (s->field * s->h + desth) / desth / 2; + syA = (y * s->h) / desth; + syB = (y * s->h + desth / 2) / desth; + + syA += field_offset; + syB += field_offset; + + if (syA >= s->h) syA = s->h; + if (syB >= s->h) syB = s->h; + + syA *= s->w; + syB *= s->w; + + reset_iir(&iirY); + reset_iir(&iirI); + reset_iir(&iirQ); + + for (x = 0; x < destw; x++) { + int fy, fi, fq; + int pA, pB; + int rA, gA, bA; + int rB, gB, bB; + int sx, ph; + int ire; /* composite signal */ + + sx = (x * s->w) / destw; + pA = s->rgb[sx + syA]; + pB = s->rgb[sx + syB]; + rA = (pA >> 16) & 0xff; + gA = (pA >> 8) & 0xff; + bA = (pA >> 0) & 0xff; + rB = (pB >> 16) & 0xff; + gB = (pB >> 8) & 0xff; + bB = (pB >> 0) & 0xff; + + /* RGB to YIQ blend with potential pixel below */ + fy = (19595 * rA + 38470 * gA + 7471 * bA + + 19595 * rB + 38470 * gB + 7471 * bB) >> 15; + fi = (39059 * rA - 18022 * gA - 21103 * bA + + 39059 * rB - 18022 * gB - 21103 * bB) >> 15; + fq = (13894 * rA - 34275 * gA + 20382 * bA + + 13894 * rB - 34275 * gB + 20382 * bB) >> 15; + ph = CC_PHASE(y + yo); + ire = BLACK_LEVEL + v->black_point; + /* bandlimit Y,I,Q */ + fy = iirf(&iirY, fy); + fi = iirf(&iirI, fi) * ph * s->cc[(x + 0) & 3] / s->ccs; + fq = iirf(&iirQ, fq) * ph * s->cc[(x + 3) & 3] / s->ccs; + ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; + if (ire < 0) ire = 0; + if (ire > 110) ire = 110; + + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + } + } +} + +/* generate the square wave for a given 9-bit pixel and phase */ +static int +square_sample(int p, int phase) +{ + static int active[6] = { + 0300, 0100, + 0500, 0400, + 0600, 0200 + }; + int bri, hue, v; + + hue = (p & 0x0f); + + /* last two columns are black */ + if (hue >= 0x0e) { + return 0; + } + + bri = ((p & 0x30) >> 4) * 300; + + switch (hue) { + case 0: + v = bri + 410; + break; + case 0x0d: + v = bri - 300; + break; + default: + v = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); + break; + } + + if (v > 1024) { + v = 1024; + } + /* red 0100, green 0200, blue 0400 */ + if ((p & 0700) & active[(phase >> 1) % 6]) { + return (v >> 1) + (v >> 2); + } + + return v; +} + +extern void +crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = CRT_LINES; + int n, phase; + int po, lo; + +#if CRT_DO_BLOOM + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > ((AV_LEN * 55500) >> 16)) { + destw = ((AV_LEN * 55500) >> 16); + } + if (desth > ((CRT_LINES * 63500) >> 16)) { + desth = ((CRT_LINES * 63500) >> 16); + } + } else { + destw = (AV_LEN * 55500) >> 16; + desth = (CRT_LINES * 63500) >> 16; + } +#else + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > AV_LEN) { + destw = AV_LEN; + } + if (desth > ((CRT_LINES * 64500) >> 16)) { + desth = ((CRT_LINES * 64500) >> 16); + } + } +#endif + + xo = AV_BEG + 4 + (AV_LEN - destw) / 2; + yo = CRT_TOP + 4 + (CRT_LINES - desth) / 2; + + /* align signal */ + xo = (xo & ~3); +#if CRT_NES_HIRES + switch (s->dot_crawl_offset % 3) { + case 0: + lo = 1; + po = 3; + break; + case 1: + lo = 3; + po = 1; + break; + case 2: + lo = 2; + po = 0; + break; + } +#else + lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ + po = lo; /* phase offset for color burst */ + if (lo == 1) { + lo = 3; + } +#endif + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + if (n <= 3 || (n >= 7 && n <= 9)) { + /* equalizing pulses - small blips of sync, mostly blank */ + while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else if (n >= 4 && n <= 6) { + int even[4] = { 46, 50, 96, 100 }; + int *offs = even; /* always progressive */ + /* vertical sync pulse - small blips of blank, mostly sync */ + while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else { + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + if (n < CRT_TOP) { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + if (s->as_color) { + int cb; + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + cb = s->cc[(t + po) & 3]; + line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; + } + } + } + } + + phase = 0; + + for (y = lo; y < desth; y++) { + int sy = (y * s->h) / desth; + if (sy >= s->h) sy = s->h; + + sy *= s->w; + phase += (xo * 3); + for (x = 0; x < destw; x++) { + int ire, p; + + p = s->data[((x * s->w) / destw) + sy]; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + if (ire < 0) ire = 0; + if (ire > 110) ire = 110; + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + phase += 3; + } + /* mod here so we don't overflow down the line */ + phase = (phase + ((CRT_HRES - destw) * 3)) % 12; + } +} + +/* search windows, in samples */ +#define HSYNC_WINDOW 8 +#define VSYNC_WINDOW 8 + +extern void +crt_draw(struct CRT *v, int noise) +{ + struct { + int y, i, q; + } out[AV_LEN + 1], *yiqA, *yiqB; + int i, j, line; +#if CRT_DO_BLOOM + int prev_e; /* filtered beam energy per scan line */ + int max_e; /* approx maximum energy in a scan line */ +#endif + int bright = v->brightness - (BLACK_LEVEL + v->black_point); + signed char *sig; + int s = 0; + int field, ratio; + int ccref[4]; /* color carrier signal */ + int huesn, huecs; + + crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 90) * 8192 / 180); + huesn >>= 11; /* make 4-bit */ + huecs >>= 11; + + memset(ccref, 0, sizeof(ccref)); + + for (i = 0; i < CRT_INPUT_SIZE; i++) { + static int rn = 194; /* 'random' noise */ + + rn = (214019 * rn + 140327895); + + /* signal + noise */ + s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * noise) >> 8); + if (s > 127) { s = 127; } + if (s < -127) { s = -127; } + v->inp[i] = s; + } + + /* Look for vertical sync. + * + * This is done by integrating the signal and + * seeing if it exceeds a threshold. The threshold of + * the vertical sync pulse is much higher because the + * vsync pulse is a lot longer than the hsync pulse. + * The signal needs to be integrated to lessen + * the noise in the signal. + */ + for (i = -VSYNC_WINDOW; i < VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->inp + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + /* increase the multiplier to make the vsync + * more stable when there is a lot of noise + */ +#if CRT_NES_HIRES + if (s <= (150 * SYNC_LEVEL)) { + goto vsync_found; + } +#else + if (s <= (100 * SYNC_LEVEL)) { + goto vsync_found; + } +#endif + } + } +vsync_found: +#if CRT_DO_VSYNC + v->vsync = line; /* vsync found (or gave up) at this line */ +#else + v->vsync = 0; +#endif + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); +#if CRT_DO_BLOOM + max_e = (128 + (noise / 2)) * AV_LEN; + prev_e = (16384 / 8); +#endif + /* ratio of output height to active video lines in the signal */ + ratio = (v->outh << 16) / CRT_LINES; + ratio = (ratio + 32768) >> 16; + + field = (field * (ratio / 2)); + + for (line = CRT_TOP; line < CRT_BOT; line++) { + unsigned pos, ln; + int scanL, scanR, dx; + int L, R; +#if CRT_DO_BLOOM + int line_w; +#endif + int *cL, *cR; + int wave[4]; + int dci, dcq; /* decoded I, Q */ + int xpos, ypos; + int beg, end; + int phasealign; + + beg = (line - CRT_TOP + 0) * v->outh / CRT_LINES + field; + end = (line - CRT_TOP + 1) * v->outh / CRT_LINES + field; + + if (beg >= v->outh) { continue; } + if (end > v->outh) { end = v->outh; } + + /* Look for horizontal sync. + * See comment above regarding vertical sync. + */ + ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; + sig = v->inp + ln + v->hsync; + s = 0; + for (i = -HSYNC_WINDOW; i < HSYNC_WINDOW; i++) { + s += sig[SYNC_BEG + i]; + if (s <= (4 * SYNC_LEVEL)) { + break; + } + } +#if CRT_DO_HSYNC + v->hsync = POSMOD(i + v->hsync, CRT_HRES); +#else + v->hsync = 0; +#endif + + sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ + for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { + int p = ccref[i & 3] * 127 / 128; /* fraction of the previous */ + int n = sig[i]; /* mixed with the new sample */ + ccref[i & 3] = p + n; + } + + xpos = POSMOD(AV_BEG + v->hsync, CRT_HRES); + ypos = POSMOD(line + v->vsync, CRT_VRES); + pos = xpos + ypos * CRT_HRES; + phasealign = pos & 3; + + /* amplitude of carrier = saturation, phase difference = hue */ + dci = ccref[(phasealign + 1) & 3] - ccref[(phasealign + 3) & 3]; + dcq = ccref[(phasealign + 2) & 3] - ccref[(phasealign + 0) & 3]; + + /* rotate them by the hue adjustment angle */ + wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; + wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; + wave[2] = -wave[0]; + wave[3] = -wave[1]; + + sig = v->inp + pos; +#if CRT_DO_BLOOM + s = 0; + for (i = 0; i < AV_LEN; i++) { + s += sig[i]; /* sum up the scan line */ + } + /* bloom emulation */ + prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e); + line_w = (AV_LEN * 112 / 128) + (prev_e >> 9); + + dx = (line_w << 12) / v->outw; + scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12; + scanR = (AV_LEN - 1) << 12; + + L = (scanL >> 12); + R = (scanR >> 12); +#else + dx = ((AV_LEN - 1) << 12) / v->outw; + scanL = 0; + scanR = (AV_LEN - 1) << 12; + L = 0; + R = AV_LEN; +#endif + reset_eq(&eqY); + reset_eq(&eqI); + reset_eq(&eqQ); + + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; + } + + cL = v->out + beg * v->outw; + cR = cL + v->outw; + + for (pos = scanL; pos < scanR && cL < cR; pos += dx) { + int y, i, q; + int r, g, b; + int aa, bb; + + R = pos & 0xfff; + L = 0xfff - R; + s = pos >> 12; + + yiqA = out + s; + yiqB = out + s + 1; + + /* interpolate between samples if needed */ + y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); + i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); + q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); + + /* YIQ to RGB */ + r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; + g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; + b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + aa = (r << 16 | g << 8 | b); + bb = *cL; + /* blend with previous color there */ + *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + } + + /* duplicate extra lines */ + ln = v->outw * sizeof(int); + for (s = beg + 1; s < end; s++) { + memcpy(v->out + s * v->outw, v->out + (s - 1) * v->outw, ln); + } + } +} diff --git a/Utilities/crt.h b/Utilities/crt.h new file mode 100644 index 000000000..4804df8db --- /dev/null +++ b/Utilities/crt.h @@ -0,0 +1,160 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#ifndef _CRT_H_ +#define _CRT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* crt.h + * + * An interface to convert a digital image to an analog NTSC signal + * and decode the NTSC signal back into a digital image. + * Can easily be integrated into real-time applications + * or be used as a command-line tool. + * + */ + +#define CRT_NES_MODE 0 +#define CRT_NES_HIRES 1 + +/* do bloom emulation (side effect: makes screen have black borders) */ +#define CRT_DO_BLOOM 0 +#define CRT_DO_VSYNC 1 /* look for VSYNC */ +#define CRT_DO_HSYNC 1 /* look for HSYNC */ +/* 0 = vertical chroma (228 chroma clocks per line) */ +/* 1 = checkered chroma (227.5 chroma clocks per line) */ +/* 2 = sawtooth chroma (227.3 chroma clocks per line) */ +#define CRT_CHROMA_PATTERN 1 + +#if CRT_NES_MODE +#undef CRT_CHROMA_PATTERN +#define CRT_CHROMA_PATTERN 2 /* force sawtooth pattern */ +#endif + +/* chroma clocks (subcarrier cycles) per line */ +#if (CRT_CHROMA_PATTERN == 1) +#define CRT_CC_LINE 2275 +#elif (CRT_CHROMA_PATTERN == 2) +#define CRT_CC_LINE 2273 +#else +/* this will give the 'rainbow' effect in the famous waterfall scene */ +#define CRT_CC_LINE 2280 +#endif + +/* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ +#if CRT_NES_MODE +#if CRT_NES_HIRES +#define CRT_CB_FREQ 6 /* carrier frequency relative to sample rate */ +#else +#define CRT_CB_FREQ 3 /* carrier frequency relative to sample rate */ +#endif +#else +#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ +#endif +#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ +#define CRT_VRES 262 /* vertical resolution */ +#define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) + +#define CRT_TOP 21 /* first line with active video */ +#define CRT_BOT 261 /* final line with active video */ +#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ + +struct CRT { + signed char analog[CRT_INPUT_SIZE]; + signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ + int hsync, vsync; /* used internally to keep track of sync over frames */ + int hue, brightness, contrast, saturation; /* common monitor settings */ + int black_point, white_point; /* user-adjustable */ + int outw, outh; /* output width/height */ + int *out; /* output image */ +}; + +/* Initializes the library. Sets up filters. + * w - width of the output image + * h - height of the output image + * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + */ +extern void crt_init(struct CRT *v, int w, int h, int *out); + +/* Updates the output image parameters + * w - width of the output image + * h - height of the output image + * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + */ +extern void crt_resize(struct CRT *v, int w, int h, int *out); + +/* Resets the CRT settings back to their defaults */ +extern void crt_reset(struct CRT *v); + +struct NTSC_SETTINGS { + const int *rgb; /* 32-bit RGB image data (packed as 0xXXRRGGBB) */ + int w, h; /* width and height of image */ + int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ + int as_color; /* 0 = monochrome, 1 = full color */ + int field; /* 0 = even, 1 = odd */ + /* color carrier sine wave. + * ex: { 0, 1, 0, -1 } + * ex: { 1, 0, -1, 0 } + */ + int cc[4]; + /* scale value for values in cc + * for example, if using { 0, 1, 0, -1 }, ccs should be 1. + * however, if using { 0, 16, 0, -16 }, ccs should be 16. + * For best results, don't scale the cc values more than 16. + */ + int ccs; +}; + +/* Convert RGB image to analog NTSC signal + * s - struct containing settings to apply to this field + */ +extern void crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s); + +struct NES_NTSC_SETTINGS { + const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ + int w, h; /* width and height of image */ + int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ + int as_color; /* 0 = monochrome, 1 = full color */ + int dot_crawl_offset; /* 0, 1, or 2 */ + /* NOTE: NES mode is always progressive */ + /* color carrier sine wave. + * ex: { 0, 1, 0, -1 } + * ex: { 1, 0, -1, 0 } + */ + int cc[4]; + /* scale value for values in cc + * for example, if using { 0, 1, 0, -1 }, ccs should be 1. + * however, if using { 0, 16, 0, -16 }, ccs should be 16. + * For best results, don't scale the cc values more than 16. + */ + int ccs; +}; + +/* Convert NES pixel data (generally 256x240) to analog NTSC signal + * s - struct containing settings to apply to this field + */ +extern void crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s); + +/* Decodes the NTSC signal generated by crt_2ntsc() + * noise - the amount of noise added to the signal (0 - inf) + */ +extern void crt_draw(struct CRT *v, int noise); + +/* Exposed utility function */ +extern void crt_sincos14(int *s, int *c, int n); + +#ifdef __cplusplus +} +#endif + +#endif From 23fb398816bf037097f8c6b284d38cfd6bbfa942 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 11 Jan 2023 02:30:29 +0800 Subject: [PATCH 02/36] Add modifications for NES NTSC composite thanks NewRisingSun, PinoBatch, and Trirosmos! --- Core/LMP88959NtscFilter.h | 4 +- Utilities/Utilities.vcxproj | 2 +- Utilities/Utilities.vcxproj.filters | 2 +- Utilities/{crt.c => crt.cpp} | 103 +++++++++++++++------------- Utilities/crt.h | 18 +++-- 5 files changed, 73 insertions(+), 56 deletions(-) rename Utilities/{crt.c => crt.cpp} (91%) diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index 94e5e280a..5b48f1130 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -8,8 +8,8 @@ class Console; class LMP88959NtscFilter : public BaseVideoFilter { private: - nes_ntsc_setup_t _ntscSetup; - nes_ntsc_t _ntscData; + struct NTSC_SETTINGS _ntsc; + struct CRT _crt; bool _keepVerticalRes = false; bool _useExternalPalette = true; uint8_t _palette[512 * 3]; diff --git a/Utilities/Utilities.vcxproj b/Utilities/Utilities.vcxproj index 810d65815..e9166ab19 100644 --- a/Utilities/Utilities.vcxproj +++ b/Utilities/Utilities.vcxproj @@ -479,7 +479,7 @@ - + diff --git a/Utilities/Utilities.vcxproj.filters b/Utilities/Utilities.vcxproj.filters index d279373b6..bffc2f544 100644 --- a/Utilities/Utilities.vcxproj.filters +++ b/Utilities/Utilities.vcxproj.filters @@ -306,6 +306,6 @@ Avi - + \ No newline at end of file diff --git a/Utilities/crt.c b/Utilities/crt.cpp similarity index 91% rename from Utilities/crt.c rename to Utilities/crt.cpp index 83a263c77..eb5d25c99 100644 --- a/Utilities/crt.c +++ b/Utilities/crt.cpp @@ -1,53 +1,63 @@ /*****************************************************************************/ /* * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation - * + * * by EMMIR 2018-2023 - * + * modifications for Mesen by Persune + * https://github.com/LMP88959/NTSC-CRT + * * YouTube: https://www.youtube.com/@EMMIR_KC/videos * Discord: https://discord.com/invite/hdYctSmyQJ */ /*****************************************************************************/ +#include "stdafx.h" + #include "crt.h" #include #include -/* - * FULL HORIZONTAL LINE SIGNAL (~63500 ns) +/* NES composite signal is measured in terms of PPU pixels, or cycles + * https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing + * + * FULL HORIZONTAL LINE SIGNAL + * (341 PPU px; one cycle skipped on odd rendered frames) * |---------------------------------------------------------------------------| - * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) + * HBLANK (58 PPU px) ACTIVE VIDEO (283 PPU px) * |-------------------||------------------------------------------------------| - * - * + * + * * WITHIN HBLANK PERIOD: - * - * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) + * + * FP (9 PPU px) SYNC (25 PPU px) BW (4 PPU px) CB (15 PPU px) BP (5 PPU px) * |--------------||---------------||------------||-------------||-------------| * BLANK SYNC BLANK BLANK BLANK - * + * */ #define LINE_BEG 0 -#define FP_ns 1500 /* front porch */ -#define SYNC_ns 4700 /* sync tip */ -#define BW_ns 600 /* breezeway */ -#define CB_ns 2500 /* color burst */ -#define BP_ns 1600 /* back porch */ -#define AV_ns 52600 /* active video */ -#define HB_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns) /* h blank */ +#define FP_PPUpx 9 /* front porch */ +#define SYNC_PPUpx 25 /* sync tip */ +#define BW_PPUpx 4 /* breezeway */ +#define CB_PPUpx 15 /* color burst */ +#define BP_PPUpx 5 /* back porch */ +#define PS_PPUpx 1 /* pulse */ +#define LB_PPUpx 15 /* left border */ +#define AV_PPUpx 256 /* active video */ +#define RB_PPUpx 11 /* right border */ +#define HB_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx) /* h blank */ /* line duration should be ~63500 ns */ -#define LINE_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns + AV_ns) +#define LINE_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx + PS_PPUpx + LB_PPUpx + AV_PPUpx + RB_PPUpx) -/* convert nanosecond offset to its corresponding point on the sampled line */ -#define ns2pos(ns) ((ns) * CRT_HRES / LINE_ns) +/* convert pixel offset to its corresponding point on the sampled line */ +#define PPUpx2pos(PPUpx) ((PPUpx) * CRT_HRES / LINE_PPUpx) /* starting points for all the different pulses */ -#define FP_BEG ns2pos(0) -#define SYNC_BEG ns2pos(FP_ns) -#define BW_BEG ns2pos(FP_ns + SYNC_ns) -#define CB_BEG ns2pos(FP_ns + SYNC_ns + BW_ns) -#define BP_BEG ns2pos(FP_ns + SYNC_ns + BW_ns + CB_ns) -#define AV_BEG ns2pos(HB_ns) -#define AV_LEN ns2pos(AV_ns) +#define FP_BEG PPUpx2pos(0) +#define SYNC_BEG PPUpx2pos(FP_PPUpx) +#define BW_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx) +#define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) +#define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) +#define AV_BEG PPUpx2pos(HB_PPUpx) +#define AV_LEN PPUpx2pos(AV_PPUpx) /* somewhere between 7 and 12 cycles */ #define CB_CYCLES 10 @@ -59,11 +69,12 @@ #define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ /* IRE units (100 = 1.0V, -40 = 0.0V) */ -#define WHITE_LEVEL 100 -#define BURST_LEVEL 20 -#define BLACK_LEVEL 7 +/* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ +#define WHITE_LEVEL 110 +#define BURST_LEVEL 30 +#define BLACK_LEVEL 0 #define BLANK_LEVEL 0 -#define SYNC_LEVEL -40 +#define SYNC_LEVEL -37 #if (CRT_CHROMA_PATTERN == 1) /* 227.5 subcarrier cycles per line means every other line has reversed phase */ @@ -508,45 +519,45 @@ crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s) /* generate the square wave for a given 9-bit pixel and phase */ static int -square_sample(int p, int phase) +square_sample(int pixel_color, int phase) { static int active[6] = { - 0300, 0100, - 0500, 0400, - 0600, 0200 + 0x0C0, 0x040, + 0x140, 0x100, + 0x180, 0x080 }; - int bri, hue, v; + int bri, hue, voltage; - hue = (p & 0x0f); + hue = (pixel_color & 0x0f); /* last two columns are black */ if (hue >= 0x0e) { return 0; } - bri = ((p & 0x30) >> 4) * 300; + bri = ((pixel_color & 0x30) >> 4) * 300; switch (hue) { case 0: - v = bri + 410; + voltage = bri + 410; break; case 0x0d: - v = bri - 300; + voltage = bri - 300; break; default: - v = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); + voltage = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); break; } - if (v > 1024) { - v = 1024; + if (voltage > 1024) { + voltage = 1024; } /* red 0100, green 0200, blue 0400 */ - if ((p & 0700) & active[(phase >> 1) % 6]) { - return (v >> 1) + (v >> 2); + if ((pixel_color & 0x1C0) & active[(phase >> 1) % 6]) { + return (voltage >> 1) + (voltage >> 2); } - return v; + return voltage; } extern void diff --git a/Utilities/crt.h b/Utilities/crt.h index 4804df8db..ffca538c1 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -3,11 +3,15 @@ * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation * * by EMMIR 2018-2023 + * modifications for Mesen by Persune + * https://github.com/LMP88959/NTSC-CRT * * YouTube: https://www.youtube.com/@EMMIR_KC/videos * Discord: https://discord.com/invite/hdYctSmyQJ */ /*****************************************************************************/ +#pragma once + #ifndef _CRT_H_ #define _CRT_H_ @@ -24,7 +28,7 @@ extern "C" { * */ -#define CRT_NES_MODE 0 +#define CRT_NES_MODE 1 #define CRT_NES_HIRES 1 /* do bloom emulation (side effect: makes screen have black borders) */ @@ -34,7 +38,7 @@ extern "C" { /* 0 = vertical chroma (228 chroma clocks per line) */ /* 1 = checkered chroma (227.5 chroma clocks per line) */ /* 2 = sawtooth chroma (227.3 chroma clocks per line) */ -#define CRT_CHROMA_PATTERN 1 +#define CRT_CHROMA_PATTERN 2 #if CRT_NES_MODE #undef CRT_CHROMA_PATTERN @@ -45,7 +49,7 @@ extern "C" { #if (CRT_CHROMA_PATTERN == 1) #define CRT_CC_LINE 2275 #elif (CRT_CHROMA_PATTERN == 2) -#define CRT_CC_LINE 2273 +#define CRT_CC_LINE 682.0/3.0 #else /* this will give the 'rainbow' effect in the famous waterfall scene */ #define CRT_CC_LINE 2280 @@ -61,12 +65,14 @@ extern "C" { #else #define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ #endif -#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ + +/* https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing */ +#define CRT_HRES int(CRT_CC_LINE * double(CRT_CB_FREQ) / 10.0) /* horizontal res */ #define CRT_VRES 262 /* vertical resolution */ #define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) -#define CRT_TOP 21 /* first line with active video */ -#define CRT_BOT 261 /* final line with active video */ +#define CRT_TOP 15 /* first line with active video */ +#define CRT_BOT 255 /* final line with active video */ #define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ struct CRT { From 0c2a651f723e6e158323abf01ed31fb2bf71027c Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 11 Jan 2023 15:00:27 +0800 Subject: [PATCH 03/36] Update NTSC-CRT library --- Utilities/crt.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++++++ Utilities/crt.h | 9 ++++ 2 files changed, 117 insertions(+) diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index eb5d25c99..19fbf9b83 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -340,6 +340,7 @@ crt_resize(struct CRT *v, int w, int h, int *out) extern void crt_reset(struct CRT *v) { + v->hue = 0; v->saturation = 18; v->brightness = 0; v->contrast = 179; @@ -517,6 +518,113 @@ crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s) } } +extern void +crt_2ntscFS(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = CRT_LINES; + int n; + + xo = AV_BEG; + yo = CRT_TOP; + + s->field &= 1; + + /* align signal */ + xo = (xo & ~3); + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + if (n <= 3 || (n >= 7 && n <= 9)) { + /* equalizing pulses - small blips of sync, mostly blank */ + while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else if (n >= 4 && n <= 6) { + int even[4] = { 46, 50, 96, 100 }; + int odd[4] = { 4, 50, 96, 100 }; + int *offs = even; + if (s->field == 1) { + offs = odd; + } + /* vertical sync pulse - small blips of blank, mostly sync */ + while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else { + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + if (n < CRT_TOP) { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + if (s->as_color) { + int cb; + + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + cb = s->cc[(t + 0) & 3]; + line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; + } + } + } + } + + for (y = 0; y < desth; y++) { + int field_offset; + int sy; + + field_offset = (s->field * s->h + desth) / desth / 2; + sy = (y * s->h) / desth; + + sy += field_offset; + + if (sy >= s->h) sy = s->h; + + sy *= s->w; + + reset_iir(&iirY); + reset_iir(&iirI); + reset_iir(&iirQ); + + for (x = 0; x < destw; x++) { + int fy, fi, fq; + int pA, rA, gA, bA; + int sx, ph; + int ire; /* composite signal */ + + sx = (x * s->w) / destw; + pA = s->rgb[sx + sy]; + rA = (pA >> 16) & 0xff; + gA = (pA >> 8) & 0xff; + bA = (pA >> 0) & 0xff; + + fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; + fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; + fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; + ph = CC_PHASE(y + yo); + ire = BLACK_LEVEL + v->black_point; + /* bandlimit Y,I,Q */ + fy = iirf(&iirY, fy); + fi = iirf(&iirI, fi) * ph * s->cc[(x + 0) & 3] / s->ccs; + fq = iirf(&iirQ, fq) * ph * s->cc[(x + 3) & 3] / s->ccs; + ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; + if (ire < 0) ire = 0; + if (ire > 110) ire = 110; + + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + } + } +} + /* generate the square wave for a given 9-bit pixel and phase */ static int square_sample(int pixel_color, int phase) diff --git a/Utilities/crt.h b/Utilities/crt.h index ffca538c1..ac3ee3554 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -125,6 +125,15 @@ struct NTSC_SETTINGS { * s - struct containing settings to apply to this field */ extern void crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s); + +/* Convert RGB image to analog NTSC signal and stretch it to fill + * the entire active video portion of the NTSC signal. + * Does not perform the slight horizontal blending which gets done in crt_2ntsc. + * Good for seeing test patterns. + * s - struct containing settings to apply to this field + * NOTE: raw is ignored in this 'FS' (fill screen) version of the 2ntsc function + */ +extern void crt_2ntscFS(struct CRT *v, struct NTSC_SETTINGS *s); struct NES_NTSC_SETTINGS { const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ From c45d7276d32e3d258b0d83d5417e74328f5f023b Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 11 Jan 2023 17:56:49 +0800 Subject: [PATCH 04/36] Begin to connect filter library --- Core/BisqwitNtscFilter.cpp | 6 +- Core/BisqwitNtscFilter.h | 2 +- Core/EmulationSettings.h | 2 + Core/LMP88959NtscFilter.cpp | 183 ++++++++++++------------------------ Core/LMP88959NtscFilter.h | 8 +- Utilities/crt.cpp | 15 +-- Utilities/crt.h | 4 +- 7 files changed, 81 insertions(+), 139 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index bd2baa037..8b246aa69 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -50,7 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + 327360); _workDone = true; } @@ -70,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } @@ -218,7 +218,7 @@ void BisqwitNtscFilter::GenerateNtscSignal(int8_t *ntscSignal, int &phase, int r phase += (341 - 256 - _paddingSize * 2) * _signalsPerPixel; } -void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase) +void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint32_t* outputBuffer, int startPhase) { int pixelsPerCycle = 8 / _resDivider; int phase = startPhase; diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index e9c1e0e93..f0fb89674 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -46,7 +46,7 @@ class BisqwitNtscFilter : public BaseVideoFilter void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0); void GenerateNtscSignal(int8_t *ntscSignal, int &phase, int rowNumber); - void DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase); + void DecodeFrame(int startRow, int endRow, uint32_t* outputBuffer, int startPhase); void OnBeforeApplyFilter(); public: diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index c5e1448b9..76bede43b 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -244,6 +244,8 @@ struct NtscFilterSettings double DecodeMatrixQG = -0.647f; double DecodeMatrixIB = -1.105f; double DecodeMatrixQB = 1.702f; + + double Noise = 0; }; enum class RamPowerOnState diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 56cc944f6..8369fbb14 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -6,168 +6,105 @@ #include "EmulationSettings.h" #include "Console.h" -LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoFilter(console) +LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console, int resDivider) : BaseVideoFilter(console) { - memset(_palette, 0, sizeof(_palette)); - memset(&_ntscData, 0, sizeof(_ntscData)); - _ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240](); + _resDivider = resDivider; + _frameBuffer = new uint32_t[(256) * 240](); } FrameInfo LMP88959NtscFilter::GetFrameInfo() { OverscanDimensions overscan = GetOverscan(); - uint32_t overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0; - uint32_t overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0; - - if(_keepVerticalRes) { - return { - (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight), - (PPU::ScreenHeight - overscan.Top - overscan.Bottom), - NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), - PPU::ScreenHeight, - 4 - }; - } else { - return { - NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) - overscanLeft - overscanRight, - (PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2, - NES_NTSC_OUT_WIDTH(PPU::ScreenWidth), - PPU::ScreenHeight * 2, - 4 - }; + if (_keepVerticalRes) { + return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 }; + } + else { + return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight() * 8 / _resDivider, PPU::ScreenWidth, PPU::ScreenHeight, 4 }; } } void LMP88959NtscFilter::OnBeforeApplyFilter() { - bool paletteChanged = false; - uint32_t* palette = _console->GetSettings()->GetRgbPalette(); - for(int i = 0, len = _console->GetSettings()->IsFullColorPalette() ? 512 : 64; i < len; i++) { - uint8_t r = (palette[i] >> 16) & 0xFF; - uint8_t g = (palette[i] >> 8) & 0xFF; - uint8_t b = palette[i] & 0xFF; - - if(_palette[i * 3] != r || _palette[i * 3 + 1] != g || _palette[i * 3 + 2] != b) { - paletteChanged = true; - - _palette[i * 3] = (palette[i] >> 16) & 0xFF; - _palette[i * 3 + 1] = (palette[i] >> 8) & 0xFF; - _palette[i * 3 + 2] = palette[i] & 0xFF; - } - } - _ntscBorder = (_console->GetModel() == NesModel::NTSC); PictureSettings pictureSettings = _console->GetSettings()->GetPictureSettings(); NtscFilterSettings ntscSettings = _console->GetSettings()->GetNtscFilterSettings(); _keepVerticalRes = ntscSettings.KeepVerticalResolution; - if (ntscSettings.UseExternalPalette != _useExternalPalette) { - paletteChanged = true; - _useExternalPalette = ntscSettings.UseExternalPalette; - } - if( - paletteChanged || - _ntscSetup.hue != pictureSettings.Hue || - _ntscSetup.saturation != pictureSettings.Saturation || - _ntscSetup.brightness != pictureSettings.Brightness || - _ntscSetup.contrast != pictureSettings.Contrast || - _ntscSetup.artifacts != ntscSettings.Artifacts || - _ntscSetup.bleed != ntscSettings.Bleed || - _ntscSetup.fringing != ntscSettings.Fringing || - _ntscSetup.gamma != ntscSettings.Gamma || - (_ntscSetup.merge_fields == 1) != ntscSettings.MergeFields || - _ntscSetup.resolution != ntscSettings.Resolution || - _ntscSetup.sharpness != ntscSettings.Sharpness - ) { - _ntscSetup.hue = pictureSettings.Hue; - _ntscSetup.saturation = pictureSettings.Saturation; - _ntscSetup.brightness = pictureSettings.Brightness; - _ntscSetup.contrast = pictureSettings.Contrast; - - _ntscSetup.artifacts = ntscSettings.Artifacts; - _ntscSetup.bleed = ntscSettings.Bleed; - _ntscSetup.fringing = ntscSettings.Fringing; - _ntscSetup.gamma = ntscSettings.Gamma; - _ntscSetup.merge_fields = (int)ntscSettings.MergeFields; - _ntscSetup.resolution = ntscSettings.Resolution; - _ntscSetup.sharpness = ntscSettings.Sharpness; - - float decodermatrix[6] = { - (float)ntscSettings.DecodeMatrixIR, - (float)ntscSettings.DecodeMatrixQR, - (float)ntscSettings.DecodeMatrixIG, - (float)ntscSettings.DecodeMatrixQG, - (float)ntscSettings.DecodeMatrixIB, - (float)ntscSettings.DecodeMatrixQB - }; - - _ntscSetup.decoder_matrix = decodermatrix; - - if (_useExternalPalette) { - if (_console->GetSettings()->IsFullColorPalette()) { - _ntscSetup.base_palette = nullptr; - _ntscSetup.palette = _palette; - } - else { - _ntscSetup.base_palette = _palette; - _ntscSetup.palette = nullptr; - } - } - else { - _ntscSetup.base_palette = nullptr; - _ntscSetup.palette = nullptr; - } + crt_init(&_crt, GetOverscan().GetScreenWidth() * 8 / _resDivider, GetOverscan().GetScreenHeight() * 8 / _resDivider, (int*)(_frameBuffer)); + + _crt.hue = static_cast(pictureSettings.Hue * 180.0); + _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 50.0); + _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); + _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); + _crt.noise = static_cast(ntscSettings.Noise * 500.0); + + _nesNTSC.data = nullptr; + _nesNTSC.w = static_cast(PPU::ScreenWidth); + _nesNTSC.h = static_cast(PPU::ScreenHeight); + _nesNTSC.raw = 1; + _nesNTSC.as_color = 1; + _nesNTSC.dot_crawl_offset = 0; + _nesNTSC.starting_phase = 0; + _nesNTSC.cc[0] = -1; + _nesNTSC.cc[1] = 0; + _nesNTSC.cc[2] = 1; + _nesNTSC.cc[3] = 0; + _nesNTSC.ccs = 1; - nes_ntsc_init(&_ntscData, &_ntscSetup); - } } void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; - for (int i = 0; i < 240; i++) { - nes_ntsc_blit(&_ntscData, - // input += in_row_width; - ppuOutputBuffer + PPU::ScreenWidth * i, - _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, - PPU::ScreenWidth, - phase, - PPU::ScreenWidth, - 1, - // rgb_out = (char*) rgb_out + out_pitch; - reinterpret_cast(_ntscBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), - NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); - - phase = (phase + 1) % 3; - } - GenerateArgbFrame(_ntscBuffer); + //for (int i = 0; i < 240; i++) { + // nes_ntsc_blit(&_ntscData, + // // input += in_row_width; + // ppuOutputBuffer + PPU::ScreenWidth * i, + // _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, + // PPU::ScreenWidth, + // phase, + // PPU::ScreenWidth, + // 1, + // // rgb_out = (char*) rgb_out + out_pitch; + // reinterpret_cast(_frameBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), + // NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); + + // phase = (phase + 1) % 3; + //} + _nesNTSC.data = (unsigned short*)(ppuOutputBuffer); + _nesNTSC.dot_crawl_offset = phase; + _nesNTSC.starting_phase = phase; + + crt_nes2ntsc(&_crt, &_nesNTSC); + crt_draw(&_crt); + + GenerateArgbFrame(_frameBuffer); } -void LMP88959NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer) +void LMP88959NtscFilter::GenerateArgbFrame(uint32_t *frameBuffer) { uint32_t* outputBuffer = GetOutputBuffer(); OverscanDimensions overscan = GetOverscan(); - int overscanLeft = overscan.Left > 0 ? NES_NTSC_OUT_WIDTH(overscan.Left) : 0; - int overscanRight = overscan.Right > 0 ? NES_NTSC_OUT_WIDTH(overscan.Right) : 0; - int rowWidth = NES_NTSC_OUT_WIDTH(PPU::ScreenWidth); + int overscanLeft = overscan.Left > 0 ? (overscan.Left) : 0; + int overscanRight = overscan.Right > 0 ? (overscan.Right) : 0; + int rowWidth = (PPU::ScreenWidth); int rowWidthOverscan = rowWidth - overscanLeft - overscanRight; if(_keepVerticalRes) { - ntscBuffer += rowWidth * overscan.Top + overscanLeft; + frameBuffer += rowWidth * overscan.Top + overscanLeft; for(uint32_t i = 0, len = overscan.GetScreenHeight(); i < len; i++) { - memcpy(outputBuffer, ntscBuffer, rowWidthOverscan * sizeof(uint32_t)); + memcpy(outputBuffer, frameBuffer, rowWidthOverscan * sizeof(uint32_t)); outputBuffer += rowWidthOverscan; - ntscBuffer += rowWidth; + frameBuffer += rowWidth; } } else { double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity; bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend; for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) { - uint32_t const* in = ntscBuffer + y * rowWidth; + uint32_t const* in = frameBuffer + y * rowWidth; uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan; if(verticalBlend || scanlineIntensity < 1.0) { @@ -209,5 +146,5 @@ void LMP88959NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer) LMP88959NtscFilter::~LMP88959NtscFilter() { - delete[] _ntscBuffer; + delete[] _frameBuffer; } \ No newline at end of file diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index 5b48f1130..949b202a2 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -8,13 +8,13 @@ class Console; class LMP88959NtscFilter : public BaseVideoFilter { private: - struct NTSC_SETTINGS _ntsc; + struct NES_NTSC_SETTINGS _nesNTSC; struct CRT _crt; bool _keepVerticalRes = false; - bool _useExternalPalette = true; uint8_t _palette[512 * 3]; - uint32_t* _ntscBuffer; + uint32_t* _frameBuffer; bool _ntscBorder = true; + int _resDivider = 1; void GenerateArgbFrame(uint32_t *outputBuffer); @@ -22,7 +22,7 @@ class LMP88959NtscFilter : public BaseVideoFilter void OnBeforeApplyFilter(); public: - LMP88959NtscFilter(shared_ptr console); + LMP88959NtscFilter(shared_ptr console, int resDivider); virtual ~LMP88959NtscFilter(); virtual void ApplyFilter(uint16_t *ppuOutputBuffer); diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 19fbf9b83..a724c7cf2 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -346,6 +346,7 @@ crt_reset(struct CRT *v) v->contrast = 179; v->black_point = 0; v->white_point = 100; + v->noise = 0; v->hsync = 0; v->vsync = 0; } @@ -768,9 +769,9 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } } } - } - - phase = 0; + } + + phase = s->starting_phase * 4; for (y = lo; y < desth; y++) { int sy = (y * s->h) / desth; @@ -803,7 +804,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) #define VSYNC_WINDOW 8 extern void -crt_draw(struct CRT *v, int noise) +crt_draw(struct CRT *v) { struct { int y, i, q; @@ -832,7 +833,7 @@ crt_draw(struct CRT *v, int noise) rn = (214019 * rn + 140327895); /* signal + noise */ - s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * noise) >> 8); + s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * v->noise) >> 8); if (s > 127) { s = 127; } if (s < -127) { s = -127; } v->inp[i] = s; @@ -876,7 +877,7 @@ crt_draw(struct CRT *v, int noise) /* if vsync signal was in second half of line, odd field */ field = (j > (CRT_HRES / 2)); #if CRT_DO_BLOOM - max_e = (128 + (noise / 2)) * AV_LEN; + max_e = (128 + (v->noise / 2)) * AV_LEN; prev_e = (16384 / 8); #endif /* ratio of output height to active video lines in the signal */ @@ -1010,7 +1011,7 @@ crt_draw(struct CRT *v, int noise) if (g > 255) g = 255; if (b > 255) b = 255; - aa = (r << 16 | g << 8 | b); + aa = 0xFF000000 | (r << 16 | g << 8 | b); bb = *cL; /* blend with previous color there */ *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); diff --git a/Utilities/crt.h b/Utilities/crt.h index ac3ee3554..c43de8870 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -81,6 +81,7 @@ struct CRT { int hsync, vsync; /* used internally to keep track of sync over frames */ int hue, brightness, contrast, saturation; /* common monitor settings */ int black_point, white_point; /* user-adjustable */ + int noise; /* noise level */ int outw, outh; /* output width/height */ int *out; /* output image */ }; @@ -141,6 +142,7 @@ struct NES_NTSC_SETTINGS { int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ int as_color; /* 0 = monochrome, 1 = full color */ int dot_crawl_offset; /* 0, 1, or 2 */ + int starting_phase; /* 0, 1, or 2 */ /* NOTE: NES mode is always progressive */ /* color carrier sine wave. * ex: { 0, 1, 0, -1 } @@ -163,7 +165,7 @@ extern void crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s); /* Decodes the NTSC signal generated by crt_2ntsc() * noise - the amount of noise added to the signal (0 - inf) */ -extern void crt_draw(struct CRT *v, int noise); +extern void crt_draw(struct CRT *v); /* Exposed utility function */ extern void crt_sincos14(int *s, int *c, int n); From b6a24195cc18178ea8168a26122346466077121a Mon Sep 17 00:00:00 2001 From: Persune Date: Thu, 12 Jan 2023 20:04:24 +0800 Subject: [PATCH 05/36] Implement emulator bindings --- Core/EmulationSettings.h | 7 +- Core/LMP88959NtscFilter.cpp | 101 +- Core/LMP88959NtscFilter.h | 3 +- Core/PgoUtilities.cpp | 2 +- Core/ScaleFilter.cpp | 3 + Core/VideoDecoder.cpp | 4 + GUI.NET/Config/VideoInfo.cs | 5 +- GUI.NET/Dependencies/resources.ca.xml | 3 + GUI.NET/Dependencies/resources.en.xml | 3 + GUI.NET/Dependencies/resources.es.xml | 3 + GUI.NET/Dependencies/resources.fr.xml | 3 + GUI.NET/Dependencies/resources.it.xml | 3 + GUI.NET/Dependencies/resources.ja.xml | 3 + GUI.NET/Dependencies/resources.pl.xml | 3 + GUI.NET/Dependencies/resources.pt.xml | 3 + GUI.NET/Dependencies/resources.ru.xml | 3 + GUI.NET/Dependencies/resources.uk.xml | 3 + GUI.NET/Dependencies/resources.zh.xml | 3 + .../Forms/Config/frmVideoConfig.Designer.cs | 190 +- GUI.NET/Forms/Config/frmVideoConfig.cs | 85 +- GUI.NET/Forms/Config/frmVideoConfig.resx | 10 +- GUI.NET/Forms/frmMain.Designer.cs | 3285 +++++++++-------- GUI.NET/Forms/frmMain.Options.cs | 15 + GUI.NET/Forms/frmMain.cs | 5 +- GUI.NET/InteropEmu.cs | 5 +- InteropDLL/ConsoleWrapper.cpp | 2 +- Libretro/libretro.cpp | 29 +- Utilities/crt.cpp | 12 +- Utilities/crt.h | 9 +- 29 files changed, 1980 insertions(+), 1825 deletions(-) diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 76bede43b..25839f3de 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -168,6 +168,9 @@ enum class VideoFilterType Prescale8x = 23, Prescale10x = 24, Raw = 25, + LMP88959NtscQuarterRes = 26, + LMP88959NtscHalfRes = 27, + LMP88959Ntsc = 28, HdPack = 999 }; @@ -1254,7 +1257,8 @@ class EmulationSettings bool verticalBlend, bool keepVerticalResolution, bool colorimetryCorrection, - bool useExternalPalette) + bool useExternalPalette, + double Noise) { _ntscFilterSettings.Artifacts = artifacts; _ntscFilterSettings.Bleed = bleed; @@ -1281,6 +1285,7 @@ class EmulationSettings _ntscFilterSettings.VerticalBlend = verticalBlend; _ntscFilterSettings.KeepVerticalResolution = keepVerticalResolution; _ntscFilterSettings.ColorimetryCorrection = colorimetryCorrection; + _ntscFilterSettings.Noise = Noise; } NtscFilterSettings GetNtscFilterSettings() diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 8369fbb14..b22a1860c 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -9,7 +9,9 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console, int resDivider) : BaseVideoFilter(console) { _resDivider = resDivider; - _frameBuffer = new uint32_t[(256) * 240](); + _frameBuffer = new uint32_t[(256 * 8 / _resDivider) * (240 * 8 / _resDivider)](); + memset(_crt.analog, 0, sizeof(_crt.analog)); + memset(_crt.inp, 0, sizeof(_crt.inp)); } FrameInfo LMP88959NtscFilter::GetFrameInfo() @@ -32,25 +34,23 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _keepVerticalRes = ntscSettings.KeepVerticalResolution; - crt_init(&_crt, GetOverscan().GetScreenWidth() * 8 / _resDivider, GetOverscan().GetScreenHeight() * 8 / _resDivider, (int*)(_frameBuffer)); + crt_init(&_crt, (256 * 8 / _resDivider), _keepVerticalRes ? 240 : (240 * 8 / _resDivider), reinterpret_cast(GetOutputBuffer())); _crt.hue = static_cast(pictureSettings.Hue * 180.0); - _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 50.0); + _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _crt.noise = static_cast(ntscSettings.Noise * 500.0); - _nesNTSC.data = nullptr; _nesNTSC.w = static_cast(PPU::ScreenWidth); _nesNTSC.h = static_cast(PPU::ScreenHeight); - _nesNTSC.raw = 1; + _nesNTSC.raw = 0; _nesNTSC.as_color = 1; _nesNTSC.dot_crawl_offset = 0; - _nesNTSC.starting_phase = 0; - _nesNTSC.cc[0] = -1; - _nesNTSC.cc[1] = 0; - _nesNTSC.cc[2] = 1; - _nesNTSC.cc[3] = 0; + _nesNTSC.cc[0] = 0; + _nesNTSC.cc[1] = 1; + _nesNTSC.cc[2] = 0; + _nesNTSC.cc[3] = -1; _nesNTSC.ccs = 1; } @@ -58,90 +58,11 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; - //for (int i = 0; i < 240; i++) { - // nes_ntsc_blit(&_ntscData, - // // input += in_row_width; - // ppuOutputBuffer + PPU::ScreenWidth * i, - // _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, - // PPU::ScreenWidth, - // phase, - // PPU::ScreenWidth, - // 1, - // // rgb_out = (char*) rgb_out + out_pitch; - // reinterpret_cast(_frameBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), - // NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); - // phase = (phase + 1) % 3; - //} - _nesNTSC.data = (unsigned short*)(ppuOutputBuffer); + _nesNTSC.data = reinterpret_cast(ppuOutputBuffer); _nesNTSC.dot_crawl_offset = phase; - _nesNTSC.starting_phase = phase; - crt_nes2ntsc(&_crt, &_nesNTSC); crt_draw(&_crt); - - GenerateArgbFrame(_frameBuffer); -} - -void LMP88959NtscFilter::GenerateArgbFrame(uint32_t *frameBuffer) -{ - uint32_t* outputBuffer = GetOutputBuffer(); - OverscanDimensions overscan = GetOverscan(); - int overscanLeft = overscan.Left > 0 ? (overscan.Left) : 0; - int overscanRight = overscan.Right > 0 ? (overscan.Right) : 0; - int rowWidth = (PPU::ScreenWidth); - int rowWidthOverscan = rowWidth - overscanLeft - overscanRight; - - if(_keepVerticalRes) { - frameBuffer += rowWidth * overscan.Top + overscanLeft; - for(uint32_t i = 0, len = overscan.GetScreenHeight(); i < len; i++) { - memcpy(outputBuffer, frameBuffer, rowWidthOverscan * sizeof(uint32_t)); - outputBuffer += rowWidthOverscan; - frameBuffer += rowWidth; - } - } else { - double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity; - bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend; - - for(int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) { - uint32_t const* in = frameBuffer + y * rowWidth; - uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan; - - if(verticalBlend || scanlineIntensity < 1.0) { - for(int x = 0; x < rowWidthOverscan; x++) { - uint32_t prev = in[overscanLeft]; - uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0; - - *out = 0xFF000000 | prev; - - /* mix 24-bit rgb without losing low bits */ - uint32_t mixed; - if(verticalBlend) { - mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1; - } else { - mixed = prev; - } - - if(scanlineIntensity < 1.0) { - uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF; - r = (uint8_t)(r * scanlineIntensity); - g = (uint8_t)(g * scanlineIntensity); - b = (uint8_t)(b * scanlineIntensity); - *(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b; - } else { - *(out + rowWidthOverscan) = 0xFF000000 | mixed; - } - in++; - out++; - } - } else { - for(int i = 0; i < rowWidthOverscan; i++) { - out[i] = 0xFF000000 | in[overscanLeft+i]; - } - memcpy(out + rowWidthOverscan, out, rowWidthOverscan * sizeof(uint32_t)); - } - } - } } LMP88959NtscFilter::~LMP88959NtscFilter() diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index 949b202a2..d27ec8547 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -11,12 +11,11 @@ class LMP88959NtscFilter : public BaseVideoFilter struct NES_NTSC_SETTINGS _nesNTSC; struct CRT _crt; bool _keepVerticalRes = false; - uint8_t _palette[512 * 3]; uint32_t* _frameBuffer; bool _ntscBorder = true; int _resDivider = 1; - void GenerateArgbFrame(uint32_t *outputBuffer); + void RecursiveBlend(int iterationCount, uint64_t* output, uint64_t* currentLine, uint64_t* nextLine, int pixelsPerCycle, bool verticalBlend); protected: void OnBeforeApplyFilter(); diff --git a/Core/PgoUtilities.cpp b/Core/PgoUtilities.cpp index 8b937835a..d7cbc0355 100644 --- a/Core/PgoUtilities.cpp +++ b/Core/PgoUtilities.cpp @@ -10,7 +10,7 @@ extern "C" { void __stdcall PgoRunTest(vector testRoms, bool enableDebugger) { - const VideoFilterType filterTypes[13] = { VideoFilterType::BisqwitNtscQuarterRes, VideoFilterType::HQ2x, VideoFilterType::HQ3x, VideoFilterType::HQ4x, VideoFilterType::NTSC, VideoFilterType::Scale2x, VideoFilterType::Scale3x, VideoFilterType::Scale4x, VideoFilterType::xBRZ2x, VideoFilterType::xBRZ3x, VideoFilterType::xBRZ4x, VideoFilterType::xBRZ5x, VideoFilterType::xBRZ6x }; + const VideoFilterType filterTypes[14] = { VideoFilterType::BisqwitNtscQuarterRes, VideoFilterType::LMP88959NtscQuarterRes, VideoFilterType::HQ2x, VideoFilterType::HQ3x, VideoFilterType::HQ4x, VideoFilterType::NTSC, VideoFilterType::Scale2x, VideoFilterType::Scale3x, VideoFilterType::Scale4x, VideoFilterType::xBRZ2x, VideoFilterType::xBRZ3x, VideoFilterType::xBRZ4x, VideoFilterType::xBRZ5x, VideoFilterType::xBRZ6x }; FolderUtilities::SetHomeFolder("../PGOMesenHome"); for(size_t i = 0; i < testRoms.size(); i++) { diff --git a/Core/ScaleFilter.cpp b/Core/ScaleFilter.cpp index 70230cbdd..d9112d012 100644 --- a/Core/ScaleFilter.cpp +++ b/Core/ScaleFilter.cpp @@ -110,6 +110,9 @@ shared_ptr ScaleFilter::GetScaleFilter(VideoFilterType filter) case VideoFilterType::NTSC: case VideoFilterType::HdPack: case VideoFilterType::Raw: + case VideoFilterType::LMP88959Ntsc: + case VideoFilterType::LMP88959NtscHalfRes: + case VideoFilterType::LMP88959NtscQuarterRes: case VideoFilterType::None: break; diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index ee4a94cb2..022e5fbb7 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -5,6 +5,7 @@ #include "DefaultVideoFilter.h" #include "RawVideoFilter.h" #include "BisqwitNtscFilter.h" +#include "LMP88959NtscFilter.h" #include "NtscFilter.h" #include "HdVideoFilter.h" #include "ScaleFilter.h" @@ -73,6 +74,9 @@ void VideoDecoder::UpdateVideoFilter() case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(_console, 1)); break; case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2)); break; case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4)); break; + case VideoFilterType::LMP88959Ntsc: _videoFilter.reset(new LMP88959NtscFilter(_console, 1)); break; + case VideoFilterType::LMP88959NtscHalfRes: _videoFilter.reset(new LMP88959NtscFilter(_console, 2)); break; + case VideoFilterType::LMP88959NtscQuarterRes: _videoFilter.reset(new LMP88959NtscFilter(_console, 4)); break; case VideoFilterType::Raw: _videoFilter.reset(new RawVideoFilter(_console)); break; default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break; } diff --git a/GUI.NET/Config/VideoInfo.cs b/GUI.NET/Config/VideoInfo.cs index ad3279db9..d613b4847 100644 --- a/GUI.NET/Config/VideoInfo.cs +++ b/GUI.NET/Config/VideoInfo.cs @@ -41,6 +41,7 @@ public class VideoInfo [MinMax(-100, 100)] public Int32 NtscSharpness = 0; public bool NtscMergeFields = false; public bool NtscVerticalBlend = true; + public bool NtscKeepVerticalResolution = false; public bool NtscColorimetryCorrection = true; public bool NtscUseExternalPalette = true; @@ -55,6 +56,8 @@ public class VideoInfo public double NtscDecodeMatrixIB = -1.105; public double NtscDecodeMatrixQB = 1.702; + [MinMax(0, 500)] public Int32 NtscNoise = 0; + public bool RemoveSpriteLimit = false; public bool AdaptiveSpriteLimit = true; public bool DisableBackground = false; @@ -118,7 +121,7 @@ static public void ApplyConfig() InteropEmu.SetVideoAspectRatio(videoInfo.AspectRatio, videoInfo.CustomAspectRatio); InteropEmu.SetPictureSettings(videoInfo.Brightness / 100.0, videoInfo.Contrast / 100.0, videoInfo.Saturation / 100.0, videoInfo.Hue / 100.0, videoInfo.ScanlineIntensity / 100.0); - InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscDecodeMatrixIR, videoInfo.NtscDecodeMatrixQR, videoInfo.NtscDecodeMatrixIG, videoInfo.NtscDecodeMatrixQG, videoInfo.NtscDecodeMatrixIB, videoInfo.NtscDecodeMatrixQB, videoInfo.NtscVerticalBlend, videoInfo.NtscColorimetryCorrection, videoInfo.NtscUseExternalPalette); + InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscDecodeMatrixIR, videoInfo.NtscDecodeMatrixQR, videoInfo.NtscDecodeMatrixIG, videoInfo.NtscDecodeMatrixQG, videoInfo.NtscDecodeMatrixIB, videoInfo.NtscDecodeMatrixQB, videoInfo.NtscVerticalBlend, videoInfo.NtscKeepVerticalResolution, videoInfo.NtscColorimetryCorrection, videoInfo.NtscUseExternalPalette, videoInfo.NtscNoise / 500.0); if(!string.IsNullOrWhiteSpace(videoInfo.PaletteData)) { try { diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index 9a2581918..c13cb150e 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -1035,6 +1035,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index 19cb2583e..a1e2a4ce8 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -1139,6 +1139,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index a97186424..2eb6111fe 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -1051,6 +1051,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 5819130a3..ac4979889 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -1065,6 +1065,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.it.xml b/GUI.NET/Dependencies/resources.it.xml index 5e1c1b2e2..b3651d307 100644 --- a/GUI.NET/Dependencies/resources.it.xml +++ b/GUI.NET/Dependencies/resources.it.xml @@ -1067,6 +1067,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index 6b1c8ef23..db8036281 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -1053,6 +1053,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pl.xml b/GUI.NET/Dependencies/resources.pl.xml index a567c5b4c..3a003139f 100644 --- a/GUI.NET/Dependencies/resources.pl.xml +++ b/GUI.NET/Dependencies/resources.pl.xml @@ -1067,6 +1067,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index 608696da1..16fdaf7d2 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -1067,6 +1067,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index eed51f53b..f56f3dd57 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -1053,6 +1053,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index 8b8085079..b1e7dbccd 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -1053,6 +1053,9 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) + NTSC 4x (LMP88959) + NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.zh.xml b/GUI.NET/Dependencies/resources.zh.xml index 615590866..b51b73be9 100644 --- a/GUI.NET/Dependencies/resources.zh.xml +++ b/GUI.NET/Dependencies/resources.zh.xml @@ -1079,6 +1079,9 @@ NTSC 2 倍 (Bisqwit) NTSC 4 倍 (Bisqwit) NTSC 8 倍 (Bisqwit) + NTSC 2 倍 (LMP88959) + NTSC 4 倍 (LMP88959) + NTSC 8 倍 (LMP88959) xBRZ 2 倍 xBRZ 3 倍 xBRZ 4 倍 diff --git a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs index ba01778c0..2737a75d3 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs @@ -63,10 +63,15 @@ private void InitializeComponent() this.btnSelectPreset = new System.Windows.Forms.Button(); this.btnResetPictureSettings = new System.Windows.Forms.Button(); this.grpNtscFilter = new System.Windows.Forms.GroupBox(); + this.tlpNtscFilter3 = new System.Windows.Forms.TableLayoutPanel(); + this.trkNoise = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tlpNtscFilter2 = new System.Windows.Forms.TableLayoutPanel(); this.trkYFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkIFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkQFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.tableLayoutPanel19 = new System.Windows.Forms.TableLayoutPanel(); + this.chkColorimetryCorrection = new System.Windows.Forms.CheckBox(); + this.chkVerticalBlend = new System.Windows.Forms.CheckBox(); this.tlpNtscFilter1 = new System.Windows.Forms.TableLayoutPanel(); this.trkArtifacts = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkBleed = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); @@ -76,9 +81,8 @@ private void InitializeComponent() this.trkSharpness = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel(); this.chkMergeFields = new System.Windows.Forms.CheckBox(); - this.chkVerticalBlend = new System.Windows.Forms.CheckBox(); - this.chkColorimetryCorrection = new System.Windows.Forms.CheckBox(); this.chkUseExternalPalette = new System.Windows.Forms.CheckBox(); + this.chkKeepVerticalResolution = new System.Windows.Forms.CheckBox(); this.grpCommon = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); this.chkBilinearInterpolation = new System.Windows.Forms.CheckBox(); @@ -162,6 +166,7 @@ private void InitializeComponent() this.mnuPaletteSonyCxa2025As = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPaletteUnsaturated = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPaletteYuv = new System.Windows.Forms.ToolStripMenuItem(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); this.tlpMain.SuspendLayout(); this.flpResolution.SuspendLayout(); this.flowLayoutPanel7.SuspendLayout(); @@ -174,7 +179,9 @@ private void InitializeComponent() this.tableLayoutPanel5.SuspendLayout(); this.tableLayoutPanel7.SuspendLayout(); this.grpNtscFilter.SuspendLayout(); + this.tlpNtscFilter3.SuspendLayout(); this.tlpNtscFilter2.SuspendLayout(); + this.tableLayoutPanel19.SuspendLayout(); this.tlpNtscFilter1.SuspendLayout(); this.tableLayoutPanel6.SuspendLayout(); this.grpCommon.SuspendLayout(); @@ -260,7 +267,6 @@ private void InitializeComponent() this.flpResolution.Size = new System.Drawing.Size(491, 27); this.flpResolution.TabIndex = 27; this.flpResolution.Visible = false; - this.flpResolution.Paint += new System.Windows.Forms.PaintEventHandler(this.flpResolution_Paint); // // lblFullscreenResolution // @@ -644,12 +650,12 @@ private void InitializeComponent() this.tableLayoutPanel7.Controls.Add(this.btnSelectPreset, 1, 0); this.tableLayoutPanel7.Controls.Add(this.btnResetPictureSettings, 0, 0); this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 341); + this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 396); this.tableLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel7.Name = "tableLayoutPanel7"; this.tableLayoutPanel7.RowCount = 1; this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tableLayoutPanel7.Size = new System.Drawing.Size(260, 55); + this.tableLayoutPanel7.Size = new System.Drawing.Size(260, 1); this.tableLayoutPanel7.TabIndex = 3; // // btnSelectPreset @@ -658,10 +664,10 @@ private void InitializeComponent() this.btnSelectPreset.AutoSize = true; this.btnSelectPreset.Image = global::Mesen.GUI.Properties.Resources.DownArrow; this.btnSelectPreset.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; - this.btnSelectPreset.Location = new System.Drawing.Point(158, 29); + this.btnSelectPreset.Location = new System.Drawing.Point(158, 3); this.btnSelectPreset.Name = "btnSelectPreset"; this.btnSelectPreset.Padding = new System.Windows.Forms.Padding(0, 0, 3, 0); - this.btnSelectPreset.Size = new System.Drawing.Size(99, 23); + this.btnSelectPreset.Size = new System.Drawing.Size(99, 1); this.btnSelectPreset.TabIndex = 3; this.btnSelectPreset.Text = "Select Preset..."; this.btnSelectPreset.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; @@ -672,9 +678,9 @@ private void InitializeComponent() // this.btnResetPictureSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnResetPictureSettings.AutoSize = true; - this.btnResetPictureSettings.Location = new System.Drawing.Point(3, 29); + this.btnResetPictureSettings.Location = new System.Drawing.Point(3, 3); this.btnResetPictureSettings.Name = "btnResetPictureSettings"; - this.btnResetPictureSettings.Size = new System.Drawing.Size(75, 23); + this.btnResetPictureSettings.Size = new System.Drawing.Size(75, 1); this.btnResetPictureSettings.TabIndex = 3; this.btnResetPictureSettings.Text = "Reset"; this.btnResetPictureSettings.UseVisualStyleBackColor = true; @@ -682,6 +688,7 @@ private void InitializeComponent() // // grpNtscFilter // + this.grpNtscFilter.Controls.Add(this.tlpNtscFilter3); this.grpNtscFilter.Controls.Add(this.tlpNtscFilter2); this.grpNtscFilter.Controls.Add(this.tlpNtscFilter1); this.grpNtscFilter.Dock = System.Windows.Forms.DockStyle.Fill; @@ -694,6 +701,34 @@ private void InitializeComponent() this.grpNtscFilter.TabStop = false; this.grpNtscFilter.Text = "NTSC Filter"; // + // tlpNtscFilter3 + // + this.tlpNtscFilter3.ColumnCount = 1; + this.tlpNtscFilter3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpNtscFilter3.Controls.Add(this.trkNoise, 0, 0); + this.tlpNtscFilter3.Location = new System.Drawing.Point(3, 16); + this.tlpNtscFilter3.Margin = new System.Windows.Forms.Padding(0); + this.tlpNtscFilter3.Name = "tlpNtscFilter3"; + this.tlpNtscFilter3.RowCount = 1; + this.tlpNtscFilter3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter3.Size = new System.Drawing.Size(253, 350); + this.tlpNtscFilter3.TabIndex = 7; + // + // trkNoise + // + this.trkNoise.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkNoise.Location = new System.Drawing.Point(0, 0); + this.trkNoise.Margin = new System.Windows.Forms.Padding(0); + this.trkNoise.Maximum = 500; + this.trkNoise.MaximumSize = new System.Drawing.Size(0, 41); + this.trkNoise.Minimum = 0; + this.trkNoise.MinimumSize = new System.Drawing.Size(206, 50); + this.trkNoise.Name = "trkNoise"; + this.trkNoise.Size = new System.Drawing.Size(253, 50); + this.trkNoise.TabIndex = 26; + this.trkNoise.Text = "Signal noise"; + this.trkNoise.Value = 0; + // // tlpNtscFilter2 // this.tlpNtscFilter2.ColumnCount = 1; @@ -701,19 +736,18 @@ private void InitializeComponent() this.tlpNtscFilter2.Controls.Add(this.trkYFilterLength, 0, 0); this.tlpNtscFilter2.Controls.Add(this.trkIFilterLength, 0, 1); this.tlpNtscFilter2.Controls.Add(this.trkQFilterLength, 0, 2); + this.tlpNtscFilter2.Controls.Add(this.tableLayoutPanel19, 0, 3); this.tlpNtscFilter2.Dock = System.Windows.Forms.DockStyle.Top; this.tlpNtscFilter2.Location = new System.Drawing.Point(3, 16); this.tlpNtscFilter2.Margin = new System.Windows.Forms.Padding(0); this.tlpNtscFilter2.Name = "tlpNtscFilter2"; - this.tlpNtscFilter2.RowCount = 7; - this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter2.RowCount = 4; this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tlpNtscFilter2.Size = new System.Drawing.Size(253, 298); + this.tlpNtscFilter2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpNtscFilter2.Size = new System.Drawing.Size(253, 350); this.tlpNtscFilter2.TabIndex = 6; // // trkYFilterLength @@ -763,6 +797,41 @@ private void InitializeComponent() this.trkQFilterLength.Text = "Q Filter (Horizontal Bleed)"; this.trkQFilterLength.Value = 0; // + // tableLayoutPanel19 + // + this.tableLayoutPanel19.ColumnCount = 2; + this.tableLayoutPanel19.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 57.08502F)); + this.tableLayoutPanel19.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 42.91498F)); + this.tableLayoutPanel19.Controls.Add(this.chkColorimetryCorrection, 0, 0); + this.tableLayoutPanel19.Controls.Add(this.checkBox1, 0, 1); + this.tableLayoutPanel19.Location = new System.Drawing.Point(3, 153); + this.tableLayoutPanel19.Name = "tableLayoutPanel19"; + this.tableLayoutPanel19.RowCount = 2; + this.tableLayoutPanel19.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel19.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel19.Size = new System.Drawing.Size(247, 50); + this.tableLayoutPanel19.TabIndex = 27; + // + // chkColorimetryCorrection + // + this.chkColorimetryCorrection.AutoSize = true; + this.chkColorimetryCorrection.Location = new System.Drawing.Point(3, 3); + this.chkColorimetryCorrection.Name = "chkColorimetryCorrection"; + this.chkColorimetryCorrection.Size = new System.Drawing.Size(101, 17); + this.chkColorimetryCorrection.TabIndex = 32; + this.chkColorimetryCorrection.Text = "Color Correction"; + this.chkColorimetryCorrection.UseVisualStyleBackColor = true; + // + // chkVerticalBlend + // + this.chkVerticalBlend.AutoSize = true; + this.chkVerticalBlend.Location = new System.Drawing.Point(3, 28); + this.chkVerticalBlend.Name = "chkVerticalBlend"; + this.chkVerticalBlend.Size = new System.Drawing.Size(134, 17); + this.chkVerticalBlend.TabIndex = 31; + this.chkVerticalBlend.Text = "Apply Vertical Blending"; + this.chkVerticalBlend.UseVisualStyleBackColor = true; + // // tlpNtscFilter1 // this.tlpNtscFilter1.ColumnCount = 1; @@ -886,8 +955,8 @@ private void InitializeComponent() this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel6.Controls.Add(this.chkMergeFields, 0, 0); this.tableLayoutPanel6.Controls.Add(this.chkVerticalBlend, 0, 1); - this.tableLayoutPanel6.Controls.Add(this.chkColorimetryCorrection, 1, 1); this.tableLayoutPanel6.Controls.Add(this.chkUseExternalPalette, 1, 0); + this.tableLayoutPanel6.Controls.Add(this.chkKeepVerticalResolution, 1, 1); this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 300); this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); @@ -908,26 +977,6 @@ private void InitializeComponent() this.chkMergeFields.Text = "Merge Fields"; this.chkMergeFields.UseVisualStyleBackColor = true; // - // chkVerticalBlend - // - this.chkVerticalBlend.AutoSize = true; - this.chkVerticalBlend.Location = new System.Drawing.Point(3, 28); - this.chkVerticalBlend.Name = "chkVerticalBlend"; - this.chkVerticalBlend.Size = new System.Drawing.Size(134, 17); - this.chkVerticalBlend.TabIndex = 31; - this.chkVerticalBlend.Text = "Apply Vertical Blending"; - this.chkVerticalBlend.UseVisualStyleBackColor = true; - // - // chkColorimetryCorrection - // - this.chkColorimetryCorrection.AutoSize = true; - this.chkColorimetryCorrection.Location = new System.Drawing.Point(143, 28); - this.chkColorimetryCorrection.Name = "chkColorimetryCorrection"; - this.chkColorimetryCorrection.Size = new System.Drawing.Size(101, 17); - this.chkColorimetryCorrection.TabIndex = 32; - this.chkColorimetryCorrection.Text = "Color Correction"; - this.chkColorimetryCorrection.UseVisualStyleBackColor = true; - // // chkUseExternalPalette // this.chkUseExternalPalette.AutoSize = true; @@ -938,6 +987,17 @@ private void InitializeComponent() this.chkUseExternalPalette.Text = "External Palette"; this.chkUseExternalPalette.UseVisualStyleBackColor = true; // + // chkKeepVerticalResolution + // + this.chkKeepVerticalResolution.AutoSize = true; + this.chkKeepVerticalResolution.Enabled = false; + this.chkKeepVerticalResolution.Location = new System.Drawing.Point(143, 28); + this.chkKeepVerticalResolution.Name = "chkKeepVerticalResolution"; + this.chkKeepVerticalResolution.Size = new System.Drawing.Size(83, 17); + this.chkKeepVerticalResolution.TabIndex = 32; + this.chkKeepVerticalResolution.Text = "Keep v. res."; + this.chkKeepVerticalResolution.UseVisualStyleBackColor = true; + // // grpCommon // this.grpCommon.Controls.Add(this.tableLayoutPanel4); @@ -1055,7 +1115,7 @@ private void InitializeComponent() this.grpScanlines.Location = new System.Drawing.Point(0, 269); this.grpScanlines.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0); this.grpScanlines.Name = "grpScanlines"; - this.grpScanlines.Size = new System.Drawing.Size(258, 72); + this.grpScanlines.Size = new System.Drawing.Size(258, 127); this.grpScanlines.TabIndex = 5; this.grpScanlines.TabStop = false; this.grpScanlines.Text = "Scanlines"; @@ -2021,33 +2081,33 @@ private void InitializeComponent() this.mnuPresetRgb, this.mnuPresetMonochrome}); this.contextPicturePresets.Name = "contextPicturePresets"; - this.contextPicturePresets.Size = new System.Drawing.Size(153, 92); + this.contextPicturePresets.Size = new System.Drawing.Size(148, 92); // // mnuPresetComposite // this.mnuPresetComposite.Name = "mnuPresetComposite"; - this.mnuPresetComposite.Size = new System.Drawing.Size(152, 22); + this.mnuPresetComposite.Size = new System.Drawing.Size(147, 22); this.mnuPresetComposite.Text = "Composite"; this.mnuPresetComposite.Click += new System.EventHandler(this.mnuPresetComposite_Click); // // mnuPresetSVideo // this.mnuPresetSVideo.Name = "mnuPresetSVideo"; - this.mnuPresetSVideo.Size = new System.Drawing.Size(152, 22); + this.mnuPresetSVideo.Size = new System.Drawing.Size(147, 22); this.mnuPresetSVideo.Text = "S-Video"; this.mnuPresetSVideo.Click += new System.EventHandler(this.mnuPresetSVideo_Click); // // mnuPresetRgb // this.mnuPresetRgb.Name = "mnuPresetRgb"; - this.mnuPresetRgb.Size = new System.Drawing.Size(152, 22); + this.mnuPresetRgb.Size = new System.Drawing.Size(147, 22); this.mnuPresetRgb.Text = "RGB"; this.mnuPresetRgb.Click += new System.EventHandler(this.mnuPresetRgb_Click); // // mnuPresetMonochrome // this.mnuPresetMonochrome.Name = "mnuPresetMonochrome"; - this.mnuPresetMonochrome.Size = new System.Drawing.Size(152, 22); + this.mnuPresetMonochrome.Size = new System.Drawing.Size(147, 22); this.mnuPresetMonochrome.Text = "Monochrome"; this.mnuPresetMonochrome.Click += new System.EventHandler(this.mnuPresetMonochrome_Click); // @@ -2065,77 +2125,87 @@ private void InitializeComponent() this.mnuPaletteUnsaturated, this.mnuPaletteYuv}); this.contextPaletteList.Name = "contextPicturePresets"; - this.contextPaletteList.Size = new System.Drawing.Size(268, 208); + this.contextPaletteList.Size = new System.Drawing.Size(255, 208); this.contextPaletteList.Opening += new System.ComponentModel.CancelEventHandler(this.contextPaletteList_Opening); // // mnuDefaultPalette // this.mnuDefaultPalette.Name = "mnuDefaultPalette"; - this.mnuDefaultPalette.Size = new System.Drawing.Size(267, 22); + this.mnuDefaultPalette.Size = new System.Drawing.Size(254, 22); this.mnuDefaultPalette.Text = "Default (NTSC)"; this.mnuDefaultPalette.Click += new System.EventHandler(this.mnuDefaultPalette_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(264, 6); + this.toolStripMenuItem1.Size = new System.Drawing.Size(251, 6); // // mnuPaletteCompositeDirect // this.mnuPaletteCompositeDirect.Name = "mnuPaletteCompositeDirect"; - this.mnuPaletteCompositeDirect.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteCompositeDirect.Size = new System.Drawing.Size(254, 22); this.mnuPaletteCompositeDirect.Text = "Composite Direct (by FirebrandX)"; this.mnuPaletteCompositeDirect.Click += new System.EventHandler(this.mnuPaletteCompositeDirect_Click); // // mnuPaletteNesClassic // this.mnuPaletteNesClassic.Name = "mnuPaletteNesClassic"; - this.mnuPaletteNesClassic.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteNesClassic.Size = new System.Drawing.Size(254, 22); this.mnuPaletteNesClassic.Text = "NES Classic (by FirebrandX)"; this.mnuPaletteNesClassic.Click += new System.EventHandler(this.mnuPaletteNesClassic_Click); // // mnuPaletteNestopiaRgb // this.mnuPaletteNestopiaRgb.Name = "mnuPaletteNestopiaRgb"; - this.mnuPaletteNestopiaRgb.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteNestopiaRgb.Size = new System.Drawing.Size(254, 22); this.mnuPaletteNestopiaRgb.Text = "Nestopia (RGB)"; this.mnuPaletteNestopiaRgb.Click += new System.EventHandler(this.mnuPaletteNestopiaRgb_Click); // // mnuPaletteOriginalHardware // this.mnuPaletteOriginalHardware.Name = "mnuPaletteOriginalHardware"; - this.mnuPaletteOriginalHardware.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteOriginalHardware.Size = new System.Drawing.Size(254, 22); this.mnuPaletteOriginalHardware.Text = "Original Hardware (by FirebrandX)"; this.mnuPaletteOriginalHardware.Click += new System.EventHandler(this.mnuPaletteOriginalHardware_Click); // // mnuPalettePvmStyle // this.mnuPalettePvmStyle.Name = "mnuPalettePvmStyle"; - this.mnuPalettePvmStyle.Size = new System.Drawing.Size(267, 22); + this.mnuPalettePvmStyle.Size = new System.Drawing.Size(254, 22); this.mnuPalettePvmStyle.Text = "PVM Style (by FirebrandX)"; this.mnuPalettePvmStyle.Click += new System.EventHandler(this.mnuPalettePvmStyle_Click); // // mnuPaletteSonyCxa2025As // this.mnuPaletteSonyCxa2025As.Name = "mnuPaletteSonyCxa2025As"; - this.mnuPaletteSonyCxa2025As.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteSonyCxa2025As.Size = new System.Drawing.Size(254, 22); this.mnuPaletteSonyCxa2025As.Text = "Sony CXA2025AS"; this.mnuPaletteSonyCxa2025As.Click += new System.EventHandler(this.mnuPaletteSonyCxa2025As_Click); // // mnuPaletteUnsaturated // this.mnuPaletteUnsaturated.Name = "mnuPaletteUnsaturated"; - this.mnuPaletteUnsaturated.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteUnsaturated.Size = new System.Drawing.Size(254, 22); this.mnuPaletteUnsaturated.Text = "Unsaturated v6 (by FirebrandX)"; this.mnuPaletteUnsaturated.Click += new System.EventHandler(this.mnuPaletteUnsaturated_Click); // // mnuPaletteYuv // this.mnuPaletteYuv.Name = "mnuPaletteYuv"; - this.mnuPaletteYuv.Size = new System.Drawing.Size(267, 22); + this.mnuPaletteYuv.Size = new System.Drawing.Size(254, 22); this.mnuPaletteYuv.Text = "YUV v3 (by FirebrandX)"; this.mnuPaletteYuv.Click += new System.EventHandler(this.mnuPaletteYuv_Click); // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(3, 28); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(134, 17); + this.checkBox1.TabIndex = 33; + this.checkBox1.Text = "Apply Vertical Blending"; + this.checkBox1.UseVisualStyleBackColor = true; + // // frmVideoConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -2170,8 +2240,11 @@ private void InitializeComponent() this.tableLayoutPanel7.ResumeLayout(false); this.tableLayoutPanel7.PerformLayout(); this.grpNtscFilter.ResumeLayout(false); + this.tlpNtscFilter3.ResumeLayout(false); this.tlpNtscFilter2.ResumeLayout(false); this.tlpNtscFilter2.PerformLayout(); + this.tableLayoutPanel19.ResumeLayout(false); + this.tableLayoutPanel19.PerformLayout(); this.tlpNtscFilter1.ResumeLayout(false); this.tableLayoutPanel6.ResumeLayout(false); this.tableLayoutPanel6.PerformLayout(); @@ -2267,7 +2340,6 @@ private void InitializeComponent() private Controls.ctrlHorizontalTrackbar trkResolution; private Controls.ctrlHorizontalTrackbar trkSharpness; private System.Windows.Forms.GroupBox grpCommon; - private System.Windows.Forms.CheckBox chkMergeFields; private System.Windows.Forms.Button btnResetPictureSettings; private System.Windows.Forms.GroupBox grpScanlines; private Controls.ctrlHorizontalTrackbar trkScanlines; @@ -2315,8 +2387,6 @@ private void InitializeComponent() private System.Windows.Forms.Label lblRequestedRefreshRate; private System.Windows.Forms.ComboBox cboRefreshRate; private System.Windows.Forms.FlowLayoutPanel flpRefreshRate; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; - private System.Windows.Forms.CheckBox chkVerticalBlend; private System.Windows.Forms.TabControl tabOverscan; private System.Windows.Forms.TabPage tpgOverscanGlobal; private System.Windows.Forms.TabPage tpgOverscanGameSpecific; @@ -2348,11 +2418,19 @@ private void InitializeComponent() private System.Windows.Forms.ComboBox cboFullscreenResolution; private System.Windows.Forms.Label lblRequestedRefreshRate2; private System.Windows.Forms.ComboBox cboRefreshRate2; - private System.Windows.Forms.CheckBox chkColorimetryCorrection; private System.Windows.Forms.TableLayoutPanel tlpNtscFilter2; private ctrlHorizontalTrackbar trkYFilterLength; private ctrlHorizontalTrackbar trkIFilterLength; private ctrlHorizontalTrackbar trkQFilterLength; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; + private System.Windows.Forms.CheckBox chkMergeFields; + private System.Windows.Forms.CheckBox chkVerticalBlend; private System.Windows.Forms.CheckBox chkUseExternalPalette; + private System.Windows.Forms.CheckBox chkKeepVerticalResolution; + private System.Windows.Forms.TableLayoutPanel tlpNtscFilter3; + private ctrlHorizontalTrackbar trkNoise; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel19; + private System.Windows.Forms.CheckBox chkColorimetryCorrection; + private System.Windows.Forms.CheckBox checkBox1; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmVideoConfig.cs b/GUI.NET/Forms/Config/frmVideoConfig.cs index b217bd93b..a7bdb0b19 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.cs @@ -64,6 +64,8 @@ public frmVideoConfig() AddBinding("NtscSharpness", trkSharpness); AddBinding("NtscMergeFields", chkMergeFields); AddBinding("NtscVerticalBlend", chkVerticalBlend); + AddBinding("NtscVerticalBlend", checkBox1); + AddBinding("NtscKeepVerticalResolution", chkKeepVerticalResolution); AddBinding("NtscColorimetryCorrection", chkColorimetryCorrection); AddBinding("NtscUseExternalPalette", chkUseExternalPalette); @@ -71,6 +73,8 @@ public frmVideoConfig() AddBinding("NtscIFilterLength", trkIFilterLength); AddBinding("NtscQFilterLength", trkQFilterLength); + AddBinding("NtscNoise", trkNoise); + AddBinding("RemoveSpriteLimit", chkRemoveSpriteLimit); AddBinding("AdaptiveSpriteLimit", chkAdaptiveSpriteLimit); AddBinding("DisableBackground", chkDisableBackground); @@ -162,22 +166,64 @@ protected override bool ValidateInput() UpdateCustomRatioVisibility(); UpdatePalette(); VideoFilterType filter = ((VideoInfo)Entity).VideoFilter; - if(filter == VideoFilterType.NTSC) { - tlpNtscFilter1.Visible = true; - tlpNtscFilter2.Visible = false; - chkMergeFields.Visible = true; - chkColorimetryCorrection.Visible = false; - chkUseExternalPalette.Visible = true; - grpNtscFilter.Visible = true; - } else if(filter == VideoFilterType.BisqwitNtsc || filter == VideoFilterType.BisqwitNtscHalfRes || filter == VideoFilterType.BisqwitNtscQuarterRes) { - tlpNtscFilter1.Visible = true; - tlpNtscFilter2.Visible = true; - chkMergeFields.Visible = false; - chkColorimetryCorrection.Visible = true; - chkUseExternalPalette.Visible = false; - grpNtscFilter.Visible = true; - } else { - grpNtscFilter.Visible = false; + switch (filter) + { + case VideoFilterType.NTSC: + { + tlpNtscFilter1.Visible = true; + tlpNtscFilter2.Visible = false; + tlpNtscFilter3.Visible = false; + + chkMergeFields.Visible = true; + chkColorimetryCorrection.Visible = false; + chkUseExternalPalette.Visible = true; + + grpNtscFilter.Visible = true; + } + break; + case VideoFilterType.BisqwitNtsc: + case VideoFilterType.BisqwitNtscHalfRes: + case VideoFilterType.BisqwitNtscQuarterRes: + { + tlpNtscFilter1.Visible = false; + tlpNtscFilter2.Visible = true; + tlpNtscFilter3.Visible = false; + + chkMergeFields.Visible = false; + chkColorimetryCorrection.Visible = true; + chkUseExternalPalette.Visible = false; + + grpNtscFilter.Visible = true; + } + break; + case VideoFilterType.LMP88959Ntsc: + case VideoFilterType.LMP88959NtscHalfRes: + case VideoFilterType.LMP88959NtscQuarterRes: + { + tlpNtscFilter1.Visible = false; + tlpNtscFilter2.Visible = false; + tlpNtscFilter3.Visible = true; + + chkMergeFields.Visible = false; + chkColorimetryCorrection.Visible = false; + chkUseExternalPalette.Visible = false; + + grpNtscFilter.Visible = true; + } + break; + default: + { + tlpNtscFilter1.Visible = false; + tlpNtscFilter2.Visible = false; + tlpNtscFilter3.Visible = false; + + chkMergeFields.Visible = false; + chkColorimetryCorrection.Visible = false; + chkUseExternalPalette.Visible = false; + + grpNtscFilter.Visible = false; + } + break; } VideoInfo.ApplyConfig(); @@ -303,11 +349,13 @@ private void btnResetPictureSettings_Click(object sender, EventArgs e) chkMergeFields.Checked = false; chkVerticalBlend.Checked = true; chkColorimetryCorrection.Checked = true; + chkKeepVerticalResolution.Checked = true; chkUseExternalPalette.Checked = true; trkYFilterLength.Value = 0; trkIFilterLength.Value = 50; trkQFilterLength.Value = 50; + trkNoise.Value = 0; } private void btnSelectPreset_Click(object sender, EventArgs e) @@ -515,10 +563,5 @@ private void chkRemoveSpriteLimit_CheckedChanged(object sender, EventArgs e) { chkAdaptiveSpriteLimit.Enabled = chkRemoveSpriteLimit.Checked; } - - private void flpResolution_Paint(object sender, PaintEventArgs e) - { - - } } } diff --git a/GUI.NET/Forms/Config/frmVideoConfig.resx b/GUI.NET/Forms/Config/frmVideoConfig.resx index 560c201a4..21c167276 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.resx +++ b/GUI.NET/Forms/Config/frmVideoConfig.resx @@ -121,14 +121,14 @@ 17, 17 - 561, 17 + 537, 17 AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACs - BQAAAk1TRnQBSQFMAwEBAAGwAQABsAEAARABAAEQAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA + BQAAAk1TRnQBSQFMAwEBAAFYAQIBWAECARABAAEQAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA ARADAAEBAQABGAYAAQweAAH5AvgB1QHBAbsBqAFyAWEBkAFHATABkAFHATABpgFuAVwB0gG8AbUB+AL2 pQAB1QHAAbkBlgFNATIBqgFaASwBuwFkASsBwAFpASkBwAFpASkBuwFlASwBqwFbAS0BmAFMATAB0wG9 AbWfAAHRAbgBrwGlAVgBMgHAAW0BLgHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHA @@ -156,12 +156,12 @@ - 229, 17 + 221, 17 - 110, 17 + 107, 17 - 406, 17 + 389, 17 \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index f810490f0..b10df4d6a 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -30,353 +30,356 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.menuTimer = new System.Windows.Forms.Timer(this.components); - this.panelRenderer = new System.Windows.Forms.Panel(); - this.ctrlRendererDualSystem = new Mesen.GUI.Controls.ctrlRenderer(); - this.ctrlLoading = new Mesen.GUI.Controls.ctrlLoadingRom(); - this.panelInfo = new System.Windows.Forms.Panel(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.picIcon = new System.Windows.Forms.PictureBox(); - this.lblVersion = new System.Windows.Forms.Label(); - this.ctrlNsfPlayer = new Mesen.GUI.Controls.ctrlNsfPlayer(); - this.ctrlRenderer = new Mesen.GUI.Controls.ctrlRenderer(); - this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames(); - this.menuStrip = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); - this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuReloadRom = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuSaveState = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuLoadState = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuLoadLastSession = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuRecentFiles = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuExit = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuGame = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPause = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuReset = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPowerCycle = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem24 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuPowerOff = new System.Windows.Forms.ToolStripMenuItem(); - this.sepFdsDisk = new System.Windows.Forms.ToolStripSeparator(); - this.mnuSwitchDiskSide = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuSelectDisk = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEjectDisk = new System.Windows.Forms.ToolStripMenuItem(); - this.sepVsSystem = new System.Windows.Forms.ToolStripSeparator(); - this.mnuGameConfig = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin1 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin2 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin3 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin4 = new System.Windows.Forms.ToolStripMenuItem(); - this.sepBarcode = new System.Windows.Forms.ToolStripSeparator(); - this.mnuInputBarcode = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTapeRecorder = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuLoadTapeFile = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuStartRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuStopRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOptions = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmulationSpeed = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmuSpeedNormal = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuIncreaseSpeed = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuDecreaseSpeed = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmuSpeedMaximumSpeed = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuEmuSpeedTriple = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmuSpeedDouble = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmuSpeedHalf = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmuSpeedQuarter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuShowFPS = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuVideoScale = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale1x = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale2x = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale3x = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale4x = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale5x = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale6x = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuFullscreen = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuVideoFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNoneFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem21 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuNtscFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscBisqwitQuarterFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscBisqwitHalfFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscBisqwitFullFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuXBRZ2xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuXBRZ3xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuXBRZ4xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuXBRZ5xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuXBRZ6xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuHQ2xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHQ3xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHQ4xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem17 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuScale2xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale3xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScale4xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem23 = new System.Windows.Forms.ToolStripSeparator(); - this.mnu2xSaiFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuSuper2xSaiFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuSuperEagleFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem18 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuPrescale2xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrescale3xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrescale4xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrescale6xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrescale8xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrescale10xFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem19 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuBilinearInterpolation = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegion = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionAuto = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionNtsc = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionPal = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRegionDendy = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuAudioConfig = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInput = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuVideoConfig = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEmulationConfig = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuPreferences = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTools = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuConnect = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlaySelectController = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlayPlayer1 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlayPlayer2 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlayPlayer3 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlayPlayer4 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNetPlayPlayer5 = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuFindServer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuMovies = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHistoryViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuWaveStop = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuVideoRecorder = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuAviRecord = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuAviStop = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuTests = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRun = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRecordFrom = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRecordStart = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRecordNow = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRecordMovie = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestRecordTest = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTestStopRecording = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRunAllTests = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRunAllGameTests = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuRunAutomaticTest = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuLogWindow = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem27 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuInstallHdPack = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHdPackEditor = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuRandomGame = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTakeScreenshot = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuDebug = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuApuViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuAssembler = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuEventViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuMemoryViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuProfiler = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPpuViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuScriptWindow = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTextHooker = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuWatchWindow = new System.Windows.Forms.ToolStripMenuItem(); - this.sepDebugDualSystemSecondaryCpu = new System.Windows.Forms.ToolStripSeparator(); - this.mnuDebugDualSystemSecondaryCpu = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuPpuViewerCompact = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOpenNametableViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOpenChrViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOpenSpriteViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOpenPaletteViewer = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem28 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuEditHeader = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHelp = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuOnlineHelp = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuHelpWindow = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem26 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuCheckForUpdates = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuReportBug = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); - this.panelRenderer.SuspendLayout(); - this.panelInfo.SuspendLayout(); - this.tableLayoutPanel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.picIcon)).BeginInit(); - this.menuStrip.SuspendLayout(); - this.SuspendLayout(); - // - // menuTimer - // - this.menuTimer.Tick += new System.EventHandler(this.menuTimer_Tick); - // - // panelRenderer - // - this.panelRenderer.BackColor = System.Drawing.Color.Black; - this.panelRenderer.Controls.Add(this.ctrlRendererDualSystem); - this.panelRenderer.Controls.Add(this.ctrlLoading); - this.panelRenderer.Controls.Add(this.panelInfo); - this.panelRenderer.Controls.Add(this.ctrlNsfPlayer); - this.panelRenderer.Controls.Add(this.ctrlRenderer); - this.panelRenderer.Dock = System.Windows.Forms.DockStyle.Fill; - this.panelRenderer.Location = new System.Drawing.Point(0, 24); - this.panelRenderer.Name = "panelRenderer"; - this.panelRenderer.Size = new System.Drawing.Size(430, 309); - this.panelRenderer.TabIndex = 2; - this.panelRenderer.Click += new System.EventHandler(this.panelRenderer_Click); - this.panelRenderer.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); - this.panelRenderer.MouseLeave += new System.EventHandler(this.panelRenderer_MouseLeave); - this.panelRenderer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); - // - // ctrlRendererDualSystem - // - this.ctrlRendererDualSystem.BackColor = System.Drawing.Color.Black; - this.ctrlRendererDualSystem.Location = new System.Drawing.Point(275, 0); - this.ctrlRendererDualSystem.Margin = new System.Windows.Forms.Padding(0); - this.ctrlRendererDualSystem.Name = "ctrlRendererDualSystem"; - this.ctrlRendererDualSystem.Size = new System.Drawing.Size(150, 90); - this.ctrlRendererDualSystem.TabIndex = 8; - this.ctrlRendererDualSystem.Visible = false; - // - // ctrlLoading - // - this.ctrlLoading.BackColor = System.Drawing.Color.Black; - this.ctrlLoading.Dock = System.Windows.Forms.DockStyle.Fill; - this.ctrlLoading.Location = new System.Drawing.Point(0, 0); - this.ctrlLoading.Name = "ctrlLoading"; - this.ctrlLoading.Size = new System.Drawing.Size(430, 309); - this.ctrlLoading.TabIndex = 4; - this.ctrlLoading.Visible = false; - // - // panelInfo - // - this.panelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.panelInfo.BackColor = System.Drawing.Color.Transparent; - this.panelInfo.Controls.Add(this.tableLayoutPanel1); - this.panelInfo.Location = new System.Drawing.Point(353, 283); - this.panelInfo.Name = "panelInfo"; - this.panelInfo.Size = new System.Drawing.Size(77, 26); - this.panelInfo.TabIndex = 6; - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.ColumnCount = 2; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.Controls.Add(this.picIcon, 1, 0); - this.tableLayoutPanel1.Controls.Add(this.lblVersion, 0, 0); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); - this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 1; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(77, 26); - this.tableLayoutPanel1.TabIndex = 6; - // - // picIcon - // - this.picIcon.Anchor = System.Windows.Forms.AnchorStyles.None; - this.picIcon.BackgroundImage = global::Mesen.GUI.Properties.Resources.MesenIconSmall; - this.picIcon.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; - this.picIcon.Location = new System.Drawing.Point(58, 5); - this.picIcon.Name = "picIcon"; - this.picIcon.Size = new System.Drawing.Size(16, 16); - this.picIcon.TabIndex = 5; - this.picIcon.TabStop = false; - // - // lblVersion - // - this.lblVersion.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.lblVersion.AutoSize = true; - this.lblVersion.BackColor = System.Drawing.Color.Transparent; - this.lblVersion.ForeColor = System.Drawing.Color.White; - this.lblVersion.Location = new System.Drawing.Point(55, 7); - this.lblVersion.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0); - this.lblVersion.Name = "lblVersion"; - this.lblVersion.Size = new System.Drawing.Size(0, 13); - this.lblVersion.TabIndex = 6; - this.lblVersion.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // ctrlNsfPlayer - // - this.ctrlNsfPlayer.BackColor = System.Drawing.Color.Black; - this.ctrlNsfPlayer.Dock = System.Windows.Forms.DockStyle.Fill; - this.ctrlNsfPlayer.Location = new System.Drawing.Point(0, 0); - this.ctrlNsfPlayer.Name = "ctrlNsfPlayer"; - this.ctrlNsfPlayer.Size = new System.Drawing.Size(430, 309); - this.ctrlNsfPlayer.TabIndex = 2; - this.ctrlNsfPlayer.Visible = false; - this.ctrlNsfPlayer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlNsfPlayer_MouseMove); - // - // ctrlRenderer - // - this.ctrlRenderer.BackColor = System.Drawing.Color.Black; - this.ctrlRenderer.Location = new System.Drawing.Point(0, 0); - this.ctrlRenderer.Margin = new System.Windows.Forms.Padding(0); - this.ctrlRenderer.Name = "ctrlRenderer"; - this.ctrlRenderer.Size = new System.Drawing.Size(150, 90); - this.ctrlRenderer.TabIndex = 1; - this.ctrlRenderer.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); - this.ctrlRenderer.Enter += new System.EventHandler(this.ctrlRenderer_Enter); - this.ctrlRenderer.MouseClick += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseClick); - this.ctrlRenderer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); - // - // ctrlRecentGames - // - this.ctrlRecentGames.BackColor = System.Drawing.Color.Transparent; - this.ctrlRecentGames.Dock = System.Windows.Forms.DockStyle.Top; - this.ctrlRecentGames.Location = new System.Drawing.Point(0, 24); - this.ctrlRecentGames.Name = "ctrlRecentGames"; - this.ctrlRecentGames.Size = new System.Drawing.Size(430, 309); - this.ctrlRecentGames.TabIndex = 7; - this.ctrlRecentGames.Visible = false; - this.ctrlRecentGames.OnRecentGameLoaded += new Mesen.GUI.Controls.ctrlRecentGames.RecentGameLoadedHandler(this.ctrlRecentGames_OnRecentGameLoaded); - this.ctrlRecentGames.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); - this.ctrlRecentGames.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); - // - // menuStrip - // - this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.components = new System.ComponentModel.Container(); + this.menuTimer = new System.Windows.Forms.Timer(this.components); + this.panelRenderer = new System.Windows.Forms.Panel(); + this.ctrlRendererDualSystem = new Mesen.GUI.Controls.ctrlRenderer(); + this.ctrlLoading = new Mesen.GUI.Controls.ctrlLoadingRom(); + this.panelInfo = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.picIcon = new System.Windows.Forms.PictureBox(); + this.lblVersion = new System.Windows.Forms.Label(); + this.ctrlNsfPlayer = new Mesen.GUI.Controls.ctrlNsfPlayer(); + this.ctrlRenderer = new Mesen.GUI.Controls.ctrlRenderer(); + this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames(); + this.menuStrip = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); + this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuReloadRom = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSaveState = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuLoadState = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuLoadLastSession = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRecentFiles = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuExit = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuGame = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPause = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuReset = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPowerCycle = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem24 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPowerOff = new System.Windows.Forms.ToolStripMenuItem(); + this.sepFdsDisk = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSwitchDiskSide = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuSelectDisk = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEjectDisk = new System.Windows.Forms.ToolStripMenuItem(); + this.sepVsSystem = new System.Windows.Forms.ToolStripSeparator(); + this.mnuGameConfig = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin1 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin2 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin3 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin4 = new System.Windows.Forms.ToolStripMenuItem(); + this.sepBarcode = new System.Windows.Forms.ToolStripSeparator(); + this.mnuInputBarcode = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTapeRecorder = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuLoadTapeFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStartRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStopRecordTapeFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOptions = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmulationSpeed = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmuSpeedNormal = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuIncreaseSpeed = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDecreaseSpeed = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmuSpeedMaximumSpeed = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuEmuSpeedTriple = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmuSpeedDouble = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmuSpeedHalf = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmuSpeedQuarter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuShowFPS = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoScale = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale1x = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale2x = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale3x = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale4x = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale5x = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale6x = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFullscreen = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNoneFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem21 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuNtscFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscBisqwitQuarterFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscBisqwitHalfFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscBisqwitFullFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscLMP88959QuarterFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscLMP88959HalfFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscLMP88959FullFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuXBRZ2xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuXBRZ3xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuXBRZ4xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuXBRZ5xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuXBRZ6xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuHQ2xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHQ3xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHQ4xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem17 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuScale2xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale3xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScale4xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem23 = new System.Windows.Forms.ToolStripSeparator(); + this.mnu2xSaiFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuSuper2xSaiFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuSuperEagleFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem18 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPrescale2xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrescale3xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrescale4xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrescale6xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrescale8xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrescale10xFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem19 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuBilinearInterpolation = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegion = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionAuto = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionNtsc = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionPal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRegionDendy = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuAudioConfig = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInput = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoConfig = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEmulationConfig = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPreferences = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTools = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuConnect = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlaySelectController = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlayPlayer1 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlayPlayer2 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlayPlayer3 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlayPlayer4 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNetPlayPlayer5 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFindServer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuMovies = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHistoryViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuCheats = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem22 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSoundRecorder = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWaveRecord = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWaveStop = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuVideoRecorder = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAviRecord = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAviStop = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuTests = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRun = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRecordFrom = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRecordStart = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRecordNow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRecordMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestRecordTest = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTestStopRecording = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunAllTests = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunAllGameTests = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunAutomaticTest = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuLogWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem27 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuInstallHdPack = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHdPackEditor = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRandomGame = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTakeScreenshot = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebug = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuApuViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAssembler = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEventViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuMemoryViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuProfiler = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPpuViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuScriptWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTextHooker = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuWatchWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.sepDebugDualSystemSecondaryCpu = new System.Windows.Forms.ToolStripSeparator(); + this.mnuDebugDualSystemSecondaryCpu = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuPpuViewerCompact = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpenNametableViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpenChrViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpenSpriteViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpenPaletteViewer = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem28 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuEditHeader = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHelp = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOnlineHelp = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHelpWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem26 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuCheckForUpdates = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuReportBug = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); + this.panelRenderer.SuspendLayout(); + this.panelInfo.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picIcon)).BeginInit(); + this.menuStrip.SuspendLayout(); + this.SuspendLayout(); + // + // menuTimer + // + this.menuTimer.Tick += new System.EventHandler(this.menuTimer_Tick); + // + // panelRenderer + // + this.panelRenderer.BackColor = System.Drawing.Color.Black; + this.panelRenderer.Controls.Add(this.ctrlRendererDualSystem); + this.panelRenderer.Controls.Add(this.ctrlLoading); + this.panelRenderer.Controls.Add(this.panelInfo); + this.panelRenderer.Controls.Add(this.ctrlNsfPlayer); + this.panelRenderer.Controls.Add(this.ctrlRenderer); + this.panelRenderer.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelRenderer.Location = new System.Drawing.Point(0, 24); + this.panelRenderer.Name = "panelRenderer"; + this.panelRenderer.Size = new System.Drawing.Size(430, 309); + this.panelRenderer.TabIndex = 2; + this.panelRenderer.Click += new System.EventHandler(this.panelRenderer_Click); + this.panelRenderer.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); + this.panelRenderer.MouseLeave += new System.EventHandler(this.panelRenderer_MouseLeave); + this.panelRenderer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); + // + // ctrlRendererDualSystem + // + this.ctrlRendererDualSystem.BackColor = System.Drawing.Color.Black; + this.ctrlRendererDualSystem.Location = new System.Drawing.Point(275, 0); + this.ctrlRendererDualSystem.Margin = new System.Windows.Forms.Padding(0); + this.ctrlRendererDualSystem.Name = "ctrlRendererDualSystem"; + this.ctrlRendererDualSystem.Size = new System.Drawing.Size(150, 90); + this.ctrlRendererDualSystem.TabIndex = 8; + this.ctrlRendererDualSystem.Visible = false; + // + // ctrlLoading + // + this.ctrlLoading.BackColor = System.Drawing.Color.Black; + this.ctrlLoading.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlLoading.Location = new System.Drawing.Point(0, 0); + this.ctrlLoading.Name = "ctrlLoading"; + this.ctrlLoading.Size = new System.Drawing.Size(430, 309); + this.ctrlLoading.TabIndex = 4; + this.ctrlLoading.Visible = false; + // + // panelInfo + // + this.panelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.panelInfo.BackColor = System.Drawing.Color.Transparent; + this.panelInfo.Controls.Add(this.tableLayoutPanel1); + this.panelInfo.Location = new System.Drawing.Point(353, 283); + this.panelInfo.Name = "panelInfo"; + this.panelInfo.Size = new System.Drawing.Size(77, 26); + this.panelInfo.TabIndex = 6; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.picIcon, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.lblVersion, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(77, 26); + this.tableLayoutPanel1.TabIndex = 6; + // + // picIcon + // + this.picIcon.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picIcon.BackgroundImage = global::Mesen.GUI.Properties.Resources.MesenIconSmall; + this.picIcon.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.picIcon.Location = new System.Drawing.Point(58, 5); + this.picIcon.Name = "picIcon"; + this.picIcon.Size = new System.Drawing.Size(16, 16); + this.picIcon.TabIndex = 5; + this.picIcon.TabStop = false; + // + // lblVersion + // + this.lblVersion.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.lblVersion.AutoSize = true; + this.lblVersion.BackColor = System.Drawing.Color.Transparent; + this.lblVersion.ForeColor = System.Drawing.Color.White; + this.lblVersion.Location = new System.Drawing.Point(55, 7); + this.lblVersion.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0); + this.lblVersion.Name = "lblVersion"; + this.lblVersion.Size = new System.Drawing.Size(0, 13); + this.lblVersion.TabIndex = 6; + this.lblVersion.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // ctrlNsfPlayer + // + this.ctrlNsfPlayer.BackColor = System.Drawing.Color.Black; + this.ctrlNsfPlayer.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlNsfPlayer.Location = new System.Drawing.Point(0, 0); + this.ctrlNsfPlayer.Name = "ctrlNsfPlayer"; + this.ctrlNsfPlayer.Size = new System.Drawing.Size(430, 309); + this.ctrlNsfPlayer.TabIndex = 2; + this.ctrlNsfPlayer.Visible = false; + this.ctrlNsfPlayer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlNsfPlayer_MouseMove); + // + // ctrlRenderer + // + this.ctrlRenderer.BackColor = System.Drawing.Color.Black; + this.ctrlRenderer.Location = new System.Drawing.Point(0, 0); + this.ctrlRenderer.Margin = new System.Windows.Forms.Padding(0); + this.ctrlRenderer.Name = "ctrlRenderer"; + this.ctrlRenderer.Size = new System.Drawing.Size(150, 90); + this.ctrlRenderer.TabIndex = 1; + this.ctrlRenderer.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); + this.ctrlRenderer.Enter += new System.EventHandler(this.ctrlRenderer_Enter); + this.ctrlRenderer.MouseClick += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseClick); + this.ctrlRenderer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); + // + // ctrlRecentGames + // + this.ctrlRecentGames.BackColor = System.Drawing.Color.Transparent; + this.ctrlRecentGames.Dock = System.Windows.Forms.DockStyle.Top; + this.ctrlRecentGames.Location = new System.Drawing.Point(0, 24); + this.ctrlRecentGames.Name = "ctrlRecentGames"; + this.ctrlRecentGames.Size = new System.Drawing.Size(430, 309); + this.ctrlRecentGames.TabIndex = 7; + this.ctrlRecentGames.Visible = false; + this.ctrlRecentGames.OnRecentGameLoaded += new Mesen.GUI.Controls.ctrlRecentGames.RecentGameLoadedHandler(this.ctrlRecentGames_OnRecentGameLoaded); + this.ctrlRecentGames.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); + this.ctrlRecentGames.DoubleClick += new System.EventHandler(this.ctrlRenderer_DoubleClick); + // + // menuStrip + // + this.menuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuFile, this.mnuGame, this.mnuOptions, this.mnuTools, this.mnuDebug, this.mnuHelp}); - this.menuStrip.Location = new System.Drawing.Point(0, 0); - this.menuStrip.Name = "menuStrip"; - this.menuStrip.Size = new System.Drawing.Size(430, 24); - this.menuStrip.TabIndex = 0; - this.menuStrip.Text = "menuStrip1"; - // - // mnuFile - // - this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.menuStrip.Location = new System.Drawing.Point(0, 0); + this.menuStrip.Name = "menuStrip"; + this.menuStrip.Size = new System.Drawing.Size(430, 24); + this.menuStrip.TabIndex = 0; + this.menuStrip.Text = "menuStrip1"; + // + // mnuFile + // + this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuOpen, this.mnuReloadRom, this.toolStripMenuItem4, @@ -387,79 +390,79 @@ private void InitializeComponent() this.mnuRecentFiles, this.toolStripMenuItem6, this.mnuExit}); - this.mnuFile.Name = "mnuFile"; - this.mnuFile.ShortcutKeyDisplayString = ""; - this.mnuFile.Size = new System.Drawing.Size(37, 20); - this.mnuFile.Text = "File"; - this.mnuFile.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuFile.DropDownOpening += new System.EventHandler(this.mnuFile_DropDownOpening); - this.mnuFile.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuOpen - // - this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.FolderOpen; - this.mnuOpen.Name = "mnuOpen"; - this.mnuOpen.Size = new System.Drawing.Size(166, 22); - this.mnuOpen.Text = "Open"; - // - // mnuReloadRom - // - this.mnuReloadRom.Name = "mnuReloadRom"; - this.mnuReloadRom.Size = new System.Drawing.Size(166, 22); - this.mnuReloadRom.Text = "Reload ROM"; - // - // toolStripMenuItem4 - // - this.toolStripMenuItem4.Name = "toolStripMenuItem4"; - this.toolStripMenuItem4.Size = new System.Drawing.Size(163, 6); - // - // mnuSaveState - // - this.mnuSaveState.Name = "mnuSaveState"; - this.mnuSaveState.Size = new System.Drawing.Size(166, 22); - this.mnuSaveState.Text = "Save State"; - this.mnuSaveState.DropDownOpening += new System.EventHandler(this.mnuSaveState_DropDownOpening); - // - // mnuLoadState - // - this.mnuLoadState.Name = "mnuLoadState"; - this.mnuLoadState.Size = new System.Drawing.Size(166, 22); - this.mnuLoadState.Text = "Load State"; - this.mnuLoadState.DropDownOpening += new System.EventHandler(this.mnuLoadState_DropDownOpening); - // - // mnuLoadLastSession - // - this.mnuLoadLastSession.Name = "mnuLoadLastSession"; - this.mnuLoadLastSession.Size = new System.Drawing.Size(166, 22); - this.mnuLoadLastSession.Text = "Load Last Session"; - // - // toolStripMenuItem7 - // - this.toolStripMenuItem7.Name = "toolStripMenuItem7"; - this.toolStripMenuItem7.Size = new System.Drawing.Size(163, 6); - // - // mnuRecentFiles - // - this.mnuRecentFiles.Name = "mnuRecentFiles"; - this.mnuRecentFiles.Size = new System.Drawing.Size(166, 22); - this.mnuRecentFiles.Text = "Recent Files"; - this.mnuRecentFiles.DropDownOpening += new System.EventHandler(this.mnuRecentFiles_DropDownOpening); - // - // toolStripMenuItem6 - // - this.toolStripMenuItem6.Name = "toolStripMenuItem6"; - this.toolStripMenuItem6.Size = new System.Drawing.Size(163, 6); - // - // mnuExit - // - this.mnuExit.Image = global::Mesen.GUI.Properties.Resources.Exit; - this.mnuExit.Name = "mnuExit"; - this.mnuExit.Size = new System.Drawing.Size(166, 22); - this.mnuExit.Text = "Exit"; - // - // mnuGame - // - this.mnuGame.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuFile.Name = "mnuFile"; + this.mnuFile.ShortcutKeyDisplayString = ""; + this.mnuFile.Size = new System.Drawing.Size(37, 20); + this.mnuFile.Text = "File"; + this.mnuFile.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuFile.DropDownOpening += new System.EventHandler(this.mnuFile_DropDownOpening); + this.mnuFile.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuOpen + // + this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.FolderOpen; + this.mnuOpen.Name = "mnuOpen"; + this.mnuOpen.Size = new System.Drawing.Size(166, 22); + this.mnuOpen.Text = "Open"; + // + // mnuReloadRom + // + this.mnuReloadRom.Name = "mnuReloadRom"; + this.mnuReloadRom.Size = new System.Drawing.Size(166, 22); + this.mnuReloadRom.Text = "Reload ROM"; + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(163, 6); + // + // mnuSaveState + // + this.mnuSaveState.Name = "mnuSaveState"; + this.mnuSaveState.Size = new System.Drawing.Size(166, 22); + this.mnuSaveState.Text = "Save State"; + this.mnuSaveState.DropDownOpening += new System.EventHandler(this.mnuSaveState_DropDownOpening); + // + // mnuLoadState + // + this.mnuLoadState.Name = "mnuLoadState"; + this.mnuLoadState.Size = new System.Drawing.Size(166, 22); + this.mnuLoadState.Text = "Load State"; + this.mnuLoadState.DropDownOpening += new System.EventHandler(this.mnuLoadState_DropDownOpening); + // + // mnuLoadLastSession + // + this.mnuLoadLastSession.Name = "mnuLoadLastSession"; + this.mnuLoadLastSession.Size = new System.Drawing.Size(166, 22); + this.mnuLoadLastSession.Text = "Load Last Session"; + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(163, 6); + // + // mnuRecentFiles + // + this.mnuRecentFiles.Name = "mnuRecentFiles"; + this.mnuRecentFiles.Size = new System.Drawing.Size(166, 22); + this.mnuRecentFiles.Text = "Recent Files"; + this.mnuRecentFiles.DropDownOpening += new System.EventHandler(this.mnuRecentFiles_DropDownOpening); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(163, 6); + // + // mnuExit + // + this.mnuExit.Image = global::Mesen.GUI.Properties.Resources.Exit; + this.mnuExit.Name = "mnuExit"; + this.mnuExit.Size = new System.Drawing.Size(166, 22); + this.mnuExit.Text = "Exit"; + // + // mnuGame + // + this.mnuGame.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuPause, this.mnuReset, this.mnuPowerCycle, @@ -478,172 +481,172 @@ private void InitializeComponent() this.sepBarcode, this.mnuInputBarcode, this.mnuTapeRecorder}); - this.mnuGame.Name = "mnuGame"; - this.mnuGame.Size = new System.Drawing.Size(50, 20); - this.mnuGame.Text = "Game"; - this.mnuGame.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuGame.DropDownOpening += new System.EventHandler(this.mnuGame_DropDownOpening); - this.mnuGame.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuPause - // - this.mnuPause.Enabled = false; - this.mnuPause.Image = global::Mesen.GUI.Properties.Resources.Pause; - this.mnuPause.Name = "mnuPause"; - this.mnuPause.Size = new System.Drawing.Size(221, 22); - this.mnuPause.Text = "Pause"; - // - // mnuReset - // - this.mnuReset.Enabled = false; - this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Reset; - this.mnuReset.Name = "mnuReset"; - this.mnuReset.Size = new System.Drawing.Size(221, 22); - this.mnuReset.Text = "Reset"; - // - // mnuPowerCycle - // - this.mnuPowerCycle.Enabled = false; - this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle; - this.mnuPowerCycle.Name = "mnuPowerCycle"; - this.mnuPowerCycle.Size = new System.Drawing.Size(221, 22); - this.mnuPowerCycle.Text = "Power Cycle"; - // - // toolStripMenuItem24 - // - this.toolStripMenuItem24.Name = "toolStripMenuItem24"; - this.toolStripMenuItem24.Size = new System.Drawing.Size(218, 6); - // - // mnuPowerOff - // - this.mnuPowerOff.Image = global::Mesen.GUI.Properties.Resources.Stop; - this.mnuPowerOff.Name = "mnuPowerOff"; - this.mnuPowerOff.Size = new System.Drawing.Size(221, 22); - this.mnuPowerOff.Text = "Power Off"; - // - // sepFdsDisk - // - this.sepFdsDisk.Name = "sepFdsDisk"; - this.sepFdsDisk.Size = new System.Drawing.Size(218, 6); - // - // mnuSwitchDiskSide - // - this.mnuSwitchDiskSide.Name = "mnuSwitchDiskSide"; - this.mnuSwitchDiskSide.Size = new System.Drawing.Size(221, 22); - this.mnuSwitchDiskSide.Text = "Switch Disk Side"; - // - // mnuSelectDisk - // - this.mnuSelectDisk.Image = global::Mesen.GUI.Properties.Resources.Floppy; - this.mnuSelectDisk.Name = "mnuSelectDisk"; - this.mnuSelectDisk.Size = new System.Drawing.Size(221, 22); - this.mnuSelectDisk.Text = "Select Disk"; - // - // mnuEjectDisk - // - this.mnuEjectDisk.Image = global::Mesen.GUI.Properties.Resources.Eject; - this.mnuEjectDisk.Name = "mnuEjectDisk"; - this.mnuEjectDisk.Size = new System.Drawing.Size(221, 22); - this.mnuEjectDisk.Text = "Eject Disk"; - // - // sepVsSystem - // - this.sepVsSystem.Name = "sepVsSystem"; - this.sepVsSystem.Size = new System.Drawing.Size(218, 6); - this.sepVsSystem.Visible = false; - // - // mnuGameConfig - // - this.mnuGameConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; - this.mnuGameConfig.Name = "mnuGameConfig"; - this.mnuGameConfig.Size = new System.Drawing.Size(221, 22); - this.mnuGameConfig.Text = "Game Configuration"; - this.mnuGameConfig.Click += new System.EventHandler(this.mnuGameConfig_Click); - // - // mnuInsertCoin1 - // - this.mnuInsertCoin1.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin1.Name = "mnuInsertCoin1"; - this.mnuInsertCoin1.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin1.Text = "Insert Coin (1)"; - this.mnuInsertCoin1.Visible = false; - // - // mnuInsertCoin2 - // - this.mnuInsertCoin2.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin2.Name = "mnuInsertCoin2"; - this.mnuInsertCoin2.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin2.Text = "Insert Coin (2)"; - this.mnuInsertCoin2.Visible = false; - // - // mnuInsertCoin3 - // - this.mnuInsertCoin3.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin3.Name = "mnuInsertCoin3"; - this.mnuInsertCoin3.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin3.Text = "Insert Coin (3 - DualSystem)"; - this.mnuInsertCoin3.Visible = false; - // - // mnuInsertCoin4 - // - this.mnuInsertCoin4.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin4.Name = "mnuInsertCoin4"; - this.mnuInsertCoin4.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin4.Text = "Insert Coin (4 - DualSystem)"; - this.mnuInsertCoin4.Visible = false; - // - // sepBarcode - // - this.sepBarcode.Name = "sepBarcode"; - this.sepBarcode.Size = new System.Drawing.Size(218, 6); - this.sepBarcode.Visible = false; - // - // mnuInputBarcode - // - this.mnuInputBarcode.Image = global::Mesen.GUI.Properties.Resources.Barcode; - this.mnuInputBarcode.Name = "mnuInputBarcode"; - this.mnuInputBarcode.Size = new System.Drawing.Size(221, 22); - this.mnuInputBarcode.Text = "Input Barcode..."; - this.mnuInputBarcode.Visible = false; - // - // mnuTapeRecorder - // - this.mnuTapeRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuGame.Name = "mnuGame"; + this.mnuGame.Size = new System.Drawing.Size(50, 20); + this.mnuGame.Text = "Game"; + this.mnuGame.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuGame.DropDownOpening += new System.EventHandler(this.mnuGame_DropDownOpening); + this.mnuGame.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuPause + // + this.mnuPause.Enabled = false; + this.mnuPause.Image = global::Mesen.GUI.Properties.Resources.Pause; + this.mnuPause.Name = "mnuPause"; + this.mnuPause.Size = new System.Drawing.Size(221, 22); + this.mnuPause.Text = "Pause"; + // + // mnuReset + // + this.mnuReset.Enabled = false; + this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Reset; + this.mnuReset.Name = "mnuReset"; + this.mnuReset.Size = new System.Drawing.Size(221, 22); + this.mnuReset.Text = "Reset"; + // + // mnuPowerCycle + // + this.mnuPowerCycle.Enabled = false; + this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle; + this.mnuPowerCycle.Name = "mnuPowerCycle"; + this.mnuPowerCycle.Size = new System.Drawing.Size(221, 22); + this.mnuPowerCycle.Text = "Power Cycle"; + // + // toolStripMenuItem24 + // + this.toolStripMenuItem24.Name = "toolStripMenuItem24"; + this.toolStripMenuItem24.Size = new System.Drawing.Size(218, 6); + // + // mnuPowerOff + // + this.mnuPowerOff.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.mnuPowerOff.Name = "mnuPowerOff"; + this.mnuPowerOff.Size = new System.Drawing.Size(221, 22); + this.mnuPowerOff.Text = "Power Off"; + // + // sepFdsDisk + // + this.sepFdsDisk.Name = "sepFdsDisk"; + this.sepFdsDisk.Size = new System.Drawing.Size(218, 6); + // + // mnuSwitchDiskSide + // + this.mnuSwitchDiskSide.Name = "mnuSwitchDiskSide"; + this.mnuSwitchDiskSide.Size = new System.Drawing.Size(221, 22); + this.mnuSwitchDiskSide.Text = "Switch Disk Side"; + // + // mnuSelectDisk + // + this.mnuSelectDisk.Image = global::Mesen.GUI.Properties.Resources.Floppy; + this.mnuSelectDisk.Name = "mnuSelectDisk"; + this.mnuSelectDisk.Size = new System.Drawing.Size(221, 22); + this.mnuSelectDisk.Text = "Select Disk"; + // + // mnuEjectDisk + // + this.mnuEjectDisk.Image = global::Mesen.GUI.Properties.Resources.Eject; + this.mnuEjectDisk.Name = "mnuEjectDisk"; + this.mnuEjectDisk.Size = new System.Drawing.Size(221, 22); + this.mnuEjectDisk.Text = "Eject Disk"; + // + // sepVsSystem + // + this.sepVsSystem.Name = "sepVsSystem"; + this.sepVsSystem.Size = new System.Drawing.Size(218, 6); + this.sepVsSystem.Visible = false; + // + // mnuGameConfig + // + this.mnuGameConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; + this.mnuGameConfig.Name = "mnuGameConfig"; + this.mnuGameConfig.Size = new System.Drawing.Size(221, 22); + this.mnuGameConfig.Text = "Game Configuration"; + this.mnuGameConfig.Click += new System.EventHandler(this.mnuGameConfig_Click); + // + // mnuInsertCoin1 + // + this.mnuInsertCoin1.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin1.Name = "mnuInsertCoin1"; + this.mnuInsertCoin1.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin1.Text = "Insert Coin (1)"; + this.mnuInsertCoin1.Visible = false; + // + // mnuInsertCoin2 + // + this.mnuInsertCoin2.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin2.Name = "mnuInsertCoin2"; + this.mnuInsertCoin2.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin2.Text = "Insert Coin (2)"; + this.mnuInsertCoin2.Visible = false; + // + // mnuInsertCoin3 + // + this.mnuInsertCoin3.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin3.Name = "mnuInsertCoin3"; + this.mnuInsertCoin3.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin3.Text = "Insert Coin (3 - DualSystem)"; + this.mnuInsertCoin3.Visible = false; + // + // mnuInsertCoin4 + // + this.mnuInsertCoin4.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin4.Name = "mnuInsertCoin4"; + this.mnuInsertCoin4.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin4.Text = "Insert Coin (4 - DualSystem)"; + this.mnuInsertCoin4.Visible = false; + // + // sepBarcode + // + this.sepBarcode.Name = "sepBarcode"; + this.sepBarcode.Size = new System.Drawing.Size(218, 6); + this.sepBarcode.Visible = false; + // + // mnuInputBarcode + // + this.mnuInputBarcode.Image = global::Mesen.GUI.Properties.Resources.Barcode; + this.mnuInputBarcode.Name = "mnuInputBarcode"; + this.mnuInputBarcode.Size = new System.Drawing.Size(221, 22); + this.mnuInputBarcode.Text = "Input Barcode..."; + this.mnuInputBarcode.Visible = false; + // + // mnuTapeRecorder + // + this.mnuTapeRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuLoadTapeFile, this.mnuStartRecordTapeFile, this.mnuStopRecordTapeFile}); - this.mnuTapeRecorder.Image = global::Mesen.GUI.Properties.Resources.Tape; - this.mnuTapeRecorder.Name = "mnuTapeRecorder"; - this.mnuTapeRecorder.Size = new System.Drawing.Size(221, 22); - this.mnuTapeRecorder.Text = "Tape Recorder"; - // - // mnuLoadTapeFile - // - this.mnuLoadTapeFile.Image = global::Mesen.GUI.Properties.Resources.Import; - this.mnuLoadTapeFile.Name = "mnuLoadTapeFile"; - this.mnuLoadTapeFile.Size = new System.Drawing.Size(157, 22); - this.mnuLoadTapeFile.Text = "Load from file..."; - this.mnuLoadTapeFile.Click += new System.EventHandler(this.mnuLoadTapeFile_Click); - // - // mnuStartRecordTapeFile - // - this.mnuStartRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Export; - this.mnuStartRecordTapeFile.Name = "mnuStartRecordTapeFile"; - this.mnuStartRecordTapeFile.Size = new System.Drawing.Size(157, 22); - this.mnuStartRecordTapeFile.Text = "Record to file..."; - this.mnuStartRecordTapeFile.Click += new System.EventHandler(this.mnuStartRecordTapeFile_Click); - // - // mnuStopRecordTapeFile - // - this.mnuStopRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Stop; - this.mnuStopRecordTapeFile.Name = "mnuStopRecordTapeFile"; - this.mnuStopRecordTapeFile.Size = new System.Drawing.Size(157, 22); - this.mnuStopRecordTapeFile.Text = "Stop recording"; - this.mnuStopRecordTapeFile.Click += new System.EventHandler(this.mnuStopRecordTapeFile_Click); - // - // mnuOptions - // - this.mnuOptions.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuTapeRecorder.Image = global::Mesen.GUI.Properties.Resources.Tape; + this.mnuTapeRecorder.Name = "mnuTapeRecorder"; + this.mnuTapeRecorder.Size = new System.Drawing.Size(221, 22); + this.mnuTapeRecorder.Text = "Tape Recorder"; + // + // mnuLoadTapeFile + // + this.mnuLoadTapeFile.Image = global::Mesen.GUI.Properties.Resources.Import; + this.mnuLoadTapeFile.Name = "mnuLoadTapeFile"; + this.mnuLoadTapeFile.Size = new System.Drawing.Size(157, 22); + this.mnuLoadTapeFile.Text = "Load from file..."; + this.mnuLoadTapeFile.Click += new System.EventHandler(this.mnuLoadTapeFile_Click); + // + // mnuStartRecordTapeFile + // + this.mnuStartRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Export; + this.mnuStartRecordTapeFile.Name = "mnuStartRecordTapeFile"; + this.mnuStartRecordTapeFile.Size = new System.Drawing.Size(157, 22); + this.mnuStartRecordTapeFile.Text = "Record to file..."; + this.mnuStartRecordTapeFile.Click += new System.EventHandler(this.mnuStartRecordTapeFile_Click); + // + // mnuStopRecordTapeFile + // + this.mnuStopRecordTapeFile.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.mnuStopRecordTapeFile.Name = "mnuStopRecordTapeFile"; + this.mnuStopRecordTapeFile.Size = new System.Drawing.Size(157, 22); + this.mnuStopRecordTapeFile.Text = "Stop recording"; + this.mnuStopRecordTapeFile.Click += new System.EventHandler(this.mnuStopRecordTapeFile_Click); + // + // mnuOptions + // + this.mnuOptions.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuEmulationSpeed, this.mnuVideoScale, this.mnuVideoFilter, @@ -655,15 +658,15 @@ private void InitializeComponent() this.mnuEmulationConfig, this.toolStripMenuItem11, this.mnuPreferences}); - this.mnuOptions.Name = "mnuOptions"; - this.mnuOptions.Size = new System.Drawing.Size(61, 20); - this.mnuOptions.Text = "Options"; - this.mnuOptions.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuOptions.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuEmulationSpeed - // - this.mnuEmulationSpeed.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuOptions.Name = "mnuOptions"; + this.mnuOptions.Size = new System.Drawing.Size(61, 20); + this.mnuOptions.Text = "Options"; + this.mnuOptions.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuOptions.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuEmulationSpeed + // + this.mnuEmulationSpeed.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuEmuSpeedNormal, this.toolStripMenuItem8, this.mnuIncreaseSpeed, @@ -676,92 +679,92 @@ private void InitializeComponent() this.mnuEmuSpeedQuarter, this.toolStripMenuItem14, this.mnuShowFPS}); - this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed; - this.mnuEmulationSpeed.Name = "mnuEmulationSpeed"; - this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22); - this.mnuEmulationSpeed.Text = "Speed"; - this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening); - // - // mnuEmuSpeedNormal - // - this.mnuEmuSpeedNormal.Name = "mnuEmuSpeedNormal"; - this.mnuEmuSpeedNormal.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedNormal.Text = "Normal (100%)"; - this.mnuEmuSpeedNormal.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); - // - // toolStripMenuItem8 - // - this.toolStripMenuItem8.Name = "toolStripMenuItem8"; - this.toolStripMenuItem8.Size = new System.Drawing.Size(160, 6); - // - // mnuIncreaseSpeed - // - this.mnuIncreaseSpeed.Name = "mnuIncreaseSpeed"; - this.mnuIncreaseSpeed.Size = new System.Drawing.Size(163, 22); - this.mnuIncreaseSpeed.Text = "Increase Speed"; - // - // mnuDecreaseSpeed - // - this.mnuDecreaseSpeed.Name = "mnuDecreaseSpeed"; - this.mnuDecreaseSpeed.Size = new System.Drawing.Size(163, 22); - this.mnuDecreaseSpeed.Text = "Decrease Speed"; - // - // mnuEmuSpeedMaximumSpeed - // - this.mnuEmuSpeedMaximumSpeed.Name = "mnuEmuSpeedMaximumSpeed"; - this.mnuEmuSpeedMaximumSpeed.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedMaximumSpeed.Text = "Maximum Speed"; - // - // toolStripMenuItem9 - // - this.toolStripMenuItem9.Name = "toolStripMenuItem9"; - this.toolStripMenuItem9.Size = new System.Drawing.Size(160, 6); - // - // mnuEmuSpeedTriple - // - this.mnuEmuSpeedTriple.Name = "mnuEmuSpeedTriple"; - this.mnuEmuSpeedTriple.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedTriple.Tag = ""; - this.mnuEmuSpeedTriple.Text = "Triple (300%)"; - this.mnuEmuSpeedTriple.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); - // - // mnuEmuSpeedDouble - // - this.mnuEmuSpeedDouble.Name = "mnuEmuSpeedDouble"; - this.mnuEmuSpeedDouble.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedDouble.Text = "Double (200%)"; - this.mnuEmuSpeedDouble.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); - // - // mnuEmuSpeedHalf - // - this.mnuEmuSpeedHalf.Name = "mnuEmuSpeedHalf"; - this.mnuEmuSpeedHalf.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedHalf.Text = "Half (50%)"; - this.mnuEmuSpeedHalf.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); - // - // mnuEmuSpeedQuarter - // - this.mnuEmuSpeedQuarter.Name = "mnuEmuSpeedQuarter"; - this.mnuEmuSpeedQuarter.Size = new System.Drawing.Size(163, 22); - this.mnuEmuSpeedQuarter.Text = "Quarter (25%)"; - this.mnuEmuSpeedQuarter.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); - // - // toolStripMenuItem14 - // - this.toolStripMenuItem14.Name = "toolStripMenuItem14"; - this.toolStripMenuItem14.Size = new System.Drawing.Size(160, 6); - // - // mnuShowFPS - // - this.mnuShowFPS.CheckOnClick = true; - this.mnuShowFPS.Name = "mnuShowFPS"; - this.mnuShowFPS.Size = new System.Drawing.Size(163, 22); - this.mnuShowFPS.Text = "Show FPS"; - this.mnuShowFPS.Click += new System.EventHandler(this.mnuShowFPS_Click); - // - // mnuVideoScale - // - this.mnuVideoScale.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed; + this.mnuEmulationSpeed.Name = "mnuEmulationSpeed"; + this.mnuEmulationSpeed.Size = new System.Drawing.Size(180, 22); + this.mnuEmulationSpeed.Text = "Speed"; + this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening); + // + // mnuEmuSpeedNormal + // + this.mnuEmuSpeedNormal.Name = "mnuEmuSpeedNormal"; + this.mnuEmuSpeedNormal.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedNormal.Text = "Normal (100%)"; + this.mnuEmuSpeedNormal.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(161, 6); + // + // mnuIncreaseSpeed + // + this.mnuIncreaseSpeed.Name = "mnuIncreaseSpeed"; + this.mnuIncreaseSpeed.Size = new System.Drawing.Size(164, 22); + this.mnuIncreaseSpeed.Text = "Increase Speed"; + // + // mnuDecreaseSpeed + // + this.mnuDecreaseSpeed.Name = "mnuDecreaseSpeed"; + this.mnuDecreaseSpeed.Size = new System.Drawing.Size(164, 22); + this.mnuDecreaseSpeed.Text = "Decrease Speed"; + // + // mnuEmuSpeedMaximumSpeed + // + this.mnuEmuSpeedMaximumSpeed.Name = "mnuEmuSpeedMaximumSpeed"; + this.mnuEmuSpeedMaximumSpeed.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedMaximumSpeed.Text = "Maximum Speed"; + // + // toolStripMenuItem9 + // + this.toolStripMenuItem9.Name = "toolStripMenuItem9"; + this.toolStripMenuItem9.Size = new System.Drawing.Size(161, 6); + // + // mnuEmuSpeedTriple + // + this.mnuEmuSpeedTriple.Name = "mnuEmuSpeedTriple"; + this.mnuEmuSpeedTriple.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedTriple.Tag = ""; + this.mnuEmuSpeedTriple.Text = "Triple (300%)"; + this.mnuEmuSpeedTriple.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); + // + // mnuEmuSpeedDouble + // + this.mnuEmuSpeedDouble.Name = "mnuEmuSpeedDouble"; + this.mnuEmuSpeedDouble.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedDouble.Text = "Double (200%)"; + this.mnuEmuSpeedDouble.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); + // + // mnuEmuSpeedHalf + // + this.mnuEmuSpeedHalf.Name = "mnuEmuSpeedHalf"; + this.mnuEmuSpeedHalf.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedHalf.Text = "Half (50%)"; + this.mnuEmuSpeedHalf.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); + // + // mnuEmuSpeedQuarter + // + this.mnuEmuSpeedQuarter.Name = "mnuEmuSpeedQuarter"; + this.mnuEmuSpeedQuarter.Size = new System.Drawing.Size(164, 22); + this.mnuEmuSpeedQuarter.Text = "Quarter (25%)"; + this.mnuEmuSpeedQuarter.Click += new System.EventHandler(this.mnuEmulationSpeedOption_Click); + // + // toolStripMenuItem14 + // + this.toolStripMenuItem14.Name = "toolStripMenuItem14"; + this.toolStripMenuItem14.Size = new System.Drawing.Size(161, 6); + // + // mnuShowFPS + // + this.mnuShowFPS.CheckOnClick = true; + this.mnuShowFPS.Name = "mnuShowFPS"; + this.mnuShowFPS.Size = new System.Drawing.Size(164, 22); + this.mnuShowFPS.Text = "Show FPS"; + this.mnuShowFPS.Click += new System.EventHandler(this.mnuShowFPS_Click); + // + // mnuVideoScale + // + this.mnuVideoScale.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuScale1x, this.mnuScale2x, this.mnuScale3x, @@ -770,67 +773,70 @@ private void InitializeComponent() this.mnuScale6x, this.toolStripMenuItem13, this.mnuFullscreen}); - this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen; - this.mnuVideoScale.Name = "mnuVideoScale"; - this.mnuVideoScale.Size = new System.Drawing.Size(135, 22); - this.mnuVideoScale.Text = "Video Size"; - // - // mnuScale1x - // - this.mnuScale1x.Name = "mnuScale1x"; - this.mnuScale1x.Size = new System.Drawing.Size(127, 22); - this.mnuScale1x.Text = "1x"; - // - // mnuScale2x - // - this.mnuScale2x.Name = "mnuScale2x"; - this.mnuScale2x.Size = new System.Drawing.Size(127, 22); - this.mnuScale2x.Text = "2x"; - // - // mnuScale3x - // - this.mnuScale3x.Name = "mnuScale3x"; - this.mnuScale3x.Size = new System.Drawing.Size(127, 22); - this.mnuScale3x.Text = "3x"; - // - // mnuScale4x - // - this.mnuScale4x.Name = "mnuScale4x"; - this.mnuScale4x.Size = new System.Drawing.Size(127, 22); - this.mnuScale4x.Text = "4x"; - // - // mnuScale5x - // - this.mnuScale5x.Name = "mnuScale5x"; - this.mnuScale5x.Size = new System.Drawing.Size(127, 22); - this.mnuScale5x.Text = "5x"; - // - // mnuScale6x - // - this.mnuScale6x.Name = "mnuScale6x"; - this.mnuScale6x.Size = new System.Drawing.Size(127, 22); - this.mnuScale6x.Text = "6x"; - // - // toolStripMenuItem13 - // - this.toolStripMenuItem13.Name = "toolStripMenuItem13"; - this.toolStripMenuItem13.Size = new System.Drawing.Size(124, 6); - // - // mnuFullscreen - // - this.mnuFullscreen.Name = "mnuFullscreen"; - this.mnuFullscreen.Size = new System.Drawing.Size(127, 22); - this.mnuFullscreen.Text = "Fullscreen"; - // - // mnuVideoFilter - // - this.mnuVideoFilter.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen; + this.mnuVideoScale.Name = "mnuVideoScale"; + this.mnuVideoScale.Size = new System.Drawing.Size(180, 22); + this.mnuVideoScale.Text = "Video Size"; + // + // mnuScale1x + // + this.mnuScale1x.Name = "mnuScale1x"; + this.mnuScale1x.Size = new System.Drawing.Size(127, 22); + this.mnuScale1x.Text = "1x"; + // + // mnuScale2x + // + this.mnuScale2x.Name = "mnuScale2x"; + this.mnuScale2x.Size = new System.Drawing.Size(127, 22); + this.mnuScale2x.Text = "2x"; + // + // mnuScale3x + // + this.mnuScale3x.Name = "mnuScale3x"; + this.mnuScale3x.Size = new System.Drawing.Size(127, 22); + this.mnuScale3x.Text = "3x"; + // + // mnuScale4x + // + this.mnuScale4x.Name = "mnuScale4x"; + this.mnuScale4x.Size = new System.Drawing.Size(127, 22); + this.mnuScale4x.Text = "4x"; + // + // mnuScale5x + // + this.mnuScale5x.Name = "mnuScale5x"; + this.mnuScale5x.Size = new System.Drawing.Size(127, 22); + this.mnuScale5x.Text = "5x"; + // + // mnuScale6x + // + this.mnuScale6x.Name = "mnuScale6x"; + this.mnuScale6x.Size = new System.Drawing.Size(127, 22); + this.mnuScale6x.Text = "6x"; + // + // toolStripMenuItem13 + // + this.toolStripMenuItem13.Name = "toolStripMenuItem13"; + this.toolStripMenuItem13.Size = new System.Drawing.Size(124, 6); + // + // mnuFullscreen + // + this.mnuFullscreen.Name = "mnuFullscreen"; + this.mnuFullscreen.Size = new System.Drawing.Size(127, 22); + this.mnuFullscreen.Text = "Fullscreen"; + // + // mnuVideoFilter + // + this.mnuVideoFilter.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuNoneFilter, this.toolStripMenuItem21, this.mnuNtscFilter, this.mnuNtscBisqwitQuarterFilter, this.mnuNtscBisqwitHalfFilter, this.mnuNtscBisqwitFullFilter, + this.mnuNtscLMP88959QuarterFilter, + this.mnuNtscLMP88959HalfFilter, + this.mnuNtscLMP88959FullFilter, this.toolStripMenuItem15, this.mnuXBRZ2xFilter, this.mnuXBRZ3xFilter, @@ -858,322 +864,343 @@ private void InitializeComponent() this.mnuPrescale10xFilter, this.toolStripMenuItem19, this.mnuBilinearInterpolation}); - this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; - this.mnuVideoFilter.Name = "mnuVideoFilter"; - this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22); - this.mnuVideoFilter.Text = "Video Filter"; - // - // mnuNoneFilter - // - this.mnuNoneFilter.Name = "mnuNoneFilter"; - this.mnuNoneFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNoneFilter.Text = "None"; - this.mnuNoneFilter.Click += new System.EventHandler(this.mnuNoneFilter_Click); - // - // toolStripMenuItem21 - // - this.toolStripMenuItem21.Name = "toolStripMenuItem21"; - this.toolStripMenuItem21.Size = new System.Drawing.Size(203, 6); - // - // mnuNtscFilter - // - this.mnuNtscFilter.Name = "mnuNtscFilter"; - this.mnuNtscFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscFilter.Text = "NTSC"; - this.mnuNtscFilter.Click += new System.EventHandler(this.mnuNtscFilter_Click); - // - // mnuNtscBisqwitQuarterFilter - // - this.mnuNtscBisqwitQuarterFilter.Name = "mnuNtscBisqwitQuarterFilter"; - this.mnuNtscBisqwitQuarterFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscBisqwitQuarterFilter.Text = "NTSC 2x (Bisqwit)"; - this.mnuNtscBisqwitQuarterFilter.Click += new System.EventHandler(this.mnuNtscBisqwitQuarterFilter_Click); - // - // mnuNtscBisqwitHalfFilter - // - this.mnuNtscBisqwitHalfFilter.Name = "mnuNtscBisqwitHalfFilter"; - this.mnuNtscBisqwitHalfFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscBisqwitHalfFilter.Text = "NTSC 4x (Bisqwit)"; - this.mnuNtscBisqwitHalfFilter.Click += new System.EventHandler(this.mnuNtscBisqwitHalfFilter_Click); - // - // mnuNtscBisqwitFullFilter - // - this.mnuNtscBisqwitFullFilter.Name = "mnuNtscBisqwitFullFilter"; - this.mnuNtscBisqwitFullFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscBisqwitFullFilter.Text = "NTSC 8x (Bisqwit)"; - this.mnuNtscBisqwitFullFilter.Click += new System.EventHandler(this.mnuNtscBisqwitFullFilter_Click); - // - // toolStripMenuItem15 - // - this.toolStripMenuItem15.Name = "toolStripMenuItem15"; - this.toolStripMenuItem15.Size = new System.Drawing.Size(203, 6); - // - // mnuXBRZ2xFilter - // - this.mnuXBRZ2xFilter.Name = "mnuXBRZ2xFilter"; - this.mnuXBRZ2xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuXBRZ2xFilter.Text = "xBRZ 2x"; - this.mnuXBRZ2xFilter.Click += new System.EventHandler(this.mnuXBRZ2xFilter_Click); - // - // mnuXBRZ3xFilter - // - this.mnuXBRZ3xFilter.Name = "mnuXBRZ3xFilter"; - this.mnuXBRZ3xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuXBRZ3xFilter.Text = "xBRZ 3x"; - this.mnuXBRZ3xFilter.Click += new System.EventHandler(this.mnuXBRZ3xFilter_Click); - // - // mnuXBRZ4xFilter - // - this.mnuXBRZ4xFilter.Name = "mnuXBRZ4xFilter"; - this.mnuXBRZ4xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuXBRZ4xFilter.Text = "xBRZ 4x"; - this.mnuXBRZ4xFilter.Click += new System.EventHandler(this.mnuXBRZ4xFilter_Click); - // - // mnuXBRZ5xFilter - // - this.mnuXBRZ5xFilter.Name = "mnuXBRZ5xFilter"; - this.mnuXBRZ5xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuXBRZ5xFilter.Text = "xBRZ 5x"; - this.mnuXBRZ5xFilter.Click += new System.EventHandler(this.mnuXBRZ5xFilter_Click); - // - // mnuXBRZ6xFilter - // - this.mnuXBRZ6xFilter.Name = "mnuXBRZ6xFilter"; - this.mnuXBRZ6xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuXBRZ6xFilter.Text = "xBRZ 6x"; - this.mnuXBRZ6xFilter.Click += new System.EventHandler(this.mnuXBRZ6xFilter_Click); - // - // toolStripMenuItem16 - // - this.toolStripMenuItem16.Name = "toolStripMenuItem16"; - this.toolStripMenuItem16.Size = new System.Drawing.Size(203, 6); - // - // mnuHQ2xFilter - // - this.mnuHQ2xFilter.Name = "mnuHQ2xFilter"; - this.mnuHQ2xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuHQ2xFilter.Text = "HQ 2x"; - this.mnuHQ2xFilter.Click += new System.EventHandler(this.mnuHQ2xFilter_Click); - // - // mnuHQ3xFilter - // - this.mnuHQ3xFilter.Name = "mnuHQ3xFilter"; - this.mnuHQ3xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuHQ3xFilter.Text = "HQ 3x"; - this.mnuHQ3xFilter.Click += new System.EventHandler(this.mnuHQ3xFilter_Click); - // - // mnuHQ4xFilter - // - this.mnuHQ4xFilter.Name = "mnuHQ4xFilter"; - this.mnuHQ4xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuHQ4xFilter.Text = "HQ 4x"; - this.mnuHQ4xFilter.Click += new System.EventHandler(this.mnuHQ4xFilter_Click); - // - // toolStripMenuItem17 - // - this.toolStripMenuItem17.Name = "toolStripMenuItem17"; - this.toolStripMenuItem17.Size = new System.Drawing.Size(203, 6); - // - // mnuScale2xFilter - // - this.mnuScale2xFilter.Name = "mnuScale2xFilter"; - this.mnuScale2xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuScale2xFilter.Text = "Scale2x"; - this.mnuScale2xFilter.Click += new System.EventHandler(this.mnuScale2xFilter_Click); - // - // mnuScale3xFilter - // - this.mnuScale3xFilter.Name = "mnuScale3xFilter"; - this.mnuScale3xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuScale3xFilter.Text = "Scale3x"; - this.mnuScale3xFilter.Click += new System.EventHandler(this.mnuScale3xFilter_Click); - // - // mnuScale4xFilter - // - this.mnuScale4xFilter.Name = "mnuScale4xFilter"; - this.mnuScale4xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuScale4xFilter.Text = "Scale4x"; - this.mnuScale4xFilter.Click += new System.EventHandler(this.mnuScale4xFilter_Click); - // - // toolStripMenuItem23 - // - this.toolStripMenuItem23.Name = "toolStripMenuItem23"; - this.toolStripMenuItem23.Size = new System.Drawing.Size(203, 6); - // - // mnu2xSaiFilter - // - this.mnu2xSaiFilter.Name = "mnu2xSaiFilter"; - this.mnu2xSaiFilter.Size = new System.Drawing.Size(206, 22); - this.mnu2xSaiFilter.Text = "2xSai"; - this.mnu2xSaiFilter.Click += new System.EventHandler(this.mnu2xSaiFilter_Click); - // - // mnuSuper2xSaiFilter - // - this.mnuSuper2xSaiFilter.Name = "mnuSuper2xSaiFilter"; - this.mnuSuper2xSaiFilter.Size = new System.Drawing.Size(206, 22); - this.mnuSuper2xSaiFilter.Text = "Super2xSai"; - this.mnuSuper2xSaiFilter.Click += new System.EventHandler(this.mnuSuper2xSaiFilter_Click); - // - // mnuSuperEagleFilter - // - this.mnuSuperEagleFilter.Name = "mnuSuperEagleFilter"; - this.mnuSuperEagleFilter.Size = new System.Drawing.Size(206, 22); - this.mnuSuperEagleFilter.Text = "SuperEagle"; - this.mnuSuperEagleFilter.Click += new System.EventHandler(this.mnuSuperEagleFilter_Click); - // - // toolStripMenuItem18 - // - this.toolStripMenuItem18.Name = "toolStripMenuItem18"; - this.toolStripMenuItem18.Size = new System.Drawing.Size(203, 6); - // - // mnuPrescale2xFilter - // - this.mnuPrescale2xFilter.Name = "mnuPrescale2xFilter"; - this.mnuPrescale2xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale2xFilter.Text = "Prescale 2x"; - this.mnuPrescale2xFilter.Click += new System.EventHandler(this.mnuPrescale2xFilter_Click); - // - // mnuPrescale3xFilter - // - this.mnuPrescale3xFilter.Name = "mnuPrescale3xFilter"; - this.mnuPrescale3xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale3xFilter.Text = "Prescale 3x"; - this.mnuPrescale3xFilter.Click += new System.EventHandler(this.mnuPrescale3xFilter_Click); - // - // mnuPrescale4xFilter - // - this.mnuPrescale4xFilter.Name = "mnuPrescale4xFilter"; - this.mnuPrescale4xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale4xFilter.Text = "Prescale 4x"; - this.mnuPrescale4xFilter.Click += new System.EventHandler(this.mnuPrescale4xFilter_Click); - // - // mnuPrescale6xFilter - // - this.mnuPrescale6xFilter.Name = "mnuPrescale6xFilter"; - this.mnuPrescale6xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale6xFilter.Text = "Prescale 6x"; - this.mnuPrescale6xFilter.Click += new System.EventHandler(this.mnuPrescale6xFilter_Click); - // - // mnuPrescale8xFilter - // - this.mnuPrescale8xFilter.Name = "mnuPrescale8xFilter"; - this.mnuPrescale8xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale8xFilter.Text = "Prescale 8x"; - this.mnuPrescale8xFilter.Click += new System.EventHandler(this.mnuPrescale8xFilter_Click); - // - // mnuPrescale10xFilter - // - this.mnuPrescale10xFilter.Name = "mnuPrescale10xFilter"; - this.mnuPrescale10xFilter.Size = new System.Drawing.Size(206, 22); - this.mnuPrescale10xFilter.Text = "Prescale 10x"; - this.mnuPrescale10xFilter.Click += new System.EventHandler(this.mnuPrescale10xFilter_Click); - // - // toolStripMenuItem19 - // - this.toolStripMenuItem19.Name = "toolStripMenuItem19"; - this.toolStripMenuItem19.Size = new System.Drawing.Size(203, 6); - // - // mnuBilinearInterpolation - // - this.mnuBilinearInterpolation.CheckOnClick = true; - this.mnuBilinearInterpolation.Name = "mnuBilinearInterpolation"; - this.mnuBilinearInterpolation.Size = new System.Drawing.Size(206, 22); - this.mnuBilinearInterpolation.Text = "Use Bilinear Interpolation"; - this.mnuBilinearInterpolation.Click += new System.EventHandler(this.mnuBilinearInterpolation_Click); - // - // mnuRegion - // - this.mnuRegion.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; + this.mnuVideoFilter.Name = "mnuVideoFilter"; + this.mnuVideoFilter.Size = new System.Drawing.Size(180, 22); + this.mnuVideoFilter.Text = "Video Filter"; + // + // mnuNoneFilter + // + this.mnuNoneFilter.Name = "mnuNoneFilter"; + this.mnuNoneFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNoneFilter.Text = "None"; + this.mnuNoneFilter.Click += new System.EventHandler(this.mnuNoneFilter_Click); + // + // toolStripMenuItem21 + // + this.toolStripMenuItem21.Name = "toolStripMenuItem21"; + this.toolStripMenuItem21.Size = new System.Drawing.Size(203, 6); + // + // mnuNtscFilter + // + this.mnuNtscFilter.Name = "mnuNtscFilter"; + this.mnuNtscFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscFilter.Text = "NTSC"; + this.mnuNtscFilter.Click += new System.EventHandler(this.mnuNtscFilter_Click); + // + // mnuNtscBisqwitQuarterFilter + // + this.mnuNtscBisqwitQuarterFilter.Name = "mnuNtscBisqwitQuarterFilter"; + this.mnuNtscBisqwitQuarterFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscBisqwitQuarterFilter.Text = "NTSC 2x (Bisqwit)"; + this.mnuNtscBisqwitQuarterFilter.Click += new System.EventHandler(this.mnuNtscBisqwitQuarterFilter_Click); + // + // mnuNtscBisqwitHalfFilter + // + this.mnuNtscBisqwitHalfFilter.Name = "mnuNtscBisqwitHalfFilter"; + this.mnuNtscBisqwitHalfFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscBisqwitHalfFilter.Text = "NTSC 4x (Bisqwit)"; + this.mnuNtscBisqwitHalfFilter.Click += new System.EventHandler(this.mnuNtscBisqwitHalfFilter_Click); + // + // mnuNtscBisqwitFullFilter + // + this.mnuNtscBisqwitFullFilter.Name = "mnuNtscBisqwitFullFilter"; + this.mnuNtscBisqwitFullFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscBisqwitFullFilter.Text = "NTSC 8x (Bisqwit)"; + this.mnuNtscBisqwitFullFilter.Click += new System.EventHandler(this.mnuNtscBisqwitFullFilter_Click); + // + // mnuNtscLMP88959QuarterFilter + // + this.mnuNtscLMP88959QuarterFilter.Name = "mnuNtscLMP88959QuarterFilter"; + this.mnuNtscLMP88959QuarterFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscLMP88959QuarterFilter.Text = "NTSC 2x (LMP88959)"; + this.mnuNtscLMP88959QuarterFilter.Click += new System.EventHandler(this.mnuNtscLMP88959QuarterFilter_Click); + // + // mnuNtscLMP88959HalfFilter + // + this.mnuNtscLMP88959HalfFilter.Name = "mnuNtscLMP88959HalfFilter"; + this.mnuNtscLMP88959HalfFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscLMP88959HalfFilter.Text = "NTSC 4x (LMP88959)"; + this.mnuNtscLMP88959HalfFilter.Click += new System.EventHandler(this.mnuNtscLMP88959HalfFilter_Click); + // + // mnuNtscLMP88959FullFilter + // + this.mnuNtscLMP88959FullFilter.Name = "mnuNtscLMP88959FullFilter"; + this.mnuNtscLMP88959FullFilter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscLMP88959FullFilter.Text = "NTSC 8x (LMP88959)"; + this.mnuNtscLMP88959FullFilter.Click += new System.EventHandler(this.mnuNtscLMP88959FullFilter_Click); + // + // toolStripMenuItem15 + // + this.toolStripMenuItem15.Name = "toolStripMenuItem15"; + this.toolStripMenuItem15.Size = new System.Drawing.Size(203, 6); + // + // mnuXBRZ2xFilter + // + this.mnuXBRZ2xFilter.Name = "mnuXBRZ2xFilter"; + this.mnuXBRZ2xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuXBRZ2xFilter.Text = "xBRZ 2x"; + this.mnuXBRZ2xFilter.Click += new System.EventHandler(this.mnuXBRZ2xFilter_Click); + // + // mnuXBRZ3xFilter + // + this.mnuXBRZ3xFilter.Name = "mnuXBRZ3xFilter"; + this.mnuXBRZ3xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuXBRZ3xFilter.Text = "xBRZ 3x"; + this.mnuXBRZ3xFilter.Click += new System.EventHandler(this.mnuXBRZ3xFilter_Click); + // + // mnuXBRZ4xFilter + // + this.mnuXBRZ4xFilter.Name = "mnuXBRZ4xFilter"; + this.mnuXBRZ4xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuXBRZ4xFilter.Text = "xBRZ 4x"; + this.mnuXBRZ4xFilter.Click += new System.EventHandler(this.mnuXBRZ4xFilter_Click); + // + // mnuXBRZ5xFilter + // + this.mnuXBRZ5xFilter.Name = "mnuXBRZ5xFilter"; + this.mnuXBRZ5xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuXBRZ5xFilter.Text = "xBRZ 5x"; + this.mnuXBRZ5xFilter.Click += new System.EventHandler(this.mnuXBRZ5xFilter_Click); + // + // mnuXBRZ6xFilter + // + this.mnuXBRZ6xFilter.Name = "mnuXBRZ6xFilter"; + this.mnuXBRZ6xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuXBRZ6xFilter.Text = "xBRZ 6x"; + this.mnuXBRZ6xFilter.Click += new System.EventHandler(this.mnuXBRZ6xFilter_Click); + // + // toolStripMenuItem16 + // + this.toolStripMenuItem16.Name = "toolStripMenuItem16"; + this.toolStripMenuItem16.Size = new System.Drawing.Size(203, 6); + // + // mnuHQ2xFilter + // + this.mnuHQ2xFilter.Name = "mnuHQ2xFilter"; + this.mnuHQ2xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuHQ2xFilter.Text = "HQ 2x"; + this.mnuHQ2xFilter.Click += new System.EventHandler(this.mnuHQ2xFilter_Click); + // + // mnuHQ3xFilter + // + this.mnuHQ3xFilter.Name = "mnuHQ3xFilter"; + this.mnuHQ3xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuHQ3xFilter.Text = "HQ 3x"; + this.mnuHQ3xFilter.Click += new System.EventHandler(this.mnuHQ3xFilter_Click); + // + // mnuHQ4xFilter + // + this.mnuHQ4xFilter.Name = "mnuHQ4xFilter"; + this.mnuHQ4xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuHQ4xFilter.Text = "HQ 4x"; + this.mnuHQ4xFilter.Click += new System.EventHandler(this.mnuHQ4xFilter_Click); + // + // toolStripMenuItem17 + // + this.toolStripMenuItem17.Name = "toolStripMenuItem17"; + this.toolStripMenuItem17.Size = new System.Drawing.Size(203, 6); + // + // mnuScale2xFilter + // + this.mnuScale2xFilter.Name = "mnuScale2xFilter"; + this.mnuScale2xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuScale2xFilter.Text = "Scale2x"; + this.mnuScale2xFilter.Click += new System.EventHandler(this.mnuScale2xFilter_Click); + // + // mnuScale3xFilter + // + this.mnuScale3xFilter.Name = "mnuScale3xFilter"; + this.mnuScale3xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuScale3xFilter.Text = "Scale3x"; + this.mnuScale3xFilter.Click += new System.EventHandler(this.mnuScale3xFilter_Click); + // + // mnuScale4xFilter + // + this.mnuScale4xFilter.Name = "mnuScale4xFilter"; + this.mnuScale4xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuScale4xFilter.Text = "Scale4x"; + this.mnuScale4xFilter.Click += new System.EventHandler(this.mnuScale4xFilter_Click); + // + // toolStripMenuItem23 + // + this.toolStripMenuItem23.Name = "toolStripMenuItem23"; + this.toolStripMenuItem23.Size = new System.Drawing.Size(203, 6); + // + // mnu2xSaiFilter + // + this.mnu2xSaiFilter.Name = "mnu2xSaiFilter"; + this.mnu2xSaiFilter.Size = new System.Drawing.Size(206, 22); + this.mnu2xSaiFilter.Text = "2xSai"; + this.mnu2xSaiFilter.Click += new System.EventHandler(this.mnu2xSaiFilter_Click); + // + // mnuSuper2xSaiFilter + // + this.mnuSuper2xSaiFilter.Name = "mnuSuper2xSaiFilter"; + this.mnuSuper2xSaiFilter.Size = new System.Drawing.Size(206, 22); + this.mnuSuper2xSaiFilter.Text = "Super2xSai"; + this.mnuSuper2xSaiFilter.Click += new System.EventHandler(this.mnuSuper2xSaiFilter_Click); + // + // mnuSuperEagleFilter + // + this.mnuSuperEagleFilter.Name = "mnuSuperEagleFilter"; + this.mnuSuperEagleFilter.Size = new System.Drawing.Size(206, 22); + this.mnuSuperEagleFilter.Text = "SuperEagle"; + this.mnuSuperEagleFilter.Click += new System.EventHandler(this.mnuSuperEagleFilter_Click); + // + // toolStripMenuItem18 + // + this.toolStripMenuItem18.Name = "toolStripMenuItem18"; + this.toolStripMenuItem18.Size = new System.Drawing.Size(203, 6); + // + // mnuPrescale2xFilter + // + this.mnuPrescale2xFilter.Name = "mnuPrescale2xFilter"; + this.mnuPrescale2xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale2xFilter.Text = "Prescale 2x"; + this.mnuPrescale2xFilter.Click += new System.EventHandler(this.mnuPrescale2xFilter_Click); + // + // mnuPrescale3xFilter + // + this.mnuPrescale3xFilter.Name = "mnuPrescale3xFilter"; + this.mnuPrescale3xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale3xFilter.Text = "Prescale 3x"; + this.mnuPrescale3xFilter.Click += new System.EventHandler(this.mnuPrescale3xFilter_Click); + // + // mnuPrescale4xFilter + // + this.mnuPrescale4xFilter.Name = "mnuPrescale4xFilter"; + this.mnuPrescale4xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale4xFilter.Text = "Prescale 4x"; + this.mnuPrescale4xFilter.Click += new System.EventHandler(this.mnuPrescale4xFilter_Click); + // + // mnuPrescale6xFilter + // + this.mnuPrescale6xFilter.Name = "mnuPrescale6xFilter"; + this.mnuPrescale6xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale6xFilter.Text = "Prescale 6x"; + this.mnuPrescale6xFilter.Click += new System.EventHandler(this.mnuPrescale6xFilter_Click); + // + // mnuPrescale8xFilter + // + this.mnuPrescale8xFilter.Name = "mnuPrescale8xFilter"; + this.mnuPrescale8xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale8xFilter.Text = "Prescale 8x"; + this.mnuPrescale8xFilter.Click += new System.EventHandler(this.mnuPrescale8xFilter_Click); + // + // mnuPrescale10xFilter + // + this.mnuPrescale10xFilter.Name = "mnuPrescale10xFilter"; + this.mnuPrescale10xFilter.Size = new System.Drawing.Size(206, 22); + this.mnuPrescale10xFilter.Text = "Prescale 10x"; + this.mnuPrescale10xFilter.Click += new System.EventHandler(this.mnuPrescale10xFilter_Click); + // + // toolStripMenuItem19 + // + this.toolStripMenuItem19.Name = "toolStripMenuItem19"; + this.toolStripMenuItem19.Size = new System.Drawing.Size(203, 6); + // + // mnuBilinearInterpolation + // + this.mnuBilinearInterpolation.CheckOnClick = true; + this.mnuBilinearInterpolation.Name = "mnuBilinearInterpolation"; + this.mnuBilinearInterpolation.Size = new System.Drawing.Size(206, 22); + this.mnuBilinearInterpolation.Text = "Use Bilinear Interpolation"; + this.mnuBilinearInterpolation.Click += new System.EventHandler(this.mnuBilinearInterpolation_Click); + // + // mnuRegion + // + this.mnuRegion.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuRegionAuto, this.mnuRegionNtsc, this.mnuRegionPal, this.mnuRegionDendy}); - this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.Globe; - this.mnuRegion.Name = "mnuRegion"; - this.mnuRegion.Size = new System.Drawing.Size(135, 22); - this.mnuRegion.Text = "Region"; - // - // mnuRegionAuto - // - this.mnuRegionAuto.Name = "mnuRegionAuto"; - this.mnuRegionAuto.Size = new System.Drawing.Size(108, 22); - this.mnuRegionAuto.Text = "Auto"; - this.mnuRegionAuto.Click += new System.EventHandler(this.mnuRegion_Click); - // - // mnuRegionNtsc - // - this.mnuRegionNtsc.Name = "mnuRegionNtsc"; - this.mnuRegionNtsc.Size = new System.Drawing.Size(108, 22); - this.mnuRegionNtsc.Text = "NTSC"; - this.mnuRegionNtsc.Click += new System.EventHandler(this.mnuRegion_Click); - // - // mnuRegionPal - // - this.mnuRegionPal.Name = "mnuRegionPal"; - this.mnuRegionPal.Size = new System.Drawing.Size(108, 22); - this.mnuRegionPal.Text = "PAL"; - this.mnuRegionPal.Click += new System.EventHandler(this.mnuRegion_Click); - // - // mnuRegionDendy - // - this.mnuRegionDendy.Name = "mnuRegionDendy"; - this.mnuRegionDendy.Size = new System.Drawing.Size(108, 22); - this.mnuRegionDendy.Text = "Dendy"; - this.mnuRegionDendy.Click += new System.EventHandler(this.mnuRegion_Click); - // - // toolStripMenuItem10 - // - this.toolStripMenuItem10.Name = "toolStripMenuItem10"; - this.toolStripMenuItem10.Size = new System.Drawing.Size(132, 6); - // - // mnuAudioConfig - // - this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio; - this.mnuAudioConfig.Name = "mnuAudioConfig"; - this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22); - this.mnuAudioConfig.Text = "Audio"; - this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); - // - // mnuInput - // - this.mnuInput.Image = global::Mesen.GUI.Properties.Resources.Controller; - this.mnuInput.Name = "mnuInput"; - this.mnuInput.Size = new System.Drawing.Size(135, 22); - this.mnuInput.Text = "Input"; - this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); - // - // mnuVideoConfig - // - this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.Video; - this.mnuVideoConfig.Name = "mnuVideoConfig"; - this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22); - this.mnuVideoConfig.Text = "Video"; - this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); - // - // mnuEmulationConfig - // - this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; - this.mnuEmulationConfig.Name = "mnuEmulationConfig"; - this.mnuEmulationConfig.Size = new System.Drawing.Size(135, 22); - this.mnuEmulationConfig.Text = "Emulation"; - this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click); - // - // toolStripMenuItem11 - // - this.toolStripMenuItem11.Name = "toolStripMenuItem11"; - this.toolStripMenuItem11.Size = new System.Drawing.Size(132, 6); - // - // mnuPreferences - // - this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Cog; - this.mnuPreferences.Name = "mnuPreferences"; - this.mnuPreferences.Size = new System.Drawing.Size(135, 22); - this.mnuPreferences.Text = "Preferences"; - this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click); - // - // mnuTools - // - this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.Globe; + this.mnuRegion.Name = "mnuRegion"; + this.mnuRegion.Size = new System.Drawing.Size(180, 22); + this.mnuRegion.Text = "Region"; + // + // mnuRegionAuto + // + this.mnuRegionAuto.Name = "mnuRegionAuto"; + this.mnuRegionAuto.Size = new System.Drawing.Size(108, 22); + this.mnuRegionAuto.Text = "Auto"; + this.mnuRegionAuto.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuRegionNtsc + // + this.mnuRegionNtsc.Name = "mnuRegionNtsc"; + this.mnuRegionNtsc.Size = new System.Drawing.Size(108, 22); + this.mnuRegionNtsc.Text = "NTSC"; + this.mnuRegionNtsc.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuRegionPal + // + this.mnuRegionPal.Name = "mnuRegionPal"; + this.mnuRegionPal.Size = new System.Drawing.Size(108, 22); + this.mnuRegionPal.Text = "PAL"; + this.mnuRegionPal.Click += new System.EventHandler(this.mnuRegion_Click); + // + // mnuRegionDendy + // + this.mnuRegionDendy.Name = "mnuRegionDendy"; + this.mnuRegionDendy.Size = new System.Drawing.Size(108, 22); + this.mnuRegionDendy.Text = "Dendy"; + this.mnuRegionDendy.Click += new System.EventHandler(this.mnuRegion_Click); + // + // toolStripMenuItem10 + // + this.toolStripMenuItem10.Name = "toolStripMenuItem10"; + this.toolStripMenuItem10.Size = new System.Drawing.Size(177, 6); + // + // mnuAudioConfig + // + this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio; + this.mnuAudioConfig.Name = "mnuAudioConfig"; + this.mnuAudioConfig.Size = new System.Drawing.Size(180, 22); + this.mnuAudioConfig.Text = "Audio"; + this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); + // + // mnuInput + // + this.mnuInput.Image = global::Mesen.GUI.Properties.Resources.Controller; + this.mnuInput.Name = "mnuInput"; + this.mnuInput.Size = new System.Drawing.Size(180, 22); + this.mnuInput.Text = "Input"; + this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); + // + // mnuVideoConfig + // + this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.Video; + this.mnuVideoConfig.Name = "mnuVideoConfig"; + this.mnuVideoConfig.Size = new System.Drawing.Size(180, 22); + this.mnuVideoConfig.Text = "Video"; + this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); + // + // mnuEmulationConfig + // + this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; + this.mnuEmulationConfig.Name = "mnuEmulationConfig"; + this.mnuEmulationConfig.Size = new System.Drawing.Size(180, 22); + this.mnuEmulationConfig.Text = "Emulation"; + this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click); + // + // toolStripMenuItem11 + // + this.toolStripMenuItem11.Name = "toolStripMenuItem11"; + this.toolStripMenuItem11.Size = new System.Drawing.Size(177, 6); + // + // mnuPreferences + // + this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Cog; + this.mnuPreferences.Name = "mnuPreferences"; + this.mnuPreferences.Size = new System.Drawing.Size(180, 22); + this.mnuPreferences.Text = "Preferences"; + this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click); + // + // mnuTools + // + this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuNetPlay, this.mnuMovies, this.mnuHistoryViewer, @@ -1190,43 +1217,43 @@ private void InitializeComponent() this.toolStripMenuItem1, this.mnuRandomGame, this.mnuTakeScreenshot}); - this.mnuTools.Name = "mnuTools"; - this.mnuTools.Size = new System.Drawing.Size(47, 20); - this.mnuTools.Text = "Tools"; - this.mnuTools.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuTools.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuNetPlay - // - this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuTools.Name = "mnuTools"; + this.mnuTools.Size = new System.Drawing.Size(46, 20); + this.mnuTools.Text = "Tools"; + this.mnuTools.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuTools.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuNetPlay + // + this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuStartServer, this.mnuConnect, this.mnuNetPlaySelectController, this.toolStripMenuItem2, this.mnuFindServer, this.mnuProfile}); - this.mnuNetPlay.Image = global::Mesen.GUI.Properties.Resources.NetPlay; - this.mnuNetPlay.Name = "mnuNetPlay"; - this.mnuNetPlay.Size = new System.Drawing.Size(182, 22); - this.mnuNetPlay.Text = "Net Play"; - // - // mnuStartServer - // - this.mnuStartServer.Name = "mnuStartServer"; - this.mnuStartServer.Size = new System.Drawing.Size(177, 22); - this.mnuStartServer.Text = "Start Server"; - this.mnuStartServer.Click += new System.EventHandler(this.mnuStartServer_Click); - // - // mnuConnect - // - this.mnuConnect.Name = "mnuConnect"; - this.mnuConnect.Size = new System.Drawing.Size(177, 22); - this.mnuConnect.Text = "Connect to Server"; - this.mnuConnect.Click += new System.EventHandler(this.mnuConnect_Click); - // - // mnuNetPlaySelectController - // - this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuNetPlay.Image = global::Mesen.GUI.Properties.Resources.NetPlay; + this.mnuNetPlay.Name = "mnuNetPlay"; + this.mnuNetPlay.Size = new System.Drawing.Size(182, 22); + this.mnuNetPlay.Text = "Net Play"; + // + // mnuStartServer + // + this.mnuStartServer.Name = "mnuStartServer"; + this.mnuStartServer.Size = new System.Drawing.Size(177, 22); + this.mnuStartServer.Text = "Start Server"; + this.mnuStartServer.Click += new System.EventHandler(this.mnuStartServer_Click); + // + // mnuConnect + // + this.mnuConnect.Name = "mnuConnect"; + this.mnuConnect.Size = new System.Drawing.Size(177, 22); + this.mnuConnect.Text = "Connect to Server"; + this.mnuConnect.Click += new System.EventHandler(this.mnuConnect_Click); + // + // mnuNetPlaySelectController + // + this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuNetPlayPlayer1, this.mnuNetPlayPlayer2, this.mnuNetPlayPlayer3, @@ -1234,328 +1261,328 @@ private void InitializeComponent() this.mnuNetPlayPlayer5, this.toolStripMenuItem3, this.mnuNetPlaySpectator}); - this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController"; - this.mnuNetPlaySelectController.Size = new System.Drawing.Size(177, 22); - this.mnuNetPlaySelectController.Text = "Select Controller"; - // - // mnuNetPlayPlayer1 - // - this.mnuNetPlayPlayer1.Name = "mnuNetPlayPlayer1"; - this.mnuNetPlayPlayer1.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlayPlayer1.Text = "Player 1"; - this.mnuNetPlayPlayer1.Click += new System.EventHandler(this.mnuNetPlayPlayer1_Click); - // - // mnuNetPlayPlayer2 - // - this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2"; - this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlayPlayer2.Text = "Player 2"; - this.mnuNetPlayPlayer2.Click += new System.EventHandler(this.mnuNetPlayPlayer2_Click); - // - // mnuNetPlayPlayer3 - // - this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3"; - this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlayPlayer3.Text = "Player 3"; - this.mnuNetPlayPlayer3.Click += new System.EventHandler(this.mnuNetPlayPlayer3_Click); - // - // mnuNetPlayPlayer4 - // - this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4"; - this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlayPlayer4.Text = "Player 4"; - this.mnuNetPlayPlayer4.Click += new System.EventHandler(this.mnuNetPlayPlayer4_Click); - // - // mnuNetPlayPlayer5 - // - this.mnuNetPlayPlayer5.Name = "mnuNetPlayPlayer5"; - this.mnuNetPlayPlayer5.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlayPlayer5.Text = "Expansion Device"; - this.mnuNetPlayPlayer5.Click += new System.EventHandler(this.mnuNetPlayPlayer5_Click); - // - // toolStripMenuItem3 - // - this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - this.toolStripMenuItem3.Size = new System.Drawing.Size(162, 6); - // - // mnuNetPlaySpectator - // - this.mnuNetPlaySpectator.Name = "mnuNetPlaySpectator"; - this.mnuNetPlaySpectator.Size = new System.Drawing.Size(165, 22); - this.mnuNetPlaySpectator.Text = "Spectator"; - this.mnuNetPlaySpectator.Click += new System.EventHandler(this.mnuNetPlaySpectator_Click); - // - // toolStripMenuItem2 - // - this.toolStripMenuItem2.Name = "toolStripMenuItem2"; - this.toolStripMenuItem2.Size = new System.Drawing.Size(174, 6); - // - // mnuFindServer - // - this.mnuFindServer.Enabled = false; - this.mnuFindServer.Name = "mnuFindServer"; - this.mnuFindServer.Size = new System.Drawing.Size(177, 22); - this.mnuFindServer.Text = "Find Public Server..."; - this.mnuFindServer.Visible = false; - // - // mnuProfile - // - this.mnuProfile.Name = "mnuProfile"; - this.mnuProfile.Size = new System.Drawing.Size(177, 22); - this.mnuProfile.Text = "Configure Profile"; - this.mnuProfile.Click += new System.EventHandler(this.mnuProfile_Click); - // - // mnuMovies - // - this.mnuMovies.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController"; + this.mnuNetPlaySelectController.Size = new System.Drawing.Size(177, 22); + this.mnuNetPlaySelectController.Text = "Select Controller"; + // + // mnuNetPlayPlayer1 + // + this.mnuNetPlayPlayer1.Name = "mnuNetPlayPlayer1"; + this.mnuNetPlayPlayer1.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlayPlayer1.Text = "Player 1"; + this.mnuNetPlayPlayer1.Click += new System.EventHandler(this.mnuNetPlayPlayer1_Click); + // + // mnuNetPlayPlayer2 + // + this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2"; + this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlayPlayer2.Text = "Player 2"; + this.mnuNetPlayPlayer2.Click += new System.EventHandler(this.mnuNetPlayPlayer2_Click); + // + // mnuNetPlayPlayer3 + // + this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3"; + this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlayPlayer3.Text = "Player 3"; + this.mnuNetPlayPlayer3.Click += new System.EventHandler(this.mnuNetPlayPlayer3_Click); + // + // mnuNetPlayPlayer4 + // + this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4"; + this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlayPlayer4.Text = "Player 4"; + this.mnuNetPlayPlayer4.Click += new System.EventHandler(this.mnuNetPlayPlayer4_Click); + // + // mnuNetPlayPlayer5 + // + this.mnuNetPlayPlayer5.Name = "mnuNetPlayPlayer5"; + this.mnuNetPlayPlayer5.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlayPlayer5.Text = "Expansion Device"; + this.mnuNetPlayPlayer5.Click += new System.EventHandler(this.mnuNetPlayPlayer5_Click); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(163, 6); + // + // mnuNetPlaySpectator + // + this.mnuNetPlaySpectator.Name = "mnuNetPlaySpectator"; + this.mnuNetPlaySpectator.Size = new System.Drawing.Size(166, 22); + this.mnuNetPlaySpectator.Text = "Spectator"; + this.mnuNetPlaySpectator.Click += new System.EventHandler(this.mnuNetPlaySpectator_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(174, 6); + // + // mnuFindServer + // + this.mnuFindServer.Enabled = false; + this.mnuFindServer.Name = "mnuFindServer"; + this.mnuFindServer.Size = new System.Drawing.Size(177, 22); + this.mnuFindServer.Text = "Find Public Server..."; + this.mnuFindServer.Visible = false; + // + // mnuProfile + // + this.mnuProfile.Name = "mnuProfile"; + this.mnuProfile.Size = new System.Drawing.Size(177, 22); + this.mnuProfile.Text = "Configure Profile"; + this.mnuProfile.Click += new System.EventHandler(this.mnuProfile_Click); + // + // mnuMovies + // + this.mnuMovies.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuPlayMovie, this.mnuRecordMovie, this.mnuStopMovie}); - this.mnuMovies.Image = global::Mesen.GUI.Properties.Resources.Movie; - this.mnuMovies.Name = "mnuMovies"; - this.mnuMovies.Size = new System.Drawing.Size(182, 22); - this.mnuMovies.Text = "Movies"; - // - // mnuPlayMovie - // - this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.Play; - this.mnuPlayMovie.Name = "mnuPlayMovie"; - this.mnuPlayMovie.Size = new System.Drawing.Size(120, 22); - this.mnuPlayMovie.Text = "Play..."; - this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click); - // - // mnuRecordMovie - // - this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record; - this.mnuRecordMovie.Name = "mnuRecordMovie"; - this.mnuRecordMovie.Size = new System.Drawing.Size(120, 22); - this.mnuRecordMovie.Text = "Record..."; - this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click); - // - // mnuStopMovie - // - this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.Stop; - this.mnuStopMovie.Name = "mnuStopMovie"; - this.mnuStopMovie.Size = new System.Drawing.Size(120, 22); - this.mnuStopMovie.Text = "Stop"; - this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click); - // - // mnuHistoryViewer - // - this.mnuHistoryViewer.Image = global::Mesen.GUI.Properties.Resources.HistoryViewer; - this.mnuHistoryViewer.Name = "mnuHistoryViewer"; - this.mnuHistoryViewer.Size = new System.Drawing.Size(182, 22); - this.mnuHistoryViewer.Text = "History Viewer"; - this.mnuHistoryViewer.Click += new System.EventHandler(this.mnuHistoryViewer_Click); - // - // mnuCheats - // - this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode; - this.mnuCheats.Name = "mnuCheats"; - this.mnuCheats.Size = new System.Drawing.Size(182, 22); - this.mnuCheats.Text = "Cheats"; - this.mnuCheats.Click += new System.EventHandler(this.mnuCheats_Click); - // - // toolStripMenuItem22 - // - this.toolStripMenuItem22.Name = "toolStripMenuItem22"; - this.toolStripMenuItem22.Size = new System.Drawing.Size(179, 6); - // - // mnuSoundRecorder - // - this.mnuSoundRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuMovies.Image = global::Mesen.GUI.Properties.Resources.Movie; + this.mnuMovies.Name = "mnuMovies"; + this.mnuMovies.Size = new System.Drawing.Size(182, 22); + this.mnuMovies.Text = "Movies"; + // + // mnuPlayMovie + // + this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.Play; + this.mnuPlayMovie.Name = "mnuPlayMovie"; + this.mnuPlayMovie.Size = new System.Drawing.Size(120, 22); + this.mnuPlayMovie.Text = "Play..."; + this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click); + // + // mnuRecordMovie + // + this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record; + this.mnuRecordMovie.Name = "mnuRecordMovie"; + this.mnuRecordMovie.Size = new System.Drawing.Size(120, 22); + this.mnuRecordMovie.Text = "Record..."; + this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click); + // + // mnuStopMovie + // + this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.mnuStopMovie.Name = "mnuStopMovie"; + this.mnuStopMovie.Size = new System.Drawing.Size(120, 22); + this.mnuStopMovie.Text = "Stop"; + this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click); + // + // mnuHistoryViewer + // + this.mnuHistoryViewer.Image = global::Mesen.GUI.Properties.Resources.HistoryViewer; + this.mnuHistoryViewer.Name = "mnuHistoryViewer"; + this.mnuHistoryViewer.Size = new System.Drawing.Size(182, 22); + this.mnuHistoryViewer.Text = "History Viewer"; + this.mnuHistoryViewer.Click += new System.EventHandler(this.mnuHistoryViewer_Click); + // + // mnuCheats + // + this.mnuCheats.Image = global::Mesen.GUI.Properties.Resources.CheatCode; + this.mnuCheats.Name = "mnuCheats"; + this.mnuCheats.Size = new System.Drawing.Size(182, 22); + this.mnuCheats.Text = "Cheats"; + this.mnuCheats.Click += new System.EventHandler(this.mnuCheats_Click); + // + // toolStripMenuItem22 + // + this.toolStripMenuItem22.Name = "toolStripMenuItem22"; + this.toolStripMenuItem22.Size = new System.Drawing.Size(179, 6); + // + // mnuSoundRecorder + // + this.mnuSoundRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuWaveRecord, this.mnuWaveStop}); - this.mnuSoundRecorder.Image = global::Mesen.GUI.Properties.Resources.Microphone; - this.mnuSoundRecorder.Name = "mnuSoundRecorder"; - this.mnuSoundRecorder.Size = new System.Drawing.Size(182, 22); - this.mnuSoundRecorder.Text = "Sound Recorder"; - // - // mnuWaveRecord - // - this.mnuWaveRecord.Image = global::Mesen.GUI.Properties.Resources.Record; - this.mnuWaveRecord.Name = "mnuWaveRecord"; - this.mnuWaveRecord.Size = new System.Drawing.Size(155, 22); - this.mnuWaveRecord.Text = "Record..."; - this.mnuWaveRecord.Click += new System.EventHandler(this.mnuWaveRecord_Click); - // - // mnuWaveStop - // - this.mnuWaveStop.Image = global::Mesen.GUI.Properties.Resources.Stop; - this.mnuWaveStop.Name = "mnuWaveStop"; - this.mnuWaveStop.Size = new System.Drawing.Size(155, 22); - this.mnuWaveStop.Text = "Stop Recording"; - this.mnuWaveStop.Click += new System.EventHandler(this.mnuWaveStop_Click); - // - // mnuVideoRecorder - // - this.mnuVideoRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuSoundRecorder.Image = global::Mesen.GUI.Properties.Resources.Microphone; + this.mnuSoundRecorder.Name = "mnuSoundRecorder"; + this.mnuSoundRecorder.Size = new System.Drawing.Size(182, 22); + this.mnuSoundRecorder.Text = "Sound Recorder"; + // + // mnuWaveRecord + // + this.mnuWaveRecord.Image = global::Mesen.GUI.Properties.Resources.Record; + this.mnuWaveRecord.Name = "mnuWaveRecord"; + this.mnuWaveRecord.Size = new System.Drawing.Size(155, 22); + this.mnuWaveRecord.Text = "Record..."; + this.mnuWaveRecord.Click += new System.EventHandler(this.mnuWaveRecord_Click); + // + // mnuWaveStop + // + this.mnuWaveStop.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.mnuWaveStop.Name = "mnuWaveStop"; + this.mnuWaveStop.Size = new System.Drawing.Size(155, 22); + this.mnuWaveStop.Text = "Stop Recording"; + this.mnuWaveStop.Click += new System.EventHandler(this.mnuWaveStop_Click); + // + // mnuVideoRecorder + // + this.mnuVideoRecorder.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuAviRecord, this.mnuAviStop}); - this.mnuVideoRecorder.Image = global::Mesen.GUI.Properties.Resources.VideoRecorder; - this.mnuVideoRecorder.Name = "mnuVideoRecorder"; - this.mnuVideoRecorder.Size = new System.Drawing.Size(182, 22); - this.mnuVideoRecorder.Text = "Video Recorder"; - // - // mnuAviRecord - // - this.mnuAviRecord.Image = global::Mesen.GUI.Properties.Resources.Record; - this.mnuAviRecord.Name = "mnuAviRecord"; - this.mnuAviRecord.Size = new System.Drawing.Size(155, 22); - this.mnuAviRecord.Text = "Record..."; - this.mnuAviRecord.Click += new System.EventHandler(this.mnuAviRecord_Click); - // - // mnuAviStop - // - this.mnuAviStop.Image = global::Mesen.GUI.Properties.Resources.Stop; - this.mnuAviStop.Name = "mnuAviStop"; - this.mnuAviStop.Size = new System.Drawing.Size(155, 22); - this.mnuAviStop.Text = "Stop Recording"; - this.mnuAviStop.Click += new System.EventHandler(this.mnuAviStop_Click); - // - // toolStripMenuItem12 - // - this.toolStripMenuItem12.Name = "toolStripMenuItem12"; - this.toolStripMenuItem12.Size = new System.Drawing.Size(179, 6); - // - // mnuTests - // - this.mnuTests.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuVideoRecorder.Image = global::Mesen.GUI.Properties.Resources.VideoRecorder; + this.mnuVideoRecorder.Name = "mnuVideoRecorder"; + this.mnuVideoRecorder.Size = new System.Drawing.Size(182, 22); + this.mnuVideoRecorder.Text = "Video Recorder"; + // + // mnuAviRecord + // + this.mnuAviRecord.Image = global::Mesen.GUI.Properties.Resources.Record; + this.mnuAviRecord.Name = "mnuAviRecord"; + this.mnuAviRecord.Size = new System.Drawing.Size(155, 22); + this.mnuAviRecord.Text = "Record..."; + this.mnuAviRecord.Click += new System.EventHandler(this.mnuAviRecord_Click); + // + // mnuAviStop + // + this.mnuAviStop.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.mnuAviStop.Name = "mnuAviStop"; + this.mnuAviStop.Size = new System.Drawing.Size(155, 22); + this.mnuAviStop.Text = "Stop Recording"; + this.mnuAviStop.Click += new System.EventHandler(this.mnuAviStop_Click); + // + // toolStripMenuItem12 + // + this.toolStripMenuItem12.Name = "toolStripMenuItem12"; + this.toolStripMenuItem12.Size = new System.Drawing.Size(179, 6); + // + // mnuTests + // + this.mnuTests.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuTestRun, this.mnuTestRecordFrom, this.mnuTestStopRecording, this.mnuRunAllTests, this.mnuRunAllGameTests, this.mnuRunAutomaticTest}); - this.mnuTests.Name = "mnuTests"; - this.mnuTests.Size = new System.Drawing.Size(182, 22); - this.mnuTests.Text = "Tests"; - // - // mnuTestRun - // - this.mnuTestRun.Name = "mnuTestRun"; - this.mnuTestRun.Size = new System.Drawing.Size(174, 22); - this.mnuTestRun.Text = "Run..."; - this.mnuTestRun.Click += new System.EventHandler(this.mnuTestRun_Click); - // - // mnuTestRecordFrom - // - this.mnuTestRecordFrom.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuTests.Name = "mnuTests"; + this.mnuTests.Size = new System.Drawing.Size(182, 22); + this.mnuTests.Text = "Tests"; + // + // mnuTestRun + // + this.mnuTestRun.Name = "mnuTestRun"; + this.mnuTestRun.Size = new System.Drawing.Size(174, 22); + this.mnuTestRun.Text = "Run..."; + this.mnuTestRun.Click += new System.EventHandler(this.mnuTestRun_Click); + // + // mnuTestRecordFrom + // + this.mnuTestRecordFrom.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuTestRecordStart, this.mnuTestRecordNow, this.mnuTestRecordMovie, this.mnuTestRecordTest}); - this.mnuTestRecordFrom.Name = "mnuTestRecordFrom"; - this.mnuTestRecordFrom.Size = new System.Drawing.Size(174, 22); - this.mnuTestRecordFrom.Text = "Record from..."; - // - // mnuTestRecordStart - // - this.mnuTestRecordStart.Name = "mnuTestRecordStart"; - this.mnuTestRecordStart.Size = new System.Drawing.Size(107, 22); - this.mnuTestRecordStart.Text = "Start"; - this.mnuTestRecordStart.Click += new System.EventHandler(this.mnuTestRecordStart_Click); - // - // mnuTestRecordNow - // - this.mnuTestRecordNow.Name = "mnuTestRecordNow"; - this.mnuTestRecordNow.Size = new System.Drawing.Size(107, 22); - this.mnuTestRecordNow.Text = "Now"; - this.mnuTestRecordNow.Click += new System.EventHandler(this.mnuTestRecordNow_Click); - // - // mnuTestRecordMovie - // - this.mnuTestRecordMovie.Name = "mnuTestRecordMovie"; - this.mnuTestRecordMovie.Size = new System.Drawing.Size(107, 22); - this.mnuTestRecordMovie.Text = "Movie"; - this.mnuTestRecordMovie.Click += new System.EventHandler(this.mnuTestRecordMovie_Click); - // - // mnuTestRecordTest - // - this.mnuTestRecordTest.Name = "mnuTestRecordTest"; - this.mnuTestRecordTest.Size = new System.Drawing.Size(107, 22); - this.mnuTestRecordTest.Text = "Test"; - this.mnuTestRecordTest.Click += new System.EventHandler(this.mnuTestRecordTest_Click); - // - // mnuTestStopRecording - // - this.mnuTestStopRecording.Name = "mnuTestStopRecording"; - this.mnuTestStopRecording.Size = new System.Drawing.Size(174, 22); - this.mnuTestStopRecording.Text = "Stop recording"; - this.mnuTestStopRecording.Click += new System.EventHandler(this.mnuTestStopRecording_Click); - // - // mnuRunAllTests - // - this.mnuRunAllTests.Name = "mnuRunAllTests"; - this.mnuRunAllTests.Size = new System.Drawing.Size(174, 22); - this.mnuRunAllTests.Text = "Run all tests"; - this.mnuRunAllTests.Click += new System.EventHandler(this.mnuRunAllTests_Click); - // - // mnuRunAllGameTests - // - this.mnuRunAllGameTests.Name = "mnuRunAllGameTests"; - this.mnuRunAllGameTests.Size = new System.Drawing.Size(174, 22); - this.mnuRunAllGameTests.Text = "Run all game tests"; - this.mnuRunAllGameTests.Click += new System.EventHandler(this.mnuRunAllGameTests_Click); - // - // mnuRunAutomaticTest - // - this.mnuRunAutomaticTest.Name = "mnuRunAutomaticTest"; - this.mnuRunAutomaticTest.Size = new System.Drawing.Size(174, 22); - this.mnuRunAutomaticTest.Text = "Run automatic test"; - this.mnuRunAutomaticTest.Click += new System.EventHandler(this.mnuRunAutomaticTest_Click); - // - // mnuLogWindow - // - this.mnuLogWindow.Image = global::Mesen.GUI.Properties.Resources.LogWindow; - this.mnuLogWindow.Name = "mnuLogWindow"; - this.mnuLogWindow.Size = new System.Drawing.Size(182, 22); - this.mnuLogWindow.Text = "Log Window"; - this.mnuLogWindow.Click += new System.EventHandler(this.mnuLogWindow_Click); - // - // toolStripMenuItem27 - // - this.toolStripMenuItem27.Name = "toolStripMenuItem27"; - this.toolStripMenuItem27.Size = new System.Drawing.Size(179, 6); - // - // mnuInstallHdPack - // - this.mnuInstallHdPack.Image = global::Mesen.GUI.Properties.Resources.Import; - this.mnuInstallHdPack.Name = "mnuInstallHdPack"; - this.mnuInstallHdPack.Size = new System.Drawing.Size(182, 22); - this.mnuInstallHdPack.Text = "Install HD Pack"; - this.mnuInstallHdPack.Click += new System.EventHandler(this.mnuInstallHdPack_Click); - // - // mnuHdPackEditor - // - this.mnuHdPackEditor.Image = global::Mesen.GUI.Properties.Resources.HdPack; - this.mnuHdPackEditor.Name = "mnuHdPackEditor"; - this.mnuHdPackEditor.Size = new System.Drawing.Size(182, 22); - this.mnuHdPackEditor.Text = "HD Pack Builder"; - this.mnuHdPackEditor.Click += new System.EventHandler(this.mnuHdPackEditor_Click); - // - // toolStripMenuItem1 - // - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(179, 6); - // - // mnuRandomGame - // - this.mnuRandomGame.Image = global::Mesen.GUI.Properties.Resources.Dice; - this.mnuRandomGame.Name = "mnuRandomGame"; - this.mnuRandomGame.Size = new System.Drawing.Size(182, 22); - this.mnuRandomGame.Text = "Load Random Game"; - // - // mnuTakeScreenshot - // - this.mnuTakeScreenshot.Image = global::Mesen.GUI.Properties.Resources.Camera; - this.mnuTakeScreenshot.Name = "mnuTakeScreenshot"; - this.mnuTakeScreenshot.Size = new System.Drawing.Size(182, 22); - this.mnuTakeScreenshot.Text = "Take Screenshot"; - // - // mnuDebug - // - this.mnuDebug.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuTestRecordFrom.Name = "mnuTestRecordFrom"; + this.mnuTestRecordFrom.Size = new System.Drawing.Size(174, 22); + this.mnuTestRecordFrom.Text = "Record from..."; + // + // mnuTestRecordStart + // + this.mnuTestRecordStart.Name = "mnuTestRecordStart"; + this.mnuTestRecordStart.Size = new System.Drawing.Size(107, 22); + this.mnuTestRecordStart.Text = "Start"; + this.mnuTestRecordStart.Click += new System.EventHandler(this.mnuTestRecordStart_Click); + // + // mnuTestRecordNow + // + this.mnuTestRecordNow.Name = "mnuTestRecordNow"; + this.mnuTestRecordNow.Size = new System.Drawing.Size(107, 22); + this.mnuTestRecordNow.Text = "Now"; + this.mnuTestRecordNow.Click += new System.EventHandler(this.mnuTestRecordNow_Click); + // + // mnuTestRecordMovie + // + this.mnuTestRecordMovie.Name = "mnuTestRecordMovie"; + this.mnuTestRecordMovie.Size = new System.Drawing.Size(107, 22); + this.mnuTestRecordMovie.Text = "Movie"; + this.mnuTestRecordMovie.Click += new System.EventHandler(this.mnuTestRecordMovie_Click); + // + // mnuTestRecordTest + // + this.mnuTestRecordTest.Name = "mnuTestRecordTest"; + this.mnuTestRecordTest.Size = new System.Drawing.Size(107, 22); + this.mnuTestRecordTest.Text = "Test"; + this.mnuTestRecordTest.Click += new System.EventHandler(this.mnuTestRecordTest_Click); + // + // mnuTestStopRecording + // + this.mnuTestStopRecording.Name = "mnuTestStopRecording"; + this.mnuTestStopRecording.Size = new System.Drawing.Size(174, 22); + this.mnuTestStopRecording.Text = "Stop recording"; + this.mnuTestStopRecording.Click += new System.EventHandler(this.mnuTestStopRecording_Click); + // + // mnuRunAllTests + // + this.mnuRunAllTests.Name = "mnuRunAllTests"; + this.mnuRunAllTests.Size = new System.Drawing.Size(174, 22); + this.mnuRunAllTests.Text = "Run all tests"; + this.mnuRunAllTests.Click += new System.EventHandler(this.mnuRunAllTests_Click); + // + // mnuRunAllGameTests + // + this.mnuRunAllGameTests.Name = "mnuRunAllGameTests"; + this.mnuRunAllGameTests.Size = new System.Drawing.Size(174, 22); + this.mnuRunAllGameTests.Text = "Run all game tests"; + this.mnuRunAllGameTests.Click += new System.EventHandler(this.mnuRunAllGameTests_Click); + // + // mnuRunAutomaticTest + // + this.mnuRunAutomaticTest.Name = "mnuRunAutomaticTest"; + this.mnuRunAutomaticTest.Size = new System.Drawing.Size(174, 22); + this.mnuRunAutomaticTest.Text = "Run automatic test"; + this.mnuRunAutomaticTest.Click += new System.EventHandler(this.mnuRunAutomaticTest_Click); + // + // mnuLogWindow + // + this.mnuLogWindow.Image = global::Mesen.GUI.Properties.Resources.LogWindow; + this.mnuLogWindow.Name = "mnuLogWindow"; + this.mnuLogWindow.Size = new System.Drawing.Size(182, 22); + this.mnuLogWindow.Text = "Log Window"; + this.mnuLogWindow.Click += new System.EventHandler(this.mnuLogWindow_Click); + // + // toolStripMenuItem27 + // + this.toolStripMenuItem27.Name = "toolStripMenuItem27"; + this.toolStripMenuItem27.Size = new System.Drawing.Size(179, 6); + // + // mnuInstallHdPack + // + this.mnuInstallHdPack.Image = global::Mesen.GUI.Properties.Resources.Import; + this.mnuInstallHdPack.Name = "mnuInstallHdPack"; + this.mnuInstallHdPack.Size = new System.Drawing.Size(182, 22); + this.mnuInstallHdPack.Text = "Install HD Pack"; + this.mnuInstallHdPack.Click += new System.EventHandler(this.mnuInstallHdPack_Click); + // + // mnuHdPackEditor + // + this.mnuHdPackEditor.Image = global::Mesen.GUI.Properties.Resources.HdPack; + this.mnuHdPackEditor.Name = "mnuHdPackEditor"; + this.mnuHdPackEditor.Size = new System.Drawing.Size(182, 22); + this.mnuHdPackEditor.Text = "HD Pack Builder"; + this.mnuHdPackEditor.Click += new System.EventHandler(this.mnuHdPackEditor_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(179, 6); + // + // mnuRandomGame + // + this.mnuRandomGame.Image = global::Mesen.GUI.Properties.Resources.Dice; + this.mnuRandomGame.Name = "mnuRandomGame"; + this.mnuRandomGame.Size = new System.Drawing.Size(182, 22); + this.mnuRandomGame.Text = "Load Random Game"; + // + // mnuTakeScreenshot + // + this.mnuTakeScreenshot.Image = global::Mesen.GUI.Properties.Resources.Camera; + this.mnuTakeScreenshot.Name = "mnuTakeScreenshot"; + this.mnuTakeScreenshot.Size = new System.Drawing.Size(182, 22); + this.mnuTakeScreenshot.Text = "Take Screenshot"; + // + // mnuDebug + // + this.mnuDebug.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuApuViewer, this.mnuAssembler, this.mnuDebugger, @@ -1573,174 +1600,174 @@ private void InitializeComponent() this.mnuPpuViewerCompact, this.toolStripMenuItem28, this.mnuEditHeader}); - this.mnuDebug.Name = "mnuDebug"; - this.mnuDebug.Size = new System.Drawing.Size(54, 20); - this.mnuDebug.Text = "Debug"; - this.mnuDebug.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuDebug.DropDownOpening += new System.EventHandler(this.mnuDebug_DropDownOpening); - this.mnuDebug.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuApuViewer - // - this.mnuApuViewer.Image = global::Mesen.GUI.Properties.Resources.Audio; - this.mnuApuViewer.Name = "mnuApuViewer"; - this.mnuApuViewer.Size = new System.Drawing.Size(258, 22); - this.mnuApuViewer.Text = "APU Viewer"; - this.mnuApuViewer.Click += new System.EventHandler(this.mnuApuViewer_Click); - // - // mnuAssembler - // - this.mnuAssembler.Image = global::Mesen.GUI.Properties.Resources.Chip; - this.mnuAssembler.Name = "mnuAssembler"; - this.mnuAssembler.Size = new System.Drawing.Size(258, 22); - this.mnuAssembler.Text = "Assembler"; - this.mnuAssembler.Click += new System.EventHandler(this.mnuAssembler_Click); - // - // mnuDebugger - // - this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Bug; - this.mnuDebugger.Name = "mnuDebugger"; - this.mnuDebugger.Size = new System.Drawing.Size(258, 22); - this.mnuDebugger.Text = "Debugger"; - this.mnuDebugger.Click += new System.EventHandler(this.mnuDebugger_Click); - // - // mnuEventViewer - // - this.mnuEventViewer.Image = global::Mesen.GUI.Properties.Resources.NesEventViewer; - this.mnuEventViewer.Name = "mnuEventViewer"; - this.mnuEventViewer.Size = new System.Drawing.Size(258, 22); - this.mnuEventViewer.Text = "Event Viewer"; - this.mnuEventViewer.Click += new System.EventHandler(this.mnuEventViewer_Click); - // - // mnuMemoryViewer - // - this.mnuMemoryViewer.Image = global::Mesen.GUI.Properties.Resources.CheatCode; - this.mnuMemoryViewer.Name = "mnuMemoryViewer"; - this.mnuMemoryViewer.Size = new System.Drawing.Size(258, 22); - this.mnuMemoryViewer.Text = "Memory Tools"; - this.mnuMemoryViewer.Click += new System.EventHandler(this.mnuMemoryViewer_Click); - // - // mnuProfiler - // - this.mnuProfiler.Image = global::Mesen.GUI.Properties.Resources.Speed; - this.mnuProfiler.Name = "mnuProfiler"; - this.mnuProfiler.Size = new System.Drawing.Size(258, 22); - this.mnuProfiler.Text = "Performance Profiler"; - this.mnuProfiler.Click += new System.EventHandler(this.mnuProfiler_Click); - // - // mnuPpuViewer - // - this.mnuPpuViewer.Image = global::Mesen.GUI.Properties.Resources.Video; - this.mnuPpuViewer.Name = "mnuPpuViewer"; - this.mnuPpuViewer.Size = new System.Drawing.Size(258, 22); - this.mnuPpuViewer.Text = "PPU Viewer"; - this.mnuPpuViewer.Click += new System.EventHandler(this.mnuPpuViewer_Click); - // - // mnuScriptWindow - // - this.mnuScriptWindow.Image = global::Mesen.GUI.Properties.Resources.Script; - this.mnuScriptWindow.Name = "mnuScriptWindow"; - this.mnuScriptWindow.Size = new System.Drawing.Size(258, 22); - this.mnuScriptWindow.Text = "Script Window"; - this.mnuScriptWindow.Click += new System.EventHandler(this.mnuScriptWindow_Click); - // - // mnuTextHooker - // - this.mnuTextHooker.Image = global::Mesen.GUI.Properties.Resources.Font; - this.mnuTextHooker.Name = "mnuTextHooker"; - this.mnuTextHooker.Size = new System.Drawing.Size(258, 22); - this.mnuTextHooker.Text = "Text Hooker"; - this.mnuTextHooker.Click += new System.EventHandler(this.mnuTextHooker_Click); - // - // mnuTraceLogger - // - this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; - this.mnuTraceLogger.Name = "mnuTraceLogger"; - this.mnuTraceLogger.Size = new System.Drawing.Size(258, 22); - this.mnuTraceLogger.Text = "Trace Logger"; - this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); - // - // mnuWatchWindow - // - this.mnuWatchWindow.Image = global::Mesen.GUI.Properties.Resources.Find; - this.mnuWatchWindow.Name = "mnuWatchWindow"; - this.mnuWatchWindow.Size = new System.Drawing.Size(258, 22); - this.mnuWatchWindow.Text = "Watch Window"; - this.mnuWatchWindow.Click += new System.EventHandler(this.mnuWatchWindow_Click); - // - // sepDebugDualSystemSecondaryCpu - // - this.sepDebugDualSystemSecondaryCpu.Name = "sepDebugDualSystemSecondaryCpu"; - this.sepDebugDualSystemSecondaryCpu.Size = new System.Drawing.Size(255, 6); - // - // mnuDebugDualSystemSecondaryCpu - // - this.mnuDebugDualSystemSecondaryCpu.Name = "mnuDebugDualSystemSecondaryCpu"; - this.mnuDebugDualSystemSecondaryCpu.Size = new System.Drawing.Size(258, 22); - this.mnuDebugDualSystemSecondaryCpu.Text = "Debug DualSystem Secondary CPU"; - this.mnuDebugDualSystemSecondaryCpu.Click += new System.EventHandler(this.mnuDebugDualSystemSecondaryCpu_Click); - // - // toolStripMenuItem25 - // - this.toolStripMenuItem25.Name = "toolStripMenuItem25"; - this.toolStripMenuItem25.Size = new System.Drawing.Size(255, 6); - // - // mnuPpuViewerCompact - // - this.mnuPpuViewerCompact.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuDebug.Name = "mnuDebug"; + this.mnuDebug.Size = new System.Drawing.Size(54, 20); + this.mnuDebug.Text = "Debug"; + this.mnuDebug.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuDebug.DropDownOpening += new System.EventHandler(this.mnuDebug_DropDownOpening); + this.mnuDebug.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuApuViewer + // + this.mnuApuViewer.Image = global::Mesen.GUI.Properties.Resources.Audio; + this.mnuApuViewer.Name = "mnuApuViewer"; + this.mnuApuViewer.Size = new System.Drawing.Size(258, 22); + this.mnuApuViewer.Text = "APU Viewer"; + this.mnuApuViewer.Click += new System.EventHandler(this.mnuApuViewer_Click); + // + // mnuAssembler + // + this.mnuAssembler.Image = global::Mesen.GUI.Properties.Resources.Chip; + this.mnuAssembler.Name = "mnuAssembler"; + this.mnuAssembler.Size = new System.Drawing.Size(258, 22); + this.mnuAssembler.Text = "Assembler"; + this.mnuAssembler.Click += new System.EventHandler(this.mnuAssembler_Click); + // + // mnuDebugger + // + this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Bug; + this.mnuDebugger.Name = "mnuDebugger"; + this.mnuDebugger.Size = new System.Drawing.Size(258, 22); + this.mnuDebugger.Text = "Debugger"; + this.mnuDebugger.Click += new System.EventHandler(this.mnuDebugger_Click); + // + // mnuEventViewer + // + this.mnuEventViewer.Image = global::Mesen.GUI.Properties.Resources.NesEventViewer; + this.mnuEventViewer.Name = "mnuEventViewer"; + this.mnuEventViewer.Size = new System.Drawing.Size(258, 22); + this.mnuEventViewer.Text = "Event Viewer"; + this.mnuEventViewer.Click += new System.EventHandler(this.mnuEventViewer_Click); + // + // mnuMemoryViewer + // + this.mnuMemoryViewer.Image = global::Mesen.GUI.Properties.Resources.CheatCode; + this.mnuMemoryViewer.Name = "mnuMemoryViewer"; + this.mnuMemoryViewer.Size = new System.Drawing.Size(258, 22); + this.mnuMemoryViewer.Text = "Memory Tools"; + this.mnuMemoryViewer.Click += new System.EventHandler(this.mnuMemoryViewer_Click); + // + // mnuProfiler + // + this.mnuProfiler.Image = global::Mesen.GUI.Properties.Resources.Speed; + this.mnuProfiler.Name = "mnuProfiler"; + this.mnuProfiler.Size = new System.Drawing.Size(258, 22); + this.mnuProfiler.Text = "Performance Profiler"; + this.mnuProfiler.Click += new System.EventHandler(this.mnuProfiler_Click); + // + // mnuPpuViewer + // + this.mnuPpuViewer.Image = global::Mesen.GUI.Properties.Resources.Video; + this.mnuPpuViewer.Name = "mnuPpuViewer"; + this.mnuPpuViewer.Size = new System.Drawing.Size(258, 22); + this.mnuPpuViewer.Text = "PPU Viewer"; + this.mnuPpuViewer.Click += new System.EventHandler(this.mnuPpuViewer_Click); + // + // mnuScriptWindow + // + this.mnuScriptWindow.Image = global::Mesen.GUI.Properties.Resources.Script; + this.mnuScriptWindow.Name = "mnuScriptWindow"; + this.mnuScriptWindow.Size = new System.Drawing.Size(258, 22); + this.mnuScriptWindow.Text = "Script Window"; + this.mnuScriptWindow.Click += new System.EventHandler(this.mnuScriptWindow_Click); + // + // mnuTextHooker + // + this.mnuTextHooker.Image = global::Mesen.GUI.Properties.Resources.Font; + this.mnuTextHooker.Name = "mnuTextHooker"; + this.mnuTextHooker.Size = new System.Drawing.Size(258, 22); + this.mnuTextHooker.Text = "Text Hooker"; + this.mnuTextHooker.Click += new System.EventHandler(this.mnuTextHooker_Click); + // + // mnuTraceLogger + // + this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; + this.mnuTraceLogger.Name = "mnuTraceLogger"; + this.mnuTraceLogger.Size = new System.Drawing.Size(258, 22); + this.mnuTraceLogger.Text = "Trace Logger"; + this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); + // + // mnuWatchWindow + // + this.mnuWatchWindow.Image = global::Mesen.GUI.Properties.Resources.Find; + this.mnuWatchWindow.Name = "mnuWatchWindow"; + this.mnuWatchWindow.Size = new System.Drawing.Size(258, 22); + this.mnuWatchWindow.Text = "Watch Window"; + this.mnuWatchWindow.Click += new System.EventHandler(this.mnuWatchWindow_Click); + // + // sepDebugDualSystemSecondaryCpu + // + this.sepDebugDualSystemSecondaryCpu.Name = "sepDebugDualSystemSecondaryCpu"; + this.sepDebugDualSystemSecondaryCpu.Size = new System.Drawing.Size(255, 6); + // + // mnuDebugDualSystemSecondaryCpu + // + this.mnuDebugDualSystemSecondaryCpu.Name = "mnuDebugDualSystemSecondaryCpu"; + this.mnuDebugDualSystemSecondaryCpu.Size = new System.Drawing.Size(258, 22); + this.mnuDebugDualSystemSecondaryCpu.Text = "Debug DualSystem Secondary CPU"; + this.mnuDebugDualSystemSecondaryCpu.Click += new System.EventHandler(this.mnuDebugDualSystemSecondaryCpu_Click); + // + // toolStripMenuItem25 + // + this.toolStripMenuItem25.Name = "toolStripMenuItem25"; + this.toolStripMenuItem25.Size = new System.Drawing.Size(255, 6); + // + // mnuPpuViewerCompact + // + this.mnuPpuViewerCompact.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuOpenNametableViewer, this.mnuOpenChrViewer, this.mnuOpenSpriteViewer, this.mnuOpenPaletteViewer}); - this.mnuPpuViewerCompact.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; - this.mnuPpuViewerCompact.Name = "mnuPpuViewerCompact"; - this.mnuPpuViewerCompact.Size = new System.Drawing.Size(258, 22); - this.mnuPpuViewerCompact.Text = "PPU Viewer (Compact)"; - // - // mnuOpenNametableViewer - // - this.mnuOpenNametableViewer.Name = "mnuOpenNametableViewer"; - this.mnuOpenNametableViewer.Size = new System.Drawing.Size(170, 22); - this.mnuOpenNametableViewer.Text = "Nametable Viewer"; - this.mnuOpenNametableViewer.Click += new System.EventHandler(this.mnuOpenNametableViewer_Click); - // - // mnuOpenChrViewer - // - this.mnuOpenChrViewer.Name = "mnuOpenChrViewer"; - this.mnuOpenChrViewer.Size = new System.Drawing.Size(170, 22); - this.mnuOpenChrViewer.Text = "CHR Viewer"; - this.mnuOpenChrViewer.Click += new System.EventHandler(this.mnuOpenChrViewer_Click); - // - // mnuOpenSpriteViewer - // - this.mnuOpenSpriteViewer.Name = "mnuOpenSpriteViewer"; - this.mnuOpenSpriteViewer.Size = new System.Drawing.Size(170, 22); - this.mnuOpenSpriteViewer.Text = "Sprite Viewer"; - this.mnuOpenSpriteViewer.Click += new System.EventHandler(this.mnuOpenSpriteViewer_Click); - // - // mnuOpenPaletteViewer - // - this.mnuOpenPaletteViewer.Name = "mnuOpenPaletteViewer"; - this.mnuOpenPaletteViewer.Size = new System.Drawing.Size(170, 22); - this.mnuOpenPaletteViewer.Text = "Palette Viewer"; - this.mnuOpenPaletteViewer.Click += new System.EventHandler(this.mnuOpenPaletteViewer_Click); - // - // toolStripMenuItem28 - // - this.toolStripMenuItem28.Name = "toolStripMenuItem28"; - this.toolStripMenuItem28.Size = new System.Drawing.Size(255, 6); - // - // mnuEditHeader - // - this.mnuEditHeader.Image = global::Mesen.GUI.Properties.Resources.Edit; - this.mnuEditHeader.Name = "mnuEditHeader"; - this.mnuEditHeader.Size = new System.Drawing.Size(258, 22); - this.mnuEditHeader.Text = "Edit iNES Header"; - this.mnuEditHeader.Click += new System.EventHandler(this.mnuEditHeader_Click); - // - // mnuHelp - // - this.mnuHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuPpuViewerCompact.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; + this.mnuPpuViewerCompact.Name = "mnuPpuViewerCompact"; + this.mnuPpuViewerCompact.Size = new System.Drawing.Size(258, 22); + this.mnuPpuViewerCompact.Text = "PPU Viewer (Compact)"; + // + // mnuOpenNametableViewer + // + this.mnuOpenNametableViewer.Name = "mnuOpenNametableViewer"; + this.mnuOpenNametableViewer.Size = new System.Drawing.Size(170, 22); + this.mnuOpenNametableViewer.Text = "Nametable Viewer"; + this.mnuOpenNametableViewer.Click += new System.EventHandler(this.mnuOpenNametableViewer_Click); + // + // mnuOpenChrViewer + // + this.mnuOpenChrViewer.Name = "mnuOpenChrViewer"; + this.mnuOpenChrViewer.Size = new System.Drawing.Size(170, 22); + this.mnuOpenChrViewer.Text = "CHR Viewer"; + this.mnuOpenChrViewer.Click += new System.EventHandler(this.mnuOpenChrViewer_Click); + // + // mnuOpenSpriteViewer + // + this.mnuOpenSpriteViewer.Name = "mnuOpenSpriteViewer"; + this.mnuOpenSpriteViewer.Size = new System.Drawing.Size(170, 22); + this.mnuOpenSpriteViewer.Text = "Sprite Viewer"; + this.mnuOpenSpriteViewer.Click += new System.EventHandler(this.mnuOpenSpriteViewer_Click); + // + // mnuOpenPaletteViewer + // + this.mnuOpenPaletteViewer.Name = "mnuOpenPaletteViewer"; + this.mnuOpenPaletteViewer.Size = new System.Drawing.Size(170, 22); + this.mnuOpenPaletteViewer.Text = "Palette Viewer"; + this.mnuOpenPaletteViewer.Click += new System.EventHandler(this.mnuOpenPaletteViewer_Click); + // + // toolStripMenuItem28 + // + this.toolStripMenuItem28.Name = "toolStripMenuItem28"; + this.toolStripMenuItem28.Size = new System.Drawing.Size(255, 6); + // + // mnuEditHeader + // + this.mnuEditHeader.Image = global::Mesen.GUI.Properties.Resources.Edit; + this.mnuEditHeader.Name = "mnuEditHeader"; + this.mnuEditHeader.Size = new System.Drawing.Size(258, 22); + this.mnuEditHeader.Text = "Edit iNES Header"; + this.mnuEditHeader.Click += new System.EventHandler(this.mnuEditHeader_Click); + // + // mnuHelp + // + this.mnuHelp.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuOnlineHelp, this.mnuHelpWindow, this.toolStripMenuItem26, @@ -1749,93 +1776,94 @@ private void InitializeComponent() this.mnuReportBug, this.toolStripMenuItem5, this.mnuAbout}); - this.mnuHelp.Name = "mnuHelp"; - this.mnuHelp.Size = new System.Drawing.Size(44, 20); - this.mnuHelp.Text = "Help"; - this.mnuHelp.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); - this.mnuHelp.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); - // - // mnuOnlineHelp - // - this.mnuOnlineHelp.Image = global::Mesen.GUI.Properties.Resources.Help; - this.mnuOnlineHelp.Name = "mnuOnlineHelp"; - this.mnuOnlineHelp.Size = new System.Drawing.Size(198, 22); - this.mnuOnlineHelp.Text = "Online Help"; - this.mnuOnlineHelp.Click += new System.EventHandler(this.mnuOnlineHelp_Click); - // - // mnuHelpWindow - // - this.mnuHelpWindow.Image = global::Mesen.GUI.Properties.Resources.CommandLine; - this.mnuHelpWindow.Name = "mnuHelpWindow"; - this.mnuHelpWindow.Size = new System.Drawing.Size(198, 22); - this.mnuHelpWindow.Text = "Command-line options"; - this.mnuHelpWindow.Click += new System.EventHandler(this.mnuHelpWindow_Click); - // - // toolStripMenuItem26 - // - this.toolStripMenuItem26.Name = "toolStripMenuItem26"; - this.toolStripMenuItem26.Size = new System.Drawing.Size(195, 6); - // - // mnuCheckForUpdates - // - this.mnuCheckForUpdates.Image = global::Mesen.GUI.Properties.Resources.SoftwareUpdate; - this.mnuCheckForUpdates.Name = "mnuCheckForUpdates"; - this.mnuCheckForUpdates.Size = new System.Drawing.Size(198, 22); - this.mnuCheckForUpdates.Text = "Check for updates"; - this.mnuCheckForUpdates.Click += new System.EventHandler(this.mnuCheckForUpdates_Click); - // - // toolStripMenuItem20 - // - this.toolStripMenuItem20.Name = "toolStripMenuItem20"; - this.toolStripMenuItem20.Size = new System.Drawing.Size(195, 6); - // - // mnuReportBug - // - this.mnuReportBug.Image = global::Mesen.GUI.Properties.Resources.Comment; - this.mnuReportBug.Name = "mnuReportBug"; - this.mnuReportBug.Size = new System.Drawing.Size(198, 22); - this.mnuReportBug.Text = "Report a bug"; - this.mnuReportBug.Click += new System.EventHandler(this.mnuReportBug_Click); - // - // toolStripMenuItem5 - // - this.toolStripMenuItem5.Name = "toolStripMenuItem5"; - this.toolStripMenuItem5.Size = new System.Drawing.Size(195, 6); - // - // mnuAbout - // - this.mnuAbout.Image = global::Mesen.GUI.Properties.Resources.Exclamation; - this.mnuAbout.Name = "mnuAbout"; - this.mnuAbout.Size = new System.Drawing.Size(198, 22); - this.mnuAbout.Text = "About"; - this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click); - // - // frmMain - // - this.AllowDrop = true; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.Black; - this.ClientSize = new System.Drawing.Size(430, 333); - this.Controls.Add(this.ctrlRecentGames); - this.Controls.Add(this.panelRenderer); - this.Controls.Add(this.menuStrip); - this.MainMenuStrip = this.menuStrip; - this.MinimumSize = new System.Drawing.Size(340, 230); - this.Name = "frmMain"; - this.Text = "Mesen"; - this.DragDrop += new System.Windows.Forms.DragEventHandler(this.frmMain_DragDrop); - this.DragEnter += new System.Windows.Forms.DragEventHandler(this.frmMain_DragEnter); - this.Resize += new System.EventHandler(this.frmMain_Resize); - this.panelRenderer.ResumeLayout(false); - this.panelInfo.ResumeLayout(false); - this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.picIcon)).EndInit(); - this.menuStrip.ResumeLayout(false); - this.menuStrip.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); + this.mnuHelp.Name = "mnuHelp"; + this.mnuHelp.Size = new System.Drawing.Size(44, 20); + this.mnuHelp.Text = "Help"; + this.mnuHelp.DropDownClosed += new System.EventHandler(this.mnu_DropDownClosed); + this.mnuHelp.DropDownOpened += new System.EventHandler(this.mnu_DropDownOpened); + // + // mnuOnlineHelp + // + this.mnuOnlineHelp.Image = global::Mesen.GUI.Properties.Resources.Help; + this.mnuOnlineHelp.Name = "mnuOnlineHelp"; + this.mnuOnlineHelp.Size = new System.Drawing.Size(198, 22); + this.mnuOnlineHelp.Text = "Online Help"; + this.mnuOnlineHelp.Click += new System.EventHandler(this.mnuOnlineHelp_Click); + // + // mnuHelpWindow + // + this.mnuHelpWindow.Image = global::Mesen.GUI.Properties.Resources.CommandLine; + this.mnuHelpWindow.Name = "mnuHelpWindow"; + this.mnuHelpWindow.Size = new System.Drawing.Size(198, 22); + this.mnuHelpWindow.Text = "Command-line options"; + this.mnuHelpWindow.Click += new System.EventHandler(this.mnuHelpWindow_Click); + // + // toolStripMenuItem26 + // + this.toolStripMenuItem26.Name = "toolStripMenuItem26"; + this.toolStripMenuItem26.Size = new System.Drawing.Size(195, 6); + // + // mnuCheckForUpdates + // + this.mnuCheckForUpdates.Image = global::Mesen.GUI.Properties.Resources.SoftwareUpdate; + this.mnuCheckForUpdates.Name = "mnuCheckForUpdates"; + this.mnuCheckForUpdates.Size = new System.Drawing.Size(198, 22); + this.mnuCheckForUpdates.Text = "Check for updates"; + this.mnuCheckForUpdates.Click += new System.EventHandler(this.mnuCheckForUpdates_Click); + // + // toolStripMenuItem20 + // + this.toolStripMenuItem20.Name = "toolStripMenuItem20"; + this.toolStripMenuItem20.Size = new System.Drawing.Size(195, 6); + // + // mnuReportBug + // + this.mnuReportBug.Image = global::Mesen.GUI.Properties.Resources.Comment; + this.mnuReportBug.Name = "mnuReportBug"; + this.mnuReportBug.Size = new System.Drawing.Size(198, 22); + this.mnuReportBug.Text = "Report a bug"; + this.mnuReportBug.Click += new System.EventHandler(this.mnuReportBug_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(195, 6); + // + // mnuAbout + // + this.mnuAbout.Image = global::Mesen.GUI.Properties.Resources.Exclamation; + this.mnuAbout.Name = "mnuAbout"; + this.mnuAbout.Size = new System.Drawing.Size(198, 22); + this.mnuAbout.Text = "About"; + this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click); + // + // frmMain + // + this.AllowDrop = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.Black; + this.ClientSize = new System.Drawing.Size(430, 333); + this.Controls.Add(this.ctrlRecentGames); + this.Controls.Add(this.panelRenderer); + this.Controls.Add(this.menuStrip); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + this.MainMenuStrip = this.menuStrip; + this.MinimumSize = new System.Drawing.Size(340, 230); + this.Name = "frmMain"; + this.Text = "Mesen"; + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.frmMain_DragDrop); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.frmMain_DragEnter); + this.Resize += new System.EventHandler(this.frmMain_Resize); + this.panelRenderer.ResumeLayout(false); + this.panelInfo.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picIcon)).EndInit(); + this.menuStrip.ResumeLayout(false); + this.menuStrip.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); } @@ -1977,6 +2005,9 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitHalfFilter; private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitFullFilter; private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitQuarterFilter; + private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959HalfFilter; + private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959FullFilter; + private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959QuarterFilter; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22; private System.Windows.Forms.ToolStripMenuItem mnuVideoRecorder; private System.Windows.Forms.ToolStripMenuItem mnuAviRecord; @@ -2033,6 +2064,6 @@ private void InitializeComponent() private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.ToolStripMenuItem mnuWatchWindow; private System.Windows.Forms.ToolStripMenuItem mnuReloadRom; - } + } } diff --git a/GUI.NET/Forms/frmMain.Options.cs b/GUI.NET/Forms/frmMain.Options.cs index 54bac9194..bcbca5f7c 100644 --- a/GUI.NET/Forms/frmMain.Options.cs +++ b/GUI.NET/Forms/frmMain.Options.cs @@ -370,5 +370,20 @@ private void mnuNtscBisqwitQuarterFilter_Click(object sender, EventArgs e) { SetVideoFilter(VideoFilterType.BisqwitNtscQuarterRes); } + + private void mnuNtscLMP88959FullFilter_Click(object sender, EventArgs e) + { + SetVideoFilter(VideoFilterType.LMP88959Ntsc); + } + + private void mnuNtscLMP88959HalfFilter_Click(object sender, EventArgs e) + { + SetVideoFilter(VideoFilterType.LMP88959NtscHalfRes); + } + + private void mnuNtscLMP88959QuarterFilter_Click(object sender, EventArgs e) + { + SetVideoFilter(VideoFilterType.LMP88959NtscQuarterRes); + } } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index b4e966244..8db7e4d0e 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -1363,6 +1363,9 @@ private void UpdateMenus() mnuNtscBisqwitQuarterFilter.Enabled = !isHdPackLoader; mnuNtscBisqwitHalfFilter.Enabled = !isHdPackLoader; mnuNtscBisqwitFullFilter.Enabled = !isHdPackLoader; + mnuNtscLMP88959QuarterFilter.Enabled = !isHdPackLoader; + mnuNtscLMP88959HalfFilter.Enabled = !isHdPackLoader; + mnuNtscLMP88959FullFilter.Enabled = !isHdPackLoader; } } catch { } } @@ -1615,5 +1618,5 @@ private void mnu_DropDownClosed(object sender, EventArgs e) })); }); } - } + } } diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 3fdb50454..907bf9554 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -232,7 +232,7 @@ public static Image GetSaveStatePreview(string saveStatePath) [DllImport(DLLPath)] public static extern void SetVideoResizeFilter(VideoResizeFilter filter); [DllImport(DLLPath)] public static extern void SetRgbPalette(byte[] palette, UInt32 paletteSize); [DllImport(DLLPath)] public static extern void SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity); - [DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, [MarshalAs(UnmanagedType.I1)]bool verticalBlend, [MarshalAs(UnmanagedType.I1)] bool colorimetryCorrection, [MarshalAs(UnmanagedType.I1)] bool useExternalPalette); + [DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, [MarshalAs(UnmanagedType.I1)]bool verticalBlend, [MarshalAs(UnmanagedType.I1)]bool keepVerticalResolution, [MarshalAs(UnmanagedType.I1)] bool colorimetryCorrection, [MarshalAs(UnmanagedType.I1)] bool useExternalPalette, double Noise); [DllImport(DLLPath)] public static extern void SetInputDisplaySettings(byte visiblePorts, InputDisplayPosition displayPosition, [MarshalAs(UnmanagedType.I1)]bool displayHorizontally); [DllImport(DLLPath)] public static extern void SetAutoSaveOptions(UInt32 delayInMinutes, [MarshalAs(UnmanagedType.I1)]bool showMessage); [DllImport(DLLPath)] public static extern void SetPauseScreenMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string message); @@ -2360,6 +2360,9 @@ public enum VideoFilterType Prescale6x = 22, Prescale8x = 23, Prescale10x = 24, + LMP88959NtscQuarterRes = 26, + LMP88959NtscHalfRes = 27, + LMP88959Ntsc = 28, } public enum HDPackOuputTileType diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index a37c6d412..48a52d7e3 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -661,7 +661,7 @@ namespace InteropEmu { DllExport void __stdcall GetRgbPalette(uint32_t *paletteBuffer) { _settings->GetUserRgbPalette(paletteBuffer); } DllExport void __stdcall SetRgbPalette(uint32_t *paletteBuffer, uint32_t paletteSize) { _settings->SetUserRgbPalette(paletteBuffer, paletteSize); } DllExport void __stdcall SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity) { _settings->SetPictureSettings(brightness, contrast, saturation, hue, scanlineIntensity); } - DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, bool verticalBlend, bool colorimetryCorrection, bool useExternalPalette) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, decodeMatrixIR, decodeMatrixQR, decodeMatrixIG, decodeMatrixQG, decodeMatrixIB, decodeMatrixQB, verticalBlend, false, colorimetryCorrection, useExternalPalette); } + DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, bool verticalBlend, bool keepVerticalResolution, bool colorimetryCorrection, bool useExternalPalette, double noise) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, decodeMatrixIR, decodeMatrixQR, decodeMatrixIG, decodeMatrixQG, decodeMatrixIB, decodeMatrixQB, verticalBlend, keepVerticalResolution, colorimetryCorrection, useExternalPalette, noise); } DllExport void __stdcall SetPauseScreenMessage(char* message) { _settings->SetPauseScreenMessage(message); } DllExport void __stdcall SetInputDisplaySettings(uint8_t visiblePorts, InputDisplayPosition displayPosition, bool displayHorizontally) { _settings->SetInputDisplaySettings(visiblePorts, displayPosition, displayHorizontally); } diff --git a/Libretro/libretro.cpp b/Libretro/libretro.cpp index 6b3215131..190dae4f6 100644 --- a/Libretro/libretro.cpp +++ b/Libretro/libretro.cpp @@ -384,27 +384,39 @@ extern "C" { _console->GetSettings()->SetVideoFilterType(VideoFilterType::None); } else if(value == "Composite (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "S-Video (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); - _console->GetSettings()->SetNtscFilterSettings(-1.0, 0, -1.0, 0, 0.2, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(-1.0, 0, -1.0, 0, 0.2, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "RGB (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetPictureSettings(0, 0, 0, 0, 0); - _console->GetSettings()->SetNtscFilterSettings(-1.0, -1.0, -1.0, 0, 0.7, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(-1.0, -1.0, -1.0, 0, 0.7, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "Monochrome (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetPictureSettings(0, 0, -1.0, 0, 0); - _console->GetSettings()->SetNtscFilterSettings(-0.2, -0.1, -0.2, 0, 0.7, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(-0.2, -0.1, -0.2, 0, 0.7, 0.2, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "Bisqwit 2x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtscQuarterRes); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "Bisqwit 4x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtscHalfRes); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } else if(value == "Bisqwit 8x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtsc); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); + } + else if (value == "LMP88959 2x") { + _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959NtscQuarterRes); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); + } + else if (value == "LMP88959 4x") { + _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959NtscHalfRes); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); + } + else if (value == "LMP88959 8x") { + _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959Ntsc); + _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } } @@ -1200,6 +1212,9 @@ extern "C" { case VideoFilterType::BisqwitNtscQuarterRes: hscale = 2; break; case VideoFilterType::BisqwitNtscHalfRes: hscale = 4; break; case VideoFilterType::BisqwitNtsc: hscale = 8; break; + case VideoFilterType::LMP88959NtscQuarterRes: hscale = 2; break; + case VideoFilterType::LMP88959NtscHalfRes: hscale = 4; break; + case VideoFilterType::LMP88959Ntsc: hscale = 8; break; default: hscale = 1; break; } diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index a724c7cf2..a303bd762 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -70,7 +70,7 @@ /* IRE units (100 = 1.0V, -40 = 0.0V) */ /* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ -#define WHITE_LEVEL 110 +#define WHITE_LEVEL 100 #define BURST_LEVEL 30 #define BLACK_LEVEL 0 #define BLANK_LEVEL 0 @@ -343,7 +343,7 @@ crt_reset(struct CRT *v) v->hue = 0; v->saturation = 18; v->brightness = 0; - v->contrast = 179; + v->contrast = 180; v->black_point = 0; v->white_point = 100; v->noise = 0; @@ -771,7 +771,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } } - phase = s->starting_phase * 4; + phase = 0; for (y = lo; y < desth; y++) { int sy = (y * s->h) / desth; @@ -800,8 +800,8 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } /* search windows, in samples */ -#define HSYNC_WINDOW 8 -#define VSYNC_WINDOW 8 +#define HSYNC_WINDOW 4 +#define VSYNC_WINDOW 6 extern void crt_draw(struct CRT *v) @@ -1011,7 +1011,7 @@ crt_draw(struct CRT *v) if (g > 255) g = 255; if (b > 255) b = 255; - aa = 0xFF000000 | (r << 16 | g << 8 | b); + aa = (r << 16 | g << 8 | b); bb = *cL; /* blend with previous color there */ *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); diff --git a/Utilities/crt.h b/Utilities/crt.h index c43de8870..42fb5e894 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -49,7 +49,7 @@ extern "C" { #if (CRT_CHROMA_PATTERN == 1) #define CRT_CC_LINE 2275 #elif (CRT_CHROMA_PATTERN == 2) -#define CRT_CC_LINE 682.0/3.0 +#define CRT_CC_LINE 2273 #else /* this will give the 'rainbow' effect in the famous waterfall scene */ #define CRT_CC_LINE 2280 @@ -67,7 +67,7 @@ extern "C" { #endif /* https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing */ -#define CRT_HRES int(CRT_CC_LINE * double(CRT_CB_FREQ) / 10.0) /* horizontal res */ +#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ #define CRT_VRES 262 /* vertical resolution */ #define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) @@ -81,7 +81,7 @@ struct CRT { int hsync, vsync; /* used internally to keep track of sync over frames */ int hue, brightness, contrast, saturation; /* common monitor settings */ int black_point, white_point; /* user-adjustable */ - int noise; /* noise level */ + int noise; /* noise level */ int outw, outh; /* output width/height */ int *out; /* output image */ }; @@ -142,13 +142,12 @@ struct NES_NTSC_SETTINGS { int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ int as_color; /* 0 = monochrome, 1 = full color */ int dot_crawl_offset; /* 0, 1, or 2 */ - int starting_phase; /* 0, 1, or 2 */ /* NOTE: NES mode is always progressive */ /* color carrier sine wave. * ex: { 0, 1, 0, -1 } * ex: { 1, 0, -1, 0 } */ - int cc[4]; + int cc[4]; /* scale value for values in cc * for example, if using { 0, 1, 0, -1 }, ccs should be 1. * however, if using { 0, 16, 0, -16 }, ccs should be 16. From ae06dc59b4723b6fb01fae8f19c6c0a722356a1f Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 01:05:54 +0800 Subject: [PATCH 06/36] Fix NTSC emulator settings - Remove variable resolution - Fix issue with scanlines - Fix image offsetting - Improve signal accuracy --- Core/BisqwitNtscFilter.cpp | 4 +- Core/Console.cpp | 5 + Core/Console.h | 1 + Core/EmulationSettings.h | 4 +- Core/LMP88959NtscFilter.cpp | 101 ++++++-- Core/LMP88959NtscFilter.h | 5 +- Core/NtscFilter.cpp | 2 +- Core/PPU.cpp | 10 +- Core/PPU.h | 11 +- Core/PgoUtilities.cpp | 2 +- Core/ScaleFilter.cpp | 2 - Core/VideoDecoder.cpp | 4 +- GUI.NET/Dependencies/resources.ca.xml | 3 - GUI.NET/Dependencies/resources.en.xml | 3 - GUI.NET/Dependencies/resources.es.xml | 3 - GUI.NET/Dependencies/resources.fr.xml | 3 - GUI.NET/Dependencies/resources.it.xml | 3 - GUI.NET/Dependencies/resources.ja.xml | 3 - GUI.NET/Dependencies/resources.pl.xml | 3 - GUI.NET/Dependencies/resources.pt.xml | 3 - GUI.NET/Dependencies/resources.ru.xml | 3 - GUI.NET/Dependencies/resources.uk.xml | 3 - GUI.NET/Dependencies/resources.zh.xml | 3 - GUI.NET/Forms/Config/frmVideoConfig.cs | 2 - GUI.NET/Forms/frmMain.Designer.cs | 44 +--- GUI.NET/Forms/frmMain.Options.cs | 12 +- GUI.NET/Forms/frmMain.cs | 4 +- GUI.NET/InteropEmu.cs | 4 +- Libretro/libretro.cpp | 14 +- Utilities/crt.cpp | 328 +++---------------------- Utilities/crt.h | 39 +-- 31 files changed, 163 insertions(+), 468 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 8b246aa69..fc62ba1bb 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -50,7 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, (_console->GetStartingPhase() * 4) + 327360); _workDone = true; } @@ -70,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), (_console->GetStartingPhase() * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } diff --git a/Core/Console.cpp b/Core/Console.cpp index ad609cc87..3e105cc5b 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -573,6 +573,11 @@ uint8_t Console::GetStartingPhase() return _ppu ? _ppu->GetStartingPhase() : 0; } +bool Console::GetDotSkipped() +{ + return _ppu ? _ppu->GetDotSkipped() : 0; +} + NesModel Console::GetModel() { return _model; diff --git a/Core/Console.h b/Core/Console.h index d714e4f08..f5442f68e 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -206,6 +206,7 @@ class Console : public std::enable_shared_from_this uint32_t GetFrameCount(); // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 uint8_t GetStartingPhase(); + bool GetDotSkipped(); NesModel GetModel(); uint32_t GetLagCounter(); diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 25839f3de..7699e2acc 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -168,9 +168,7 @@ enum class VideoFilterType Prescale8x = 23, Prescale10x = 24, Raw = 25, - LMP88959NtscQuarterRes = 26, - LMP88959NtscHalfRes = 27, - LMP88959Ntsc = 28, + LMP88959Ntsc = 26, HdPack = 999 }; diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index b22a1860c..572c45ff7 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -6,22 +6,22 @@ #include "EmulationSettings.h" #include "Console.h" -LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console, int resDivider) : BaseVideoFilter(console) +LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoFilter(console) { - _resDivider = resDivider; - _frameBuffer = new uint32_t[(256 * 8 / _resDivider) * (240 * 8 / _resDivider)](); - memset(_crt.analog, 0, sizeof(_crt.analog)); - memset(_crt.inp, 0, sizeof(_crt.inp)); + memset(&_crt, 0, sizeof(CRT)); + memset(&_nesNTSC, 0, sizeof(NES_NTSC_SETTINGS)); + _frameBuffer = new uint32_t[(256 * 2) * 240](); + crt_init(&_crt, (256 * 2), 240, reinterpret_cast(_frameBuffer)); } FrameInfo LMP88959NtscFilter::GetFrameInfo() { OverscanDimensions overscan = GetOverscan(); if (_keepVerticalRes) { - return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 }; + return { overscan.GetScreenWidth() * 2, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 }; } else { - return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight() * 8 / _resDivider, PPU::ScreenWidth, PPU::ScreenHeight, 4 }; + return { overscan.GetScreenWidth() * 2, overscan.GetScreenHeight() * 2, PPU::ScreenWidth, PPU::ScreenHeight, 4 }; } } @@ -34,8 +34,6 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _keepVerticalRes = ntscSettings.KeepVerticalResolution; - crt_init(&_crt, (256 * 8 / _resDivider), _keepVerticalRes ? 240 : (240 * 8 / _resDivider), reinterpret_cast(GetOutputBuffer())); - _crt.hue = static_cast(pictureSettings.Hue * 180.0); _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); @@ -44,28 +42,95 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _nesNTSC.w = static_cast(PPU::ScreenWidth); _nesNTSC.h = static_cast(PPU::ScreenHeight); - _nesNTSC.raw = 0; - _nesNTSC.as_color = 1; - _nesNTSC.dot_crawl_offset = 0; _nesNTSC.cc[0] = 0; - _nesNTSC.cc[1] = 1; + _nesNTSC.cc[1] = 16; _nesNTSC.cc[2] = 0; - _nesNTSC.cc[3] = -1; - _nesNTSC.ccs = 1; + _nesNTSC.cc[3] = -16; + _nesNTSC.ccs = 16; } void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; - _nesNTSC.data = reinterpret_cast(ppuOutputBuffer); - _nesNTSC.dot_crawl_offset = phase; + _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); + _nesNTSC.dot_skipped = _console->GetDotSkipped(); crt_nes2ntsc(&_crt, &_nesNTSC); crt_draw(&_crt); + GenerateArgbFrame(_frameBuffer); +} + +void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) +{ + int resMultiplier = 2; + uint32_t* outputBuffer = GetOutputBuffer(); + OverscanDimensions overscan = GetOverscan(); + int overscanLeft = (overscan.Left > 0 ? overscan.Left : 0) * resMultiplier; + int overscanRight = (overscan.Right > 0 ? overscan.Right : 0) * resMultiplier; + int rowWidth = PPU::ScreenWidth * resMultiplier; + int rowWidthOverscan = rowWidth - overscanLeft - overscanRight; + + if (_keepVerticalRes) { + frameBuffer += rowWidth * overscan.Top + overscanLeft; + for (uint32_t i = 0, len = overscan.GetScreenHeight(); i < len; i++) { + memcpy(outputBuffer, frameBuffer, rowWidthOverscan * sizeof(uint32_t)); + outputBuffer += rowWidthOverscan; + frameBuffer += rowWidth; + } + } + else { + double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity; + bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend; + + for (int y = (int)overscan.Top; + y <= PPU::ScreenHeight - 1 - overscan.Bottom; + y++) { + uint32_t const* in = frameBuffer + y * rowWidth; + uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan; + + if (verticalBlend || scanlineIntensity < 1.0) { + for (int x = 0; x < rowWidthOverscan; x++) { + uint32_t prev = in[overscanLeft]; + uint32_t next = y < 239 ? in[overscanLeft + rowWidth] : 0; + + *out = 0xFF000000 | prev; + + /* mix 24-bit rgb without losing low bits */ + uint32_t mixed; + if (verticalBlend) { + mixed = (prev + next + ((prev ^ next) & 0x030303)) >> 1; + } + else { + mixed = prev; + } + + if (scanlineIntensity < 1.0) { + uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF; + r = (uint8_t)(r * scanlineIntensity); + g = (uint8_t)(g * scanlineIntensity); + b = (uint8_t)(b * scanlineIntensity); + *(out + rowWidthOverscan) = 0xFF000000 | (r << 16) | (g << 8) | b; + } + else { + *(out + rowWidthOverscan) = 0xFF000000 | mixed; + } + in++; + out++; + } + } + else { + for (int i = 0; i < rowWidthOverscan; i++) { + out[i] = 0xFF000000 | in[overscanLeft + i]; + } + memcpy(out + rowWidthOverscan, out, rowWidthOverscan * sizeof(uint32_t)); + } + } + } } LMP88959NtscFilter::~LMP88959NtscFilter() { + memset(&_crt, 0, sizeof(CRT)); + memset(&_nesNTSC, 0, sizeof(NES_NTSC_SETTINGS)); delete[] _frameBuffer; } \ No newline at end of file diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index d27ec8547..7f4c0f0b7 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -13,15 +13,14 @@ class LMP88959NtscFilter : public BaseVideoFilter bool _keepVerticalRes = false; uint32_t* _frameBuffer; bool _ntscBorder = true; - int _resDivider = 1; - void RecursiveBlend(int iterationCount, uint64_t* output, uint64_t* currentLine, uint64_t* nextLine, int pixelsPerCycle, bool verticalBlend); + void GenerateArgbFrame(uint32_t* frameBuffer); protected: void OnBeforeApplyFilter(); public: - LMP88959NtscFilter(shared_ptr console, int resDivider); + LMP88959NtscFilter(shared_ptr console); virtual ~LMP88959NtscFilter(); virtual void ApplyFilter(uint16_t *ppuOutputBuffer); diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 9fe0a1830..b786f8446 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -125,7 +125,7 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; + uint8_t phase = _console->GetStartingPhase(); for (int i = 0; i < 240; i++) { nes_ntsc_blit(&_ntscData, // input += in_row_width; diff --git a/Core/PPU.cpp b/Core/PPU.cpp index fee1169b7..50a9a57d0 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -36,6 +36,8 @@ PPU::PPU(shared_ptr console) _console->InitializeRam(_spriteRAM, 0x100); _console->InitializeRam(_secondarySpriteRAM, 0x20); + _isDotSkipped = false; + Reset(); } @@ -105,8 +107,6 @@ void PPU::Reset() memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles)); _enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay); - _startingPhase = 0; - UpdateMinimumDrawCycles(); } @@ -1014,6 +1014,10 @@ void PPU::ProcessScanline() //This behavior is NTSC-specific - PAL frames are always the same number of cycles //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) _cycle = 340; + _isDotSkipped = true; + } + else { + _isDotSkipped = false; } } } @@ -1355,8 +1359,6 @@ void PPU::Exec() if(_needStateUpdate) { UpdateState(); } - - _startingPhase = _cycle % 3; } void PPU::UpdateState() diff --git a/Core/PPU.h b/Core/PPU.h index 9007d03f9..d09d05e29 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -109,8 +109,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _enableOamDecay; bool _corruptOamRow[32]; - // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - uint8_t _startingPhase; + bool _isDotSkipped; void UpdateStatusFlag(); @@ -222,7 +221,13 @@ class PPU : public IMemoryHandler, public Snapshotable uint8_t GetStartingPhase() { - return _startingPhase; + // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 + return _nesModel == NesModel::NTSC ? _cycle % 3 : 0; + } + + bool GetDotSkipped() + { + return _isDotSkipped; } uint32_t GetFrameCycle() diff --git a/Core/PgoUtilities.cpp b/Core/PgoUtilities.cpp index d7cbc0355..e0181d69d 100644 --- a/Core/PgoUtilities.cpp +++ b/Core/PgoUtilities.cpp @@ -10,7 +10,7 @@ extern "C" { void __stdcall PgoRunTest(vector testRoms, bool enableDebugger) { - const VideoFilterType filterTypes[14] = { VideoFilterType::BisqwitNtscQuarterRes, VideoFilterType::LMP88959NtscQuarterRes, VideoFilterType::HQ2x, VideoFilterType::HQ3x, VideoFilterType::HQ4x, VideoFilterType::NTSC, VideoFilterType::Scale2x, VideoFilterType::Scale3x, VideoFilterType::Scale4x, VideoFilterType::xBRZ2x, VideoFilterType::xBRZ3x, VideoFilterType::xBRZ4x, VideoFilterType::xBRZ5x, VideoFilterType::xBRZ6x }; + const VideoFilterType filterTypes[14] = { VideoFilterType::BisqwitNtscQuarterRes, VideoFilterType::LMP88959Ntsc, VideoFilterType::HQ2x, VideoFilterType::HQ3x, VideoFilterType::HQ4x, VideoFilterType::NTSC, VideoFilterType::Scale2x, VideoFilterType::Scale3x, VideoFilterType::Scale4x, VideoFilterType::xBRZ2x, VideoFilterType::xBRZ3x, VideoFilterType::xBRZ4x, VideoFilterType::xBRZ5x, VideoFilterType::xBRZ6x }; FolderUtilities::SetHomeFolder("../PGOMesenHome"); for(size_t i = 0; i < testRoms.size(); i++) { diff --git a/Core/ScaleFilter.cpp b/Core/ScaleFilter.cpp index d9112d012..d87cd3ac4 100644 --- a/Core/ScaleFilter.cpp +++ b/Core/ScaleFilter.cpp @@ -111,8 +111,6 @@ shared_ptr ScaleFilter::GetScaleFilter(VideoFilterType filter) case VideoFilterType::HdPack: case VideoFilterType::Raw: case VideoFilterType::LMP88959Ntsc: - case VideoFilterType::LMP88959NtscHalfRes: - case VideoFilterType::LMP88959NtscQuarterRes: case VideoFilterType::None: break; diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index 022e5fbb7..3a6f40dde 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -74,9 +74,7 @@ void VideoDecoder::UpdateVideoFilter() case VideoFilterType::BisqwitNtsc: _videoFilter.reset(new BisqwitNtscFilter(_console, 1)); break; case VideoFilterType::BisqwitNtscHalfRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 2)); break; case VideoFilterType::BisqwitNtscQuarterRes: _videoFilter.reset(new BisqwitNtscFilter(_console, 4)); break; - case VideoFilterType::LMP88959Ntsc: _videoFilter.reset(new LMP88959NtscFilter(_console, 1)); break; - case VideoFilterType::LMP88959NtscHalfRes: _videoFilter.reset(new LMP88959NtscFilter(_console, 2)); break; - case VideoFilterType::LMP88959NtscQuarterRes: _videoFilter.reset(new LMP88959NtscFilter(_console, 4)); break; + case VideoFilterType::LMP88959Ntsc: _videoFilter.reset(new LMP88959NtscFilter(_console)); break; case VideoFilterType::Raw: _videoFilter.reset(new RawVideoFilter(_console)); break; default: _scaleFilter = ScaleFilter::GetScaleFilter(_videoFilterType); break; } diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index c13cb150e..9a2581918 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -1035,9 +1035,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index a1e2a4ce8..19cb2583e 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -1139,9 +1139,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index 2eb6111fe..a97186424 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -1051,9 +1051,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index ac4979889..5819130a3 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -1065,9 +1065,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.it.xml b/GUI.NET/Dependencies/resources.it.xml index b3651d307..5e1c1b2e2 100644 --- a/GUI.NET/Dependencies/resources.it.xml +++ b/GUI.NET/Dependencies/resources.it.xml @@ -1067,9 +1067,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index db8036281..6b1c8ef23 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -1053,9 +1053,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pl.xml b/GUI.NET/Dependencies/resources.pl.xml index 3a003139f..a567c5b4c 100644 --- a/GUI.NET/Dependencies/resources.pl.xml +++ b/GUI.NET/Dependencies/resources.pl.xml @@ -1067,9 +1067,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index 16fdaf7d2..608696da1 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -1067,9 +1067,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index f56f3dd57..eed51f53b 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -1053,9 +1053,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index b1e7dbccd..8b8085079 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -1053,9 +1053,6 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) - NTSC 2x (LMP88959) - NTSC 4x (LMP88959) - NTSC 8x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.zh.xml b/GUI.NET/Dependencies/resources.zh.xml index b51b73be9..615590866 100644 --- a/GUI.NET/Dependencies/resources.zh.xml +++ b/GUI.NET/Dependencies/resources.zh.xml @@ -1079,9 +1079,6 @@ NTSC 2 倍 (Bisqwit) NTSC 4 倍 (Bisqwit) NTSC 8 倍 (Bisqwit) - NTSC 2 倍 (LMP88959) - NTSC 4 倍 (LMP88959) - NTSC 8 倍 (LMP88959) xBRZ 2 倍 xBRZ 3 倍 xBRZ 4 倍 diff --git a/GUI.NET/Forms/Config/frmVideoConfig.cs b/GUI.NET/Forms/Config/frmVideoConfig.cs index a7bdb0b19..1e947a52e 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.cs @@ -197,8 +197,6 @@ protected override bool ValidateInput() } break; case VideoFilterType.LMP88959Ntsc: - case VideoFilterType.LMP88959NtscHalfRes: - case VideoFilterType.LMP88959NtscQuarterRes: { tlpNtscFilter1.Visible = false; tlpNtscFilter2.Visible = false; diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index b10df4d6a..8559ff38a 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -106,9 +106,7 @@ private void InitializeComponent() this.mnuNtscBisqwitQuarterFilter = new System.Windows.Forms.ToolStripMenuItem(); this.mnuNtscBisqwitHalfFilter = new System.Windows.Forms.ToolStripMenuItem(); this.mnuNtscBisqwitFullFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscLMP88959QuarterFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscLMP88959HalfFilter = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuNtscLMP88959FullFilter = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuNtscLMP88959Filter = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); this.mnuXBRZ2xFilter = new System.Windows.Forms.ToolStripMenuItem(); this.mnuXBRZ3xFilter = new System.Windows.Forms.ToolStripMenuItem(); @@ -834,9 +832,7 @@ private void InitializeComponent() this.mnuNtscBisqwitQuarterFilter, this.mnuNtscBisqwitHalfFilter, this.mnuNtscBisqwitFullFilter, - this.mnuNtscLMP88959QuarterFilter, - this.mnuNtscLMP88959HalfFilter, - this.mnuNtscLMP88959FullFilter, + this.mnuNtscLMP88959Filter, this.toolStripMenuItem15, this.mnuXBRZ2xFilter, this.mnuXBRZ3xFilter, @@ -909,26 +905,12 @@ private void InitializeComponent() this.mnuNtscBisqwitFullFilter.Text = "NTSC 8x (Bisqwit)"; this.mnuNtscBisqwitFullFilter.Click += new System.EventHandler(this.mnuNtscBisqwitFullFilter_Click); // - // mnuNtscLMP88959QuarterFilter + // mnuNtscLMP88959Filter // - this.mnuNtscLMP88959QuarterFilter.Name = "mnuNtscLMP88959QuarterFilter"; - this.mnuNtscLMP88959QuarterFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscLMP88959QuarterFilter.Text = "NTSC 2x (LMP88959)"; - this.mnuNtscLMP88959QuarterFilter.Click += new System.EventHandler(this.mnuNtscLMP88959QuarterFilter_Click); - // - // mnuNtscLMP88959HalfFilter - // - this.mnuNtscLMP88959HalfFilter.Name = "mnuNtscLMP88959HalfFilter"; - this.mnuNtscLMP88959HalfFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscLMP88959HalfFilter.Text = "NTSC 4x (LMP88959)"; - this.mnuNtscLMP88959HalfFilter.Click += new System.EventHandler(this.mnuNtscLMP88959HalfFilter_Click); - // - // mnuNtscLMP88959FullFilter - // - this.mnuNtscLMP88959FullFilter.Name = "mnuNtscLMP88959FullFilter"; - this.mnuNtscLMP88959FullFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscLMP88959FullFilter.Text = "NTSC 8x (LMP88959)"; - this.mnuNtscLMP88959FullFilter.Click += new System.EventHandler(this.mnuNtscLMP88959FullFilter_Click); + this.mnuNtscLMP88959Filter.Name = "mnuNtscLMP88959Filter"; + this.mnuNtscLMP88959Filter.Size = new System.Drawing.Size(206, 22); + this.mnuNtscLMP88959Filter.Text = "NTSC (LMP88959)"; + this.mnuNtscLMP88959Filter.Click += new System.EventHandler(this.mnuNtscLMP88959Filter_Click); // // toolStripMenuItem15 // @@ -1123,28 +1105,28 @@ private void InitializeComponent() // mnuRegionAuto // this.mnuRegionAuto.Name = "mnuRegionAuto"; - this.mnuRegionAuto.Size = new System.Drawing.Size(108, 22); + this.mnuRegionAuto.Size = new System.Drawing.Size(180, 22); this.mnuRegionAuto.Text = "Auto"; this.mnuRegionAuto.Click += new System.EventHandler(this.mnuRegion_Click); // // mnuRegionNtsc // this.mnuRegionNtsc.Name = "mnuRegionNtsc"; - this.mnuRegionNtsc.Size = new System.Drawing.Size(108, 22); + this.mnuRegionNtsc.Size = new System.Drawing.Size(180, 22); this.mnuRegionNtsc.Text = "NTSC"; this.mnuRegionNtsc.Click += new System.EventHandler(this.mnuRegion_Click); // // mnuRegionPal // this.mnuRegionPal.Name = "mnuRegionPal"; - this.mnuRegionPal.Size = new System.Drawing.Size(108, 22); + this.mnuRegionPal.Size = new System.Drawing.Size(180, 22); this.mnuRegionPal.Text = "PAL"; this.mnuRegionPal.Click += new System.EventHandler(this.mnuRegion_Click); // // mnuRegionDendy // this.mnuRegionDendy.Name = "mnuRegionDendy"; - this.mnuRegionDendy.Size = new System.Drawing.Size(108, 22); + this.mnuRegionDendy.Size = new System.Drawing.Size(180, 22); this.mnuRegionDendy.Text = "Dendy"; this.mnuRegionDendy.Click += new System.EventHandler(this.mnuRegion_Click); // @@ -2005,9 +1987,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitHalfFilter; private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitFullFilter; private System.Windows.Forms.ToolStripMenuItem mnuNtscBisqwitQuarterFilter; - private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959HalfFilter; - private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959FullFilter; - private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959QuarterFilter; + private System.Windows.Forms.ToolStripMenuItem mnuNtscLMP88959Filter; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22; private System.Windows.Forms.ToolStripMenuItem mnuVideoRecorder; private System.Windows.Forms.ToolStripMenuItem mnuAviRecord; diff --git a/GUI.NET/Forms/frmMain.Options.cs b/GUI.NET/Forms/frmMain.Options.cs index bcbca5f7c..820489475 100644 --- a/GUI.NET/Forms/frmMain.Options.cs +++ b/GUI.NET/Forms/frmMain.Options.cs @@ -371,19 +371,9 @@ private void mnuNtscBisqwitQuarterFilter_Click(object sender, EventArgs e) SetVideoFilter(VideoFilterType.BisqwitNtscQuarterRes); } - private void mnuNtscLMP88959FullFilter_Click(object sender, EventArgs e) + private void mnuNtscLMP88959Filter_Click(object sender, EventArgs e) { SetVideoFilter(VideoFilterType.LMP88959Ntsc); } - - private void mnuNtscLMP88959HalfFilter_Click(object sender, EventArgs e) - { - SetVideoFilter(VideoFilterType.LMP88959NtscHalfRes); - } - - private void mnuNtscLMP88959QuarterFilter_Click(object sender, EventArgs e) - { - SetVideoFilter(VideoFilterType.LMP88959NtscQuarterRes); - } } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 8db7e4d0e..820b60c4e 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -1363,9 +1363,7 @@ private void UpdateMenus() mnuNtscBisqwitQuarterFilter.Enabled = !isHdPackLoader; mnuNtscBisqwitHalfFilter.Enabled = !isHdPackLoader; mnuNtscBisqwitFullFilter.Enabled = !isHdPackLoader; - mnuNtscLMP88959QuarterFilter.Enabled = !isHdPackLoader; - mnuNtscLMP88959HalfFilter.Enabled = !isHdPackLoader; - mnuNtscLMP88959FullFilter.Enabled = !isHdPackLoader; + mnuNtscLMP88959Filter.Enabled = !isHdPackLoader; } } catch { } } diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 907bf9554..a4e36936e 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -2360,9 +2360,7 @@ public enum VideoFilterType Prescale6x = 22, Prescale8x = 23, Prescale10x = 24, - LMP88959NtscQuarterRes = 26, - LMP88959NtscHalfRes = 27, - LMP88959Ntsc = 28, + LMP88959Ntsc = 26, } public enum HDPackOuputTileType diff --git a/Libretro/libretro.cpp b/Libretro/libretro.cpp index 190dae4f6..584789fb2 100644 --- a/Libretro/libretro.cpp +++ b/Libretro/libretro.cpp @@ -406,15 +406,7 @@ extern "C" { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtsc); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } - else if (value == "LMP88959 2x") { - _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959NtscQuarterRes); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); - } - else if (value == "LMP88959 4x") { - _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959NtscHalfRes); - _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); - } - else if (value == "LMP88959 8x") { + else if (value == "LMP88959") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::LMP88959Ntsc); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, true, true, true, 0); } @@ -1212,9 +1204,7 @@ extern "C" { case VideoFilterType::BisqwitNtscQuarterRes: hscale = 2; break; case VideoFilterType::BisqwitNtscHalfRes: hscale = 4; break; case VideoFilterType::BisqwitNtsc: hscale = 8; break; - case VideoFilterType::LMP88959NtscQuarterRes: hscale = 2; break; - case VideoFilterType::LMP88959NtscHalfRes: hscale = 4; break; - case VideoFilterType::LMP88959Ntsc: hscale = 8; break; + case VideoFilterType::LMP88959Ntsc: hscale = 2; break; default: hscale = 1; break; } diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index a303bd762..768cb5b48 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -51,13 +51,13 @@ /* convert pixel offset to its corresponding point on the sampled line */ #define PPUpx2pos(PPUpx) ((PPUpx) * CRT_HRES / LINE_PPUpx) /* starting points for all the different pulses */ -#define FP_BEG PPUpx2pos(0) -#define SYNC_BEG PPUpx2pos(FP_PPUpx) -#define BW_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx) -#define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) -#define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) -#define AV_BEG PPUpx2pos(HB_PPUpx) -#define AV_LEN PPUpx2pos(AV_PPUpx) +#define FP_BEG PPUpx2pos(0) /* front porch point */ +#define SYNC_BEG PPUpx2pos(FP_PPUpx) /* sync tip point */ +#define BW_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx) /* breezeway point */ +#define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) /* color burst point */ +#define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) /* back porch point */ +#define AV_BEG PPUpx2pos(HB_PPUpx) /* active video point */ +#define AV_LEN PPUpx2pos(AV_PPUpx) /* active video length */ /* somewhere between 7 and 12 cycles */ #define CB_CYCLES 10 @@ -373,259 +373,6 @@ crt_init(struct CRT *v, int w, int h, int *out) init_iir(&iirQ, L_FREQ, Q_FREQ); } -extern void -crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s) -{ - int x, y, xo, yo; - int destw = AV_LEN; - int desth = ((CRT_LINES * 64500) >> 16); - int n; -#if CRT_DO_BLOOM - if (s->raw) { - destw = s->w; - desth = s->h; - if (destw > ((AV_LEN * 55500) >> 16)) { - destw = ((AV_LEN * 55500) >> 16); - } - if (desth > ((CRT_LINES * 63500) >> 16)) { - desth = ((CRT_LINES * 63500) >> 16); - } - } else { - destw = (AV_LEN * 55500) >> 16; - desth = (CRT_LINES * 63500) >> 16; - } -#else - if (s->raw) { - destw = s->w; - desth = s->h; - if (destw > AV_LEN) { - destw = AV_LEN; - } - if (desth > ((CRT_LINES * 64500) >> 16)) { - desth = ((CRT_LINES * 64500) >> 16); - } - } -#endif - - xo = AV_BEG + 4 + (AV_LEN - destw) / 2; - yo = CRT_TOP + 4 + (CRT_LINES - desth) / 2; - - s->field &= 1; - - /* align signal */ - xo = (xo & ~3); - - for (n = 0; n < CRT_VRES; n++) { - int t; /* time */ - signed char *line = &v->analog[n * CRT_HRES]; - - t = LINE_BEG; - - if (n <= 3 || (n >= 7 && n <= 9)) { - /* equalizing pulses - small blips of sync, mostly blank */ - while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - } else if (n >= 4 && n <= 6) { - int even[4] = { 46, 50, 96, 100 }; - int odd[4] = { 4, 50, 96, 100 }; - int *offs = even; - if (s->field == 1) { - offs = odd; - } - /* vertical sync pulse - small blips of blank, mostly sync */ - while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - } else { - /* video line */ - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ - if (n < CRT_TOP) { - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - } - if (s->as_color) { - int cb; - /* CB_CYCLES of color burst at 3.579545 Mhz */ - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { - cb = s->cc[(t + 0) & 3]; - line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; - } - } - } - } - - for (y = 0; y < desth; y++) { - int field_offset; - int syA, syB; - - field_offset = (s->field * s->h + desth) / desth / 2; - syA = (y * s->h) / desth; - syB = (y * s->h + desth / 2) / desth; - - syA += field_offset; - syB += field_offset; - - if (syA >= s->h) syA = s->h; - if (syB >= s->h) syB = s->h; - - syA *= s->w; - syB *= s->w; - - reset_iir(&iirY); - reset_iir(&iirI); - reset_iir(&iirQ); - - for (x = 0; x < destw; x++) { - int fy, fi, fq; - int pA, pB; - int rA, gA, bA; - int rB, gB, bB; - int sx, ph; - int ire; /* composite signal */ - - sx = (x * s->w) / destw; - pA = s->rgb[sx + syA]; - pB = s->rgb[sx + syB]; - rA = (pA >> 16) & 0xff; - gA = (pA >> 8) & 0xff; - bA = (pA >> 0) & 0xff; - rB = (pB >> 16) & 0xff; - gB = (pB >> 8) & 0xff; - bB = (pB >> 0) & 0xff; - - /* RGB to YIQ blend with potential pixel below */ - fy = (19595 * rA + 38470 * gA + 7471 * bA - + 19595 * rB + 38470 * gB + 7471 * bB) >> 15; - fi = (39059 * rA - 18022 * gA - 21103 * bA - + 39059 * rB - 18022 * gB - 21103 * bB) >> 15; - fq = (13894 * rA - 34275 * gA + 20382 * bA - + 13894 * rB - 34275 * gB + 20382 * bB) >> 15; - ph = CC_PHASE(y + yo); - ire = BLACK_LEVEL + v->black_point; - /* bandlimit Y,I,Q */ - fy = iirf(&iirY, fy); - fi = iirf(&iirI, fi) * ph * s->cc[(x + 0) & 3] / s->ccs; - fq = iirf(&iirQ, fq) * ph * s->cc[(x + 3) & 3] / s->ccs; - ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; - if (ire < 0) ire = 0; - if (ire > 110) ire = 110; - - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; - } - } -} - -extern void -crt_2ntscFS(struct CRT *v, struct NTSC_SETTINGS *s) -{ - int x, y, xo, yo; - int destw = AV_LEN; - int desth = CRT_LINES; - int n; - - xo = AV_BEG; - yo = CRT_TOP; - - s->field &= 1; - - /* align signal */ - xo = (xo & ~3); - - for (n = 0; n < CRT_VRES; n++) { - int t; /* time */ - signed char *line = &v->analog[n * CRT_HRES]; - - t = LINE_BEG; - - if (n <= 3 || (n >= 7 && n <= 9)) { - /* equalizing pulses - small blips of sync, mostly blank */ - while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - } else if (n >= 4 && n <= 6) { - int even[4] = { 46, 50, 96, 100 }; - int odd[4] = { 4, 50, 96, 100 }; - int *offs = even; - if (s->field == 1) { - offs = odd; - } - /* vertical sync pulse - small blips of blank, mostly sync */ - while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - } else { - /* video line */ - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ - if (n < CRT_TOP) { - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - } - if (s->as_color) { - int cb; - - /* CB_CYCLES of color burst at 3.579545 Mhz */ - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { - cb = s->cc[(t + 0) & 3]; - line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; - } - } - } - } - - for (y = 0; y < desth; y++) { - int field_offset; - int sy; - - field_offset = (s->field * s->h + desth) / desth / 2; - sy = (y * s->h) / desth; - - sy += field_offset; - - if (sy >= s->h) sy = s->h; - - sy *= s->w; - - reset_iir(&iirY); - reset_iir(&iirI); - reset_iir(&iirQ); - - for (x = 0; x < destw; x++) { - int fy, fi, fq; - int pA, rA, gA, bA; - int sx, ph; - int ire; /* composite signal */ - - sx = (x * s->w) / destw; - pA = s->rgb[sx + sy]; - rA = (pA >> 16) & 0xff; - gA = (pA >> 8) & 0xff; - bA = (pA >> 0) & 0xff; - - fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; - fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; - fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; - ph = CC_PHASE(y + yo); - ire = BLACK_LEVEL + v->black_point; - /* bandlimit Y,I,Q */ - fy = iirf(&iirY, fy); - fi = iirf(&iirI, fi) * ph * s->cc[(x + 0) & 3] / s->ccs; - fq = iirf(&iirQ, fq) * ph * s->cc[(x + 3) & 3] / s->ccs; - ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; - if (ire < 0) ire = 0; - if (ire > 110) ire = 110; - - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; - } - } -} - /* generate the square wave for a given 9-bit pixel and phase */ static int square_sample(int pixel_color, int phase) @@ -705,8 +452,8 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } #endif - xo = AV_BEG + 4 + (AV_LEN - destw) / 2; - yo = CRT_TOP + 4 + (CRT_LINES - desth) / 2; + xo = AV_BEG; + yo = CRT_TOP; /* align signal */ xo = (xo & ~3); @@ -727,7 +474,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } #else lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ - po = lo; /* phase offset for color burst */ + po = lo; if (lo == 1) { lo = 3; } @@ -738,42 +485,33 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) t = LINE_BEG; - if (n <= 3 || (n >= 7 && n <= 9)) { - /* equalizing pulses - small blips of sync, mostly blank */ - while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - } else if (n >= 4 && n <= 6) { - int even[4] = { 46, 50, 96, 100 }; - int *offs = even; /* always progressive */ - /* vertical sync pulse - small blips of blank, mostly sync */ - while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; - while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; - while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + // vertical sync scanlines + if (n >= 259 && n <= CRT_VRES) { + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ } else { - /* video line */ + /* prerender/postrender/video scanlines */ while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ - if (n < CRT_TOP) { - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - } - if (s->as_color) { + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) { /* BW + CB + BP */ + while (t < CB_BEG) line[t++] = BLANK_LEVEL; int cb; /* CB_CYCLES of color burst at 3.579545 Mhz */ - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); t++) { cb = s->cc[(t + po) & 3]; line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; } + while (t < AV_BEG) line[t++] = BLANK_LEVEL; + line[t++] = BLANK_LEVEL; } + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; } } - phase = 0; + phase = (s->dot_crawl_offset % 3) * 4; - for (y = lo; y < desth; y++) { + for (y = (lo - 3); y < desth; y++) { int sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; @@ -789,8 +527,6 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; - if (ire < 0) ire = 0; - if (ire > 110) ire = 110; v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; phase += 3; } @@ -800,8 +536,8 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } /* search windows, in samples */ -#define HSYNC_WINDOW 4 -#define VSYNC_WINDOW 6 +#define HSYNC_WINDOW 8 +#define VSYNC_WINDOW 8 extern void crt_draw(struct CRT *v) @@ -872,7 +608,7 @@ crt_draw(struct CRT *v) #if CRT_DO_VSYNC v->vsync = line; /* vsync found (or gave up) at this line */ #else - v->vsync = 0; + v->vsync = 4; #endif /* if vsync signal was in second half of line, odd field */ field = (j > (CRT_HRES / 2)); @@ -921,7 +657,7 @@ crt_draw(struct CRT *v) #if CRT_DO_HSYNC v->hsync = POSMOD(i + v->hsync, CRT_HRES); #else - v->hsync = 0; + v->hsync = 1; #endif sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ @@ -930,9 +666,9 @@ crt_draw(struct CRT *v) int n = sig[i]; /* mixed with the new sample */ ccref[i & 3] = p + n; } - - xpos = POSMOD(AV_BEG + v->hsync, CRT_HRES); - ypos = POSMOD(line + v->vsync, CRT_VRES); + int xnudge = -3, ynudge = 3; + xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); + ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); pos = xpos + ypos * CRT_HRES; phasealign = pos & 3; diff --git a/Utilities/crt.h b/Utilities/crt.h index 42fb5e894..dcf9de10d 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -29,7 +29,7 @@ extern "C" { */ #define CRT_NES_MODE 1 -#define CRT_NES_HIRES 1 +#define CRT_NES_HIRES 0 /* do bloom emulation (side effect: makes screen have black borders) */ #define CRT_DO_BLOOM 0 @@ -60,7 +60,7 @@ extern "C" { #if CRT_NES_HIRES #define CRT_CB_FREQ 6 /* carrier frequency relative to sample rate */ #else -#define CRT_CB_FREQ 3 /* carrier frequency relative to sample rate */ +#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ #endif #else #define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ @@ -103,45 +103,12 @@ extern void crt_resize(struct CRT *v, int w, int h, int *out); /* Resets the CRT settings back to their defaults */ extern void crt_reset(struct CRT *v); -struct NTSC_SETTINGS { - const int *rgb; /* 32-bit RGB image data (packed as 0xXXRRGGBB) */ - int w, h; /* width and height of image */ - int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ - int as_color; /* 0 = monochrome, 1 = full color */ - int field; /* 0 = even, 1 = odd */ - /* color carrier sine wave. - * ex: { 0, 1, 0, -1 } - * ex: { 1, 0, -1, 0 } - */ - int cc[4]; - /* scale value for values in cc - * for example, if using { 0, 1, 0, -1 }, ccs should be 1. - * however, if using { 0, 16, 0, -16 }, ccs should be 16. - * For best results, don't scale the cc values more than 16. - */ - int ccs; -}; - -/* Convert RGB image to analog NTSC signal - * s - struct containing settings to apply to this field - */ -extern void crt_2ntsc(struct CRT *v, struct NTSC_SETTINGS *s); - -/* Convert RGB image to analog NTSC signal and stretch it to fill - * the entire active video portion of the NTSC signal. - * Does not perform the slight horizontal blending which gets done in crt_2ntsc. - * Good for seeing test patterns. - * s - struct containing settings to apply to this field - * NOTE: raw is ignored in this 'FS' (fill screen) version of the 2ntsc function - */ -extern void crt_2ntscFS(struct CRT *v, struct NTSC_SETTINGS *s); - struct NES_NTSC_SETTINGS { const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ int w, h; /* width and height of image */ int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ - int as_color; /* 0 = monochrome, 1 = full color */ int dot_crawl_offset; /* 0, 1, or 2 */ + int dot_skipped; /* NOTE: NES mode is always progressive */ /* color carrier sine wave. * ex: { 0, 1, 0, -1 } From 4ace70278ad6e4a0ce7ae4a6a749389de92fcb99 Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 02:17:03 +0800 Subject: [PATCH 07/36] Fix hue flickering --- Utilities/crt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 768cb5b48..611503061 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -509,7 +509,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } } - phase = (s->dot_crawl_offset % 3) * 4; + phase = 3; for (y = (lo - 3); y < desth; y++) { int sy = (y * s->h) / desth; From 4a4ce1e373023a3b86d5cfc7e8c1bdfe2dd5b28b Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 03:15:32 +0800 Subject: [PATCH 08/36] Fix issues - Refactor dot skip detection - Avoid accessing null PPU buffer --- Core/LMP88959NtscFilter.cpp | 8 +++++--- Core/PPU.cpp | 3 --- Core/PPU.h | 7 ++++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 572c45ff7..76d92ed56 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -55,9 +55,11 @@ void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _nesNTSC.data = reinterpret_cast(ppuOutputBuffer); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); _nesNTSC.dot_skipped = _console->GetDotSkipped(); - crt_nes2ntsc(&_crt, &_nesNTSC); - crt_draw(&_crt); - GenerateArgbFrame(_frameBuffer); + if (_nesNTSC.data){ + crt_nes2ntsc(&_crt, &_nesNTSC); + crt_draw(&_crt); + GenerateArgbFrame(_frameBuffer); + } } void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 50a9a57d0..267b7e764 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -1016,9 +1016,6 @@ void PPU::ProcessScanline() _cycle = 340; _isDotSkipped = true; } - else { - _isDotSkipped = false; - } } } } diff --git a/Core/PPU.h b/Core/PPU.h index d09d05e29..0fcccf8e2 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -227,7 +227,12 @@ class PPU : public IMemoryHandler, public Snapshotable bool GetDotSkipped() { - return _isDotSkipped; + if (_isDotSkipped) { + _isDotSkipped = false; + return true; + } + else + return _isDotSkipped; } uint32_t GetFrameCycle() From c028173a3a53e9ea661e3a7cdcc1231d81f129f7 Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 03:47:22 +0800 Subject: [PATCH 09/36] Increase colorburst integration rate Thanks Emmir! --- Core/LMP88959NtscFilter.cpp | 2 +- Utilities/crt.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 76d92ed56..a4661538c 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -35,7 +35,7 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _keepVerticalRes = ntscSettings.KeepVerticalResolution; _crt.hue = static_cast(pictureSettings.Hue * 180.0); - _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); + _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 800); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _crt.noise = static_cast(ntscSettings.Noise * 500.0); diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 611503061..8ba612f4f 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -662,7 +662,7 @@ crt_draw(struct CRT *v) sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { - int p = ccref[i & 3] * 127 / 128; /* fraction of the previous */ + int p = ccref[i & 3] * 3 / 4; /* fraction of the previous */ int n = sig[i]; /* mixed with the new sample */ ccref[i & 3] = p + n; } From 664cc799f1585bd6ae6eb8afec41e1da1675cb04 Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 05:42:23 +0800 Subject: [PATCH 10/36] Directly set carrier phase for faster convergence - Begin to implement border color --- Core/LMP88959NtscFilter.cpp | 4 +++- Utilities/crt.cpp | 47 ++++++++++++++++++++++++++++--------- Utilities/crt.h | 2 ++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index a4661538c..e7a4a791f 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -35,13 +35,14 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _keepVerticalRes = ntscSettings.KeepVerticalResolution; _crt.hue = static_cast(pictureSettings.Hue * 180.0); - _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 800); + _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _crt.noise = static_cast(ntscSettings.Noise * 500.0); _nesNTSC.w = static_cast(PPU::ScreenWidth); _nesNTSC.h = static_cast(PPU::ScreenHeight); +// _nesNTSC.raw = 1; _nesNTSC.cc[0] = 0; _nesNTSC.cc[1] = 16; _nesNTSC.cc[2] = 0; @@ -55,6 +56,7 @@ void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _nesNTSC.data = reinterpret_cast(ppuOutputBuffer); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); _nesNTSC.dot_skipped = _console->GetDotSkipped(); + _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; if (_nesNTSC.data){ crt_nes2ntsc(&_crt, &_nesNTSC); crt_draw(&_crt); diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 8ba612f4f..4b14ea0f5 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -33,6 +33,13 @@ * |--------------||---------------||------------||-------------||-------------| * BLANK SYNC BLANK BLANK BLANK * + * + * WITHIN ACTIVE VIDEO PERIOD: + * + * LB (15 PPU px) AV (256 PPU px) RB (11 PPU px) + * |--------------||--------------------------------------------||-------------| + * BORDER VIDEO BORDER + * */ #define LINE_BEG 0 #define FP_PPUpx 9 /* front porch */ @@ -479,11 +486,15 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) lo = 3; } #endif + + phase = 0; + for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; t = LINE_BEG; + phase += (xo * 3); // vertical sync scanlines if (n >= 259 && n <= CRT_VRES) { @@ -501,11 +512,22 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); t++) { cb = s->cc[(t + po) & 3]; line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; + v->ccf[t & 3] = line[t]; } - while (t < AV_BEG) line[t++] = BLANK_LEVEL; - line[t++] = BLANK_LEVEL; + // if (n >= CRT_TOP && n <= CRT_BOT + 2) { + // int ire, p; + // while (t < CRT_HRES) { + // p = s->borderdata; + // ire = BLACK_LEVEL; + // ire += square_sample(p, phase + po); + // ire = (ire * WHITE_LEVEL) >> 12; + // line[t++] = ire; + // phase += 4; + // } + // } else { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + // } } - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; } } @@ -521,12 +543,12 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) int ire, p; p = s->data[((x * s->w) / destw) + sy]; - ire = BLACK_LEVEL + v->black_point; + ire = BLACK_LEVEL; ire += square_sample(p, phase + 0); ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + ire = (ire * WHITE_LEVEL) >> 12; v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; phase += 3; } @@ -536,8 +558,8 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } /* search windows, in samples */ -#define HSYNC_WINDOW 8 -#define VSYNC_WINDOW 8 +#define HSYNC_WINDOW 4 +#define VSYNC_WINDOW 6 extern void crt_draw(struct CRT *v) @@ -554,15 +576,18 @@ crt_draw(struct CRT *v) signed char *sig; int s = 0; int field, ratio; - int ccref[4]; /* color carrier signal */ + static int ccref[4]; /* color carrier signal */ int huesn, huecs; crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 90) * 8192 / 180); huesn >>= 11; /* make 4-bit */ huecs >>= 11; - memset(ccref, 0, sizeof(ccref)); - + ccref[0] = v->ccf[0] << 7; + ccref[1] = v->ccf[1] << 7; + ccref[2] = v->ccf[2] << 7; + ccref[3] = v->ccf[3] << 7; + for (i = 0; i < CRT_INPUT_SIZE; i++) { static int rn = 194; /* 'random' noise */ @@ -662,7 +687,7 @@ crt_draw(struct CRT *v) sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { - int p = ccref[i & 3] * 3 / 4; /* fraction of the previous */ + int p = ccref[i & 3] * 127 / 128; /* fraction of the previous */ int n = sig[i]; /* mixed with the new sample */ ccref[i & 3] = p + n; } diff --git a/Utilities/crt.h b/Utilities/crt.h index dcf9de10d..5f3a38e95 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -84,6 +84,7 @@ struct CRT { int noise; /* noise level */ int outw, outh; /* output width/height */ int *out; /* output image */ + int ccf[4]; /* color carrier reference for faster convergence */ }; /* Initializes the library. Sets up filters. @@ -105,6 +106,7 @@ extern void crt_reset(struct CRT *v); struct NES_NTSC_SETTINGS { const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ + unsigned int borderdata; /* border color, either BG or black */ int w, h; /* width and height of image */ int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ int dot_crawl_offset; /* 0, 1, or 2 */ From fd1d4de8d002a123ab526818ff01e795ede474ac Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 12:10:54 +0800 Subject: [PATCH 11/36] Implement border color and gray pulse - Begin to optimize NTSC filter code --- Core/LMP88959NtscFilter.cpp | 8 ++--- Core/LMP88959NtscFilter.h | 1 + Utilities/crt.cpp | 60 ++++++++++++++++++++++--------------- Utilities/crt.h | 2 +- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index e7a4a791f..ad8e2c6cb 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -42,7 +42,6 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _nesNTSC.w = static_cast(PPU::ScreenWidth); _nesNTSC.h = static_cast(PPU::ScreenHeight); -// _nesNTSC.raw = 1; _nesNTSC.cc[0] = 0; _nesNTSC.cc[1] = 16; _nesNTSC.cc[2] = 0; @@ -53,15 +52,16 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - _nesNTSC.data = reinterpret_cast(ppuOutputBuffer); + _ppuOutputBuffer = ppuOutputBuffer; + _nesNTSC.data = reinterpret_cast(_ppuOutputBuffer); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); _nesNTSC.dot_skipped = _console->GetDotSkipped(); _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; - if (_nesNTSC.data){ + if (_ppuOutputBuffer != nullptr){ crt_nes2ntsc(&_crt, &_nesNTSC); crt_draw(&_crt); - GenerateArgbFrame(_frameBuffer); } + GenerateArgbFrame(_frameBuffer); } void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index 7f4c0f0b7..b03131bb0 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -12,6 +12,7 @@ class LMP88959NtscFilter : public BaseVideoFilter struct CRT _crt; bool _keepVerticalRes = false; uint32_t* _frameBuffer; + uint16_t* _ppuOutputBuffer; bool _ntscBorder = true; void GenerateArgbFrame(uint32_t* frameBuffer); diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 4b14ea0f5..e8e1999e0 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -64,6 +64,7 @@ #define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) /* color burst point */ #define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) /* back porch point */ #define AV_BEG PPUpx2pos(HB_PPUpx) /* active video point */ +#define PPUAV_BEG PPUpx2pos(HB_PPUpx + PS_PPUpx + LB_PPUpx) /* PPU active video point */ #define AV_LEN PPUpx2pos(AV_PPUpx) /* active video length */ /* somewhere between 7 and 12 cycles */ @@ -351,8 +352,8 @@ crt_reset(struct CRT *v) v->saturation = 18; v->brightness = 0; v->contrast = 180; - v->black_point = 0; - v->white_point = 100; + //v->black_point = 0; + //v->white_point = 100; v->noise = 0; v->hsync = 0; v->vsync = 0; @@ -459,9 +460,9 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } #endif - xo = AV_BEG; + xo = PPUAV_BEG; yo = CRT_TOP; - + /* align signal */ xo = (xo & ~3); #if CRT_NES_HIRES @@ -487,14 +488,13 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } #endif - phase = 0; + phase = (1 + po) * 3; for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; t = LINE_BEG; - phase += (xo * 3); // vertical sync scanlines if (n >= 259 && n <= CRT_VRES) { @@ -509,24 +509,36 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) while (t < CB_BEG) line[t++] = BLANK_LEVEL; int cb; /* CB_CYCLES of color burst at 3.579545 Mhz */ - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); t++) { + int skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { cb = s->cc[(t + po) & 3]; line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; v->ccf[t & 3] = line[t]; } - // if (n >= CRT_TOP && n <= CRT_BOT + 2) { - // int ire, p; - // while (t < CRT_HRES) { - // p = s->borderdata; - // ire = BLACK_LEVEL; - // ire += square_sample(p, phase + po); - // ire = (ire * WHITE_LEVEL) >> 12; - // line[t++] = ire; - // phase += 4; - // } - // } else { + t = AV_BEG; + } + while (t < CRT_HRES) { + phase += t * 3; + if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { + while (t < CRT_HRES) { + int ire, p; + p = s->borderdata; + if (t == AV_BEG) p = 0xF0; + ire = BLACK_LEVEL; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + ire = (ire * WHITE_LEVEL) >> 12; + line[t++] = ire; + phase += 3; + } + } + else { while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - // } + phase += (CRT_HRES - AV_BEG) * 3; + } + phase %= 12; } } } @@ -543,7 +555,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) int ire, p; p = s->data[((x * s->w) / destw) + sy]; - ire = BLACK_LEVEL; + ire = BLANK_LEVEL; ire += square_sample(p, phase + 0); ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); @@ -558,7 +570,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) } /* search windows, in samples */ -#define HSYNC_WINDOW 4 +#define HSYNC_WINDOW 6 #define VSYNC_WINDOW 6 extern void @@ -572,12 +584,13 @@ crt_draw(struct CRT *v) int prev_e; /* filtered beam energy per scan line */ int max_e; /* approx maximum energy in a scan line */ #endif - int bright = v->brightness - (BLACK_LEVEL + v->black_point); + int bright = v->brightness - (BLACK_LEVEL); //+ v->black_point); signed char *sig; int s = 0; int field, ratio; static int ccref[4]; /* color carrier signal */ int huesn, huecs; + int xnudge = -3, ynudge = 3; crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 90) * 8192 / 180); huesn >>= 11; /* make 4-bit */ @@ -691,8 +704,7 @@ crt_draw(struct CRT *v) int n = sig[i]; /* mixed with the new sample */ ccref[i & 3] = p + n; } - int xnudge = -3, ynudge = 3; - xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); + xpos = POSMOD(PPUAV_BEG + v->hsync + xnudge, CRT_HRES); ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); pos = xpos + ypos * CRT_HRES; phasealign = pos & 3; diff --git a/Utilities/crt.h b/Utilities/crt.h index 5f3a38e95..10f9d84bc 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -80,7 +80,7 @@ struct CRT { signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ int hsync, vsync; /* used internally to keep track of sync over frames */ int hue, brightness, contrast, saturation; /* common monitor settings */ - int black_point, white_point; /* user-adjustable */ + //int black_point, white_point; /* user-adjustable */ not needed int noise; /* noise level */ int outw, outh; /* output width/height */ int *out; /* output image */ From 0f19ef1ff7278da6039a7cdeed71a9480c06a4fc Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 16:13:43 +0800 Subject: [PATCH 12/36] Precalculate composite voltage values --- Core/BisqwitNtscFilter.cpp | 21 ------- Core/BisqwitNtscFilter.h | 53 +++++++++++++++++- Core/LMP88959NtscFilter.cpp | 8 +-- Utilities/crt.cpp | 107 ++++++++++++++++++++++++++---------- 4 files changed, 133 insertions(+), 56 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index fc62ba1bb..4368f89c7 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -13,27 +13,6 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider _resDivider = resDivider; _stopThread = false; _workDone = false; - - const int8_t signalLumaLow[2][4] = { { -29, -15, 22, 71 }, { -38, -28, -1, 34 } }; - const int8_t signalLumaHigh[2][4] = { { 32, 66, 105, 105 }, { 6, 31, 58, 58 } }; - - //Precalculate the low and high signal chosen for each 64 base colors - //with their respective attenuated values - for (int h = 0; h <= 1; h++) { - for(int i = 0; i <= 0x3F; i++) { - int r = (i & 0x0F) >= 0x0E ? 0x1D : i; - - int m = signalLumaLow[h][r / 0x10]; - int q = signalLumaHigh[h][r / 0x10]; - if((r & 0x0F) == 13) { - q = m; - } else if((r & 0x0F) == 0) { - m = q; - } - _signalLow[h][i] = m; - _signalHigh[h][i] = q; - } - } _extraThread = std::thread([=]() { //Worker thread to improve decode speed while(!_stopThread) { diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index f0fb89674..6ee0ec755 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -38,8 +38,57 @@ class BisqwitNtscFilter : public BaseVideoFilter //To finetune hue, you would have to recalculate sinetable[]. (Coarse changes can be made with Phase0.) int8_t _sinetable[27]; // 8*sin(x*2pi/12) - int8_t _signalLow[2][0x40]; - int8_t _signalHigh[2][0x40]; + //Precalculate the low and high signal chosen for each 64 base colors + //with their respective attenuated values + const int8_t _signalLow[2][0x40] = { + // normal + { + // 0x + 32, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -15, -15, + // 1x + 66, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, + // 2x + 110, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, -15, -15, + // 3x + 110, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, -15, -15 + }, + // attenuated + { + // 0x + 26, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -15, -15, + // 1x + 51, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -15, -15, + // 2x + 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -15, -15, + // 3x + 82, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, -15, -15 + } + }; + + const int8_t _signalHigh[2][0x40] = { + // normal + { + // 0x + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -29, -15, -15, + // 1x + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, -15, -15, -15, + // 2x + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 22, -15, -15, + // 3x + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 71, -15, -15, + }, + // attenuated + { + // 0x + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -38, -15, -15, + // 1x + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, -28, -15, -15, + // 2x + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, -1, -15, -15, + // 3x + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 34, -15, -15, + } + }; void RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend); diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index ad8e2c6cb..463588ad8 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -57,10 +57,10 @@ void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); _nesNTSC.dot_skipped = _console->GetDotSkipped(); _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; - if (_ppuOutputBuffer != nullptr){ - crt_nes2ntsc(&_crt, &_nesNTSC); - crt_draw(&_crt); - } + + crt_nes2ntsc(&_crt, &_nesNTSC); + crt_draw(&_crt); + GenerateArgbFrame(_frameBuffer); } diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index e8e1999e0..2be450f0a 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -78,7 +78,7 @@ /* IRE units (100 = 1.0V, -40 = 0.0V) */ /* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ -#define WHITE_LEVEL 100 +#define WHITE_LEVEL 110 #define BURST_LEVEL 30 #define BLACK_LEVEL 0 #define BLANK_LEVEL 0 @@ -333,6 +333,62 @@ iirf(struct IIRLP *f, int s) #endif } +//Precalculate the low and high signal chosen for each 64 base colors +//with their respective attenuated values +// https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels +const int8_t IRE_levels[2][2][0x40] { + // waveform low + { + // normal + { + // 0x + 43, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, + // 1x + 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 2x + 110, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 0, 0, + // 3x + 110, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0 + }, + // attenuated + { + // 0x + 26 , -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, 0, 0, + // 1x + 51, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, 0, 0, + // 2x + 82, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, + // 3x + 82, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0 + } + }, + // waveform high + { + // normal + { + // 0x + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -12, 0, 0, + // 1x + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 0, 0, 0, + // 2x + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 34, 0, 0, + // 3x + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 80, 0, 0 + }, + // attenuated + { + // 0x + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -17, 0, 0, + // 1x + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, -8, 0, 0, + // 2x + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 19, 0, 0, + // 3x + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 56, 0, 0 + } + } +}; + /*****************************************************************************/ /***************************** PUBLIC FUNCTIONS ******************************/ /*****************************************************************************/ @@ -390,38 +446,30 @@ square_sample(int pixel_color, int phase) 0x140, 0x100, 0x180, 0x080 }; - int bri, hue, voltage; + int pixel_index, hue, level, emphasis = 0; + pixel_index = pixel_color & 0x3F; + hue = (pixel_index & 0x0f); - hue = (pixel_color & 0x0f); - - /* last two columns are black */ - if (hue >= 0x0e) { - return 0; - } + if (hue >= 0x0e) return 0; - bri = ((pixel_color & 0x30) >> 4) * 300; - switch (hue) { - case 0: - voltage = bri + 410; - break; - case 0x0d: - voltage = bri - 300; - break; - default: - voltage = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); - break; + case 0: + level = 1; + break; + case 0x0d: + level = 0; + break; + default: + level = (((hue + phase) % 12) < 6); + break; } - if (voltage > 1024) { - voltage = 1024; - } /* red 0100, green 0200, blue 0400 */ - if ((pixel_color & 0x1C0) & active[(phase >> 1) % 6]) { - return (voltage >> 1) + (voltage >> 2); + if (((pixel_color & 0x1C0) & active[(phase >> 1) % 6]) && hue < 0x0e) { + emphasis = 1; } - return voltage; + return IRE_levels[level][emphasis][(pixel_index)]; } extern void @@ -529,7 +577,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * WHITE_LEVEL) >> 12; + ire >>= 2; line[t++] = ire; phase += 3; } @@ -548,6 +596,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) for (y = (lo - 3); y < desth; y++) { int sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; + if (sy < 0) sy = 0; sy *= s->w; phase += (xo * 3); @@ -560,7 +609,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * WHITE_LEVEL) >> 12; + ire >>= 2; v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; phase += 3; } @@ -646,7 +695,7 @@ crt_draw(struct CRT *v) #if CRT_DO_VSYNC v->vsync = line; /* vsync found (or gave up) at this line */ #else - v->vsync = 4; + v->vsync = -3; #endif /* if vsync signal was in second half of line, odd field */ field = (j > (CRT_HRES / 2)); @@ -695,7 +744,7 @@ crt_draw(struct CRT *v) #if CRT_DO_HSYNC v->hsync = POSMOD(i + v->hsync, CRT_HRES); #else - v->hsync = 1; + v->hsync = 3; #endif sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ From 1aaad191acd0b451bd21984334b79ce0644374fe Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 16:39:19 +0800 Subject: [PATCH 13/36] Properly name resource label --- GUI.NET/Dependencies/resources.ca.xml | 1 + GUI.NET/Dependencies/resources.en.xml | 3 +- GUI.NET/Dependencies/resources.es.xml | 1 + GUI.NET/Dependencies/resources.fr.xml | 1 + GUI.NET/Dependencies/resources.it.xml | 1 + GUI.NET/Dependencies/resources.ja.xml | 1 + GUI.NET/Dependencies/resources.pl.xml | 1 + GUI.NET/Dependencies/resources.pt.xml | 1 + GUI.NET/Dependencies/resources.ru.xml | 1 + GUI.NET/Dependencies/resources.uk.xml | 1 + GUI.NET/Dependencies/resources.zh.xml | 1 + .../Forms/Config/frmVideoConfig.Designer.cs | 40 +++++++++---------- GUI.NET/Forms/frmMain.Designer.cs | 2 +- 13 files changed, 33 insertions(+), 22 deletions(-) diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index 9a2581918..4cb22272c 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -1035,6 +1035,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index 19cb2583e..ed3ff0309 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -49,7 +49,7 @@ Fullscreen Video Filter None - NTSC 2x (blargg) + NTSC Filter Use Bilinear Interpolation Audio Input @@ -1139,6 +1139,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index a97186424..8812e6238 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -1051,6 +1051,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 5819130a3..21e229575 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -1065,6 +1065,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.it.xml b/GUI.NET/Dependencies/resources.it.xml index 5e1c1b2e2..c14b5cbd2 100644 --- a/GUI.NET/Dependencies/resources.it.xml +++ b/GUI.NET/Dependencies/resources.it.xml @@ -1067,6 +1067,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index 6b1c8ef23..e65000801 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -1053,6 +1053,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pl.xml b/GUI.NET/Dependencies/resources.pl.xml index a567c5b4c..655f4d166 100644 --- a/GUI.NET/Dependencies/resources.pl.xml +++ b/GUI.NET/Dependencies/resources.pl.xml @@ -1067,6 +1067,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index 608696da1..a61fb7f33 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -1067,6 +1067,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index eed51f53b..f4288a372 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -1053,6 +1053,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index 8b8085079..fbbaaf05b 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -1053,6 +1053,7 @@ NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) + NTSC 2x (LMP88959) xBRZ 2x xBRZ 3x xBRZ 4x diff --git a/GUI.NET/Dependencies/resources.zh.xml b/GUI.NET/Dependencies/resources.zh.xml index 615590866..bcaeba608 100644 --- a/GUI.NET/Dependencies/resources.zh.xml +++ b/GUI.NET/Dependencies/resources.zh.xml @@ -1079,6 +1079,7 @@ NTSC 2 倍 (Bisqwit) NTSC 4 倍 (Bisqwit) NTSC 8 倍 (Bisqwit) + NTSC 2 倍 (LMP88959) xBRZ 2 倍 xBRZ 3 倍 xBRZ 4 倍 diff --git a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs index 2737a75d3..76d25c4dd 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs @@ -71,7 +71,7 @@ private void InitializeComponent() this.trkQFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tableLayoutPanel19 = new System.Windows.Forms.TableLayoutPanel(); this.chkColorimetryCorrection = new System.Windows.Forms.CheckBox(); - this.chkVerticalBlend = new System.Windows.Forms.CheckBox(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); this.tlpNtscFilter1 = new System.Windows.Forms.TableLayoutPanel(); this.trkArtifacts = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkBleed = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); @@ -81,6 +81,7 @@ private void InitializeComponent() this.trkSharpness = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel(); this.chkMergeFields = new System.Windows.Forms.CheckBox(); + this.chkVerticalBlend = new System.Windows.Forms.CheckBox(); this.chkUseExternalPalette = new System.Windows.Forms.CheckBox(); this.chkKeepVerticalResolution = new System.Windows.Forms.CheckBox(); this.grpCommon = new System.Windows.Forms.GroupBox(); @@ -166,7 +167,6 @@ private void InitializeComponent() this.mnuPaletteSonyCxa2025As = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPaletteUnsaturated = new System.Windows.Forms.ToolStripMenuItem(); this.mnuPaletteYuv = new System.Windows.Forms.ToolStripMenuItem(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); this.tlpMain.SuspendLayout(); this.flpResolution.SuspendLayout(); this.flowLayoutPanel7.SuspendLayout(); @@ -822,15 +822,15 @@ private void InitializeComponent() this.chkColorimetryCorrection.Text = "Color Correction"; this.chkColorimetryCorrection.UseVisualStyleBackColor = true; // - // chkVerticalBlend + // checkBox1 // - this.chkVerticalBlend.AutoSize = true; - this.chkVerticalBlend.Location = new System.Drawing.Point(3, 28); - this.chkVerticalBlend.Name = "chkVerticalBlend"; - this.chkVerticalBlend.Size = new System.Drawing.Size(134, 17); - this.chkVerticalBlend.TabIndex = 31; - this.chkVerticalBlend.Text = "Apply Vertical Blending"; - this.chkVerticalBlend.UseVisualStyleBackColor = true; + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(3, 28); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(134, 17); + this.checkBox1.TabIndex = 33; + this.checkBox1.Text = "Apply Vertical Blending"; + this.checkBox1.UseVisualStyleBackColor = true; // // tlpNtscFilter1 // @@ -977,6 +977,16 @@ private void InitializeComponent() this.chkMergeFields.Text = "Merge Fields"; this.chkMergeFields.UseVisualStyleBackColor = true; // + // chkVerticalBlend + // + this.chkVerticalBlend.AutoSize = true; + this.chkVerticalBlend.Location = new System.Drawing.Point(3, 28); + this.chkVerticalBlend.Name = "chkVerticalBlend"; + this.chkVerticalBlend.Size = new System.Drawing.Size(134, 17); + this.chkVerticalBlend.TabIndex = 31; + this.chkVerticalBlend.Text = "Apply Vertical Blending"; + this.chkVerticalBlend.UseVisualStyleBackColor = true; + // // chkUseExternalPalette // this.chkUseExternalPalette.AutoSize = true; @@ -2196,16 +2206,6 @@ private void InitializeComponent() this.mnuPaletteYuv.Text = "YUV v3 (by FirebrandX)"; this.mnuPaletteYuv.Click += new System.EventHandler(this.mnuPaletteYuv_Click); // - // checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(3, 28); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(134, 17); - this.checkBox1.TabIndex = 33; - this.checkBox1.Text = "Apply Vertical Blending"; - this.checkBox1.UseVisualStyleBackColor = true; - // // frmVideoConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index 8559ff38a..2170a6fb1 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -909,7 +909,7 @@ private void InitializeComponent() // this.mnuNtscLMP88959Filter.Name = "mnuNtscLMP88959Filter"; this.mnuNtscLMP88959Filter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscLMP88959Filter.Text = "NTSC (LMP88959)"; + this.mnuNtscLMP88959Filter.Text = "NTSC 2x (LMP88959)"; this.mnuNtscLMP88959Filter.Click += new System.EventHandler(this.mnuNtscLMP88959Filter_Click); // // toolStripMenuItem15 From 27e1308be9dca069dc3c5662f15226521184bd83 Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 14 Jan 2023 16:47:55 +0800 Subject: [PATCH 14/36] Optimize NTSC signal encoding --- Utilities/crt.cpp | 70 ++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 2be450f0a..9e3fdf678 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -447,10 +447,10 @@ square_sample(int pixel_color, int phase) 0x180, 0x080 }; int pixel_index, hue, level, emphasis = 0; - pixel_index = pixel_color & 0x3F; + pixel_index = pixel_color & 0x3F; hue = (pixel_index & 0x0f); - if (hue >= 0x0e) return 0; + if (hue >= 0x0e) return 0; switch (hue) { case 0: @@ -466,10 +466,10 @@ square_sample(int pixel_color, int phase) /* red 0100, green 0200, blue 0400 */ if (((pixel_color & 0x1C0) & active[(phase >> 1) % 6]) && hue < 0x0e) { - emphasis = 1; + emphasis = 1; } - return IRE_levels[level][emphasis][(pixel_index)]; + return IRE_levels[level][emphasis][(pixel_index)]; } extern void @@ -553,41 +553,37 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) /* prerender/postrender/video scanlines */ while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < AV_BEG) { /* BW + CB + BP */ - while (t < CB_BEG) line[t++] = BLANK_LEVEL; - int cb; - /* CB_CYCLES of color burst at 3.579545 Mhz */ - int skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { - cb = s->cc[(t + po) & 3]; - line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; - v->ccf[t & 3] = line[t]; - } - t = AV_BEG; + while (t < CB_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + int cb; + /* CB_CYCLES of color burst at 3.579545 Mhz */ + int skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { + cb = s->cc[(t + po) & 3]; + line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; + v->ccf[t & 3] = line[t]; } - while (t < CRT_HRES) { - phase += t * 3; - if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { - while (t < CRT_HRES) { - int ire, p; - p = s->borderdata; - if (t == AV_BEG) p = 0xF0; - ire = BLACK_LEVEL; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - ire >>= 2; - line[t++] = ire; - phase += 3; - } - } - else { - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - phase += (CRT_HRES - AV_BEG) * 3; + while (t < AV_BEG) line[t++] = BLANK_LEVEL; + phase += t * 3; + if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { + while (t < CRT_HRES) { + int ire, p; + p = s->borderdata; + if (t == AV_BEG) p = 0xF0; + ire = BLACK_LEVEL; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + ire >>= 2; + line[t++] = ire; + phase += 3; } - phase %= 12; } + else { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + phase += (CRT_HRES - AV_BEG) * 3; + } + phase %= 12; } } @@ -609,7 +605,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire >>= 2; + ire >>= 2; v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; phase += 3; } From 614834e04b0b3dd3400d1ec1afe41b6658157407 Mon Sep 17 00:00:00 2001 From: Persune Date: Sun, 15 Jan 2023 15:35:44 +0800 Subject: [PATCH 15/36] Fix resource labels --- GUI.NET/Dependencies/resources.ca.xml | 2 +- GUI.NET/Dependencies/resources.en.xml | 2 +- .../Forms/Config/frmVideoConfig.Designer.cs | 50 +++++++++---------- GUI.NET/Forms/Config/frmVideoConfig.resx | 2 +- GUI.NET/Forms/frmMain.Designer.cs | 2 +- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index 4cb22272c..5d56fa99c 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -1031,7 +1031,7 @@ Cap - NTSC (blargg) + NTSC 2x (blargg) NTSC 2x (Bisqwit) NTSC 4x (Bisqwit) NTSC 8x (Bisqwit) diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index ed3ff0309..8d41920cf 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -49,7 +49,7 @@ Fullscreen Video Filter None - NTSC Filter + NTSC 2x (blargg) Use Bilinear Interpolation Audio Input diff --git a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs index 76d25c4dd..589b6199a 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs @@ -650,12 +650,12 @@ private void InitializeComponent() this.tableLayoutPanel7.Controls.Add(this.btnSelectPreset, 1, 0); this.tableLayoutPanel7.Controls.Add(this.btnResetPictureSettings, 0, 0); this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 396); + this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 341); this.tableLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel7.Name = "tableLayoutPanel7"; this.tableLayoutPanel7.RowCount = 1; this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tableLayoutPanel7.Size = new System.Drawing.Size(260, 1); + this.tableLayoutPanel7.Size = new System.Drawing.Size(260, 55); this.tableLayoutPanel7.TabIndex = 3; // // btnSelectPreset @@ -664,10 +664,10 @@ private void InitializeComponent() this.btnSelectPreset.AutoSize = true; this.btnSelectPreset.Image = global::Mesen.GUI.Properties.Resources.DownArrow; this.btnSelectPreset.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; - this.btnSelectPreset.Location = new System.Drawing.Point(158, 3); + this.btnSelectPreset.Location = new System.Drawing.Point(158, 29); this.btnSelectPreset.Name = "btnSelectPreset"; this.btnSelectPreset.Padding = new System.Windows.Forms.Padding(0, 0, 3, 0); - this.btnSelectPreset.Size = new System.Drawing.Size(99, 1); + this.btnSelectPreset.Size = new System.Drawing.Size(99, 23); this.btnSelectPreset.TabIndex = 3; this.btnSelectPreset.Text = "Select Preset..."; this.btnSelectPreset.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; @@ -678,9 +678,9 @@ private void InitializeComponent() // this.btnResetPictureSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnResetPictureSettings.AutoSize = true; - this.btnResetPictureSettings.Location = new System.Drawing.Point(3, 3); + this.btnResetPictureSettings.Location = new System.Drawing.Point(3, 29); this.btnResetPictureSettings.Name = "btnResetPictureSettings"; - this.btnResetPictureSettings.Size = new System.Drawing.Size(75, 1); + this.btnResetPictureSettings.Size = new System.Drawing.Size(75, 23); this.btnResetPictureSettings.TabIndex = 3; this.btnResetPictureSettings.Text = "Reset"; this.btnResetPictureSettings.UseVisualStyleBackColor = true; @@ -1125,7 +1125,7 @@ private void InitializeComponent() this.grpScanlines.Location = new System.Drawing.Point(0, 269); this.grpScanlines.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0); this.grpScanlines.Name = "grpScanlines"; - this.grpScanlines.Size = new System.Drawing.Size(258, 127); + this.grpScanlines.Size = new System.Drawing.Size(258, 72); this.grpScanlines.TabIndex = 5; this.grpScanlines.TabStop = false; this.grpScanlines.Text = "Scanlines"; @@ -2413,24 +2413,24 @@ private void InitializeComponent() private System.Windows.Forms.Label lblGameSpecificOverscanLeft; private System.Windows.Forms.CheckBox chkRemoveSpriteLimit; private System.Windows.Forms.CheckBox chkAdaptiveSpriteLimit; - private System.Windows.Forms.FlowLayoutPanel flpResolution; - private System.Windows.Forms.Label lblFullscreenResolution; - private System.Windows.Forms.ComboBox cboFullscreenResolution; - private System.Windows.Forms.Label lblRequestedRefreshRate2; - private System.Windows.Forms.ComboBox cboRefreshRate2; - private System.Windows.Forms.TableLayoutPanel tlpNtscFilter2; - private ctrlHorizontalTrackbar trkYFilterLength; - private ctrlHorizontalTrackbar trkIFilterLength; - private ctrlHorizontalTrackbar trkQFilterLength; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; - private System.Windows.Forms.CheckBox chkMergeFields; - private System.Windows.Forms.CheckBox chkVerticalBlend; - private System.Windows.Forms.CheckBox chkUseExternalPalette; - private System.Windows.Forms.CheckBox chkKeepVerticalResolution; - private System.Windows.Forms.TableLayoutPanel tlpNtscFilter3; - private ctrlHorizontalTrackbar trkNoise; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel19; + private System.Windows.Forms.FlowLayoutPanel flpResolution; + private System.Windows.Forms.Label lblFullscreenResolution; + private System.Windows.Forms.ComboBox cboFullscreenResolution; + private System.Windows.Forms.Label lblRequestedRefreshRate2; + private System.Windows.Forms.ComboBox cboRefreshRate2; + private System.Windows.Forms.TableLayoutPanel tlpNtscFilter2; + private ctrlHorizontalTrackbar trkYFilterLength; + private ctrlHorizontalTrackbar trkIFilterLength; + private ctrlHorizontalTrackbar trkQFilterLength; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; + private System.Windows.Forms.CheckBox chkMergeFields; + private System.Windows.Forms.CheckBox chkVerticalBlend; + private System.Windows.Forms.CheckBox chkUseExternalPalette; + private System.Windows.Forms.CheckBox chkKeepVerticalResolution; + private System.Windows.Forms.TableLayoutPanel tlpNtscFilter3; + private ctrlHorizontalTrackbar trkNoise; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel19; private System.Windows.Forms.CheckBox chkColorimetryCorrection; - private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.CheckBox checkBox1; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmVideoConfig.resx b/GUI.NET/Forms/Config/frmVideoConfig.resx index 21c167276..60982bfb0 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.resx +++ b/GUI.NET/Forms/Config/frmVideoConfig.resx @@ -128,7 +128,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACs - BQAAAk1TRnQBSQFMAwEBAAFYAQIBWAECARABAAEQAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA + BQAAAk1TRnQBSQFMAwEBAAFwAQIBcAECARABAAEQAQAE/wEZAQAI/wFCAU0BNgcAATYDAAEoAwABQAMA ARADAAEBAQABGAYAAQweAAH5AvgB1QHBAbsBqAFyAWEBkAFHATABkAFHATABpgFuAVwB0gG8AbUB+AL2 pQAB1QHAAbkBlgFNATIBqgFaASwBuwFkASsBwAFpASkBwAFpASkBuwFlASwBqwFbAS0BmAFMATAB0wG9 AbWfAAHRAbgBrwGlAVgBMgHAAW0BLgHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHCAW0BLQHA diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index 2170a6fb1..0a1f0c19e 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -881,7 +881,7 @@ private void InitializeComponent() // this.mnuNtscFilter.Name = "mnuNtscFilter"; this.mnuNtscFilter.Size = new System.Drawing.Size(206, 22); - this.mnuNtscFilter.Text = "NTSC"; + this.mnuNtscFilter.Text = "NTSC 2x (blargg)"; this.mnuNtscFilter.Click += new System.EventHandler(this.mnuNtscFilter_Click); // // mnuNtscBisqwitQuarterFilter From 1b9024f9e052918ec34153323f3d867dd2a5a5f3 Mon Sep 17 00:00:00 2001 From: Persune Date: Sun, 15 Jan 2023 15:36:54 +0800 Subject: [PATCH 16/36] Set artifact phase as member object --- Core/BisqwitNtscFilter.cpp | 7 +++++-- Core/BisqwitNtscFilter.h | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 4368f89c7..9f5355cc7 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -13,6 +13,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider _resDivider = resDivider; _stopThread = false; _workDone = false; + _startingPhase = _console->GetStartingPhase(); _extraThread = std::thread([=]() { //Worker thread to improve decode speed while(!_stopThread) { @@ -29,7 +30,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, (_console->GetStartingPhase() * 4) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, (_startingPhase * 4) + 327360); _workDone = true; } @@ -49,7 +50,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), (_console->GetStartingPhase() * 4) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), (_startingPhase * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } @@ -82,6 +83,8 @@ void BisqwitNtscFilter::OnBeforeApplyFilter() _sinetable[i] = (int8_t)(8 * std::sin(i * 2 * pi / 12 + pictureSettings.Hue * pi)); } + _startingPhase = _console->GetStartingPhase(); + _yWidth = (int)(12 + ntscSettings.YFilterLength * 22); _iWidth = (int)(12 + ntscSettings.IFilterLength * 22); _qWidth = (int)(12 + ntscSettings.QFilterLength * 22); diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index 6ee0ec755..5e94e6e30 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -38,6 +38,9 @@ class BisqwitNtscFilter : public BaseVideoFilter //To finetune hue, you would have to recalculate sinetable[]. (Coarse changes can be made with Phase0.) int8_t _sinetable[27]; // 8*sin(x*2pi/12) + + uint8_t _startingPhase = 0; + //Precalculate the low and high signal chosen for each 64 base colors //with their respective attenuated values const int8_t _signalLow[2][0x40] = { From d57b5a31afdd334a16a085e154e5cff798e13b04 Mon Sep 17 00:00:00 2001 From: Persune Date: Sun, 15 Jan 2023 15:37:22 +0800 Subject: [PATCH 17/36] Add helper thread for blargg NTSC --- Core/NtscFilter.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++--- Core/NtscFilter.h | 8 ++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index b786f8446..6a4fa9cdd 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -10,6 +10,35 @@ NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) memset(&_ntscData, 0, sizeof(_ntscData)); _ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240](); + + + _startingPhase = _console->GetStartingPhase(); + _extraThread = std::thread([=]() { + //Worker thread to improve decode speed + while (!_stopThread) { + _waitWork.Wait(); + if (_stopThread) { + break; + } + uint8_t phase = _startingPhase; + for (int i = 0; i < 120; i++) { + nes_ntsc_blit(&_ntscData, + // input += in_row_width; + _ppuOutputBuffer + PPU::ScreenWidth * i, + _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, + PPU::ScreenWidth, + phase, + PPU::ScreenWidth, + 1, + // rgb_out = (char*) rgb_out + out_pitch; + reinterpret_cast(_ntscBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); + + phase = (phase + 1) % 3; + } + _workDone = true; + } + }); } FrameInfo NtscFilter::GetFrameInfo() @@ -121,15 +150,20 @@ void NtscFilter::OnBeforeApplyFilter() nes_ntsc_init(&_ntscData, &_ntscSetup); } + _startingPhase = _console->GetStartingPhase(); } void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - uint8_t phase = _console->GetStartingPhase(); - for (int i = 0; i < 240; i++) { + _ppuOutputBuffer = ppuOutputBuffer; + + _workDone = false; + _waitWork.Signal(); + uint8_t phase = _startingPhase; + for (int i = 120; i < 240; i++) { nes_ntsc_blit(&_ntscData, // input += in_row_width; - ppuOutputBuffer + PPU::ScreenWidth * i, + _ppuOutputBuffer + PPU::ScreenWidth * i, _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, PPU::ScreenWidth, phase, @@ -141,6 +175,9 @@ void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) phase = (phase + 1) % 3; } + + while (!_workDone) {} + GenerateArgbFrame(_ntscBuffer); } @@ -207,5 +244,8 @@ void NtscFilter::GenerateArgbFrame(uint32_t *ntscBuffer) NtscFilter::~NtscFilter() { + _stopThread = true; + _waitWork.Signal(); + _extraThread.join(); delete[] _ntscBuffer; } \ No newline at end of file diff --git a/Core/NtscFilter.h b/Core/NtscFilter.h index 071448633..86791ff89 100644 --- a/Core/NtscFilter.h +++ b/Core/NtscFilter.h @@ -2,6 +2,7 @@ #include "stdafx.h" #include "BaseVideoFilter.h" #include "../Utilities/nes_ntsc.h" +#include "../Utilities/AutoResetEvent.h" class Console; @@ -12,10 +13,17 @@ class NtscFilter : public BaseVideoFilter nes_ntsc_t _ntscData; bool _keepVerticalRes = false; bool _useExternalPalette = true; + uint8_t _startingPhase = 0; uint8_t _palette[512 * 3]; + uint16_t* _ppuOutputBuffer = nullptr; uint32_t* _ntscBuffer; bool _ntscBorder = true; + std::thread _extraThread; + AutoResetEvent _waitWork; + atomic _stopThread; + atomic _workDone; + void GenerateArgbFrame(uint32_t *outputBuffer); protected: From 0ff807fa1e0f23f349365993c44d97fce32e1a4f Mon Sep 17 00:00:00 2001 From: Persune Date: Sun, 15 Jan 2023 15:37:46 +0800 Subject: [PATCH 18/36] Optimize NTSC-CRT library --- Core/LMP88959NtscFilter.cpp | 27 ++++++------ Core/LMP88959NtscFilter.h | 4 ++ Utilities/crt.cpp | 82 +++++++++++++++++++------------------ Utilities/crt.h | 2 +- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 463588ad8..705af3aba 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -11,7 +11,16 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoF memset(&_crt, 0, sizeof(CRT)); memset(&_nesNTSC, 0, sizeof(NES_NTSC_SETTINGS)); _frameBuffer = new uint32_t[(256 * 2) * 240](); + crt_init(&_crt, (256 * 2), 240, reinterpret_cast(_frameBuffer)); + + _nesNTSC.w = static_cast(PPU::ScreenWidth); + _nesNTSC.h = static_cast(PPU::ScreenHeight); + _nesNTSC.cc[0] = 0; + _nesNTSC.cc[1] = 16; + _nesNTSC.cc[2] = 0; + _nesNTSC.cc[3] = -16; + _nesNTSC.ccs = 16; } FrameInfo LMP88959NtscFilter::GetFrameInfo() @@ -40,28 +49,22 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _crt.noise = static_cast(ntscSettings.Noise * 500.0); - _nesNTSC.w = static_cast(PPU::ScreenWidth); - _nesNTSC.h = static_cast(PPU::ScreenHeight); - _nesNTSC.cc[0] = 0; - _nesNTSC.cc[1] = 16; - _nesNTSC.cc[2] = 0; - _nesNTSC.cc[3] = -16; - _nesNTSC.ccs = 16; - + _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); + _nesNTSC.dot_skipped = _console->GetDotSkipped(); + _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; } void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { _ppuOutputBuffer = ppuOutputBuffer; + _nesNTSC.data = reinterpret_cast(_ppuOutputBuffer); - _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); - _nesNTSC.dot_skipped = _console->GetDotSkipped(); - _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; crt_nes2ntsc(&_crt, &_nesNTSC); + crt_draw(&_crt); - GenerateArgbFrame(_frameBuffer); + } void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index b03131bb0..2582e44ad 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -1,3 +1,5 @@ +// NTSC filter based on EMMIR's composite modem +// https://github.com/LMP88959/NTSC-CRT #pragma once #include "stdafx.h" #include "BaseVideoFilter.h" @@ -10,7 +12,9 @@ class LMP88959NtscFilter : public BaseVideoFilter private: struct NES_NTSC_SETTINGS _nesNTSC; struct CRT _crt; + bool _keepVerticalRes = false; + uint32_t* _frameBuffer; uint16_t* _ppuOutputBuffer; bool _ntscBorder = true; diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 9e3fdf678..99a124c3f 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -333,23 +333,23 @@ iirf(struct IIRLP *f, int s) #endif } -//Precalculate the low and high signal chosen for each 64 base colors -//with their respective attenuated values +// Precalculate the low and high signal chosen for each 64 base colors +// with their respective attenuated values // https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels const int8_t IRE_levels[2][2][0x40] { // waveform low { // normal { - // 0x - 43, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, - // 1x - 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // 2x - 110, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 0, 0, - // 3x - 110, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0 - }, + // 0x + 43, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, + // 1x + 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 2x + 110, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 0, 0, + // 3x + 110, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0 + }, // attenuated { // 0x @@ -446,34 +446,35 @@ square_sample(int pixel_color, int phase) 0x140, 0x100, 0x180, 0x080 }; - int pixel_index, hue, level, emphasis = 0; - pixel_index = pixel_color & 0x3F; - hue = (pixel_index & 0x0f); + int pixel_index = pixel_color & 0x3F; + int hue = (pixel_index & 0x0f); + int level; + int emphasis = 0; if (hue >= 0x0e) return 0; switch (hue) { case 0: - level = 1; - break; + level = 1; + break; case 0x0d: - level = 0; - break; + level = 0; + break; default: - level = (((hue + phase) % 12) < 6); - break; + level = (((hue + phase) % 12) < 6); + break; } /* red 0100, green 0200, blue 0400 */ - if (((pixel_color & 0x1C0) & active[(phase >> 1) % 6]) && hue < 0x0e) { - emphasis = 1; + if (((pixel_color & 0x1C0) & active[(phase >> 1) % 6])) { + emphasis = 1; } - return IRE_levels[level][emphasis][(pixel_index)]; + return IRE_levels[level][emphasis][pixel_index]; } extern void -crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) +crt_nes2ntsc(struct CRT* v, struct NES_NTSC_SETTINGS *s) { int x, y, xo, yo; int destw = AV_LEN; @@ -491,7 +492,8 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) if (desth > ((CRT_LINES * 63500) >> 16)) { desth = ((CRT_LINES * 63500) >> 16); } - } else { + } + else { destw = (AV_LEN * 55500) >> 16; desth = (CRT_LINES * 63500) >> 16; } @@ -510,29 +512,29 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) xo = PPUAV_BEG; yo = CRT_TOP; - + /* align signal */ xo = (xo & ~3); #if CRT_NES_HIRES switch (s->dot_crawl_offset % 3) { - case 0: - lo = 1; - po = 3; - break; - case 1: - lo = 3; - po = 1; - break; - case 2: - lo = 2; - po = 0; - break; + case 0: + lo = 1; + po = 3; + break; + case 1: + lo = 3; + po = 1; + break; + case 2: + lo = 2; + po = 0; + break; } #else lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ po = lo; if (lo == 1) { - lo = 3; + lo = 3; } #endif @@ -541,6 +543,7 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; + int skipdot = (n == 14 && s->dot_skipped) ? PPUpx2pos(1) : 0; t = LINE_BEG; @@ -556,7 +559,6 @@ crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s) while (t < CB_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ int cb; /* CB_CYCLES of color burst at 3.579545 Mhz */ - int skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { cb = s->cc[(t + po) & 3]; line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; diff --git a/Utilities/crt.h b/Utilities/crt.h index 10f9d84bc..85d0df918 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -60,7 +60,7 @@ extern "C" { #if CRT_NES_HIRES #define CRT_CB_FREQ 6 /* carrier frequency relative to sample rate */ #else -#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ +#define CRT_CB_FREQ 3 /* carrier frequency relative to sample rate */ #endif #else #define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ From fd4e04632627f1dcd2298a8fb96eccfea0b22966 Mon Sep 17 00:00:00 2001 From: Persune Date: Mon, 16 Jan 2023 14:58:30 +0800 Subject: [PATCH 19/36] Optimize filter Thanks Emmir! - Precalculate sync and blanking - Remove non-essential code - Begin to integrate frame mix toggle --- Core/LMP88959NtscFilter.cpp | 7 +- Utilities/crt.cpp | 189 ++++++++++++++---------------------- Utilities/crt.h | 9 ++ 3 files changed, 84 insertions(+), 121 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 705af3aba..9d63f3094 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -13,14 +13,14 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoF _frameBuffer = new uint32_t[(256 * 2) * 240](); crt_init(&_crt, (256 * 2), 240, reinterpret_cast(_frameBuffer)); - - _nesNTSC.w = static_cast(PPU::ScreenWidth); - _nesNTSC.h = static_cast(PPU::ScreenHeight); + _nesNTSC.w = PPU::ScreenWidth; + _nesNTSC.h = PPU::ScreenHeight; _nesNTSC.cc[0] = 0; _nesNTSC.cc[1] = 16; _nesNTSC.cc[2] = 0; _nesNTSC.cc[3] = -16; _nesNTSC.ccs = 16; + crtnes_setup_field(&_crt, &_nesNTSC); } FrameInfo LMP88959NtscFilter::GetFrameInfo() @@ -48,6 +48,7 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _crt.noise = static_cast(ntscSettings.Noise * 500.0); + _crt.frameblend = static_cast(false); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); _nesNTSC.dot_skipped = _console->GetDotSkipped(); diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp index 99a124c3f..c852f3a5e 100644 --- a/Utilities/crt.cpp +++ b/Utilities/crt.cpp @@ -473,6 +473,31 @@ square_sample(int pixel_color, int phase) return IRE_levels[level][emphasis][pixel_index]; } +extern void +crtnes_setup_field(struct CRT *v, struct NES_NTSC_SETTINGS *s) +{ + int n; + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + /* vertical sync scanlines */ + if (n >= 259 && n <= CRT_VRES) { + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ + } else { + /* prerender/postrender/video scanlines */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + } +} + extern void crt_nes2ntsc(struct CRT* v, struct NES_NTSC_SETTINGS *s) { @@ -481,26 +506,11 @@ crt_nes2ntsc(struct CRT* v, struct NES_NTSC_SETTINGS *s) int desth = CRT_LINES; int n, phase; int po, lo; + int burst_level[4]; -#if CRT_DO_BLOOM - if (s->raw) { - destw = s->w; - desth = s->h; - if (destw > ((AV_LEN * 55500) >> 16)) { - destw = ((AV_LEN * 55500) >> 16); - } - if (desth > ((CRT_LINES * 63500) >> 16)) { - desth = ((CRT_LINES * 63500) >> 16); - } - } - else { - destw = (AV_LEN * 55500) >> 16; - desth = (CRT_LINES * 63500) >> 16; - } -#else if (s->raw) { - destw = s->w; - desth = s->h; + destw = NES_AV_WIDTH; + desth = NES_AV_HEIGHT; if (destw > AV_LEN) { destw = AV_LEN; } @@ -508,107 +518,77 @@ crt_nes2ntsc(struct CRT* v, struct NES_NTSC_SETTINGS *s) desth = ((CRT_LINES * 64500) >> 16); } } -#endif xo = PPUAV_BEG; yo = CRT_TOP; /* align signal */ xo = (xo & ~3); -#if CRT_NES_HIRES - switch (s->dot_crawl_offset % 3) { - case 0: - lo = 1; - po = 3; - break; - case 1: - lo = 3; - po = 1; - break; - case 2: - lo = 2; - po = 0; - break; - } -#else + lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ po = lo; if (lo == 1) { lo = 3; } -#endif - phase = (1 + po) * 3; + phase = po * 3; - for (n = 0; n < CRT_VRES; n++) { + for (n = CRT_TOP; n <= (CRT_BOT + 2); n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; - int skipdot = (n == 14 && s->dot_skipped) ? PPUpx2pos(1) : 0; - - t = LINE_BEG; - // vertical sync scanlines - if (n >= 259 && n <= CRT_VRES) { - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ - } else { - /* prerender/postrender/video scanlines */ - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < CB_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ - int cb; - /* CB_CYCLES of color burst at 3.579545 Mhz */ - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { - cb = s->cc[(t + po) & 3]; - line[t] = BLANK_LEVEL + (cb * BURST_LEVEL) / s->ccs; - v->ccf[t & 3] = line[t]; - } - while (t < AV_BEG) line[t++] = BLANK_LEVEL; - phase += t * 3; - if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { - while (t < CRT_HRES) { - int ire, p; - p = s->borderdata; - if (t == AV_BEG) p = 0xF0; - ire = BLACK_LEVEL; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - ire >>= 2; - line[t++] = ire; - phase += 3; - } - } - else { - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - phase += (CRT_HRES - AV_BEG) * 3; - } - phase %= 12; + phase += AV_BEG * 3; + t = AV_BEG; + + while (t < CRT_HRES) { + int ire, p; + p = s->borderdata; + if (t == AV_BEG) p = 0xf0; + ire = BLACK_LEVEL; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + line[t++] = ire >> 2; + phase += 3; } + phase %= 12; } - phase = 3; - + /* precompute to save CPU */ + for (n = 0; n < 4; n++) { + burst_level[n] = BLANK_LEVEL + (s->cc[n] * BURST_LEVEL) / s->ccs; + } for (y = (lo - 3); y < desth; y++) { - int sy = (y * s->h) / desth; + signed char *line; + int t, sy; + int skipdot; + + n = (y + yo); + line = &v->analog[n * CRT_HRES]; + sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; if (sy < 0) sy = 0; - + + /* CB_CYCLES of color burst at 3.579545 Mhz */ + skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { + line[t] = burst_level[(t + po) & 3]; + v->ccf[t & 3] = line[t]; + } + sy *= s->w; phase += (xo * 3); for (x = 0; x < destw; x++) { int ire, p; - + p = s->data[((x * s->w) / destw) + sy]; - ire = BLANK_LEVEL; + ire = BLACK_LEVEL; ire += square_sample(p, phase + 0); ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire >>= 2; - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; phase += 3; } /* mod here so we don't overflow down the line */ @@ -627,10 +607,6 @@ crt_draw(struct CRT *v) int y, i, q; } out[AV_LEN + 1], *yiqA, *yiqB; int i, j, line; -#if CRT_DO_BLOOM - int prev_e; /* filtered beam energy per scan line */ - int max_e; /* approx maximum energy in a scan line */ -#endif int bright = v->brightness - (BLACK_LEVEL); //+ v->black_point); signed char *sig; int s = 0; @@ -697,10 +673,6 @@ crt_draw(struct CRT *v) #endif /* if vsync signal was in second half of line, odd field */ field = (j > (CRT_HRES / 2)); -#if CRT_DO_BLOOM - max_e = (128 + (v->noise / 2)) * AV_LEN; - prev_e = (16384 / 8); -#endif /* ratio of output height to active video lines in the signal */ ratio = (v->outh << 16) / CRT_LINES; ratio = (ratio + 32768) >> 16; @@ -711,9 +683,6 @@ crt_draw(struct CRT *v) unsigned pos, ln; int scanL, scanR, dx; int L, R; -#if CRT_DO_BLOOM - int line_w; -#endif int *cL, *cR; int wave[4]; int dci, dcq; /* decoded I, Q */ @@ -767,28 +736,11 @@ crt_draw(struct CRT *v) wave[3] = -wave[1]; sig = v->inp + pos; -#if CRT_DO_BLOOM - s = 0; - for (i = 0; i < AV_LEN; i++) { - s += sig[i]; /* sum up the scan line */ - } - /* bloom emulation */ - prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e); - line_w = (AV_LEN * 112 / 128) + (prev_e >> 9); - - dx = (line_w << 12) / v->outw; - scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12; - scanR = (AV_LEN - 1) << 12; - - L = (scanL >> 12); - R = (scanR >> 12); -#else dx = ((AV_LEN - 1) << 12) / v->outw; scanL = 0; scanR = (AV_LEN - 1) << 12; L = 0; R = AV_LEN; -#endif reset_eq(&eqY); reset_eq(&eqI); reset_eq(&eqQ); @@ -833,8 +785,9 @@ crt_draw(struct CRT *v) aa = (r << 16 | g << 8 | b); bb = *cL; - /* blend with previous color there */ - *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + /* blend with previous color there */ + if (v->frameblend) *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + else *cL++ = aa; } /* duplicate extra lines */ diff --git a/Utilities/crt.h b/Utilities/crt.h index 85d0df918..97f25cea9 100644 --- a/Utilities/crt.h +++ b/Utilities/crt.h @@ -74,6 +74,9 @@ extern "C" { #define CRT_TOP 15 /* first line with active video */ #define CRT_BOT 255 /* final line with active video */ #define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ + +#define NES_AV_WIDTH 256 +#define NES_AV_HEIGHT CRT_LINES struct CRT { signed char analog[CRT_INPUT_SIZE]; @@ -85,6 +88,7 @@ struct CRT { int outw, outh; /* output width/height */ int *out; /* output image */ int ccf[4]; /* color carrier reference for faster convergence */ + int frameblend; /* blend successive frames */ }; /* Initializes the library. Sets up filters. @@ -125,6 +129,11 @@ struct NES_NTSC_SETTINGS { int ccs; }; +/* Setup analog NTSC blanking and sync signal + * s - struct containing settings to apply to this field + */ +extern void crtnes_setup_field(struct CRT* v, struct NES_NTSC_SETTINGS* s); + /* Convert NES pixel data (generally 256x240) to analog NTSC signal * s - struct containing settings to apply to this field */ From 51bfe82abc3c0ae41428e2b00a3227845386b0c7 Mon Sep 17 00:00:00 2001 From: Persune Date: Mon, 16 Jan 2023 19:59:46 +0800 Subject: [PATCH 20/36] Restore Mapper 431 --- Core/Core.vcxproj.filters | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 1736d7af3..3f7a5fff2 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -874,6 +874,9 @@ Nes\Mappers\Unnamed + + Nes\Mappers\Unnamed + Nes\Mappers\Unnamed From bf6f4fb4d733bd51958017a4d398865371b20b3c Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 15:26:56 +0800 Subject: [PATCH 21/36] Implement library updates Thanks Emmir! --- Core/LMP88959NtscFilter.cpp | 58 +- Core/LMP88959NtscFilter.h | 7 +- Utilities/Utilities.vcxproj | 6 +- Utilities/Utilities.vcxproj.filters | 8 +- Utilities/crt.cpp | 799 ---------------------------- Utilities/crt.h | 154 ------ Utilities/crt_core.cpp | 471 ++++++++++++++++ Utilities/crt_core.h | 103 ++++ Utilities/crt_nes.cpp | 225 ++++++++ Utilities/crt_nes.h | 137 +++++ 10 files changed, 984 insertions(+), 984 deletions(-) delete mode 100644 Utilities/crt.cpp delete mode 100644 Utilities/crt.h create mode 100644 Utilities/crt_core.cpp create mode 100644 Utilities/crt_core.h create mode 100644 Utilities/crt_nes.cpp create mode 100644 Utilities/crt_nes.h diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 9d63f3094..98147fced 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -9,28 +9,39 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoFilter(console) { memset(&_crt, 0, sizeof(CRT)); - memset(&_nesNTSC, 0, sizeof(NES_NTSC_SETTINGS)); - _frameBuffer = new uint32_t[(256 * 2) * 240](); + memset(&_nesNTSC, 0, sizeof(NTSC_SETTINGS)); + _frameBuffer = new uint32_t[(PPUpx2pos(256)) * 240](); + + crt_init(&_crt, (PPUpx2pos(256)), 240, reinterpret_cast(_frameBuffer)); + crt_setup_field(&_crt); - crt_init(&_crt, (256 * 2), 240, reinterpret_cast(_frameBuffer)); _nesNTSC.w = PPU::ScreenWidth; _nesNTSC.h = PPU::ScreenHeight; - _nesNTSC.cc[0] = 0; - _nesNTSC.cc[1] = 16; - _nesNTSC.cc[2] = 0; - _nesNTSC.cc[3] = -16; - _nesNTSC.ccs = 16; - crtnes_setup_field(&_crt, &_nesNTSC); } FrameInfo LMP88959NtscFilter::GetFrameInfo() { OverscanDimensions overscan = GetOverscan(); + uint32_t overscanLeft = overscan.Left > 0 ? PPUpx2pos(overscan.Left) : 0; + uint32_t overscanRight = overscan.Right > 0 ? PPUpx2pos(overscan.Right) : 0; + if (_keepVerticalRes) { - return { overscan.GetScreenWidth() * 2, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 }; + return { + (PPUpx2pos(PPU::ScreenWidth) - overscanLeft - overscanRight), + (PPU::ScreenHeight - overscan.Top - overscan.Bottom), + PPUpx2pos(PPU::ScreenWidth), + PPU::ScreenHeight, + 4 + }; } else { - return { overscan.GetScreenWidth() * 2, overscan.GetScreenHeight() * 2, PPU::ScreenWidth, PPU::ScreenHeight, 4 }; + return { + PPUpx2pos(PPU::ScreenWidth) - overscanLeft - overscanRight, + (PPU::ScreenHeight - overscan.Top - overscan.Bottom) * 2, + PPUpx2pos(PPU::ScreenWidth), + PPU::ScreenHeight * 2, + 4 + }; } } @@ -47,12 +58,12 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); - _crt.noise = static_cast(ntscSettings.Noise * 500.0); - _crt.frameblend = static_cast(false); + _noise = static_cast(ntscSettings.Noise * 500.0); + _crt.blend = static_cast(false); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); - _nesNTSC.dot_skipped = _console->GetDotSkipped(); - _nesNTSC.borderdata = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; + //_nesNTSC.dot_skipped = _console->GetDotSkipped(); + _nesNTSC.border_color = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; } void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) @@ -61,21 +72,20 @@ void LMP88959NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _nesNTSC.data = reinterpret_cast(_ppuOutputBuffer); - crt_nes2ntsc(&_crt, &_nesNTSC); + crt_modulate(&_crt, &_nesNTSC); - crt_draw(&_crt); + crt_demodulate(&_crt, _noise); GenerateArgbFrame(_frameBuffer); } void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) { - int resMultiplier = 2; uint32_t* outputBuffer = GetOutputBuffer(); OverscanDimensions overscan = GetOverscan(); - int overscanLeft = (overscan.Left > 0 ? overscan.Left : 0) * resMultiplier; - int overscanRight = (overscan.Right > 0 ? overscan.Right : 0) * resMultiplier; - int rowWidth = PPU::ScreenWidth * resMultiplier; + int overscanLeft = overscan.Left > 0 ? PPUpx2pos(overscan.Left) : 0; + int overscanRight = overscan.Right > 0 ? PPUpx2pos(overscan.Right) : 0; + int rowWidth = PPUpx2pos(PPU::ScreenWidth); int rowWidthOverscan = rowWidth - overscanLeft - overscanRight; if (_keepVerticalRes) { @@ -90,9 +100,7 @@ void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity; bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend; - for (int y = (int)overscan.Top; - y <= PPU::ScreenHeight - 1 - overscan.Bottom; - y++) { + for (int y = PPU::ScreenHeight - 1 - overscan.Bottom; y >= (int)overscan.Top; y--) { uint32_t const* in = frameBuffer + y * rowWidth; uint32_t* out = outputBuffer + (y - overscan.Top) * 2 * rowWidthOverscan; @@ -139,6 +147,6 @@ void LMP88959NtscFilter::GenerateArgbFrame(uint32_t* frameBuffer) LMP88959NtscFilter::~LMP88959NtscFilter() { memset(&_crt, 0, sizeof(CRT)); - memset(&_nesNTSC, 0, sizeof(NES_NTSC_SETTINGS)); + memset(&_nesNTSC, 0, sizeof(NTSC_SETTINGS)); delete[] _frameBuffer; } \ No newline at end of file diff --git a/Core/LMP88959NtscFilter.h b/Core/LMP88959NtscFilter.h index 2582e44ad..2212e8ba1 100644 --- a/Core/LMP88959NtscFilter.h +++ b/Core/LMP88959NtscFilter.h @@ -3,16 +3,19 @@ #pragma once #include "stdafx.h" #include "BaseVideoFilter.h" -#include "../Utilities/crt.h" +#include "../Utilities/crt_core.h" +#include "../Utilities/crt_nes.h" class Console; class LMP88959NtscFilter : public BaseVideoFilter { private: - struct NES_NTSC_SETTINGS _nesNTSC; + struct NTSC_SETTINGS _nesNTSC; struct CRT _crt; + int _noise = 0; + bool _keepVerticalRes = false; uint32_t* _frameBuffer; diff --git a/Utilities/Utilities.vcxproj b/Utilities/Utilities.vcxproj index e9166ab19..e48176518 100644 --- a/Utilities/Utilities.vcxproj +++ b/Utilities/Utilities.vcxproj @@ -427,7 +427,8 @@ - + + @@ -479,7 +480,8 @@ - + + diff --git a/Utilities/Utilities.vcxproj.filters b/Utilities/Utilities.vcxproj.filters index bffc2f544..c0fd9dfff 100644 --- a/Utilities/Utilities.vcxproj.filters +++ b/Utilities/Utilities.vcxproj.filters @@ -175,7 +175,10 @@ Misc - + + Misc + + Misc @@ -306,6 +309,7 @@ Avi - + + \ No newline at end of file diff --git a/Utilities/crt.cpp b/Utilities/crt.cpp deleted file mode 100644 index c852f3a5e..000000000 --- a/Utilities/crt.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/*****************************************************************************/ -/* - * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation - * - * by EMMIR 2018-2023 - * modifications for Mesen by Persune - * https://github.com/LMP88959/NTSC-CRT - * - * YouTube: https://www.youtube.com/@EMMIR_KC/videos - * Discord: https://discord.com/invite/hdYctSmyQJ - */ -/*****************************************************************************/ -#include "stdafx.h" - -#include "crt.h" - -#include -#include - -/* NES composite signal is measured in terms of PPU pixels, or cycles - * https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing - * - * FULL HORIZONTAL LINE SIGNAL - * (341 PPU px; one cycle skipped on odd rendered frames) - * |---------------------------------------------------------------------------| - * HBLANK (58 PPU px) ACTIVE VIDEO (283 PPU px) - * |-------------------||------------------------------------------------------| - * - * - * WITHIN HBLANK PERIOD: - * - * FP (9 PPU px) SYNC (25 PPU px) BW (4 PPU px) CB (15 PPU px) BP (5 PPU px) - * |--------------||---------------||------------||-------------||-------------| - * BLANK SYNC BLANK BLANK BLANK - * - * - * WITHIN ACTIVE VIDEO PERIOD: - * - * LB (15 PPU px) AV (256 PPU px) RB (11 PPU px) - * |--------------||--------------------------------------------||-------------| - * BORDER VIDEO BORDER - * - */ -#define LINE_BEG 0 -#define FP_PPUpx 9 /* front porch */ -#define SYNC_PPUpx 25 /* sync tip */ -#define BW_PPUpx 4 /* breezeway */ -#define CB_PPUpx 15 /* color burst */ -#define BP_PPUpx 5 /* back porch */ -#define PS_PPUpx 1 /* pulse */ -#define LB_PPUpx 15 /* left border */ -#define AV_PPUpx 256 /* active video */ -#define RB_PPUpx 11 /* right border */ -#define HB_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx) /* h blank */ -/* line duration should be ~63500 ns */ -#define LINE_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx + PS_PPUpx + LB_PPUpx + AV_PPUpx + RB_PPUpx) - -/* convert pixel offset to its corresponding point on the sampled line */ -#define PPUpx2pos(PPUpx) ((PPUpx) * CRT_HRES / LINE_PPUpx) -/* starting points for all the different pulses */ -#define FP_BEG PPUpx2pos(0) /* front porch point */ -#define SYNC_BEG PPUpx2pos(FP_PPUpx) /* sync tip point */ -#define BW_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx) /* breezeway point */ -#define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) /* color burst point */ -#define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) /* back porch point */ -#define AV_BEG PPUpx2pos(HB_PPUpx) /* active video point */ -#define PPUAV_BEG PPUpx2pos(HB_PPUpx + PS_PPUpx + LB_PPUpx) /* PPU active video point */ -#define AV_LEN PPUpx2pos(AV_PPUpx) /* active video length */ - -/* somewhere between 7 and 12 cycles */ -#define CB_CYCLES 10 - -/* frequencies for bandlimiting */ -#define L_FREQ 1431818 /* full line */ -#define Y_FREQ 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ -#define I_FREQ 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ -#define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ - -/* IRE units (100 = 1.0V, -40 = 0.0V) */ -/* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ -#define WHITE_LEVEL 110 -#define BURST_LEVEL 30 -#define BLACK_LEVEL 0 -#define BLANK_LEVEL 0 -#define SYNC_LEVEL -37 - -#if (CRT_CHROMA_PATTERN == 1) -/* 227.5 subcarrier cycles per line means every other line has reversed phase */ -#define CC_PHASE(ln) (((ln) & 1) ? -1 : 1) -#else -#define CC_PHASE(ln) (1) -#endif - -/* ensure negative values for x get properly modulo'd */ -#define POSMOD(x, n) (((x) % (n) + (n)) % (n)) - -/*****************************************************************************/ -/***************************** FIXED POINT MATH ******************************/ -/*****************************************************************************/ - -#define T14_2PI 16384 -#define T14_MASK (T14_2PI - 1) -#define T14_PI (T14_2PI / 2) - -static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ - 0x0000, - 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, - 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, - 0x7f60 -}; - -static int -sintabil8(int n) -{ - int f, i, a, b; - - /* looks scary but if you don't change T14_2PI - * it won't cause out of bounds memory reads - */ - f = n >> 0 & 0xff; - i = n >> 8 & 0xff; - a = sigpsin15[i]; - b = sigpsin15[i + 1]; - return (a + ((b - a) * f >> 8)); -} - -/* 14-bit interpolated sine/cosine */ -extern void -crt_sincos14(int *s, int *c, int n) -{ - int h; - - n &= T14_MASK; - h = n & ((T14_2PI >> 1) - 1); - - if (h > ((T14_2PI >> 2) - 1)) { - *c = -sintabil8(h - (T14_2PI >> 2)); - *s = sintabil8((T14_2PI >> 1) - h); - } else { - *c = sintabil8((T14_2PI >> 2) - h); - *s = sintabil8(h); - } - if (n > ((T14_2PI >> 1) - 1)) { - *c = -*c; - *s = -*s; - } -} - -#define EXP_P 11 -#define EXP_ONE (1 << EXP_P) -#define EXP_MASK (EXP_ONE - 1) -#define EXP_PI 6434 -#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P) -#define EXP_DIV(x, y) (((x) << EXP_P) / (y)) - -static int e11[] = { - EXP_ONE, - 5567, /* e */ - 15133, /* e^2 */ - 41135, /* e^3 */ - 111817 /* e^4 */ -}; - -/* fixed point e^x */ -static int -expx(int n) -{ - int neg, idx, res; - int nxt, acc, del; - int i; - - if (n == 0) { - return EXP_ONE; - } - neg = n < 0; - if (neg) { - n = -n; - } - idx = n >> EXP_P; - res = EXP_ONE; - for (i = 0; i < idx / 4; i++) { - res = EXP_MUL(res, e11[4]); - } - idx &= 3; - if (idx > 0) { - res = EXP_MUL(res, e11[idx]); - } - - n &= EXP_MASK; - nxt = EXP_ONE; - acc = 0; - del = 1; - for (i = 1; i < 17; i++) { - acc += nxt / del; - nxt = EXP_MUL(nxt, n); - del *= i; - if (del > nxt || nxt <= 0 || del <= 0) { - break; - } - } - res = EXP_MUL(res, acc); - - if (neg) { - res = EXP_DIV(EXP_ONE, res); - } - return res; -} - -/*****************************************************************************/ -/********************************* FILTERS ***********************************/ -/*****************************************************************************/ - -#define HISTLEN 3 -#define HISTOLD (HISTLEN - 1) /* oldest entry */ -#define HISTNEW 0 /* newest entry */ - -#define EQ_P 16 /* if changed, the gains will need to be adjusted */ -#define EQ_R (1 << (EQ_P - 1)) /* rounding */ -/* three band equalizer */ -static struct EQF { - int lf, hf; /* fractions */ - int g[3]; /* gains */ - int fL[4]; - int fH[4]; - int h[HISTLEN]; /* history */ -} eqY, eqI, eqQ; - -/* f_lo - low cutoff frequency - * f_hi - high cutoff frequency - * rate - sampling rate - * g_lo, g_mid, g_hi - gains - */ -static void -init_eq(struct EQF *f, - int f_lo, int f_hi, int rate, - int g_lo, int g_mid, int g_hi) -{ - int sn, cs; - - memset(f, 0, sizeof(struct EQF)); - - f->g[0] = g_lo; - f->g[1] = g_mid; - f->g[2] = g_hi; - - crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); - if (EQ_P >= 15) { - f->lf = 2 * (sn << (EQ_P - 15)); - } else { - f->lf = 2 * (sn >> (15 - EQ_P)); - } - crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); - if (EQ_P >= 15) { - f->hf = 2 * (sn << (EQ_P - 15)); - } else { - f->hf = 2 * (sn >> (15 - EQ_P)); - } -} - -static void -reset_eq(struct EQF *f) -{ - memset(f->fL, 0, sizeof(f->fL)); - memset(f->fH, 0, sizeof(f->fH)); - memset(f->h, 0, sizeof(f->h)); -} - -static int -eqf(struct EQF *f, int s) -{ - int i, r[3]; - - f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; - f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; - - for (i = 1; i < 4; i++) { - f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; - f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; - } - - r[0] = f->fL[3]; - r[1] = f->fH[3] - f->fL[3]; - r[2] = f->h[HISTOLD] - f->fH[3]; - - for (i = 0; i < 3; i++) { - r[i] = (r[i] * f->g[i]) >> EQ_P; - } - - for (i = HISTOLD; i > 0; i--) { - f->h[i] = f->h[i - 1]; - } - f->h[HISTNEW] = s; - - return (r[0] + r[1] + r[2]); -} - -/* infinite impulse response low pass filter for bandlimiting YIQ */ -static struct IIRLP { - int c; - int h; /* history */ -} iirY, iirI, iirQ; - -/* freq - total bandwidth - * limit - max frequency - */ -static void -init_iir(struct IIRLP *f, int freq, int limit) -{ - int rate; /* cycles/pixel rate */ - - memset(f, 0, sizeof(struct IIRLP)); - rate = (freq << 9) / limit; - f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); -} - -static void -reset_iir(struct IIRLP *f) -{ - f->h = 0; -} - -/* hi-pass for debugging */ -#define HIPASS 0 - -static int -iirf(struct IIRLP *f, int s) -{ - f->h += EXP_MUL(s - f->h, f->c); -#if HIPASS - return s - f->h; -#else - return f->h; -#endif -} - -// Precalculate the low and high signal chosen for each 64 base colors -// with their respective attenuated values -// https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels -const int8_t IRE_levels[2][2][0x40] { - // waveform low - { - // normal - { - // 0x - 43, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, - // 1x - 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // 2x - 110, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 0, 0, - // 3x - 110, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0 - }, - // attenuated - { - // 0x - 26 , -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, 0, 0, - // 1x - 51, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, 0, 0, - // 2x - 82, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, - // 3x - 82, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0 - } - }, - // waveform high - { - // normal - { - // 0x - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -12, 0, 0, - // 1x - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 0, 0, 0, - // 2x - 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 34, 0, 0, - // 3x - 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 80, 0, 0 - }, - // attenuated - { - // 0x - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -17, 0, 0, - // 1x - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, -8, 0, 0, - // 2x - 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 19, 0, 0, - // 3x - 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 56, 0, 0 - } - } -}; - -/*****************************************************************************/ -/***************************** PUBLIC FUNCTIONS ******************************/ -/*****************************************************************************/ - -extern void -crt_resize(struct CRT *v, int w, int h, int *out) -{ - v->outw = w; - v->outh = h; - v->out = out; -} - -extern void -crt_reset(struct CRT *v) -{ - v->hue = 0; - v->saturation = 18; - v->brightness = 0; - v->contrast = 180; - //v->black_point = 0; - //v->white_point = 100; - v->noise = 0; - v->hsync = 0; - v->vsync = 0; -} - -extern void -crt_init(struct CRT *v, int w, int h, int *out) -{ - memset(v, 0, sizeof(struct CRT)); - crt_resize(v, w, h, out); - crt_reset(v); - - /* kilohertz to line sample conversion */ -#define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) - - /* band gains are pre-scaled as 16-bit fixed point - * if you change the EQ_P define, you'll need to update these gains too - */ - init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); - init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); - init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); - - init_iir(&iirY, L_FREQ, Y_FREQ); - init_iir(&iirI, L_FREQ, I_FREQ); - init_iir(&iirQ, L_FREQ, Q_FREQ); -} - -/* generate the square wave for a given 9-bit pixel and phase */ -static int -square_sample(int pixel_color, int phase) -{ - static int active[6] = { - 0x0C0, 0x040, - 0x140, 0x100, - 0x180, 0x080 - }; - int pixel_index = pixel_color & 0x3F; - int hue = (pixel_index & 0x0f); - int level; - int emphasis = 0; - - if (hue >= 0x0e) return 0; - - switch (hue) { - case 0: - level = 1; - break; - case 0x0d: - level = 0; - break; - default: - level = (((hue + phase) % 12) < 6); - break; - } - - /* red 0100, green 0200, blue 0400 */ - if (((pixel_color & 0x1C0) & active[(phase >> 1) % 6])) { - emphasis = 1; - } - - return IRE_levels[level][emphasis][pixel_index]; -} - -extern void -crtnes_setup_field(struct CRT *v, struct NES_NTSC_SETTINGS *s) -{ - int n; - - for (n = 0; n < CRT_VRES; n++) { - int t; /* time */ - signed char *line = &v->analog[n * CRT_HRES]; - - t = LINE_BEG; - - /* vertical sync scanlines */ - if (n >= 259 && n <= CRT_VRES) { - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ - } else { - /* prerender/postrender/video scanlines */ - while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ - while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ - while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - } - } -} - -extern void -crt_nes2ntsc(struct CRT* v, struct NES_NTSC_SETTINGS *s) -{ - int x, y, xo, yo; - int destw = AV_LEN; - int desth = CRT_LINES; - int n, phase; - int po, lo; - int burst_level[4]; - - if (s->raw) { - destw = NES_AV_WIDTH; - desth = NES_AV_HEIGHT; - if (destw > AV_LEN) { - destw = AV_LEN; - } - if (desth > ((CRT_LINES * 64500) >> 16)) { - desth = ((CRT_LINES * 64500) >> 16); - } - } - - xo = PPUAV_BEG; - yo = CRT_TOP; - - /* align signal */ - xo = (xo & ~3); - - lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ - po = lo; - if (lo == 1) { - lo = 3; - } - - phase = po * 3; - - for (n = CRT_TOP; n <= (CRT_BOT + 2); n++) { - int t; /* time */ - signed char *line = &v->analog[n * CRT_HRES]; - - phase += AV_BEG * 3; - t = AV_BEG; - - while (t < CRT_HRES) { - int ire, p; - p = s->borderdata; - if (t == AV_BEG) p = 0xf0; - ire = BLACK_LEVEL; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - line[t++] = ire >> 2; - phase += 3; - } - phase %= 12; - } - phase = 3; - /* precompute to save CPU */ - for (n = 0; n < 4; n++) { - burst_level[n] = BLANK_LEVEL + (s->cc[n] * BURST_LEVEL) / s->ccs; - } - for (y = (lo - 3); y < desth; y++) { - signed char *line; - int t, sy; - int skipdot; - - n = (y + yo); - line = &v->analog[n * CRT_HRES]; - sy = (y * s->h) / desth; - if (sy >= s->h) sy = s->h; - if (sy < 0) sy = 0; - - /* CB_CYCLES of color burst at 3.579545 Mhz */ - skipdot = PPUpx2pos(((n == 14 && s->dot_skipped) ? 1 : 0)); - for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ) - skipdot; t++) { - line[t] = burst_level[(t + po) & 3]; - v->ccf[t & 3] = line[t]; - } - - sy *= s->w; - phase += (xo * 3); - for (x = 0; x < destw; x++) { - int ire, p; - - p = s->data[((x * s->w) / destw) + sy]; - ire = BLACK_LEVEL; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; - phase += 3; - } - /* mod here so we don't overflow down the line */ - phase = (phase + ((CRT_HRES - destw) * 3)) % 12; - } -} - -/* search windows, in samples */ -#define HSYNC_WINDOW 6 -#define VSYNC_WINDOW 6 - -extern void -crt_draw(struct CRT *v) -{ - struct { - int y, i, q; - } out[AV_LEN + 1], *yiqA, *yiqB; - int i, j, line; - int bright = v->brightness - (BLACK_LEVEL); //+ v->black_point); - signed char *sig; - int s = 0; - int field, ratio; - static int ccref[4]; /* color carrier signal */ - int huesn, huecs; - int xnudge = -3, ynudge = 3; - - crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 90) * 8192 / 180); - huesn >>= 11; /* make 4-bit */ - huecs >>= 11; - - ccref[0] = v->ccf[0] << 7; - ccref[1] = v->ccf[1] << 7; - ccref[2] = v->ccf[2] << 7; - ccref[3] = v->ccf[3] << 7; - - for (i = 0; i < CRT_INPUT_SIZE; i++) { - static int rn = 194; /* 'random' noise */ - - rn = (214019 * rn + 140327895); - - /* signal + noise */ - s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * v->noise) >> 8); - if (s > 127) { s = 127; } - if (s < -127) { s = -127; } - v->inp[i] = s; - } - - /* Look for vertical sync. - * - * This is done by integrating the signal and - * seeing if it exceeds a threshold. The threshold of - * the vertical sync pulse is much higher because the - * vsync pulse is a lot longer than the hsync pulse. - * The signal needs to be integrated to lessen - * the noise in the signal. - */ - for (i = -VSYNC_WINDOW; i < VSYNC_WINDOW; i++) { - line = POSMOD(v->vsync + i, CRT_VRES); - sig = v->inp + line * CRT_HRES; - s = 0; - for (j = 0; j < CRT_HRES; j++) { - s += sig[j]; - /* increase the multiplier to make the vsync - * more stable when there is a lot of noise - */ -#if CRT_NES_HIRES - if (s <= (150 * SYNC_LEVEL)) { - goto vsync_found; - } -#else - if (s <= (100 * SYNC_LEVEL)) { - goto vsync_found; - } -#endif - } - } -vsync_found: -#if CRT_DO_VSYNC - v->vsync = line; /* vsync found (or gave up) at this line */ -#else - v->vsync = -3; -#endif - /* if vsync signal was in second half of line, odd field */ - field = (j > (CRT_HRES / 2)); - /* ratio of output height to active video lines in the signal */ - ratio = (v->outh << 16) / CRT_LINES; - ratio = (ratio + 32768) >> 16; - - field = (field * (ratio / 2)); - - for (line = CRT_TOP; line < CRT_BOT; line++) { - unsigned pos, ln; - int scanL, scanR, dx; - int L, R; - int *cL, *cR; - int wave[4]; - int dci, dcq; /* decoded I, Q */ - int xpos, ypos; - int beg, end; - int phasealign; - - beg = (line - CRT_TOP + 0) * v->outh / CRT_LINES + field; - end = (line - CRT_TOP + 1) * v->outh / CRT_LINES + field; - - if (beg >= v->outh) { continue; } - if (end > v->outh) { end = v->outh; } - - /* Look for horizontal sync. - * See comment above regarding vertical sync. - */ - ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; - sig = v->inp + ln + v->hsync; - s = 0; - for (i = -HSYNC_WINDOW; i < HSYNC_WINDOW; i++) { - s += sig[SYNC_BEG + i]; - if (s <= (4 * SYNC_LEVEL)) { - break; - } - } -#if CRT_DO_HSYNC - v->hsync = POSMOD(i + v->hsync, CRT_HRES); -#else - v->hsync = 3; -#endif - - sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ - for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { - int p = ccref[i & 3] * 127 / 128; /* fraction of the previous */ - int n = sig[i]; /* mixed with the new sample */ - ccref[i & 3] = p + n; - } - xpos = POSMOD(PPUAV_BEG + v->hsync + xnudge, CRT_HRES); - ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); - pos = xpos + ypos * CRT_HRES; - phasealign = pos & 3; - - /* amplitude of carrier = saturation, phase difference = hue */ - dci = ccref[(phasealign + 1) & 3] - ccref[(phasealign + 3) & 3]; - dcq = ccref[(phasealign + 2) & 3] - ccref[(phasealign + 0) & 3]; - - /* rotate them by the hue adjustment angle */ - wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; - wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; - wave[2] = -wave[0]; - wave[3] = -wave[1]; - - sig = v->inp + pos; - dx = ((AV_LEN - 1) << 12) / v->outw; - scanL = 0; - scanR = (AV_LEN - 1) << 12; - L = 0; - R = AV_LEN; - reset_eq(&eqY); - reset_eq(&eqI); - reset_eq(&eqQ); - - for (i = L; i < R; i++) { - out[i].y = eqf(&eqY, sig[i] + bright) << 4; - out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; - out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; - } - - cL = v->out + beg * v->outw; - cR = cL + v->outw; - - for (pos = scanL; pos < scanR && cL < cR; pos += dx) { - int y, i, q; - int r, g, b; - int aa, bb; - - R = pos & 0xfff; - L = 0xfff - R; - s = pos >> 12; - - yiqA = out + s; - yiqB = out + s + 1; - - /* interpolate between samples if needed */ - y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); - i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); - q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); - - /* YIQ to RGB */ - r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; - g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; - b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; - - if (r < 0) r = 0; - if (g < 0) g = 0; - if (b < 0) b = 0; - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - - aa = (r << 16 | g << 8 | b); - bb = *cL; - /* blend with previous color there */ - if (v->frameblend) *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); - else *cL++ = aa; - } - - /* duplicate extra lines */ - ln = v->outw * sizeof(int); - for (s = beg + 1; s < end; s++) { - memcpy(v->out + s * v->outw, v->out + (s - 1) * v->outw, ln); - } - } -} diff --git a/Utilities/crt.h b/Utilities/crt.h deleted file mode 100644 index 97f25cea9..000000000 --- a/Utilities/crt.h +++ /dev/null @@ -1,154 +0,0 @@ -/*****************************************************************************/ -/* - * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation - * - * by EMMIR 2018-2023 - * modifications for Mesen by Persune - * https://github.com/LMP88959/NTSC-CRT - * - * YouTube: https://www.youtube.com/@EMMIR_KC/videos - * Discord: https://discord.com/invite/hdYctSmyQJ - */ -/*****************************************************************************/ -#pragma once - -#ifndef _CRT_H_ -#define _CRT_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/* crt.h - * - * An interface to convert a digital image to an analog NTSC signal - * and decode the NTSC signal back into a digital image. - * Can easily be integrated into real-time applications - * or be used as a command-line tool. - * - */ - -#define CRT_NES_MODE 1 -#define CRT_NES_HIRES 0 - -/* do bloom emulation (side effect: makes screen have black borders) */ -#define CRT_DO_BLOOM 0 -#define CRT_DO_VSYNC 1 /* look for VSYNC */ -#define CRT_DO_HSYNC 1 /* look for HSYNC */ -/* 0 = vertical chroma (228 chroma clocks per line) */ -/* 1 = checkered chroma (227.5 chroma clocks per line) */ -/* 2 = sawtooth chroma (227.3 chroma clocks per line) */ -#define CRT_CHROMA_PATTERN 2 - -#if CRT_NES_MODE -#undef CRT_CHROMA_PATTERN -#define CRT_CHROMA_PATTERN 2 /* force sawtooth pattern */ -#endif - -/* chroma clocks (subcarrier cycles) per line */ -#if (CRT_CHROMA_PATTERN == 1) -#define CRT_CC_LINE 2275 -#elif (CRT_CHROMA_PATTERN == 2) -#define CRT_CC_LINE 2273 -#else -/* this will give the 'rainbow' effect in the famous waterfall scene */ -#define CRT_CC_LINE 2280 -#endif - -/* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ -#if CRT_NES_MODE -#if CRT_NES_HIRES -#define CRT_CB_FREQ 6 /* carrier frequency relative to sample rate */ -#else -#define CRT_CB_FREQ 3 /* carrier frequency relative to sample rate */ -#endif -#else -#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ -#endif - -/* https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing */ -#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ -#define CRT_VRES 262 /* vertical resolution */ -#define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) - -#define CRT_TOP 15 /* first line with active video */ -#define CRT_BOT 255 /* final line with active video */ -#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ - -#define NES_AV_WIDTH 256 -#define NES_AV_HEIGHT CRT_LINES - -struct CRT { - signed char analog[CRT_INPUT_SIZE]; - signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ - int hsync, vsync; /* used internally to keep track of sync over frames */ - int hue, brightness, contrast, saturation; /* common monitor settings */ - //int black_point, white_point; /* user-adjustable */ not needed - int noise; /* noise level */ - int outw, outh; /* output width/height */ - int *out; /* output image */ - int ccf[4]; /* color carrier reference for faster convergence */ - int frameblend; /* blend successive frames */ -}; - -/* Initializes the library. Sets up filters. - * w - width of the output image - * h - height of the output image - * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB - */ -extern void crt_init(struct CRT *v, int w, int h, int *out); - -/* Updates the output image parameters - * w - width of the output image - * h - height of the output image - * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB - */ -extern void crt_resize(struct CRT *v, int w, int h, int *out); - -/* Resets the CRT settings back to their defaults */ -extern void crt_reset(struct CRT *v); - -struct NES_NTSC_SETTINGS { - const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ - unsigned int borderdata; /* border color, either BG or black */ - int w, h; /* width and height of image */ - int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ - int dot_crawl_offset; /* 0, 1, or 2 */ - int dot_skipped; - /* NOTE: NES mode is always progressive */ - /* color carrier sine wave. - * ex: { 0, 1, 0, -1 } - * ex: { 1, 0, -1, 0 } - */ - int cc[4]; - /* scale value for values in cc - * for example, if using { 0, 1, 0, -1 }, ccs should be 1. - * however, if using { 0, 16, 0, -16 }, ccs should be 16. - * For best results, don't scale the cc values more than 16. - */ - int ccs; -}; - -/* Setup analog NTSC blanking and sync signal - * s - struct containing settings to apply to this field - */ -extern void crtnes_setup_field(struct CRT* v, struct NES_NTSC_SETTINGS* s); - -/* Convert NES pixel data (generally 256x240) to analog NTSC signal - * s - struct containing settings to apply to this field - */ -extern void crt_nes2ntsc(struct CRT *v, struct NES_NTSC_SETTINGS *s); - -/* Decodes the NTSC signal generated by crt_2ntsc() - * noise - the amount of noise added to the signal (0 - inf) - */ -extern void crt_draw(struct CRT *v); - -/* Exposed utility function */ -extern void crt_sincos14(int *s, int *c, int n); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Utilities/crt_core.cpp b/Utilities/crt_core.cpp new file mode 100644 index 000000000..f270aea44 --- /dev/null +++ b/Utilities/crt_core.cpp @@ -0,0 +1,471 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#include "stdafx.h" +#include "crt_core.h" + +#include +#include + +/* ensure negative values for x get properly modulo'd */ +#define POSMOD(x, n) (((x) % (n) + (n)) % (n)) + +static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ + 0x0000, + 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, + 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, + 0x7f60 +}; + +static int +sintabil8(int n) +{ + int f, i, a, b; + + /* looks scary but if you don't change T14_2PI + * it won't cause out of bounds memory reads + */ + f = n >> 0 & 0xff; + i = n >> 8 & 0xff; + a = sigpsin15[i]; + b = sigpsin15[i + 1]; + return (a + ((b - a) * f >> 8)); +} + +/* 14-bit interpolated sine/cosine */ +extern void +crt_sincos14(int *s, int *c, int n) +{ + int h; + + n &= T14_MASK; + h = n & ((T14_2PI >> 1) - 1); + + if (h > ((T14_2PI >> 2) - 1)) { + *c = -sintabil8(h - (T14_2PI >> 2)); + *s = sintabil8((T14_2PI >> 1) - h); + } else { + *c = sintabil8((T14_2PI >> 2) - h); + *s = sintabil8(h); + } + if (n > ((T14_2PI >> 1) - 1)) { + *c = -*c; + *s = -*s; + } +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +#define HISTLEN 3 +#define HISTOLD (HISTLEN - 1) /* oldest entry */ +#define HISTNEW 0 /* newest entry */ + +#define EQ_P 16 /* if changed, the gains will need to be adjusted */ +#define EQ_R (1 << (EQ_P - 1)) /* rounding */ +#define EQ_CONVOLUTION 1 /* NOT 3 band equalizer, convolution instead */ +/* three band equalizer */ +static struct EQF { + int lf, hf; /* fractions */ + int g[3]; /* gains */ + int fL[4]; + int fH[4]; + int h[HISTLEN]; /* history */ +} eqY, eqI, eqQ; + +/* f_lo - low cutoff frequency + * f_hi - high cutoff frequency + * rate - sampling rate + * g_lo, g_mid, g_hi - gains + */ +#if EQ_CONVOLUTION +#define USE_7_SAMPLE 0 +#define USE_6_SAMPLE 0 +#define USE_5_SAMPLE 0 + +/* params unused to keep the function the same */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + memset(f, 0, sizeof(struct EQF)); +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i; + int *h = f->h; + + for (i = 6; i > 0; i--) { + h[i] = h[i - 1]; + } + h[0] = s; +#if USE_7_SAMPLE + /* index : 0 1 2 3 4 5 6 */ + /* weight: 1 4 7 8 7 4 1 */ + s = (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; +#elseif USE_6_SAMPLE + /* index : 0 1 2 3 4 5 */ + /* weight: 1 3 4 4 3 1 */ + s = (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; +#elseif USE_5_SAMPLE + /* index : 0 1 2 3 4 */ + /* weight: 1 2 2 2 1 */ + s = (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; +#else + /* index : 0 1 2 3 */ + /* weight: 1 1 1 1*/ + s = (s + h[3] + h[1] + h[2]) >> 2; +#endif + return s; +} +#else +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + int sn, cs; + + memset(f, 0, sizeof(struct EQF)); + + f->g[0] = g_lo; + f->g[1] = g_mid; + f->g[2] = g_hi; + + crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); + if (EQ_P >= 15) { + f->lf = 2 * (sn << (EQ_P - 15)); + } else { + f->lf = 2 * (sn >> (15 - EQ_P)); + } + crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); + if (EQ_P >= 15) { + f->hf = 2 * (sn << (EQ_P - 15)); + } else { + f->hf = 2 * (sn >> (15 - EQ_P)); + } +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->fL, 0, sizeof(f->fL)); + memset(f->fH, 0, sizeof(f->fH)); + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i, r[3]; + + f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; + f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; + + for (i = 1; i < 4; i++) { + f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; + f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; + } + + r[0] = f->fL[3]; + r[1] = f->fH[3] - f->fL[3]; + r[2] = f->h[HISTOLD] - f->fH[3]; + + for (i = 0; i < 3; i++) { + r[i] = (r[i] * f->g[i]) >> EQ_P; + } + + for (i = HISTOLD; i > 0; i--) { + f->h[i] = f->h[i - 1]; + } + f->h[HISTNEW] = s; + + return (r[0] + r[1] + r[2]); +} +#endif +/*****************************************************************************/ +/***************************** PUBLIC FUNCTIONS ******************************/ +/*****************************************************************************/ + +extern void +crt_resize(struct CRT *v, int w, int h, int *out) +{ + v->outw = w; + v->outh = h; + v->out = out; +} + +extern void +crt_reset(struct CRT *v) +{ + v->hue = 0; + v->saturation = 10; + v->brightness = 0; + v->contrast = 180; + v->black_point = 0; + v->white_point = 100; + v->hsync = 0; + v->vsync = 0; +} + +extern void +crt_init(struct CRT *v, int w, int h, int *out) +{ + memset(v, 0, sizeof(struct CRT)); + crt_resize(v, w, h, out); + crt_reset(v); + v->rn = 194; + + /* kilohertz to line sample conversion */ +#define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) + + /* band gains are pre-scaled as 16-bit fixed point + * if you change the EQ_P define, you'll need to update these gains too + */ + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +} + +/* search windows, in samples */ +#define HSYNC_WINDOW 6 +#define VSYNC_WINDOW 6 + +extern void +crt_demodulate(struct CRT *v, int noise) +{ + struct { + int y, i, q; + } out[AV_LEN + 1], *yiqA, *yiqB; + int i, j, line, rn; + signed char *sig; + int s = 0; + int field, ratio; + int *ccr; /* color carrier signal */ + int huesn, huecs; + int xnudge = -3, ynudge = 3; + int bright = v->brightness - (BLACK_LEVEL + v->black_point); +#if CRT_DO_BLOOM + int prev_e; /* filtered beam energy per scan line */ + int max_e; /* approx maximum energy in a scan line */ +#endif + + crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); + huesn >>= 11; /* make 4-bit */ + huecs >>= 11; + + rn = v->rn; + for (i = 0; i < CRT_INPUT_SIZE; i++) { + rn = (214019 * rn + 140327895); + + /* signal + noise */ + s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * noise) >> 8); + if (s > 127) { s = 127; } + if (s < -127) { s = -127; } + v->inp[i] = s; + } + v->rn = rn; + + /* Look for vertical sync. + * + * This is done by integrating the signal and + * seeing if it exceeds a threshold. The threshold of + * the vertical sync pulse is much higher because the + * vsync pulse is a lot longer than the hsync pulse. + * The signal needs to be integrated to lessen + * the noise in the signal. + */ + for (i = -VSYNC_WINDOW; i < VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->inp + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + /* increase the multiplier to make the vsync + * more stable when there is a lot of noise + */ + if (s <= (94 * SYNC_LEVEL)) { + goto vsync_found; + } + } + } +vsync_found: +#if CRT_DO_VSYNC + v->vsync = line; /* vsync found (or gave up) at this line */ +#else + v->vsync = -3; +#endif + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); +#if CRT_DO_BLOOM + max_e = (128 + (noise / 2)) * AV_LEN; + prev_e = (16384 / 8); +#endif + /* ratio of output height to active video lines in the signal */ + ratio = (v->outh << 16) / CRT_LINES; + ratio = (ratio + 32768) >> 16; + + field = (field * (ratio / 2)); + + for (line = CRT_TOP; line < CRT_BOT; line++) { + unsigned pos, ln; + int scanL, scanR, dx; + int L, R; + int *cL, *cR; + int wave[4]; + int dci, dcq; /* decoded I, Q */ + int xpos, ypos; + int beg, end; + int phasealign; +#if CRT_DO_BLOOM + int line_w; +#endif + + beg = (line - CRT_TOP + 0) * v->outh / CRT_LINES + field; + end = (line - CRT_TOP + 1) * v->outh / CRT_LINES + field; + + if (beg >= v->outh) { continue; } + if (end > v->outh) { end = v->outh; } + + /* Look for horizontal sync. + * See comment above regarding vertical sync. + */ + ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; + sig = v->inp + ln + v->hsync; + s = 0; + for (i = -HSYNC_WINDOW; i < HSYNC_WINDOW; i++) { + s += sig[SYNC_BEG + i]; + if (s <= (4 * SYNC_LEVEL)) { + break; + } + } +#if CRT_DO_HSYNC + v->hsync = POSMOD(i + v->hsync, CRT_HRES); +#else + v->hsync = 0; +#endif + + xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); + ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); + pos = xpos + ypos * CRT_HRES; + + ccr = v->ccf[ypos & 3]; + sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ + for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { + int p, n; + p = ccr[i & 3] * 127 / 128; /* fraction of the previous */ + n = sig[i]; /* mixed with the new sample */ + ccr[i & 3] = p + n; + } + + phasealign = POSMOD(v->hsync, 4); + + /* amplitude of carrier = saturation, phase difference = hue */ + dci = ccr[(phasealign + 1) & 3] - ccr[(phasealign + 3) & 3]; + dcq = ccr[(phasealign + 2) & 3] - ccr[(phasealign + 0) & 3]; + + /* rotate them by the hue adjustment angle */ + wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; + wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; + wave[2] = -wave[0]; + wave[3] = -wave[1]; + + sig = v->inp + pos; +#if CRT_DO_BLOOM + s = 0; + for (i = 0; i < AV_LEN; i++) { + s += sig[i]; /* sum up the scan line */ + } + /* bloom emulation */ + prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e); + line_w = (AV_LEN * 112 / 128) + (prev_e >> 9); + + dx = (line_w << 12) / v->outw; + scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12; + scanR = (AV_LEN - 1) << 12; + + L = (scanL >> 12); + R = (scanR >> 12); +#else + dx = ((AV_LEN - 1) << 12) / v->outw; + scanL = 0; + scanR = (AV_LEN - 1) << 12; + L = 0; + R = AV_LEN; +#endif + reset_eq(&eqY); + reset_eq(&eqI); + reset_eq(&eqQ); + + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; + } + + cL = v->out + beg * v->outw; + cR = cL + v->outw; + + for (pos = scanL; pos < scanR && cL < cR; pos += dx) { + int y, i, q; + int r, g, b; + int aa, bb; + + R = pos & 0xfff; + L = 0xfff - R; + s = pos >> 12; + + yiqA = out + s; + yiqB = out + s + 1; + + /* interpolate between samples if needed */ + y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); + i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); + q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); + + /* YIQ to RGB */ + r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; + g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; + b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + if (v->blend) { + aa = (r << 16 | g << 8 | b); + bb = *cL; + /* blend with previous color there */ + *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + } else { + *cL++ = (r << 16 | g << 8 | b); + } + } + + /* duplicate extra lines */ + ln = v->outw * sizeof(int); + for (s = beg + 1; s < (end - v->scanlines); s++) { + memcpy(v->out + s * v->outw, v->out + (s - 1) * v->outw, ln); + } + } +} diff --git a/Utilities/crt_core.h b/Utilities/crt_core.h new file mode 100644 index 000000000..46f113d97 --- /dev/null +++ b/Utilities/crt_core.h @@ -0,0 +1,103 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#pragma once + +#ifndef _CRT_CORE_H_ +#define _CRT_CORE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* crt_core.h + * + * The demodulator. This is also where you can define which system to emulate. + * + */ +#define CRT_SYSTEM_NTSC 0 /* standard NTSC */ +#define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */ + +/* the system to be compiled */ +#define CRT_SYSTEM CRT_SYSTEM_NES + +#if (CRT_SYSTEM == CRT_SYSTEM_NES) +#include "crt_nes.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_NTSC) +#include "crt_ntsc.h" +#else +#error No system defined +#endif + +/* do bloom emulation (side effect: makes screen have black borders) */ +#define CRT_DO_BLOOM 0 /* does not work for NES */ +#define CRT_DO_VSYNC 1 /* look for VSYNC */ +#define CRT_DO_HSYNC 1 /* look for HSYNC */ + +struct CRT { + signed char analog[CRT_INPUT_SIZE]; + signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ + + int outw, outh; /* output width/height */ + int *out; /* output image */ + + int hue, brightness, contrast, saturation; /* common monitor settings */ + int black_point, white_point; /* user-adjustable */ + int scanlines; /* leave gaps between lines if necessary */ + int blend; /* blend new field onto previous image */ + + /* internal data */ + int ccf[4][4]; /* faster color carrier convergence */ + int hsync, vsync; /* keep track of sync over frames */ + int rn; /* seed for the 'random' noise */ +}; + +/* Initializes the library. Sets up filters. + * w - width of the output image + * h - height of the output image + * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + */ +extern void crt_init(struct CRT *v, int w, int h, int *out); + +/* Updates the output image parameters + * w - width of the output image + * h - height of the output image + * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + */ +extern void crt_resize(struct CRT *v, int w, int h, int *out); + +/* Resets the CRT settings back to their defaults */ +extern void crt_reset(struct CRT *v); + +/* Modulates RGB image into an analog NTSC signal + * s - struct containing settings to apply to this field + */ +extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s); + +/* Demodulates the NTSC signal generated by crt_modulate() + * noise - the amount of noise added to the signal (0 - inf) + */ +extern void crt_demodulate(struct CRT *v, int noise); + +/*****************************************************************************/ +/*************************** FIXED POINT SIN/COS *****************************/ +/*****************************************************************************/ + +#define T14_2PI 16384 +#define T14_MASK (T14_2PI - 1) +#define T14_PI (T14_2PI / 2) + +extern void crt_sincos14(int *s, int *c, int n); + +#ifdef __cplusplus +} +#endif + +#endif /* _CRT_SINCOS_H_ */ diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp new file mode 100644 index 000000000..7a126b25a --- /dev/null +++ b/Utilities/crt_nes.cpp @@ -0,0 +1,225 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * modifications for Mesen by Persune + * https://github.com/LMP88959/NTSC-CRT + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#include "stdafx.h" +#include "crt_core.h" + +#if (CRT_SYSTEM == CRT_SYSTEM_NES) +#include +#include + +// Precalculate the low and high signal chosen for each 64 base colors +// with their respective attenuated values +// https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels +const int8_t IRE_levels[2][2][0x40]{ + // waveform low + { + // normal + { + // 0x + 43, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 0, 0, + // 1x + 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 2x + 110, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 0, 0, + // 3x + 110, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0 + }, + // attenuated + { + // 0x + 26 , -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, 0, 0, + // 1x + 51, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, 0, 0, + // 2x + 82, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, + // 3x + 82, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0 + } + }, + // waveform high + { + // normal + { + // 0x + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, -12, 0, 0, + // 1x + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 0, 0, 0, + // 2x + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 34, 0, 0, + // 3x + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 80, 0, 0 + }, + // attenuated + { + // 0x + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -17, 0, 0, + // 1x + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, -8, 0, 0, + // 2x + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 19, 0, 0, + // 3x + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 56, 0, 0 + } + } +}; + +/* generate the square wave for a given 9-bit pixel and phase */ +static int +square_sample(int p, int phase) +{ + static int active[6] = { + 0300, 0100, + 0500, 0400, + 0600, 0200 + }; + int hue; + bool e, v; + + hue = (p & 0x0f); + + v = (((hue + phase) % 12) < 6); + + /* red 0100, green 0200, blue 0400 */ + e = ((p & 0700) & active[(phase >> 1) % 6]); + + return IRE_levels[v][e][p & 0x3F]; +} + +extern void +crt_setup_field(struct CRT *v) +{ + int n; + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + /* vertical sync scanlines */ + if (n >= 259 && n <= CRT_VRES) { + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ + } else { + /* prerender/postrender/video scanlines */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + } +} + + +extern void +crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = CRT_LINES; + int n, phase; + int po, lo; + int iccf[4]; + int ccburst[4]; /* color phase for burst */ + int sn, cs; + + for (x = 0; x < 4; x++) { + n = s->hue + x * 90; + crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); + ccburst[x] = sn >> 10; + } + xo = AV_BEG; + yo = CRT_TOP; + + /* align signal */ + xo = (xo & ~3); + + /* this mess of offsetting logic was reached through trial and error */ + lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ + po = lo + 1; + if (lo == 1) { + lo = 3; + } + phase = po * 3; + + for (n = CRT_TOP; n <= (CRT_BOT + 2); n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + phase += LAV_BEG * 3; + t = LAV_BEG; + while (t < CRT_HRES) { + int ire, p; + p = s->border_color; + if (t == LAV_BEG) p = 0xf0; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + line[t++] = ire >> 2; + phase += 3; + } + + phase %= 12; + + } + + phase = 6; + + for (y = (lo - 3); y < desth; y++) { + signed char *line; + int t, cb; + int sy = (y * s->h) / desth; + + if (sy >= s->h) sy = s->h; + if (sy < 0) sy = 0; + + n = (y + yo); + line = &v->analog[n * CRT_HRES]; + + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + cb = ccburst[(t + po + n) & 3]; + line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + iccf[(t + n) & 3] = line[t]; + } + sy *= s->w; + phase += (xo * 3); + for (x = 0; x < destw; x++) { + int ire, p; + if (y >= 0) { + p = s->data[((x * s->w) / destw) + sy]; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; + } + phase += 3; + } + /* mod here so we don't overflow down the line */ + phase = (phase + ((CRT_HRES - destw) * 3)) % 12; + } + + for (x = 0; x < 4; x++) { + for (n = 0; n < 4; n++) { + /* don't know why, but it works */ + v->ccf[n][x] = iccf[(x + n + 1) & 3] << 7; + } + } +} +#endif diff --git a/Utilities/crt_nes.h b/Utilities/crt_nes.h new file mode 100644 index 000000000..48791687d --- /dev/null +++ b/Utilities/crt_nes.h @@ -0,0 +1,137 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * modifications for Mesen by Persune + * https://github.com/LMP88959/NTSC-CRT + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#pragma once + +#ifndef _CRT_NES_H_ +#define _CRT_NES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* crt_nes.h + * + * An interface to convert NES PPU output to an analog NTSC signal. + * + */ + +/* 0 = vertical chroma (228 chroma clocks per line) */ +/* 1 = checkered chroma (227.5 chroma clocks per line) */ +/* 2 = sawtooth chroma (227.3 chroma clocks per line) */ +#define CRT_CHROMA_PATTERN 2 + +/* chroma clocks (subcarrier cycles) per line */ +#if (CRT_CHROMA_PATTERN == 1) +#define CRT_CC_LINE 2275 +#elif (CRT_CHROMA_PATTERN == 2) +#define CRT_CC_LINE 2273 +#else +/* this will give the 'rainbow' effect in the famous waterfall scene */ +#define CRT_CC_LINE 2280 +#endif + +/* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ +#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ + +/* https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing */ +#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ +#define CRT_VRES 262 /* vertical resolution */ +#define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) + +#define CRT_TOP 15 /* first line with active video */ +#define CRT_BOT 255 /* final line with active video */ +#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ + +/* NES composite signal is measured in terms of PPU pixels, or cycles + * https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing + * + * FULL HORIZONTAL LINE SIGNAL + * (341 PPU px; one cycle skipped on odd rendered frames) + * |---------------------------------------------------------------------------| + * HBLANK (58 PPU px) ACTIVE VIDEO (283 PPU px) + * |-------------------||------------------------------------------------------| + * + * + * WITHIN HBLANK PERIOD: + * + * FP (9 PPU px) SYNC (25 PPU px) BW (4 PPU px) CB (15 PPU px) BP (5 PPU px) + * |--------------||---------------||------------||-------------||-------------| + * BLANK SYNC BLANK BLANK BLANK + * + * + * WITHIN ACTIVE VIDEO PERIOD: + * + * LB (15 PPU px) AV (256 PPU px) RB (11 PPU px) + * |--------------||--------------------------------------------||-------------| + * BORDER VIDEO BORDER + * + */ +#define LINE_BEG 0 +#define FP_PPUpx 9 /* front porch */ +#define SYNC_PPUpx 25 /* sync tip */ +#define BW_PPUpx 4 /* breezeway */ +#define CB_PPUpx 15 /* color burst */ +#define BP_PPUpx 5 /* back porch */ +#define PS_PPUpx 1 /* pulse */ +#define LB_PPUpx 15 /* left border */ +#define AV_PPUpx 256 /* active video */ +#define RB_PPUpx 11 /* right border */ +#define HB_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx) /* h blank */ +/* line duration should be ~63500 ns */ +#define LINE_PPUpx (FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx + BP_PPUpx + PS_PPUpx + LB_PPUpx + AV_PPUpx + RB_PPUpx) + +/* convert pixel offset to its corresponding point on the sampled line */ +#define PPUpx2pos(PPUpx) ((PPUpx) * CRT_HRES / LINE_PPUpx) +/* starting points for all the different pulses */ +#define FP_BEG PPUpx2pos(0) /* front porch point */ +#define SYNC_BEG PPUpx2pos(FP_PPUpx) /* sync tip point */ +#define BW_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx) /* breezeway point */ +#define CB_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx) /* color burst point */ +#define BP_BEG PPUpx2pos(FP_PPUpx + SYNC_PPUpx + BW_PPUpx + CB_PPUpx) /* back porch point */ +#define LAV_BEG PPUpx2pos(HB_PPUpx) /* full active video point */ +#define AV_BEG PPUpx2pos(HB_PPUpx + PS_PPUpx + LB_PPUpx) /* PPU active video point */ +#define AV_LEN PPUpx2pos(AV_PPUpx) /* active video length */ + +/* somewhere between 7 and 12 cycles */ +#define CB_CYCLES 10 + +/* line frequency */ +#define L_FREQ 1431818 /* full line */ + +/* IRE units (100 = 1.0V, -40 = 0.0V) */ +/* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ +#define WHITE_LEVEL 110 +#define BURST_LEVEL 30 +#define BLACK_LEVEL 0 +#define BLANK_LEVEL 0 +#define SYNC_LEVEL -37 + +struct NTSC_SETTINGS { + const unsigned short *data; /* 6 or 9-bit NES 'pixels' */ + int w, h; /* width and height of image */ + unsigned int border_color; /* either BG or black */ + int dot_crawl_offset; /* 0, 1, or 2 */ + /* NOTE: NES mode is always progressive */ + int hue; /* 0-359 */ +}; + +/* Setup analog NTSC blanking and sync signal + * s - struct containing settings to apply to this field + */ +extern void crt_setup_field(struct CRT* v); + +#ifdef __cplusplus +} +#endif + +#endif From ccb49227921e6675f3fec46b73a3bd4e5a97fa22 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 15:59:48 +0800 Subject: [PATCH 22/36] Add binding for frame blending --- Core/EmulationSettings.h | 5 ++- Core/LMP88959NtscFilter.cpp | 3 +- GUI.NET/Config/VideoInfo.cs | 3 +- .../Forms/Config/frmVideoConfig.Designer.cs | 38 +++++++++++++++++-- GUI.NET/Forms/Config/frmVideoConfig.cs | 6 +++ GUI.NET/InteropEmu.cs | 2 +- InteropDLL/ConsoleWrapper.cpp | 2 +- 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 7699e2acc..599b86574 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -247,6 +247,7 @@ struct NtscFilterSettings double DecodeMatrixQB = 1.702f; double Noise = 0; + bool FrameBlend = false; }; enum class RamPowerOnState @@ -1256,7 +1257,8 @@ class EmulationSettings bool keepVerticalResolution, bool colorimetryCorrection, bool useExternalPalette, - double Noise) + double Noise, + bool FrameBlend) { _ntscFilterSettings.Artifacts = artifacts; _ntscFilterSettings.Bleed = bleed; @@ -1284,6 +1286,7 @@ class EmulationSettings _ntscFilterSettings.KeepVerticalResolution = keepVerticalResolution; _ntscFilterSettings.ColorimetryCorrection = colorimetryCorrection; _ntscFilterSettings.Noise = Noise; + _ntscFilterSettings.FrameBlend = FrameBlend; } NtscFilterSettings GetNtscFilterSettings() diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 98147fced..45b1e65a4 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -59,10 +59,9 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); _noise = static_cast(ntscSettings.Noise * 500.0); - _crt.blend = static_cast(false); + _crt.blend = static_cast(ntscSettings.FrameBlend); _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); - //_nesNTSC.dot_skipped = _console->GetDotSkipped(); _nesNTSC.border_color = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; } diff --git a/GUI.NET/Config/VideoInfo.cs b/GUI.NET/Config/VideoInfo.cs index d613b4847..9a5e9a961 100644 --- a/GUI.NET/Config/VideoInfo.cs +++ b/GUI.NET/Config/VideoInfo.cs @@ -57,6 +57,7 @@ public class VideoInfo public double NtscDecodeMatrixQB = 1.702; [MinMax(0, 500)] public Int32 NtscNoise = 0; + public bool NtscFrameBlend = true; public bool RemoveSpriteLimit = false; public bool AdaptiveSpriteLimit = true; @@ -121,7 +122,7 @@ static public void ApplyConfig() InteropEmu.SetVideoAspectRatio(videoInfo.AspectRatio, videoInfo.CustomAspectRatio); InteropEmu.SetPictureSettings(videoInfo.Brightness / 100.0, videoInfo.Contrast / 100.0, videoInfo.Saturation / 100.0, videoInfo.Hue / 100.0, videoInfo.ScanlineIntensity / 100.0); - InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscDecodeMatrixIR, videoInfo.NtscDecodeMatrixQR, videoInfo.NtscDecodeMatrixIG, videoInfo.NtscDecodeMatrixQG, videoInfo.NtscDecodeMatrixIB, videoInfo.NtscDecodeMatrixQB, videoInfo.NtscVerticalBlend, videoInfo.NtscKeepVerticalResolution, videoInfo.NtscColorimetryCorrection, videoInfo.NtscUseExternalPalette, videoInfo.NtscNoise / 500.0); + InteropEmu.SetNtscFilterSettings(videoInfo.NtscArtifacts / 100.0, videoInfo.NtscBleed / 100.0, videoInfo.NtscFringing / 100.0, videoInfo.NtscGamma / 100.0, videoInfo.NtscResolution / 100.0, videoInfo.NtscSharpness / 100.0, videoInfo.NtscMergeFields, videoInfo.NtscYFilterLength / 100.0, videoInfo.NtscIFilterLength / 100.0, videoInfo.NtscQFilterLength / 100.0, videoInfo.NtscDecodeMatrixIR, videoInfo.NtscDecodeMatrixQR, videoInfo.NtscDecodeMatrixIG, videoInfo.NtscDecodeMatrixQG, videoInfo.NtscDecodeMatrixIB, videoInfo.NtscDecodeMatrixQB, videoInfo.NtscVerticalBlend, videoInfo.NtscKeepVerticalResolution, videoInfo.NtscColorimetryCorrection, videoInfo.NtscUseExternalPalette, videoInfo.NtscNoise / 500.0, videoInfo.NtscFrameBlend); if(!string.IsNullOrWhiteSpace(videoInfo.PaletteData)) { try { diff --git a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs index 589b6199a..5f09c393f 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.Designer.cs @@ -65,11 +65,13 @@ private void InitializeComponent() this.grpNtscFilter = new System.Windows.Forms.GroupBox(); this.tlpNtscFilter3 = new System.Windows.Forms.TableLayoutPanel(); this.trkNoise = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); + this.chkFrameBlend = new System.Windows.Forms.CheckBox(); this.tlpNtscFilter2 = new System.Windows.Forms.TableLayoutPanel(); this.trkYFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkIFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.trkQFilterLength = new Mesen.GUI.Controls.ctrlHorizontalTrackbar(); this.tableLayoutPanel19 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel20 = new System.Windows.Forms.TableLayoutPanel(); this.chkColorimetryCorrection = new System.Windows.Forms.CheckBox(); this.checkBox1 = new System.Windows.Forms.CheckBox(); this.tlpNtscFilter1 = new System.Windows.Forms.TableLayoutPanel(); @@ -182,6 +184,7 @@ private void InitializeComponent() this.tlpNtscFilter3.SuspendLayout(); this.tlpNtscFilter2.SuspendLayout(); this.tableLayoutPanel19.SuspendLayout(); + this.tableLayoutPanel20.SuspendLayout(); this.tlpNtscFilter1.SuspendLayout(); this.tableLayoutPanel6.SuspendLayout(); this.grpCommon.SuspendLayout(); @@ -706,11 +709,13 @@ private void InitializeComponent() this.tlpNtscFilter3.ColumnCount = 1; this.tlpNtscFilter3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tlpNtscFilter3.Controls.Add(this.trkNoise, 0, 0); + this.tlpNtscFilter3.Controls.Add(this.tableLayoutPanel20, 0, 1); this.tlpNtscFilter3.Location = new System.Drawing.Point(3, 16); this.tlpNtscFilter3.Margin = new System.Windows.Forms.Padding(0); this.tlpNtscFilter3.Name = "tlpNtscFilter3"; - this.tlpNtscFilter3.RowCount = 1; + this.tlpNtscFilter3.RowCount = 2; this.tlpNtscFilter3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpNtscFilter3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tlpNtscFilter3.Size = new System.Drawing.Size(253, 350); this.tlpNtscFilter3.TabIndex = 7; // @@ -729,6 +734,28 @@ private void InitializeComponent() this.trkNoise.Text = "Signal noise"; this.trkNoise.Value = 0; // + // tableLayoutPanel20 + // + this.tableLayoutPanel20.ColumnCount = 1; + this.tableLayoutPanel20.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel20.Controls.Add(this.chkFrameBlend, 0, 0); + this.tableLayoutPanel20.Location = new System.Drawing.Point(3, 153); + this.tableLayoutPanel20.Name = "tableLayoutPanel20"; + this.tableLayoutPanel20.RowCount = 1; + this.tableLayoutPanel20.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel20.Size = new System.Drawing.Size(247, 50); + this.tableLayoutPanel20.TabIndex = 27; + // + // chkFrameBlend + // + this.chkFrameBlend.AutoSize = true; + this.chkFrameBlend.Location = new System.Drawing.Point(3, 3); + this.chkFrameBlend.Name = "chkFrameBlend"; + this.chkFrameBlend.Size = new System.Drawing.Size(101, 17); + this.chkFrameBlend.TabIndex = 32; + this.chkFrameBlend.Text = "Frame blend"; + this.chkFrameBlend.UseVisualStyleBackColor = true; + // // tlpNtscFilter2 // this.tlpNtscFilter2.ColumnCount = 1; @@ -799,9 +826,8 @@ private void InitializeComponent() // // tableLayoutPanel19 // - this.tableLayoutPanel19.ColumnCount = 2; - this.tableLayoutPanel19.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 57.08502F)); - this.tableLayoutPanel19.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 42.91498F)); + this.tableLayoutPanel19.ColumnCount = 1; + this.tableLayoutPanel19.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel19.Controls.Add(this.chkColorimetryCorrection, 0, 0); this.tableLayoutPanel19.Controls.Add(this.checkBox1, 0, 1); this.tableLayoutPanel19.Location = new System.Drawing.Point(3, 153); @@ -2245,6 +2271,8 @@ private void InitializeComponent() this.tlpNtscFilter2.PerformLayout(); this.tableLayoutPanel19.ResumeLayout(false); this.tableLayoutPanel19.PerformLayout(); + this.tableLayoutPanel20.ResumeLayout(false); + this.tableLayoutPanel20.PerformLayout(); this.tlpNtscFilter1.ResumeLayout(false); this.tableLayoutPanel6.ResumeLayout(false); this.tableLayoutPanel6.PerformLayout(); @@ -2429,7 +2457,9 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkKeepVerticalResolution; private System.Windows.Forms.TableLayoutPanel tlpNtscFilter3; private ctrlHorizontalTrackbar trkNoise; + private System.Windows.Forms.CheckBox chkFrameBlend; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel19; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel20; private System.Windows.Forms.CheckBox chkColorimetryCorrection; private System.Windows.Forms.CheckBox checkBox1; } diff --git a/GUI.NET/Forms/Config/frmVideoConfig.cs b/GUI.NET/Forms/Config/frmVideoConfig.cs index 1e947a52e..fef402b7c 100644 --- a/GUI.NET/Forms/Config/frmVideoConfig.cs +++ b/GUI.NET/Forms/Config/frmVideoConfig.cs @@ -74,6 +74,7 @@ public frmVideoConfig() AddBinding("NtscQFilterLength", trkQFilterLength); AddBinding("NtscNoise", trkNoise); + AddBinding("NtscFrameBlend", chkFrameBlend); AddBinding("RemoveSpriteLimit", chkRemoveSpriteLimit); AddBinding("AdaptiveSpriteLimit", chkAdaptiveSpriteLimit); @@ -177,6 +178,7 @@ protected override bool ValidateInput() chkMergeFields.Visible = true; chkColorimetryCorrection.Visible = false; chkUseExternalPalette.Visible = true; + chkFrameBlend.Visible = false; grpNtscFilter.Visible = true; } @@ -192,6 +194,7 @@ protected override bool ValidateInput() chkMergeFields.Visible = false; chkColorimetryCorrection.Visible = true; chkUseExternalPalette.Visible = false; + chkFrameBlend.Visible = false; grpNtscFilter.Visible = true; } @@ -205,6 +208,7 @@ protected override bool ValidateInput() chkMergeFields.Visible = false; chkColorimetryCorrection.Visible = false; chkUseExternalPalette.Visible = false; + chkFrameBlend.Visible = true; grpNtscFilter.Visible = true; } @@ -218,6 +222,7 @@ protected override bool ValidateInput() chkMergeFields.Visible = false; chkColorimetryCorrection.Visible = false; chkUseExternalPalette.Visible = false; + chkFrameBlend.Visible = false; grpNtscFilter.Visible = false; } @@ -349,6 +354,7 @@ private void btnResetPictureSettings_Click(object sender, EventArgs e) chkColorimetryCorrection.Checked = true; chkKeepVerticalResolution.Checked = true; chkUseExternalPalette.Checked = true; + chkFrameBlend.Checked = false; trkYFilterLength.Value = 0; trkIFilterLength.Value = 50; diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index a4e36936e..2b37ab123 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -232,7 +232,7 @@ public static Image GetSaveStatePreview(string saveStatePath) [DllImport(DLLPath)] public static extern void SetVideoResizeFilter(VideoResizeFilter filter); [DllImport(DLLPath)] public static extern void SetRgbPalette(byte[] palette, UInt32 paletteSize); [DllImport(DLLPath)] public static extern void SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity); - [DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, [MarshalAs(UnmanagedType.I1)]bool verticalBlend, [MarshalAs(UnmanagedType.I1)]bool keepVerticalResolution, [MarshalAs(UnmanagedType.I1)] bool colorimetryCorrection, [MarshalAs(UnmanagedType.I1)] bool useExternalPalette, double Noise); + [DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, [MarshalAs(UnmanagedType.I1)]bool verticalBlend, [MarshalAs(UnmanagedType.I1)]bool keepVerticalResolution, [MarshalAs(UnmanagedType.I1)] bool colorimetryCorrection, [MarshalAs(UnmanagedType.I1)] bool useExternalPalette, double Noise, [MarshalAs(UnmanagedType.I1)] bool frameBlend); [DllImport(DLLPath)] public static extern void SetInputDisplaySettings(byte visiblePorts, InputDisplayPosition displayPosition, [MarshalAs(UnmanagedType.I1)]bool displayHorizontally); [DllImport(DLLPath)] public static extern void SetAutoSaveOptions(UInt32 delayInMinutes, [MarshalAs(UnmanagedType.I1)]bool showMessage); [DllImport(DLLPath)] public static extern void SetPauseScreenMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string message); diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 48a52d7e3..367eda9e8 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -661,7 +661,7 @@ namespace InteropEmu { DllExport void __stdcall GetRgbPalette(uint32_t *paletteBuffer) { _settings->GetUserRgbPalette(paletteBuffer); } DllExport void __stdcall SetRgbPalette(uint32_t *paletteBuffer, uint32_t paletteSize) { _settings->SetUserRgbPalette(paletteBuffer, paletteSize); } DllExport void __stdcall SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity) { _settings->SetPictureSettings(brightness, contrast, saturation, hue, scanlineIntensity); } - DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, bool verticalBlend, bool keepVerticalResolution, bool colorimetryCorrection, bool useExternalPalette, double noise) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, decodeMatrixIR, decodeMatrixQR, decodeMatrixIG, decodeMatrixQG, decodeMatrixIB, decodeMatrixQB, verticalBlend, keepVerticalResolution, colorimetryCorrection, useExternalPalette, noise); } + DllExport void __stdcall SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, bool mergeFields, double yFilterLength, double iFilterLength, double qFilterLength, double decodeMatrixIR, double decodeMatrixQR, double decodeMatrixIG, double decodeMatrixQG, double decodeMatrixIB, double decodeMatrixQB, bool verticalBlend, bool keepVerticalResolution, bool colorimetryCorrection, bool useExternalPalette, double noise, bool frameBlend) { _settings->SetNtscFilterSettings(artifacts, bleed, fringing, gamma, resolution, sharpness, mergeFields, yFilterLength, iFilterLength, qFilterLength, decodeMatrixIR, decodeMatrixQR, decodeMatrixIG, decodeMatrixQG, decodeMatrixIB, decodeMatrixQB, verticalBlend, keepVerticalResolution, colorimetryCorrection, useExternalPalette, noise, frameBlend); } DllExport void __stdcall SetPauseScreenMessage(char* message) { _settings->SetPauseScreenMessage(message); } DllExport void __stdcall SetInputDisplaySettings(uint8_t visiblePorts, InputDisplayPosition displayPosition, bool displayHorizontally) { _settings->SetInputDisplaySettings(visiblePorts, displayPosition, displayHorizontally); } From d3bcf36c152d949b31b79ad0b55d4fc9b03a1995 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 16:11:10 +0800 Subject: [PATCH 23/36] Sync with library implementation --- Utilities/crt_core.cpp | 73 ++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/Utilities/crt_core.cpp b/Utilities/crt_core.cpp index f270aea44..4b890b132 100644 --- a/Utilities/crt_core.cpp +++ b/Utilities/crt_core.cpp @@ -65,31 +65,20 @@ crt_sincos14(int *s, int *c, int n) /********************************* FILTERS ***********************************/ /*****************************************************************************/ -#define HISTLEN 3 -#define HISTOLD (HISTLEN - 1) /* oldest entry */ -#define HISTNEW 0 /* newest entry */ +/* convolution is much faster but the EQ looks softer, more authentic, and more analog */ +#define USE_CONVOLUTION 1 +#define USE_7_SAMPLE_KERNEL 0 +#define USE_6_SAMPLE_KERNEL 0 +#define USE_5_SAMPLE_KERNEL 0 -#define EQ_P 16 /* if changed, the gains will need to be adjusted */ -#define EQ_R (1 << (EQ_P - 1)) /* rounding */ -#define EQ_CONVOLUTION 1 /* NOT 3 band equalizer, convolution instead */ -/* three band equalizer */ -static struct EQF { - int lf, hf; /* fractions */ - int g[3]; /* gains */ - int fL[4]; - int fH[4]; - int h[HISTLEN]; /* history */ -} eqY, eqI, eqQ; +#if USE_CONVOLUTION -/* f_lo - low cutoff frequency - * f_hi - high cutoff frequency - * rate - sampling rate - * g_lo, g_mid, g_hi - gains +/* NOT 3 band equalizer, faster convolution instead. + * eq function names preserved to keep code clean */ -#if EQ_CONVOLUTION -#define USE_7_SAMPLE 0 -#define USE_6_SAMPLE 0 -#define USE_5_SAMPLE 0 +static struct EQF { + int h[5]; +} eqY, eqI, eqQ; /* params unused to keep the function the same */ static void @@ -112,30 +101,51 @@ eqf(struct EQF *f, int s) int i; int *h = f->h; - for (i = 6; i > 0; i--) { + for (i = 4; i > 0; i--) { h[i] = h[i - 1]; } h[0] = s; #if USE_7_SAMPLE /* index : 0 1 2 3 4 5 6 */ /* weight: 1 4 7 8 7 4 1 */ - s = (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; + return (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; #elseif USE_6_SAMPLE /* index : 0 1 2 3 4 5 */ /* weight: 1 3 4 4 3 1 */ - s = (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; + return (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; #elseif USE_5_SAMPLE - /* index : 0 1 2 3 4 */ + /* index : 0 1 2 3 4 */ /* weight: 1 2 2 2 1 */ - s = (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; + return (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; #else - /* index : 0 1 2 3 */ - /* weight: 1 1 1 1*/ - s = (s + h[3] + h[1] + h[2]) >> 2; + /* index : 0 1 2 3 */ + /* weight: 1 1 1 1*/ + return (s + h[3] + h[1] + h[2]) >> 2; #endif - return s; } + #else + +#define HISTLEN 3 +#define HISTOLD (HISTLEN - 1) /* oldest entry */ +#define HISTNEW 0 /* newest entry */ + +#define EQ_P 16 /* if changed, the gains will need to be adjusted */ +#define EQ_R (1 << (EQ_P - 1)) /* rounding */ +/* three band equalizer */ +static struct EQF { + int lf, hf; /* fractions */ + int g[3]; /* gains */ + int fL[4]; + int fH[4]; + int h[HISTLEN]; /* history */ +} eqY, eqI, eqQ; + +/* f_lo - low cutoff frequency + * f_hi - high cutoff frequency + * rate - sampling rate + * g_lo, g_mid, g_hi - gains + */ static void init_eq(struct EQF *f, int f_lo, int f_hi, int rate, @@ -199,6 +209,7 @@ eqf(struct EQF *f, int s) return (r[0] + r[1] + r[2]); } + #endif /*****************************************************************************/ /***************************** PUBLIC FUNCTIONS ******************************/ From c0fb97e845770a5f3a1ec043eb51246d67a2ce40 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 16:24:55 +0800 Subject: [PATCH 24/36] Sync with NTSC-CRT v2.0.0 --- Core/EmulationSettings.h | 2 +- Core/LMP88959NtscFilter.cpp | 2 +- Utilities/crt_core.h | 7 ++ Utilities/crt_nes.cpp | 224 ++++++++++++++++++++++++++++++++---- Utilities/crt_nes.h | 2 +- 5 files changed, 214 insertions(+), 23 deletions(-) diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 599b86574..bc0dd4c02 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -247,7 +247,7 @@ struct NtscFilterSettings double DecodeMatrixQB = 1.702f; double Noise = 0; - bool FrameBlend = false; + bool FrameBlend = true; }; enum class RamPowerOnState diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 45b1e65a4..c118ce50d 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -13,7 +13,7 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoF _frameBuffer = new uint32_t[(PPUpx2pos(256)) * 240](); crt_init(&_crt, (PPUpx2pos(256)), 240, reinterpret_cast(_frameBuffer)); - crt_setup_field(&_crt); + setup_field(&_crt); _nesNTSC.w = PPU::ScreenWidth; _nesNTSC.h = PPU::ScreenHeight; diff --git a/Utilities/crt_core.h b/Utilities/crt_core.h index 46f113d97..15a383f42 100644 --- a/Utilities/crt_core.h +++ b/Utilities/crt_core.h @@ -22,6 +22,13 @@ extern "C" { * The demodulator. This is also where you can define which system to emulate. * */ + +/* library version */ +#define CRT_MAJOR 2 +#define CRT_MINOR 0 +#define CRT_PATCH 0 + + #define CRT_SYSTEM_NTSC 0 /* standard NTSC */ #define CRT_SYSTEM_NES 1 /* decode 6 or 9-bit NES pixels */ diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp index 7a126b25a..4e8226c86 100644 --- a/Utilities/crt_nes.cpp +++ b/Utilities/crt_nes.cpp @@ -17,10 +17,13 @@ #include #include +#define USE_LUT 1 + +#if USE_LUT // Precalculate the low and high signal chosen for each 64 base colors // with their respective attenuated values // https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels -const int8_t IRE_levels[2][2][0x40]{ +const int IRE_levels[2][2][0x40]{ // waveform low { // normal @@ -94,18 +97,74 @@ square_sample(int p, int phase) return IRE_levels[v][e][p & 0x3F]; } +#else +/* generate the square wave for a given 9-bit pixel and phase */ +static int +square_sample(int p, int phase) +{ + static int active[6] = { + 0300, 0100, + 0500, 0400, + 0600, 0200 + }; + int bri, hue, v; -extern void -crt_setup_field(struct CRT *v) + hue = (p & 0x0f); + + /* last two columns are black */ + if (hue >= 0x0e) { + return 0; + } + + bri = ((p & 0x30) >> 4) * 300; + + switch (hue) { + case 0: + v = bri + 410; + break; + case 0x0d: + v = bri - 300; + break; + default: + v = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); + break; + } + + if (v > 1024) { + v = 1024; + } + /* red 0100, green 0200, blue 0400 */ + if ((p & 0700) & active[(phase >> 1) % 6]) { + return (v >> 1) + (v >> 2); + } + + return v; +} +#endif + +#define NES_OPTIMIZED 1 + +/* the optimized version is NOT the most optimized version, it just performs + * some simple refactoring to prevent a few redundant computations + */ +#if NES_OPTIMIZED + + +/* this function is an optimization + * basically factoring out the field setup since as long as CRT->analog + * does not get cleared, all of this should remain the same every update + */ +static void +setup_field(struct CRT *v) { int n; - + for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; - + t = LINE_BEG; - + /* vertical sync scanlines */ if (n >= 259 && n <= CRT_VRES) { while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ @@ -119,11 +178,11 @@ crt_setup_field(struct CRT *v) } } } - - + extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) { + static int init = 0; int x, y, xo, yo; int destw = AV_LEN; int desth = CRT_LINES; @@ -131,8 +190,12 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) int po, lo; int iccf[4]; int ccburst[4]; /* color phase for burst */ + int burst_level[4]; int sn, cs; - + if (!init) { + setup_field(v); + init = 1; + } for (x = 0; x < 4; x++) { n = s->hue + x * 90; crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); @@ -151,13 +214,13 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) lo = 3; } phase = po * 3; - + for (n = CRT_TOP; n <= (CRT_BOT + 2); n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; t = LINE_BEG; - + phase += LAV_BEG * 3; t = LAV_BEG; while (t < CRT_HRES) { @@ -169,52 +232,172 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - line[t++] = ire >> 2; + ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + line[t++] = ire; phase += 3; } phase %= 12; } - + phase = 6; - + /* precalculate to save some CPU */ + for (x = 0; x < 4; x++) { + burst_level[x] = (BLANK_LEVEL + (ccburst[x] * BURST_LEVEL)) >> 5; + } for (y = (lo - 3); y < desth; y++) { signed char *line; - int t, cb; + int t; int sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; if (sy < 0) sy = 0; - + n = (y + yo); line = &v->analog[n * CRT_HRES]; /* CB_CYCLES of color burst at 3.579545 Mhz */ for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { - cb = ccburst[(t + po + n) & 3]; - line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + line[t] = burst_level[(t + po + n) & 3]; iccf[(t + n) & 3] = line[t]; } sy *= s->w; phase += (xo * 3); for (x = 0; x < destw; x++) { - int ire, p; if (y >= 0) { + int ire, p; + p = s->data[((x * s->w) / destw) + sy]; ire = BLACK_LEVEL + v->black_point; ire += square_sample(p, phase + 0); ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; + ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; } phase += 3; } /* mod here so we don't overflow down the line */ phase = (phase + ((CRT_HRES - destw) * 3)) % 12; } + + for (x = 0; x < 4; x++) { + for (n = 0; n < 4; n++) { + /* don't know why, but it works */ + v->ccf[n][x] = iccf[(x + n + 1) & 3] << 7; + } + } +} +#else +/* NOT NES_OPTIMIZED */ +extern void +crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = CRT_LINES; + int n, phase; + int po, lo; + int iccf[4]; + int ccburst[4]; /* color phase for burst */ + int sn, cs; + + for (x = 0; x < 4; x++) { + n = s->hue + x * 90; + crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); + ccburst[x] = sn >> 10; + } + xo = AV_BEG; + yo = CRT_TOP; + + /* align signal */ + xo = (xo & ~3); + + /* this mess of offsetting logic was reached through trial and error */ + lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ + po = lo + 1; + if (lo == 1) { + lo = 3; + } + phase = 3 + po * 3; + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + /* vertical sync scanlines */ + if (n >= 259 && n <= CRT_VRES) { + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < PPUpx2pos(327)) line[t++] = SYNC_LEVEL; /* sync separator */ + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; /* blank */ + } else { + int cb; + /* prerender/postrender/video scanlines */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < CB_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { + cb = ccburst[(t + po + n) & 3]; + line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + iccf[(t + n) & 3] = line[t]; + } + while (t < LAV_BEG) line[t++] = BLANK_LEVEL; + phase += t * 3; + if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { + while (t < CRT_HRES) { + int ire, p; + p = s->border_color; + if (t == LAV_BEG) p = 0xf0; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + line[t++] = ire; + phase += 3; + } + } else { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + phase += (CRT_HRES - LAV_BEG) * 3; + } + phase %= 12; + } + } + + phase = 6; + + for (y = (lo - 3); y < desth; y++) { + int sy = (y * s->h) / desth; + if (sy >= s->h) sy = s->h; + if (sy < 0) sy = 0; + + sy *= s->w; + phase += (xo * 3); + for (x = 0; x < destw; x++) { + if (y >= 0) { + int ire, p; + + p = s->data[((x * s->w) / destw) + sy]; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + } + phase += 3; + } + /* mod here so we don't overflow down the line */ + phase = (phase + ((CRT_HRES - destw) * 3)) % 12; + } + for (x = 0; x < 4; x++) { for (n = 0; n < 4; n++) { /* don't know why, but it works */ @@ -223,3 +406,4 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) } } #endif +#endif diff --git a/Utilities/crt_nes.h b/Utilities/crt_nes.h index 48791687d..85393105b 100644 --- a/Utilities/crt_nes.h +++ b/Utilities/crt_nes.h @@ -128,7 +128,7 @@ struct NTSC_SETTINGS { /* Setup analog NTSC blanking and sync signal * s - struct containing settings to apply to this field */ -extern void crt_setup_field(struct CRT* v); +extern void setup_field(struct CRT* v); #ifdef __cplusplus } From 868da7bc146fbbcac141abd2a531b5db46edd0d6 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 16:55:45 +0800 Subject: [PATCH 25/36] Fix linker issue --- Utilities/crt_nes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp index 4e8226c86..2182f7679 100644 --- a/Utilities/crt_nes.cpp +++ b/Utilities/crt_nes.cpp @@ -154,8 +154,8 @@ square_sample(int p, int phase) * basically factoring out the field setup since as long as CRT->analog * does not get cleared, all of this should remain the same every update */ -static void -setup_field(struct CRT *v) +extern void +setup_field(struct CRT* v) { int n; From 394ee8f9ade001dba0a89be71b53053d59c70394 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 16:57:31 +0800 Subject: [PATCH 26/36] Normalize IRE level output --- Utilities/crt_nes.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp index 2182f7679..764221d7d 100644 --- a/Utilities/crt_nes.cpp +++ b/Utilities/crt_nes.cpp @@ -232,8 +232,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; - line[t++] = ire; + line[t++] = ire >> 2; phase += 3; } @@ -274,8 +273,7 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; } phase += 3; } From 0f590c6b349136067a095a86f927b8a12d74fbec Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 18 Jan 2023 20:06:15 +0800 Subject: [PATCH 27/36] Remove unnecessary code --- Utilities/crt_nes.cpp | 51 ++----------------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp index 764221d7d..24dfecfc4 100644 --- a/Utilities/crt_nes.cpp +++ b/Utilities/crt_nes.cpp @@ -17,13 +17,10 @@ #include #include -#define USE_LUT 1 - -#if USE_LUT // Precalculate the low and high signal chosen for each 64 base colors // with their respective attenuated values // https://www.nesdev.org/wiki/NTSC_video#Brightness_Levels -const int IRE_levels[2][2][0x40]{ +const int8_t IRE_levels[2][2][0x40]{ // waveform low { // normal @@ -80,7 +77,7 @@ const int IRE_levels[2][2][0x40]{ static int square_sample(int p, int phase) { - static int active[6] = { + static const int active[6] = { 0300, 0100, 0500, 0400, 0600, 0200 @@ -97,50 +94,6 @@ square_sample(int p, int phase) return IRE_levels[v][e][p & 0x3F]; } -#else -/* generate the square wave for a given 9-bit pixel and phase */ -static int -square_sample(int p, int phase) -{ - static int active[6] = { - 0300, 0100, - 0500, 0400, - 0600, 0200 - }; - int bri, hue, v; - - hue = (p & 0x0f); - - /* last two columns are black */ - if (hue >= 0x0e) { - return 0; - } - - bri = ((p & 0x30) >> 4) * 300; - - switch (hue) { - case 0: - v = bri + 410; - break; - case 0x0d: - v = bri - 300; - break; - default: - v = (((hue + phase) % 12) < 6) ? (bri + 410) : (bri - 300); - break; - } - - if (v > 1024) { - v = 1024; - } - /* red 0100, green 0200, blue 0400 */ - if ((p & 0700) & active[(phase >> 1) % 6]) { - return (v >> 1) + (v >> 2); - } - - return v; -} -#endif #define NES_OPTIMIZED 1 From 8738a09cdb995d209909602710c8b9788dd23e2f Mon Sep 17 00:00:00 2001 From: Persune Date: Thu, 26 Jan 2023 18:31:30 +0800 Subject: [PATCH 28/36] Adjust default contrast value to avoid clipping --- Core/LMP88959NtscFilter.cpp | 2 +- Utilities/crt_nes.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index c118ce50d..119d126ce 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -57,7 +57,7 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _crt.hue = static_cast(pictureSettings.Hue * 180.0); _crt.saturation = static_cast(((pictureSettings.Saturation + 1.0) / 2.0) * 25.0); _crt.brightness = static_cast(pictureSettings.Brightness * 100.0); - _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 360.0); + _crt.contrast = static_cast(((pictureSettings.Contrast + 1.0) / 2.0) * 301.0); _noise = static_cast(ntscSettings.Noise * 500.0); _crt.blend = static_cast(ntscSettings.FrameBlend); diff --git a/Utilities/crt_nes.h b/Utilities/crt_nes.h index 85393105b..09a42c15d 100644 --- a/Utilities/crt_nes.h +++ b/Utilities/crt_nes.h @@ -110,7 +110,7 @@ extern "C" { /* IRE units (100 = 1.0V, -40 = 0.0V) */ /* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ -#define WHITE_LEVEL 110 +#define WHITE_LEVEL 100 #define BURST_LEVEL 30 #define BLACK_LEVEL 0 #define BLANK_LEVEL 0 From 0cccf6728c93209ce8cbe519bc777a5d920232df Mon Sep 17 00:00:00 2001 From: Persune Date: Thu, 26 Jan 2023 18:39:54 +0800 Subject: [PATCH 29/36] Increase sync window --- Utilities/crt_core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Utilities/crt_core.cpp b/Utilities/crt_core.cpp index 4b890b132..ff32423dc 100644 --- a/Utilities/crt_core.cpp +++ b/Utilities/crt_core.cpp @@ -256,8 +256,8 @@ crt_init(struct CRT *v, int w, int h, int *out) } /* search windows, in samples */ -#define HSYNC_WINDOW 6 -#define VSYNC_WINDOW 6 +#define HSYNC_WINDOW 8 +#define VSYNC_WINDOW 8 extern void crt_demodulate(struct CRT *v, int noise) From 7610d057b847a1e31a2b2cdbc9737d373d209c74 Mon Sep 17 00:00:00 2001 From: Persune Date: Thu, 26 Jan 2023 19:32:48 +0800 Subject: [PATCH 30/36] Fix broken emphasis colors on Bisqwit NTSC --- Core/BisqwitNtscFilter.cpp | 35 +++++++++++++++++++++--- Core/BisqwitNtscFilter.h | 54 ++------------------------------------ 2 files changed, 33 insertions(+), 56 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 9f5355cc7..8d6d8cba1 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -13,6 +13,36 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider _resDivider = resDivider; _stopThread = false; _workDone = false; + + const int8_t signalLumaLow[2][4] = { { -29, -15, 22, 71 }, { -38, -28, -1, 34 } }; + const int8_t signalLumaHigh[2][4] = { { 32, 66, 105, 105 }, { 6, 31, 58, 58 } }; + + //Precalculate the low and high signal chosen for each 64 base colors + //with their respective attenuated values + for (int h = 0; h <= 1; h++) { + for (int i = 0; i <= 0x3F; i++) { + + int m = signalLumaLow[h][i / 0x10]; + int q = signalLumaHigh[h][i / 0x10]; + + if ((i & 0x0F) == 0x0D) { + q = m; + } + else if ((i & 0x0F) == 0) { + m = q; + } + else if ((i & 0x0F) >= 0x0E) { + // colors $xE and $xF are not affected by emphasis + // https://forums.nesdev.org/viewtopic.php?p=160669#p160669 + m = signalLumaLow[0][1]; + q = signalLumaLow[0][1]; + } + + _signalLow[h][i] = m; + _signalHigh[h][i] = q; + } + } + _startingPhase = _console->GetStartingPhase(); _extraThread = std::thread([=]() { //Worker thread to improve decode speed @@ -176,10 +206,7 @@ void BisqwitNtscFilter::GenerateNtscSignal(int8_t *ntscSignal, int &phase, int r int8_t voltage; for(int j = 0; j < _signalsPerPixel; j++) { - // colors $xE and $xF are not affected by emphasis - // https://forums.nesdev.org/viewtopic.php?p=160669#p160669 - if ((color & 0x0F) <= 0x0D) - attenuate = (phaseBitmask & emphasis_wave); + attenuate = (phaseBitmask & emphasis_wave); voltage = _signalHigh[attenuate][color]; diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index 5e94e6e30..8eed6df4a 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -38,61 +38,11 @@ class BisqwitNtscFilter : public BaseVideoFilter //To finetune hue, you would have to recalculate sinetable[]. (Coarse changes can be made with Phase0.) int8_t _sinetable[27]; // 8*sin(x*2pi/12) + int8_t _signalLow[2][0x40]; + int8_t _signalHigh[2][0x40]; uint8_t _startingPhase = 0; - //Precalculate the low and high signal chosen for each 64 base colors - //with their respective attenuated values - const int8_t _signalLow[2][0x40] = { - // normal - { - // 0x - 32, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -15, -15, - // 1x - 66, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, - // 2x - 110, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, -15, -15, - // 3x - 110, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, -15, -15 - }, - // attenuated - { - // 0x - 26, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -15, -15, - // 1x - 51, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -15, -15, - // 2x - 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -15, -15, - // 3x - 82, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, -15, -15 - } - }; - - const int8_t _signalHigh[2][0x40] = { - // normal - { - // 0x - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -29, -15, -15, - // 1x - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, -15, -15, -15, - // 2x - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 22, -15, -15, - // 3x - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 71, -15, -15, - }, - // attenuated - { - // 0x - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, -38, -15, -15, - // 1x - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, -28, -15, -15, - // 2x - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, -1, -15, -15, - // 3x - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 34, -15, -15, - } - }; - void RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend); void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0); From 11979ce1112989fc5e65aad49b2c27200f5ce43a Mon Sep 17 00:00:00 2001 From: Persune Date: Tue, 28 Mar 2023 09:03:04 +0800 Subject: [PATCH 31/36] Refactor dot crawl phase Parity with https://github.com/SourMesen/Mesen2/commit/77acdab5840cbd38624ab9ae14c7ff2359e71e88 --- Core/BaseVideoFilter.cpp | 8 +++++++- Core/BaseVideoFilter.h | 4 +++- Core/BisqwitNtscFilter.cpp | 7 ++----- Core/BisqwitNtscFilter.h | 2 -- Core/Console.cpp | 4 ++-- Core/Console.h | 2 +- Core/LMP88959NtscFilter.cpp | 2 +- Core/NtscFilter.cpp | 7 ++----- Core/NtscFilter.h | 1 - Core/PPU.cpp | 2 ++ Core/PPU.h | 5 +++-- Core/SaveStateManager.cpp | 2 +- Core/VideoDecoder.cpp | 5 +++-- Core/VideoDecoder.h | 1 + 14 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 95ab58812..0d7ada459 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -52,11 +52,17 @@ bool BaseVideoFilter::IsOddFrame() return _isOddFrame; } -void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber) +uint32_t BaseVideoFilter::GetVideoPhase() +{ + return _videoPhase; +} + +void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhase) { _frameLock.Acquire(); _overscan = _console->GetSettings()->GetOverscanDimensions(); _isOddFrame = frameNumber & 0x01; + _videoPhase = videoPhase; UpdateBufferSize(); OnBeforeApplyFilter(); ApplyFilter(ppuOutputBuffer); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index add010d91..e3a195862 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -15,6 +15,7 @@ class BaseVideoFilter SimpleLock _frameLock; OverscanDimensions _overscan; bool _isOddFrame; + uint32_t _videoPhase = 0; void UpdateBufferSize(); @@ -30,7 +31,8 @@ class BaseVideoFilter uint32_t* GetOutputBuffer(); bool IsOddFrame(); - void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber); + uint32_t GetVideoPhase(); + void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber, uint32_t videoPhase); void TakeScreenshot(string romName, VideoFilterType filterType); void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr, bool rawScreenshot = false); diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 8d6d8cba1..66eff1e69 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -43,7 +43,6 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } } - _startingPhase = _console->GetStartingPhase(); _extraThread = std::thread([=]() { //Worker thread to improve decode speed while(!_stopThread) { @@ -60,7 +59,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, (_startingPhase * 4) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, outputBuffer, (GetVideoPhase() * 4) + 327360); _workDone = true; } @@ -80,7 +79,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), (_startingPhase * 4) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, GetOutputBuffer(), (GetVideoPhase() * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } @@ -113,8 +112,6 @@ void BisqwitNtscFilter::OnBeforeApplyFilter() _sinetable[i] = (int8_t)(8 * std::sin(i * 2 * pi / 12 + pictureSettings.Hue * pi)); } - _startingPhase = _console->GetStartingPhase(); - _yWidth = (int)(12 + ntscSettings.YFilterLength * 22); _iWidth = (int)(12 + ntscSettings.IFilterLength * 22); _qWidth = (int)(12 + ntscSettings.QFilterLength * 22); diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index 8eed6df4a..f0fb89674 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -41,8 +41,6 @@ class BisqwitNtscFilter : public BaseVideoFilter int8_t _signalLow[2][0x40]; int8_t _signalHigh[2][0x40]; - uint8_t _startingPhase = 0; - void RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend); void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0); diff --git a/Core/Console.cpp b/Core/Console.cpp index 3e105cc5b..5c738d01d 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -568,9 +568,9 @@ uint32_t Console::GetFrameCount() return _ppu ? _ppu->GetFrameCount() : 0; } -uint8_t Console::GetStartingPhase() +uint8_t Console::GetVideoPhase() { - return _ppu ? _ppu->GetStartingPhase() : 0; + return _ppu ? _ppu->GetVideoPhase() : 0; } bool Console::GetDotSkipped() diff --git a/Core/Console.h b/Core/Console.h index f5442f68e..174b015d1 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -205,7 +205,7 @@ class Console : public std::enable_shared_from_this RomInfo GetRomInfo(); uint32_t GetFrameCount(); // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - uint8_t GetStartingPhase(); + uint8_t GetVideoPhase(); bool GetDotSkipped(); NesModel GetModel(); diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index 119d126ce..b0fb99c20 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -61,7 +61,7 @@ void LMP88959NtscFilter::OnBeforeApplyFilter() _noise = static_cast(ntscSettings.Noise * 500.0); _crt.blend = static_cast(ntscSettings.FrameBlend); - _nesNTSC.dot_crawl_offset = _console->GetStartingPhase(); + _nesNTSC.dot_crawl_offset = GetVideoPhase(); _nesNTSC.border_color = _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F; } diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 6a4fa9cdd..4e662da99 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -11,8 +11,6 @@ NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) _ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240](); - - _startingPhase = _console->GetStartingPhase(); _extraThread = std::thread([=]() { //Worker thread to improve decode speed while (!_stopThread) { @@ -20,7 +18,7 @@ NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) if (_stopThread) { break; } - uint8_t phase = _startingPhase; + uint8_t phase = GetVideoPhase(); for (int i = 0; i < 120; i++) { nes_ntsc_blit(&_ntscData, // input += in_row_width; @@ -150,7 +148,6 @@ void NtscFilter::OnBeforeApplyFilter() nes_ntsc_init(&_ntscData, &_ntscSetup); } - _startingPhase = _console->GetStartingPhase(); } void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) @@ -159,7 +156,7 @@ void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - uint8_t phase = _startingPhase; + uint8_t phase = GetVideoPhase(); for (int i = 120; i < 240; i++) { nes_ntsc_blit(&_ntscData, // input += in_row_width; diff --git a/Core/NtscFilter.h b/Core/NtscFilter.h index 86791ff89..afbe5f3d7 100644 --- a/Core/NtscFilter.h +++ b/Core/NtscFilter.h @@ -13,7 +13,6 @@ class NtscFilter : public BaseVideoFilter nes_ntsc_t _ntscData; bool _keepVerticalRes = false; bool _useExternalPalette = true; - uint8_t _startingPhase = 0; uint8_t _palette[512 * 3]; uint16_t* _ppuOutputBuffer = nullptr; uint32_t* _ntscBuffer; diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 267b7e764..bfe63b328 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -1186,6 +1186,8 @@ void PPU::DebugCopyOutputBuffer(uint16_t *target) void PPU::SendFrame() { UpdateGrayscaleAndIntensifyBits(); + //Get phase at the start of the current frame (341*241 cycles ago) + _videoPhase = ((_masterClock / _masterClockDivider) - 82181) % 3; _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); diff --git a/Core/PPU.h b/Core/PPU.h index 0fcccf8e2..8ec006e1b 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -110,6 +110,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _corruptOamRow[32]; bool _isDotSkipped; + uint32_t _videoPhase; void UpdateStatusFlag(); @@ -219,10 +220,10 @@ class PPU : public IMemoryHandler, public Snapshotable return _frameCount; } - uint8_t GetStartingPhase() + uint8_t GetVideoPhase() { // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - return _nesModel == NesModel::NTSC ? _cycle % 3 : 0; + return _videoPhase; } bool GetDotSkipped() diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index 02e26bc0d..016fbb7ff 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -337,7 +337,7 @@ int32_t SaveStateManager::GetSaveStatePreview(string saveStatePath, uint8_t* png if(GetScreenshotData(frameData, stream)) { DefaultVideoFilter filter(_console); FrameInfo frameInfo = filter.GetFrameInfo(); - filter.SendFrame((uint16_t*)frameData.data(), 0); + filter.SendFrame((uint16_t*)frameData.data(), 0, 0); std::stringstream pngStream; PNGHelper::WritePNG(pngStream, filter.GetOutputBuffer(), frameInfo.Width, frameInfo.Height); diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index 3a6f40dde..01ac090bb 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -102,7 +102,7 @@ void VideoDecoder::DecodeFrame(bool synchronous) if(_hdFilterEnabled) { ((HdVideoFilter*)_videoFilter.get())->SetHdScreenTiles(_hdScreenInfo); } - _videoFilter->SendFrame(_ppuOutputBuffer, _frameNumber); + _videoFilter->SendFrame(_ppuOutputBuffer, _frameNumber, _videoPhase); uint32_t* outputBuffer = _videoFilter->GetOutputBuffer(); FrameInfo frameInfo = _videoFilter->GetFrameInfo(); @@ -195,6 +195,7 @@ void VideoDecoder::UpdateFrame(void *ppuOutputBuffer, HdScreenInfo *hdScreenInfo } _frameNumber = _console->GetFrameCount(); + _videoPhase = _console->GetVideoPhase(); _hdScreenInfo = hdScreenInfo; _ppuOutputBuffer = (uint16_t*)ppuOutputBuffer; _frameChanged = true; @@ -271,7 +272,7 @@ void VideoDecoder::TakeScreenshot(std::stringstream &stream, bool rawScreenshot) if(rawScreenshot) { //Take screenshot without NTSC filter on DefaultVideoFilter filter(_console); - filter.SendFrame(_ppuOutputBuffer, 0); + filter.SendFrame(_ppuOutputBuffer, 0, 0); filter.TakeScreenshot(_videoFilterType, "", &stream, rawScreenshot); } else if(_videoFilter) { _videoFilter->TakeScreenshot(_videoFilterType, "", &stream, rawScreenshot); diff --git a/Core/VideoDecoder.h b/Core/VideoDecoder.h index 2a2249765..274f8598b 100644 --- a/Core/VideoDecoder.h +++ b/Core/VideoDecoder.h @@ -41,6 +41,7 @@ class VideoDecoder atomic _frameChanged; atomic _stopFlag; uint32_t _frameCount = 0; + uint32_t _videoPhase = 0; ScreenSize _previousScreenSize = {}; double _previousScale = 0; From ddac2446d16234fcac10a3b7f3127d0b28ccdd00 Mon Sep 17 00:00:00 2001 From: Persune Date: Tue, 28 Mar 2023 09:31:29 +0800 Subject: [PATCH 32/36] Force 2-phase dot crawl when overclocking is used Thanks Sour! --- Core/PPU.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/PPU.cpp b/Core/PPU.cpp index bfe63b328..3299d3061 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -1189,6 +1189,11 @@ void PPU::SendFrame() //Get phase at the start of the current frame (341*241 cycles ago) _videoPhase = ((_masterClock / _masterClockDivider) - 82181) % 3; + if (_settings->GetPpuExtraScanlinesAfterNmi() != 0 || _settings->GetPpuExtraScanlinesBeforeNmi() != 0) { + //Force 2-phase pattern when overclocking is used + _videoPhase = _frameCount & 0x01; + } + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer); #ifdef LIBRETRO From bcb1569404ea288a2fafe2c670101b288476a262 Mon Sep 17 00:00:00 2001 From: Persune Date: Sun, 2 Apr 2023 15:44:51 +0800 Subject: [PATCH 33/36] Sync with NTSC-CRT v2.2.0 --- Core/LMP88959NtscFilter.cpp | 3 +- Utilities/crt_core.cpp | 189 ++++++++++++++++++++++++++++++----- Utilities/crt_core.h | 40 ++++++-- Utilities/crt_nes.cpp | 194 +++++++++++++++++------------------- Utilities/crt_nes.h | 25 +++-- 5 files changed, 304 insertions(+), 147 deletions(-) diff --git a/Core/LMP88959NtscFilter.cpp b/Core/LMP88959NtscFilter.cpp index b0fb99c20..7be98610a 100644 --- a/Core/LMP88959NtscFilter.cpp +++ b/Core/LMP88959NtscFilter.cpp @@ -12,8 +12,7 @@ LMP88959NtscFilter::LMP88959NtscFilter(shared_ptr console) : BaseVideoF memset(&_nesNTSC, 0, sizeof(NTSC_SETTINGS)); _frameBuffer = new uint32_t[(PPUpx2pos(256)) * 240](); - crt_init(&_crt, (PPUpx2pos(256)), 240, reinterpret_cast(_frameBuffer)); - setup_field(&_crt); + crt_init(&_crt, (PPUpx2pos(256)), 240, 5, reinterpret_cast(_frameBuffer)); _nesNTSC.w = PPU::ScreenWidth; _nesNTSC.h = PPU::ScreenHeight; diff --git a/Utilities/crt_core.cpp b/Utilities/crt_core.cpp index ff32423dc..bfa5bc6e0 100644 --- a/Utilities/crt_core.cpp +++ b/Utilities/crt_core.cpp @@ -61,6 +61,23 @@ crt_sincos14(int *s, int *c, int n) } } +extern int +crt_bpp4fmt(int format) +{ + switch (format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_BGR: + return 3; + case CRT_PIX_FORMAT_ARGB: + case CRT_PIX_FORMAT_RGBA: + case CRT_PIX_FORMAT_ABGR: + case CRT_PIX_FORMAT_BGRA: + return 4; + default: + return 0; + } +} + /*****************************************************************************/ /********************************* FILTERS ***********************************/ /*****************************************************************************/ @@ -71,6 +88,12 @@ crt_sincos14(int *s, int *c, int n) #define USE_6_SAMPLE_KERNEL 0 #define USE_5_SAMPLE_KERNEL 0 +#if (CRT_CC_SAMPLES != 4) +/* the current convolutions do not filter properly at > 4 samples */ +#undef USE_CONVOLUTION +#define USE_CONVOLUTION 0 +#endif + #if USE_CONVOLUTION /* NOT 3 band equalizer, faster convolution instead. @@ -211,15 +234,17 @@ eqf(struct EQF *f, int s) } #endif + /*****************************************************************************/ /***************************** PUBLIC FUNCTIONS ******************************/ /*****************************************************************************/ extern void -crt_resize(struct CRT *v, int w, int h, int *out) +crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out) { v->outw = w; v->outh = h; + v->out_format = f; v->out = out; } @@ -237,10 +262,10 @@ crt_reset(struct CRT *v) } extern void -crt_init(struct CRT *v, int w, int h, int *out) +crt_init(struct CRT *v, int w, int h, int f, unsigned char *out) { memset(v, 0, sizeof(struct CRT)); - crt_resize(v, w, h, out); + crt_resize(v, w, h, f, out); crt_reset(v); v->rn = 194; @@ -250,9 +275,18 @@ crt_init(struct CRT *v, int w, int h, int *out) /* band gains are pre-scaled as 16-bit fixed point * if you change the EQ_P define, you'll need to update these gains too */ - init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); +#if (CRT_CC_SAMPLES == 4) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#elif (CRT_CC_SAMPLES == 5) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 12192, 7775); init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#else +#error "NTSC-CRT currently only supports 4 or 5 samples per chroma period." +#endif + } /* search windows, in samples */ @@ -262,7 +296,8 @@ crt_init(struct CRT *v, int w, int h, int *out) extern void crt_demodulate(struct CRT *v, int noise) { - struct { + /* made static so all this data does not go on the stack */ + static struct { int y, i, q; } out[AV_LEN + 1], *yiqA, *yiqB; int i, j, line, rn; @@ -273,11 +308,18 @@ crt_demodulate(struct CRT *v, int noise) int huesn, huecs; int xnudge = -3, ynudge = 3; int bright = v->brightness - (BLACK_LEVEL + v->black_point); + int bpp, pitch; #if CRT_DO_BLOOM int prev_e; /* filtered beam energy per scan line */ int max_e; /* approx maximum energy in a scan line */ #endif + bpp = crt_bpp4fmt(v->out_format); + if (bpp == 0) { + return; + } + pitch = v->outw * bpp; + crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); huesn >>= 11; /* make 4-bit */ huecs >>= 11; @@ -339,8 +381,13 @@ crt_demodulate(struct CRT *v, int noise) unsigned pos, ln; int scanL, scanR, dx; int L, R; - int *cL, *cR; - int wave[4]; + unsigned char *cL, *cR; +#if (CRT_CC_SAMPLES == 4) + int wave[CRT_CC_SAMPLES]; +#else + int waveI[CRT_CC_SAMPLES]; + int waveQ[CRT_CC_SAMPLES]; +#endif int dci, dcq; /* decoded I, Q */ int xpos, ypos; int beg, end; @@ -348,9 +395,9 @@ crt_demodulate(struct CRT *v, int noise) #if CRT_DO_BLOOM int line_w; #endif - - beg = (line - CRT_TOP + 0) * v->outh / CRT_LINES + field; - end = (line - CRT_TOP + 1) * v->outh / CRT_LINES + field; + + beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field; + end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field; if (beg >= v->outh) { continue; } if (end > v->outh) { end = v->outh; } @@ -377,27 +424,61 @@ crt_demodulate(struct CRT *v, int noise) ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); pos = xpos + ypos * CRT_HRES; - ccr = v->ccf[ypos & 3]; - sig = v->inp + ln + (v->hsync & ~3); /* burst @ 1/CB_FREQ sample rate */ + ccr = v->ccf[ypos % CRT_CC_VPER]; +#if (CRT_CC_SAMPLES == 4) + sig = v->inp + ln + (v->hsync & ~3); /* faster */ +#else + sig = v->inp + ln + (v->hsync - (v->hsync % CRT_CC_SAMPLES)); +#endif for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { int p, n; - p = ccr[i & 3] * 127 / 128; /* fraction of the previous */ + p = ccr[i % CRT_CC_SAMPLES] * 127 / 128; /* fraction of the previous */ n = sig[i]; /* mixed with the new sample */ - ccr[i & 3] = p + n; + ccr[i % CRT_CC_SAMPLES] = p + n; } - - phasealign = POSMOD(v->hsync, 4); + phasealign = POSMOD(v->hsync, CRT_CC_SAMPLES); + +#if (CRT_CC_SAMPLES == 4) /* amplitude of carrier = saturation, phase difference = hue */ dci = ccr[(phasealign + 1) & 3] - ccr[(phasealign + 3) & 3]; dcq = ccr[(phasealign + 2) & 3] - ccr[(phasealign + 0) & 3]; - /* rotate them by the hue adjustment angle */ wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; wave[2] = -wave[0]; wave[3] = -wave[1]; - +#elif (CRT_CC_SAMPLES == 5) + { + int dciA, dciB; + int dcqA, dcqB; + int ang = (v->hue % 360); + int off180 = CRT_CC_SAMPLES / 2; + int off90 = CRT_CC_SAMPLES / 4; + int peakA = phasealign + off90; + int peakB = phasealign + 0; + dciA = dciB = dcqA = dcqB = 0; + /* amplitude of carrier = saturation, phase difference = hue */ + dciA = ccr[(peakA) % CRT_CC_SAMPLES]; + /* average */ + dciB = (ccr[(peakA + off180) % CRT_CC_SAMPLES] + + ccr[(peakA + off180 + 1) % CRT_CC_SAMPLES]) / 2; + dcqA = ccr[(peakB + off180) % CRT_CC_SAMPLES]; + dcqB = ccr[(peakB) % CRT_CC_SAMPLES]; + dci = dciA - dciB; + dcq = dcqA - dcqB; + /* create wave tables and rotate them by the hue adjustment angle */ + for (i = 0; i < CRT_CC_SAMPLES; i++) { + int sn, cs; + crt_sincos14(&sn, &cs, ang * 8192 / 180); + waveI[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + /* Q is offset by 90 */ + crt_sincos14(&sn, &cs, (ang + 90) * 8192 / 180); + waveQ[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + ang += (360 / CRT_CC_SAMPLES); + } + } +#endif sig = v->inp + pos; #if CRT_DO_BLOOM s = 0; @@ -425,14 +506,22 @@ crt_demodulate(struct CRT *v, int noise) reset_eq(&eqI); reset_eq(&eqQ); +#if (CRT_CC_SAMPLES == 4) for (i = L; i < R; i++) { out[i].y = eqf(&eqY, sig[i] + bright) << 4; out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; } +#else + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * waveI[i % CRT_CC_SAMPLES] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * waveQ[i % CRT_CC_SAMPLES] >> 9) >> 3; + } +#endif - cL = v->out + beg * v->outw; - cR = cL + v->outw; + cL = v->out + (beg * pitch); + cR = cL + pitch; for (pos = scanL; pos < scanR && cL < cR; pos += dx) { int y, i, q; @@ -462,21 +551,69 @@ crt_demodulate(struct CRT *v, int noise) if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; - + if (v->blend) { aa = (r << 16 | g << 8 | b); - bb = *cL; + + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + bb = cL[0] << 16 | cL[1] << 8 | cL[2]; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + bb = cL[2] << 16 | cL[1] << 8 | cL[0]; + break; + case CRT_PIX_FORMAT_ARGB: + bb = cL[1] << 16 | cL[2] << 8 | cL[3]; + break; + case CRT_PIX_FORMAT_ABGR: + bb = cL[3] << 16 | cL[2] << 8 | cL[1]; + break; + default: + bb = 0; + break; + } + /* blend with previous color there */ - *cL++ = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); } else { - *cL++ = (r << 16 | g << 8 | b); + bb = (r << 16 | g << 8 | b); + } + + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + cL[0] = bb >> 0 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 16 & 0xff; + break; + case CRT_PIX_FORMAT_ARGB: + cL[1] = bb >> 16 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 0 & 0xff; + break; + case CRT_PIX_FORMAT_ABGR: + cL[1] = bb >> 0 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 16 & 0xff; + break; + default: + break; } + + cL += bpp; } /* duplicate extra lines */ - ln = v->outw * sizeof(int); for (s = beg + 1; s < (end - v->scanlines); s++) { - memcpy(v->out + s * v->outw, v->out + (s - 1) * v->outw, ln); + memcpy(v->out + s * pitch, v->out + (s - 1) * pitch, pitch); } } } diff --git a/Utilities/crt_core.h b/Utilities/crt_core.h index 15a383f42..e47833e08 100644 --- a/Utilities/crt_core.h +++ b/Utilities/crt_core.h @@ -25,7 +25,7 @@ extern "C" { /* library version */ #define CRT_MAJOR 2 -#define CRT_MINOR 0 +#define CRT_MINOR 2 #define CRT_PATCH 0 @@ -39,10 +39,20 @@ extern "C" { #include "crt_nes.h" #elif (CRT_SYSTEM == CRT_SYSTEM_NTSC) #include "crt_ntsc.h" +#elif (CRT_SYSTEM == CRT_SYSTEM_PV1K) +#include "crt_pv1k.h" #else #error No system defined #endif +/* NOTE: this library does not use the alpha channel at all */ +#define CRT_PIX_FORMAT_RGB 0 /* 3 bytes per pixel [R,G,B,R,G,B,R,G,B...] */ +#define CRT_PIX_FORMAT_BGR 1 /* 3 bytes per pixel [B,G,R,B,G,R,B,G,R...] */ +#define CRT_PIX_FORMAT_ARGB 2 /* 4 bytes per pixel [A,R,G,B,A,R,G,B...] */ +#define CRT_PIX_FORMAT_RGBA 3 /* 4 bytes per pixel [R,G,B,A,R,G,B,A...] */ +#define CRT_PIX_FORMAT_ABGR 4 /* 4 bytes per pixel [A,B,G,R,A,B,G,R...] */ +#define CRT_PIX_FORMAT_BGRA 5 /* 4 bytes per pixel [B,G,R,A,B,G,R,A...] */ + /* do bloom emulation (side effect: makes screen have black borders) */ #define CRT_DO_BLOOM 0 /* does not work for NES */ #define CRT_DO_VSYNC 1 /* look for VSYNC */ @@ -53,15 +63,17 @@ struct CRT { signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ int outw, outh; /* output width/height */ - int *out; /* output image */ + int out_format; /* output pixel format (one of the CRT_PIX_FORMATs) */ + unsigned char *out; /* output image */ int hue, brightness, contrast, saturation; /* common monitor settings */ int black_point, white_point; /* user-adjustable */ int scanlines; /* leave gaps between lines if necessary */ int blend; /* blend new field onto previous image */ - + unsigned v_fac; /* factor to stretch img vertically onto the output img */ + /* internal data */ - int ccf[4][4]; /* faster color carrier convergence */ + int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */ int hsync, vsync; /* keep track of sync over frames */ int rn; /* seed for the 'random' noise */ }; @@ -69,16 +81,18 @@ struct CRT { /* Initializes the library. Sets up filters. * w - width of the output image * h - height of the output image - * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + * f - format of the output image + * out - pointer to output image data */ -extern void crt_init(struct CRT *v, int w, int h, int *out); +extern void crt_init(struct CRT *v, int w, int h, int f, unsigned char *out); /* Updates the output image parameters * w - width of the output image * h - height of the output image - * out - pointer to output image data 32-bit RGB packed as 0xXXRRGGBB + * f - format of the output image + * out - pointer to output image data */ -extern void crt_resize(struct CRT *v, int w, int h, int *out); +extern void crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out); /* Resets the CRT settings back to their defaults */ extern void crt_reset(struct CRT *v); @@ -93,6 +107,14 @@ extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s); */ extern void crt_demodulate(struct CRT *v, int noise); +/* Get the bytes per pixel for a certain CRT_PIX_FORMAT_ + * + * format - the format to get the bytes per pixel for + * + * returns 0 if the specified format does not exist + */ +extern int crt_bpp4fmt(int format); + /*****************************************************************************/ /*************************** FIXED POINT SIN/COS *****************************/ /*****************************************************************************/ @@ -107,4 +129,4 @@ extern void crt_sincos14(int *s, int *c, int n); } #endif -#endif /* _CRT_SINCOS_H_ */ +#endif diff --git a/Utilities/crt_nes.cpp b/Utilities/crt_nes.cpp index 24dfecfc4..372bf6bfb 100644 --- a/Utilities/crt_nes.cpp +++ b/Utilities/crt_nes.cpp @@ -74,10 +74,11 @@ const int8_t IRE_levels[2][2][0x40]{ }; /* generate the square wave for a given 9-bit pixel and phase */ +/* note from persune: highly optimized for realtime use in Mesen*/ static int square_sample(int p, int phase) { - static const int active[6] = { + static int active[6] = { 0300, 0100, 0500, 0400, 0600, 0200 @@ -96,6 +97,12 @@ square_sample(int p, int phase) } #define NES_OPTIMIZED 1 +/* toggle drawing of NES border + * (normally not in visible region, but it depends on your emulator) + * highly recommended to keep disabled for better performance if the border + * is not visible in your emulator. + */ +#define NES_BORDER 1 /* the optimized version is NOT the most optimized version, it just performs * some simple refactoring to prevent a few redundant computations @@ -107,8 +114,8 @@ square_sample(int p, int phase) * basically factoring out the field setup since as long as CRT->analog * does not get cleared, all of this should remain the same every update */ -extern void -setup_field(struct CRT* v) +static void +setup_field(struct CRT *v) { int n; @@ -135,46 +142,43 @@ setup_field(struct CRT* v) extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) { - static int init = 0; int x, y, xo, yo; int destw = AV_LEN; int desth = CRT_LINES; int n, phase; - int po, lo; - int iccf[4]; - int ccburst[4]; /* color phase for burst */ - int burst_level[4]; + int iccf[CRT_CC_VPER][CRT_CC_SAMPLES]; + int ccburst[CRT_CC_VPER][CRT_CC_SAMPLES]; /* color phase for burst */ int sn, cs; - if (!init) { + static int phasetab[CRT_CC_VPER] = { 0, 4, 8 }; + + if (!s->field_initialized) { setup_field(v); - init = 1; + s->field_initialized = 1; } - for (x = 0; x < 4; x++) { - n = s->hue + x * 90; - crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); - ccburst[x] = sn >> 10; + + for (y = 0; y < CRT_CC_VPER; y++) { + xo = (y + s->dot_crawl_offset) * (360 / CRT_CC_VPER); + for (x = 0; x < CRT_CC_SAMPLES; x++) { + n = (s->hue + x * (360 / CRT_CC_SAMPLES) + xo + 33) % 360; + crt_sincos14(&sn, &cs, n * 8192 / 180); + ccburst[y][x] = sn >> 10; + } } - xo = AV_BEG; - yo = CRT_TOP; + + xo = AV_BEG + s->xoffset; + yo = CRT_TOP + s->yoffset; /* align signal */ xo = (xo & ~3); - /* this mess of offsetting logic was reached through trial and error */ - lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ - po = lo + 1; - if (lo == 1) { - lo = 3; - } - phase = po * 3; - +#if NES_BORDER for (n = CRT_TOP; n <= (CRT_BOT + 2); n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; t = LINE_BEG; - phase += LAV_BEG * 3; + phase = phasetab[(n + s->dot_crawl_offset) % CRT_CC_VPER] + 6; t = LAV_BEG; while (t < CRT_HRES) { int ire, p; @@ -185,22 +189,15 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); + /* note from persune: highly optimized for realtime use in Mesen*/ line[t++] = ire >> 2; phase += 3; } - - phase %= 12; - } - - phase = 6; - /* precalculate to save some CPU */ - for (x = 0; x < 4; x++) { - burst_level[x] = (BLANK_LEVEL + (ccburst[x] * BURST_LEVEL)) >> 5; - } - for (y = (lo - 3); y < desth; y++) { +#endif + for (y = 0; y < desth; y++) { signed char *line; - int t; + int t, cb; int sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; @@ -211,33 +208,30 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) /* CB_CYCLES of color burst at 3.579545 Mhz */ for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { - line[t] = burst_level[(t + po + n) & 3]; - iccf[(t + n) & 3] = line[t]; + cb = ccburst[n % CRT_CC_VPER][t % CRT_CC_SAMPLES]; + line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + iccf[n % CRT_CC_VPER][t % CRT_CC_SAMPLES] = line[t]; } sy *= s->w; - phase += (xo * 3); + phase = phasetab[(y + yo + s->dot_crawl_offset) % CRT_CC_VPER]; for (x = 0; x < destw; x++) { - if (y >= 0) { - int ire, p; - - p = s->data[((x * s->w) / destw) + sy]; - ire = BLACK_LEVEL + v->black_point; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; - } + int ire, p; + + p = s->data[((x * s->w) / destw) + sy]; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + /* note from persune: highly optimized for realtime use in Mesen*/ + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; phase += 3; } - /* mod here so we don't overflow down the line */ - phase = (phase + ((CRT_HRES - destw) * 3)) % 12; } - for (x = 0; x < 4; x++) { - for (n = 0; n < 4; n++) { - /* don't know why, but it works */ - v->ccf[n][x] = iccf[(x + n + 1) & 3] << 7; + for (n = 0; n < CRT_CC_VPER; n++) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + v->ccf[n][x] = iccf[n][x] << 7; } } } @@ -250,30 +244,26 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) int destw = AV_LEN; int desth = CRT_LINES; int n, phase; - int po, lo; - int iccf[4]; - int ccburst[4]; /* color phase for burst */ + int iccf[CRT_CC_VPER][CRT_CC_SAMPLES]; + int ccburst[CRT_CC_VPER][CRT_CC_SAMPLES]; /* color phase for burst */ int sn, cs; - - for (x = 0; x < 4; x++) { - n = s->hue + x * 90; - crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); - ccburst[x] = sn >> 10; + static int phasetab[CRT_CC_VPER] = { 0, 4, 8 }; + + for (y = 0; y < CRT_CC_VPER; y++) { + xo = (y + s->dot_crawl_offset) * (360 / CRT_CC_VPER); + for (x = 0; x < CRT_CC_SAMPLES; x++) { + n = (s->hue + x * (360 / CRT_CC_SAMPLES) + xo + 33) % 360; + crt_sincos14(&sn, &cs, n * 8192 / 180); + ccburst[y][x] = sn >> 10; + } } - xo = AV_BEG; - yo = CRT_TOP; - + + xo = AV_BEG + s->xoffset; + yo = CRT_TOP + s->yoffset; + /* align signal */ xo = (xo & ~3); - /* this mess of offsetting logic was reached through trial and error */ - lo = (s->dot_crawl_offset % 3); /* line offset to match color burst */ - po = lo + 1; - if (lo == 1) { - lo = 3; - } - phase = 3 + po * 3; - for (n = 0; n < CRT_VRES; n++) { int t; /* time */ signed char *line = &v->analog[n * CRT_HRES]; @@ -293,13 +283,14 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) while (t < CB_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ /* CB_CYCLES of color burst at 3.579545 Mhz */ for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { - cb = ccburst[(t + po + n) & 3]; + cb = ccburst[n % CRT_CC_VPER][t % CRT_CC_SAMPLES]; line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; - iccf[(t + n) & 3] = line[t]; + iccf[n % CRT_CC_VPER][t % CRT_CC_SAMPLES] = line[t]; } while (t < LAV_BEG) line[t++] = BLANK_LEVEL; - phase += t * 3; +#if NES_BORDER if (n >= CRT_TOP && n <= (CRT_BOT + 2)) { + phase = phasetab[(n + s->dot_crawl_offset) % CRT_CC_VPER] + 6; while (t < CRT_HRES) { int ire, p; p = s->border_color; @@ -309,52 +300,47 @@ crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) ire += square_sample(p, phase + 1); ire += square_sample(p, phase + 2); ire += square_sample(p, phase + 3); - ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; - line[t++] = ire; + /* note from persune: highly optimized for realtime use in Mesen*/ + line[t++] = ire >> 2; phase += 3; } } else { +#endif while (t < CRT_HRES) line[t++] = BLANK_LEVEL; - phase += (CRT_HRES - LAV_BEG) * 3; +#if NES_BORDER } - phase %= 12; +#endif } } - phase = 6; - - for (y = (lo - 3); y < desth; y++) { + for (y = 0; y < desth; y++) { int sy = (y * s->h) / desth; if (sy >= s->h) sy = s->h; if (sy < 0) sy = 0; sy *= s->w; - phase += (xo * 3); + phase = phasetab[(y + yo + s->dot_crawl_offset) % CRT_CC_VPER]; for (x = 0; x < destw; x++) { - if (y >= 0) { - int ire, p; - - p = s->data[((x * s->w) / destw) + sy]; - ire = BLACK_LEVEL + v->black_point; - ire += square_sample(p, phase + 0); - ire += square_sample(p, phase + 1); - ire += square_sample(p, phase + 2); - ire += square_sample(p, phase + 3); - ire = (ire * (WHITE_LEVEL * v->white_point / 100)) >> 12; - v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; - } + int ire, p; + + p = s->data[((x * s->w) / destw) + sy]; + ire = BLACK_LEVEL + v->black_point; + ire += square_sample(p, phase + 0); + ire += square_sample(p, phase + 1); + ire += square_sample(p, phase + 2); + ire += square_sample(p, phase + 3); + /* note from persune: highly optimized for realtime use in Mesen*/ + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire >> 2; phase += 3; } - /* mod here so we don't overflow down the line */ - phase = (phase + ((CRT_HRES - destw) * 3)) % 12; } - for (x = 0; x < 4; x++) { - for (n = 0; n < 4; n++) { - /* don't know why, but it works */ - v->ccf[n][x] = iccf[(x + n + 1) & 3] << 7; + for (n = 0; n < CRT_CC_VPER; n++) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + v->ccf[n][x] = iccf[n][x] << 7; } } } #endif + #endif diff --git a/Utilities/crt_nes.h b/Utilities/crt_nes.h index 09a42c15d..b597abd9b 100644 --- a/Utilities/crt_nes.h +++ b/Utilities/crt_nes.h @@ -52,6 +52,20 @@ extern "C" { #define CRT_BOT 255 /* final line with active video */ #define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ +#define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */ +#define CRT_CC_VPER 3 /* vertical period in which the artifacts repeat */ + +/* search windows, in samples */ +#define CRT_HSYNC_WINDOW 6 +#define CRT_VSYNC_WINDOW 6 + +/* accumulated signal threshold required for sync detection. + * Larger = more stable, until it's so large that it is never reached in which + * case the CRT won't be able to sync + */ +#define CRT_HSYNC_THRESH 4 +#define CRT_VSYNC_THRESH 94 + /* NES composite signal is measured in terms of PPU pixels, or cycles * https://www.nesdev.org/wiki/NTSC_video#Scanline_Timing * @@ -110,7 +124,7 @@ extern "C" { /* IRE units (100 = 1.0V, -40 = 0.0V) */ /* https://www.nesdev.org/wiki/NTSC_video#Terminated_measurement */ -#define WHITE_LEVEL 100 +#define WHITE_LEVEL 110 #define BURST_LEVEL 30 #define BLACK_LEVEL 0 #define BLANK_LEVEL 0 @@ -123,13 +137,12 @@ struct NTSC_SETTINGS { int dot_crawl_offset; /* 0, 1, or 2 */ /* NOTE: NES mode is always progressive */ int hue; /* 0-359 */ + int xoffset; /* x offset in sample space. 0 is minimum value */ + int yoffset; /* y offset in # of lines. 0 is minimum value */ + /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ + int field_initialized; /* internal state */ }; -/* Setup analog NTSC blanking and sync signal - * s - struct containing settings to apply to this field - */ -extern void setup_field(struct CRT* v); - #ifdef __cplusplus } #endif From 41adb3663fed5015bef28c42b41920d232f265ea Mon Sep 17 00:00:00 2001 From: Persune Date: Mon, 3 Apr 2023 14:23:28 +0800 Subject: [PATCH 34/36] Update sync window implementation --- Utilities/crt_core.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Utilities/crt_core.cpp b/Utilities/crt_core.cpp index bfa5bc6e0..b83fb82b3 100644 --- a/Utilities/crt_core.cpp +++ b/Utilities/crt_core.cpp @@ -289,10 +289,6 @@ crt_init(struct CRT *v, int w, int h, int f, unsigned char *out) } -/* search windows, in samples */ -#define HSYNC_WINDOW 8 -#define VSYNC_WINDOW 8 - extern void crt_demodulate(struct CRT *v, int noise) { @@ -345,7 +341,7 @@ crt_demodulate(struct CRT *v, int noise) * The signal needs to be integrated to lessen * the noise in the signal. */ - for (i = -VSYNC_WINDOW; i < VSYNC_WINDOW; i++) { + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { line = POSMOD(v->vsync + i, CRT_VRES); sig = v->inp + line * CRT_HRES; s = 0; @@ -354,7 +350,7 @@ crt_demodulate(struct CRT *v, int noise) /* increase the multiplier to make the vsync * more stable when there is a lot of noise */ - if (s <= (94 * SYNC_LEVEL)) { + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { goto vsync_found; } } @@ -408,9 +404,9 @@ crt_demodulate(struct CRT *v, int noise) ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; sig = v->inp + ln + v->hsync; s = 0; - for (i = -HSYNC_WINDOW; i < HSYNC_WINDOW; i++) { + for (i = -CRT_HSYNC_WINDOW; i < CRT_HSYNC_WINDOW; i++) { s += sig[SYNC_BEG + i]; - if (s <= (4 * SYNC_LEVEL)) { + if (s <= (CRT_HSYNC_THRESH * SYNC_LEVEL)) { break; } } From d5a535b616b2da90e6683e0af28a89430496d422 Mon Sep 17 00:00:00 2001 From: Persune Date: Mon, 3 Apr 2023 18:06:12 +0800 Subject: [PATCH 35/36] Calibrate decoding matrix to match Bisqwit's... palette generator --- Core/BisqwitNtscFilter.cpp | 41 +++++++++++++++++++++++++++----------- Core/BisqwitNtscFilter.h | 2 ++ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 66eff1e69..c13ca863d 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -105,8 +105,14 @@ void BisqwitNtscFilter::OnBeforeApplyFilter() int saturation = (int)((pictureSettings.Saturation + 1.0) * (pictureSettings.Saturation + 1.0) * 144044); bool colorimetryCorrection = _console->GetSettings()->GetNtscFilterSettings().ColorimetryCorrection; - // [saturation at 0] * 100 / [I or Q width at 0] - double SatFactor = 144044 * 100 / 12; + _brightness = static_cast(pictureSettings.Brightness * 750); + + // for some reason the original coefficients + // of bisqwit's decoding matrix has been reduced by at least 10^-6 + // the aim here is to more closely match Bisqwit's palette generator + // at hue 0, saturation 1.0, contrast 1.0, brightness 1.0, gamma 1.8 + // https://bisqwit.iki.fi/utils/nespalette.php + double SatFactor = 1000000; for(int i = 0; i < 27; i++) { _sinetable[i] = (int8_t)(8 * std::sin(i * 2 * pi / 12 + pictureSettings.Hue * pi)); @@ -118,14 +124,26 @@ void BisqwitNtscFilter::OnBeforeApplyFilter() _y = contrast / _yWidth; - _ir = colorimetryCorrection ? (int)(contrast * 1.994681e-6 * saturation / _iWidth) : (int)(contrast * (0.95599 / SatFactor) * saturation / _iWidth); - _qr = colorimetryCorrection ? (int)(contrast * 9.915742e-7 * saturation / _qWidth) : (int)(contrast * (0.62082 / SatFactor) * saturation / _qWidth); - - _ig = colorimetryCorrection ? (int)(contrast * 9.151351e-8 * saturation / _iWidth) : (int)(contrast * (-0.27201 / SatFactor) * saturation / _iWidth); - _qg = colorimetryCorrection ? (int)(contrast * -6.334805e-7 * saturation / _qWidth) : (int)(contrast * (-0.64720 / SatFactor) * saturation / _qWidth); - - _ib = colorimetryCorrection ? (int)(contrast * -1.012984e-6 * saturation / _iWidth) : (int)(contrast * (-1.10674 / SatFactor) * saturation / _iWidth); - _qb = colorimetryCorrection ? (int)(contrast * 1.667217e-6 * saturation / _qWidth) : (int)(contrast * (1.70423 / SatFactor) * saturation / _qWidth); + _ir = colorimetryCorrection ? + (int)(contrast * 1.994681e-6 * saturation / _iWidth) : + (int)(contrast * (0.95599 / SatFactor) * saturation / _iWidth); + _qr = colorimetryCorrection ? + (int)(contrast * 9.915742e-7 * saturation / _qWidth) : + (int)(contrast * (0.62082 / SatFactor) * saturation / _qWidth); + + _ig = colorimetryCorrection ? + (int)(contrast * 9.151351e-8 * saturation / _iWidth) : + (int)(contrast * (-0.27201 / SatFactor) * saturation / _iWidth); + _qg = colorimetryCorrection ? + (int)(contrast * -6.334805e-7 * saturation / _qWidth) : + (int)(contrast * (-0.64720 / SatFactor) * saturation / _qWidth); + + _ib = colorimetryCorrection ? + (int)(contrast * -1.012984e-6 * saturation / _iWidth) : + (int)(contrast * (-1.10674 / SatFactor) * saturation / _iWidth); + _qb = colorimetryCorrection ? + (int)(contrast * 1.667217e-6 * saturation / _qWidth) : + (int)(contrast * (1.70423 / SatFactor) * saturation / _qWidth); } void BisqwitNtscFilter::RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend) @@ -302,8 +320,7 @@ void BisqwitNtscFilter::NtscDecodeLine(int width, const int8_t* signal, uint32_t auto Cos = [=](int pos) -> char { return _sinetable[(pos + 36) % 12 + phase0]; }; auto Sin = [=](int pos) -> char { return _sinetable[(pos + 36) % 12 + 3 + phase0]; }; - int brightness = (int)(_console->GetSettings()->GetPictureSettings().Brightness * 750); - int ysum = brightness, isum = 0, qsum = 0; + int ysum = _brightness, isum = 0, qsum = 0; int offset = _resDivider + 4; int leftOverscan = (GetOverscan().Left + _paddingSize) * 8 + offset; int rightOverscan = width - (GetOverscan().Right + _paddingSize) * 8 + offset; diff --git a/Core/BisqwitNtscFilter.h b/Core/BisqwitNtscFilter.h index f0fb89674..9a025679b 100644 --- a/Core/BisqwitNtscFilter.h +++ b/Core/BisqwitNtscFilter.h @@ -23,6 +23,8 @@ class BisqwitNtscFilter : public BaseVideoFilter int _resDivider = 1; uint16_t *_ppuOutputBuffer = nullptr; + + int _brightness = 0; /* Ywidth, Iwidth and Qwidth are the filter widths for Y,I,Q respectively. * All widths at 12 produce the best signal quality. From cd834fb2f4de0069b1fc4b5881886d8b8858cfb5 Mon Sep 17 00:00:00 2001 From: Persune Date: Sat, 8 Apr 2023 17:54:43 +0800 Subject: [PATCH 36/36] Optimize emphasis waveform generation --- Core/BisqwitNtscFilter.cpp | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index c13ca863d..2796ee12a 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -194,29 +194,25 @@ void BisqwitNtscFilter::GenerateNtscSignal(int8_t *ntscSignal, int &phase, int r { for(int x = -_paddingSize; x < 256 + _paddingSize; x++) { // pixel_color = Pixel color (9-bit) given as input. Bitmask format: "eeellcccc". - uint16_t pixel_color = _ppuOutputBuffer[(rowNumber << 8) | (x < 0 ? 0 : (x >= 256 ? 255 : x))]; + uint16_t pixel_color = _ppuOutputBuffer[(rowNumber << 8) | x]; int8_t emphasis = pixel_color >> 6; int8_t color = pixel_color & 0x3F; - - auto phase_shift_up = [=](uint16_t value, uint16_t amt) { - amt = amt % 12; - uint16_t uint12_value = value & 0xFFF; - uint32_t result = (((uint12_value << 12) | uint12_value) & 0xFFFFFFFF); - return uint16_t((result >> (amt % 12)) & 0xFFFF); - }; + int8_t hue = color & 0x0F; uint16_t emphasis_wave = 0; - if (emphasis & 0b001) // tint R; color phase C - emphasis_wave |= 0b000000111111; - if (emphasis & 0b010) // tint G; color phase 4 - emphasis_wave |= 0b001111110000; - if (emphasis & 0b100) // tint B; color phase 8 - emphasis_wave |= 0b111100000011; - if (emphasis) - emphasis_wave = phase_shift_up(emphasis_wave, (color & 0x0F)); - - uint16_t phaseBitmask = _bitmaskLut[std::abs(phase - (color & 0x0F)) % 12]; + if(emphasis) { + if(emphasis & 0b001) // tint R; aligned to color phase C + emphasis_wave |= 0b000000111111; + if(emphasis & 0b010) // tint G; aligned to color phase 4 + emphasis_wave |= 0b001111110000; + if(emphasis & 0b100) // tint B; aligned to color phase 8 + emphasis_wave |= 0b111100000011; + // phase shift 12-bit waveform relative to pixel hue + emphasis_wave = ((emphasis_wave >> (hue % 12)) | (emphasis_wave << (12 - (hue % 12)))) & 0xFFFF; + } + + uint16_t phaseBitmask = _bitmaskLut[std::abs(phase - hue) % 12]; bool attenuate = 0; int8_t voltage;