/*---------------------------------------------------------------------------- VBE video mode information utility Chris Giese http://SisAndHappy.com/ChrisGiese This code is public domain (no copyright). You can do whatever you want with it. Release date: January 31, 2008 Compile with DJGPP or 32-bit Watcom C. ----------------------------------------------------------------------------*/ /* union REGS, outportb(), int386() */ #include #if 0 /* C99 fixed-width types */ #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif /*---------------------------------------------------------------------------- GRAPHICS DEMO CODE The VBE mode-set code is below. If that's all you want, then delete this demo code. ----------------------------------------------------------------------------*/ #include /* getch(). For Watcom C: outp() */ #if defined(__WATCOMC__) #if !defined(__386__) #error 32-bit program -- compile with WCC386.EXE #endif #define outportb(P,V) outp(P,V) #endif #define C_BLACK g_std_colors[g_color_model][0] #define C_BLUE g_std_colors[g_color_model][1] #define C_GREEN g_std_colors[g_color_model][2] #define C_CYAN g_std_colors[g_color_model][3] #define C_RED g_std_colors[g_color_model][4] #define C_MAGENTA g_std_colors[g_color_model][5] #define C_BROWN g_std_colors[g_color_model][6] #define C_LIGHTGRAY g_std_colors[g_color_model][7] /* finish filling out g_std_colors[] if you want to use these: #define C_DARKGRAY g_std_colors[g_color_model][8] #define C_LIGHTBLUE g_std_colors[g_color_model][9] #define C_LIGHTGREEN g_std_colors[g_color_model][10] #define C_LIGHTCYAN g_std_colors[g_color_model][11] #define C_LIGHTRED g_std_colors[g_color_model][12] #define C_LIGHTMAGENTA g_std_colors[g_color_model][13] #define C_YELLOW g_std_colors[g_color_model][14] #define C_WHITE g_std_colors[g_color_model][15] */ typedef unsigned long color_t; static unsigned g_wd, g_ht; #define HUGE /* nothing */ static unsigned char HUGE *g_raster; static unsigned long g_bytes_per_row; /* for banked framebuffer: */ static unsigned g_use_win_a, g_gran_per_64k; /* for demo code: */ static void (*g_write_pixel)(unsigned x, unsigned y, color_t c); static unsigned g_color_model; /* 4 "color models"; 8 "standard" colors */ static color_t g_std_colors[4][8] = { /* g_color_model=0: palette color (monochrome, 16-color, and 256-color) */ { /*0, 1, 2, 3, 4, 5, 6, 7,*/ 8, 9, 10, 11, 12, 13, 14, 15 }, { /* g_color_model=1: 15-bit color (5-5-5; True Color) */ #undef RGB2V #define RGB2V(R,G,B) \ (((((R) >> 3) & 0x1F) << 10) | \ ((((G) >> 3) & 0x1F) << 5) | \ ((((B) >> 3) & 0x1F) << 0)) RGB2V(0, 0, 0), RGB2V(0, 0, 0xFF), RGB2V(0, 0xFF,0), RGB2V(0, 0xFF,0xFF), RGB2V(0xFF,0, 0), RGB2V(0xFF,0, 0xFF), RGB2V(0xFF,0xFF,0), RGB2V(0xFF,0xFF,0xFF) }, { /* g_color_model=2: 16-bit color (5-6-5; True Color) */ #undef RGB2V #define RGB2V(R,G,B) \ (((((R) >> 3) & 0x1F) << 11) | \ ((((G) >> 2) & 0x3F) << 5) | \ ((((B) >> 3) & 0x1F) << 0)) RGB2V(0, 0, 0), RGB2V(0, 0, 0xFF), RGB2V(0, 0xFF,0), RGB2V(0, 0xFF,0xFF), RGB2V(0xFF,0, 0), RGB2V(0xFF,0, 0xFF), RGB2V(0xFF,0xFF,0), RGB2V(0xFF,0xFF,0xFF) }, { /* g_color_model=3: 24- or 32-bit color (High Color) */ #undef RGB2V #define RGB2V(R,G,B) \ ((unsigned long)(R) << 16) | ((G) << 8) | (B) RGB2V(0, 0, 0), RGB2V(0, 0, 0xFF), RGB2V(0, 0xFF,0), RGB2V(0, 0xFF,0xFF), RGB2V(0xFF,0, 0), RGB2V(0xFF,0, 0xFF), RGB2V(0xFF,0xFF,0), RGB2V(0xFF,0xFF,0xFF) } }; /***************************************************************************** *****************************************************************************/ #define VGA_SEQ_INDEX 0x3C4 #define VGA_SEQ_DATA 0x3C5 #define VGA_GC_INDEX 0x3CE #define VGA_GC_DATA 0x3CF static void set_plane(unsigned p) { unsigned pmask; p &= 3; pmask = 1 << p; outportb(VGA_GC_INDEX, 4); outportb(VGA_GC_DATA, p); outportb(VGA_SEQ_INDEX, 2); outportb(VGA_SEQ_DATA, pmask); } /***************************************************************************** *****************************************************************************/ static void set_bank(unsigned b) { static unsigned curr_bank = -1u; /**/ union REGS regs; if(b == curr_bank) return; curr_bank = b; regs.w.ax = 0x4F05; /* g_use_win_a and g_gran_per_64k were set by INT 10h AX=4F01h */ regs.w.bx = g_use_win_a ? 0x0000 : 0x0001; regs.w.dx = b * g_gran_per_64k; int386(0x10, ®s, ®s); } /***************************************************************************** Note: This function does not support 4-plane framebuffer with banks i.e. VBE modes 0x104 (1024x768x4) or 0x106 (1280x1024x4) *****************************************************************************/ static void write_pixel_4p(unsigned x, unsigned y, color_t c) { unsigned char HUGE *ptr, mask, pmask, p; if(x >= g_wd || y >= g_ht) return; ptr = &g_raster[g_bytes_per_row * y + x / 8]; mask = 0x80 >> (x & 7); pmask = 1; for(p = 0; p < 4; p++) { set_plane(p); if(pmask & c) ptr[0] |= mask; else ptr[0] &= ~mask; pmask <<= 1; } } /***************************************************************************** this routine can be used for VGA mode 13h since the framebuffer is smaller than 64K in that mode *****************************************************************************/ #undef BPP #define BPP 1 static void write_pixel_8(unsigned x, unsigned y, color_t c) { unsigned long offset; uint8_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = &g_raster[offset]; *ptr = c; } /***************************************************************************** *****************************************************************************/ static void write_pixel_8b(unsigned x, unsigned y, color_t c) { unsigned long offset; uint8_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = &g_raster[offset & 0xFFFF]; offset >>= 16; set_bank((unsigned)offset); *ptr = c; } /***************************************************************************** these two functions also work for 15-bit color, but the color value is different Little-endian 16-bit value: b15......b12.........b8.b7.......b4..........b0 16-bit color (5-6-5): R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0 15-bit color (5-5-5): - R4 R3 R2 R1 R0 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0 *****************************************************************************/ #undef BPP #define BPP 2 static void write_pixel_16(unsigned x, unsigned y, color_t c) { unsigned long offset; uint16_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint16_t HUGE *)(&g_raster[offset]); *ptr = c; } /***************************************************************************** *****************************************************************************/ static void write_pixel_16b(unsigned x, unsigned y, color_t c) { unsigned long offset; uint16_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint16_t HUGE *)&g_raster[offset & 0xFFFF]; offset >>= 16; set_bank((unsigned)offset); *ptr = c; } /***************************************************************************** *****************************************************************************/ #undef BPP #define BPP 3 static void write_pixel_24(unsigned x, unsigned y, color_t c) { unsigned long offset; uint8_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint8_t HUGE *)(&g_raster[offset]); ptr[0] = c; c >>= 8; ptr[1] = c; c >>= 8; ptr[2] = c; } /***************************************************************************** *****************************************************************************/ static void write_pixel_24b(unsigned x, unsigned y, color_t c) { unsigned long offset; uint8_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint8_t HUGE *)(&g_raster[offset & 0xFFFF]); offset >>= 16; set_bank((unsigned)offset); ptr[0] = c; c >>= 8; ptr[1] = c; c >>= 8; ptr[2] = c; } /***************************************************************************** *****************************************************************************/ #undef BPP #define BPP 4 static void write_pixel_32(unsigned x, unsigned y, color_t c) { unsigned long offset; uint32_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint32_t HUGE *)(&g_raster[offset]); *ptr = c; } /***************************************************************************** *****************************************************************************/ static void write_pixel_32b(unsigned x, unsigned y, color_t c) { unsigned long offset; uint32_t HUGE *ptr; if(x >= g_wd || y >= g_ht) return; offset = g_bytes_per_row * y + x * BPP; ptr = (uint32_t HUGE *)(&g_raster[offset & 0xFFFF]); offset >>= 16; set_bank((unsigned)offset); *ptr = c; } /***************************************************************************** *****************************************************************************/ static void draw_x(unsigned mode_num) { union REGS regs; unsigned i; /* switch to VBE graphics mode */ regs.w.ax = 0x4F02; regs.w.bx = mode_num; int386(0x10, ®s, ®s); /* draw an 'X' */ for(i = 0; i < g_ht; i++) { g_write_pixel((g_wd - g_ht) / 2 + i, i, C_BLUE); g_write_pixel((g_ht + g_wd) / 2 - i, i, C_RED); } /* wait for key pressed */ if(getch() == 0) (void)getch(); /* go back to text mode */ regs.w.ax = 0x0003; int386(0x10, ®s, ®s); } /*---------------------------------------------------------------------------- VBE CODE ----------------------------------------------------------------------------*/ #include /* strcpy */ #include /* printf(), sscanf() */ /* structure used by INT 10h AX=4F00h */ #pragma pack(1) typedef struct { char sig[4]; uint8_t ver_minor; uint8_t ver_major; /* char far *oem_name; */ uint16_t oem_name_off, oem_name_seg; uint32_t capabilities; /* b1=1 for non-VGA board */ /* uint16_t far *mode_list; */ uint16_t mode_list_off, mode_list_seg; uint16_t vid_mem_size; /* in units of 64K */ char reserved1[492]; } vbe_info_t; /* structure used by INT 10h AX=4F01h */ #pragma pack(1) typedef struct { uint16_t mode_attrib; /* b5=1 for non-VGA mode */ uint8_t win_a_attrib; uint8_t win_b_attrib; uint16_t k_per_gran; uint16_t win_size; uint16_t win_a_seg; uint16_t win_b_seg; char reserved1[4]; /* this is not always the expected value; rounded up to the next power of 2 for some video boards: */ uint16_t bytes_per_row; /* OEM modes and VBE 1.2 only: */ uint16_t wd; uint16_t ht; uint8_t char_wd; uint8_t char_ht; uint8_t planes; uint8_t depth; uint8_t banks; uint8_t memory_model; uint8_t k_per_bank; uint8_t num_pages; /* ? */ char reserved2; /* VBE 1.2 only */ uint8_t red_width; uint8_t red_shift; uint8_t green_width; uint8_t green_shift; uint8_t blue_width; uint8_t blue_shift; char reserved3[3]; uint32_t lfb_adr; char reserved4[212]; } mode_info_t; #if defined(__DJGPP__) /* __djgpp_conventional_base, __djgpp_nearptr_enable() */ #include #include /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */ #include /* __dpmi_... */ #elif defined(__WATCOMC__) #include /* union REGS, struct SREGS, int386(), int386x() */ /* These work with CauseWay DOS extender only: */ #define dosmemget(off,n,buf) memcpy(buf, (void *)(off), n) #define dosmemput(buf,n,off) memcpy((void *)(off), buf, n) #define __djgpp_conventional_base 0 /* same layout as DJGPP */ typedef union { struct { uint32_t edi; uint32_t esi; uint32_t ebp; uint32_t res; uint32_t ebx; uint32_t edx; uint32_t ecx; uint32_t eax; } d; struct { uint16_t di, res_di; uint16_t si, res_si; uint16_t bp, res_bp; uint16_t res[2]; uint16_t bx, res_bx; uint16_t dx, res_dx; uint16_t cx, res_cx; uint16_t ax, res_ax; uint16_t flags, es, ds, fs, gs; uint16_t ip, cs, sp, ss; } x; struct { uint8_t res[16]; uint8_t bl, bh, res_bx[2]; uint8_t dl, dh, res_dx[2]; uint8_t cl, ch, res_cx[2]; uint8_t al, ah, res_ax[2]; } h; } __dpmi_regs; typedef struct { unsigned long size, address; } __dpmi_meminfo; /***************************************************************************** *****************************************************************************/ int __dpmi_int(unsigned int_num, __dpmi_regs *rm_regs) { struct SREGS sregs; union REGS regs; memset(&sregs, 0, sizeof(sregs)); memset(®s, 0, sizeof(regs)); sregs.es = FP_SEG(rm_regs); regs.x.edi = FP_OFF(rm_regs); regs.x.eax = 0x0300; /* simulate real-mode interrupt */ regs.x.ebx = int_num; /* AH=flags=0 */ regs.x.ecx = 0; /* number of words to copy between stacks */ int386x(0x31, ®s, ®s, &sregs); return regs.w.cflag ? -1 : 0; } /***************************************************************************** *****************************************************************************/ int __dpmi_allocate_dos_memory(unsigned paragraphs, int *sel) { union REGS regs; memset(®s, 0, sizeof(regs)); regs.w.ax = 0x0100; regs.w.bx = paragraphs; int386(0x31, ®s, ®s); *sel = regs.w.dx; return regs.w.cflag ? -1 : regs.w.ax; } /***************************************************************************** *****************************************************************************/ int __dpmi_free_dos_memory(int sel) { union REGS regs; memset(®s, 0, sizeof(regs)); regs.w.ax = 0x0101; regs.w.dx = sel; int386(0x31, ®s, ®s); return regs.w.cflag ? -1 : regs.w.ax; } /***************************************************************************** *****************************************************************************/ int __dpmi_physical_address_mapping(__dpmi_meminfo *info) { union REGS regs; memset(®s, 0, sizeof(regs)); regs.w.ax = 0x0800; regs.w.bx = info->address >> 16; regs.w.cx = info->address; regs.w.si = info->size >> 16; regs.w.di = info->size; int386(0x31, ®s, ®s); info->address = ((unsigned long)regs.w.bx << 16) | regs.w.cx; return regs.w.cflag ? -1 : 0; } #endif /***************************************************************************** *****************************************************************************/ static int rm_int_es_di(void *buf, unsigned buf_size, __dpmi_regs *regs, unsigned int_num) { int seg, sel; /* allocate conventional memory block (CMB) */ seg = __dpmi_allocate_dos_memory((buf_size + 15) / 16, &sel); if(seg == -1) { printf("Error: can't allocate conventional memory\n"); return -1; } /* copy buffer to CMB */ dosmemput(buf, buf_size, seg * 16); /* point real-mode ES and DI to CMB and do interrupt */ regs->x.es = seg; regs->x.di = 0; __dpmi_int(int_num, regs); /* copy CMB back to buffer */ dosmemget(seg * 16, buf_size, buf); /* free CMB */ __dpmi_free_dos_memory(sel); return 0; } /***************************************************************************** this function is needed by the graphics demo code; above *****************************************************************************/ static int get_mode_info(mode_info_t *info, unsigned mode_num) { if((info->mode_attrib & 0x10) == 0) { printf("Mode 0x%X is a text mode -- can't " "demo graphics\n", mode_num); return -1; } g_wd = info->wd; g_ht = info->ht; g_bytes_per_row = info->bytes_per_row; g_gran_per_64k = 64 / info->k_per_gran; /* linear framebuffer */ if(info->mode_attrib & 0x80) { __dpmi_meminfo mem_info; /* ...map it into the virtual address space */ mem_info.size = info->ht * (unsigned long)info->bytes_per_row; mem_info.address = info->lfb_adr; if(__dpmi_physical_address_mapping(&mem_info) == -1) { printf("Error mapping linear framebuffer " "for video mode 0x%X\n", mode_num); return -1; } g_raster = (unsigned char HUGE *)(mem_info.address + __djgpp_conventional_base); switch(info->depth) { case 4: g_write_pixel = write_pixel_4p; g_color_model = 0; break; case 8: g_write_pixel = write_pixel_8; g_color_model = 0; break; case 15: g_write_pixel = write_pixel_16; g_color_model = 1; break; case 16: g_write_pixel = write_pixel_16; g_color_model = 2; break; case 24: g_write_pixel = write_pixel_24; g_color_model = 3; break; case 32: g_write_pixel = write_pixel_32; g_color_model = 3; break; default: printf("This software does not support %u-" "bit graphics\n", info->depth); return -1; } } /* banked framebuffer */ else { if((info->win_a_attrib & 7) == 7) { g_raster = (unsigned char HUGE *) (info->win_a_seg * 16uL + __djgpp_conventional_base); g_use_win_a = 1; } else if((info->win_b_attrib & 7) == 7) { g_raster = (unsigned char HUGE *) (info->win_b_seg * 16uL + __djgpp_conventional_base); g_use_win_a = 0; } switch(info->depth) { case 4: g_write_pixel = write_pixel_4p; g_color_model = 0; break; case 8: g_write_pixel = write_pixel_8b; g_color_model = 0; break; case 15: g_write_pixel = write_pixel_16b; g_color_model = 1; break; case 16: g_write_pixel = write_pixel_16b; g_color_model = 2; break; case 24: g_write_pixel = write_pixel_24b; g_color_model = 3; break; case 32: g_write_pixel = write_pixel_32b; g_color_model = 3; break; default: printf("This software does not support %u-" "bit graphics\n", info->depth); return -1; } } return 0; } /***************************************************************************** *****************************************************************************/ int main(int arg_c, char *arg_v[]) { /* INT 10h AX=4F00h provides a list of modes. Copy it to a buffer before using it, because subsequent calls to INT 10h AX=4F01h may overwrite the mode list. Ralf Brown sez: "The list of supported video modes is stored in the reserved portion of the SuperVGA information record by some implementations, and it may thus be necessary to either copy the mode list or use a different buffer for all subsequent VESA calls" */ static uint16_t mode_list[128]; /**/ mode_info_t mode_info; vbe_info_t vbe_info; unsigned long linear; unsigned rmode, i; __dpmi_regs regs; #if defined(__DJGPP__) /* turn off data segment limit, for nearptr access */ if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR)) { if(!__djgpp_nearptr_enable()) { printf("Error: can not enable near pointer " "access (WinNT/2k/XP?)\n"); return 1; } } #endif if(arg_c == 1) rmode = 0; else if(arg_c == 2) { sscanf(arg_v[1], "%x", &rmode); if(rmode < 0x100) { printf("Illegal VBE mode number 0x%X\n", rmode); return 1; } } else { printf("Argument must be hex number of supported VBE mode\n" "Use 'vbe' with no arguments to list modes\n"); return 1; } /* detect VBE and get info */ strcpy(vbe_info.sig, "VBE2"); memset(®s, 0, sizeof(regs)); /* stupid Watcom C... */ regs.x.ax = 0x4F00; if(rm_int_es_di(&vbe_info, sizeof(vbe_info), ®s, 0x10)) return 1; if(regs.x.ax != 0x004F) { printf("INT 10h AX=4F00h failed (no VBE?)\n"); return 1; } /* display VBE info */ printf("VBE version %u.%u, %uK video memory\n", vbe_info.ver_major, vbe_info.ver_minor, vbe_info.vid_mem_size * 64); /* OEM name */ { unsigned char buf[256]; linear = vbe_info.oem_name_off + vbe_info.oem_name_seg * 16uL; dosmemget(linear, sizeof(buf), buf); printf("OEM name: %s\n", buf); } printf("Video board is"); if(vbe_info.capabilities & 0x02) printf(" NOT"); printf(" register-compatible with VGA in some modes\n"); /* list of supported mode numbers */ linear = vbe_info.mode_list_off + vbe_info.mode_list_seg * 16uL; dosmemget(linear, sizeof(mode_list), mode_list); /* walk mode list */ printf( " Banked Linear\n" "Mode Gran frame- frame-\n" "num- -ule buffer buffer\n" "ber Width Height Depth size address address\n" "---- ----- ------ ----- ---- -------- --------\n"); for(i = 0; i < sizeof(mode_list) / sizeof(mode_list[0]); i++) { unsigned mode_num; unsigned long mem; mode_num = mode_list[i]; if(mode_num == 0xFFFF) break; /* try getting info for LFB mode */ mode_num |= 0x4000; regs.x.cx = mode_num; regs.x.ax = 0x4F01; if(rm_int_es_di(&mode_info, sizeof(mode_info), ®s, 0x10)) continue; /* that didn't work; try banked FB */ if(regs.x.ax != 0x004F) { mode_num &= ~0x4000; regs.x.cx = mode_num; regs.x.ax = 0x4F01; if(rm_int_es_di(&mode_info, sizeof(mode_info), ®s, 0x10)) continue; if(regs.x.ax != 0x004F) { printf("INT 10h AX=4F01h CX=%04Xh failed\n", mode_num); continue; } } /* mode not supported (probably because there's not enough video memory) */ if((mode_info.mode_attrib & 0x01) == 0) continue; /* verify enough video memory */ mem = (unsigned long)mode_info.bytes_per_row * mode_info.ht; if(mem > vbe_info.vid_mem_size * 0x10000L) continue; /* display info about mode */ printf("%4X %5u %6u %5u", mode_num, mode_info.wd, mode_info.ht, mode_info.depth); /* bank-switching supported? */ if(mode_info.mode_attrib & 0x40) printf(" ----"); else printf(" %4u", mode_info.k_per_gran); /* banked framebuffer must exist, be readable, and be writable */ if((mode_info.win_a_attrib & 7) == 7) printf(" %8lX", mode_info.win_a_seg * 16uL); else if((mode_info.win_b_attrib & 7) == 7) printf(" %8lX", mode_info.win_b_seg * 16uL); else printf(" ????????"); /* LFB */ if(mode_info.mode_attrib & 0x80) printf(" %8lX", mode_info.lfb_adr); else printf(" --------"); printf("\n"); /* if this is the mode we want to demo, store info */ if(mode_num == rmode) { if(get_mode_info(&mode_info, rmode)) rmode = 0; } } /* user specified an unsupported mode */ if(rmode != 0 && g_wd == 0 && g_ht == 0) { printf("Mode 0x%X not supported\n", rmode); rmode = 0; } /* switch to graphics mode, draw an 'X', quit when key pressed */ if(rmode != 0) draw_x(rmode); return 0; }