b5d88c39d5bc12c20412144ca2d37350943855ad
[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 <math.h>
112
113
114
115 #define MARGIN_PERCENT    1.8   /* % of active vertical image                */
116 #define CELL_GRAN         8.0   /* assumed character cell granularity        */
117 #define MIN_PORCH         1     /* minimum front porch                       */
118 #define V_SYNC_RQD        3     /* width of vsync in lines                   */
119 #define H_SYNC_PERCENT    8.0   /* width of hsync as % of total line         */
120 #define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
121 #define M                 600.0 /* blanking formula gradient                 */
122 #define C                 40.0  /* blanking formula offset                   */
123 #define K                 128.0 /* blanking formula scaling factor           */
124 #define J                 20.0  /* blanking formula scaling factor           */
125
126 /* C' and M' are part of the Blanking Duty Cycle computation */
127
128 #define C_PRIME           (((C - J) * K/256.0) + J)
129 #define M_PRIME           (K/256.0 * M)
130
131
132 /* struct definitions */
133
134 typedef struct __mode
135 {
136     int hr, hss, hse, hfl;
137     int vr, vss, vse, vfl;
138     float pclk, h_freq, v_freq;
139 } mode;
140
141
142 typedef struct __options
143 {
144     int x, y;
145     int xf86mode, fbmode;
146     float v_freq;
147 } options;
148
149
150
151
152 /* prototypes */
153
154 void print_value(int n, char *name, float val);
155 void print_xf86_mode (mode *m);
156 void print_fb_mode (mode *m);
157 mode *vert_refresh (int h_pixels, int v_lines, float freq,
158                     int interlaced, int margins);
159 options *parse_command_line (int argc, char *argv[]);
160
161
162
163
164 /*
165  * print_value() - print the result of the named computation; this is
166  * useful when comparing against the GTF EXCEL spreadsheet.
167  */
168
169 int global_verbose = 0;
170
171 void print_value(int n, char *name, float val)
172 {
173     if (global_verbose) {
174         printf("%2d: %-27s: %15f\n", n, name, val);
175     }
176 } // print_value()
177
178
179
180 /* print_xf86_mode() - print the XFree86 modeline, given mode timings. */
181
182 void print_xf86_mode (mode *m)
183 {
184     printf ("\n");
185     printf ("  # %dx%d @ %.2f Hz (GTF) hsync: %.2f kHz; pclk: %.2f MHz\n",
186             m->hr, m->vr, m->v_freq, m->h_freq, m->pclk);
187     
188     printf ("  Modeline \"%dx%d_%.2f\"  %.2f"
189             "  %d %d %d %d"
190             "  %d %d %d %d"
191             "  -HSync +Vsync\n\n",
192             m->hr, m->vr, m->v_freq, m->pclk,
193             m->hr, m->hss, m->hse, m->hfl,
194             m->vr, m->vss, m->vse, m->vfl);
195     
196 } // print_xf86_mode()
197
198
199
200 /*
201  * print_fb_mode() - print a mode description in fbset(8) format;
202  * see the fb.modes(8) manpage.  The timing description used in
203  * this is rather odd; they use "left and right margin" to refer
204  * to the portion of the hblank before and after the sync pulse
205  * by conceptually wrapping the portion of the blank after the pulse
206  * to infront of the visible region; ie:
207  * 
208  *
209  * Timing description I'm accustomed to:
210  *
211  *
212  *
213  *     <--------1--------> <--2--> <--3--> <--4-->
214  *                                _________
215  *    |-------------------|_______|       |_______
216  *
217  *                        R       SS      SE     FL
218  *       
219  * 1: visible image
220  * 2: blank before sync (aka front porch)
221  * 3: sync pulse
222  * 4: blank after sync (aka back porch)
223  * R: Resolution
224  * SS: Sync Start
225  * SE: Sync End
226  * FL: Frame Length
227  *
228  *
229  * But the fb.modes format is:
230  *
231  *
232  *    <--4--> <--------1--------> <--2--> <--3--> 
233  *                                       _________
234  *    _______|-------------------|_______|       |
235  *  
236  * The fb.modes(8) manpage refers to <4> and <2> as the left and
237  * right "margin" (as well as upper and lower margin in the vertical
238  * direction) -- note that this has nothing to do with the term
239  * "margin" used in the GTF Timing Standard.
240  *
241  * XXX always prints the 32 bit mode -- should I provide a command
242  * line option to specify the bpp?  It's simple enough for a user
243  * to edit the mode description after it's generated.
244  */
245
246 void print_fb_mode (mode *m)
247 {
248     printf ("\n");
249     printf ("mode \"%dx%d %.2fHz 32bit (GTF)\"\n",
250             m->hr, m->vr, m->v_freq);
251     printf ("    # PCLK: %.2f MHz, H: %.2f kHz, V: %.2f Hz\n",
252             m->pclk, m->h_freq, m->v_freq);
253     printf ("    geometry %d %d %d %d 32\n",
254             m->hr, m->vr, m->hr, m->vr);
255     printf ("    timings %d %d %d %d %d %d %d\n",
256             (int) rint(1000000.0/m->pclk),// pixclock in picoseconds
257             m->hfl - m->hse,              // left margin (in pixels)
258             m->hss - m->hr,               // right margin (in pixels)
259             m->vfl - m->vse,              // upper margin (in pixel lines)
260             m->vss - m->vr,               // lower margin (in pixel lines)
261             m->hse - m->hss,              // horizontal sync length (in pixels)
262             m->vse - m->vss);             // vert sync length (in pixel lines)
263     printf ("    hsync low\n");
264     printf ("    vsync high\n");
265     printf ("endmode\n\n");
266     
267 } // print_fb_mode()
268
269
270
271
272 /*
273  * vert_refresh() - as defined by the GTF Timing Standard, compute the
274  * Stage 1 Parameters using the vertical refresh frequency.  In other
275  * words: input a desired resolution and desired refresh rate, and
276  * output the GTF mode timings.
277  *
278  * XXX All the code is in place to compute interlaced modes, but I don't
279  * feel like testing it right now.
280  *
281  * XXX margin computations are implemented but not tested (nor used by
282  * XFree86 of fbset mode descriptions, from what I can tell).
283  */
284
285 mode *vert_refresh (int h_pixels, int v_lines, float freq,
286                     int interlaced, int margins)
287 {
288     float h_pixels_rnd;
289     float v_lines_rnd;
290     float v_field_rate_rqd;
291     float top_margin;
292     float bottom_margin;
293     float interlace;
294     float h_period_est;
295     float vsync_plus_bp;
296     float v_back_porch;
297     float total_v_lines;
298     float v_field_rate_est;
299     float h_period;
300     float v_field_rate;
301     float v_frame_rate;
302     float left_margin;
303     float right_margin;
304     float total_active_pixels;
305     float ideal_duty_cycle;
306     float h_blank;
307     float total_pixels;
308     float pixel_freq;
309     float h_freq;
310
311     float h_sync;
312     float h_front_porch;
313     float v_odd_front_porch_lines;
314
315     mode *m = (mode*) malloc (sizeof (mode));
316     
317     
318     /*  1. In order to give correct results, the number of horizontal
319      *  pixels requested is first processed to ensure that it is divisible
320      *  by the character size, by rounding it to the nearest character
321      *  cell boundary:
322      *
323      *  [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND])
324      */
325     
326     h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN;
327     
328     print_value(1, "[H PIXELS RND]", h_pixels_rnd);
329
330     
331     /*  2. If interlace is requested, the number of vertical lines assumed
332      *  by the calculation must be halved, as the computation calculates
333      *  the number of vertical lines per field. In either case, the
334      *  number of lines is rounded to the nearest integer.
335      *   
336      *  [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0),
337      *                                     ROUND([V LINES],0))
338      */
339
340     v_lines_rnd = interlaced ?
341             rint((float) v_lines) / 2.0 :
342             rint((float) v_lines);
343     
344     print_value(2, "[V LINES RND]", v_lines_rnd);
345     
346     
347     /*  3. Find the frame rate required:
348      *
349      *  [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2,
350      *                                          [I/P FREQ RQD])
351      */
352
353     v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq);
354
355     print_value(3, "[V FIELD RATE RQD]", v_field_rate_rqd);
356     
357
358     /*  4. Find number of lines in Top margin:
359      *
360      *  [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
361      *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
362      *          0)
363      */
364
365     top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
366
367     print_value(4, "[TOP MARGIN (LINES)]", top_margin);
368     
369
370     /*  5. Find number of lines in Bottom margin:
371      *
372      *  [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
373      *          ROUND(([MARGIN%]/100*[V LINES RND]),0),
374      *          0)
375      */
376
377     bottom_margin = margins ? rint(MARGIN_PERCENT/100.0 * v_lines_rnd) : (0.0);
378
379     print_value(5, "[BOT MARGIN (LINES)]", bottom_margin);
380
381     
382     /*  6. If interlace is required, then set variable [INTERLACE]=0.5:
383      *   
384      *  [INTERLACE]=(IF([INT RQD?]="y",0.5,0))
385      */
386
387     interlace = interlaced ? 0.5 : 0.0;
388
389     print_value(6, "[INTERLACE]", interlace);
390     
391
392     /*  7. Estimate the Horizontal period
393      *
394      *  [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) /
395      *                    ([V LINES RND] + (2*[TOP MARGIN (LINES)]) +
396      *                     [MIN PORCH RND]+[INTERLACE]) * 1000000
397      */
398
399     h_period_est = (((1.0/v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP/1000000.0))
400                     / (v_lines_rnd + (2*top_margin) + MIN_PORCH + interlace)
401                     * 1000000.0);
402
403     print_value(7, "[H PERIOD EST]", h_period_est);
404     
405
406     /*  8. Find the number of lines in V sync + back porch:
407      *
408      *  [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0)
409      */
410
411     vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP/h_period_est);
412
413     print_value(8, "[V SYNC+BP]", vsync_plus_bp);
414     
415     
416     /*  9. Find the number of lines in V back porch alone:
417      *
418      *  [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND]
419      *
420      *  XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]?
421      */
422     
423     v_back_porch = vsync_plus_bp - V_SYNC_RQD;
424     
425     print_value(9, "[V BACK PORCH]", v_back_porch);
426     
427
428     /*  10. Find the total number of lines in Vertical field period:
429      *
430      *  [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] +
431      *                    [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] +
432      *                    [MIN PORCH RND]
433      */
434
435     total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp +
436         interlace + MIN_PORCH;
437     
438     print_value(10, "[TOTAL V LINES]", total_v_lines);
439     
440
441     /*  11. Estimate the Vertical field frequency:
442      *
443      *  [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000
444      */
445
446     v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
447     
448     print_value(11, "[V FIELD RATE EST]", v_field_rate_est);
449     
450
451     /*  12. Find the actual horizontal period:
452      *
453      *  [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST])
454      */
455
456     h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
457     
458     print_value(12, "[H PERIOD]", h_period);
459     
460
461     /*  13. Find the actual Vertical field frequency:
462      *
463      *  [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000
464      */
465
466     v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0;
467
468     print_value(13, "[V FIELD RATE]", v_field_rate);
469     
470
471     /*  14. Find the Vertical frame frequency:
472      *
473      *  [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE]))
474      */
475
476     v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate;
477
478     print_value(14, "[V FRAME RATE]", v_frame_rate);
479     
480
481     /*  15. Find number of pixels in left margin:
482      *
483      *  [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
484      *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
485      *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
486      *          0))
487      */
488
489     left_margin = margins ?
490         rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
491         0.0;
492     
493     print_value(15, "[LEFT MARGIN (PIXELS)]", left_margin);
494     
495
496     /*  16. Find number of pixels in right margin:
497      *
498      *  [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
499      *          (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
500      *                   [CELL GRAN RND]),0)) * [CELL GRAN RND],
501      *          0))
502      */
503     
504     right_margin = margins ?
505         rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN :
506         0.0;
507     
508     print_value(16, "[RIGHT MARGIN (PIXELS)]", right_margin);
509     
510
511     /*  17. Find total number of active pixels in image and left and right
512      *  margins:
513      *
514      *  [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] +
515      *                          [RIGHT MARGIN (PIXELS)]
516      */
517
518     total_active_pixels = h_pixels_rnd + left_margin + right_margin;
519     
520     print_value(17, "[TOTAL ACTIVE PIXELS]", total_active_pixels);
521     
522     
523     /*  18. Find the ideal blanking duty cycle from the blanking duty cycle
524      *  equation:
525      *
526      *  [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000)
527      */
528
529     ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
530     
531     print_value(18, "[IDEAL DUTY CYCLE]", ideal_duty_cycle);
532     
533
534     /*  19. Find the number of pixels in the blanking time to the nearest
535      *  double character cell:
536      *
537      *  [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] *
538      *                               [IDEAL DUTY CYCLE] /
539      *                               (100-[IDEAL DUTY CYCLE]) /
540      *                               (2*[CELL GRAN RND])), 0))
541      *                       * (2*[CELL GRAN RND])
542      */
543
544     h_blank = rint(total_active_pixels *
545                    ideal_duty_cycle /
546                    (100.0 - ideal_duty_cycle) /
547                    (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
548     
549     print_value(19, "[H BLANK (PIXELS)]", h_blank);
550     
551
552     /*  20. Find total number of pixels:
553      *
554      *  [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)]
555      */
556
557     total_pixels = total_active_pixels + h_blank;
558     
559     print_value(20, "[TOTAL PIXELS]", total_pixels);
560     
561
562     /*  21. Find pixel clock frequency:
563      *
564      *  [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD]
565      */
566     
567     pixel_freq = total_pixels / h_period;
568     
569     print_value(21, "[PIXEL FREQ]", pixel_freq);
570     
571
572     /*  22. Find horizontal frequency:
573      *
574      *  [H FREQ] = 1000 / [H PERIOD]
575      */
576
577     h_freq = 1000.0 / h_period;
578     
579     print_value(22, "[H FREQ]", h_freq);
580     
581
582
583     /* Stage 1 computations are now complete; I should really pass
584        the results to another function and do the Stage 2
585        computations, but I only need a few more values so I'll just
586        append the computations here for now */
587
588     
589
590     /*  17. Find the number of pixels in the horizontal sync period:
591      *
592      *  [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] /
593      *                             [CELL GRAN RND]),0))*[CELL GRAN RND]
594      */
595
596     h_sync = rint(H_SYNC_PERCENT/100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
597
598     print_value(17, "[H SYNC (PIXELS)]", h_sync);
599     
600
601     /*  18. Find the number of pixels in the horizontal front porch period:
602      *
603      *  [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)]
604      */
605
606     h_front_porch = (h_blank / 2.0) - h_sync;
607
608     print_value(18, "[H FRONT PORCH (PIXELS)]", h_front_porch);
609     
610     
611     /*  36. Find the number of lines in the odd front porch period:
612      *
613      *  [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE])
614      */
615     
616     v_odd_front_porch_lines = MIN_PORCH + interlace;
617     
618     print_value(36, "[V ODD FRONT PORCH(LINES)]", v_odd_front_porch_lines);
619     
620
621     /* finally, pack the results in the mode struct */
622     
623     m->hr  = (int) (h_pixels_rnd);
624     m->hss = (int) (h_pixels_rnd + h_front_porch);
625     m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync);
626     m->hfl = (int) (total_pixels);
627
628     m->vr  = (int) (v_lines_rnd);
629     m->vss = (int) (v_lines_rnd + v_odd_front_porch_lines);
630     m->vse = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD);
631     m->vfl = (int) (total_v_lines);
632
633     m->pclk   = pixel_freq;
634     m->h_freq = h_freq;
635     m->v_freq = freq;
636
637     return (m);
638     
639 } // vert_refresh()
640
641
642
643
644 /*
645  * parse_command_line() - parse the command line and return an
646  * alloced structure containing the results.  On error print usage
647  * and return NULL.
648  */ 
649
650 options *parse_command_line (int argc, char *argv[])
651 {
652     int n;
653
654     options *o = (options *) calloc (1, sizeof (options));
655
656     if (argc < 4) goto bad_option;
657
658     o->x = atoi (argv[1]);
659     o->y = atoi (argv[2]);
660     o->v_freq = atof (argv[3]);
661
662     /* XXX should check for errors in the above */
663     
664     n = 4;
665
666     while (n < argc) {
667         if ((strcmp (argv[n], "-v") == 0) ||
668             (strcmp (argv[n], "--verbose") == 0)) {
669             global_verbose = 1;
670         } else if ((strcmp (argv[n], "-f") == 0) ||
671                    (strcmp (argv[n], "--fbmode") == 0)) {
672             o->fbmode = 1;
673         } else if ((strcmp (argv[n], "-x") == 0) ||
674                    (strcmp (argv[n], "--xf86mode") == 0)) {
675             o->xf86mode = 1;
676         } else {
677             goto bad_option;
678         }
679         
680         n++;
681     }
682
683     /* if neither xf86mode nor fbmode were requested, default to
684        xf86mode */
685
686     if (!o->fbmode && !o->xf86mode) o->xf86mode = 1;
687     
688     return (o);
689     
690  bad_option:
691
692     fprintf (stderr, "\n");
693     fprintf (stderr, "usage: %s x y refresh [-v|--verbose] "
694              "[-f|--fbmode] [-x|-xf86mode]\n", argv[0]);
695
696     fprintf (stderr, "\n");
697     
698     fprintf (stderr, "            x : the desired horizontal "
699              "resolution (required)\n");
700     fprintf (stderr, "            y : the desired vertical "
701              "resolution (required)\n");
702     fprintf (stderr, "      refresh : the desired refresh "
703              "rate (required)\n");
704     fprintf (stderr, " -v|--verbose : enable verbose printouts "
705              "(traces each step of the computation)\n");
706     fprintf (stderr, "  -f|--fbmode : output an fbset(8)-style mode "
707              "description\n");
708     fprintf (stderr, " -x|-xf86mode : output an XFree86-style mode "
709              "description (this is the default\n"
710              "                if no mode description is requested)\n");
711     
712     fprintf (stderr, "\n");
713     
714     free (o);
715     return (NULL);
716
717 } // parse_command_line()
718
719
720
721 int main (int argc, char *argv[])
722 {
723     mode *m;
724     options *o;
725
726     o = parse_command_line (argc, argv);
727     if (!o) exit (1);
728     
729     m = vert_refresh (o->x, o->y, o->v_freq, 0, 0);
730     if (!m) exit (1);
731
732     if (o->xf86mode)
733         print_xf86_mode(m);
734     
735     if (o->fbmode)
736         print_fb_mode(m);
737     
738     return 0;
739     
740 } // main()