|
|
1.1 root 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(®s, &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(®s, 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,®s,®s);
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, ®s, ®s);
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.