1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20#include <errno.h>
21#include <unistd.h>
22#include <signal.h>
23#include <stdlib.h>
24#include <limits.h>
25#include <sys/time.h>
26#include <sys/types.h>
27#include <dir.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <sys/stat.h>
33#include <string.h>
34#include <dpmi.h>
35#include <sys/nearptr.h>
36#include <conio.h>
37
38#include "quakedef.h"
39#include "dosisms.h"
40
41#define MINIMUM_WIN_MEMORY			0x800000
42#define MINIMUM_WIN_MEMORY_LEVELPAK	(MINIMUM_WIN_MEMORY + 0x100000)
43
44int			end_of_memory;
45qboolean	lockmem, lockunlockmem, unlockmem;
46static int	win95;
47
48#define STDOUT	1
49
50#define	KEYBUF_SIZE	256
51static unsigned char	keybuf[KEYBUF_SIZE];
52static int				keybuf_head=0;
53static int				keybuf_tail=0;
54
55static quakeparms_t	quakeparms;
56int					sys_checksum;
57static double		curtime = 0.0;
58static double		lastcurtime = 0.0;
59static double		oldtime = 0.0;
60
61static qboolean		isDedicated;
62
63static int			minmem;
64
65float				fptest_temp;
66
67extern char	start_of_memory __asm__("start");
68
69//=============================================================================
70
71// this is totally dependent on cwsdpmi putting the stack right after tge
72// global data
73
74// This does evil things in a Win95 DOS box!!!
75#if 0
76extern byte end;
77#define	CHECKBYTE	0xed
78void Sys_InitStackCheck (void)
79{
80	int		i;
81
82	for (i=0 ; i<128*1024 ; i++)
83		(&end)[i] = CHECKBYTE;
84}
85
86void Sys_StackCheck (void)
87{
88	int		i;
89
90	for (i=0 ; i<128*1024 ; i++)
91		if ( (&end)[i] != CHECKBYTE )
92			break;
93
94	Con_Printf ("%i undisturbed stack bytes\n", i);
95	if (end != CHECKBYTE)
96		Sys_Error ("System stack overflow!");
97}
98#endif
99
100//=============================================================================
101
102byte        scantokey[128] =
103					{
104//  0           1       2       3       4       5       6       7
105//  8           9       A       B       C       D       E       F
106	0  ,    27,     '1',    '2',    '3',    '4',    '5',    '6',
107	'7',    '8',    '9',    '0',    '-',    '=',    K_BACKSPACE, 9, // 0
108	'q',    'w',    'e',    'r',    't',    'y',    'u',    'i',
109	'o',    'p',    '[',    ']',    13 ,    K_CTRL,'a',  's',      // 1
110	'd',    'f',    'g',    'h',    'j',    'k',    'l',    ';',
111	'\'' ,    '`',    K_SHIFT,'\\',  'z',    'x',    'c',    'v',      // 2
112	'b',    'n',    'm',    ',',    '.',    '/',    K_SHIFT,'*',
113	K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3
114	K_F6, K_F7, K_F8, K_F9, K_F10,0  ,    0  , K_HOME,
115	K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, //4
116	K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11,
117	K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
118	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
119	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6
120	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
121	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7
122					};
123
124byte        shiftscantokey[128] =
125					{
126//  0           1       2       3       4       5       6       7
127//  8           9       A       B       C       D       E       F
128	0  ,    27,     '!',    '@',    '#',    '$',    '%',    '^',
129	'&',    '*',    '(',    ')',    '_',    '+',    K_BACKSPACE, 9, // 0
130	'Q',    'W',    'E',    'R',    'T',    'Y',    'U',    'I',
131	'O',    'P',    '{',    '}',    13 ,    K_CTRL,'A',  'S',      // 1
132	'D',    'F',    'G',    'H',    'J',    'K',    'L',    ':',
133	'"' ,    '~',    K_SHIFT,'|',  'Z',    'X',    'C',    'V',      // 2
134	'B',    'N',    'M',    '<',    '>',    '?',    K_SHIFT,'*',
135	K_ALT,' ',   0  ,    K_F1, K_F2, K_F3, K_F4, K_F5,   // 3
136	K_F6, K_F7, K_F8, K_F9, K_F10,0  ,    0  , K_HOME,
137	K_UPARROW,K_PGUP,'_',K_LEFTARROW,'%',K_RIGHTARROW,'+',K_END, //4
138	K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0,             0,              K_F11,
139	K_F12,0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 5
140	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
141	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,        // 6
142	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0,
143	0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0  ,    0         // 7
144					};
145
146void TrapKey(void)
147{
148//	static int ctrl=0;
149	keybuf[keybuf_head] = dos_inportb(0x60);
150	dos_outportb(0x20, 0x20);
151	/*
152	if (scantokey[keybuf[keybuf_head]&0x7f] == K_CTRL)
153		ctrl=keybuf[keybuf_head]&0x80;
154	if (ctrl && scantokey[keybuf[keybuf_head]&0x7f] == 'c')
155		Sys_Error("ctrl-c hit\n");
156	*/
157	keybuf_head = (keybuf_head + 1) & (KEYBUF_SIZE-1);
158}
159
160#define SC_UPARROW              0x48
161#define SC_DOWNARROW    0x50
162#define SC_LEFTARROW            0x4b
163#define SC_RIGHTARROW   0x4d
164#define SC_LEFTSHIFT   0x2a
165#define SC_RIGHTSHIFT   0x36
166#define SC_RIGHTARROW   0x4d
167
168void MaskExceptions (void);
169void Sys_InitFloatTime (void);
170void Sys_PushFPCW_SetHigh (void);
171void Sys_PopFPCW (void);
172
173#define LEAVE_FOR_CACHE (512*1024)		//FIXME: tune
174#define LOCKED_FOR_MALLOC (128*1024)	//FIXME: tune
175
176
177void Sys_DetectWin95 (void)
178{
179	__dpmi_regs				r;
180
181	r.x.ax = 0x160a;		/* Get Windows Version */
182	__dpmi_int(0x2f, &r);
183
184	if(r.x.ax || r.h.bh < 4)	/* Not windows or earlier than Win95 */
185	{
186		win95 = 0;
187		lockmem = true;
188		lockunlockmem = false;
189		unlockmem = true;
190	}
191	else
192	{
193		win95 = 1;
194		lockunlockmem = COM_CheckParm ("-winlockunlock");
195
196		if (lockunlockmem)
197			lockmem = true;
198		else
199			lockmem = COM_CheckParm ("-winlock");
200
201		unlockmem = lockmem && !lockunlockmem;
202	}
203}
204
205
206void *dos_getmaxlockedmem(int *size)
207{
208	__dpmi_free_mem_info	meminfo;
209	__dpmi_meminfo			info;
210	int						working_size;
211	void					*working_memory;
212	int						last_locked;
213	int						extra, 	i, j, allocsize;
214	static char				*msg = "Locking data...";
215	int						m, n;
216	byte					*x;
217
218// first lock all the current executing image so the locked count will
219// be accurate.  It doesn't hurt to lock the memory multiple times
220	last_locked = __djgpp_selector_limit + 1;
221	info.size = last_locked - 4096;
222	info.address = __djgpp_base_address + 4096;
223
224	if (lockmem)
225	{
226		if(__dpmi_lock_linear_region(&info))
227		{
228			Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n",
229						info.address, info.size/1024);
230		}
231	}
232
233	__dpmi_get_free_memory_information(&meminfo);
234
235	if (!win95)		/* Not windows or earlier than Win95 */
236	{
237		working_size = meminfo.maximum_locked_page_allocation_in_pages * 4096;
238	}
239	else
240	{
241		working_size = meminfo.largest_available_free_block_in_bytes -
242				LEAVE_FOR_CACHE;
243	}
244
245	working_size &= ~0xffff;		/* Round down to 64K */
246	working_size += 0x10000;
247
248	do
249	{
250		working_size -= 0x10000;		/* Decrease 64K and try again */
251		working_memory = sbrk(working_size);
252	} while (working_memory == (void *)-1);
253
254	extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff);
255
256	if (extra > 0)
257	{
258		sbrk(extra);
259		working_size += extra;
260	}
261
262// now grab the memory
263	info.address = last_locked + __djgpp_base_address;
264
265	if (!win95)
266	{
267	    info.size = __djgpp_selector_limit + 1 - last_locked;
268
269		while (info.size > 0 && __dpmi_lock_linear_region(&info))
270		{
271			info.size -= 0x1000;
272			working_size -= 0x1000;
273			sbrk(-0x1000);
274		}
275	}
276	else
277	{			/* Win95 section */
278		j = COM_CheckParm("-winmem");
279
280		if (standard_quake)
281			minmem = MINIMUM_WIN_MEMORY;
282		else
283			minmem = MINIMUM_WIN_MEMORY_LEVELPAK;
284
285		if (j)
286		{
287			allocsize = ((int)(Q_atoi(com_argv[j+1]))) * 0x100000 +
288					LOCKED_FOR_MALLOC;
289
290			if (allocsize < (minmem + LOCKED_FOR_MALLOC))
291				allocsize = minmem + LOCKED_FOR_MALLOC;
292		}
293		else
294		{
295			allocsize = minmem + LOCKED_FOR_MALLOC;
296		}
297
298		if (!lockmem)
299		{
300		// we won't lock, just sbrk the memory
301			info.size = allocsize;
302			goto UpdateSbrk;
303		}
304
305		// lock the memory down
306		write (STDOUT, msg, strlen (msg));
307
308		for (j=allocsize ; j>(minmem + LOCKED_FOR_MALLOC) ;
309			 j -= 0x100000)
310		{
311			info.size = j;
312
313			if (!__dpmi_lock_linear_region(&info))
314				goto Locked;
315
316			write (STDOUT, ".", 1);
317		}
318
319	// finally, try with the absolute minimum amount
320		for (i=0 ; i<10 ; i++)
321		{
322			info.size = minmem + LOCKED_FOR_MALLOC;
323
324			if (!__dpmi_lock_linear_region(&info))
325				goto Locked;
326		}
327
328		Sys_Error ("Can't lock memory; %d Mb lockable RAM required. "
329				   "Try shrinking smartdrv.", info.size / 0x100000);
330
331Locked:
332
333UpdateSbrk:
334
335		info.address += info.size;
336		info.address -= __djgpp_base_address + 4; // ending point, malloc align
337		working_size = info.address - (int)working_memory;
338		sbrk(info.address-(int)sbrk(0));		// negative adjustment
339	}
340
341
342	if (lockunlockmem)
343	{
344		__dpmi_unlock_linear_region (&info);
345		printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000);
346	}
347	else if (lockmem)
348	{
349		printf ("Locked %d Mb data\n", working_size / 0x100000);
350	}
351	else
352	{
353		printf ("Allocated %d Mb data\n", working_size / 0x100000);
354	}
355
356// touch all the memory to make sure it's there. The 16-page skip is to
357// keep Win 95 from thinking we're trying to page ourselves in (we are
358// doing that, of course, but there's no reason we shouldn't)
359	x = (byte *)working_memory;
360
361	for (n=0 ; n<4 ; n++)
362	{
363		for (m=0 ; m<(working_size - 16 * 0x1000) ; m += 4)
364		{
365			sys_checksum += *(int *)&x[m];
366			sys_checksum += *(int *)&x[m + 16 * 0x1000];
367		}
368	}
369
370// give some of what we locked back for malloc before returning.  Done
371// by cheating and passing a negative value to sbrk
372	working_size -= LOCKED_FOR_MALLOC;
373	sbrk( -(LOCKED_FOR_MALLOC));
374	*size = working_size;
375	return working_memory;
376}
377
378
379/*
380============
381Sys_FileTime
382
383returns -1 if not present
384============
385*/
386int	Sys_FileTime (char *path)
387{
388	struct	stat	buf;
389
390	if (stat (path,&buf) == -1)
391		return -1;
392
393	return buf.st_mtime;
394}
395
396void Sys_mkdir (char *path)
397{
398	mkdir (path, 0777);
399}
400
401
402void Sys_Sleep(void)
403{
404}
405
406
407char *Sys_ConsoleInput(void)
408{
409	static char	text[256];
410	static int	len = 0;
411	char		ch;
412
413	if (!isDedicated)
414		return NULL;
415
416	if (! kbhit())
417		return NULL;
418
419	ch = getche();
420
421	switch (ch)
422	{
423		case '\r':
424			putch('\n');
425			if (len)
426			{
427				text[len] = 0;
428				len = 0;
429				return text;
430			}
431			break;
432
433		case '\b':
434			putch(' ');
435			if (len)
436			{
437				len--;
438				putch('\b');
439			}
440			break;
441
442		default:
443			text[len] = ch;
444			len = (len + 1) & 0xff;
445			break;
446	}
447
448	return NULL;
449}
450
451void Sys_Init(void)
452{
453
454	MaskExceptions ();
455
456	Sys_SetFPCW ();
457
458    dos_outportb(0x43, 0x34); // set system timer to mode 2
459    dos_outportb(0x40, 0);    // for the Sys_FloatTime() function
460    dos_outportb(0x40, 0);
461
462	Sys_InitFloatTime ();
463
464	_go32_interrupt_stack_size = 4 * 1024;;
465	_go32_rmcb_stack_size = 4 * 1024;
466}
467
468void Sys_Shutdown(void)
469{
470	if (!isDedicated)
471		dos_restoreintr(9);
472
473	if (unlockmem)
474	{
475		dos_unlockmem (&start_of_memory,
476					   end_of_memory - (int)&start_of_memory);
477		dos_unlockmem (quakeparms.membase, quakeparms.memsize);
478	}
479}
480
481
482#define SC_RSHIFT       0x36
483#define SC_LSHIFT       0x2a
484void Sys_SendKeyEvents (void)
485{
486	int k, next;
487	int outkey;
488
489// get key events
490
491	while (keybuf_head != keybuf_tail)
492	{
493
494		k = keybuf[keybuf_tail++];
495		keybuf_tail &= (KEYBUF_SIZE-1);
496
497		if (k==0xe0)
498			continue;               // special / pause keys
499		next = keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)];
500		if (next == 0xe1)
501			continue;                               // pause key bullshit
502		if (k==0xc5 && next == 0x9d)
503		{
504			Key_Event (K_PAUSE, true);
505			continue;
506		}
507
508		// extended keyboard shift key bullshit
509		if ( (k&0x7f)==SC_LSHIFT || (k&0x7f)==SC_RSHIFT )
510		{
511			if ( keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)]==0xe0 )
512				continue;
513			k &= 0x80;
514			k |= SC_RSHIFT;
515		}
516
517		if (k==0xc5 && keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)] == 0x9d)
518			continue; // more pause bullshit
519
520		outkey = scantokey[k & 0x7f];
521
522		if (k & 0x80)
523			Key_Event (outkey, false);
524		else
525			Key_Event (outkey, true);
526
527	}
528
529}
530
531
532// =======================================================================
533// General routines
534// =======================================================================
535
536/*
537================
538Sys_Printf
539================
540*/
541
542void Sys_Printf (char *fmt, ...)
543{
544	va_list		argptr;
545	char		text[1024];
546
547	va_start (argptr,fmt);
548	vsprintf (text,fmt,argptr);
549	va_end (argptr);
550
551	if (cls.state == ca_dedicated)
552		fprintf(stderr, "%s", text);
553}
554
555void Sys_AtExit (void)
556{
557
558// shutdown only once (so Sys_Error can call this function to shutdown, then
559// print the error message, then call exit without exit calling this function
560// again)
561	Sys_Shutdown();
562}
563
564
565void Sys_Quit (void)
566{
567	byte	screen[80*25*2];
568	byte	*d;
569	char			ver[6];
570	int			i;
571
572
573// load the sell screen before shuting everything down
574	if (registered.value)
575		d = COM_LoadHunkFile ("end2.bin");
576	else
577		d = COM_LoadHunkFile ("end1.bin");
578	if (d)
579		memcpy (screen, d, sizeof(screen));
580
581// write the version number directly to the end screen
582	sprintf (ver, " v%4.2f", VERSION);
583	for (i=0 ; i<6 ; i++)
584		screen[0*80*2 + 72*2 + i*2] = ver[i];
585
586	Host_Shutdown();
587
588// do the text mode sell screen
589	if (d)
590	{
591		memcpy ((void *)real2ptr(0xb8000), screen,80*25*2);
592
593	// set text pos
594		regs.x.ax = 0x0200;
595		regs.h.bh = 0;
596		regs.h.dl = 0;
597		regs.h.dh = 22;
598		dos_int86 (0x10);
599	}
600	else
601		printf ("couldn't load endscreen.\n");
602
603	exit(0);
604}
605
606void Sys_Error (char *error, ...)
607{
608    va_list     argptr;
609    char        string[1024];
610
611    va_start (argptr,error);
612    vsprintf (string,error,argptr);
613    va_end (argptr);
614
615	Host_Shutdown();
616	fprintf(stderr, "Error: %s\n", string);
617// Sys_AtExit is called by exit to shutdown the system
618	exit(0);
619}
620
621
622int Sys_FileOpenRead (char *path, int *handle)
623{
624	int	h;
625	struct stat	fileinfo;
626
627	h = open (path, O_RDONLY|O_BINARY, 0666);
628	*handle = h;
629	if (h == -1)
630		return -1;
631
632	if (fstat (h,&fileinfo) == -1)
633		Sys_Error ("Error fstating %s", path);
634
635	return fileinfo.st_size;
636}
637
638int Sys_FileOpenWrite (char *path)
639{
640	int     handle;
641
642	umask (0);
643
644	handle = open(path,O_RDWR | O_BINARY | O_CREAT | O_TRUNC
645	, 0666);
646
647	if (handle == -1)
648		Sys_Error ("Error opening %s: %s", path,strerror(errno));
649
650	return handle;
651}
652
653void Sys_FileClose (int handle)
654{
655	close (handle);
656}
657
658void Sys_FileSeek (int handle, int position)
659{
660	lseek (handle, position, SEEK_SET);
661}
662
663int Sys_FileRead (int handle, void *dest, int count)
664{
665   return read (handle, dest, count);
666}
667
668int Sys_FileWrite (int handle, void *data, int count)
669{
670	return write (handle, data, count);
671}
672
673/*
674================
675Sys_MakeCodeWriteable
676================
677*/
678void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
679{
680	// it's always writeable
681}
682
683
684/*
685================
686Sys_FloatTime
687================
688*/
689double Sys_FloatTime (void)
690{
691    int				r;
692    unsigned		t, tick;
693	double			ft, time;
694	static int		sametimecount;
695
696	Sys_PushFPCW_SetHigh ();
697
698//{static float t = 0; t=t+0.05; return t;}	// DEBUG
699
700    t = *(unsigned short*)real2ptr(0x46c) * 65536;
701
702    dos_outportb(0x43, 0); // latch time
703    r = dos_inportb(0x40);
704    r |= dos_inportb(0x40) << 8;
705    r = (r-1) & 0xffff;
706
707    tick = *(unsigned short*)real2ptr(0x46c) * 65536;
708    if ((tick != t) && (r & 0x8000))
709		t = tick;
710
711	ft = (double) (t+(65536-r)) / 1193200.0;
712	time = ft - oldtime;
713	oldtime = ft;
714
715	if (time < 0)
716	{
717		if (time > -3000.0)
718			time = 0.0;
719		else
720			time += 3600.0;
721	}
722
723	curtime += time;
724
725	if (curtime == lastcurtime)
726	{
727		sametimecount++;
728
729		if (sametimecount > 100000)
730		{
731			curtime += 1.0;
732			sametimecount = 0;
733		}
734	}
735	else
736	{
737		sametimecount = 0;
738	}
739
740	lastcurtime = curtime;
741
742	Sys_PopFPCW ();
743
744    return curtime;
745}
746
747
748/*
749================
750Sys_InitFloatTime
751================
752*/
753void Sys_InitFloatTime (void)
754{
755	int		j;
756
757	Sys_FloatTime ();
758
759	oldtime = curtime;
760
761	j = COM_CheckParm("-starttime");
762
763	if (j)
764	{
765		curtime = (double) (Q_atof(com_argv[j+1]));
766	}
767	else
768	{
769		curtime = 0.0;
770	}
771	lastcurtime = curtime;
772}
773
774
775/*
776================
777Sys_GetMemory
778================
779*/
780void Sys_GetMemory(void)
781{
782	int		j, tsize;
783
784	j = COM_CheckParm("-mem");
785	if (j)
786	{
787		quakeparms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024);
788		quakeparms.membase = malloc (quakeparms.memsize);
789	}
790	else
791	{
792		quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize);
793	}
794
795	fprintf(stderr, "malloc'd: %d\n", quakeparms.memsize);
796
797	if (COM_CheckParm ("-heapsize"))
798	{
799		tsize = Q_atoi (com_argv[COM_CheckParm("-heapsize") + 1]) * 1024;
800
801		if (tsize < quakeparms.memsize)
802			quakeparms.memsize = tsize;
803	}
804}
805
806
807/*
808================
809Sys_PageInProgram
810
811walks the text, data, and bss to make sure it's all paged in so that the
812actual physical memory detected by Sys_GetMemory is correct.
813================
814*/
815void Sys_PageInProgram(void)
816{
817	int		i, j;
818
819	end_of_memory = (int)sbrk(0);
820
821	if (lockmem)
822	{
823		if (dos_lockmem ((void *)&start_of_memory,
824						 end_of_memory - (int)&start_of_memory))
825			Sys_Error ("Couldn't lock text and data");
826	}
827
828	if (lockunlockmem)
829	{
830		dos_unlockmem((void *)&start_of_memory,
831						 end_of_memory - (int)&start_of_memory);
832		printf ("Locked and unlocked %d Mb image\n",
833				(end_of_memory - (int)&start_of_memory) / 0x100000);
834	}
835	else if (lockmem)
836	{
837		printf ("Locked %d Mb image\n",
838				(end_of_memory - (int)&start_of_memory) / 0x100000);
839	}
840	else
841	{
842		printf ("Loaded %d Mb image\n",
843				(end_of_memory - (int)&start_of_memory) / 0x100000);
844	}
845
846// touch the entire image, doing the 16-page skip so Win95 doesn't think we're
847// trying to page ourselves in
848	for (j=0 ; j<4 ; j++)
849	{
850		for(i=(int)&start_of_memory ; i<(end_of_memory - 16 * 0x1000) ; i += 4)
851		{
852			sys_checksum += *(int *)i;
853			sys_checksum += *(int *)(i + 16 * 0x1000);
854		}
855	}
856}
857
858
859/*
860================
861Sys_NoFPUExceptionHandler
862================
863*/
864void Sys_NoFPUExceptionHandler(int whatever)
865{
866	printf ("\nError: Quake requires a floating-point processor\n");
867	exit (0);
868}
869
870
871/*
872================
873Sys_DefaultExceptionHandler
874================
875*/
876void Sys_DefaultExceptionHandler(int whatever)
877{
878}
879
880
881/*
882================
883main
884================
885*/
886int main (int c, char **v)
887{
888	double			time, oldtime, newtime;
889	extern void (*dos_error_func)(char *, ...);
890	static	char	cwd[1024];
891
892	printf ("Quake v%4.2f\n", VERSION);
893
894// make sure there's an FPU
895	signal(SIGNOFP, Sys_NoFPUExceptionHandler);
896	signal(SIGABRT, Sys_DefaultExceptionHandler);
897	signal(SIGALRM, Sys_DefaultExceptionHandler);
898	signal(SIGKILL, Sys_DefaultExceptionHandler);
899	signal(SIGQUIT, Sys_DefaultExceptionHandler);
900	signal(SIGINT, Sys_DefaultExceptionHandler);
901
902	if (fptest_temp >= 0.0)
903		fptest_temp += 0.1;
904
905	COM_InitArgv (c, v);
906
907	quakeparms.argc = com_argc;
908	quakeparms.argv = com_argv;
909
910	dos_error_func = Sys_Error;
911
912	Sys_DetectWin95 ();
913	Sys_PageInProgram ();
914	Sys_GetMemory ();
915
916	atexit (Sys_AtExit);	// in case we crash
917
918	getwd (cwd);
919	if (cwd[Q_strlen(cwd)-1] == '/') cwd[Q_strlen(cwd)-1] = 0;
920	quakeparms.basedir = cwd; //"f:/quake";
921
922	isDedicated = (COM_CheckParm ("-dedicated") != 0);
923
924	Sys_Init ();
925
926	if (!isDedicated)
927		dos_registerintr(9, TrapKey);
928
929//Sys_InitStackCheck ();
930
931	Host_Init(&quakeparms);
932
933//Sys_StackCheck ();
934
935//Con_Printf ("Top of stack: 0x%x\n", &time);
936	oldtime = Sys_FloatTime ();
937	while (1)
938	{
939		newtime = Sys_FloatTime ();
940		time = newtime - oldtime;
941
942		if (cls.state == ca_dedicated && (time<sys_ticrate.value))
943			continue;
944
945		Host_Frame (time);
946
947//Sys_StackCheck ();
948
949		oldtime = newtime;
950	}
951}
952
953
954