Skip to content

Commit 36699ed

Browse files
authored
Added WIP SNES version
1 parent f9b21da commit 36699ed

File tree

2 files changed

+491
-0
lines changed

2 files changed

+491
-0
lines changed

crt_snes.c

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/*****************************************************************************/
2+
/*
3+
* NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation
4+
*
5+
* by EMMIR 2018-2023
6+
*
7+
* YouTube: https://www.youtube.com/@EMMIR_KC/videos
8+
* Discord: https://discord.com/invite/hdYctSmyQJ
9+
*/
10+
/*****************************************************************************/
11+
12+
#include "crt_core.h"
13+
14+
#if (CRT_SYSTEM == CRT_SYSTEM_SNES)
15+
#include <stdlib.h>
16+
#include <string.h>
17+
18+
#define EXP_P 11
19+
#define EXP_ONE (1 << EXP_P)
20+
#define EXP_MASK (EXP_ONE - 1)
21+
#define EXP_PI 6434
22+
#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P)
23+
#define EXP_DIV(x, y) (((x) << EXP_P) / (y))
24+
25+
static int e11[] = {
26+
EXP_ONE,
27+
5567, /* e */
28+
15133, /* e^2 */
29+
41135, /* e^3 */
30+
111817 /* e^4 */
31+
};
32+
33+
/* fixed point e^x */
34+
static int
35+
expx(int n)
36+
{
37+
int neg, idx, res;
38+
int nxt, acc, del;
39+
int i;
40+
41+
if (n == 0) {
42+
return EXP_ONE;
43+
}
44+
neg = n < 0;
45+
if (neg) {
46+
n = -n;
47+
}
48+
idx = n >> EXP_P;
49+
res = EXP_ONE;
50+
for (i = 0; i < idx / 4; i++) {
51+
res = EXP_MUL(res, e11[4]);
52+
}
53+
idx &= 3;
54+
if (idx > 0) {
55+
res = EXP_MUL(res, e11[idx]);
56+
}
57+
58+
n &= EXP_MASK;
59+
nxt = EXP_ONE;
60+
acc = 0;
61+
del = 1;
62+
for (i = 1; i < 17; i++) {
63+
acc += nxt / del;
64+
nxt = EXP_MUL(nxt, n);
65+
del *= i;
66+
if (del > nxt || nxt <= 0 || del <= 0) {
67+
break;
68+
}
69+
}
70+
res = EXP_MUL(res, acc);
71+
72+
if (neg) {
73+
res = EXP_DIV(EXP_ONE, res);
74+
}
75+
return res;
76+
}
77+
78+
/*****************************************************************************/
79+
/********************************* FILTERS ***********************************/
80+
/*****************************************************************************/
81+
82+
/* infinite impulse response low pass filter for bandlimiting YIQ */
83+
static struct IIRLP {
84+
int c;
85+
int h; /* history */
86+
} iirY, iirI, iirQ;
87+
88+
/* freq - total bandwidth
89+
* limit - max frequency
90+
*/
91+
static void
92+
init_iir(struct IIRLP *f, int freq, int limit)
93+
{
94+
int rate; /* cycles/pixel rate */
95+
96+
memset(f, 0, sizeof(struct IIRLP));
97+
rate = (freq << 9) / limit;
98+
f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate));
99+
}
100+
101+
static void
102+
reset_iir(struct IIRLP *f)
103+
{
104+
f->h = 0;
105+
}
106+
107+
/* hi-pass for debugging */
108+
#define HIPASS 0
109+
110+
static int
111+
iirf(struct IIRLP *f, int s)
112+
{
113+
#if CRT_DO_BANDLIMITING
114+
f->h += EXP_MUL(s - f->h, f->c);
115+
#if HIPASS
116+
return s - f->h;
117+
#else
118+
return f->h;
119+
#endif
120+
#else
121+
return s;
122+
#endif
123+
}
124+
125+
extern void
126+
crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s)
127+
{
128+
int x, y, xo, yo;
129+
int destw = AV_LEN;
130+
int desth = ((CRT_LINES * 64500) >> 16);
131+
int iccf[CRT_CC_VPER][CRT_CC_SAMPLES];
132+
int ccmodI[CRT_CC_VPER][CRT_CC_SAMPLES]; /* color phase for mod */
133+
int ccmodQ[CRT_CC_VPER][CRT_CC_SAMPLES]; /* color phase for mod */
134+
int ccburst[CRT_CC_VPER][CRT_CC_SAMPLES]; /* color phase for burst */
135+
int sn, cs, n, ph;
136+
int bpp;
137+
138+
if (!s->iirs_initialized) {
139+
init_iir(&iirY, L_FREQ, Y_FREQ);
140+
init_iir(&iirI, L_FREQ, I_FREQ);
141+
init_iir(&iirQ, L_FREQ, Q_FREQ);
142+
s->iirs_initialized = 1;
143+
}
144+
#if CRT_DO_BLOOM
145+
if (s->raw) {
146+
destw = s->w;
147+
desth = s->h;
148+
if (destw > ((AV_LEN * 55500) >> 16)) {
149+
destw = ((AV_LEN * 55500) >> 16);
150+
}
151+
if (desth > ((CRT_LINES * 63500) >> 16)) {
152+
desth = ((CRT_LINES * 63500) >> 16);
153+
}
154+
} else {
155+
destw = (AV_LEN * 55500) >> 16;
156+
desth = (CRT_LINES * 63500) >> 16;
157+
}
158+
#else
159+
if (s->raw) {
160+
destw = s->w;
161+
desth = s->h;
162+
if (destw > AV_LEN) {
163+
destw = AV_LEN;
164+
}
165+
if (desth > ((CRT_LINES * 64500) >> 16)) {
166+
desth = ((CRT_LINES * 64500) >> 16);
167+
}
168+
}
169+
#endif
170+
if (s->as_color) {
171+
for (y = 0; y < CRT_CC_VPER; y++) {
172+
int vert = (y + s->dot_crawl_offset) * (360 / CRT_CC_VPER);
173+
for (x = 0; x < CRT_CC_SAMPLES; x++) {
174+
int step = (360 / CRT_CC_SAMPLES);
175+
n = vert + s->hue + x * step;
176+
crt_sincos14(&sn, &cs, (n - step + HUE_OFFSET) * 8192 / 180);
177+
ccburst[y][x] = sn >> 10;
178+
crt_sincos14(&sn, &cs, n * 8192 / 180);
179+
ccmodI[y][x] = sn >> 10;
180+
crt_sincos14(&sn, &cs, (n + Q_OFFSET) * 8192 / 180);
181+
ccmodQ[y][x] = sn >> 10;
182+
}
183+
}
184+
} else {
185+
memset(ccburst, 0, sizeof(ccburst));
186+
memset(ccmodI, 0, sizeof(ccmodI));
187+
memset(ccmodQ, 0, sizeof(ccmodQ));
188+
}
189+
190+
bpp = crt_bpp4fmt(s->format);
191+
if (bpp == 0) {
192+
return; /* just to be safe */
193+
}
194+
xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2;
195+
yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2;
196+
197+
s->field &= 1;
198+
s->frame &= 1;
199+
200+
/* align signal */
201+
xo = xo - (xo % CRT_CC_SAMPLES);
202+
203+
for (n = 0; n < CRT_VRES; n++) {
204+
int t; /* time */
205+
signed char *line = &v->analog[n * CRT_HRES];
206+
207+
t = LINE_BEG;
208+
209+
if ((n >= EQU_REGION_A_LO && n <= EQU_REGION_A_HI) ||
210+
(n >= EQU_REGION_B_LO && n <= EQU_REGION_B_HI)) {
211+
/* equalizing pulses - small blips of sync, mostly blank */
212+
while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
213+
while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
214+
while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
215+
while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
216+
} else if (n >= SYNC_REGION_LO && n <= SYNC_REGION_HI) {
217+
int even[4] = { 46, 50, 96, 100 };
218+
int *offs = even;
219+
/* vertical sync pulse - small blips of blank, mostly sync */
220+
while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
221+
while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
222+
while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL;
223+
while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL;
224+
} else {
225+
int cb;
226+
227+
/* video line */
228+
while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */
229+
while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */
230+
while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */
231+
if (n < CRT_TOP) {
232+
while (t < CRT_HRES) line[t++] = BLANK_LEVEL;
233+
}
234+
235+
/* CB_CYCLES of color burst */
236+
for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) {
237+
cb = ccburst[n % CRT_CC_VPER][t % CRT_CC_SAMPLES];
238+
line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5;
239+
iccf[(n + 3) % CRT_CC_VPER][t % CRT_CC_SAMPLES] = line[t];
240+
}
241+
}
242+
}
243+
244+
for (y = 0; y < desth; y++) {
245+
int sy;
246+
247+
sy = (y * s->h) / desth;
248+
249+
250+
if (sy >= s->h) sy = s->h;
251+
252+
sy *= s->w;
253+
254+
reset_iir(&iirY);
255+
reset_iir(&iirI);
256+
reset_iir(&iirQ);
257+
258+
ph = (y + yo) % CRT_CC_VPER;
259+
260+
for (x = 0; x < destw; x++) {
261+
int fy, fi, fq;
262+
int rA, gA, bA;
263+
const unsigned char *pix;
264+
int ire; /* composite signal */
265+
int xoff;
266+
/* RGB to YIQ matrix in 16.16 fixed point format */
267+
static int yiqmat[9] = {
268+
19595, 38470, 7471, /* Y */
269+
39059, -18022, -21103, /* I */
270+
13894, -34275, 20382, /* Q */
271+
};
272+
pix = s->data + ((((x * s->w) / destw) + sy) * bpp);
273+
switch (s->format) {
274+
case CRT_PIX_FORMAT_RGB:
275+
case CRT_PIX_FORMAT_RGBA:
276+
rA = pix[0];
277+
gA = pix[1];
278+
bA = pix[2];
279+
break;
280+
case CRT_PIX_FORMAT_BGR:
281+
case CRT_PIX_FORMAT_BGRA:
282+
rA = pix[2];
283+
gA = pix[1];
284+
bA = pix[0];
285+
break;
286+
case CRT_PIX_FORMAT_ARGB:
287+
rA = pix[1];
288+
gA = pix[2];
289+
bA = pix[3];
290+
break;
291+
case CRT_PIX_FORMAT_ABGR:
292+
rA = pix[3];
293+
gA = pix[2];
294+
bA = pix[1];
295+
break;
296+
default:
297+
rA = gA = bA = 0;
298+
break;
299+
}
300+
301+
/* RGB to YIQ */
302+
fy = (yiqmat[0] * rA + yiqmat[1] * gA + yiqmat[2] * bA) >> 14;
303+
fi = (yiqmat[3] * rA + yiqmat[4] * gA + yiqmat[5] * bA) >> 14;
304+
fq = (yiqmat[6] * rA + yiqmat[7] * gA + yiqmat[8] * bA) >> 14;
305+
ire = BLACK_LEVEL + v->black_point;
306+
307+
xoff = (x + xo) % CRT_CC_SAMPLES;
308+
/* bandlimit Y,I,Q */
309+
fy = iirf(&iirY, fy);
310+
fi = iirf(&iirI, fi) * ccmodI[ph][xoff] >> 4;
311+
fq = iirf(&iirQ, fq) * ccmodQ[ph][xoff] >> 4;
312+
/* modulate as (Y + sin(x) * I + cos(x) * Q) */
313+
ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10;
314+
if (ire < IRE_MIN) ire = IRE_MIN;
315+
if (ire > IRE_MAX) ire = IRE_MAX;
316+
317+
v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire;
318+
}
319+
}
320+
321+
for (n = 0; n < CRT_CC_VPER; n++) {
322+
for (x = 0; x < CRT_CC_SAMPLES; x++) {
323+
v->ccf[n][x] = iccf[n][x] << 7;
324+
}
325+
}
326+
}
327+
#endif

0 commit comments

Comments
 (0)