]> 4ch.mooo.com Git - 16.git/blob - 16/BANKBLIT.C
new file: 16/BANKBLIT.C
[16.git] / 16 / BANKBLIT.C
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
6 \r
7 Compile with any of the following DOS compilers:\r
8 - Turbo C++ 1.0\r
9 - Borland C++ 3.1\r
10 - 16-bit Watcom C\r
11 - DJGPP\r
12 \r
13 April 4, 2008\r
14 - Initial release\r
15 \r
16 More notes:\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
34 #include <stdio.h>\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
38 #if 0\r
39 /* C99 fixed-width types */\r
40 #include <stdint.h>\r
41 #else\r
42 typedef unsigned char   uint8_t;\r
43 typedef unsigned short  uint16_t;\r
44 typedef unsigned long   uint32_t;\r
45 #endif\r
46 \r
47 #if defined(__TURBOC__)\r
48 #if __TURBOC__==0x401\r
49 #error Sorry, 'huge' is broken in Turbo C++ 3.0\r
50 #endif\r
51 \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
55 #endif\r
56 \r
57 #define HUGE            huge\r
58 #define MEMCPY(D,S,N)   _fmemcpy(D,S,N)\r
59 #define MEMSET(D,C,N)   _fmemset(D,C,N)\r
60 \r
61 #elif defined(__WATCOMC__)&&!defined(__386__)\r
62 #define outportb(P,V)   outp(P,V)\r
63 #define HUGE            huge\r
64 #define MEMCPY(D,S,N)   _fmemcpy(D,S,N)\r
65 #define MEMSET(D,C,N)   _fmemset(D,C,N)\r
66 \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
72 \r
73 #define __386__         1\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
77 \r
78 #else\r
79 #error Sorry, unsupported compiler\r
80 #endif\r
81 \r
82 #define MIN(X,Y)        (((X) < (Y)) ? (X) : (Y))\r
83 \r
84 /* structure used by INT 10h AX=4F01h */\r
85 #pragma pack(1)\r
86 typedef struct\r
87 {\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
92         uint16_t win_size;\r
93         uint16_t win_a_seg;\r
94         uint16_t win_b_seg;\r
95         char reserved1[4];\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
100         uint16_t wd;\r
101         uint16_t ht;\r
102         uint8_t char_wd;\r
103         uint8_t char_ht;\r
104         uint8_t planes;\r
105         uint8_t depth;\r
106         uint8_t banks;\r
107         uint8_t memory_model;\r
108         uint8_t k_per_bank;\r
109         uint8_t num_pages;       /* ? */\r
110         char reserved2;\r
111 /* VBE 1.2 only */\r
112         uint8_t red_width;\r
113         uint8_t red_shift;\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
118         char reserved3[3];\r
119         uint32_t lfb_adr;\r
120         char reserved4[212];\r
121 } vbe_mode_info_t;\r
122 \r
123 typedef struct\r
124 {\r
125         unsigned wd, ht;\r
126         unsigned long bytes_per_row;\r
127         unsigned char HUGE *raster;\r
128 } img_t;\r
129 \r
130 typedef struct\r
131 {\r
132         int src_x, src_y, dst_x, dst_y;\r
133         unsigned wd, ht;\r
134 } clip_t;\r
135 \r
136 static img_t g_fb;\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
144                 unsigned n)\r
145 {\r
146         unsigned char huge *dst = dst_ptr;\r
147         unsigned char huge *src = src_ptr;\r
148 \r
149         for(; n != 0; n--)\r
150         {\r
151                 *dst = *src;\r
152                 dst++;\r
153                 src++;\r
154         }\r
155         return dst_ptr;\r
156 }\r
157 #endif\r
158 #endif\r
159 /*****************************************************************************\r
160 *****************************************************************************/\r
161 static void set_bank(unsigned b)\r
162 {\r
163         static unsigned curr_bank = -1u;\r
164 /**/\r
165         union REGS regs;\r
166 \r
167         if(b == curr_bank)\r
168                 return;\r
169         curr_bank = b;\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, &regs, &regs);\r
175 }\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
182 {\r
183         unsigned bank, y, row_with_bank_switch, max_y;\r
184         unsigned char HUGE *src;\r
185         unsigned long off32;\r
186         uint16_t off16, i;\r
187 \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
195         off32 >>= 16;\r
196         bank = (uint16_t)off32;\r
197 /* set bank and increment bank number */\r
198         set_bank(bank);\r
199         bank++;\r
200         for(y = clip->dst_y; ; )\r
201         {\r
202 /* in which row does the next bank-switch occur? */\r
203                 off32 = bank;\r
204                 off32 <<= 16;\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
210                 {\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
214                 }\r
215 /* exit now if done */\r
216                 if(y >= clip->dst_y + clip->ht)\r
217                         break;\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
221                 {\r
222                         set_bank(bank);\r
223                         MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);\r
224                 }\r
225 /* line of pixels in this row ends BEFORE bank switch */\r
226                 else if(off16 + (unsigned long)clip->wd * BPP <= 0x10000L)\r
227                 {\r
228                         MEMCPY(g_fb.raster + off16, src, clip->wd * BPP);\r
229                         set_bank(bank);\r
230                 }\r
231 /* line of pixels in this row STRADDLES bank switch */\r
232                 else\r
233                 {\r
234                         i = (uint16_t)(0x10000uL - off16);\r
235                         MEMCPY(g_fb.raster + off16, src + 0,\r
236                                 i);\r
237                         set_bank(bank);\r
238                         MEMCPY(g_fb.raster + 0    , src + i,\r
239                                 clip->wd * BPP - i);\r
240                 }\r
241                 off16 += g_fb.bytes_per_row;\r
242                 src += src_img->bytes_per_row;\r
243                 bank++;\r
244                 y++;\r
245         }\r
246 }\r
247 /*****************************************************************************\r
248 *****************************************************************************/\r
249 int main(void)\r
250 {\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
268 /**/\r
269         union REGS regs;\r
270         unsigned i;\r
271         clip_t clip;\r
272         img_t img;\r
273 \r
274 #if !defined(__386__)\r
275         static vbe_mode_info_t vbe_mode_info;\r
276         struct SREGS sregs;\r
277 \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, &regs, &regs, &sregs);\r
284         if(regs.x.ax != 0x004F)\r
285         {\r
286                 printf("Error getting info for VBE mode 0x%X\n",\r
287                         vbe_mode_num);\r
288                 return 1;\r
289         }\r
290 /* init g_fb */\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
296         {\r
297                 g_fb.raster = (unsigned char HUGE *)\r
298                         MK_FP(vbe_mode_info.win_a_seg, 0);\r
299                 g_use_win_a = 1;\r
300         }\r
301         else if(vbe_mode_info.win_b_attrib == 7)\r
302         {\r
303                 g_fb.raster = (unsigned char HUGE *)\r
304                         MK_FP(vbe_mode_info.win_b_seg, 0);\r
305                 g_use_win_a = 0;\r
306         }\r
307         else\r
308         {\r
309                 printf("Error locating banked framebuffer "\r
310                         "for VBE mode 0x%X\n", vbe_mode_num);\r
311                 return -1;\r
312         }\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
316         __dpmi_regs dregs;\r
317 \r
318 /* turn off data segment limit, for nearptr access */\r
319         if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))\r
320         {\r
321                 if(!__djgpp_nearptr_enable())\r
322                 {\r
323                         printf("Error: can't enable near pointer "\r
324                                 "access to framebuffer (WinNT/2k/XP?)\n");\r
325                         return 1;\r
326                 }\r
327         }\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
331         {\r
332                 printf("Error: can't allocate conventional memory\n");\r
333                 return 1;\r
334         }\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
343         dregs.x.di = 0;\r
344         __dpmi_int(0x10, &dregs);\r
345         if(dregs.x.ax != 0x004F)\r
346         {\r
347                 printf("Error getting info for VBE mode 0x%X\n",\r
348                         vbe_mode_num);\r
349                 return 1;\r
350         }\r
351 /* init g_fb */\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
357         {\r
358                 g_fb.raster = (unsigned char HUGE *)\r
359                         (vbe_mode_info->win_a_seg * 16uL +\r
360                                 __djgpp_conventional_base);\r
361                 g_use_win_a = 1;\r
362         }\r
363         else if(vbe_mode_info->win_b_attrib == 7)\r
364         {\r
365                 g_fb.raster = (unsigned char HUGE *)\r
366                         (vbe_mode_info->win_b_seg * 16uL +\r
367                                 __djgpp_conventional_base);\r
368                 g_use_win_a = 0;\r
369         }\r
370         else\r
371         {\r
372                 printf("Error locating banked framebuffer "\r
373                         "for VBE mode 0x%X\n", vbe_mode_num);\r
374                 return -1;\r
375         }\r
376 #endif\r
377 /* init img */\r
378         img.wd = 14;\r
379         img.ht = 14;\r
380         img.bytes_per_row = img.wd;\r
381         img.raster = raster;\r
382 /* init clip_t */\r
383         clip.wd = img.wd;\r
384         clip.ht = img.ht;\r
385         clip.src_x = 0;\r
386         clip.src_y = 0;\r
387 /* set graphics mode */\r
388         regs.x.bx = vbe_mode_num;\r
389         regs.x.ax = 0x4F02;\r
390         int86(0x10, &regs, &regs);\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
396         {\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
400         }\r
401 /* test banked blit function */\r
402         clip.dst_y = 0;\r
403         for(; clip.dst_y + clip.ht < g_fb.ht; clip.dst_y += clip.ht)\r
404         {\r
405                 clip.dst_x = 0;\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
408         }\r
409         if(getch() == 0)\r
410                 (void)getch();\r
411 /* set text mode */\r
412         regs.x.ax = 0x0003;\r
413         int86(0x10, &regs, &regs);\r
414         return 0;\r
415 }\r