1 
2 /*
3 ** sysspec.c
4 ** System-specific routines.
5 **
6 ** BYTEmark (tm)
7 ** BYTE's Native Mode Benchmarks
8 ** Rick Grehan, BYTE Magazine
9 **
10 ** Creation:
11 ** Revision: 3/95;10/95
12 **
13 ** DISCLAIMER
14 ** The source, executable, and documentation files that comprise
15 ** the BYTEmark benchmarks are made available on an "as is" basis.
16 ** This means that we at BYTE Magazine have made every reasonable
17 ** effort to verify that the there are no errors in the source and
18 ** executable code.  We cannot, however, guarantee that the programs
19 ** are error-free.  Consequently, McGraw-HIll and BYTE Magazine make
20 ** no claims in regard to the fitness of the source code, executable
21 ** code, and documentation of the BYTEmark.
22 **  Furthermore, BYTE Magazine, McGraw-Hill, and all employees
23 ** of McGraw-Hill cannot be held responsible for any damages resulting
24 ** from the use of this code or the results obtained from using
25 ** this code.
26 */
27 
28 /***********************************
29 **    SYSTEM-SPECIFIC ROUTINES    **
30 ************************************
31 **
32 ** These are the routines that provide functions that are
33 ** system-specific.  If the benchmarks are to be ported
34 ** to new hardware/new O.S., this is the first place to
35 ** start.
36 */
37 #include "sysspec.h"
38 
39 #ifdef DOS16
40 #include <io.h>
41 #include <fcntl.h>
42 #include <sys\stat.h>
43 #endif
44 /*********************************
45 **  MEMORY MANAGEMENT ROUTINES  **
46 *********************************/
47 
48 
49 /****************************
50 ** AllocateMemory
51 ** This routine returns a void pointer to a memory
52 ** block.  The size of the memory block is given in bytes
53 ** as the first argument.  This routine also returns an
54 ** error code in the second argument.
55 ** 10/95 Update:
56 **  Added an associative array for memory alignment reasons.
57 **  mem_array[2][MEM_ARRAY_SIZE]
58 **   mem_array[0][n] = Actual address (from malloc)
59 **   mem_array[1][n] = Aligned address
60 ** Currently, mem_array[][] is only used if you use malloc;
61 **  it is not used for the 16-bit DOS and MAC versions.
62 */
AllocateMemory(unsigned long nbytes,int * errorcode)63 farvoid *AllocateMemory(unsigned long nbytes,   /* # of bytes to alloc */
64 		int *errorcode)                 /* Returned error code */
65 {
66 #ifdef DOS16MEM
67 union REGS registers;
68 unsigned short nparas;            /* # of paragraphs */
69 
70 /*
71 ** Set # of paragraphs to nbytes/16 +1.  The +1 is a
72 ** slop factor.
73 */
74 nparas=(unsigned short)(nbytes/16L) + 1;
75 
76 /*
77 ** Set incoming registers.
78 */
79 registers.h.ah=0x48;            /* Allocate memory */
80 registers.x.bx=nparas;          /* # of paragraphs */
81 
82 
83 intdos(&registers,&registers);  /* Call DOS */
84 
85 /*
86 ** See if things succeeded.
87 */
88 if(registers.x.cflag)
89 {       printf("error: %d Lgst: %d\n",registers.x.ax,registers.x.bx);
90 	    *errorcode=ERROR_MEMORY;
91 	return((farvoid *)NULL);
92 }
93 
94 /*
95 ** Create a farvoid pointer to return.
96 */
97 *errorcode=0;
98 return((farvoid *)MK_FP(registers.x.ax,0));
99 
100 #endif
101 
102 #ifdef MACMEM
103 /*
104 ** For MAC CodeWarrior, we'll use the MacOS NewPtr call
105 */
106 farvoid *returnval;
107 returnval=(farvoid *)NewPtr((Size)nbytes);
108 if(returnval==(farvoid *)NULL)
109 	*errorcode=ERROR_MEMORY;
110 else
111 	*errorcode=0;
112 return(returnval);
113 #endif
114 
115 #ifdef MALLOCMEM
116 /*
117 ** Everyone else, its pretty straightforward, given
118 ** that you use a 32-bit compiler which treats size_t as
119 ** a 4-byte entity.
120 */
121 farvoid *returnval;             /* Return value */
122 ulong true_addr;		/* True address */
123 ulong adj_addr;			/* Adjusted address */
124 
125 returnval=(farvoid *)malloc((size_t)(nbytes+2L*(long)global_align));
126 if(returnval==(farvoid *)NULL)
127 	*errorcode=ERROR_MEMORY;
128 else
129 	*errorcode=0;
130 
131 /*
132 ** Check for alignment
133 */
134 adj_addr=true_addr=(ulong)returnval;
135 if(global_align==0)
136 {
137 	if(AddMemArray(true_addr, adj_addr))
138 		*errorcode=ERROR_MEMARRAY_FULL;
139 	return(returnval);
140 }
141 
142 if(global_align==1)
143 {
144         if(true_addr%2==0) adj_addr++;
145 }
146 else
147 {
148 	while(adj_addr%global_align!=0) ++adj_addr;
149 	if(adj_addr%(global_align*2)==0) adj_addr+=global_align;
150 }
151 returnval=(void *)adj_addr;
152 if(AddMemArray(true_addr,adj_addr))
153 	*errorcode=ERROR_MEMARRAY_FULL;
154 return(returnval);
155 #endif
156 
157 }
158 
159 
160 /****************************
161 ** FreeMemory
162 ** This is the reverse of AllocateMemory.  The memory
163 ** block passed in is freed.  Should an error occur,
164 ** that error is returned in errorcode.
165 */
FreeMemory(farvoid * mempointer,int * errorcode)166 void FreeMemory(farvoid *mempointer,    /* Pointer to memory block */
167 		int *errorcode)
168 {
169 #ifdef DOS16MEM
170 /*
171 ** 16-bit DOS VERSION!!
172 */
173 unsigned int segment;
174 unsigned int offset;
175 union REGS registers;
176 struct SREGS sregisters;
177 
178 /*
179 ** First get the segment/offset of the farvoid pointer.
180 */
181 segment=FP_SEG(mempointer);
182 offset=FP_OFF(mempointer);
183 
184 /*
185 ** Align the segment properly.  For as long as offset > 16,
186 ** subtract 16 from offset and add 1 to segment.
187 */
188 while(offset>=16)
189 {       offset-=16;
190 	segment++;
191 }
192 
193 /*
194 ** Build the call to DOS
195 */
196 registers.h.ah=0x49;            /* Free memory */
197 sregisters.es=segment;
198 
199 intdosx(&registers,&registers,&sregisters);
200 
201 /*
202 ** Check for error
203 */
204 if(registers.x.cflag)
205 {       *errorcode=ERROR_MEMORY;
206 	return;
207 }
208 
209 *errorcode=0;
210 return;
211 #endif
212 
213 #ifdef MACMEM
214 DisposPtr((Ptr)mempointer);
215 *errorcode=0;
216 return;
217 #endif
218 
219 #ifdef MALLOCMEM
220 ulong adj_addr, true_addr;
221 
222 /* Locate item in memory array */
223 adj_addr=(ulong)mempointer;
224 if(RemoveMemArray(adj_addr, &true_addr))
225 {	*errorcode=ERROR_MEMARRAY_NFOUND;
226 	return;
227 }
228 mempointer=(void *)true_addr;
229 free(mempointer);
230 *errorcode=0;
231 return;
232 #endif
233 }
234 
235 /****************************
236 ** MoveMemory
237 ** Moves n bytes from a to b.  Handles overlap.
238 ** In most cases, this is just a memmove operation.
239 ** But, not in DOS....noooo....
240 */
MoveMemory(farvoid * destination,farvoid * source,unsigned long nbytes)241 void MoveMemory( farvoid *destination,  /* Destination address */
242 		farvoid *source,        /* Source address */
243 		unsigned long nbytes)
244 {
245 
246 /* +++16-bit DOS VERSION+++ */
247 #ifdef DOS16MEM
248 
249 	FarDOSmemmove( destination, source, nbytes);
250 
251 #else
252 
253 memmove(destination, source, nbytes);
254 
255 #endif
256 }
257 
258 #ifdef DOS16MEM
259 
260 /****************************
261 ** FarDOSmemmove
262 ** Performs the same function as memmove for DOS when
263 ** the arrays are defined with far pointers.
264 */
FarDOSmemmove(farvoid * destination,farvoid * source,unsigned long nbytes)265 void FarDOSmemmove(farvoid *destination,        /* Destination pointer */
266 		farvoid *source,        /* Source pointer */
267 		unsigned long nbytes)   /* # of bytes to move */
268 {
269 unsigned char huge *uchsource;  /* Temp source */
270 unsigned char huge *uchdest;    /* Temp destination */
271 unsigned long saddr;            /* Source "true" address */
272 unsigned long daddr;            /* Destination "true" address */
273 
274 
275 /*
276 ** Get unsigned char pointer equivalents
277 */
278 uchsource=(unsigned char huge *)source;
279 uchdest=(unsigned char huge *)destination;
280 
281 /*
282 ** Calculate true address of source and destination and
283 ** compare.
284 */
285 saddr=(unsigned long)(FP_SEG(source)*16 + FP_OFF(source));
286 daddr=(unsigned long)(FP_SEG(destination)*16 + FP_OFF(destination));
287 
288 if(saddr > daddr)
289 {
290 	/*
291 	** Source is greater than destination.
292 	** Use a series of standard move operations.
293 	** We'll move 65535 bytes at a time.
294 	*/
295 	while(nbytes>=65535L)
296 	{       _fmemmove((farvoid *)uchdest,
297 			(farvoid *)uchsource,
298 			(size_t) 65535);
299 		uchsource+=65535;       /* Advance pointers */
300 		uchdest+=65535;
301 		nbytes-=65535;
302 	}
303 
304 	/*
305 	** Move remaining bytes
306 	*/
307 	if(nbytes!=0L)
308 		_fmemmove((farvoid *)uchdest,
309 			(farvoid *)uchsource,
310 			(size_t)(nbytes & 0xFFFF));
311 
312 }
313 else
314 {
315 	/*
316 	** Destination is greater than source.
317 	** Advance pointers to the end of their
318 	** respective blocks.
319 	*/
320 	uchsource+=nbytes;
321 	uchdest+=nbytes;
322 
323 	/*
324 	** Again, move 65535 bytes at a time.  However,
325 	** "back" the pointers up before doing the
326 	** move.
327 	*/
328 	while(nbytes>=65535L)
329 	{
330 		uchsource-=65535;
331 		uchdest-=65535;
332 		_fmemmove((farvoid *)uchdest,
333 			(farvoid *)uchsource,
334 			(size_t) 65535);
335 		nbytes-=65535;
336 	}
337 
338 	/*
339 	** Move remaining bytes.
340 	*/
341 	if(nbytes!=0L)
342 	{       uchsource-=nbytes;
343 		uchdest-=nbytes;
344 		_fmemmove((farvoid *)uchdest,
345 			(farvoid *)uchsource,
346 			(size_t)(nbytes & 0xFFFF));
347 	}
348 }
349 return;
350 }
351 #endif
352 
353 /***********************************
354 ** MEMORY ARRAY HANDLING ROUTINES **
355 ***********************************/
356 /****************************
357 ** InitMemArray
358 ** Initialize the memory array.  This simply amounts to
359 ** setting mem_array_ents to zero, indicating that there
360 ** isn't anything in the memory array.
361 */
InitMemArray(void)362 void InitMemArray(void)
363 {
364 mem_array_ents=0;
365 return;
366 }
367 
368 /***************************
369 ** AddMemArray
370 ** Add a pair of items to the memory array.
371 **  true_addr is the true address (mem_array[0][n])
372 **  adj_addr is the adjusted address (mem_array[0][n])
373 ** Returns 0 if ok
374 ** -1 if not enough room
375 */
AddMemArray(ulong true_addr,ulong adj_addr)376 int AddMemArray(ulong true_addr,
377 		ulong adj_addr)
378 {
379 if(mem_array_ents>=MEM_ARRAY_SIZE)
380 	return(-1);
381 
382 mem_array[0][mem_array_ents]=true_addr;
383 mem_array[1][mem_array_ents]=adj_addr;
384 mem_array_ents++;
385 return(0);
386 }
387 
388 /*************************
389 ** RemoveMemArray
390 ** Given an adjusted address value (mem_array[1][n]), locate
391 ** the entry and remove it from the mem_array.
392 ** Also returns the associated true address.
393 ** Returns 0 if ok
394 ** -1 if not found.
395 */
RemoveMemArray(ulong adj_addr,ulong * true_addr)396 int RemoveMemArray(ulong adj_addr,ulong *true_addr)
397 {
398 int i,j;
399 
400 /* Locate the item in the array. */
401 for(i=0;i<mem_array_ents;i++)
402 	if(mem_array[1][i]==adj_addr)
403 	{       /* Found it..bubble stuff down */
404 		*true_addr=mem_array[0][i];
405 		j=i;
406 		while(j+1<mem_array_ents)
407 		{       mem_array[0][j]=mem_array[0][j+1];
408 			mem_array[1][j]=mem_array[1][j+1];
409 			j++;
410 		}
411 		mem_array_ents--;
412 		return(0);      /* Return if found */
413 	}
414 
415 /* If we made it here...something's wrong...show error */
416 return(-1);
417 }
418 
419 /**********************************
420 **    FILE HANDLING ROUTINES     **
421 **********************************/
422 
423 /****************************
424 ** CreateFile
425 ** This routine accepts a filename for an argument and
426 ** creates that file in the current directory (unless the
427 ** name contains a path that overrides the current directory).
428 ** Note that the routine does not OPEN the file.
429 ** If the file exists, it is truncated to length 0.
430 */
CreateFile(char * filename,int * errorcode)431 void CreateFile(char *filename,
432 		int *errorcode)
433 {
434 
435 #ifdef DOS16
436 /*
437 ** DOS VERSION!!
438 */
439 int fhandle;            /* File handle used internally */
440 
441 fhandle=open(filename,O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
442 
443 if(fhandle==-1)
444 	*errorcode=ERROR_FILECREATE;
445 else
446 	*errorcode=0;
447 
448 /*
449 ** Since all we're doing here is creating the file,
450 ** go ahead and close it.
451 */
452 close(fhandle);
453 
454 return;
455 #endif
456 
457 #ifdef LINUX
458 FILE *fhandle;            /* File handle used internally */
459 
460 fhandle=fopen(filename,"w");
461 
462 if(fhandle==NULL)
463 	*errorcode=ERROR_FILECREATE;
464 else
465 	*errorcode=0;
466 
467 /*
468 ** Since all we're doing here is creating the file,
469 ** go ahead and close it.
470 */
471 fclose(fhandle);
472 
473 return;
474 #endif
475 }
476 
477 /****************************
478 ** bmOpenFile
479 ** Opens the file given by fname, returning its handle.
480 ** If an error occurs, returns its code in errorcode.
481 ** The file is opened in read-write exclusive mode.
482 */
483 #ifdef DOS16
484 /*
485 ** DOS VERSION!!
486 */
487 
bmOpenFile(char * fname,int * errorcode)488 int bmOpenFile(char *fname,       /* File name */
489 	int *errorcode)         /* Error code returned */
490 {
491 
492 int fhandle;            /* Returned file handle */
493 
494 fhandle=open(fname,O_BINARY | O_RDWR, S_IREAD | S_IWRITE);
495 
496 if(fhandle==-1)
497 	*errorcode=ERROR_FILEOPEN;
498 else
499 	*errorcode=0;
500 
501 return(fhandle);
502 }
503 #endif
504 
505 
506 #ifdef LINUX
507 
bmOpenFile(char * fname,int * errorcode)508 FILE *bmOpenFile(char *fname,       /* File name */
509 	    int *errorcode)         /* Error code returned */
510 {
511 
512 FILE *fhandle;            /* Returned file handle */
513 
514 fhandle=fopen(fname,"w+");
515 
516 if(fhandle==NULL)
517 	*errorcode=ERROR_FILEOPEN;
518 else
519 	*errorcode=0;
520 
521 return(fhandle);
522 }
523 #endif
524 
525 
526 /****************************
527 ** CloseFile
528 ** Closes the file identified by fhandle.
529 ** A more inocuous routine there never was.
530 */
531 #ifdef DOS16
532 /*
533 ** DOS VERSION!!!
534 */
CloseFile(int fhandle,int * errorcode)535 void CloseFile(int fhandle,             /* File handle */
536 		int *errorcode)         /* Returned error code */
537 {
538 
539 close(fhandle);
540 *errorcode=0;
541 return;
542 }
543 #endif
544 #ifdef LINUX
CloseFile(FILE * fhandle,int * errorcode)545 void CloseFile(FILE *fhandle,             /* File handle */
546 		int *errorcode)         /* Returned error code */
547 {
548 fclose(fhandle);
549 *errorcode=0;
550 return;
551 }
552 #endif
553 
554 /****************************
555 ** readfile
556 ** Read bytes from an opened file.  This routine
557 ** is a combination seek-and-read.
558 ** Note that this routine expects the offset to be from
559 ** the beginning of the file.
560 */
561 #ifdef DOS16
562 /*
563 ** DOS VERSION!!
564 */
565 
readfile(int fhandle,unsigned long offset,unsigned long nbytes,void * buffer,int * errorcode)566 void readfile(int fhandle,              /* File handle */
567 	unsigned long offset,           /* Offset into file */
568 	unsigned long nbytes,           /* # of bytes to read */
569 	void *buffer,                   /* Buffer to read into */
570 	int *errorcode)                 /* Returned error code */
571 {
572 
573 long newoffset;                         /* New offset by lseek */
574 int readcode;                           /* Return code from read */
575 
576 /*
577 ** Presume success.
578 */
579 *errorcode=0;
580 
581 /*
582 ** Seek to the proper offset.
583 */
584 newoffset=lseek(fhandle,(long)offset,SEEK_SET);
585 if(newoffset==-1L)
586 {       *errorcode=ERROR_FILESEEK;
587 	return;
588 }
589 
590 /*
591 ** Do the read.
592 */
593 readcode=read(fhandle,buffer,(unsigned)(nbytes & 0xFFFF));
594 if(readcode==-1)
595 	*errorcode=ERROR_FILEREAD;
596 
597 return;
598 }
599 #endif
600 #ifdef LINUX
readfile(FILE * fhandle,unsigned long offset,unsigned long nbytes,void * buffer,int * errorcode)601 void readfile(FILE *fhandle,            /* File handle */
602 	unsigned long offset,           /* Offset into file */
603 	unsigned long nbytes,           /* # of bytes to read */
604 	void *buffer,                   /* Buffer to read into */
605 	int *errorcode)                 /* Returned error code */
606 {
607 
608 long newoffset;                         /* New offset by fseek */
609 size_t nelems;                          /* Expected return code from read */
610 size_t readcode;                        /* Actual return code from read */
611 
612 /*
613 ** Presume success.
614 */
615 *errorcode=0;
616 
617 /*
618 ** Seek to the proper offset.
619 */
620 newoffset=fseek(fhandle,(long)offset,SEEK_SET);
621 if(newoffset==-1L)
622 {       *errorcode=ERROR_FILESEEK;
623 	return;
624 }
625 
626 /*
627 ** Do the read.
628 */
629 nelems=(size_t)(nbytes & 0xFFFF);
630 readcode=fread(buffer,(size_t)1,nelems,fhandle);
631 if(readcode!=nelems)
632 	*errorcode=ERROR_FILEREAD;
633 
634 return;
635 }
636 #endif
637 
638 /****************************
639 ** writefile
640 ** writes bytes to an opened file.  This routine is
641 ** a combination seek-and-write.
642 ** Note that this routine expects the offset to be from
643 ** the beinning of the file.
644 */
645 #ifdef DOS16
646 /*
647 ** DOS VERSION!!
648 */
649 
writefile(int fhandle,unsigned long offset,unsigned long nbytes,void * buffer,int * errorcode)650 void writefile(int fhandle,             /* File handle */
651 	unsigned long offset,           /* Offset into file */
652 	unsigned long nbytes,           /* # of bytes to read */
653 	void *buffer,                   /* Buffer to read into */
654 	int *errorcode)                 /* Returned error code */
655 {
656 
657 long newoffset;                         /* New offset by lseek */
658 int writecode;                          /* Return code from write */
659 
660 /*
661 ** Presume success.
662 */
663 *errorcode=0;
664 
665 /*
666 ** Seek to the proper offset.
667 */
668 newoffset=lseek(fhandle,(long)offset,SEEK_SET);
669 if(newoffset==-1L)
670 {       *errorcode=ERROR_FILESEEK;
671 	return;
672 }
673 
674 /*
675 ** Do the write.
676 */
677 writecode=write(fhandle,buffer,(unsigned)(nbytes & 0xFFFF));
678 if(writecode==-1)
679 	*errorcode=ERROR_FILEWRITE;
680 
681 return;
682 }
683 #endif
684 
685 #ifdef LINUX
686 
writefile(FILE * fhandle,unsigned long offset,unsigned long nbytes,void * buffer,int * errorcode)687 void writefile(FILE *fhandle,           /* File handle */
688 	unsigned long offset,           /* Offset into file */
689 	unsigned long nbytes,           /* # of bytes to read */
690 	void *buffer,                   /* Buffer to read into */
691 	int *errorcode)                 /* Returned error code */
692 {
693 
694 long newoffset;                         /* New offset by lseek */
695 size_t nelems;                          /* Expected return code from write */
696 size_t writecode;                       /* Actual return code from write */
697 
698 /*
699 ** Presume success.
700 */
701 *errorcode=0;
702 
703 /*
704 ** Seek to the proper offset.
705 */
706 newoffset=fseek(fhandle,(long)offset,SEEK_SET);
707 if(newoffset==-1L)
708 {       *errorcode=ERROR_FILESEEK;
709 	return;
710 }
711 
712 /*
713 ** Do the write.
714 */
715 nelems=(size_t)(nbytes & 0xFFFF);
716 writecode=fwrite(buffer,(size_t)1,nelems,fhandle);
717 if(writecode==nelems)
718 	*errorcode=ERROR_FILEWRITE;
719 
720 return;
721 }
722 #endif
723 
724 
725 /********************************
726 **   ERROR HANDLING ROUTINES   **
727 ********************************/
728 
729 /****************************
730 ** ReportError
731 ** Report error message condition.
732 */
ReportError(char * errorcontext,int errorcode)733 void ReportError(char *errorcontext,    /* Error context string */
734 		int errorcode)          /* Error code number */
735 {
736 
737 /*
738 ** Display error context
739 */
740 printf("ERROR CONDITION\nContext: %s\n",errorcontext);
741 
742 /*
743 ** Display code
744 */
745 printf("Code: %d",errorcode);
746 
747 return;
748 }
749 
750 /****************************
751 ** ErrorExit
752 ** Peforms an exit from an error condition.
753 */
ErrorExit()754 void ErrorExit()
755 {
756 
757 /*
758 ** For profiling on the Mac with MetroWerks -- 11/17/94 RG
759 ** Have to do this to turn off profiler.
760 */
761 #ifdef MACCWPROF
762 #if __profile__
763 ProfilerTerm();
764 #endif
765 #endif
766 
767 /*
768 ** FOR NOW...SIMPLE EXIT
769 */
770 exit(1);
771 }
772 
773 /*****************************
774 **    STOPWATCH ROUTINES    **
775 *****************************/
776 
777 /****************************
778 ** StartStopwatch
779 ** Starts a software stopwatch.  Returns the first value of
780 ** the stopwatch in ticks.
781 */
StartStopwatch()782 unsigned long StartStopwatch()
783 {
784 #ifdef MACTIMEMGR
785 /*
786 ** For Mac code warrior, use timer. In this case, what we return is really
787 ** a dummy value.
788 */
789 InsTime((QElemPtr)&myTMTask);
790 PrimeTime((QElemPtr)&myTMTask,-MacHSTdelay);
791 return((unsigned long)1);
792 #else
793 #ifdef WIN31TIMER
794 /*
795 ** Win 3.x timer returns a DWORD, which we coax into a long.
796 */
797 _Call16(lpfn,"p",&win31tinfo);
798 return((unsigned long)win31tinfo.dwmsSinceStart);
799 #else
800 return((unsigned long)clock());
801 #endif
802 #endif
803 }
804 
805 /****************************
806 ** StopStopwatch
807 ** Stops the software stopwatch.  Expects as an input argument
808 ** the stopwatch start time.
809 */
StopStopwatch(unsigned long startticks)810 unsigned long StopStopwatch(unsigned long startticks)
811 {
812 
813 #ifdef MACTIMEMGR
814 /*
815 ** For Mac code warrior...ignore startticks.  Return val. in microseconds
816 */
817 RmvTime((QElemPtr)&myTMTask);
818 return((unsigned long)(MacHSTdelay+myTMTask.tmCount-MacHSTohead));
819 #else
820 #ifdef WIN31TIMER
821 _Call16(lpfn,"p",&win31tinfo);
822 return((unsigned long)win31tinfo.dwmsSinceStart-startticks);
823 #else
824 return((unsigned long)clock()-startticks);
825 #endif
826 #endif
827 }
828 
829 /****************************
830 ** TicksToSecs
831 ** Converts ticks to seconds.  Converts ticks to integer
832 ** seconds, discarding any fractional amount.
833 */
TicksToSecs(unsigned long tickamount)834 unsigned long TicksToSecs(unsigned long tickamount)
835 {
836 #ifdef CLOCKWCT
837 return((unsigned long)(tickamount/CLK_TCK));
838 #endif
839 
840 #ifdef MACTIMEMGR
841 /* +++ MAC time manager version (using timer in microseconds) +++ */
842 return((unsigned long)(tickamount/1000000));
843 #endif
844 
845 #ifdef CLOCKWCPS
846 /* Everybody else */
847 return((unsigned long)(tickamount/CLOCKS_PER_SEC));
848 #endif
849 
850 #ifdef WIN31TIMER
851 /* Each tick is 840 nanoseconds */
852 return((unsigned long)(tickamount/1000L));
853 #endif
854 
855 }
856 
857 /****************************
858 ** TicksToFracSecs
859 ** Converts ticks to fractional seconds.  In other words,
860 ** this returns the exact conversion from ticks to
861 ** seconds.
862 */
TicksToFracSecs(unsigned long tickamount)863 double TicksToFracSecs(unsigned long tickamount)
864 {
865 #ifdef CLOCKWCT
866 return((double)tickamount/(double)CLK_TCK);
867 #endif
868 
869 #ifdef MACTIMEMGR
870 /* +++ MAC time manager version +++ */
871 return((double)tickamount/(double)1000000);
872 #endif
873 
874 #ifdef CLOCKWCPS
875 /* Everybody else */
876 return((double)tickamount/(double)CLOCKS_PER_SEC);
877 #endif
878 
879 #ifdef WIN31TIMER
880 /* Using 840 nanosecond ticks */
881 return((double)tickamount/(double)1000);
882 #endif
883 }
884 
885