| |
| /* |
| ** sysspec.c |
| ** System-specific routines. |
| ** |
| ** BYTEmark (tm) |
| ** BYTE's Native Mode Benchmarks |
| ** Rick Grehan, BYTE Magazine |
| ** |
| ** Creation: |
| ** Revision: 3/95;10/95 |
| ** |
| ** DISCLAIMER |
| ** The source, executable, and documentation files that comprise |
| ** the BYTEmark benchmarks are made available on an "as is" basis. |
| ** This means that we at BYTE Magazine have made every reasonable |
| ** effort to verify that the there are no errors in the source and |
| ** executable code. We cannot, however, guarantee that the programs |
| ** are error-free. Consequently, McGraw-HIll and BYTE Magazine make |
| ** no claims in regard to the fitness of the source code, executable |
| ** code, and documentation of the BYTEmark. |
| ** Furthermore, BYTE Magazine, McGraw-Hill, and all employees |
| ** of McGraw-Hill cannot be held responsible for any damages resulting |
| ** from the use of this code or the results obtained from using |
| ** this code. |
| */ |
| |
| /*********************************** |
| ** SYSTEM-SPECIFIC ROUTINES ** |
| ************************************ |
| ** |
| ** These are the routines that provide functions that are |
| ** system-specific. If the benchmarks are to be ported |
| ** to new hardware/new O.S., this is the first place to |
| ** start. |
| */ |
| #include "sysspec.h" |
| |
| #ifdef DOS16 |
| #include <io.h> |
| #include <fcntl.h> |
| #include <sys\stat.h> |
| #endif |
| /********************************* |
| ** MEMORY MANAGEMENT ROUTINES ** |
| *********************************/ |
| |
| |
| /**************************** |
| ** AllocateMemory |
| ** This routine returns a void pointer to a memory |
| ** block. The size of the memory block is given in bytes |
| ** as the first argument. This routine also returns an |
| ** error code in the second argument. |
| ** 10/95 Update: |
| ** Added an associative array for memory alignment reasons. |
| ** mem_array[2][MEM_ARRAY_SIZE] |
| ** mem_array[0][n] = Actual address (from malloc) |
| ** mem_array[1][n] = Aligned address |
| ** Currently, mem_array[][] is only used if you use malloc; |
| ** it is not used for the 16-bit DOS and MAC versions. |
| */ |
| farvoid *AllocateMemory(unsigned long nbytes, /* # of bytes to alloc */ |
| int *errorcode) /* Returned error code */ |
| { |
| #ifdef DOS16MEM |
| union REGS registers; |
| unsigned short nparas; /* # of paragraphs */ |
| |
| /* |
| ** Set # of paragraphs to nbytes/16 +1. The +1 is a |
| ** slop factor. |
| */ |
| nparas=(unsigned short)(nbytes/16L) + 1; |
| |
| /* |
| ** Set incoming registers. |
| */ |
| registers.h.ah=0x48; /* Allocate memory */ |
| registers.x.bx=nparas; /* # of paragraphs */ |
| |
| |
| intdos(®isters,®isters); /* Call DOS */ |
| |
| /* |
| ** See if things succeeded. |
| */ |
| if(registers.x.cflag) |
| { printf("error: %d Lgst: %d\n",registers.x.ax,registers.x.bx); |
| *errorcode=ERROR_MEMORY; |
| return((farvoid *)NULL); |
| } |
| |
| /* |
| ** Create a farvoid pointer to return. |
| */ |
| *errorcode=0; |
| return((farvoid *)MK_FP(registers.x.ax,0)); |
| |
| #endif |
| |
| #ifdef MACMEM |
| /* |
| ** For MAC CodeWarrior, we'll use the MacOS NewPtr call |
| */ |
| farvoid *returnval; |
| returnval=(farvoid *)NewPtr((Size)nbytes); |
| if(returnval==(farvoid *)NULL) |
| *errorcode=ERROR_MEMORY; |
| else |
| *errorcode=0; |
| return(returnval); |
| #endif |
| |
| #ifdef MALLOCMEM |
| /* |
| ** Everyone else, its pretty straightforward, given |
| ** that you use a 32-bit compiler which treats size_t as |
| ** a 4-byte entity. |
| */ |
| farvoid *returnval; /* Return value */ |
| ulong true_addr; /* True address */ |
| ulong adj_addr; /* Adjusted address */ |
| |
| returnval=(farvoid *)malloc((size_t)(nbytes+2L*(long)global_align)); |
| if(returnval==(farvoid *)NULL) |
| *errorcode=ERROR_MEMORY; |
| else |
| *errorcode=0; |
| |
| /* |
| ** Check for alignment |
| */ |
| adj_addr=true_addr=(ulong)returnval; |
| if(global_align==0) |
| { |
| if(AddMemArray(true_addr, adj_addr)) |
| *errorcode=ERROR_MEMARRAY_FULL; |
| return(returnval); |
| } |
| |
| if(global_align==1) |
| { |
| if(true_addr%2==0) adj_addr++; |
| } |
| else |
| { |
| while(adj_addr%global_align!=0) ++adj_addr; |
| if(adj_addr%(global_align*2)==0) adj_addr+=global_align; |
| } |
| returnval=(void *)adj_addr; |
| if(AddMemArray(true_addr,adj_addr)) |
| *errorcode=ERROR_MEMARRAY_FULL; |
| return(returnval); |
| #endif |
| |
| } |
| |
| |
| /**************************** |
| ** FreeMemory |
| ** This is the reverse of AllocateMemory. The memory |
| ** block passed in is freed. Should an error occur, |
| ** that error is returned in errorcode. |
| */ |
| void FreeMemory(farvoid *mempointer, /* Pointer to memory block */ |
| int *errorcode) |
| { |
| #ifdef DOS16MEM |
| /* |
| ** 16-bit DOS VERSION!! |
| */ |
| unsigned int segment; |
| unsigned int offset; |
| union REGS registers; |
| struct SREGS sregisters; |
| |
| /* |
| ** First get the segment/offset of the farvoid pointer. |
| */ |
| segment=FP_SEG(mempointer); |
| offset=FP_OFF(mempointer); |
| |
| /* |
| ** Align the segment properly. For as long as offset > 16, |
| ** subtract 16 from offset and add 1 to segment. |
| */ |
| while(offset>=16) |
| { offset-=16; |
| segment++; |
| } |
| |
| /* |
| ** Build the call to DOS |
| */ |
| registers.h.ah=0x49; /* Free memory */ |
| sregisters.es=segment; |
| |
| intdosx(®isters,®isters,&sregisters); |
| |
| /* |
| ** Check for error |
| */ |
| if(registers.x.cflag) |
| { *errorcode=ERROR_MEMORY; |
| return; |
| } |
| |
| *errorcode=0; |
| return; |
| #endif |
| |
| #ifdef MACMEM |
| DisposPtr((Ptr)mempointer); |
| *errorcode=0; |
| return; |
| #endif |
| |
| #ifdef MALLOCMEM |
| ulong adj_addr, true_addr; |
| |
| /* Locate item in memory array */ |
| adj_addr=(ulong)mempointer; |
| if(RemoveMemArray(adj_addr, &true_addr)) |
| { *errorcode=ERROR_MEMARRAY_NFOUND; |
| return; |
| } |
| mempointer=(void *)true_addr; |
| free(mempointer); |
| *errorcode=0; |
| return; |
| #endif |
| } |
| |
| /**************************** |
| ** MoveMemory |
| ** Moves n bytes from a to b. Handles overlap. |
| ** In most cases, this is just a memmove operation. |
| ** But, not in DOS....noooo.... |
| */ |
| void MoveMemory( farvoid *destination, /* Destination address */ |
| farvoid *source, /* Source address */ |
| unsigned long nbytes) |
| { |
| |
| /* +++16-bit DOS VERSION+++ */ |
| #ifdef DOS16MEM |
| |
| FarDOSmemmove( destination, source, nbytes); |
| |
| #else |
| |
| memmove(destination, source, nbytes); |
| |
| #endif |
| } |
| |
| #ifdef DOS16MEM |
| |
| /**************************** |
| ** FarDOSmemmove |
| ** Performs the same function as memmove for DOS when |
| ** the arrays are defined with far pointers. |
| */ |
| void FarDOSmemmove(farvoid *destination, /* Destination pointer */ |
| farvoid *source, /* Source pointer */ |
| unsigned long nbytes) /* # of bytes to move */ |
| { |
| unsigned char huge *uchsource; /* Temp source */ |
| unsigned char huge *uchdest; /* Temp destination */ |
| unsigned long saddr; /* Source "true" address */ |
| unsigned long daddr; /* Destination "true" address */ |
| |
| |
| /* |
| ** Get unsigned char pointer equivalents |
| */ |
| uchsource=(unsigned char huge *)source; |
| uchdest=(unsigned char huge *)destination; |
| |
| /* |
| ** Calculate true address of source and destination and |
| ** compare. |
| */ |
| saddr=(unsigned long)(FP_SEG(source)*16 + FP_OFF(source)); |
| daddr=(unsigned long)(FP_SEG(destination)*16 + FP_OFF(destination)); |
| |
| if(saddr > daddr) |
| { |
| /* |
| ** Source is greater than destination. |
| ** Use a series of standard move operations. |
| ** We'll move 65535 bytes at a time. |
| */ |
| while(nbytes>=65535L) |
| { _fmemmove((farvoid *)uchdest, |
| (farvoid *)uchsource, |
| (size_t) 65535); |
| uchsource+=65535; /* Advance pointers */ |
| uchdest+=65535; |
| nbytes-=65535; |
| } |
| |
| /* |
| ** Move remaining bytes |
| */ |
| if(nbytes!=0L) |
| _fmemmove((farvoid *)uchdest, |
| (farvoid *)uchsource, |
| (size_t)(nbytes & 0xFFFF)); |
| |
| } |
| else |
| { |
| /* |
| ** Destination is greater than source. |
| ** Advance pointers to the end of their |
| ** respective blocks. |
| */ |
| uchsource+=nbytes; |
| uchdest+=nbytes; |
| |
| /* |
| ** Again, move 65535 bytes at a time. However, |
| ** "back" the pointers up before doing the |
| ** move. |
| */ |
| while(nbytes>=65535L) |
| { |
| uchsource-=65535; |
| uchdest-=65535; |
| _fmemmove((farvoid *)uchdest, |
| (farvoid *)uchsource, |
| (size_t) 65535); |
| nbytes-=65535; |
| } |
| |
| /* |
| ** Move remaining bytes. |
| */ |
| if(nbytes!=0L) |
| { uchsource-=nbytes; |
| uchdest-=nbytes; |
| _fmemmove((farvoid *)uchdest, |
| (farvoid *)uchsource, |
| (size_t)(nbytes & 0xFFFF)); |
| } |
| } |
| return; |
| } |
| #endif |
| |
| /*********************************** |
| ** MEMORY ARRAY HANDLING ROUTINES ** |
| ***********************************/ |
| /**************************** |
| ** InitMemArray |
| ** Initialize the memory array. This simply amounts to |
| ** setting mem_array_ents to zero, indicating that there |
| ** isn't anything in the memory array. |
| */ |
| void InitMemArray(void) |
| { |
| mem_array_ents=0; |
| return; |
| } |
| |
| /*************************** |
| ** AddMemArray |
| ** Add a pair of items to the memory array. |
| ** true_addr is the true address (mem_array[0][n]) |
| ** adj_addr is the adjusted address (mem_array[0][n]) |
| ** Returns 0 if ok |
| ** -1 if not enough room |
| */ |
| int AddMemArray(ulong true_addr, |
| ulong adj_addr) |
| { |
| if(mem_array_ents>=MEM_ARRAY_SIZE) |
| return(-1); |
| |
| mem_array[0][mem_array_ents]=true_addr; |
| mem_array[1][mem_array_ents]=adj_addr; |
| mem_array_ents++; |
| return(0); |
| } |
| |
| /************************* |
| ** RemoveMemArray |
| ** Given an adjusted address value (mem_array[1][n]), locate |
| ** the entry and remove it from the mem_array. |
| ** Also returns the associated true address. |
| ** Returns 0 if ok |
| ** -1 if not found. |
| */ |
| int RemoveMemArray(ulong adj_addr,ulong *true_addr) |
| { |
| int i,j; |
| |
| /* Locate the item in the array. */ |
| for(i=0;i<mem_array_ents;i++) |
| if(mem_array[1][i]==adj_addr) |
| { /* Found it..bubble stuff down */ |
| *true_addr=mem_array[0][i]; |
| j=i; |
| while(j+1<mem_array_ents) |
| { mem_array[0][j]=mem_array[0][j+1]; |
| mem_array[1][j]=mem_array[1][j+1]; |
| j++; |
| } |
| mem_array_ents--; |
| return(0); /* Return if found */ |
| } |
| |
| /* If we made it here...something's wrong...show error */ |
| return(-1); |
| } |
| |
| /********************************** |
| ** FILE HANDLING ROUTINES ** |
| **********************************/ |
| |
| /**************************** |
| ** CreateFile |
| ** This routine accepts a filename for an argument and |
| ** creates that file in the current directory (unless the |
| ** name contains a path that overrides the current directory). |
| ** Note that the routine does not OPEN the file. |
| ** If the file exists, it is truncated to length 0. |
| */ |
| void CreateFile(char *filename, |
| int *errorcode) |
| { |
| |
| #ifdef DOS16 |
| /* |
| ** DOS VERSION!! |
| */ |
| int fhandle; /* File handle used internally */ |
| |
| fhandle=open(filename,O_CREAT | O_TRUNC, S_IREAD | S_IWRITE); |
| |
| if(fhandle==-1) |
| *errorcode=ERROR_FILECREATE; |
| else |
| *errorcode=0; |
| |
| /* |
| ** Since all we're doing here is creating the file, |
| ** go ahead and close it. |
| */ |
| close(fhandle); |
| |
| return; |
| #endif |
| |
| #ifdef LINUX |
| FILE *fhandle; /* File handle used internally */ |
| |
| fhandle=fopen(filename,"w"); |
| |
| if(fhandle==NULL) |
| *errorcode=ERROR_FILECREATE; |
| else |
| *errorcode=0; |
| |
| /* |
| ** Since all we're doing here is creating the file, |
| ** go ahead and close it. |
| */ |
| fclose(fhandle); |
| |
| return; |
| #endif |
| } |
| |
| /**************************** |
| ** bmOpenFile |
| ** Opens the file given by fname, returning its handle. |
| ** If an error occurs, returns its code in errorcode. |
| ** The file is opened in read-write exclusive mode. |
| */ |
| #ifdef DOS16 |
| /* |
| ** DOS VERSION!! |
| */ |
| |
| int bmOpenFile(char *fname, /* File name */ |
| int *errorcode) /* Error code returned */ |
| { |
| |
| int fhandle; /* Returned file handle */ |
| |
| fhandle=open(fname,O_BINARY | O_RDWR, S_IREAD | S_IWRITE); |
| |
| if(fhandle==-1) |
| *errorcode=ERROR_FILEOPEN; |
| else |
| *errorcode=0; |
| |
| return(fhandle); |
| } |
| #endif |
| |
| |
| #ifdef LINUX |
| |
| FILE *bmOpenFile(char *fname, /* File name */ |
| int *errorcode) /* Error code returned */ |
| { |
| |
| FILE *fhandle; /* Returned file handle */ |
| |
| fhandle=fopen(fname,"w+"); |
| |
| if(fhandle==NULL) |
| *errorcode=ERROR_FILEOPEN; |
| else |
| *errorcode=0; |
| |
| return(fhandle); |
| } |
| #endif |
| |
| |
| /**************************** |
| ** CloseFile |
| ** Closes the file identified by fhandle. |
| ** A more inocuous routine there never was. |
| */ |
| #ifdef DOS16 |
| /* |
| ** DOS VERSION!!! |
| */ |
| void CloseFile(int fhandle, /* File handle */ |
| int *errorcode) /* Returned error code */ |
| { |
| |
| close(fhandle); |
| *errorcode=0; |
| return; |
| } |
| #endif |
| #ifdef LINUX |
| void CloseFile(FILE *fhandle, /* File handle */ |
| int *errorcode) /* Returned error code */ |
| { |
| fclose(fhandle); |
| *errorcode=0; |
| return; |
| } |
| #endif |
| |
| /**************************** |
| ** readfile |
| ** Read bytes from an opened file. This routine |
| ** is a combination seek-and-read. |
| ** Note that this routine expects the offset to be from |
| ** the beginning of the file. |
| */ |
| #ifdef DOS16 |
| /* |
| ** DOS VERSION!! |
| */ |
| |
| void readfile(int fhandle, /* File handle */ |
| unsigned long offset, /* Offset into file */ |
| unsigned long nbytes, /* # of bytes to read */ |
| void *buffer, /* Buffer to read into */ |
| int *errorcode) /* Returned error code */ |
| { |
| |
| long newoffset; /* New offset by lseek */ |
| int readcode; /* Return code from read */ |
| |
| /* |
| ** Presume success. |
| */ |
| *errorcode=0; |
| |
| /* |
| ** Seek to the proper offset. |
| */ |
| newoffset=lseek(fhandle,(long)offset,SEEK_SET); |
| if(newoffset==-1L) |
| { *errorcode=ERROR_FILESEEK; |
| return; |
| } |
| |
| /* |
| ** Do the read. |
| */ |
| readcode=read(fhandle,buffer,(unsigned)(nbytes & 0xFFFF)); |
| if(readcode==-1) |
| *errorcode=ERROR_FILEREAD; |
| |
| return; |
| } |
| #endif |
| #ifdef LINUX |
| void readfile(FILE *fhandle, /* File handle */ |
| unsigned long offset, /* Offset into file */ |
| unsigned long nbytes, /* # of bytes to read */ |
| void *buffer, /* Buffer to read into */ |
| int *errorcode) /* Returned error code */ |
| { |
| |
| long newoffset; /* New offset by fseek */ |
| size_t nelems; /* Expected return code from read */ |
| size_t readcode; /* Actual return code from read */ |
| |
| /* |
| ** Presume success. |
| */ |
| *errorcode=0; |
| |
| /* |
| ** Seek to the proper offset. |
| */ |
| newoffset=fseek(fhandle,(long)offset,SEEK_SET); |
| if(newoffset==-1L) |
| { *errorcode=ERROR_FILESEEK; |
| return; |
| } |
| |
| /* |
| ** Do the read. |
| */ |
| nelems=(size_t)(nbytes & 0xFFFF); |
| readcode=fread(buffer,(size_t)1,nelems,fhandle); |
| if(readcode!=nelems) |
| *errorcode=ERROR_FILEREAD; |
| |
| return; |
| } |
| #endif |
| |
| /**************************** |
| ** writefile |
| ** writes bytes to an opened file. This routine is |
| ** a combination seek-and-write. |
| ** Note that this routine expects the offset to be from |
| ** the beinning of the file. |
| */ |
| #ifdef DOS16 |
| /* |
| ** DOS VERSION!! |
| */ |
| |
| void writefile(int fhandle, /* File handle */ |
| unsigned long offset, /* Offset into file */ |
| unsigned long nbytes, /* # of bytes to read */ |
| void *buffer, /* Buffer to read into */ |
| int *errorcode) /* Returned error code */ |
| { |
| |
| long newoffset; /* New offset by lseek */ |
| int writecode; /* Return code from write */ |
| |
| /* |
| ** Presume success. |
| */ |
| *errorcode=0; |
| |
| /* |
| ** Seek to the proper offset. |
| */ |
| newoffset=lseek(fhandle,(long)offset,SEEK_SET); |
| if(newoffset==-1L) |
| { *errorcode=ERROR_FILESEEK; |
| return; |
| } |
| |
| /* |
| ** Do the write. |
| */ |
| writecode=write(fhandle,buffer,(unsigned)(nbytes & 0xFFFF)); |
| if(writecode==-1) |
| *errorcode=ERROR_FILEWRITE; |
| |
| return; |
| } |
| #endif |
| |
| #ifdef LINUX |
| |
| void writefile(FILE *fhandle, /* File handle */ |
| unsigned long offset, /* Offset into file */ |
| unsigned long nbytes, /* # of bytes to read */ |
| void *buffer, /* Buffer to read into */ |
| int *errorcode) /* Returned error code */ |
| { |
| |
| long newoffset; /* New offset by lseek */ |
| size_t nelems; /* Expected return code from write */ |
| size_t writecode; /* Actual return code from write */ |
| |
| /* |
| ** Presume success. |
| */ |
| *errorcode=0; |
| |
| /* |
| ** Seek to the proper offset. |
| */ |
| newoffset=fseek(fhandle,(long)offset,SEEK_SET); |
| if(newoffset==-1L) |
| { *errorcode=ERROR_FILESEEK; |
| return; |
| } |
| |
| /* |
| ** Do the write. |
| */ |
| nelems=(size_t)(nbytes & 0xFFFF); |
| writecode=fwrite(buffer,(size_t)1,nelems,fhandle); |
| if(writecode==nelems) |
| *errorcode=ERROR_FILEWRITE; |
| |
| return; |
| } |
| #endif |
| |
| |
| /******************************** |
| ** ERROR HANDLING ROUTINES ** |
| ********************************/ |
| |
| /**************************** |
| ** ReportError |
| ** Report error message condition. |
| */ |
| void ReportError(char *errorcontext, /* Error context string */ |
| int errorcode) /* Error code number */ |
| { |
| |
| /* |
| ** Display error context |
| */ |
| printf("ERROR CONDITION\nContext: %s\n",errorcontext); |
| |
| /* |
| ** Display code |
| */ |
| printf("Code: %d",errorcode); |
| |
| return; |
| } |
| |
| /**************************** |
| ** ErrorExit |
| ** Peforms an exit from an error condition. |
| */ |
| void ErrorExit() |
| { |
| |
| /* |
| ** For profiling on the Mac with MetroWerks -- 11/17/94 RG |
| ** Have to do this to turn off profiler. |
| */ |
| #ifdef MACCWPROF |
| #if __profile__ |
| ProfilerTerm(); |
| #endif |
| #endif |
| |
| /* |
| ** FOR NOW...SIMPLE EXIT |
| */ |
| exit(1); |
| } |
| |
| /***************************** |
| ** STOPWATCH ROUTINES ** |
| *****************************/ |
| |
| /**************************** |
| ** StartStopwatch |
| ** Starts a software stopwatch. Returns the first value of |
| ** the stopwatch in ticks. |
| */ |
| unsigned long StartStopwatch() |
| { |
| /* no timing code in this version. */ |
| return 0; |
| } |
| |
| /**************************** |
| ** StopStopwatch |
| ** Stops the software stopwatch. Expects as an input argument |
| ** the stopwatch start time. |
| */ |
| unsigned long StopStopwatch(unsigned long startticks) |
| { |
| /* no timing code in this version. */ |
| return 1000; |
| } |
| |
| /**************************** |
| ** TicksToSecs |
| ** Converts ticks to seconds. Converts ticks to integer |
| ** seconds, discarding any fractional amount. |
| */ |
| unsigned long TicksToSecs(unsigned long tickamount) |
| { |
| /* no timing code in this version. */ |
| return 0; |
| } |
| |
| /**************************** |
| ** TicksToFracSecs |
| ** Converts ticks to fractional seconds. In other words, |
| ** this returns the exact conversion from ticks to |
| ** seconds. |
| */ |
| double TicksToFracSecs(unsigned long tickamount) |
| { |
| /* no timing code in this version. */ |
| return 0; |
| } |
| |