optimized AngleVectors calls (pass NULL for vectors that should not be generated)
[xonotic/darkplaces.git] / zone.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // Z_zone.c
21
22 #include "quakedef.h"
23
24 // LordHavoc: everyone used a -zone 512, but 128k is sufficient usually, I think...
25 #define DYNAMIC_SIZE    0x20000
26 //#define       DYNAMIC_SIZE    0xc000
27
28 #define ZONEID  0x1d4a11
29 #define MINFRAGMENT     64
30
31 typedef struct memblock_s
32 {
33         int             size;           // including the header and possibly tiny fragments
34         int     tag;            // a tag of 0 is a free block
35         int     id;                     // should be ZONEID
36         struct memblock_s       *next, *prev;
37         int             pad;                    // pad to 64 bit boundary
38 } memblock_t;
39
40 typedef struct
41 {
42         int             size;           // total bytes malloced, including header
43         memblock_t      blocklist;              // start / end cap for linked list
44         memblock_t      *rover;
45 } memzone_t;
46
47 void Cache_FreeLow (int new_low_hunk);
48 void Cache_FreeHigh (int new_high_hunk);
49
50
51 /*
52 ==============================================================================
53
54                                                 ZONE MEMORY ALLOCATION
55
56 There is never any space between memblocks, and there will never be two
57 contiguous free memblocks.
58
59 The rover can be left pointing at a non-empty block
60
61 The zone calls are pretty much only used for small strings and structures,
62 all big things are allocated on the hunk.
63 ==============================================================================
64 */
65
66 memzone_t       *mainzone;
67
68 void Z_ClearZone (memzone_t *zone, int size);
69
70
71 /*
72 ========================
73 Z_ClearZone
74 ========================
75 */
76 void Z_ClearZone (memzone_t *zone, int size)
77 {
78         memblock_t      *block;
79         
80 // set the entire zone to one free block
81
82         zone->blocklist.next = zone->blocklist.prev = block =
83                 (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
84         zone->blocklist.tag = 1;        // in use block
85         zone->blocklist.id = 0;
86         zone->blocklist.size = 0;
87         zone->rover = block;
88         
89         block->prev = block->next = &zone->blocklist;
90         block->tag = 0;                 // free block
91         block->id = ZONEID;
92         block->size = size - sizeof(memzone_t);
93 }
94
95
96 /*
97 ========================
98 Z_Free
99 ========================
100 */
101 void Z_Free (void *ptr)
102 {
103         memblock_t      *block, *other;
104         
105         if (!ptr)
106                 Sys_Error ("Z_Free: NULL pointer");
107
108         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
109         if (block->id != ZONEID)
110                 Sys_Error ("Z_Free: freed a pointer without ZONEID");
111         if (block->tag == 0)
112                 Sys_Error ("Z_Free: freed a freed pointer");
113
114         block->tag = 0;         // mark as free
115         
116         other = block->prev;
117         if (!other->tag)
118         {       // merge with previous free block
119                 other->size += block->size;
120                 other->next = block->next;
121                 other->next->prev = other;
122                 if (block == mainzone->rover)
123                         mainzone->rover = other;
124                 block = other;
125         }
126         
127         other = block->next;
128         if (!other->tag)
129         {       // merge the next free block onto the end
130                 block->size += other->size;
131                 block->next = other->next;
132                 block->next->prev = block;
133                 if (other == mainzone->rover)
134                         mainzone->rover = block;
135         }
136 }
137
138
139 /*
140 ========================
141 Z_Malloc
142 ========================
143 */
144 void *Z_Malloc (int size)
145 {
146         void    *buf;
147         
148 Z_CheckHeap (); // DEBUG
149         buf = Z_TagMalloc (size, 1);
150         if (!buf)
151                 Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
152         memset (buf, 0, size);
153
154         return buf;
155 }
156
157 void *Z_TagMalloc (int size, int tag)
158 {
159         int             extra;
160         memblock_t      *start, *rover, *new, *base;
161
162         if (!tag)
163                 Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
164
165 //
166 // scan through the block list looking for the first free block
167 // of sufficient size
168 //
169         size += sizeof(memblock_t);     // account for size of block header
170         size += 4;                                      // space for memory trash tester
171         size = (size + 7) & ~7;         // align to 8-byte boundary
172         
173         base = rover = mainzone->rover;
174         start = base->prev;
175         
176         do
177         {
178                 if (rover == start)     // scaned all the way around the list
179                         return NULL;
180                 if (rover->tag)
181                         base = rover = rover->next;
182                 else
183                         rover = rover->next;
184         } while (base->tag || base->size < size);
185         
186 //
187 // found a block big enough
188 //
189         extra = base->size - size;
190         if (extra >  MINFRAGMENT)
191         {       // there will be a free fragment after the allocated block
192                 new = (memblock_t *) ((byte *)base + size );
193                 new->size = extra;
194                 new->tag = 0;                   // free block
195                 new->prev = base;
196                 new->id = ZONEID;
197                 new->next = base->next;
198                 new->next->prev = new;
199                 base->next = new;
200                 base->size = size;
201         }
202         
203         base->tag = tag;                                // no longer a free block
204         
205         mainzone->rover = base->next;   // next allocation will start looking here
206         
207         base->id = ZONEID;
208
209 // marker for memory trash testing
210         *(int *)((byte *)base + base->size - 4) = ZONEID;
211
212         return (void *) ((byte *)base + sizeof(memblock_t));
213 }
214
215
216 /*
217 ========================
218 Z_Print
219 ========================
220 */
221 void Z_Print (memzone_t *zone)
222 {
223         memblock_t      *block;
224         
225         Con_Printf ("zone size: %i  location: %p\n",mainzone->size,mainzone);
226         
227         for (block = zone->blocklist.next ; ; block = block->next)
228         {
229                 Con_Printf ("block:%p    size:%7i    tag:%3i\n",
230                         block, block->size, block->tag);
231                 
232                 if (block->next == &zone->blocklist)
233                         break;                  // all blocks have been hit     
234                 if ( (byte *)block + block->size != (byte *)block->next)
235                         Con_Printf ("ERROR: block size does not touch the next block\n");
236                 if ( block->next->prev != block)
237                         Con_Printf ("ERROR: next block doesn't have proper back link\n");
238                 if (!block->tag && !block->next->tag)
239                         Con_Printf ("ERROR: two consecutive free blocks\n");
240         }
241 }
242
243
244 /*
245 ========================
246 Z_CheckHeap
247 ========================
248 */
249 void Z_CheckHeap (void)
250 {
251         memblock_t      *block;
252         
253         for (block = mainzone->blocklist.next ; ; block = block->next)
254         {
255                 if (block->next == &mainzone->blocklist)
256                         break;                  // all blocks have been hit     
257                 if ( (byte *)block + block->size != (byte *)block->next)
258                         Sys_Error ("Z_CheckHeap: block size does not touch the next block\n");
259                 if ( block->next->prev != block)
260                         Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
261                 if (!block->tag && !block->next->tag)
262                         Sys_Error ("Z_CheckHeap: two consecutive free blocks\n");
263         }
264 }
265
266 //============================================================================
267
268 #define HUNK_SENTINAL   0x1df001ed
269
270 typedef struct
271 {
272         int             sentinal;
273         int             size;           // including sizeof(hunk_t), -1 = not allocated
274         char    name[56];
275 } hunk_t;
276
277 byte    *hunk_base;
278 int             hunk_size;
279
280 int             hunk_low_used;
281 int             hunk_high_used;
282
283 void R_FreeTextures (void);
284
285 /*
286 ==============
287 Hunk_Check
288
289 Run consistancy and sentinal trashing checks
290 ==============
291 */
292 void Hunk_Check (void)
293 {
294         hunk_t  *h;
295         
296         for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; )
297         {
298                 if (h->sentinal != HUNK_SENTINAL)
299                         Sys_Error ("Hunk_Check: trashed sentinal");
300                 if (h->size < sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size)
301                         Sys_Error ("Hunk_Check: bad size");
302                 h = (hunk_t *)((byte *)h+h->size);
303         }
304 }
305
306 /*
307 ==============
308 Hunk_Print
309
310 If "all" is specified, every single allocation is printed.
311 Otherwise, allocations with the same name will be totaled up before printing.
312 ==============
313 */
314 void Hunk_Print (qboolean all)
315 {
316         hunk_t  *h, *next, *endlow, *starthigh, *endhigh;
317         int             count, sum, i;
318         int             totalblocks;
319         char    name[51];
320
321         name[50] = 0;
322         count = 0;
323         sum = 0;
324         totalblocks = 0;
325         
326         h = (hunk_t *)hunk_base;
327         endlow = (hunk_t *)(hunk_base + hunk_low_used);
328         starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
329         endhigh = (hunk_t *)(hunk_base + hunk_size);
330
331         Con_Printf ("          :%8i total hunk                 size\n", hunk_size);
332         Con_Printf ("-------------------------\n");
333
334         while (1)
335         {
336         //
337         // skip to the high hunk if done with low hunk
338         //
339                 if ( h == endlow )
340                 {
341                         Con_Printf ("-------------------------\n");
342                         Con_Printf ("          :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
343                         Con_Printf ("-------------------------\n");
344                         h = starthigh;
345                 }
346                 
347         //
348         // if totally done, break
349         //
350                 if ( h == endhigh )
351                         break;
352
353         //
354         // run consistancy checks
355         //
356                 if (h->sentinal != HUNK_SENTINAL)
357                         Sys_Error ("Hunk_Check: trashed sentinal");
358                 if (h->size < sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size)
359                         Sys_Error ("Hunk_Check: bad size");
360                         
361                 next = (hunk_t *)((byte *)h+h->size);
362                 count++;
363                 totalblocks++;
364                 sum += h->size;
365
366         //
367         // print the single block
368         //
369                 // LordHavoc: pad name to full length
370                 for (i = 0;i < 50;i++)
371                 {
372                         if (!h->name[i])
373                                 break;
374                         name[i] = h->name[i];
375                 }
376                 for (;i < 50;i++)
377                         name[i] = ' ';
378                 //memcpy (name, h->name, 51);
379                 if (all)
380                         Con_Printf ("%8p :%8i %s\n",h, h->size, name);
381                         
382         //
383         // print the total
384         //
385                 if (next == endlow || next == endhigh || strncmp(h->name, next->name, 50))
386                 {
387                         if (!all)
388                                 Con_Printf ("         :%8i %s (TOTAL)\n",sum, name);
389                         count = 0;
390                         sum = 0;
391                 }
392
393                 h = next;
394         }
395
396 //      Con_Printf ("-------------------------\n");
397         Con_Printf ("%8i total blocks\n", totalblocks);
398         
399 }
400
401 /*
402 ===================
403 Hunk_AllocName
404 ===================
405 */
406 void *Hunk_AllocName (int size, char *name)
407 {
408         hunk_t  *h;
409         
410 #ifdef PARANOID
411         Hunk_Check ();
412 #endif
413
414         if (size < 0)
415                 Sys_Error ("Hunk_Alloc: bad size: %i", size);
416                 
417         size = sizeof(hunk_t) + ((size+15)&~15);
418         
419         if (hunk_size - hunk_low_used - hunk_high_used < size)
420                 Sys_Error ("Hunk_Alloc: failed on %i bytes (name = %s)",size, name);
421         
422         h = (hunk_t *)(hunk_base + hunk_low_used);
423         hunk_low_used += size;
424
425         Cache_FreeLow (hunk_low_used);
426
427         memset (h, 0, size);
428         
429         h->size = size;
430         h->sentinal = HUNK_SENTINAL;
431         strncpy (h->name, name, 50);
432         h->name[50] = 0;
433         
434         return (void *)(h+1);
435 }
436
437 int     Hunk_LowMark (void)
438 {
439         return hunk_low_used;
440 }
441
442 void Hunk_FreeToLowMark (int mark)
443 {
444         if (mark < 0 || mark > hunk_low_used)
445                 Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
446         memset (hunk_base + mark, 0, hunk_low_used - mark);
447         hunk_low_used = mark;
448 }
449
450 int     Hunk_HighMark (void)
451 {
452         return hunk_high_used;
453 }
454
455 void Hunk_FreeToHighMark (int mark)
456 {
457         if (mark < 0 || mark > hunk_high_used)
458                 Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
459         memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
460         hunk_high_used = mark;
461 }
462
463
464 /*
465 ===================
466 Hunk_HighAllocName
467 ===================
468 */
469 void *Hunk_HighAllocName (int size, char *name)
470 {
471         hunk_t  *h;
472
473         if (size < 0)
474                 Sys_Error ("Hunk_HighAllocName: bad size: %i", size);
475
476 #ifdef PARANOID
477         Hunk_Check ();
478 #endif
479
480         size = sizeof(hunk_t) + ((size+15)&~15);
481
482         if (hunk_size - hunk_low_used - hunk_high_used < size)
483         {
484                 Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size);
485                 return NULL;
486         }
487
488         hunk_high_used += size;
489         Cache_FreeHigh (hunk_high_used);
490
491         h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
492
493         memset (h, 0, size);
494         h->size = size;
495         h->sentinal = HUNK_SENTINAL;
496         strncpy (h->name, name, 8);
497
498         return (void *)(h+1);
499 }
500
501 /*
502 ===============================================================================
503
504 CACHE MEMORY
505
506 ===============================================================================
507 */
508
509 typedef struct cache_system_s
510 {
511         int                                             size;           // including this header
512         cache_user_t                    *user;
513         char                                    name[16];
514         struct cache_system_s   *prev, *next;
515         struct cache_system_s   *lru_prev, *lru_next;   // for LRU flushing     
516 } cache_system_t;
517
518 cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
519
520 cache_system_t  cache_head;
521
522 /*
523 ===========
524 Cache_Move
525 ===========
526 */
527 void Cache_Move ( cache_system_t *c)
528 {
529         cache_system_t          *new;
530
531 // we are clearing up space at the bottom, so only allocate it late
532         new = Cache_TryAlloc (c->size, true);
533         if (new)
534         {
535 //              Con_Printf ("cache_move ok\n");
536
537                 memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) );
538                 new->user = c->user;
539                 memcpy (new->name, c->name, sizeof(new->name));
540                 Cache_Free (c->user);
541                 new->user->data = (void *)(new+1);
542         }
543         else
544         {
545 //              Con_Printf ("cache_move failed\n");
546
547                 Cache_Free (c->user);           // tough luck...
548         }
549 }
550
551 /*
552 ============
553 Cache_FreeLow
554
555 Throw things out until the hunk can be expanded to the given point
556 ============
557 */
558 void Cache_FreeLow (int new_low_hunk)
559 {
560         cache_system_t  *c;
561         
562         while (1)
563         {
564                 c = cache_head.next;
565                 if (c == &cache_head)
566                         return;         // nothing in cache at all
567                 if ((byte *)c >= hunk_base + new_low_hunk)
568                         return;         // there is space to grow the hunk
569                 Cache_Move ( c );       // reclaim the space
570         }
571 }
572
573 /*
574 ============
575 Cache_FreeHigh
576
577 Throw things out until the hunk can be expanded to the given point
578 ============
579 */
580 void Cache_FreeHigh (int new_high_hunk)
581 {
582         cache_system_t  *c, *prev;
583         
584         prev = NULL;
585         while (1)
586         {
587                 c = cache_head.prev;
588                 if (c == &cache_head)
589                         return;         // nothing in cache at all
590                 if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
591                         return;         // there is space to grow the hunk
592                 if (c == prev)
593                         Cache_Free (c->user);   // didn't move out of the way
594                 else
595                 {
596                         Cache_Move (c); // try to move it
597                         prev = c;
598                 }
599         }
600 }
601
602 void Cache_UnlinkLRU (cache_system_t *cs)
603 {
604         if (!cs->lru_next || !cs->lru_prev)
605                 Sys_Error ("Cache_UnlinkLRU: NULL link");
606
607         cs->lru_next->lru_prev = cs->lru_prev;
608         cs->lru_prev->lru_next = cs->lru_next;
609         
610         cs->lru_prev = cs->lru_next = NULL;
611 }
612
613 void Cache_MakeLRU (cache_system_t *cs)
614 {
615         if (cs->lru_next || cs->lru_prev)
616                 Sys_Error ("Cache_MakeLRU: active link");
617
618         cache_head.lru_next->lru_prev = cs;
619         cs->lru_next = cache_head.lru_next;
620         cs->lru_prev = &cache_head;
621         cache_head.lru_next = cs;
622 }
623
624 /*
625 ============
626 Cache_TryAlloc
627
628 Looks for a free block of memory between the high and low hunk marks
629 Size should already include the header and padding
630 ============
631 */
632 cache_system_t *Cache_TryAlloc (int size, qboolean nobottom)
633 {
634         cache_system_t  *cs, *new;
635         
636 // is the cache completely empty?
637
638         if (!nobottom && cache_head.prev == &cache_head)
639         {
640                 if (hunk_size - hunk_high_used - hunk_low_used < size)
641                         Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
642
643                 new = (cache_system_t *) (hunk_base + hunk_low_used);
644                 memset (new, 0, sizeof(*new));
645                 new->size = size;
646
647                 cache_head.prev = cache_head.next = new;
648                 new->prev = new->next = &cache_head;
649                 
650                 Cache_MakeLRU (new);
651                 return new;
652         }
653         
654 // search from the bottom up for space
655
656         new = (cache_system_t *) (hunk_base + hunk_low_used);
657         cs = cache_head.next;
658         
659         do
660         {
661                 if (!nobottom || cs != cache_head.next)
662                 {
663                         if ( (byte *)cs - (byte *)new >= size)
664                         {       // found space
665                                 memset (new, 0, sizeof(*new));
666                                 new->size = size;
667                                 
668                                 new->next = cs;
669                                 new->prev = cs->prev;
670                                 cs->prev->next = new;
671                                 cs->prev = new;
672                                 
673                                 Cache_MakeLRU (new);
674         
675                                 return new;
676                         }
677                 }
678
679         // continue looking             
680                 new = (cache_system_t *)((byte *)cs + cs->size);
681                 cs = cs->next;
682
683         } while (cs != &cache_head);
684         
685 // try to allocate one at the very end
686         if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size)
687         {
688                 memset (new, 0, sizeof(*new));
689                 new->size = size;
690                 
691                 new->next = &cache_head;
692                 new->prev = cache_head.prev;
693                 cache_head.prev->next = new;
694                 cache_head.prev = new;
695                 
696                 Cache_MakeLRU (new);
697
698                 return new;
699         }
700         
701         return NULL;            // couldn't allocate
702 }
703
704 /*
705 ============
706 Cache_Flush
707
708 Throw everything out, so new data will be demand cached
709 ============
710 */
711 void Cache_Flush (void)
712 {
713         while (cache_head.next != &cache_head)
714                 Cache_Free ( cache_head.next->user );   // reclaim the space
715 }
716
717
718 /*
719 ============
720 Cache_Print
721
722 ============
723 */
724 void Cache_Print (void)
725 {
726         cache_system_t  *cd;
727
728         for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
729         {
730                 Con_Printf ("%8i : %s\n", cd->size, cd->name);
731         }
732 }
733
734 /*
735 ============
736 Cache_Report
737
738 ============
739 */
740 void Cache_Report (void)
741 {
742         Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
743 }
744
745 /*
746 ============
747 Cache_Compact
748
749 ============
750 */
751 void Cache_Compact (void)
752 {
753 }
754
755 /*
756 ============
757 Cache_Init
758
759 ============
760 */
761 void Cache_Init (void)
762 {
763         cache_head.next = cache_head.prev = &cache_head;
764         cache_head.lru_next = cache_head.lru_prev = &cache_head;
765
766         Cmd_AddCommand ("flush", Cache_Flush);
767 }
768
769 /*
770 ==============
771 Cache_Free
772
773 Frees the memory and removes it from the LRU list
774 ==============
775 */
776 void Cache_Free (cache_user_t *c)
777 {
778         cache_system_t  *cs;
779
780         if (!c->data)
781                 Sys_Error ("Cache_Free: not allocated");
782
783         cs = ((cache_system_t *)c->data) - 1;
784
785         cs->prev->next = cs->next;
786         cs->next->prev = cs->prev;
787         cs->next = cs->prev = NULL;
788
789         c->data = NULL;
790
791         Cache_UnlinkLRU (cs);
792 }
793
794
795
796 /*
797 ==============
798 Cache_Check
799 ==============
800 */
801 void *Cache_Check (cache_user_t *c)
802 {
803         cache_system_t  *cs;
804
805         if (!c->data)
806                 return NULL;
807
808         cs = ((cache_system_t *)c->data) - 1;
809
810 // move to head of LRU
811         Cache_UnlinkLRU (cs);
812         Cache_MakeLRU (cs);
813         
814         return c->data;
815 }
816
817
818 /*
819 ==============
820 Cache_Alloc
821 ==============
822 */
823 void *Cache_Alloc (cache_user_t *c, int size, char *name)
824 {
825         cache_system_t  *cs;
826
827         if (c->data)
828                 Sys_Error ("Cache_Alloc: already allocated");
829         
830         if (size <= 0)
831                 Sys_Error ("Cache_Alloc: size %i", size);
832
833         size = (size + sizeof(cache_system_t) + 15) & ~15;
834
835 // find memory for it   
836         while (1)
837         {
838                 cs = Cache_TryAlloc (size, false);
839                 if (cs)
840                 {
841                         strncpy (cs->name, name, sizeof(cs->name)-1);
842                         c->data = (void *)(cs+1);
843                         cs->user = c;
844                         break;
845                 }
846         
847         // free the least recently used cahedat
848                 if (cache_head.lru_prev == &cache_head)
849                         Sys_Error ("Cache_Alloc: out of memory");
850                                                                                                         // not enough memory at all
851                 Cache_Free ( cache_head.lru_prev->user );
852         } 
853         
854         return Cache_Check (c);
855 }
856
857 //============================================================================
858
859
860 void HunkList_f(void)
861 {
862         if (Cmd_Argc() == 2)
863                 if (strcmp(Cmd_Argv(1), "all"))
864                         Con_Printf("usage: hunklist [all]\n");
865                 else
866                         Hunk_Print(true);
867         else
868                 Hunk_Print(false);
869 }
870
871 /*
872 ========================
873 Memory_Init
874 ========================
875 */
876 void Memory_Init (void *buf, int size)
877 {
878         int p;
879         int zonesize = DYNAMIC_SIZE;
880
881         hunk_base = buf;
882         hunk_size = size;
883         hunk_low_used = 0;
884         hunk_high_used = 0;
885         
886         Cache_Init ();
887         p = COM_CheckParm ("-zone");
888         if (p)
889         {
890                 if (p < com_argc-1)
891                         zonesize = atoi (com_argv[p+1]) * 1024;
892                 else
893                         Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
894         }
895         mainzone = Hunk_AllocName (zonesize, "zone" );
896         Z_ClearZone (mainzone, zonesize);
897         Cmd_AddCommand ("hunklist", HunkList_f);
898 }
899