+/*----------------------------------------------------------------------------\r
+blit() function for VBE 1.x banked framebuffer\r
+Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer/\r
+This code is public domain (no copyright).\r
+You can do whatever you want with it.\r
+\r
+Compile with any of the following DOS compilers:\r
+- Turbo C++ 1.0\r
+- Borland C++ 3.1\r
+- 16-bit Watcom C\r
+- DJGPP\r
+\r
+April 4, 2008\r
+- Initial release\r
+\r
+More notes:\r
+- The 'BPP' macro can be changed to 2, 3, or 4 to work with color\r
+ depths 16, 24, and 32, respectively. BPP can also be a run-\r
+ time variable; letting you use the same blit() function for\r
+ various color depths.\r
+- Some video boards (Intel i810 integrated video) report compliance\r
+ with VBE 2.x but _still_ do not support a linear framebuffer\r
+- Left as an exercise for the reader: it should be easy to convert\r
+ blit_mem_to_fb() to blit_fb_to_mem()\r
+- Left as an exercise for the reader: it should be easy to convert\r
+ blit_mem_to_fb() to blot_fb(); a function that draws a solid\r
+ filled rectangle. (Hint: replace MEMCPY()s with MEMSET()s)\r
+- To handle arbitrary blitting in a system with a banked framebuffer,\r
+ you need four functions: blit_mem_to_mem(), blit_mem_to_fb(),\r
+ blit_fb_to_mem(), and blit_fb_to_fb()\r
+----------------------------------------------------------------------------*/\r
+#include <string.h> /* memset(), [_f]memcpy() */\r
+/* EOF, FILE, fopen(), fwrite(), fputc(), fclose(), remove(), printf() */\r
+#include <stdio.h>\r
+#include <conio.h> /* getch(), outp[ortb]() */\r
+/* union REGS, struct SREGS, int86(), int86x() */\r
+#include <dos.h> /* FP_SEG(), FP_OFF(), MK_FP() */\r
+#if 0\r
+/* C99 fixed-width types */\r
+#include <stdint.h>\r
+#else\r
+typedef unsigned char uint8_t;\r
+typedef unsigned short uint16_t;\r
+typedef unsigned long uint32_t;\r
+#endif\r
+\r
+#if defined(__TURBOC__)\r
+#if __TURBOC__==0x401\r
+#error Sorry, 'huge' is broken in Turbo C++ 3.0\r
+#endif\r
+\r
+/* This code exposes some bugs in Borland C++ 3.1, so... */\r
+#if __TURBOC__==0x410\r
+#pragma option -Od /* ...disable all optimizations */\r
+#endif\r
+\r
+#define HUGE huge\r
+#define MEMCPY(D,S,N) _fmemcpy(D,S,N)\r
+#define MEMSET(D,C,N) _fmemset(D,C,N)\r
+\r
+#elif defined(__WATCOMC__)&&!defined(__386__)\r
+#define outportb(P,V) outp(P,V)\r
+#define HUGE huge\r
+#define MEMCPY(D,S,N) _fmemcpy(D,S,N)\r
+#define MEMSET(D,C,N) _fmemset(D,C,N)\r
+\r
+#elif defined(__DJGPP__)\r
+/* __djgpp_conventional_base, __djgpp_nearptr_enable() */\r
+#include <sys/nearptr.h>\r
+#include <dpmi.h> /* __dpmi_regs, __dpmi_int(), __dpmi_allocate_dos_memory() */\r
+#include <crt0.h> /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */\r
+\r
+#define __386__ 1\r
+#define HUGE /* nothing */\r
+#define MEMCPY(D,S,N) memcpy(D,S,N)\r
+#define MEMSET(D,C,N) memset(D,C,N)\r
+\r
+#else\r
+#error Sorry, unsupported compiler\r
+#endif\r
+\r
+#define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))\r
+\r
+/* structure used by INT 10h AX=4F01h */\r
+#pragma pack(1)\r
+typedef struct\r
+{\r
+ uint16_t mode_attrib; /* b5=1 for non-VGA mode */\r
+ uint8_t win_a_attrib;\r
+ uint8_t win_b_attrib;\r
+ uint16_t k_per_gran;\r
+ uint16_t win_size;\r
+ uint16_t win_a_seg;\r
+ uint16_t win_b_seg;\r
+ char reserved1[4];\r
+/* this is not always the expected value;\r
+rounded up to the next power of 2 for some video boards: */\r
+ uint16_t bytes_per_row;\r
+/* OEM modes and VBE 1.2 only: */\r
+ uint16_t wd;\r
+ uint16_t ht;\r
+ uint8_t char_wd;\r
+ uint8_t char_ht;\r
+ uint8_t planes;\r
+ uint8_t depth;\r
+ uint8_t banks;\r
+ uint8_t memory_model;\r
+ uint8_t k_per_bank;\r
+ uint8_t num_pages; /* ? */\r
+ char reserved2;\r
+/* VBE 1.2 only */\r
+ uint8_t red_width;\r
+ uint8_t red_shift;\r
+ uint8_t green_width;\r
+ uint8_t green_shift;\r
+ uint8_t blue_width;\r
+ uint8_t blue_shift;\r
+ char reserved3[3];\r
+ uint32_t lfb_adr;\r
+ char reserved4[212];\r
+} vbe_mode_info_t;\r
+\r
+typedef struct\r
+{\r
+ unsigned wd, ht;\r
+ unsigned long bytes_per_row;\r
+ unsigned char HUGE *raster;\r
+} img_t;\r
+\r
+typedef struct\r
+{\r
+ int src_x, src_y, dst_x, dst_y;\r
+ unsigned wd, ht;\r
+} clip_t;\r
+\r
+static img_t g_fb;\r
+static unsigned g_use_win_a, g_gran_per_64k;\r
+/*****************************************************************************\r
+no _fmemcpy() in Turbo C++ 1.0\r
+*****************************************************************************/\r
+#if defined(__TURBOC__)\r
+#if __TURBOC__<0x300\r
+void far * far _fmemcpy(void far *dst_ptr, const void far *src_ptr,\r
+ unsigned n)\r
+{\r
+ unsigned char huge *dst = dst_ptr;\r
+ unsigned char huge *src = src_ptr;\r
+\r
+ for(; n != 0; n--)\r
+ {\r
+ *dst = *src;\r
+ dst++;\r
+ src++;\r
+ }\r
+ return dst_ptr;\r
+}\r
+#endif\r
+#endif\r
+/*****************************************************************************\r
+*****************************************************************************/\r
+static void set_bank(unsigned b)\r
+{\r
+ static unsigned curr_bank = -1u;\r
+/**/\r
+ union REGS regs;\r
+\r
+ if(b == curr_bank)\r
+ return;\r
+ curr_bank = b;\r
+ regs.x.ax = 0x4F05;\r
+/* g_use_win_a and g_gran_per_64k were set by INT 10h AX=4F01h */\r
+ regs.x.bx = g_use_win_a ? 0x0000 : 0x0001;\r
+ regs.x.dx = b * g_gran_per_64k;\r
+ int86(0x10, ®s, ®s);\r
+}\r
+/*****************************************************************************\r
+If using Borland C, compile this without optimizations.\r
+Even without optimizations, it probably won't work with Turbo C++ 3.0\r
+*****************************************************************************/\r
+#define BPP 1 /* bytes per pixel */\r
+static void blit_mem_to_fb(img_t *src_img, clip_t *clip)\r
+{\r
+ unsigned bank, y, row_with_bank_switch, max_y;\r
+ unsigned char HUGE *src;\r
+ unsigned long off32;\r
+ uint16_t off16, i;\r
+\r
+ src = src_img->raster +\r
+ src_img->bytes_per_row * clip->src_y + clip->src_x * BPP;\r
+/* calculate 32-bit offset into framebuffer corresponding to\r
+(clip->dst_x, clip->dst_y) */\r
+ off32 = g_fb.bytes_per_row * clip->dst_y + clip->dst_x * BPP;\r
+/* use low 16 bits for offset into 64K bank; use top 16 bits for bank */\r
+ off16 = (uint16_t)off32;\r
+ off32 >>= 16;\r
+ bank = (uint16_t)off32;\r
+/* set bank and increment bank number */\r
+ set_bank(bank);\r
+ bank++;\r
+ for(y = clip->dst_y; ; )\r
+ {\r
+/* in which row does the next bank-switch occur? */\r
+ off32 = bank;\r
+ off32 <<= 16;\r
+ row_with_bank_switch = (unsigned)\r
+ (off32 / g_fb.bytes_per_row);\r
+/* blit until we reach that row or until we reach (clip->dst_y + clip->ht) */\r
+ max_y = MIN(clip->dst_y + clip->ht, row_with_bank_switch);\r
+ for(; y < max_y; y++)\r
+ {\r
+ MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);\r
+ off16 += g_fb.bytes_per_row;\r
+ src += src_img->bytes_per_row;\r
+ }\r
+/* exit now if done */\r
+ if(y >= clip->dst_y + clip->ht)\r
+ break;\r
+/* else it's a row with bank switch. There are 3 possibilities:\r
+line of pixels in this row starts AFTER bank switch */\r
+ if(off16 < g_fb.bytes_per_row) /* overflowed 64K (clever!) */\r
+ {\r
+ set_bank(bank);\r
+ MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);\r
+ }\r
+/* line of pixels in this row ends BEFORE bank switch */\r
+ else if(off16 + (unsigned long)clip->wd * BPP <= 0x10000L)\r
+ {\r
+ MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);\r
+ set_bank(bank);\r
+ }\r
+/* line of pixels in this row STRADDLES bank switch */\r
+ else\r
+ {\r
+ i = (uint16_t)(0x10000uL - off16);\r
+ MEMCPY(g_fb.raster + off16, src + 0,\r
+ i);\r
+ set_bank(bank);\r
+ MEMCPY(g_fb.raster + 0 , src + i,\r
+ clip->wd * BPP - i);\r
+ }\r
+ off16 += g_fb.bytes_per_row;\r
+ src += src_img->bytes_per_row;\r
+ bank++;\r
+ y++;\r
+ }\r
+}\r
+/*****************************************************************************\r
+*****************************************************************************/\r
+int main(void)\r
+{\r
+ static const unsigned vbe_mode_num = 0x101; /* 640x480x256 */\r
+/* 14x14 test bitmap */\r
+ static unsigned char raster[] =\r
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x02\x02\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x03\x03\x03\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x04\x04\x04\x04\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x05\x05\x05\x05\x05\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x06\x06\x06\x06\x06\x06\x06\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x07\x07\x07\x07\x07\x07\x07\x07\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x09\x0A\x0B\x0C\x0D"\r
+ "\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x0A\x0B\x0C\x0D"\r
+ "\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0B\x0C\x0D"\r
+ "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0C\x0D"\r
+ "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0D"\r
+ "\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D";\r
+/**/\r
+ union REGS regs;\r
+ unsigned i;\r
+ clip_t clip;\r
+ img_t img;\r
+\r
+#if !defined(__386__)\r
+ static vbe_mode_info_t vbe_mode_info;\r
+ struct SREGS sregs;\r
+\r
+/* get info for VBE mode */\r
+ regs.x.ax = 0x4F01;\r
+ regs.x.cx = vbe_mode_num;\r
+ sregs.es = FP_SEG(&vbe_mode_info);\r
+ regs.x.di = FP_OFF(&vbe_mode_info);\r
+ int86x(0x10, ®s, ®s, &sregs);\r
+ if(regs.x.ax != 0x004F)\r
+ {\r
+ printf("Error getting info for VBE mode 0x%X\n",\r
+ vbe_mode_num);\r
+ return 1;\r
+ }\r
+/* init g_fb */\r
+ g_fb.wd = vbe_mode_info.wd;\r
+ g_fb.ht = vbe_mode_info.ht;\r
+ g_fb.bytes_per_row = vbe_mode_info.bytes_per_row;\r
+ g_gran_per_64k = 64 / vbe_mode_info.k_per_gran;\r
+ if(vbe_mode_info.win_a_attrib == 7)\r
+ {\r
+ g_fb.raster = (unsigned char HUGE *)\r
+ MK_FP(vbe_mode_info.win_a_seg, 0);\r
+ g_use_win_a = 1;\r
+ }\r
+ else if(vbe_mode_info.win_b_attrib == 7)\r
+ {\r
+ g_fb.raster = (unsigned char HUGE *)\r
+ MK_FP(vbe_mode_info.win_b_seg, 0);\r
+ g_use_win_a = 0;\r
+ }\r
+ else\r
+ {\r
+ printf("Error locating banked framebuffer "\r
+ "for VBE mode 0x%X\n", vbe_mode_num);\r
+ return -1;\r
+ }\r
+#else /* if defined(__DJGPP__) */\r
+ vbe_mode_info_t *vbe_mode_info;\r
+ int conv_mem_seg, conv_mem_sel;\r
+ __dpmi_regs dregs;\r
+\r
+/* turn off data segment limit, for nearptr access */\r
+ if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))\r
+ {\r
+ if(!__djgpp_nearptr_enable())\r
+ {\r
+ printf("Error: can't enable near pointer "\r
+ "access to framebuffer (WinNT/2k/XP?)\n");\r
+ return 1;\r
+ }\r
+ }\r
+/* allocate conventional memory for INT 10h AX=4F01h buffer */\r
+ conv_mem_seg = __dpmi_allocate_dos_memory(256 / 16, &conv_mem_sel);\r
+ if(conv_mem_seg == -1)\r
+ {\r
+ printf("Error: can't allocate conventional memory\n");\r
+ return 1;\r
+ }\r
+ vbe_mode_info = (vbe_mode_info_t *)\r
+ (conv_mem_seg * 16uL + __djgpp_conventional_base);\r
+printf("vbe_mode_info: sel=0x%X, real-mode seg=0x%X, linear=0x%lX, near=0x%p\n",\r
+ conv_mem_sel, conv_mem_seg, conv_mem_seg * 16uL, vbe_mode_info);\r
+/* get info for VBE mode */\r
+ dregs.x.ax = 0x4F01;\r
+ dregs.x.cx = vbe_mode_num;\r
+ dregs.x.es = conv_mem_seg;\r
+ dregs.x.di = 0;\r
+ __dpmi_int(0x10, &dregs);\r
+ if(dregs.x.ax != 0x004F)\r
+ {\r
+ printf("Error getting info for VBE mode 0x%X\n",\r
+ vbe_mode_num);\r
+ return 1;\r
+ }\r
+/* init g_fb */\r
+ g_fb.wd = vbe_mode_info->wd;\r
+ g_fb.ht = vbe_mode_info->ht;\r
+ g_fb.bytes_per_row = vbe_mode_info->bytes_per_row;\r
+ g_gran_per_64k = 64 / vbe_mode_info->k_per_gran;\r
+ if(vbe_mode_info->win_a_attrib == 7)\r
+ {\r
+ g_fb.raster = (unsigned char HUGE *)\r
+ (vbe_mode_info->win_a_seg * 16uL +\r
+ __djgpp_conventional_base);\r
+ g_use_win_a = 1;\r
+ }\r
+ else if(vbe_mode_info->win_b_attrib == 7)\r
+ {\r
+ g_fb.raster = (unsigned char HUGE *)\r
+ (vbe_mode_info->win_b_seg * 16uL +\r
+ __djgpp_conventional_base);\r
+ g_use_win_a = 0;\r
+ }\r
+ else\r
+ {\r
+ printf("Error locating banked framebuffer "\r
+ "for VBE mode 0x%X\n", vbe_mode_num);\r
+ return -1;\r
+ }\r
+#endif\r
+/* init img */\r
+ img.wd = 14;\r
+ img.ht = 14;\r
+ img.bytes_per_row = img.wd;\r
+ img.raster = raster;\r
+/* init clip_t */\r
+ clip.wd = img.wd;\r
+ clip.ht = img.ht;\r
+ clip.src_x = 0;\r
+ clip.src_y = 0;\r
+/* set graphics mode */\r
+ regs.x.bx = vbe_mode_num;\r
+ regs.x.ax = 0x4F02;\r
+ int86(0x10, ®s, ®s);\r
+/* green palette */\r
+#define VGA_DAC_WRITE_INDEX 0x3C8\r
+#define VGA_DAC_DATA 0x3C9\r
+ outportb(VGA_DAC_WRITE_INDEX, 0);\r
+ for(i = 0; i < 16; i++)\r
+ {\r
+ outportb(VGA_DAC_DATA, /* red= */0 >> 2);\r
+ outportb(VGA_DAC_DATA, /* green= */i * 3);\r
+ outportb(VGA_DAC_DATA, /* blue= */0 >> 2);\r
+ }\r
+/* test banked blit function */\r
+ clip.dst_y = 0;\r
+ for(; clip.dst_y + clip.ht < g_fb.ht; clip.dst_y += clip.ht)\r
+ {\r
+ clip.dst_x = 0;\r
+ for(; clip.dst_x + clip.wd < g_fb.wd; clip.dst_x += clip.wd)\r
+ blit_mem_to_fb(&img, &clip);\r
+ }\r
+ if(getch() == 0)\r
+ (void)getch();\r
+/* set text mode */\r
+ regs.x.ax = 0x0003;\r
+ int86(0x10, ®s, ®s);\r
+ return 0;\r
+}\r