Home | History | Annotate | Download | only in lfbprof
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    283 void FatalError(char *msg)
    284 {
    285     fprintf(stderr,"%s\n", msg);
    286     exit(1);
    287 }
    288 
    289 static void ExitVBEBuf(void)
    290 {
    291     DPMI_freeRealSeg(VESABuf_sel);
    292 }
    293 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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