Added tag 0.9.42 for changeset a60a55d2465f
[grml-scripts.git] / compile / gtf.c
1 /* gtf.c  Generate mode timings using the GTF Timing Standard
2  *
3  * gcc gtf.c -o gtf -lm -Wall
4  *
5  * Copyright (c) 2001, Andy Ritger  aritger@nvidia.com
6  * All rights reserved.
7  *
8  * Source http://osdn.dl.sourceforge.net/sourceforge/gtf/gtf.c
9  * 
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 
14  * o Redistributions of source code must retain the above copyright
15  *   notice, this list of conditions and the following disclaimer.
16  * o Redistributions in binary form must reproduce the above copyright
17  *   notice, this list of conditions and the following disclaimer
18  *   in the documentation and/or other materials provided with the
19  *   distribution.
20  * o Neither the name of NVIDIA nor the names of its contributors
21  *   may be used to endorse or promote products derived from this
22  *   software without specific prior written permission.
23  *
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
27  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
28  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29  * THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * 
39  *
40  * This program is based on the Generalized Timing Formula(GTF TM)
41  * Standard Version: 1.0, Revision: 1.0
42  *
43  * The GTF Document contains the following Copyright information:
44  *
45  * Copyright (c) 1994, 1995, 1996 - Video Electronics Standards
46  * Association. Duplication of this document within VESA member
47  * companies for review purposes is permitted. All other rights
48  * reserved.
49  *
50  * While every precaution has been taken in the preparation
51  * of this standard, the Video Electronics Standards Association and
52  * its contributors assume no responsibility for errors or omissions,
53  * and make no warranties, expressed or implied, of functionality
54  * of suitability for any purpose. The sample code contained within
55  * this standard may be used without restriction.
56  *
57  * 
58  *
59  * The GTF EXCEL(TM) SPREADSHEET, a sample (and the definitive)
60  * implementation of the GTF Timing Standard, is available at:
61  *
62  * ftp://ftp.vesa.org/pub/GTF/GTF_V1R1.xls
63  *
64  *
65  *
66  * This program takes a desired resolution and vertical refresh rate,
67  * and computes mode timings according to the GTF Timing Standard.
68  * These mode timings can then be formatted as an XFree86 modeline
69  * or a mode description for use by fbset(8).
70  *
71  *
72  *
73  * NOTES:
74  *
75  * The GTF allows for computation of "margins" (the visible border
76  * surrounding the addressable video); on most non-overscan type
77  * systems, the margin period is zero.  I've implemented the margin
78  * computations but not enabled it because 1) I don't really have
79  * any experience with this, and 2) neither XFree86 modelines nor
80  * fbset fb.modes provide an obvious way for margin timings to be
81  * included in their mode descriptions (needs more investigation).
82  * 
83  * The GTF provides for computation of interlaced mode timings;
84  * I've implemented the computations but not enabled them, yet.
85  * I should probably enable and test this at some point.
86  *
87  * 
88  *
89  * TODO:
90  *
91  * o Add support for interlaced modes.
92  *
93  * o Implement the other portions of the GTF: compute mode timings
94  *   given either the desired pixel clock or the desired horizontal
95  *   frequency.
96  *
97  * o It would be nice if this were more general purpose to do things
98  *   outside the scope of the GTF: like generate double scan mode
99  *   timings, for example.
100  *   
101  * o Printing digits to the right of the decimal point when the
102  *   digits are 0 annoys me.
103  *
104  * o Error checking.
105  *
106  */
107
108
109 #include <stdio.h>
110 #include <stdlib.h>
111 #include <string.h>
112 #include <math.h>
113
114
115
116 #define MARGIN_PERCENT    1.8   /* % of active vertical image                */
117 #define CELL_GRAN         8.0   /* assumed character cell granularity        */
118 #define MIN_PORCH         1     /* minimum front porch                       */
119 #define V_SYNC_RQD        3     /* width of vsync in lines                   */
120 #define H_SYNC_PERCENT    8.0   /* width of hsync as % of total line         */
121 #define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
122 #define M                 600.0 /* blanking formula gradient                 */
123 #define C                 40.0  /* blanking formula offset                   */
124 #define K                 128.0 /* blanking formula scaling factor           */
125 #define J                 20.0  /* blanking formula scaling factor           */
126
127 /* C' and M' are part of the Blanking Duty Cycle computation */
128
129 #define C_PRIME           (((C - J) * K/256.0) + J)
130 #define M_PRIME           (K/256.0 * M)
131
132
133 /* struct definitions */
134
135 typedef struct __mode
136 {
137     int hr, hss, hse, hfl;
138     int vr, vss, vse, vfl;
139     float pclk, h_freq, v_freq;
140 } mode;
141
142
143 typedef struct __options
144 {
145     int x, y;
146     int xf86mode, fbmode;
147     float v_freq;
148 } options;
149
150
151
152
153 /* prototypes */
154
155 void print_value(int n, char *name, float val);
156 void print_xf86_mode (mode *m);
157 void print_fb_mode (mode *m);
158 mode *vert_refresh (int h_pixels, int v_lines, float freq,
159                     int interlaced, int margins);
160 options *parse_command_line (int argc, char *argv[]);
161
162
163
164
165 /*
166  * print_value() - print the result of the named computation; this is
167  * useful when comparing against the GTF EXCEL spreadsheet.
168  */
169
170 int global_verbose = 0;
171
172 void print_value(int n, char *name, float val)
173 {
174     if (global_verbose) {
175         printf("%2d: %-27s: %15f\n", n, name, val);
176     }
177 } // print_value()
178
179
180
181 /* print_xf86_mode() - print the XFree86 modeline, given mode timings. */
182
183 void print_xf86_mode (mode *m)
184 {
185     printf ("\n");
186     printf ("  # %dx%d @ %.2f Hz (GTF) hsync: %.2f kHz; pclk: %.2f MHz\n",
187             m->hr, m->vr, m->v_freq, m->h_freq, m->pclk);
188     
189     printf ("  Modeline \"%dx%d_%.2f\"  %.2f"
190             "  %d %d %d %d"
191             "  %d %d %d %d"
192             "  -HSync +Vsync\n\n",
193             m->hr, m->vr, m->v_freq, m->pclk,
194             m->hr, m->hss, m->hse, m->hfl,
195             m->vr, m->vss, m->vse, m->vfl);
196     
197 } // print_xf86_mode()
198
199
200
201 /*
202  * print_fb_mode() - print a mode description in fbset(8) format;
203  * see the fb.modes(8) manpage.  The timing description used in
204  * this is rather odd; they use "left and right margin" to refer
205  * to the portion of the hblank before and after the sync pulse
206  * by conceptually wrapping the portion of the blank after the pulse
207  * to infront of the visible region; ie:
208  * 
209  *
210  * Timing description I'm accustomed to:
211  *
212  *
213  *
214  *     <--------1--------> <--2--> <--3--> <--4-->
215  *                                _________
216  *    |-------------------|_______|       |_______
217  *
218  *                        R       SS      SE     FL
219  *       
220  * 1: visible image
221  * 2: blank before sync (aka front porch)
222  * 3: sync pulse
223  * 4: blank after sync (aka back porch)
224  * R: Resolution
225  * SS: Sync Start
226  * SE: Sync End
227  * FL: Frame Length
228  *
229  *
230  * But the fb.modes format is:
231  *
232  *
233  *    <--4--> <--------1--------> <--2--> <--3--> 
234  *                                       _________
235  *    _______|-------------------|_______|       |
236  *  
237  * The fb.modes(8) manpage refers to <4> and <2> as the left and
238  * right "margin" (as well as upper and lower margin in the vertical
239  * direction) -- note that this has nothing to do with the term
240  * "margin" used in the GTF Timing Standard.
241  *
242  * XXX always prints the 32 bit mode -- should I provide a command
243  * line option to specify the bpp?  It's simple enough for a user
244  * to edit the mode description after it's generated.
245  */
246
247 void print_fb_mode (mode *m)
248 {
249     printf ("\n");
250     printf ("mode \"%dx%d %.2fHz 32bit (GTF)\"\n",
251             m->hr, m->vr, m->v_freq);
252     printf ("    # PCLK: %.2f MHz, H: %.2f kHz, V: %.2f Hz\n",
253             m->pclk, m->h_freq, m->v_freq);
254     printf ("    geometry %d %d %d %d 32\n",
255             m->hr, m->vr, m->hr, m->vr);
256     printf ("    timings %d %d %d %d %d %d %d\n",
257             (int) rint(1000000.0/m->pclk),// pixclock in picoseconds
258             m->hfl - m->hse,              // left margin (in pixels)
259             m->hss - m->hr,               // right margin (in pixels)
260             m->vfl - m->vse,              // upper margin (in pixel lines)
261             m->vss - m->vr,               // lower margin (in pixel lines)
262             m->hse - m->hss,              // horizontal sync length (in pixels)
263             m->vse - m->vss);             // vert sync length (in pixel lines)
264     printf ("    hsync low\n");
265     printf ("    vsync high\n");
266     printf ("endmode\n\n");
267     
268 } // print_fb_mode()
269
270
271
272
273 /*
274  * vert_refresh() - as defined by the GTF Timing Standard, compute the
275  * Stage 1 Parameters using the vertical refresh frequency.  In other
276  * words: input a desired resolution and desired refresh rate, and
277  * output the GTF mode timings.
278  *
279  * XXX All the code is in place to compute interlaced modes, but I don't
280  * feel like testing it right now.
281  *
282  * XXX margin computations are implemented but not tested (nor used by
283  * XFree86 of fbset mode descriptions, from what I can tell).
284  */
285
286 mode *vert_refresh (int h_pixels, int v_lines, float freq,
287                     int interlaced, int margins)
288 {
289     float h_pixels_rnd;
290     float v_lines_rnd;
291     float v_field_rate_rqd;
292     float top_margin;
293     float bottom_margin;
294     float interlace;
295     float h_period_est;
296     float vsync_plus_bp;
297     float v_back_porch;
298     float total_v_lines;
299     float v_field_rate_est;
300     float h_period;
301     float v_field_rate;
302     float v_frame_rate;
303     float left_margin;
304     float right_margin;
305     float total_active_pixels;
306     float ideal_duty_cycle;
307     float h_blank;
308     float total_pixels;
309     float pixel_freq;
310     float h_freq;
311
312     float h_sync;
313     float h_front_porch;
314     float v_odd_front_porch_lines;
315
316     mode *m = (mode*) malloc (sizeof (mode));
317     
318     
319     /*  1. In order to give correct results, the number of horizontal
320      *  pixels requested is first processed to ensure that it is divisible
321      *  by the character size, by rounding it to the nearest character
322      *  cell boundary:
323      *
324      *  [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND])
325      */
326     
327     h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN;
328     
329     print_value(1, "[H PIXELS RND]", h_pixels_rnd);
330
331     
332     /*  2. If interlace is requested, the number of vertical lines assumed
333      *  by the calculation must be halved, as the computation calculates
334      *  the number of vertical lines per field. In either case, the
335      *  number of lines is rounded to the nearest integer.
336      *   
337      *  [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0),
338      *                                     ROUND([V LINES],0))
339      */
340
341     v_lines_rnd = interlaced ?
342             rint((float) v_lines) / 2.0 :
343             rint((float) v_lines);
344     
345     print_value(2, "[V LINES RND]", v_lines_rnd);
346     
347     
348     /*  3. Find the frame rate required:
349      *
350      *  [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2,
351      *                                          [I/P FREQ RQD])
352      */
353
354     v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq);
355
356     print_value(3, "[V FIELD RATE RQD]", v_field_rate_rqd);
357     
358
359     /*  4. Find number of lines in Top margin:
360      *
361      *  [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
362      *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
363      *          0)
364      */
365
366     top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
367
368     print_value(4, "[TOP MARGIN (LINES)]", top_margin);
369     
370
371     /*  5. Find number of lines in Bottom margin:
372      *
373      *  [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
374      *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
375      *          0)
376      */
377
378     bottom_margin = margins ? rint(MARGIN_PERCENT/100.0 * v_lines_rnd) : (0.0);
379
380     print_value(5, "[BOT MARGIN (LINES)]", bottom_margin);
381
382     
383     /*  6. If interlace is required, then set variable [INTERLACE]=0.5:
384      *   
385      *  [INTERLACE]=(IF([INT RQD?]="y",0.5,0))
386      */
387
388     interlace = interlaced ? 0.5 : 0.0;
389
390     print_value(6, "[INTERLACE]", interlace);
391     
392
393     /*  7. Estimate the Horizontal period
394      *
395      *  [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) /
396      *                    ([V LINES RND] + (2*[TOP MARGIN (LINES)]) +
397      *                     [MIN PORCH RND]+[INTERLACE]) * 1000000
398      */
399
400     h_period_est = (((1.0/v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP/1000000.0))
401                     / (v_lines_rnd + (2*top_margin) + MIN_PORCH + interlace)
402                     * 1000000.0);
403
404     print_value(7, "[H PERIOD EST]", h_period_est);
405     
406
407     /*  8. Find the number of lines in V sync + back porch:
408      *
409      *  [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0)
410      */
411
412     vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP/h_period_est);
413
414     print_value(8, "[V SYNC+BP]", vsync_plus_bp);
415     
416     
417     /*  9. Find the number of lines in V back porch alone:
418      *
419      *  [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND]
420      *
421      *  XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]?
422      */
423     
424     v_back_porch = vsync_plus_bp - V_SYNC_RQD;
425     
426     print_value(9, "[V BACK PORCH]", v_back_porch);
427     
428
429     /*  10. Find the total number of lines in Vertical field period:
430      *
431      *  [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] +
432      *                    [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] +
433      *                    [MIN PORCH RND]
434      */
435
436     total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp +
437         interlace + MIN_PORCH;
438     
439     print_value(10, "[TOTAL V LINES]", total_v_lines);
440     
441
442     /*  11. Estimate the Vertical field frequency:
443      *
444      *  [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000
445      */
446
447     v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
448     
449     print_value(11, "[V FIELD RATE EST]", v_field_rate_est);
450     
451
452     /*  12. Find the actual horizontal period:
453      *
454      *  [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST])
455      */
456
457     h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
458     
459     print_value(12, "[H PERIOD]", h_period);
460     
461
462     /*  13. Find the actual Vertical field frequency:
463      *
464      *  [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000
465      */
466
467     v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0;
468
469     print_value(13, "[V FIELD RATE]", v_field_rate);
470     
471
472     /*  14. Find the Vertical frame frequency:
473      *
474      *  [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE]))
475      */
476
477     v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate;
478
479     print_value(14, "[V FRAME RATE]", v_frame_rate);
480     
481
482     /*  15. Find number of pixels in left margin:
483      *
484      *  [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
485      *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
486      *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
487      *          0))
488      */
489
490     left_margin = margins ?
491         rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
492         0.0;
493     
494     print_value(15, "[LEFT MARGIN (PIXELS)]", left_margin);
495     
496
497     /*  16. Find number of pixels in right margin:
498      *
499      *  [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
500      *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
501      *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
502      *          0))
503      */
504     
505     right_margin = margins ?
506         rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
507         0.0;
508     
509     print_value(16, "[RIGHT MARGIN (PIXELS)]", right_margin);
510     
511
512     /*  17. Find total number of active pixels in image and left and right
513      *  margins:
514      *
515      *  [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] +
516      *                          [RIGHT MARGIN (PIXELS)]
517      */
518
519     total_active_pixels = h_pixels_rnd + left_margin + right_margin;
520     
521     print_value(17, "[TOTAL ACTIVE PIXELS]", total_active_pixels);
522     
523     
524     /*  18. Find the ideal blanking duty cycle from the blanking duty cycle
525      *  equation:
526      *
527      *  [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000)
528      */
529
530     ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
531     
532     print_value(18, "[IDEAL DUTY CYCLE]", ideal_duty_cycle);
533     
534
535     /*  19. Find the number of pixels in the blanking time to the nearest
536      *  double character cell:
537      *
538      *  [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] *
539      *                               [IDEAL DUTY CYCLE] /
540      *                               (100-[IDEAL DUTY CYCLE]) /
541      *                               (2*[CELL GRAN RND])), 0))
542      *                       * (2*[CELL GRAN RND])
543      */
544
545     h_blank = rint(total_active_pixels *
546                    ideal_duty_cycle /
547                    (100.0 - ideal_duty_cycle) /
548                    (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
549     
550     print_value(19, "[H BLANK (PIXELS)]", h_blank);
551     
552
553     /*  20. Find total number of pixels:
554      *
555      *  [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)]
556      */
557
558     total_pixels = total_active_pixels + h_blank;
559     
560     print_value(20, "[TOTAL PIXELS]", total_pixels);
561     
562
563     /*  21. Find pixel clock frequency:
564      *
565      *  [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD]
566      */
567     
568     pixel_freq = total_pixels / h_period;
569     
570     print_value(21, "[PIXEL FREQ]", pixel_freq);
571     
572
573     /*  22. Find horizontal frequency:
574      *
575      *  [H FREQ] = 1000 / [H PERIOD]
576      */
577
578     h_freq = 1000.0 / h_period;
579     
580     print_value(22, "[H FREQ]", h_freq);
581     
582
583
584     /* Stage 1 computations are now complete; I should really pass
585        the results to another function and do the Stage 2
586        computations, but I only need a few more values so I'll just
587        append the computations here for now */
588
589     
590
591     /*  17. Find the number of pixels in the horizontal sync period:
592      *
593      *  [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] /
594      *                             [CELL GRAN RND]),0))*[CELL GRAN RND]
595      */
596
597     h_sync = rint(H_SYNC_PERCENT/100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
598
599     print_value(17, "[H SYNC (PIXELS)]", h_sync);
600     
601
602     /*  18. Find the number of pixels in the horizontal front porch period:
603      *
604      *  [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)]
605      */
606
607     h_front_porch = (h_blank / 2.0) - h_sync;
608
609     print_value(18, "[H FRONT PORCH (PIXELS)]", h_front_porch);
610     
611     
612     /*  36. Find the number of lines in the odd front porch period:
613      *
614      *  [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE])
615      */
616     
617     v_odd_front_porch_lines = MIN_PORCH + interlace;
618     
619     print_value(36, "[V ODD FRONT PORCH(LINES)]", v_odd_front_porch_lines);
620     
621
622     /* finally, pack the results in the mode struct */
623     
624     m->hr  = (int) (h_pixels_rnd);
625     m->hss = (int) (h_pixels_rnd + h_front_porch);
626     m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync);
627     m->hfl = (int) (total_pixels);
628
629     m->vr  = (int) (v_lines_rnd);
630     m->vss = (int) (v_lines_rnd + v_odd_front_porch_lines);
631     m->vse = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD);
632     m->vfl = (int) (total_v_lines);
633
634     m->pclk   = pixel_freq;
635     m->h_freq = h_freq;
636     m->v_freq = freq;
637
638     return (m);
639     
640 } // vert_refresh()
641
642
643
644
645 /*
646  * parse_command_line() - parse the command line and return an
647  * alloced structure containing the results.  On error print usage
648  * and return NULL.
649  */ 
650
651 options *parse_command_line (int argc, char *argv[])
652 {
653     int n;
654
655     options *o = (options *) calloc (1, sizeof (options));
656
657     if (argc < 4) goto bad_option;
658
659     o->x = atoi (argv[1]);
660     o->y = atoi (argv[2]);
661     o->v_freq = atof (argv[3]);
662
663     /* XXX should check for errors in the above */
664     
665     n = 4;
666
667     while (n < argc) {
668         if ((strcmp (argv[n], "-v") == 0) ||
669             (strcmp (argv[n], "--verbose") == 0)) {
670             global_verbose = 1;
671         } else if ((strcmp (argv[n], "-f") == 0) ||
672                    (strcmp (argv[n], "--fbmode") == 0)) {
673             o->fbmode = 1;
674         } else if ((strcmp (argv[n], "-x") == 0) ||
675                    (strcmp (argv[n], "--xf86mode") == 0)) {
676             o->xf86mode = 1;
677         } else {
678             goto bad_option;
679         }
680         
681         n++;
682     }
683
684     /* if neither xf86mode nor fbmode were requested, default to
685        xf86mode */
686
687     if (!o->fbmode && !o->xf86mode) o->xf86mode = 1;
688     
689     return (o);
690     
691  bad_option:
692
693     fprintf (stderr, "\n");
694     fprintf (stderr, "usage: %s x y refresh [-v|--verbose] "
695              "[-f|--fbmode] [-x|-xf86mode]\n", argv[0]);
696
697     fprintf (stderr, "\n");
698     
699     fprintf (stderr, "            x : the desired horizontal "
700              "resolution (required)\n");
701     fprintf (stderr, "            y : the desired vertical "
702              "resolution (required)\n");
703     fprintf (stderr, "      refresh : the desired refresh "
704              "rate (required)\n");
705     fprintf (stderr, " -v|--verbose : enable verbose printouts "
706              "(traces each step of the computation)\n");
707     fprintf (stderr, "  -f|--fbmode : output an fbset(8)-style mode "
708              "description\n");
709     fprintf (stderr, " -x|-xf86mode : output an XFree86-style mode "
710              "description (this is the default\n"
711              "                if no mode description is requested)\n");
712     
713     fprintf (stderr, "\n");
714     
715     free (o);
716     return (NULL);
717
718 } // parse_command_line()
719
720
721
722 int main (int argc, char *argv[])
723 {
724     mode *m;
725     options *o;
726
727     o = parse_command_line (argc, argv);
728     if (!o) exit (1);
729     
730     m = vert_refresh (o->x, o->y, o->v_freq, 0, 0);
731     if (!m) exit (1);
732
733     if (o->xf86mode)
734         print_xf86_mode(m);
735     
736     if (o->fbmode)
737         print_fb_mode(m);
738     
739     return 0;
740     
741 } // main()