1 /*----------------------------------------------------------------------------
\r
2 Sets text video modes
\r
3 Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer/
\r
4 This code is public domain (no copyright).
\r
5 You can do whatever you want with it.
\r
6 Release date: March 31, 2009
\r
8 Compile with Turbo C, Borland C for DOS, or 16-bit Watcom C. I used:
\r
9 bcc -O2 -2 -Z -d -mt -f- -w -c setres.c
\r
10 tlink /Lc:\bc\lib /x/t c0t.obj setres.obj,setres.com,,cs.lib
\r
12 On one of my systems, the following video modes are supported:
\r
13 40x25[b ] 40x50[b ] 80x25[b ] 80x50[b ] 132x25[v ] 132x43[v ]
\r
14 40x30[tb] 40x60[tb] 80x30[tb] 80x60[tb] 90x25[tb] 90x30[tb]
\r
15 90x50[tb] 90x60[tb] 132x30[tv] 132x50[tv] 132x60[tv]
\r
17 [ b]=VGA BIOS (INT 10h AH=00h) modes [tb]=tweaked VGA BIOS modes
\r
18 [ v]=VBE BIOS (INT 10h AX=4F0xh) modes [tv]=tweaked VBE BIOS modes
\r
20 The 'tweaked' modes start with a VGA or VBE BIOS mode, then write to the
\r
21 CRTC registers to change the resolution. By default, this will happen only
\r
22 if INT 10h AX=4F00h reports that the hardware is register-compatible with
\r
23 VGA and INT 10h AX=4F01h reports that the mode is also register-compatible
\r
24 with VGA. '-v' option overrides this -- use it at your own risk. Tweaking
\r
25 does not (should not?) change the sync frequencies to unsupported values.
\r
26 ----------------------------------------------------------------------------*/
\r
27 #include <string.h> /* strcpy() */
\r
28 #include <stdlib.h> /* realloc(), atoi() */
\r
29 #include <stdio.h> /* printf(), putchar() */
\r
30 #include <ctype.h> /* tolower() */
\r
31 /* union REGS, struct SREGS, int86(), int86x(), FP_SEG(), FP_OFF(), */
\r
32 #include <dos.h> /* pokeb(), poke(), inportb(), outportb() */
\r
36 typedef unsigned char uint8_t;
\r
37 typedef unsigned short uint16_t;
\r
38 typedef unsigned long uint32_t;
\r
41 #if defined(__TURBOC__)
\r
42 #include <conio.h> /* clrscr() */
\r
44 #elif defined(__WATCOMC__)
\r
45 #if defined(__386__)
\r
46 #error 16-bit program -- compile with WCC.EXE
\r
48 #define inportb(P) inp(P)
\r
49 #define outportb(P,V) outp(P,V)
\r
50 #define pokeb(S,O,V) *(uint8_t far *)MK_FP(S,O)=(V)
\r
51 #define poke(S,O,V) *(uint16_t far *)MK_FP(S,O)=(V)
\r
52 #define peek(S,O) *(uint16_t far *)MK_FP(S,O)
\r
58 regs.h.bh = 0; /* page number */
\r
59 regs.h.dh = 0; /* top-most row */
\r
60 regs.h.dl = 0; /* left-most column */
\r
61 int86(0x10, ®s, ®s);
\r
65 #error Sorry, unsupported compiler
\r
68 /* besides the CRTC, VGA sequencer register #1 is also modified */
\r
69 #define VGA_SEQ_INDEX 0x3C4
\r
70 #define VGA_SEQ_DATA 0x3C5
\r
71 /* emulation (color or mono) is read from this register --
\r
72 it determines the CRTC I/O address (0x3B4 or 0x3D4) */
\r
73 #define VGA_MISC_READ 0x3CC
\r
74 #define VGA_CRTC_INDEX (g_crtc_io + 0)
\r
75 #define VGA_CRTC_DATA (g_crtc_io + 1)
\r
77 /* this is like an X11 "modeline" */
\r
80 unsigned disp, blank_start, sync_start, sync_end, blank_end, total;
\r
83 /* for 30-line modes, use the 16-pixel-high font instead of 8-pixel */
\r
84 static timing_t g_60_lines =
\r
86 /* blank sync sync blank
\r
87 disp start start end end tot
\r
88 ---- ----- ----- ---- ----- --- */
\r
89 480, 488, 490, 493, 517, 525
\r
92 static timing_t g_90_cols =
\r
94 /* blank sync sync blank
\r
95 disp start start end end tot
\r
96 ---- ----- ----- ---- ----- --- */
\r
97 90, 90, 95, 108, 98, 112
\r
102 unsigned char cols, rows;
\r
103 unsigned mode_num; /* VGA (<0x100) or VBE (>=0x100) mode number */
\r
104 unsigned char set_font; /* set 8x8 font after mode-set or no? */
\r
105 timing_t *horiz, *vert; /* CRTC timing 'tweaks' */
\r
108 static mode_t *g_mode;
\r
109 static unsigned g_crtc_io, g_num_modes;
\r
110 /*****************************************************************************
\r
111 *****************************************************************************/
\r
112 static void add_mode(unsigned cols, unsigned rows, unsigned mode_num,
\r
113 unsigned set_font, timing_t *horiz, timing_t *vert)
\r
117 new_mode = realloc(g_mode, (g_num_modes + 1) * sizeof(mode_t));
\r
118 if(new_mode == NULL)
\r
120 printf("Error: out of memory\n");
\r
124 new_mode = &g_mode[g_num_modes];
\r
126 new_mode->cols = cols;
\r
127 new_mode->rows = rows;
\r
128 new_mode->mode_num = mode_num;
\r
129 new_mode->set_font = set_font;
\r
130 new_mode->horiz = horiz;
\r
131 new_mode->vert = vert;
\r
133 /*****************************************************************************
\r
134 *****************************************************************************/
\r
135 static void dump_modes(void)
\r
137 unsigned scn_wd, csr_x, i;
\r
140 scn_wd = peek(0x40, 0x4A);
\r
141 printf("The following video modes are supported\n");
\r
142 for(i = 0; i < g_num_modes; i++)
\r
144 /* get cursor X position */
\r
147 int86(0x10, ®s, ®s);
\r
149 /* emit newline now if next listing will wrap */
\r
150 if(csr_x + 7 >= scn_wd)
\r
152 /* resolution listing is 7 characters wide */
\r
153 printf("%4ux%-2u ", g_mode[i].cols, g_mode[i].rows);
\r
157 /*****************************************************************************
\r
158 *****************************************************************************/
\r
159 static mode_t *find_mode(unsigned cols, unsigned rows)
\r
163 for(i = 0; i < g_num_modes; i++)
\r
165 if(g_mode[i].cols == cols && g_mode[i].rows == rows)
\r
170 /*****************************************************************************
\r
171 *****************************************************************************/
\r
172 static void set_horiz(timing_t *t)
\r
176 /* remove write-protection from CRTC registers 0-5 (and 6-7) */
\r
177 outportb(VGA_CRTC_INDEX, 17);
\r
178 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) & ~0x80);
\r
179 /* set horizontal displayed */
\r
180 outportb(VGA_CRTC_INDEX, 1);
\r
181 outportb(VGA_CRTC_DATA, t->disp - 1);
\r
182 /* set horizontal blanking start */
\r
183 outportb(VGA_CRTC_INDEX, 2);
\r
184 outportb(VGA_CRTC_DATA, t->blank_start);
\r
185 /* set horizontal sync start */
\r
186 outportb(VGA_CRTC_INDEX, 4);
\r
187 outportb(VGA_CRTC_DATA, t->sync_start);
\r
188 /* set horizontal sync end */
\r
189 outportb(VGA_CRTC_INDEX, 5);
\r
190 i = inportb(VGA_CRTC_DATA) & ~0x1F;
\r
191 outportb(VGA_CRTC_DATA, (t->sync_end & 0x1F) | i);
\r
192 /* set horizontal blanking end */
\r
193 outportb(VGA_CRTC_INDEX, 3);
\r
194 i = inportb(VGA_CRTC_DATA) & ~0x1F;
\r
195 outportb(VGA_CRTC_DATA, (t->blank_end & 0x1F) | i);
\r
196 /* set horizontal total */
\r
197 outportb(VGA_CRTC_INDEX, 0);
\r
198 outportb(VGA_CRTC_DATA, t->total - 5);
\r
199 /* set "offset" (words per line) */
\r
200 outportb(VGA_CRTC_INDEX, 19);
\r
201 outportb(VGA_CRTC_DATA, t->disp / 2);
\r
202 /* make characters 8 or 9 pixels wide */
\r
203 outportb(VGA_SEQ_INDEX, 1);
\r
204 i = inportb(VGA_SEQ_DATA) & ~0x01;
\r
207 outportb(VGA_SEQ_DATA, i);
\r
209 /*****************************************************************************
\r
210 *****************************************************************************/
\r
211 static void set_vert(timing_t *t)
\r
215 /* remove write-protection from CRTC registers 6-7 (and 0-5) */
\r
216 outportb(VGA_CRTC_INDEX, 17);
\r
217 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) & ~0x80);
\r
218 /* set vertical displayed */
\r
220 outportb(VGA_CRTC_INDEX, 18);
\r
221 outportb(VGA_CRTC_DATA, i);
\r
222 outportb(VGA_CRTC_INDEX, 7);
\r
223 j = inportb(VGA_CRTC_DATA) & ~0x42;
\r
228 outportb(VGA_CRTC_DATA, j);
\r
229 /* set vertical blanking start */
\r
230 i = t->blank_start;
\r
231 outportb(VGA_CRTC_INDEX, 21);
\r
232 outportb(VGA_CRTC_DATA, i);
\r
233 outportb(VGA_CRTC_INDEX, 7);
\r
234 j = inportb(VGA_CRTC_DATA) & ~0x08;
\r
237 outportb(VGA_CRTC_DATA, j);
\r
238 /* set vertical sync (retrace) start */
\r
240 outportb(VGA_CRTC_INDEX, 16);
\r
241 outportb(VGA_CRTC_DATA, i);
\r
242 outportb(VGA_CRTC_INDEX, 7);
\r
243 j = inportb(VGA_CRTC_DATA) & ~0x84;
\r
248 outportb(VGA_CRTC_DATA, j);
\r
249 /* set vertical sync (retrace) end */
\r
250 outportb(VGA_CRTC_INDEX, 17);
\r
251 i = inportb(VGA_CRTC_DATA) & ~0x0F;
\r
252 outportb(VGA_CRTC_DATA, (t->sync_end & 0x0F) | i);
\r
253 /* set vertical blanking end */
\r
254 outportb(VGA_CRTC_INDEX, 22);
\r
255 /* i = inportb(VGA_CRTC_DATA) & ~0x7F;
\r
256 outportb(VGA_CRTC_DATA, (t->blank_end & 0x7F) | i); */
\r
257 outportb(VGA_CRTC_DATA, t->blank_end);
\r
258 /* set vertical total */
\r
260 outportb(VGA_CRTC_INDEX, 6);
\r
261 outportb(VGA_CRTC_DATA, i);
\r
262 outportb(VGA_CRTC_INDEX, 7);
\r
263 j = inportb(VGA_CRTC_DATA) & ~0x21;
\r
268 outportb(VGA_CRTC_DATA, j);
\r
270 /*****************************************************************************
\r
271 *****************************************************************************/
\r
272 static void set_mode(mode_t *m)
\r
276 /* set (initial) mode; using either the VGA... */
\r
277 if(m->mode_num < 0x100)
\r
279 regs.x.ax = m->mode_num;
\r
280 int86(0x10, ®s, ®s);
\r
282 /* ...or VBE BIOS */
\r
285 regs.x.ax = 0x4F02;
\r
286 regs.x.bx = m->mode_num;
\r
287 int86(0x10, ®s, ®s);
\r
289 /* set 8x8 font for 50- and 60-row VGA and tweaked modes */
\r
292 regs.x.ax = 0x1112;
\r
294 int86(0x10, ®s, ®s);
\r
296 if(m->horiz || m->vert)
\r
298 /* get CRTC address */
\r
299 if((inportb(VGA_MISC_READ) & 0x01) == 0)
\r
300 g_crtc_io = 0x3B4; /* monochrome emulation */
\r
302 g_crtc_io = 0x3D4; /* color emulation */
\r
303 /* tweak CRTC timing */
\r
305 set_horiz(m->horiz);
\r
309 /* let the BIOS know what we've done so text output works properly */
\r
310 pokeb(0x40, 0x84, m->rows - 1);
\r
311 poke(0x40, 0x4A, m->cols);
\r
313 /*****************************************************************************
\r
314 *****************************************************************************/
\r
315 static void usage(void)
\r
317 printf("Sets text video modes. Usage:\n"
\r
318 "\tSETRES [-vd] cols rows\t\t" "-v skips VGA compatability test\n"
\r
319 "\t\t\t\t\t" "-d prints debug messages\n"
\r
320 "\tSETRES -l[vd]\t\t\t" "lists available modes\n"
\r
321 "\tSETRES -a\t\t\t" "displays author info\n");
\r
324 /*****************************************************************************
\r
325 *****************************************************************************/
\r
326 int main(int arg_c, char *arg_v[])
\r
328 /* structure used by INT 10h AX=4F00h */
\r
335 char far *oem_name;
\r
336 uint32_t capabilities; /* b1=1 for non-VGA board */
\r
337 uint16_t far *mode_list;
\r
338 char res0[494]; /* fields we don't care about */
\r
340 /* structure used by INT 10h AX=4F01h */
\r
344 uint16_t mode_attrib; /* b4=0 for text modes */
\r
345 char res0[16]; /* fields we don't care about */
\r
346 /* OEM modes and VBE 1.2+ only: */
\r
351 char res1[232]; /* fields we don't care about */
\r
353 /* command-line options: */
\r
354 char assume_vga, list_modes, info, debug;
\r
355 unsigned i, num_count, wd, ht;
\r
356 uint16_t far *mnp; /* Mode Number Pointer */
\r
357 struct SREGS sregs;
\r
362 /* process command-line */
\r
366 assume_vga = list_modes = info = debug = 0;
\r
367 for(i = 1; i < arg_c; i++)
\r
370 if(arg_v[i][0] == '-')
\r
372 for(s = &arg_v[i][1]; *s != '\0'; s++)
\r
374 if(tolower(*s) == 'v')
\r
376 else if(tolower(*s) == 'l')
\r
378 else if(tolower(*s) == 'a')
\r
380 else if(tolower(*s) == 'd')
\r
384 printf("Error: invalid option '%c'\n",
\r
390 /* not an option, not a number */
\r
391 else if(atoi(arg_v[i]) == 0)
\r
393 printf("Error: invalid parameter '%s'\n", arg_v[i]);
\r
396 /* 1st number on command line = rows */
\r
397 else if(num_count == 0)
\r
399 wd = atoi(arg_v[i]);
\r
402 /* 2nd number on command line = cols */
\r
403 else if(num_count == 1)
\r
405 ht = atoi(arg_v[i]);
\r
408 /* too many numbers */
\r
411 printf("Error: invalid parameter '%s'\n", arg_v[i]);
\r
417 printf( "Sets text video modes\n"
\r
418 "Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer/\n"
\r
419 "This code is public domain (no copyright).\n"
\r
420 "You can do whatever you want with it.\n"
\r
421 "Release date: March 31, 2009\n");
\r
424 /* I assume these VGA text modes are supported on all systems: */
\r
426 printf("Adding VGA BIOS modes...\n");
\r
427 add_mode(40, 25, 1, 0, NULL, NULL);
\r
428 add_mode(40, 50, 1, 1, NULL, NULL);
\r
429 add_mode(80, 25, 3, 0, NULL, NULL);
\r
430 add_mode(80, 50, 3, 1, NULL, NULL);
\r
431 /* check if VBE present */
\r
433 printf("VBE BIOS...");
\r
434 strcpy(vbe_info.sig, "VBE2");
\r
435 sregs.es = FP_SEG(&vbe_info);
\r
436 regs.x.di = FP_OFF(&vbe_info);
\r
437 regs.x.ax = 0x4F00;
\r
438 int86x(0x10, ®s, ®s, &sregs);
\r
439 /* (the indentation got a little ugly, so I'm going to use goto)
\r
440 need VBE 1.2 or better */
\r
441 if(regs.x.ax != 0x004F)
\r
444 printf("not detected\n");
\r
448 printf("version %u.%u; OEM name '%Fs'\n", vbe_info.ver_major,
\r
449 vbe_info.ver_minor,vbe_info.oem_name);
\r
450 if(vbe_info.ver_major < 1 ||
\r
451 (vbe_info.ver_major == 1 && vbe_info.ver_minor < 2))
\r
454 printf("Warning: VBE 1.2+ required\n");
\r
457 /* iterate over VBE modes */
\r
459 printf("Hex VBE mode numbers:\n");
\r
460 for(mnp = vbe_info.mode_list; *mnp != 0xFFFF; mnp++)
\r
463 printf("%4X ", *mnp);
\r
464 /* get mode info */
\r
465 sregs.es = FP_SEG(&mode_info);
\r
466 regs.x.di = FP_OFF(&mode_info);
\r
468 regs.x.ax = 0x4F01;
\r
469 int86x(0x10, ®s, ®s, &sregs);
\r
470 if(regs.x.ax != 0x004F)
\r
472 /* ignore all but text modes */
\r
473 if(mode_info.mode_attrib & 0x10)
\r
475 /* add VBE text mode to list */
\r
477 printf("\nAdding VBE mode: %ux%u\n",
\r
478 mode_info.wd, mode_info.ht);
\r
479 add_mode(mode_info.wd, mode_info.ht, *mnp, 0, NULL, NULL);
\r
484 printf("\n-v option; assuming hardware is VGA-compatible\n");
\r
487 printf("\nHardware is ");
\r
488 if(vbe_info.capabilities & 0x01)
\r
490 printf("register-compatible with VGA\n");
\r
493 /* check if board is register-compatible with VGA
\r
494 (unless overriden with '-v' option...) */
\r
495 if(!assume_vga && (vbe_info.capabilities & 0x01))
\r
497 /* add 'tweaked' VGA modes to list */
\r
499 printf("Adding tweaked VGA modes...\n");
\r
500 add_mode(40, 30, 1, 0, NULL, &g_60_lines);
\r
501 add_mode(40, 60, 1, 1, NULL, &g_60_lines);
\r
502 add_mode(80, 30, 3, 0, NULL, &g_60_lines);
\r
503 add_mode(80, 60, 3, 1, NULL, &g_60_lines);
\r
504 add_mode(90, 25, 3, 0, &g_90_cols, NULL);
\r
505 add_mode(90, 30, 3, 0, &g_90_cols, &g_60_lines);
\r
506 add_mode(90, 50, 3, 1, &g_90_cols, NULL);
\r
507 add_mode(90, 60, 3, 1, &g_90_cols, &g_60_lines);
\r
508 /* prepare to tweak VBE modes */
\r
509 for(i = 0; i < g_num_modes; i++)
\r
511 static unsigned rows[] = { 25, 30, 50, 60 };
\r
513 unsigned j, set_font, tweak_vert;
\r
515 /* find VBE modes... */
\r
516 if(g_mode[i].mode_num < 0x100)
\r
518 /* ...with 25, 30, 50, or 60 rows */
\r
519 if(g_mode[i].rows != 25 && g_mode[i].rows != 30
\r
520 && g_mode[i].rows != 50 && g_mode[i].rows != 60)
\r
522 /* check if this mode is register-compatible with VGA */
\r
525 sregs.es = FP_SEG(&mode_info);
\r
526 regs.x.di = FP_OFF(&mode_info);
\r
527 regs.x.cx = g_mode[i].mode_num;
\r
528 regs.x.ax = 0x4F01;
\r
529 int86x(0x10, ®s, ®s, &sregs);
\r
530 if(regs.x.ax != 0x004F)
\r
532 /* xxx - b5 of mode_info.mode_attrib may be VBE 2.0+ only,
\r
533 according to Ralf Brown's list */
\r
534 if(mode_info.mode_attrib & 0x20)
\r
537 printf("VBE mode 0x%X is NOT "
\r
538 "register-compatible with "
\r
539 "VGA; will not tweak\n",
\r
540 g_mode[i].mode_num);
\r
544 /* now find mode with same number of columns
\r
545 and complementary number of rows */
\r
546 for(j = 0; j < sizeof(rows) / sizeof(rows[0]); j++)
\r
548 if(g_mode[i].rows == rows[j])
\r
550 /* if the complementary mode does not already exist... */
\r
551 mode = find_mode(g_mode[i].cols, rows[j]);
\r
555 set_font = (rows[j] >= 50);
\r
556 tweak_vert = (rows[j] == 30 || rows[j] == 60);
\r
558 printf("Adding tweaked VBE mode: %ux%u\n",
\r
559 g_mode[i].cols, rows[j]);
\r
560 add_mode(g_mode[i].cols, rows[j],
\r
561 g_mode[i].mode_num, set_font, NULL,
\r
562 (tweak_vert ? &g_60_lines : NULL));
\r
566 /* just list the supported modes */
\r
572 /* otherwise we need two numbers on the command-line
\r
573 (the case of more than 2 numbers was handled above) */
\r
576 printf("Error: must specify cols and rows to set video mode\n");
\r
579 /* see if selected resolution supported */
\r
580 mode = find_mode(wd, ht);
\r
583 printf("Error: mode %ux%u not supported\n", wd, ht);
\r
593 /* DEBUG: display horizontal and vertical 'rulers' */
\r
594 /* textattr(0x17); doesn't work -- no blue background after clrscr() */
\r
596 for(i = 1; i < 0xFF00; i += 2)
\r
597 pokeb(0xB800, i, 0x17);
\r
598 for(i = 0; i < wd - 1; )
\r
612 for(i = 1; i < ht - 4; i++)
\r
614 printf("Current screen resolution is %ux%u\n", wd, ht);
\r