1 /*
2  *  Copyright (C) 2002-2010  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (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.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* $Id: memory.cpp,v 1.56 2009-05-27 09:15:41 qbix79 Exp $ */
20 
21 #include "dosbox.h"
22 #include "mem.h"
23 #include "inout.h"
24 #include "setup.h"
25 #include "paging.h"
26 #include "regs.h"
27 
28 #include <string.h>
29 
30 #define PAGES_IN_BLOCK	((1024*1024)/MEM_PAGE_SIZE)
31 #define SAFE_MEMORY	32
32 #define MAX_MEMORY	64
33 #define MAX_PAGE_ENTRIES (MAX_MEMORY*1024*1024/4096)
34 #define LFB_PAGES	512
35 #define MAX_LINKS	((MAX_MEMORY*1024/4)+4096)		//Hopefully enough
36 
37 struct LinkBlock {
38 	Bitu used;
39 	Bit32u pages[MAX_LINKS];
40 };
41 
42 static struct MemoryBlock {
43 	Bitu pages;
44 	PageHandler * * phandlers;
45 	MemHandle * mhandles;
46 	LinkBlock links;
47 	struct	{
48 		Bitu		start_page;
49 		Bitu		end_page;
50 		Bitu		pages;
51 		PageHandler *handler;
52 		PageHandler *mmiohandler;
53 	} lfb;
54 	struct {
55 		bool enabled;
56 		Bit8u controlport;
57 	} a20;
58 } memory;
59 
60 HostPt MemBase;
61 
62 class IllegalPageHandler : public PageHandler {
63 public:
IllegalPageHandler()64 	IllegalPageHandler() {
65 		flags=PFLAG_INIT|PFLAG_NOCODE;
66 	}
readb(PhysPt addr)67 	Bitu readb(PhysPt addr) {
68 #if C_DEBUG
69 		LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
70 #else
71 		static Bits lcount=0;
72 		if (lcount<1000) {
73 			lcount++;
74 			LOG_MSG("Illegal read from %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
75 		}
76 #endif
77 		return 0;
78 	}
writeb(PhysPt addr,Bitu val)79 	void writeb(PhysPt addr,Bitu val) {
80 #if C_DEBUG
81 		LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
82 #else
83 		static Bits lcount=0;
84 		if (lcount<1000) {
85 			lcount++;
86 			LOG_MSG("Illegal write to %x, CS:IP %8x:%8x",addr,SegValue(cs),reg_eip);
87 		}
88 #endif
89 	}
90 };
91 
92 class RAMPageHandler : public PageHandler {
93 public:
RAMPageHandler()94 	RAMPageHandler() {
95 		flags=PFLAG_READABLE|PFLAG_WRITEABLE;
96 	}
GetHostReadPt(Bitu phys_page)97 	HostPt GetHostReadPt(Bitu phys_page) {
98 		return MemBase+phys_page*MEM_PAGESIZE;
99 	}
GetHostWritePt(Bitu phys_page)100 	HostPt GetHostWritePt(Bitu phys_page) {
101 		return MemBase+phys_page*MEM_PAGESIZE;
102 	}
103 };
104 
105 class ROMPageHandler : public RAMPageHandler {
106 public:
ROMPageHandler()107 	ROMPageHandler() {
108 		flags=PFLAG_READABLE|PFLAG_HASROM;
109 	}
writeb(PhysPt addr,Bitu val)110 	void writeb(PhysPt addr,Bitu val){
111 		LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
112 	}
writew(PhysPt addr,Bitu val)113 	void writew(PhysPt addr,Bitu val){
114 		LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
115 	}
writed(PhysPt addr,Bitu val)116 	void writed(PhysPt addr,Bitu val){
117 		LOG(LOG_CPU,LOG_ERROR)("Write %x to rom at %x",val,addr);
118 	}
119 };
120 
121 
122 
123 static IllegalPageHandler illegal_page_handler;
124 static RAMPageHandler ram_page_handler;
125 static ROMPageHandler rom_page_handler;
126 
MEM_SetLFB(Bitu page,Bitu pages,PageHandler * handler,PageHandler * mmiohandler)127 void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler) {
128 	memory.lfb.handler=handler;
129 	memory.lfb.mmiohandler=mmiohandler;
130 	memory.lfb.start_page=page;
131 	memory.lfb.end_page=page+pages;
132 	memory.lfb.pages=pages;
133 	PAGING_ClearTLB();
134 }
135 
MEM_GetPageHandler(Bitu phys_page)136 PageHandler * MEM_GetPageHandler(Bitu phys_page) {
137 	if (phys_page<memory.pages) {
138 		return memory.phandlers[phys_page];
139 	} else if ((phys_page>=memory.lfb.start_page) && (phys_page<memory.lfb.end_page)) {
140 		return memory.lfb.handler;
141 	} else if ((phys_page>=memory.lfb.start_page+0x01000000/4096) &&
142 				(phys_page<memory.lfb.start_page+0x01000000/4096+16)) {
143 		return memory.lfb.mmiohandler;
144 	}
145 	return &illegal_page_handler;
146 }
147 
MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler)148 void MEM_SetPageHandler(Bitu phys_page,Bitu pages,PageHandler * handler) {
149 	for (;pages>0;pages--) {
150 		memory.phandlers[phys_page]=handler;
151 		phys_page++;
152 	}
153 }
154 
MEM_ResetPageHandler(Bitu phys_page,Bitu pages)155 void MEM_ResetPageHandler(Bitu phys_page, Bitu pages) {
156 	for (;pages>0;pages--) {
157 		memory.phandlers[phys_page]=&ram_page_handler;
158 		phys_page++;
159 	}
160 }
161 
mem_strlen(PhysPt pt)162 Bitu mem_strlen(PhysPt pt) {
163 	Bitu x=0;
164 	while (x<1024) {
165 		if (!mem_readb_inline(pt+x)) return x;
166 		x++;
167 	}
168 	return 0;		//Hope this doesn't happen
169 }
170 
mem_strcpy(PhysPt dest,PhysPt src)171 void mem_strcpy(PhysPt dest,PhysPt src) {
172 	Bit8u r;
173 	while ( (r = mem_readb(src++)) ) mem_writeb_inline(dest++,r);
174 	mem_writeb_inline(dest,0);
175 }
176 
mem_memcpy(PhysPt dest,PhysPt src,Bitu size)177 void mem_memcpy(PhysPt dest,PhysPt src,Bitu size) {
178 	while (size--) mem_writeb_inline(dest++,mem_readb_inline(src++));
179 }
180 
MEM_BlockRead(PhysPt pt,void * data,Bitu size)181 void MEM_BlockRead(PhysPt pt,void * data,Bitu size) {
182 	Bit8u * write=reinterpret_cast<Bit8u *>(data);
183 	while (size--) {
184 		*write++=mem_readb_inline(pt++);
185 	}
186 }
187 
MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size)188 void MEM_BlockWrite(PhysPt pt,void const * const data,Bitu size) {
189 	Bit8u const * read = reinterpret_cast<Bit8u const * const>(data);
190 	while (size--) {
191 		mem_writeb_inline(pt++,*read++);
192 	}
193 }
194 
MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size)195 void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size) {
196 	mem_memcpy(dest,src,size);
197 }
198 
MEM_StrCopy(PhysPt pt,char * data,Bitu size)199 void MEM_StrCopy(PhysPt pt,char * data,Bitu size) {
200 	while (size--) {
201 		Bit8u r=mem_readb_inline(pt++);
202 		if (!r) break;
203 		*data++=r;
204 	}
205 	*data=0;
206 }
207 
MEM_TotalPages(void)208 Bitu MEM_TotalPages(void) {
209 	return memory.pages;
210 }
211 
MEM_FreeLargest(void)212 Bitu MEM_FreeLargest(void) {
213 	Bitu size=0;Bitu largest=0;
214 	Bitu index=XMS_START;
215 	while (index<memory.pages) {
216 		if (!memory.mhandles[index]) {
217 			size++;
218 		} else {
219 			if (size>largest) largest=size;
220 			size=0;
221 		}
222 		index++;
223 	}
224 	if (size>largest) largest=size;
225 	return largest;
226 }
227 
MEM_FreeTotal(void)228 Bitu MEM_FreeTotal(void) {
229 	Bitu free=0;
230 	Bitu index=XMS_START;
231 	while (index<memory.pages) {
232 		if (!memory.mhandles[index]) free++;
233 		index++;
234 	}
235 	return free;
236 }
237 
MEM_AllocatedPages(MemHandle handle)238 Bitu MEM_AllocatedPages(MemHandle handle)
239 {
240 	Bitu pages = 0;
241 	while (handle>0) {
242 		pages++;
243 		handle=memory.mhandles[handle];
244 	}
245 	return pages;
246 }
247 
248 //TODO Maybe some protection for this whole allocation scheme
249 
BestMatch(Bitu size)250 INLINE Bitu BestMatch(Bitu size) {
251 	Bitu index=XMS_START;
252 	Bitu first=0;
253 	Bitu best=0xfffffff;
254 	Bitu best_first=0;
255 	while (index<memory.pages) {
256 		/* Check if we are searching for first free page */
257 		if (!first) {
258 			/* Check if this is a free page */
259 			if (!memory.mhandles[index]) {
260 				first=index;
261 			}
262 		} else {
263 			/* Check if this still is used page */
264 			if (memory.mhandles[index]) {
265 				Bitu pages=index-first;
266 				if (pages==size) {
267 					return first;
268 				} else if (pages>size) {
269 					if (pages<best) {
270 						best=pages;
271 						best_first=first;
272 					}
273 				}
274 				first=0;			//Always reset for new search
275 			}
276 		}
277 		index++;
278 	}
279 	/* Check for the final block if we can */
280 	if (first && (index-first>=size) && (index-first<best)) {
281 		return first;
282 	}
283 	return best_first;
284 }
285 
MEM_AllocatePages(Bitu pages,bool sequence)286 MemHandle MEM_AllocatePages(Bitu pages,bool sequence) {
287 	MemHandle ret;
288 	if (!pages) return 0;
289 	if (sequence) {
290 		Bitu index=BestMatch(pages);
291 		if (!index) return 0;
292 		MemHandle * next=&ret;
293 		while (pages) {
294 			*next=index;
295 			next=&memory.mhandles[index];
296 			index++;pages--;
297 		}
298 		*next=-1;
299 	} else {
300 		if (MEM_FreeTotal()<pages) return 0;
301 		MemHandle * next=&ret;
302 		while (pages) {
303 			Bitu index=BestMatch(1);
304 			if (!index) E_Exit("MEM:corruption during allocate");
305 			while (pages && (!memory.mhandles[index])) {
306 				*next=index;
307 				next=&memory.mhandles[index];
308 				index++;pages--;
309 			}
310 			*next=-1;		//Invalidate it in case we need another match
311 		}
312 	}
313 	return ret;
314 }
315 
MEM_GetNextFreePage(void)316 MemHandle MEM_GetNextFreePage(void) {
317 	return (MemHandle)BestMatch(1);
318 }
319 
MEM_ReleasePages(MemHandle handle)320 void MEM_ReleasePages(MemHandle handle) {
321 	while (handle>0) {
322 		MemHandle next=memory.mhandles[handle];
323 		memory.mhandles[handle]=0;
324 		handle=next;
325 	}
326 }
327 
MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence)328 bool MEM_ReAllocatePages(MemHandle & handle,Bitu pages,bool sequence) {
329 	if (handle<=0) {
330 		if (!pages) return true;
331 		handle=MEM_AllocatePages(pages,sequence);
332 		return (handle>0);
333 	}
334 	if (!pages) {
335 		MEM_ReleasePages(handle);
336 		handle=-1;
337 		return true;
338 	}
339 	MemHandle index=handle;
340 	MemHandle last;Bitu old_pages=0;
341 	while (index>0) {
342 		old_pages++;
343 		last=index;
344 		index=memory.mhandles[index];
345 	}
346 	if (old_pages == pages) return true;
347 	if (old_pages > pages) {
348 		/* Decrease size */
349 		pages--;index=handle;old_pages--;
350 		while (pages) {
351 			index=memory.mhandles[index];
352 			pages--;old_pages--;
353 		}
354 		MemHandle next=memory.mhandles[index];
355 		memory.mhandles[index]=-1;
356 		index=next;
357 		while (old_pages) {
358 			next=memory.mhandles[index];
359 			memory.mhandles[index]=0;
360 			index=next;
361 			old_pages--;
362 		}
363 		return true;
364 	} else {
365 		/* Increase size, check for enough free space */
366 		Bitu need=pages-old_pages;
367 		if (sequence) {
368 			index=last+1;
369 			Bitu free=0;
370 			while ((index<(MemHandle)memory.pages) && !memory.mhandles[index]) {
371 				index++;free++;
372 			}
373 			if (free>=need) {
374 				/* Enough space allocate more pages */
375 				index=last;
376 				while (need) {
377 					memory.mhandles[index]=index+1;
378 					need--;index++;
379 				}
380 				memory.mhandles[index]=-1;
381 				return true;
382 			} else {
383 				/* Not Enough space allocate new block and copy */
384 				MemHandle newhandle=MEM_AllocatePages(pages,true);
385 				if (!newhandle) return false;
386 				MEM_BlockCopy(newhandle*4096,handle*4096,old_pages*4096);
387 				MEM_ReleasePages(handle);
388 				handle=newhandle;
389 				return true;
390 			}
391 		} else {
392 			MemHandle rem=MEM_AllocatePages(need,false);
393 			if (!rem) return false;
394 			memory.mhandles[last]=rem;
395 			return true;
396 		}
397 	}
398 	return 0;
399 }
400 
MEM_NextHandle(MemHandle handle)401 MemHandle MEM_NextHandle(MemHandle handle) {
402 	return memory.mhandles[handle];
403 }
404 
MEM_NextHandleAt(MemHandle handle,Bitu where)405 MemHandle MEM_NextHandleAt(MemHandle handle,Bitu where) {
406 	while (where) {
407 		where--;
408 		handle=memory.mhandles[handle];
409 	}
410 	return handle;
411 }
412 
413 
414 /*
415 	A20 line handling,
416 	Basically maps the 4 pages at the 1mb to 0mb in the default page directory
417 */
MEM_A20_Enabled(void)418 bool MEM_A20_Enabled(void) {
419 	return memory.a20.enabled;
420 }
421 
MEM_A20_Enable(bool enabled)422 void MEM_A20_Enable(bool enabled) {
423 	Bitu phys_base=enabled ? (1024/4) : 0;
424 	for (Bitu i=0;i<16;i++) PAGING_MapPage((1024/4)+i,phys_base+i);
425 	memory.a20.enabled=enabled;
426 }
427 
428 
429 /* Memory access functions */
mem_unalignedreadw(PhysPt address)430 Bit16u mem_unalignedreadw(PhysPt address) {
431 	Bit16u ret = mem_readb_inline(address);
432 	ret       |= mem_readb_inline(address+1) << 8;
433 	return ret;
434 }
435 
mem_unalignedreadd(PhysPt address)436 Bit32u mem_unalignedreadd(PhysPt address) {
437 	Bit32u ret = mem_readb_inline(address);
438 	ret       |= mem_readb_inline(address+1) << 8;
439 	ret       |= mem_readb_inline(address+2) << 16;
440 	ret       |= mem_readb_inline(address+3) << 24;
441 	return ret;
442 }
443 
444 
mem_unalignedwritew(PhysPt address,Bit16u val)445 void mem_unalignedwritew(PhysPt address,Bit16u val) {
446 	mem_writeb_inline(address,(Bit8u)val);val>>=8;
447 	mem_writeb_inline(address+1,(Bit8u)val);
448 }
449 
mem_unalignedwrited(PhysPt address,Bit32u val)450 void mem_unalignedwrited(PhysPt address,Bit32u val) {
451 	mem_writeb_inline(address,(Bit8u)val);val>>=8;
452 	mem_writeb_inline(address+1,(Bit8u)val);val>>=8;
453 	mem_writeb_inline(address+2,(Bit8u)val);val>>=8;
454 	mem_writeb_inline(address+3,(Bit8u)val);
455 }
456 
457 
mem_unalignedreadw_checked(PhysPt address,Bit16u * val)458 bool mem_unalignedreadw_checked(PhysPt address, Bit16u * val) {
459 	Bit8u rval1,rval2;
460 	if (mem_readb_checked(address+0, &rval1)) return true;
461 	if (mem_readb_checked(address+1, &rval2)) return true;
462 	*val=(Bit16u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8));
463 	return false;
464 }
465 
mem_unalignedreadd_checked(PhysPt address,Bit32u * val)466 bool mem_unalignedreadd_checked(PhysPt address, Bit32u * val) {
467 	Bit8u rval1,rval2,rval3,rval4;
468 	if (mem_readb_checked(address+0, &rval1)) return true;
469 	if (mem_readb_checked(address+1, &rval2)) return true;
470 	if (mem_readb_checked(address+2, &rval3)) return true;
471 	if (mem_readb_checked(address+3, &rval4)) return true;
472 	*val=(Bit32u)(((Bit8u)rval1) | (((Bit8u)rval2) << 8) | (((Bit8u)rval3) << 16) | (((Bit8u)rval4) << 24));
473 	return false;
474 }
475 
mem_unalignedwritew_checked(PhysPt address,Bit16u val)476 bool mem_unalignedwritew_checked(PhysPt address,Bit16u val) {
477 	if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
478 	if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;
479 	return false;
480 }
481 
mem_unalignedwrited_checked(PhysPt address,Bit32u val)482 bool mem_unalignedwrited_checked(PhysPt address,Bit32u val) {
483 	if (mem_writeb_checked(address,(Bit8u)(val & 0xff))) return true;val>>=8;
484 	if (mem_writeb_checked(address+1,(Bit8u)(val & 0xff))) return true;val>>=8;
485 	if (mem_writeb_checked(address+2,(Bit8u)(val & 0xff))) return true;val>>=8;
486 	if (mem_writeb_checked(address+3,(Bit8u)(val & 0xff))) return true;
487 	return false;
488 }
489 
mem_readb(PhysPt address)490 Bit8u mem_readb(PhysPt address) {
491 	return mem_readb_inline(address);
492 }
493 
mem_readw(PhysPt address)494 Bit16u mem_readw(PhysPt address) {
495 	return mem_readw_inline(address);
496 }
497 
mem_readd(PhysPt address)498 Bit32u mem_readd(PhysPt address) {
499 	return mem_readd_inline(address);
500 }
501 
mem_writeb(PhysPt address,Bit8u val)502 void mem_writeb(PhysPt address,Bit8u val) {
503 	mem_writeb_inline(address,val);
504 }
505 
mem_writew(PhysPt address,Bit16u val)506 void mem_writew(PhysPt address,Bit16u val) {
507 	mem_writew_inline(address,val);
508 }
509 
mem_writed(PhysPt address,Bit32u val)510 void mem_writed(PhysPt address,Bit32u val) {
511 	mem_writed_inline(address,val);
512 }
513 
write_p92(Bitu port,Bitu val,Bitu iolen)514 static void write_p92(Bitu port,Bitu val,Bitu iolen) {
515 	// Bit 0 = system reset (switch back to real mode)
516 	if (val&1) E_Exit("XMS: CPU reset via port 0x92 not supported.");
517 	memory.a20.controlport = val & ~2;
518 	MEM_A20_Enable((val & 2)>0);
519 }
520 
read_p92(Bitu port,Bitu iolen)521 static Bitu read_p92(Bitu port,Bitu iolen) {
522 	return memory.a20.controlport | (memory.a20.enabled ? 0x02 : 0);
523 }
524 
RemoveEMSPageFrame(void)525 void RemoveEMSPageFrame(void) {
526 	/* Setup rom at 0xe0000-0xf0000 */
527 	for (Bitu ct=0xe0;ct<0xf0;ct++) {
528 		memory.phandlers[ct] = &rom_page_handler;
529 	}
530 }
531 
PreparePCJRCartRom(void)532 void PreparePCJRCartRom(void) {
533 	/* Setup rom at 0xd0000-0xe0000 */
534 	for (Bitu ct=0xd0;ct<0xe0;ct++) {
535 		memory.phandlers[ct] = &rom_page_handler;
536 	}
537 }
538 
GetMemBase(void)539 HostPt GetMemBase(void) { return MemBase; }
540 
541 class MEMORY:public Module_base{
542 private:
543 	IO_ReadHandleObject ReadHandler;
544 	IO_WriteHandleObject WriteHandler;
545 public:
MEMORY(Section * configuration)546 	MEMORY(Section* configuration):Module_base(configuration){
547 		Bitu i;
548 		Section_prop * section=static_cast<Section_prop *>(configuration);
549 
550 		/* Setup the Physical Page Links */
551 		Bitu memsize=section->Get_int("memsize");
552 
553 		if (memsize < 1) memsize = 1;
554 		/* max 63 to solve problems with certain xms handlers */
555 		if (memsize > MAX_MEMORY-1) {
556 			LOG_MSG("Maximum memory size is %d MB",MAX_MEMORY - 1);
557 			memsize = MAX_MEMORY-1;
558 		}
559 		if (memsize > SAFE_MEMORY-1) {
560 			LOG_MSG("Memory sizes above %d MB are NOT recommended.",SAFE_MEMORY - 1);
561 			LOG_MSG("Stick with the default values unless you are absolutely certain.");
562 		}
563 		MemBase = new Bit8u[memsize*1024*1024];
564 		if (!MemBase) E_Exit("Can't allocate main memory of %d MB",memsize);
565 		/* Clear the memory, as new doesn't always give zeroed memory
566 		 * (Visual C debug mode). We want zeroed memory though. */
567 		memset((void*)MemBase,0,memsize*1024*1024);
568 		memory.pages = (memsize*1024*1024)/4096;
569 		/* Allocate the data for the different page information blocks */
570 		memory.phandlers=new  PageHandler * [memory.pages];
571 		memory.mhandles=new MemHandle [memory.pages];
572 		for (i = 0;i < memory.pages;i++) {
573 			memory.phandlers[i] = &ram_page_handler;
574 			memory.mhandles[i] = 0;				//Set to 0 for memory allocation
575 		}
576 		/* Setup rom at 0xc0000-0xc8000 */
577 		for (i=0xc0;i<0xc8;i++) {
578 			memory.phandlers[i] = &rom_page_handler;
579 		}
580 		/* Setup rom at 0xf0000-0x100000 */
581 		for (i=0xf0;i<0x100;i++) {
582 			memory.phandlers[i] = &rom_page_handler;
583 		}
584 		if (machine==MCH_PCJR) {
585 			/* Setup cartridge rom at 0xe0000-0xf0000 */
586 			for (i=0xe0;i<0xf0;i++) {
587 				memory.phandlers[i] = &rom_page_handler;
588 			}
589 		}
590 		/* Reset some links */
591 		memory.links.used = 0;
592 		// A20 Line - PS/2 system control port A
593 		WriteHandler.Install(0x92,write_p92,IO_MB);
594 		ReadHandler.Install(0x92,read_p92,IO_MB);
595 		MEM_A20_Enable(false);
596 	}
~MEMORY()597 	~MEMORY(){
598 		delete [] MemBase;
599 		delete [] memory.phandlers;
600 		delete [] memory.mhandles;
601 	}
602 };
603 
604 
605 static MEMORY* test;
606 
MEM_ShutDown(Section * sec)607 static void MEM_ShutDown(Section * sec) {
608 	delete test;
609 }
610 
MEM_Init(Section * sec)611 void MEM_Init(Section * sec) {
612 	/* shutdown function */
613 	test = new MEMORY(sec);
614 	sec->AddDestroyFunction(&MEM_ShutDown);
615 }
616