1 /*----------------------------------------------------------------------------
\r
2 blit() function for VBE 1.x banked framebuffer
\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
7 Compile with any of the following DOS compilers:
\r
17 - The 'BPP' macro can be changed to 2, 3, or 4 to work with color
\r
18 depths 16, 24, and 32, respectively. BPP can also be a run-
\r
19 time variable; letting you use the same blit() function for
\r
20 various color depths.
\r
21 - Some video boards (Intel i810 integrated video) report compliance
\r
22 with VBE 2.x but _still_ do not support a linear framebuffer
\r
23 - Left as an exercise for the reader: it should be easy to convert
\r
24 blit_mem_to_fb() to blit_fb_to_mem()
\r
25 - Left as an exercise for the reader: it should be easy to convert
\r
26 blit_mem_to_fb() to blot_fb(); a function that draws a solid
\r
27 filled rectangle. (Hint: replace MEMCPY()s with MEMSET()s)
\r
28 - To handle arbitrary blitting in a system with a banked framebuffer,
\r
29 you need four functions: blit_mem_to_mem(), blit_mem_to_fb(),
\r
30 blit_fb_to_mem(), and blit_fb_to_fb()
\r
31 ----------------------------------------------------------------------------*/
\r
32 #include <string.h> /* memset(), [_f]memcpy() */
\r
33 /* EOF, FILE, fopen(), fwrite(), fputc(), fclose(), remove(), printf() */
\r
35 #include <conio.h> /* getch(), outp[ortb]() */
\r
36 /* union REGS, struct SREGS, int86(), int86x() */
\r
37 #include <dos.h> /* FP_SEG(), FP_OFF(), MK_FP() */
\r
39 /* C99 fixed-width types */
\r
42 typedef unsigned char uint8_t;
\r
43 typedef unsigned short uint16_t;
\r
44 typedef unsigned long uint32_t;
\r
47 #if defined(__TURBOC__)
\r
48 #if __TURBOC__==0x401
\r
49 #error Sorry, 'huge' is broken in Turbo C++ 3.0
\r
52 /* This code exposes some bugs in Borland C++ 3.1, so... */
\r
53 #if __TURBOC__==0x410
\r
54 #pragma option -Od /* ...disable all optimizations */
\r
58 #define MEMCPY(D,S,N) _fmemcpy(D,S,N)
\r
59 #define MEMSET(D,C,N) _fmemset(D,C,N)
\r
61 #elif defined(__WATCOMC__)&&!defined(__386__)
\r
62 #define outportb(P,V) outp(P,V)
\r
64 #define MEMCPY(D,S,N) _fmemcpy(D,S,N)
\r
65 #define MEMSET(D,C,N) _fmemset(D,C,N)
\r
67 #elif defined(__DJGPP__)
\r
68 /* __djgpp_conventional_base, __djgpp_nearptr_enable() */
\r
69 #include <sys/nearptr.h>
\r
70 #include <dpmi.h> /* __dpmi_regs, __dpmi_int(), __dpmi_allocate_dos_memory() */
\r
71 #include <crt0.h> /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */
\r
74 #define HUGE /* nothing */
\r
75 #define MEMCPY(D,S,N) memcpy(D,S,N)
\r
76 #define MEMSET(D,C,N) memset(D,C,N)
\r
79 #error Sorry, unsupported compiler
\r
82 #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
\r
84 /* structure used by INT 10h AX=4F01h */
\r
88 uint16_t mode_attrib; /* b5=1 for non-VGA mode */
\r
89 uint8_t win_a_attrib;
\r
90 uint8_t win_b_attrib;
\r
91 uint16_t k_per_gran;
\r
96 /* this is not always the expected value;
\r
97 rounded up to the next power of 2 for some video boards: */
\r
98 uint16_t bytes_per_row;
\r
99 /* OEM modes and VBE 1.2 only: */
\r
107 uint8_t memory_model;
\r
108 uint8_t k_per_bank;
\r
109 uint8_t num_pages; /* ? */
\r
114 uint8_t green_width;
\r
115 uint8_t green_shift;
\r
116 uint8_t blue_width;
\r
117 uint8_t blue_shift;
\r
120 char reserved4[212];
\r
126 unsigned long bytes_per_row;
\r
127 unsigned char HUGE *raster;
\r
132 int src_x, src_y, dst_x, dst_y;
\r
137 static unsigned g_use_win_a, g_gran_per_64k;
\r
138 /*****************************************************************************
\r
139 no _fmemcpy() in Turbo C++ 1.0
\r
140 *****************************************************************************/
\r
141 #if defined(__TURBOC__)
\r
142 #if __TURBOC__<0x300
\r
143 void far * far _fmemcpy(void far *dst_ptr, const void far *src_ptr,
\r
146 unsigned char huge *dst = dst_ptr;
\r
147 unsigned char huge *src = src_ptr;
\r
159 /*****************************************************************************
\r
160 *****************************************************************************/
\r
161 static void set_bank(unsigned b)
\r
163 static unsigned curr_bank = -1u;
\r
170 regs.x.ax = 0x4F05;
\r
171 /* g_use_win_a and g_gran_per_64k were set by INT 10h AX=4F01h */
\r
172 regs.x.bx = g_use_win_a ? 0x0000 : 0x0001;
\r
173 regs.x.dx = b * g_gran_per_64k;
\r
174 int86(0x10, ®s, ®s);
\r
176 /*****************************************************************************
\r
177 If using Borland C, compile this without optimizations.
\r
178 Even without optimizations, it probably won't work with Turbo C++ 3.0
\r
179 *****************************************************************************/
\r
180 #define BPP 1 /* bytes per pixel */
\r
181 static void blit_mem_to_fb(img_t *src_img, clip_t *clip)
\r
183 unsigned bank, y, row_with_bank_switch, max_y;
\r
184 unsigned char HUGE *src;
\r
185 unsigned long off32;
\r
188 src = src_img->raster +
\r
189 src_img->bytes_per_row * clip->src_y + clip->src_x * BPP;
\r
190 /* calculate 32-bit offset into framebuffer corresponding to
\r
191 (clip->dst_x, clip->dst_y) */
\r
192 off32 = g_fb.bytes_per_row * clip->dst_y + clip->dst_x * BPP;
\r
193 /* use low 16 bits for offset into 64K bank; use top 16 bits for bank */
\r
194 off16 = (uint16_t)off32;
\r
196 bank = (uint16_t)off32;
\r
197 /* set bank and increment bank number */
\r
200 for(y = clip->dst_y; ; )
\r
202 /* in which row does the next bank-switch occur? */
\r
205 row_with_bank_switch = (unsigned)
\r
206 (off32 / g_fb.bytes_per_row);
\r
207 /* blit until we reach that row or until we reach (clip->dst_y + clip->ht) */
\r
208 max_y = MIN(clip->dst_y + clip->ht, row_with_bank_switch);
\r
209 for(; y < max_y; y++)
\r
211 MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);
\r
212 off16 += g_fb.bytes_per_row;
\r
213 src += src_img->bytes_per_row;
\r
215 /* exit now if done */
\r
216 if(y >= clip->dst_y + clip->ht)
\r
218 /* else it's a row with bank switch. There are 3 possibilities:
\r
219 line of pixels in this row starts AFTER bank switch */
\r
220 if(off16 < g_fb.bytes_per_row) /* overflowed 64K (clever!) */
\r
223 MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);
\r
225 /* line of pixels in this row ends BEFORE bank switch */
\r
226 else if(off16 + (unsigned long)clip->wd * BPP <= 0x10000L)
\r
228 MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);
\r
231 /* line of pixels in this row STRADDLES bank switch */
\r
234 i = (uint16_t)(0x10000uL - off16);
\r
235 MEMCPY(g_fb.raster + off16, src + 0,
\r
238 MEMCPY(g_fb.raster + 0 , src + i,
\r
239 clip->wd * BPP - i);
\r
241 off16 += g_fb.bytes_per_row;
\r
242 src += src_img->bytes_per_row;
\r
247 /*****************************************************************************
\r
248 *****************************************************************************/
\r
251 static const unsigned vbe_mode_num = 0x101; /* 640x480x256 */
\r
252 /* 14x14 test bitmap */
\r
253 static unsigned char raster[] =
\r
254 "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
255 "\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
256 "\x02\x02\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
257 "\x03\x03\x03\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
258 "\x04\x04\x04\x04\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
259 "\x05\x05\x05\x05\x05\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
260 "\x06\x06\x06\x06\x06\x06\x06\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
261 "\x07\x07\x07\x07\x07\x07\x07\x07\x08\x09\x0A\x0B\x0C\x0D"
\r
262 "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x09\x0A\x0B\x0C\x0D"
\r
263 "\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x0A\x0B\x0C\x0D"
\r
264 "\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0A\x0B\x0C\x0D"
\r
265 "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0C\x0D"
\r
266 "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0D"
\r
267 "\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D\x0D";
\r
274 #if !defined(__386__)
\r
275 static vbe_mode_info_t vbe_mode_info;
\r
276 struct SREGS sregs;
\r
278 /* get info for VBE mode */
\r
279 regs.x.ax = 0x4F01;
\r
280 regs.x.cx = vbe_mode_num;
\r
281 sregs.es = FP_SEG(&vbe_mode_info);
\r
282 regs.x.di = FP_OFF(&vbe_mode_info);
\r
283 int86x(0x10, ®s, ®s, &sregs);
\r
284 if(regs.x.ax != 0x004F)
\r
286 printf("Error getting info for VBE mode 0x%X\n",
\r
291 g_fb.wd = vbe_mode_info.wd;
\r
292 g_fb.ht = vbe_mode_info.ht;
\r
293 g_fb.bytes_per_row = vbe_mode_info.bytes_per_row;
\r
294 g_gran_per_64k = 64 / vbe_mode_info.k_per_gran;
\r
295 if(vbe_mode_info.win_a_attrib == 7)
\r
297 g_fb.raster = (unsigned char HUGE *)
\r
298 MK_FP(vbe_mode_info.win_a_seg, 0);
\r
301 else if(vbe_mode_info.win_b_attrib == 7)
\r
303 g_fb.raster = (unsigned char HUGE *)
\r
304 MK_FP(vbe_mode_info.win_b_seg, 0);
\r
309 printf("Error locating banked framebuffer "
\r
310 "for VBE mode 0x%X\n", vbe_mode_num);
\r
313 #else /* if defined(__DJGPP__) */
\r
314 vbe_mode_info_t *vbe_mode_info;
\r
315 int conv_mem_seg, conv_mem_sel;
\r
318 /* turn off data segment limit, for nearptr access */
\r
319 if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))
\r
321 if(!__djgpp_nearptr_enable())
\r
323 printf("Error: can't enable near pointer "
\r
324 "access to framebuffer (WinNT/2k/XP?)\n");
\r
328 /* allocate conventional memory for INT 10h AX=4F01h buffer */
\r
329 conv_mem_seg = __dpmi_allocate_dos_memory(256 / 16, &conv_mem_sel);
\r
330 if(conv_mem_seg == -1)
\r
332 printf("Error: can't allocate conventional memory\n");
\r
335 vbe_mode_info = (vbe_mode_info_t *)
\r
336 (conv_mem_seg * 16uL + __djgpp_conventional_base);
\r
337 printf("vbe_mode_info: sel=0x%X, real-mode seg=0x%X, linear=0x%lX, near=0x%p\n",
\r
338 conv_mem_sel, conv_mem_seg, conv_mem_seg * 16uL, vbe_mode_info);
\r
339 /* get info for VBE mode */
\r
340 dregs.x.ax = 0x4F01;
\r
341 dregs.x.cx = vbe_mode_num;
\r
342 dregs.x.es = conv_mem_seg;
\r
344 __dpmi_int(0x10, &dregs);
\r
345 if(dregs.x.ax != 0x004F)
\r
347 printf("Error getting info for VBE mode 0x%X\n",
\r
352 g_fb.wd = vbe_mode_info->wd;
\r
353 g_fb.ht = vbe_mode_info->ht;
\r
354 g_fb.bytes_per_row = vbe_mode_info->bytes_per_row;
\r
355 g_gran_per_64k = 64 / vbe_mode_info->k_per_gran;
\r
356 if(vbe_mode_info->win_a_attrib == 7)
\r
358 g_fb.raster = (unsigned char HUGE *)
\r
359 (vbe_mode_info->win_a_seg * 16uL +
\r
360 __djgpp_conventional_base);
\r
363 else if(vbe_mode_info->win_b_attrib == 7)
\r
365 g_fb.raster = (unsigned char HUGE *)
\r
366 (vbe_mode_info->win_b_seg * 16uL +
\r
367 __djgpp_conventional_base);
\r
372 printf("Error locating banked framebuffer "
\r
373 "for VBE mode 0x%X\n", vbe_mode_num);
\r
380 img.bytes_per_row = img.wd;
\r
381 img.raster = raster;
\r
387 /* set graphics mode */
\r
388 regs.x.bx = vbe_mode_num;
\r
389 regs.x.ax = 0x4F02;
\r
390 int86(0x10, ®s, ®s);
\r
391 /* green palette */
\r
392 #define VGA_DAC_WRITE_INDEX 0x3C8
\r
393 #define VGA_DAC_DATA 0x3C9
\r
394 outportb(VGA_DAC_WRITE_INDEX, 0);
\r
395 for(i = 0; i < 16; i++)
\r
397 outportb(VGA_DAC_DATA, /* red= */0 >> 2);
\r
398 outportb(VGA_DAC_DATA, /* green= */i * 3);
\r
399 outportb(VGA_DAC_DATA, /* blue= */0 >> 2);
\r
401 /* test banked blit function */
\r
403 for(; clip.dst_y + clip.ht < g_fb.ht; clip.dst_y += clip.ht)
\r
406 for(; clip.dst_x + clip.wd < g_fb.wd; clip.dst_x += clip.wd)
\r
407 blit_mem_to_fb(&img, &clip);
\r
411 /* set text mode */
\r
412 regs.x.ax = 0x0003;
\r
413 int86(0x10, ®s, ®s);
\r