1 /****************************************************************************
3 * VBE 2.0 Linear Framebuffer Profiler
4 * By Kendall Bennett and Brian Hook
8 * Environment: Watcom C/C++ 10.0a with DOS4GW
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.
14 * For simplicity, this program only supports 256 color
15 * SuperVGA video modes that support a linear framebuffer.
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)
22 ****************************************************************************/
31 /*---------------------------- Global Variables ---------------------------*/
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 */
46 /*------------------------- DPMI interface routines -----------------------*/
48 void DPMI_allocRealSeg(int size,int *sel,int *r_seg)
49 /****************************************************************************
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
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.
60 ****************************************************************************/
64 r.w.ax = 0x100; /* DPMI allocate DOS memory */
65 r.w.bx = (size + 0xF) >> 4; /* number of paragraphs */
68 FatalError("DPMI_allocRealSeg failed!");
69 *sel = r.w.dx; /* Protected mode selector */
70 *r_seg = r.w.ax; /* Real mode segment */
73 void DPMI_freeRealSeg(unsigned sel)
74 /****************************************************************************
76 * Function: DPMI_allocRealSeg
77 * Parameters: sel - Protected mode selector of block to free
79 * Description: Frees a block of real mode memory.
81 ****************************************************************************/
85 r.w.ax = 0x101; /* DPMI free DOS memory */
86 r.w.dx = sel; /* DX := selector from 0x100 */
100 short es,ds,fs,gs,ip,cs,sp,ss;
103 #define IN(reg) rmregs.e##reg = in->x.reg
104 #define OUT(reg) out->x.reg = rmregs.e##reg
106 int DPMI_int86(int intno, RMREGS *in, RMREGS *out)
107 /****************************************************************************
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
115 * Description: Issues a real mode interrupt using DPMI services.
117 ****************************************************************************/
123 memset(&rmregs, 0, sizeof(rmregs));
124 IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
127 r.w.ax = 0x300; /* DPMI issue real interrupt */
132 r.x.edi = (unsigned)&rmregs;
133 int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
135 OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
136 out->x.cflag = rmregs.flags & 0x1;
140 int DPMI_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
141 /****************************************************************************
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
150 * Description: Issues a real mode interrupt using DPMI services.
152 ****************************************************************************/
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;
164 r.w.ax = 0x300; /* DPMI issue real interrupt */
169 r.x.edi = (unsigned)&rmregs;
170 int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
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;
181 int DPMI_allocSelector(void)
182 /****************************************************************************
184 * Function: DPMI_allocSelector
185 * Returns: Newly allocated protected mode selector
187 * Description: Allocates a new protected mode selector using DPMI
188 * services. This selector has a base address and limit of 0.
190 ****************************************************************************/
195 r.w.ax = 0; /* DPMI allocate selector */
196 r.w.cx = 1; /* Allocate a single selector */
197 int386(0x31, &r, &r);
199 FatalError("DPMI_allocSelector() failed!");
202 r.w.ax = 9; /* DPMI set access rights */
204 r.w.cx = 0x8092; /* 32 bit page granular */
205 int386(0x31, &r, &r);
209 long DPMI_mapPhysicalToLinear(long physAddr,long limit)
210 /****************************************************************************
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
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.
222 ****************************************************************************/
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);
233 FatalError("DPMI_mapPhysicalToLinear() failed!");
234 return ((long)r.w.bx << 16) + r.w.cx;
237 void DPMI_setSelectorBase(int sel,long linAddr)
238 /****************************************************************************
240 * Function: DPMI_setSelectorBase
241 * Parameters: sel - Selector to change base address for
242 * linAddr - Linear address used for new base address
244 * Description: Sets the base address for the specified selector.
246 ****************************************************************************/
250 r.w.ax = 7; /* DPMI set selector base address */
252 r.w.cx = linAddr >> 16;
253 r.w.dx = linAddr & 0xFFFF;
254 int386(0x31, &r, &r);
256 FatalError("DPMI_setSelectorBase() failed!");
259 void DPMI_setSelectorLimit(int sel,long limit)
260 /****************************************************************************
262 * Function: DPMI_setSelectorLimit
263 * Parameters: sel - Selector to change limit for
264 * limit - Limit-1 for the selector
266 * Description: Sets the memory limit for the specified selector.
268 ****************************************************************************/
272 r.w.ax = 8; /* DPMI set selector limit */
274 r.w.cx = limit >> 16;
275 r.w.dx = limit & 0xFFFF;
276 int386(0x31, &r, &r);
278 FatalError("DPMI_setSelectorLimit() failed!");
281 /*-------------------------- VBE Interface routines -----------------------*/
283 void FatalError(char *msg)
285 fprintf(stderr,"%s\n", msg);
289 static void ExitVBEBuf(void)
291 DPMI_freeRealSeg(VESABuf_sel);
294 void VBE_initRMBuf(void)
295 /****************************************************************************
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.
303 ****************************************************************************/
306 DPMI_allocRealSeg(VESABuf_len, &VESABuf_sel, &VESABuf_rseg);
311 void VBE_callESDI(RMREGS *regs, void *buffer, int size)
312 /****************************************************************************
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
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.
325 ****************************************************************************/
330 sregs.es = VESABuf_rseg;
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);
338 /****************************************************************************
340 * Function: VBE_detect
341 * Parameters: vgaInfo - Place to store the VGA information block
342 * Returns: VBE version number, or 0 if not detected.
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.
348 ****************************************************************************/
351 unsigned short *p1,*p2;
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.
358 strncpy(vgaInfo.VESASignature,"VBE2",4);
360 /* Get the SuperVGA Information block */
362 VBE_callESDI(®s, &vgaInfo, sizeof(VBE_vgaInfo));
363 if (regs.x.ax != 0x004F)
365 if (strncmp(vgaInfo.VESASignature,"VESA",4) != 0)
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
374 printf("videomodeptr %x\n",vgaInfo.VideoModePtr);
375 p1 = LfbMapRealPointer(vgaInfo.VideoModePtr);
379 printf("found mode %x\n",*p1);
383 return vgaInfo.VESAVersion;
386 int VBE_getModeInfo(int mode,VBE_modeInfo *modeInfo)
387 /****************************************************************************
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.
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.
399 ****************************************************************************/
403 regs.x.ax = 0x4F01; /* Get mode information */
405 VBE_callESDI(®s, modeInfo, sizeof(VBE_modeInfo));
406 if (regs.x.ax != 0x004F)
408 if ((modeInfo->ModeAttributes & vbeMdAvailable) == 0)
413 void VBE_setVideoMode(int mode)
414 /****************************************************************************
416 * Function: VBE_setVideoMode
417 * Parameters: mode - VBE mode number to initialise
419 ****************************************************************************/
424 DPMI_int86(0x10,®s,®s);
427 /*-------------------- Application specific routines ----------------------*/
429 void *GetPtrToLFB(long physAddr)
430 /****************************************************************************
432 * Function: GetPtrToLFB
433 * Parameters: physAddr - Physical memory address of linear framebuffer
434 * Returns: Far pointer to the linear framebuffer memory
436 ****************************************************************************/
439 long linAddr,limit = (4096 * 1024) - 1;
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;
449 void AvailableModes(void)
450 /****************************************************************************
452 * Function: AvailableModes
454 * Description: Display a list of available LFB mode resolutions.
456 ****************************************************************************/
459 VBE_modeInfo modeInfo;
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)
468 if (modeInfo.MemoryModel != vbeMemPK
469 || modeInfo.BitsPerPixel != 8
470 || modeInfo.NumberOfPlanes != 1)
472 printf(" %4d x %4d %d bits per pixel\n",
473 modeInfo.XResolution, modeInfo.YResolution,
474 modeInfo.BitsPerPixel);
480 void InitGraphics(int x,int y)
481 /****************************************************************************
483 * Function: InitGraphics
484 * Parameters: x,y - Requested video mode resolution
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.
490 ****************************************************************************/
493 VBE_modeInfo modeInfo;
494 printf("InitGraphics\n");
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)
501 if (modeInfo.MemoryModel != vbeMemPK
502 || modeInfo.BitsPerPixel != 8
503 || modeInfo.NumberOfPlanes != 1)
505 if (modeInfo.XResolution != x || modeInfo.YResolution != y)
509 bytesperline = modeInfo.BytesPerScanLine;
510 imageSize = bytesperline * yres;
511 VBE_setVideoMode(*p | vbeUseLFB);
512 LFBPtr = GetPtrToLFB(modeInfo.PhysBasePtr);
516 printf("Valid video mode not found\n");
520 void EndGraphics(void)
521 /****************************************************************************
523 * Function: EndGraphics
525 * Description: Restores text mode.
527 ****************************************************************************/
530 printf("EndGraphics\n");
532 DPMI_int86(0x10, ®s, ®s);
535 void ProfileMode(void)
536 /****************************************************************************
538 * Function: ProfileMode
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
547 ****************************************************************************/
549 int i,numClears,numBlts,maxImages;
550 long startTicks,endTicks;
551 void *image[10],*dst;
552 printf("ProfileMode\n");
554 /* Profile screen clearing operation */
555 startTicks = LfbGetTicks();
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;
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);
571 startTicks = LfbGetTicks();
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;
580 void main(int argc, char *argv[])
582 if (VBE_detect() < 0x200)
583 FatalError("This program requires VBE 2.0; Please install UniVBE 5.1.");
585 AvailableModes(); /* Display available modes */
587 InitGraphics(atoi(argv[1]),atoi(argv[2])); /* Start graphics */
588 ProfileMode(); /* Profile the video mode */
589 EndGraphics(); /* Restore text mode */
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);