• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2 *
3 *                   VBE 2.0 Linear Framebuffer Profiler
4 *                    By Kendall Bennett and Brian Hook
5 *
6 * Filename:     LFBPROF.C
7 * Language:     ANSI C
8 * Environment:  Watcom C/C++ 10.0a with DOS4GW
9 *
10 * Description:  Simple program to profile the speed of screen clearing
11 *               and full screen BitBlt operations using a VESA VBE 2.0
12 *               linear framebuffer from 32 bit protected mode.
13 *
14 *               For simplicity, this program only supports 256 color
15 *               SuperVGA video modes that support a linear framebuffer.
16 *
17 *
18 * 2002/02/18: Jeroen Janssen <japj at xs4all dot nl>
19 *               - fixed unsigned short for mode list (-1 != 0xffff otherwise)
20 *               - fixed LfbMapRealPointer macro mask problem (some modes were skipped)
21 *
22 ****************************************************************************/
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <conio.h>
28 #include <dos.h>
29 #include "lfbprof.h"
30 
31 /*---------------------------- Global Variables ---------------------------*/
32 
33 int     VESABuf_len = 1024;         /* Length of VESABuf                */
34 int     VESABuf_sel = 0;            /* Selector for VESABuf             */
35 int     VESABuf_rseg;               /* Real mode segment of VESABuf     */
36 unsigned short   modeList[50];      /* List of available VBE modes      */
37 float   clearsPerSec;               /* Number of clears per second      */
38 float   clearsMbPerSec;             /* Memory transfer for clears       */
39 float   bitBltsPerSec;              /* Number of BitBlt's per second    */
40 float   bitBltsMbPerSec;            /* Memory transfer for bitblt's     */
41 int     xres,yres;                  /* Video mode resolution            */
42 int     bytesperline;               /* Bytes per scanline for mode      */
43 long    imageSize;                  /* Length of the video image        */
44 char    *LFBPtr;                	/* Pointer to linear framebuffer    */
45 
46 /*------------------------- DPMI interface routines -----------------------*/
47 
DPMI_allocRealSeg(int size,int * sel,int * r_seg)48 void DPMI_allocRealSeg(int size,int *sel,int *r_seg)
49 /****************************************************************************
50 *
51 * Function:     DPMI_allocRealSeg
52 * Parameters:   size    - Size of memory block to allocate
53 *               sel     - Place to return protected mode selector
54 *               r_seg   - Place to return real mode segment
55 *
56 * Description:  Allocates a block of real mode memory using DPMI services.
57 *               This routine returns both a protected mode selector and
58 *               real mode segment for accessing the memory block.
59 *
60 ****************************************************************************/
61 {
62     union REGS      r;
63 
64     r.w.ax = 0x100;                 /* DPMI allocate DOS memory         */
65     r.w.bx = (size + 0xF) >> 4;     /* number of paragraphs             */
66     int386(0x31, &r, &r);
67     if (r.w.cflag)
68         FatalError("DPMI_allocRealSeg failed!");
69     *sel = r.w.dx;                  /* Protected mode selector          */
70     *r_seg = r.w.ax;                /* Real mode segment                */
71 }
72 
DPMI_freeRealSeg(unsigned sel)73 void DPMI_freeRealSeg(unsigned sel)
74 /****************************************************************************
75 *
76 * Function:     DPMI_allocRealSeg
77 * Parameters:   sel - Protected mode selector of block to free
78 *
79 * Description:  Frees a block of real mode memory.
80 *
81 ****************************************************************************/
82 {
83     union REGS  r;
84 
85     r.w.ax = 0x101;                 /* DPMI free DOS memory             */
86     r.w.dx = sel;                   /* DX := selector from 0x100        */
87     int386(0x31, &r, &r);
88 }
89 
90 typedef struct {
91     long    edi;
92     long    esi;
93     long    ebp;
94     long    reserved;
95     long    ebx;
96     long    edx;
97     long    ecx;
98     long    eax;
99     short   flags;
100     short   es,ds,fs,gs,ip,cs,sp,ss;
101     } _RMREGS;
102 
103 #define IN(reg)     rmregs.e##reg = in->x.reg
104 #define OUT(reg)    out->x.reg = rmregs.e##reg
105 
DPMI_int86(int intno,RMREGS * in,RMREGS * out)106 int DPMI_int86(int intno, RMREGS *in, RMREGS *out)
107 /****************************************************************************
108 *
109 * Function:     DPMI_int86
110 * Parameters:   intno   - Interrupt number to issue
111 *               in      - Pointer to structure for input registers
112 *               out     - Pointer to structure for output registers
113 * Returns:      Value returned by interrupt in AX
114 *
115 * Description:  Issues a real mode interrupt using DPMI services.
116 *
117 ****************************************************************************/
118 {
119     _RMREGS         rmregs;
120     union REGS      r;
121     struct SREGS    sr;
122 
123     memset(&rmregs, 0, sizeof(rmregs));
124     IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
125 
126     segread(&sr);
127     r.w.ax = 0x300;                 /* DPMI issue real interrupt        */
128     r.h.bl = intno;
129     r.h.bh = 0;
130     r.w.cx = 0;
131     sr.es = sr.ds;
132     r.x.edi = (unsigned)&rmregs;
133     int386x(0x31, &r, &r, &sr);     /* Issue the interrupt              */
134 
135     OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
136     out->x.cflag = rmregs.flags & 0x1;
137     return out->x.ax;
138 }
139 
DPMI_int86x(int intno,RMREGS * in,RMREGS * out,RMSREGS * sregs)140 int DPMI_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
141 /****************************************************************************
142 *
143 * Function:     DPMI_int86
144 * Parameters:   intno   - Interrupt number to issue
145 *               in      - Pointer to structure for input registers
146 *               out     - Pointer to structure for output registers
147 *               sregs   - Values to load into segment registers
148 * Returns:      Value returned by interrupt in AX
149 *
150 * Description:  Issues a real mode interrupt using DPMI services.
151 *
152 ****************************************************************************/
153 {
154     _RMREGS         rmregs;
155     union REGS      r;
156     struct SREGS    sr;
157 
158     memset(&rmregs, 0, sizeof(rmregs));
159     IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
160     rmregs.es = sregs->es;
161     rmregs.ds = sregs->ds;
162 
163     segread(&sr);
164     r.w.ax = 0x300;                 /* DPMI issue real interrupt        */
165     r.h.bl = intno;
166     r.h.bh = 0;
167     r.w.cx = 0;
168     sr.es = sr.ds;
169     r.x.edi = (unsigned)&rmregs;
170     int386x(0x31, &r, &r, &sr);     /* Issue the interrupt */
171 
172     OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
173     sregs->es = rmregs.es;
174     sregs->cs = rmregs.cs;
175     sregs->ss = rmregs.ss;
176     sregs->ds = rmregs.ds;
177     out->x.cflag = rmregs.flags & 0x1;
178     return out->x.ax;
179 }
180 
DPMI_allocSelector(void)181 int DPMI_allocSelector(void)
182 /****************************************************************************
183 *
184 * Function:     DPMI_allocSelector
185 * Returns:      Newly allocated protected mode selector
186 *
187 * Description:  Allocates a new protected mode selector using DPMI
188 *               services. This selector has a base address and limit of 0.
189 *
190 ****************************************************************************/
191 {
192     int         sel;
193     union REGS  r;
194 
195     r.w.ax = 0;                     /* DPMI allocate selector           */
196     r.w.cx = 1;                     /* Allocate a single selector       */
197     int386(0x31, &r, &r);
198     if (r.x.cflag)
199         FatalError("DPMI_allocSelector() failed!");
200     sel = r.w.ax;
201 
202     r.w.ax = 9;                     /* DPMI set access rights           */
203     r.w.bx = sel;
204     r.w.cx = 0x8092;                /* 32 bit page granular             */
205     int386(0x31, &r, &r);
206     return sel;
207 }
208 
DPMI_mapPhysicalToLinear(long physAddr,long limit)209 long DPMI_mapPhysicalToLinear(long physAddr,long limit)
210 /****************************************************************************
211 *
212 * Function:     DPMI_mapPhysicalToLinear
213 * Parameters:   physAddr    - Physical memory address to map
214 *               limit       - Length-1 of physical memory region to map
215 * Returns:      Starting linear address for mapped memory
216 *
217 * Description:  Maps a section of physical memory into the linear address
218 *               space of a process using DPMI calls. Note that this linear
219 *               address cannot be used directly, but must be used as the
220 *               base address for a selector.
221 *
222 ****************************************************************************/
223 {
224     union REGS  r;
225 
226     r.w.ax = 0x800;                 /* DPMI map physical to linear      */
227     r.w.bx = physAddr >> 16;
228     r.w.cx = physAddr & 0xFFFF;
229     r.w.si = limit >> 16;
230     r.w.di = limit & 0xFFFF;
231     int386(0x31, &r, &r);
232     if (r.x.cflag)
233         FatalError("DPMI_mapPhysicalToLinear() failed!");
234     return ((long)r.w.bx << 16) + r.w.cx;
235 }
236 
DPMI_setSelectorBase(int sel,long linAddr)237 void DPMI_setSelectorBase(int sel,long linAddr)
238 /****************************************************************************
239 *
240 * Function:     DPMI_setSelectorBase
241 * Parameters:   sel     - Selector to change base address for
242 *               linAddr - Linear address used for new base address
243 *
244 * Description:  Sets the base address for the specified selector.
245 *
246 ****************************************************************************/
247 {
248     union REGS  r;
249 
250     r.w.ax = 7;                     /* DPMI set selector base address   */
251     r.w.bx = sel;
252     r.w.cx = linAddr >> 16;
253     r.w.dx = linAddr & 0xFFFF;
254     int386(0x31, &r, &r);
255     if (r.x.cflag)
256         FatalError("DPMI_setSelectorBase() failed!");
257 }
258 
DPMI_setSelectorLimit(int sel,long limit)259 void DPMI_setSelectorLimit(int sel,long limit)
260 /****************************************************************************
261 *
262 * Function:     DPMI_setSelectorLimit
263 * Parameters:   sel     - Selector to change limit for
264 *               limit   - Limit-1 for the selector
265 *
266 * Description:  Sets the memory limit for the specified selector.
267 *
268 ****************************************************************************/
269 {
270     union REGS  r;
271 
272     r.w.ax = 8;                     /* DPMI set selector limit          */
273     r.w.bx = sel;
274     r.w.cx = limit >> 16;
275     r.w.dx = limit & 0xFFFF;
276     int386(0x31, &r, &r);
277     if (r.x.cflag)
278         FatalError("DPMI_setSelectorLimit() failed!");
279 }
280 
281 /*-------------------------- VBE Interface routines -----------------------*/
282 
FatalError(char * msg)283 void FatalError(char *msg)
284 {
285     fprintf(stderr,"%s\n", msg);
286     exit(1);
287 }
288 
ExitVBEBuf(void)289 static void ExitVBEBuf(void)
290 {
291     DPMI_freeRealSeg(VESABuf_sel);
292 }
293 
VBE_initRMBuf(void)294 void VBE_initRMBuf(void)
295 /****************************************************************************
296 *
297 * Function:     VBE_initRMBuf
298 * Description:  Initialises the VBE transfer buffer in real mode memory.
299 *               This routine is called by the VESAVBE module every time
300 *               it needs to use the transfer buffer, so we simply allocate
301 *               it once and then return.
302 *
303 ****************************************************************************/
304 {
305     if (!VESABuf_sel) {
306         DPMI_allocRealSeg(VESABuf_len, &VESABuf_sel, &VESABuf_rseg);
307         atexit(ExitVBEBuf);
308         }
309 }
310 
VBE_callESDI(RMREGS * regs,void * buffer,int size)311 void VBE_callESDI(RMREGS *regs, void *buffer, int size)
312 /****************************************************************************
313 *
314 * Function:     VBE_callESDI
315 * Parameters:   regs    - Registers to load when calling VBE
316 *               buffer  - Buffer to copy VBE info block to
317 *               size    - Size of buffer to fill
318 *
319 * Description:  Calls the VESA VBE and passes in a buffer for the VBE to
320 *               store information in, which is then copied into the users
321 *               buffer space. This works in protected mode as the buffer
322 *               passed to the VESA VBE is allocated in conventional
323 *               memory, and is then copied into the users memory block.
324 *
325 ****************************************************************************/
326 {
327     RMSREGS sregs;
328 
329     VBE_initRMBuf();
330     sregs.es = VESABuf_rseg;
331     regs->x.di = 0;
332     _fmemcpy(MK_FP(VESABuf_sel,0),buffer,size);
333     DPMI_int86x(0x10, regs, regs, &sregs);
334     _fmemcpy(buffer,MK_FP(VESABuf_sel,0),size);
335 }
336 
VBE_detect(void)337 int VBE_detect(void)
338 /****************************************************************************
339 *
340 * Function:     VBE_detect
341 * Parameters:   vgaInfo - Place to store the VGA information block
342 * Returns:      VBE version number, or 0 if not detected.
343 *
344 * Description:  Detects if a VESA VBE is out there and functioning
345 *               correctly. If we detect a VBE interface we return the
346 *               VGAInfoBlock returned by the VBE and the VBE version number.
347 *
348 ****************************************************************************/
349 {
350     RMREGS      regs;
351     unsigned    short    *p1,*p2;
352     VBE_vgaInfo vgaInfo;
353 
354     /* Put 'VBE2' into the signature area so that the VBE 2.0 BIOS knows
355      * that we have passed a 512 byte extended block to it, and wish
356      * the extended information to be filled in.
357      */
358     strncpy(vgaInfo.VESASignature,"VBE2",4);
359 
360     /* Get the SuperVGA Information block */
361     regs.x.ax = 0x4F00;
362     VBE_callESDI(&regs, &vgaInfo, sizeof(VBE_vgaInfo));
363     if (regs.x.ax != 0x004F)
364         return 0;
365     if (strncmp(vgaInfo.VESASignature,"VESA",4) != 0)
366         return 0;
367 
368     /* Now that we have detected a VBE interface, copy the list of available
369      * video modes into our local buffer. We *must* copy this mode list,
370      * since the VBE will build the mode list in the VBE_vgaInfo buffer
371      * that we have passed, so the next call to the VBE will trash the
372      * list of modes.
373      */
374     printf("videomodeptr %x\n",vgaInfo.VideoModePtr);
375     p1 = LfbMapRealPointer(vgaInfo.VideoModePtr);
376     p2 = modeList;
377     while (*p1 != -1)
378     {
379         printf("found mode %x\n",*p1);
380         *p2++ = *p1++;
381     }
382     *p2 = -1;
383     return vgaInfo.VESAVersion;
384 }
385 
VBE_getModeInfo(int mode,VBE_modeInfo * modeInfo)386 int VBE_getModeInfo(int mode,VBE_modeInfo *modeInfo)
387 /****************************************************************************
388 *
389 * Function:     VBE_getModeInfo
390 * Parameters:   mode        - VBE mode to get information for
391 *               modeInfo    - Place to store VBE mode information
392 * Returns:      1 on success, 0 if function failed.
393 *
394 * Description:  Obtains information about a specific video mode from the
395 *               VBE. You should use this function to find the video mode
396 *               you wish to set, as the new VBE 2.0 mode numbers may be
397 *               completely arbitrary.
398 *
399 ****************************************************************************/
400 {
401     RMREGS  regs;
402 
403     regs.x.ax = 0x4F01;             /* Get mode information         */
404     regs.x.cx = mode;
405     VBE_callESDI(&regs, modeInfo, sizeof(VBE_modeInfo));
406     if (regs.x.ax != 0x004F)
407         return 0;
408     if ((modeInfo->ModeAttributes & vbeMdAvailable) == 0)
409         return 0;
410     return 1;
411 }
412 
VBE_setVideoMode(int mode)413 void VBE_setVideoMode(int mode)
414 /****************************************************************************
415 *
416 * Function:     VBE_setVideoMode
417 * Parameters:   mode    - VBE mode number to initialise
418 *
419 ****************************************************************************/
420 {
421     RMREGS  regs;
422     regs.x.ax = 0x4F02;
423     regs.x.bx = mode;
424     DPMI_int86(0x10,&regs,&regs);
425 }
426 
427 /*-------------------- Application specific routines ----------------------*/
428 
GetPtrToLFB(long physAddr)429 void *GetPtrToLFB(long physAddr)
430 /****************************************************************************
431 *
432 * Function:     GetPtrToLFB
433 * Parameters:   physAddr    - Physical memory address of linear framebuffer
434 * Returns:      Far pointer to the linear framebuffer memory
435 *
436 ****************************************************************************/
437 {
438     int     sel;
439     long    linAddr,limit = (4096 * 1024) - 1;
440 
441 //	sel = DPMI_allocSelector();
442 	linAddr = DPMI_mapPhysicalToLinear(physAddr,limit);
443 //	DPMI_setSelectorBase(sel,linAddr);
444 //	DPMI_setSelectorLimit(sel,limit);
445 //	return MK_FP(sel,0);
446 	return (void*)linAddr;
447 }
448 
AvailableModes(void)449 void AvailableModes(void)
450 /****************************************************************************
451 *
452 * Function:     AvailableModes
453 *
454 * Description:  Display a list of available LFB mode resolutions.
455 *
456 ****************************************************************************/
457 {
458     unsigned short           *p;
459     VBE_modeInfo    modeInfo;
460 
461     printf("Usage: LFBPROF <xres> <yres>\n\n");
462     printf("Available 256 color video modes:\n");
463     for (p = modeList; *p != -1; p++) {
464         if (VBE_getModeInfo(*p, &modeInfo)) {
465             /* Filter out only 8 bit linear framebuffer modes */
466             if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
467                 continue;
468             if (modeInfo.MemoryModel != vbeMemPK
469                 || modeInfo.BitsPerPixel != 8
470                 || modeInfo.NumberOfPlanes != 1)
471                 continue;
472             printf("    %4d x %4d %d bits per pixel\n",
473                 modeInfo.XResolution, modeInfo.YResolution,
474                 modeInfo.BitsPerPixel);
475             }
476         }
477     exit(1);
478 }
479 
InitGraphics(int x,int y)480 void InitGraphics(int x,int y)
481 /****************************************************************************
482 *
483 * Function:     InitGraphics
484 * Parameters:   x,y - Requested video mode resolution
485 *
486 * Description:  Initialise the specified video mode. We search through
487 *               the list of available video modes for one that matches
488 *               the resolution and color depth are are looking for.
489 *
490 ****************************************************************************/
491 {
492     unsigned short           *p;
493     VBE_modeInfo    modeInfo;
494     printf("InitGraphics\n");
495 
496     for (p = modeList; *p != -1; p++) {
497         if (VBE_getModeInfo(*p, &modeInfo)) {
498             /* Filter out only 8 bit linear framebuffer modes */
499             if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
500                 continue;
501             if (modeInfo.MemoryModel != vbeMemPK
502                 || modeInfo.BitsPerPixel != 8
503                 || modeInfo.NumberOfPlanes != 1)
504                 continue;
505             if (modeInfo.XResolution != x || modeInfo.YResolution != y)
506                 continue;
507             xres = x;
508             yres = y;
509             bytesperline = modeInfo.BytesPerScanLine;
510             imageSize = bytesperline * yres;
511             VBE_setVideoMode(*p | vbeUseLFB);
512             LFBPtr = GetPtrToLFB(modeInfo.PhysBasePtr);
513             return;
514             }
515         }
516     printf("Valid video mode not found\n");
517     exit(1);
518 }
519 
EndGraphics(void)520 void EndGraphics(void)
521 /****************************************************************************
522 *
523 * Function:     EndGraphics
524 *
525 * Description:  Restores text mode.
526 *
527 ****************************************************************************/
528 {
529     RMREGS  regs;
530     printf("EndGraphics\n");
531     regs.x.ax = 0x3;
532     DPMI_int86(0x10, &regs, &regs);
533 }
534 
ProfileMode(void)535 void ProfileMode(void)
536 /****************************************************************************
537 *
538 * Function:     ProfileMode
539 *
540 * Description:  Profiles framebuffer performance for simple screen clearing
541 *               and for copying from system memory to video memory (BitBlt).
542 *               This routine thrashes the CPU cache by cycling through
543 *               enough system memory buffers to invalidate the entire
544 *               CPU external cache before re-using the first memory buffer
545 *               again.
546 *
547 ****************************************************************************/
548 {
549     int     i,numClears,numBlts,maxImages;
550     long    startTicks,endTicks;
551     void    *image[10],*dst;
552     printf("ProfileMode\n");
553 
554     /* Profile screen clearing operation */
555     startTicks = LfbGetTicks();
556     numClears = 0;
557     while ((LfbGetTicks() - startTicks) < 182)
558 		LfbMemset(LFBPtr,numClears++,imageSize);
559 	endTicks = LfbGetTicks();
560 	clearsPerSec = numClears / ((endTicks - startTicks) * 0.054925);
561 	clearsMbPerSec = (clearsPerSec * imageSize) / 1048576.0;
562 
563 	/* Profile system memory to video memory copies */
564 	maxImages = ((512 * 1024U) / imageSize) + 2;
565 	for (i = 0; i < maxImages; i++) {
566 		image[i] = malloc(imageSize);
567 		if (image[i] == NULL)
568 			FatalError("Not enough memory to profile BitBlt!");
569 		memset(image[i],i+1,imageSize);
570 		}
571 	startTicks = LfbGetTicks();
572 	numBlts = 0;
573 	while ((LfbGetTicks() - startTicks) < 182)
574 		LfbMemcpy(LFBPtr,image[numBlts++ % maxImages],imageSize);
575     endTicks = LfbGetTicks();
576     bitBltsPerSec = numBlts / ((endTicks - startTicks) * 0.054925);
577     bitBltsMbPerSec = (bitBltsPerSec * imageSize) / 1048576.0;
578 }
579 
main(int argc,char * argv[])580 void main(int argc, char *argv[])
581 {
582     if (VBE_detect() < 0x200)
583         FatalError("This program requires VBE 2.0; Please install UniVBE 5.1.");
584     if (argc != 3)
585         AvailableModes();       /* Display available modes              */
586 
587     InitGraphics(atoi(argv[1]),atoi(argv[2]));  /* Start graphics       */
588     ProfileMode();              /* Profile the video mode               */
589     EndGraphics();              /* Restore text mode                    */
590 
591     printf("Profiling results for %dx%d 8 bits per pixel.\n",xres,yres);
592     printf("%3.2f clears/s, %2.2f Mb/s\n", clearsPerSec, clearsMbPerSec);
593     printf("%3.2f bitBlt/s, %2.2f Mb/s\n", bitBltsPerSec, bitBltsMbPerSec);
594 }
595