1 /**************************************************************
2 
3    modeline.cpp - Modeline generation and scoring routines
4 
5    ---------------------------------------------------------
6 
7    Switchres   Modeline generation engine for emulation
8 
9    License     GPL-2.0+
10    Copyright   2010-2021 Chris Kennedy, Antonio Giner,
11                          Alexandre Wodarczyk, Gil Delescluse
12 
13  **************************************************************/
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <cstddef>
18 #include "modeline.h"
19 #include "log.h"
20 
21 #define max(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a > _b ? _a : _b; })
22 #define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
23 
24 
25 //============================================================
26 //  PROTOTYPES
27 //============================================================
28 
29 int get_line_params(modeline *mode, monitor_range *range, int char_size);
30 int scale_into_range (int value, int lower_limit, int higher_limit);
31 int scale_into_range (double value, double lower_limit, double higher_limit);
32 int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff);
33 int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace);
34 int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace);
35 double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace);
36 
37 //============================================================
38 //  modeline_create
39 //============================================================
40 
modeline_create(modeline * s_mode,modeline * t_mode,monitor_range * range,generator_settings * cs)41 int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, generator_settings *cs)
42 {
43 	double vfreq_real = 0;
44 	double interlace = 1;
45 	double doublescan = 1;
46 	double scan_factor = 1;
47 	int x_scale = 0;
48 	int y_scale = 0;
49 	int v_scale = 0;
50 	double x_diff = 0;
51 	double y_diff = 0;
52 	double v_diff = 0;
53 	double y_ratio = 0;
54 	double x_ratio = 0;
55 	double borders = 0;
56 	t_mode->result.weight = 0;
57 
58 	// ≈≈≈ Vertical refresh ≈≈≈
59 	// try to fit vertical frequency into current range
60 	v_scale = scale_into_range(t_mode->vfreq, range->vfreq_min, range->vfreq_max);
61 
62 	if (!v_scale && (t_mode->type & V_FREQ_EDITABLE))
63 	{
64 		t_mode->vfreq = t_mode->vfreq < range->vfreq_min? range->vfreq_min : range->vfreq_max;
65 		v_scale = 1;
66 	}
67 	else if (v_scale != 1 && !(t_mode->type & V_FREQ_EDITABLE))
68 	{
69 		t_mode->result.weight |= R_OUT_OF_RANGE;
70 		return -1;
71 	}
72 
73 	// ≈≈≈ Vertical resolution ≈≈≈
74 	// try to fit active lines in the progressive range first
75 	if (range->progressive_lines_min && (!t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
76 		y_scale = scale_into_range(t_mode->vactive, range->progressive_lines_min, range->progressive_lines_max);
77 
78 	// if not possible, try to fit in the interlaced range, if any
79 	if (!y_scale && range->interlaced_lines_min && cs->interlace && (t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
80 	{
81 		y_scale = scale_into_range(t_mode->vactive, range->interlaced_lines_min, range->interlaced_lines_max);
82 		interlace = 2;
83 	}
84 
85 	// if we succeeded, let's see if we can apply integer scaling
86 	if (y_scale == 1 || (y_scale > 1 && (t_mode->type & Y_RES_EDITABLE)))
87 	{
88 		// check if we should apply doublescan
89 		if (cs->doublescan && y_scale % 2 == 0)
90 		{
91 			y_scale /= 2;
92 			doublescan = 0.5;
93 		}
94 		scan_factor = interlace * doublescan;
95 
96 		// Calculate top border in case of multi-standard consumer TVs
97 		if (cs->v_shift_correct)
98 			borders = (range->progressive_lines_max - t_mode->vactive * y_scale / interlace) * (1.0 / range->hfreq_min) / 2;
99 
100 		// calculate expected achievable refresh for this height
101 		vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive * y_scale, range, borders, scan_factor));
102 		if (vfreq_real != t_mode->vfreq * v_scale && !(t_mode->type & V_FREQ_EDITABLE))
103 		{
104 			t_mode->result.weight |= R_OUT_OF_RANGE;
105 			return -1;
106 		}
107 
108 		// calculate the ratio that our scaled yres represents with respect to the original height
109 		y_ratio = double(t_mode->vactive) * y_scale / s_mode->vactive;
110 		int y_source_scaled = s_mode->vactive * floor(y_ratio);
111 
112 		// if our original height doesn't fit the target height, we're forced to stretch
113 		if (!y_source_scaled)
114 			t_mode->result.weight |= R_RES_STRETCH;
115 
116 		// otherwise we try to perform integer scaling
117 		else
118 		{
119 			// exclude lcd ranges from raw border computation
120 			if (t_mode->type & V_FREQ_EDITABLE && range->progressive_lines_max - range->progressive_lines_min > 0)
121 			{
122 				// calculate y borders considering physical lines (instead of logical resolution)
123 				int tot_yres = total_lines_for_yres(t_mode->vactive * y_scale, vfreq_real, range, borders, scan_factor);
124 				int tot_source = total_lines_for_yres(y_source_scaled, t_mode->vfreq * v_scale, range, borders, scan_factor);
125 				y_diff = tot_yres > tot_source?double(tot_yres % tot_source) / tot_yres * 100:0;
126 
127 				// we penalize for the logical lines we need to add in order to meet the user's lower active lines limit
128 				int y_min = interlace == 2?range->interlaced_lines_min:range->progressive_lines_min;
129 				int tot_rest = (y_min >= y_source_scaled / doublescan)? y_min % int(y_source_scaled / doublescan):0;
130 				y_diff += double(tot_rest) / tot_yres * 100;
131 			}
132 			else
133 				y_diff = double((t_mode->vactive * y_scale) % y_source_scaled) / (t_mode->vactive * y_scale) * 100;
134 
135 			// we save the integer ratio between source and target resolutions, this will be used for prescaling
136 			y_scale = floor(y_ratio);
137 
138 			// now if the borders obtained are low enough (< 10%) we'll finally apply integer scaling
139 			// otherwise we'll stretch the original resolution over the target one
140 			if (!(y_ratio >= 1.0 && y_ratio < 16.0 && y_diff < 10.0))
141 				t_mode->result.weight |= R_RES_STRETCH;
142 		}
143 	}
144 
145 	// otherwise, check if we're allowed to apply fractional scaling
146 	else if (t_mode->type & Y_RES_EDITABLE)
147 		t_mode->result.weight |= R_RES_STRETCH;
148 
149 	// if there's nothing we can do, we're out of range
150 	else
151 	{
152 		t_mode->result.weight |= R_OUT_OF_RANGE;
153 		return -1;
154 	}
155 
156 	// ≈≈≈ Horizontal resolution ≈≈≈
157 	// make the best possible adjustment of xres depending on what happened in the previous steps
158 	// let's start with the SCALED case
159 	if (!(t_mode->result.weight & R_RES_STRETCH))
160 	{
161 		// apply integer scaling to yres
162 		if (t_mode->type & Y_RES_EDITABLE) t_mode->vactive *= y_scale;
163 
164 		// if we can, let's apply the same scaling to both directions
165 		if (t_mode->type & X_RES_EDITABLE)
166 		{
167 			x_scale = y_scale;
168 			double aspect_corrector = max(1.0f, cs->monitor_aspect / (cs->rotation? (1.0/(STANDARD_CRT_ASPECT)) : (STANDARD_CRT_ASPECT)));
169 			t_mode->hactive = normalize(double(t_mode->hactive) * double(x_scale) * aspect_corrector, 8);
170 		}
171 
172 		// otherwise, try to get the best out of our current xres
173 		else
174 		{
175 			x_scale = t_mode->hactive / s_mode->hactive;
176 			// if the source width fits our xres, try applying integer scaling
177 			if (x_scale)
178 			{
179 				x_scale = scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff);
180 				if (x_diff > 15.0 && t_mode->hactive < cs->super_width)
181 						t_mode->result.weight |= R_RES_STRETCH;
182 			}
183 			// otherwise apply fractional scaling
184 			else
185 				t_mode->result.weight |= R_RES_STRETCH;
186 		}
187 	}
188 
189 	// if the result was fractional scaling in any of the previous steps, deal with it
190 	if (t_mode->result.weight & R_RES_STRETCH)
191 	{
192 		if (t_mode->type & Y_RES_EDITABLE)
193 		{
194 			// always try to use the interlaced range first if it exists, for better resolution
195 			t_mode->vactive = stretch_into_range(t_mode->vfreq * v_scale, range, borders, cs->interlace, &interlace);
196 
197 			// check in case we couldn't achieve the desired refresh
198 			vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive, range, borders, interlace));
199 		}
200 
201 		// check if we can create a normal aspect resolution
202 		if (t_mode->type & X_RES_EDITABLE)
203 			t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, 8));
204 
205 		// calculate integer scale for prescaling
206 		x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff));
207 		y_scale = max(1, floor(double(t_mode->vactive) / s_mode->vactive));
208 
209 		scan_factor = interlace;
210 		doublescan = 1;
211 	}
212 
213 	x_ratio = double(t_mode->hactive) / s_mode->hactive;
214 	y_ratio = double(t_mode->vactive) / s_mode->vactive;
215 	v_scale = max(round_near(vfreq_real / s_mode->vfreq), 1);
216 	v_diff = (vfreq_real / v_scale) -  s_mode->vfreq;
217 	if (fabs(v_diff) > cs->refresh_tolerance)
218 		t_mode->result.weight |= R_V_FREQ_OFF;
219 
220 	// ≈≈≈ Modeline generation ≈≈≈
221 	// compute new modeline if we are allowed to
222 	if (t_mode->type & V_FREQ_EDITABLE)
223 	{
224 		double margin = 0;
225 		double vblank_lines = 0;
226 		double vvt_ini = 0;
227 
228 		// Get resulting refresh
229 		t_mode->vfreq = vfreq_real;
230 
231 		// Get total vertical lines
232 		vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, borders, scan_factor) + (interlace == 2?0.5:0);
233 
234 		// Calculate horizontal frequency
235 		t_mode->hfreq = t_mode->vfreq * vvt_ini;
236 
237 		horizontal_values:
238 
239 		// Fill horizontal part of modeline
240 		get_line_params(t_mode, range, cs->pixel_precision? 1 : 8);
241 
242 		// Calculate pixel clock
243 		t_mode->pclock = t_mode->htotal * t_mode->hfreq;
244 		if (t_mode->pclock <= cs->pclock_min)
245 		{
246 			if (t_mode->type & X_RES_EDITABLE)
247 			{
248 				x_scale *= 2;
249 				t_mode->hactive *= 2;
250 				goto horizontal_values;
251 			}
252 			else
253 			{
254 				t_mode->result.weight |= R_OUT_OF_RANGE;
255 				return -1;
256 			}
257 		}
258 
259 		// Vertical blanking
260 		t_mode->vtotal = vvt_ini * scan_factor;
261 		vblank_lines = int(t_mode->hfreq * (range->vertical_blank + borders)) + (interlace == 2?0.5:0);
262 		margin = (t_mode->vtotal - t_mode->vactive - vblank_lines * scan_factor) / (cs->v_shift_correct? 1 : 2);
263 
264 		t_mode->vbegin = t_mode->vactive + max(round_near(t_mode->hfreq * range->vfront_porch * scan_factor + margin), 1);
265 		t_mode->vend = t_mode->vbegin + max(round_near(t_mode->hfreq * range->vsync_pulse * scan_factor), 1);
266 
267 		// Recalculate final vfreq
268 		t_mode->vfreq = (t_mode->hfreq / t_mode->vtotal) * scan_factor;
269 
270 		t_mode->hsync = range->hsync_polarity;
271 		t_mode->vsync = range->vsync_polarity;
272 		t_mode->interlace = interlace == 2?1:0;
273 		t_mode->doublescan = doublescan == 1?0:1;
274 
275 		// Apply interlace fixes
276 		if (cs->interlace_force_even && interlace == 2)
277 		{
278 			t_mode->vbegin = (t_mode->vbegin / 2) * 2;
279 			t_mode->vend = (t_mode->vend / 2) * 2;
280 			t_mode->vtotal++;
281 		}
282 	}
283 
284 	// finally, store result
285 	t_mode->result.scan_penalty = (s_mode->interlace != t_mode->interlace? 1:0) + (s_mode->doublescan != t_mode->doublescan? 1:0);
286 	t_mode->result.x_scale = x_scale;
287 	t_mode->result.y_scale = y_scale;
288 	t_mode->result.v_scale = v_scale;
289 	t_mode->result.x_diff = x_diff;
290 	t_mode->result.y_diff = y_diff;
291 	t_mode->result.v_diff = v_diff;
292 	t_mode->result.x_ratio = x_ratio;
293 	t_mode->result.y_ratio = y_ratio;
294 	t_mode->result.v_ratio = 0;
295 
296 	return 0;
297 }
298 
299 //============================================================
300 //  get_line_params
301 //============================================================
302 
get_line_params(modeline * mode,monitor_range * range,int char_size)303 int get_line_params(modeline *mode, monitor_range *range, int char_size)
304 {
305 	int hhi, hhf, hht;
306 	int hh, hs, he, ht;
307 	double line_time, char_time, new_char_time;
308 	double hfront_porch_min, hsync_pulse_min, hback_porch_min;
309 
310 	hfront_porch_min = range->hfront_porch * .90;
311 	hsync_pulse_min  = range->hsync_pulse  * .90;
312 	hback_porch_min  = range->hback_porch  * .90;
313 
314 	line_time = 1 / mode->hfreq * 1000000;
315 
316 	hh = round(mode->hactive / char_size);
317 	hs = he = ht = 1;
318 
319 	do {
320 		char_time = line_time / (hh + hs + he + ht);
321 		if (hs * char_time < hfront_porch_min ||
322 			fabs((hs + 1) * char_time - range->hfront_porch) < fabs(hs * char_time - range->hfront_porch))
323 			hs++;
324 
325 		if (he * char_time < hsync_pulse_min ||
326 			fabs((he + 1) * char_time - range->hsync_pulse) < fabs(he * char_time - range->hsync_pulse))
327 			he++;
328 
329 		if (ht * char_time < hback_porch_min ||
330 			fabs((ht + 1) * char_time - range->hback_porch) < fabs(ht * char_time - range->hback_porch))
331 			ht++;
332 
333 		new_char_time = line_time / (hh + hs + he + ht);
334 	} while (new_char_time != char_time);
335 
336 	hhi = (hh + hs) * char_size;
337 	hhf = (hh + hs + he) * char_size;
338 	hht = (hh + hs + he + ht) * char_size;
339 
340 	mode->hbegin  = hhi;
341 	mode->hend    = hhf;
342 	mode->htotal  = hht;
343 
344 	return 0;
345 }
346 
347 //============================================================
348 //  scale_into_range
349 //============================================================
350 
scale_into_range(int value,int lower_limit,int higher_limit)351 int scale_into_range (int value, int lower_limit, int higher_limit)
352 {
353 	int scale = 1;
354 	while (value * scale < lower_limit) scale ++;
355 	if (value * scale <= higher_limit)
356 		return scale;
357 	else
358 		return 0;
359 }
360 
361 //============================================================
362 //  scale_into_range
363 //============================================================
364 
scale_into_range(double value,double lower_limit,double higher_limit)365 int scale_into_range (double value, double lower_limit, double higher_limit)
366 {
367 	int scale = 1;
368 	while (value * scale < lower_limit) scale ++;
369 	if (value * scale <= higher_limit)
370 		return scale;
371 	else
372 		return 0;
373 }
374 
375 //============================================================
376 //  scale_into_aspect
377 //============================================================
378 
scale_into_aspect(int source_res,int tot_res,double original_monitor_aspect,double users_monitor_aspect,double * best_diff)379 int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff)
380 {
381 	int scale = 1, best_scale = 1;
382 	double diff = 0;
383 	*best_diff = 0;
384 
385 	while (source_res * scale <= tot_res)
386 	{
387 		diff = fabs(1.0 - (users_monitor_aspect / (double(tot_res) / double(source_res * scale) * original_monitor_aspect))) * 100.0;
388 		if (diff < *best_diff || *best_diff == 0)
389 		{
390 			*best_diff = diff;
391 			best_scale = scale;
392 		}
393 		scale ++;
394 	}
395 	return best_scale;
396 }
397 
398 //============================================================
399 //  stretch_into_range
400 //============================================================
401 
stretch_into_range(double vfreq,monitor_range * range,double borders,bool interlace_allowed,double * interlace)402 int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace)
403 {
404 	int yres, lower_limit;
405 
406 	if (range->interlaced_lines_min && interlace_allowed)
407 	{
408 		yres = range->interlaced_lines_max;
409 		lower_limit = range->interlaced_lines_min;
410 		*interlace = 2;
411 	}
412 	else
413 	{
414 		yres = range->progressive_lines_max;
415 		lower_limit = range->progressive_lines_min;
416 	}
417 
418 	while (yres > lower_limit && max_vfreq_for_yres(yres, range, borders, *interlace) < vfreq)
419 		yres -= 8;
420 
421 	return yres;
422 }
423 
424 
425 //============================================================
426 //  total_lines_for_yres
427 //============================================================
428 
total_lines_for_yres(int yres,double vfreq,monitor_range * range,double borders,double interlace)429 int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace)
430 {
431 	int vvt = max(yres / interlace + round_near(vfreq * yres / (interlace * (1.0 - vfreq * (range->vertical_blank + borders))) * (range->vertical_blank + borders)), 1);
432 	while ((vfreq * vvt < range->hfreq_min) && (vfreq * (vvt + 1) < range->hfreq_max)) vvt++;
433 	return vvt;
434 }
435 
436 //============================================================
437 //  max_vfreq_for_yres
438 //============================================================
439 
max_vfreq_for_yres(int yres,monitor_range * range,double borders,double interlace)440 double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace)
441 {
442 	return range->hfreq_max / (yres / interlace + round_near(range->hfreq_max * (range->vertical_blank + borders)));
443 }
444 
445 //============================================================
446 //  modeline_print
447 //============================================================
448 
modeline_print(modeline * mode,char * modeline,int flags)449 char * modeline_print(modeline *mode, char *modeline, int flags)
450 {
451 	char label[48]={'\x00'};
452 	char params[192]={'\x00'};
453 
454 	if (flags & MS_LABEL)
455 		sprintf(label, "\"%dx%d_%d%s %.6fKHz %.6fHz\"", mode->hactive, mode->vactive, mode->refresh, mode->interlace?"i":"", mode->hfreq/1000, mode->vfreq);
456 
457 	if (flags & MS_LABEL_SDL)
458 		sprintf(label, "\"%dx%d_%.6f\"", mode->hactive, mode->vactive, mode->vfreq);
459 
460 	if (flags & MS_PARAMS)
461 		sprintf(params, " %.6f %d %d %d %d %d %d %d %d %s %s %s %s", double(mode->pclock)/1000000.0, mode->hactive, mode->hbegin, mode->hend, mode->htotal, mode->vactive, mode->vbegin, mode->vend, mode->vtotal,
462 			mode->interlace?"interlace":"", mode->doublescan?"doublescan":"", mode->hsync?"+hsync":"-hsync", mode->vsync?"+vsync":"-vsync");
463 
464 	sprintf(modeline, "%s%s", label, params);
465 
466 	return modeline;
467 }
468 
469 //============================================================
470 //  modeline_result
471 //============================================================
472 
modeline_result(modeline * mode,char * result)473 char * modeline_result(modeline *mode, char *result)
474 {
475 	log_verbose("   rng(%d): ", mode->range);
476 
477 	if (mode->result.weight & R_OUT_OF_RANGE)
478 		sprintf(result, " out of range");
479 
480 	else
481 		sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%d, %d, %d) diff(%.2f, %.2f, %.4f) ratio(%.3f, %.3f)",
482 			mode->hactive, mode->vactive, mode->vfreq, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->hfreq/1000, mode->result.weight & R_RES_STRETCH?"fract":"integ",
483 			mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff, mode->result.x_ratio, mode->result.y_ratio);
484 	return result;
485 }
486 
487 //============================================================
488 //  modeline_compare
489 //============================================================
490 
modeline_compare(modeline * t,modeline * best)491 int modeline_compare(modeline *t, modeline *best)
492 {
493 	bool vector = (t->hactive == (int)t->result.x_ratio);
494 
495 	if (t->result.weight < best->result.weight)
496 		return 1;
497 
498 	else if (t->result.weight <= best->result.weight)
499 	{
500 		double t_v_diff = fabs(t->result.v_diff);
501 		double b_v_diff = fabs(best->result.v_diff);
502 
503 		if (t->result.weight & R_RES_STRETCH || vector)
504 		{
505 			double t_y_score = t->result.y_ratio * (t->interlace?(2.0/3.0):1.0);
506 			double b_y_score = best->result.y_ratio * (best->interlace?(2.0/3.0):1.0);
507 
508 			if  ((t_v_diff <  b_v_diff) ||
509 				((t_v_diff == b_v_diff) && (t_y_score > b_y_score)) ||
510 				((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_ratio > best->result.x_ratio)))
511 					return 1;
512 		}
513 		else
514 		{
515 			int t_y_score = t->result.y_scale + t->result.scan_penalty;
516 			int b_y_score = best->result.y_scale + best->result.scan_penalty;
517 			double xy_diff = roundf((t->result.x_diff + t->result.y_diff) * 100) / 100;
518 			double best_xy_diff = roundf((best->result.x_diff + best->result.y_diff) * 100) / 100;
519 
520 			if  ((t_y_score < b_y_score) ||
521 				((t_y_score == b_y_score) && (xy_diff < best_xy_diff)) ||
522 				((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale < best->result.x_scale)) ||
523 				((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale == best->result.x_scale) && (t_v_diff <  b_v_diff)))
524 					return 1;
525 		}
526 	}
527 	return 0;
528 }
529 
530 //============================================================
531 //  modeline_vesa_gtf
532 //  Based on the VESA GTF spreadsheet by Andy Morrish 1/5/97
533 //============================================================
534 
modeline_vesa_gtf(modeline * m)535 int modeline_vesa_gtf(modeline *m)
536 {
537 	int C, M;
538 	int v_sync_lines, v_porch_lines_min, v_front_porch_lines, v_back_porch_lines, v_sync_v_back_porch_lines, v_total_lines;
539 	int h_sync_width_percent, h_sync_width_pixels, h_blanking_pixels, h_front_porch_pixels, h_total_pixels;
540 	double v_freq, v_freq_est, v_freq_real, v_sync_v_back_porch;
541 	double h_freq, h_period, h_period_real, h_ideal_blanking;
542 	double pixel_freq, interlace;
543 
544 	// Check if there's a value defined for vfreq. We're assuming input vfreq is the total field vfreq regardless interlace
545 	v_freq = m->vfreq? m->vfreq:double(m->refresh);
546 
547 	// These values are GTF defined defaults
548 	v_sync_lines = 3;
549 	v_porch_lines_min = 1;
550 	v_front_porch_lines = v_porch_lines_min;
551 	v_sync_v_back_porch = 550;
552 	h_sync_width_percent = 8;
553 	M = 128.0 / 256 * 600;
554 	C = ((40 - 20) * 128.0 / 256) + 20;
555 
556 	// GTF calculation
557 	interlace = m->interlace?0.5:0;
558 	h_period = ((1.0 / v_freq) - (v_sync_v_back_porch / 1000000)) / ((double)m->height + v_front_porch_lines + interlace) * 1000000;
559 	v_sync_v_back_porch_lines = round_near(v_sync_v_back_porch / h_period);
560 	v_back_porch_lines = v_sync_v_back_porch_lines - v_sync_lines;
561 	v_total_lines = m->height + v_front_porch_lines + v_sync_lines + v_back_porch_lines;
562 	v_freq_est = (1.0 / h_period) / v_total_lines * 1000000;
563 	h_period_real = h_period / (v_freq / v_freq_est);
564 	v_freq_real = (1.0 / h_period_real) / v_total_lines * 1000000;
565 	h_ideal_blanking = double(C - (M * h_period_real / 1000));
566 	h_blanking_pixels = round_near(m->width * h_ideal_blanking /(100 - h_ideal_blanking) / (2 * 8)) * (2 * 8);
567 	h_total_pixels = m->width + h_blanking_pixels;
568 	pixel_freq = h_total_pixels / h_period_real * 1000000;
569 	h_freq = 1000000 / h_period_real;
570 	h_sync_width_pixels = round_near(h_sync_width_percent * h_total_pixels / 100 / 8) * 8;
571 	h_front_porch_pixels = (h_blanking_pixels / 2) - h_sync_width_pixels;
572 
573 	// Results
574 	m->hactive = m->width;
575 	m->hbegin = m->hactive + h_front_porch_pixels;
576 	m->hend = m->hbegin + h_sync_width_pixels;
577 	m->htotal = h_total_pixels;
578 	m->vactive = m->height;
579 	m->vbegin = m->vactive + v_front_porch_lines;
580 	m->vend = m->vbegin + v_sync_lines;
581 	m->vtotal = v_total_lines;
582 	m->hfreq = h_freq;
583 	m->vfreq = v_freq_real;
584 	m->pclock = pixel_freq;
585 	m->hsync = 0;
586 	m->vsync = 1;
587 
588 	return true;
589 }
590 
591 //============================================================
592 //  modeline_parse
593 //============================================================
594 
modeline_parse(const char * user_modeline,modeline * mode)595 int modeline_parse(const char *user_modeline, modeline *mode)
596 {
597 	char modeline_txt[256]={'\x00'};
598 
599 	if (!strcmp(user_modeline, "auto"))
600 		return false;
601 
602 	// Remove quotes
603 	char *quote_start, *quote_end;
604 	quote_start = strstr((char*)user_modeline, "\"");
605 	if (quote_start)
606 	{
607 		quote_start++;
608 		quote_end = strstr(quote_start, "\"");
609 		if (!quote_end || *quote_end++ == 0)
610 			return false;
611 		user_modeline = quote_end;
612 	}
613 
614 	// Get timing flags
615 	mode->interlace = strstr(user_modeline, "interlace")?1:0;
616 	mode->doublescan = strstr(user_modeline, "doublescan")?1:0;
617 	mode->hsync = strstr(user_modeline, "+hsync")?1:0;
618 	mode->vsync = strstr(user_modeline, "+vsync")?1:0;
619 
620 	// Get timing values
621 	double pclock;
622 	int e = sscanf(user_modeline, " %lf %d %d %d %d %d %d %d %d",
623 		&pclock,
624 		&mode->hactive, &mode->hbegin, &mode->hend, &mode->htotal,
625 		&mode->vactive, &mode->vbegin, &mode->vend, &mode->vtotal);
626 
627 	if (e != 9)
628 	{
629 		log_error("SwitchRes: missing parameter in user modeline\n  %s\n", user_modeline);
630 		memset(mode, 0, sizeof(struct modeline));
631 		return false;
632 	}
633 
634 	// Calculate timings
635 	mode->pclock = pclock * 1000000.0;
636 	mode->hfreq = mode->pclock / mode->htotal;
637 	mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
638 	mode->refresh = mode->vfreq;
639 	mode->width = mode->hactive;
640 	mode->height = mode->vactive;
641 	log_verbose("SwitchRes: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
642 
643 	return true;
644 }
645 
646 //============================================================
647 //  modeline_to_monitor_range
648 //============================================================
649 
modeline_to_monitor_range(monitor_range * range,modeline * mode)650 int modeline_to_monitor_range(monitor_range *range, modeline *mode)
651 {
652 	if (range->vfreq_min == 0)
653 	{
654 		range->vfreq_min = mode->vfreq - 0.2;
655 		range->vfreq_max = mode->vfreq + 0.2;
656 	}
657 
658 	double line_time = 1 / mode->hfreq;
659 	double pixel_time = line_time / mode->htotal * 1000000;
660 
661 	range->hfront_porch = pixel_time * (mode->hbegin - mode->hactive);
662 	range->hsync_pulse = pixel_time * (mode->hend - mode->hbegin);
663 	range->hback_porch = pixel_time * (mode->htotal - mode->hend);
664 
665 	range->vfront_porch = line_time * (mode->vbegin - mode->vactive);
666 	range->vsync_pulse = line_time * (mode->vend - mode->vbegin);
667 	range->vback_porch = line_time * (mode->vtotal - mode->vend);
668 	range->vertical_blank = range->vfront_porch + range->vsync_pulse + range->vback_porch;
669 
670 	range->hsync_polarity = mode->hsync;
671 	range->vsync_polarity = mode->vsync;
672 
673 	range->progressive_lines_min = mode->interlace?0:mode->vactive;
674 	range->progressive_lines_max = mode->interlace?0:mode->vactive;
675 	range->interlaced_lines_min = mode->interlace?mode->vactive:0;
676 	range->interlaced_lines_max= mode->interlace?mode->vactive:0;
677 
678 	range->hfreq_min = range->vfreq_min * mode->vtotal;
679 	range->hfreq_max = range->vfreq_max * mode->vtotal;
680 
681 	return 1;
682 }
683 
684 //============================================================
685 //  modeline_is_different
686 //============================================================
687 
modeline_is_different(modeline * n,modeline * p)688 int modeline_is_different(modeline *n, modeline *p)
689 {
690 	// Remove on last fields in modeline comparison
691 	return memcmp(n, p, offsetof(struct modeline, vfreq));
692 }
693 
694 //============================================================
695 //  monitor_fill_vesa_gtf
696 //============================================================
697 
monitor_fill_vesa_gtf(monitor_range * range,const char * max_lines)698 int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines)
699 {
700 	int lines = 0;
701 	sscanf(max_lines, "vesa_%d", &lines);
702 
703 	if (!lines)
704 		return 0;
705 
706 	int i = 0;
707 	if (lines >= 480)
708 		i += monitor_fill_vesa_range(&range[i], 384, 480);
709 	if (lines >= 600)
710 		i += monitor_fill_vesa_range(&range[i], 480, 600);
711 	if (lines >= 768)
712 		i += monitor_fill_vesa_range(&range[i], 600, 768);
713 	if (lines >= 1024)
714 		i += monitor_fill_vesa_range(&range[i], 768, 1024);
715 
716 	return i;
717 }
718 
719 //============================================================
720 //  monitor_fill_vesa_range
721 //============================================================
722 
monitor_fill_vesa_range(monitor_range * range,int lines_min,int lines_max)723 int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max)
724 {
725 	modeline mode;
726 	memset(&mode, 0, sizeof(modeline));
727 
728 	mode.width = real_res(STANDARD_CRT_ASPECT * lines_max);
729 	mode.height = lines_max;
730 	mode.refresh = 60;
731 	range->vfreq_min = 50;
732 	range->vfreq_max = 65;
733 
734 	modeline_vesa_gtf(&mode);
735 	modeline_to_monitor_range(range, &mode);
736 
737 	range->progressive_lines_min = lines_min;
738 	range->hfreq_min = mode.hfreq - 500;
739 	range->hfreq_max = mode.hfreq + 500;
740 	monitor_show_range(range);
741 
742 	return 1;
743 }
744 
745 //============================================================
746 //  round_near
747 //============================================================
748 
round_near(double number)749 int round_near(double number)
750 {
751 	return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
752 }
753 
754 //============================================================
755 //  normalize
756 //============================================================
757 
normalize(int a,int b)758 int normalize(int a, int b)
759 {
760 	int c, d;
761 	c = a % b;
762 	d = a / b;
763 	if (c) d++;
764 	return d * b;
765 }
766 
767 //============================================================
768 //  real_res
769 //============================================================
770 
real_res(int x)771 int real_res(int x) {return (int) (x / 8) * 8;}
772