1 /**************************************************************
2 
3    monitor.cpp - Monitor presets and custom monitor definition
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 "monitor.h"
18 #include "log.h"
19 
20 //============================================================
21 //  CONSTANTS
22 //============================================================
23 
24 #define HFREQ_MIN  14000
25 #define HFREQ_MAX  540672 // 8192 * 1.1 * 60
26 #define VFREQ_MIN  40
27 #define VFREQ_MAX  200
28 #define PROGRESSIVE_LINES_MIN 128
29 
30 //============================================================
31 //  monitor_fill_range
32 //============================================================
33 
monitor_fill_range(monitor_range * range,const char * specs_line)34 int monitor_fill_range(monitor_range *range, const char *specs_line)
35 {
36 	monitor_range new_range;
37 
38 	if (strcmp(specs_line, "auto")) {
39 		int e = sscanf(specs_line, "%lf-%lf,%lf-%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d",
40 			&new_range.hfreq_min, &new_range.hfreq_max,
41 			&new_range.vfreq_min, &new_range.vfreq_max,
42 			&new_range.hfront_porch, &new_range.hsync_pulse, &new_range.hback_porch,
43 			&new_range.vfront_porch, &new_range.vsync_pulse, &new_range.vback_porch,
44 			&new_range.hsync_polarity, &new_range.vsync_polarity,
45 			&new_range.progressive_lines_min, &new_range.progressive_lines_max,
46 			&new_range.interlaced_lines_min, &new_range.interlaced_lines_max);
47 
48 		if (e != 16) {
49 			log_error("Switchres: Error trying to fill monitor range with\n  %s\n", specs_line);
50 			return -1;
51 		}
52 
53 		new_range.vfront_porch /= 1000;
54 		new_range.vsync_pulse /= 1000;
55 		new_range.vback_porch /= 1000;
56 		new_range.vertical_blank = (new_range.vfront_porch + new_range.vsync_pulse + new_range.vback_porch);
57 
58 		if (monitor_evaluate_range(&new_range))
59 		{
60 			log_error("Switchres: Error in monitor range (ignoring): %s\n", specs_line);
61 			return -1;
62 		}
63 		else
64 		{
65 			memcpy(range, &new_range, sizeof(struct monitor_range));
66 			monitor_show_range(range);
67 		}
68 	}
69 	return 0;
70 }
71 
72 //============================================================
73 //  monitor_fill_lcd_range
74 //============================================================
75 
monitor_fill_lcd_range(monitor_range * range,const char * specs_line)76 int monitor_fill_lcd_range(monitor_range *range, const char *specs_line)
77 {
78 	if (strcmp(specs_line, "auto"))
79 	{
80 		if (sscanf(specs_line, "%lf-%lf", &range->vfreq_min, &range->vfreq_max) == 2)
81 		{
82 			log_verbose("Switchres: LCD vfreq range set by user as %f-%f\n", range->vfreq_min, range->vfreq_max);
83 			return true;
84 		}
85 		else
86 			log_error("Switchres: Error trying to fill LCD range with\n  %s\n", specs_line);
87 	}
88 	// Use default values
89 	range->vfreq_min = 59;
90 	range->vfreq_max = 61;
91 	log_verbose("Switchres: Using default vfreq range for LCD %f-%f\n", range->vfreq_min, range->vfreq_max);
92 
93 	return 0;
94 }
95 
96 //============================================================
97 //  monitor_show_range
98 //============================================================
99 
monitor_show_range(monitor_range * range)100 int monitor_show_range(monitor_range *range)
101 {
102 	log_verbose("Switchres: Monitor range %.2f-%.2f,%.2f-%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%d,%d,%d,%d,%d,%d\n",
103 		range->hfreq_min, range->hfreq_max,
104 		range->vfreq_min, range->vfreq_max,
105 		range->hfront_porch, range->hsync_pulse, range->hback_porch,
106 		range->vfront_porch * 1000, range->vsync_pulse * 1000, range->vback_porch * 1000,
107 		range->hsync_polarity, range->vsync_polarity,
108 		range->progressive_lines_min, range->progressive_lines_max,
109 		range->interlaced_lines_min, range->interlaced_lines_max);
110 
111 	return 0;
112 }
113 
114 //============================================================
115 //  monitor_set_preset
116 //============================================================
117 
monitor_set_preset(char * type,monitor_range * range)118 int monitor_set_preset(char *type, monitor_range *range)
119 {
120 	// PAL TV - 50 Hz/625
121 	if (!strcmp(type, "pal"))
122 	{
123 		monitor_fill_range(&range[0], "15625.00-15625.00, 50.00-50.00, 1.500, 4.700, 5.800, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
124 		return 1;
125 	}
126 	// NTSC TV - 60 Hz/525
127 	else if (!strcmp(type, "ntsc"))
128 	{
129 		monitor_fill_range(&range[0], "15734.26-15734.26, 59.94-59.94, 1.500, 4.700, 4.700, 0.191, 0.191, 0.953, 0, 0, 192, 240, 448, 480");
130 		return 1;
131 	}
132 	// Generic 15.7 kHz
133 	else if (!strcmp(type, "generic_15"))
134 	{
135 		monitor_fill_range(&range[0], "15625-15750, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
136 		return 1;
137 	}
138 	// Arcade 15.7 kHz - standard resolution
139 	else if (!strcmp(type, "arcade_15"))
140 	{
141 		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
142 		return 1;
143 	}
144 	// Arcade 15.7-16.5 kHz - extended resolution
145 	else if (!strcmp(type, "arcade_15ex"))
146 	{
147 		monitor_fill_range(&range[0], "15625-16500, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
148 		return 1;
149 	}
150 	// Arcade 25.0 kHz - medium resolution
151 	else if (!strcmp(type, "arcade_25"))
152 	{
153 		monitor_fill_range(&range[0], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
154 		return 1;
155 	}
156 	// Arcade 31.5 kHz - medium resolution
157 	else if (!strcmp(type, "arcade_31"))
158 	{
159 		monitor_fill_range(&range[0], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
160 		return 1;
161 	}
162 	// Arcade 15.7/25.0 kHz - dual-sync
163 	else if (!strcmp(type, "arcade_15_25"))
164 	{
165 		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
166 		monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
167 		return 2;
168 	}
169 	// Arcade 15.7/31.5 kHz - dual-sync
170 	else if (!strcmp(type, "arcade_15_31"))
171 	{
172 		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
173 		monitor_fill_range(&range[1], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
174 		return 2;
175 	}
176 	// Arcade 15.7/25.0/31.5 kHz - tri-sync
177 	else if (!strcmp(type, "arcade_15_25_31"))
178 	{
179 		monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
180 		monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
181 		monitor_fill_range(&range[2], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
182 		return 3;
183 	}
184 	// Makvision 2929D
185 	else if (!strcmp(type, "m2929"))
186 	{
187 		monitor_fill_range(&range[0], "30000-40000, 47.00-90.00, 0.600, 2.500, 2.800, 0.032, 0.096, 0.448, 0, 0, 384, 640, 0, 0");
188 		return 1;
189 	}
190 	// Wells Gardner D9800, D9400
191 	else if (!strcmp(type, "d9800") || !strcmp(type, "d9400"))
192 	{
193 		monitor_fill_range(&range[0], "15250-18000, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
194 		monitor_fill_range(&range[1], "18001-19000, 40-80, 2.187, 4.688, 6.719, 0.140, 0.191, 0.950, 0, 0, 288, 320, 0, 0");
195 		monitor_fill_range(&range[2], "20501-29000, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 320, 384, 0, 0");
196 		monitor_fill_range(&range[3], "29001-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 384, 480, 0, 0");
197 		monitor_fill_range(&range[4], "32001-34000, 40-80, 0.636, 3.813, 1.906, 0.020, 0.106, 0.607, 0, 0, 480, 576, 0, 0");
198 		monitor_fill_range(&range[5], "34001-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 576, 600, 0, 0");
199 		return 6;
200 	}
201 	// Wells Gardner D9200
202 	else if (!strcmp(type, "d9200"))
203 	{
204 		monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
205 		monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
206 		monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 400, 512, 0, 0");
207 		monitor_fill_range(&range[3], "37000-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 512, 600, 0, 0");
208 		return 4;
209 	}
210 	// Wells Gardner K7000
211 	else if (!strcmp(type, "k7000"))
212 	{
213 		monitor_fill_range(&range[0], "15625-15800, 49.50-63.00, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
214 		return 1;
215 	}
216 	// Wells Gardner 25K7131
217 	else if (!strcmp(type, "k7131"))
218 	{
219 		monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
220 		return 1;
221 	}
222 	// Wei-Ya M3129
223 	else if (!strcmp(type, "m3129"))
224 	{
225 		monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 1, 1, 192, 288, 448, 576");
226 		monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 1, 1, 384, 400, 0, 0");
227 		monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 1, 1, 400, 512, 0, 0");
228 		return 3;
229 	}
230 	// Hantarex MTC 9110
231 	else if (!strcmp(type, "h9110") || !strcmp(type, "polo"))
232 	{
233 		monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
234 		return 1;
235 	}
236 	// Hantarex Polostar 25
237 	else if (!strcmp(type, "pstar"))
238 	{
239 		monitor_fill_range(&range[0], "15700-15800, 50-65, 1.800, 0.400, 7.400, 0.064, 0.160, 1.056, 0, 0, 192, 256, 0, 0");
240 		monitor_fill_range(&range[1], "16200-16300, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 256, 264, 512, 528");
241 		monitor_fill_range(&range[2], "25300-25400, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 384, 400, 768, 800");
242 		monitor_fill_range(&range[3], "31500-31600, 50-65, 0.170, 0.350, 5.500, 0.040, 0.040, 0.640, 0, 0, 400, 512, 0, 0");
243 		return 4;
244 	}
245 	// Nanao MS-2930, MS-2931
246 	else if (!strcmp(type, "ms2930"))
247 	{
248 		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
249 		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
250 		monitor_fill_range(&range[2], "31000-32000, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 480, 512, 0, 0");
251 		return 3;
252 	}
253 	// Nanao MS9-29
254 	else if (!strcmp(type, "ms929"))
255 	{
256 		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.910, 4.700, 6.850, 0.190, 0.191, 1.018, 0, 0, 192, 288, 448, 576");
257 		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 384, 400, 0, 0");
258 		return 2;
259 	}
260 	// Rodotron 666B-29
261 	else if (!strcmp(type, "r666b"))
262 	{
263 		monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
264 		monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
265 		monitor_fill_range(&range[2], "31000-32500, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 400, 512, 0, 0");
266 		return 3;
267 	}
268 	// PC CRT 70kHz/120Hz
269 	else if (!strcmp(type, "pc_31_120"))
270 	{
271 		monitor_fill_range(&range[0], "31400-31600, 100-130, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 200, 256, 0, 0");
272 		monitor_fill_range(&range[1], "31400-31600, 50-65, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 400, 512, 0, 0");
273 		return 2;
274 	}
275 	// PC CRT 70kHz/120Hz
276 	else if (!strcmp(type, "pc_70_120"))
277 	{
278 		monitor_fill_range(&range[0], "30000-70000, 100-130, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 192, 320, 0, 0");
279 		monitor_fill_range(&range[1], "30000-70000, 50-65, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 400, 1024, 0, 0");
280 		return 2;
281 	}
282 	// VESA GTF
283 	else if (!strcmp(type, "vesa_480") || !strcmp(type, "vesa_600") || !strcmp(type, "vesa_768") || !strcmp(type, "vesa_1024"))
284 	{
285 		return monitor_fill_vesa_gtf(&range[0], type);
286 	}
287 
288 	log_error("Switchres: Monitor type unknown: %s\n", type);
289 	return 0;
290 }
291 
292 //============================================================
293 //  monitor_evaluate_range
294 //============================================================
295 
monitor_evaluate_range(monitor_range * range)296 int monitor_evaluate_range(monitor_range *range)
297 {
298 	// First we check that all frequency ranges are reasonable
299 	if (range->hfreq_min < HFREQ_MIN || range->hfreq_min > HFREQ_MAX)
300 	{
301 		log_error("Switchres: hfreq_min %.2f out of range\n", range->hfreq_min);
302 		return 1;
303 	}
304 	if (range->hfreq_max < HFREQ_MIN || range->hfreq_max < range->hfreq_min || range->hfreq_max > HFREQ_MAX)
305 	{
306 		log_error("Switchres: hfreq_max %.2f out of range\n", range->hfreq_max);
307 		return 1;
308 	}
309 	if (range->vfreq_min < VFREQ_MIN || range->vfreq_min > VFREQ_MAX)
310 	{
311 		log_error("Switchres: vfreq_min %.2f out of range\n", range->vfreq_min);
312 		return 1;
313 	}
314 	if (range->vfreq_max < VFREQ_MIN || range->vfreq_max < range->vfreq_min || range->vfreq_max > VFREQ_MAX)
315 	{
316 		log_error("Switchres: vfreq_max %.2f out of range\n", range->vfreq_max);
317 		return 1;
318 	}
319 
320 	// line_time in μs. We check that no horizontal value is longer than a whole line
321 	double line_time = 1 / range->hfreq_max * 1000000;
322 
323 	if (range->hfront_porch <= 0 || range->hfront_porch > line_time)
324 	{
325 		log_error("Switchres: hfront_porch %.3f out of range\n", range->hfront_porch);
326 		return 1;
327 	}
328 	if (range->hsync_pulse <= 0 || range->hsync_pulse > line_time)
329 	{
330 		log_error("Switchres: hsync_pulse %.3f out of range\n", range->hsync_pulse);
331 		return 1;
332 	}
333 	if (range->hback_porch <= 0 || range->hback_porch > line_time)
334 	{
335 		log_error("Switchres: hback_porch %.3f out of range\n", range->hback_porch);
336 		return 1;
337 	}
338 
339 	// frame_time in ms. We check that no vertical value is longer than a whole frame
340 	double frame_time = 1 / range->vfreq_max * 1000;
341 
342 	if (range->vfront_porch <= 0 || range->vfront_porch > frame_time)
343 	{
344 		log_error("Switchres: vfront_porch %.3f out of range\n", range->vfront_porch);
345 		return 1;
346 	}
347 	if (range->vsync_pulse <= 0 || range->vsync_pulse > frame_time)
348 	{
349 		log_error("Switchres: vsync_pulse %.3f out of range\n", range->vsync_pulse);
350 		return 1;
351 	}
352 	if (range->vback_porch <= 0 || range->vback_porch > frame_time)
353 	{
354 		log_error("Switchres: vback_porch %.3f out of range\n", range->vback_porch);
355 		return 1;
356 	}
357 
358 	// Now we check sync polarities
359 	if (range->hsync_polarity != 0 && range->hsync_polarity != 1)
360 	{
361 		log_error("Switchres: Hsync polarity can be only 0 or 1\n");
362 		return 1;
363 	}
364 	if (range->vsync_polarity != 0 && range->vsync_polarity != 1)
365 	{
366 		log_error("Switchres: Vsync polarity can be only 0 or 1\n");
367 		return 1;
368 	}
369 
370 	// Finally we check that the line limiters are reasonable
371 	// Progressive range:
372 	if (range->progressive_lines_min > 0 && range->progressive_lines_min < PROGRESSIVE_LINES_MIN)
373 	{
374 		log_error("Switchres: progressive_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN);
375 		return 1;
376 	}
377 	if ((range->progressive_lines_min + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
378 	{
379 		log_error("Switchres: progressive_lines_min %d out of range\n", range->progressive_lines_min);
380 		return 1;
381 	}
382 	if (range->progressive_lines_max < range->progressive_lines_min)
383 	{
384 		log_error("Switchres: progressive_lines_max must greater than progressive_lines_min\n");
385 		return 1;
386 	}
387 	if ((range->progressive_lines_max + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
388 	{
389 		log_error("Switchres: progressive_lines_max %d out of range\n", range->progressive_lines_max);
390 		return 1;
391 	}
392 
393 	// Interlaced range:
394 	if (range->interlaced_lines_min != 0)
395 	{
396 		if (range->interlaced_lines_min < range->progressive_lines_max)
397 		{
398 			log_error("Switchres: interlaced_lines_min must greater than progressive_lines_max\n");
399 			return 1;
400 		}
401 		if (range->interlaced_lines_min < PROGRESSIVE_LINES_MIN * 2)
402 		{
403 			log_error("Switchres: interlaced_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN * 2);
404 			return 1;
405 		}
406 		if ((range->interlaced_lines_min / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
407 		{
408 			log_error("Switchres: interlaced_lines_min %d out of range\n", range->interlaced_lines_min);
409 			return 1;
410 		}
411 		if (range->interlaced_lines_max < range->interlaced_lines_min)
412 		{
413 			log_error("Switchres: interlaced_lines_max must greater than interlaced_lines_min\n");
414 			return 1;
415 		}
416 		if ((range->interlaced_lines_max / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
417 		{
418 			log_error("Switchres: interlaced_lines_max %d out of range\n", range->interlaced_lines_max);
419 			return 1;
420 		}
421 	}
422 	else
423 	{
424 		if (range->interlaced_lines_max != 0)
425 		{
426 			log_error("Switchres: interlaced_lines_max must be zero if interlaced_lines_min is not defined\n");
427 			return 1;
428 		}
429 	}
430 	return 0;
431 }
432