ALLOC.CPP (23303B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /*************************************************************************** 17 ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** 18 *************************************************************************** 19 * * 20 * Project Name : Westwood Library * 21 * * 22 * File Name : ALLOC.CPP * 23 * * 24 * Programmer : Joe L. Bostic * 25 * * 26 * Start Date : February 1, 1992 * 27 * * 28 * Last Update : March 9, 1995 [JLB] * 29 * * 30 *-------------------------------------------------------------------------* 31 * Functions: * 32 * Alloc -- Allocates system RAM. * 33 * Ram_Free -- Determines the largest free chunk of RAM. * 34 * Free -- Free an Alloc'ed block of RAM. * 35 * Resize_Alloc -- Change the size of an allocated block. * 36 * Heap_Size -- Size of the heap we have. * 37 * Total_Ram_Free -- Total amount of free RAM. * 38 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 39 40 #if (0) 41 42 #include <malloc.h> 43 #include <string.h> 44 #include <stdlib.h> 45 #include <dos.h> 46 #include <bios.h> 47 #include <stdio.h> 48 49 50 #ifndef WWMEM_H 51 #include "wwmem.h" 52 #endif 53 54 55 extern "C" unsigned long Largest_Mem_Block ( void ) ; 56 57 // 58 // use double-word alignment for allocs 59 // 60 #define LONG_ALIGNMENT 1 61 62 /* 63 ** Define the equates necessary to call a DPMI interrupt. 64 */ 65 #define DPMI_INT 0x0031 66 #define DPMI_LOCK_MEM 0x0600 67 #define DPMI_UNLOCK_MEM 0x0601 68 #define LOGGING FALSE 69 /*=========================================================================*/ 70 /* The following PRIVATE functions are in this file: */ 71 /*=========================================================================*/ 72 73 74 /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ 75 76 unsigned long MinRam=0L; // Record of least memory at worst case. 77 unsigned long MaxRam=0L; // Record of total allocated at worst case. 78 static unsigned long TotalRam = 0L; 79 static unsigned long Memory_Calls = 0L; 80 static unsigned long RequestedSystemRam = 8*1024*1024; 81 static unsigned long LargestRamBlock = 0L; 82 83 void (*Memory_Error)(void) = NULL; 84 void (*Memory_Error_Exit)(char *string) = NULL; 85 86 /*************************************************************************** 87 * DPMI_LOCK -- handles locking a block of DPMI memory * 88 * * 89 * INPUT: * 90 * * 91 * OUTPUT: * 92 * * 93 * WARNINGS: * 94 * * 95 * HISTORY: * 96 * 06/23/1995 PWG : Created. * 97 *=========================================================================*/ 98 #include"mono.h" 99 void DPMI_Lock(VOID const *ptr, long const size) 100 { 101 union REGS regs; 102 struct SREGS sregs; 103 104 /* 105 ** Lock memory 106 ** AX = 0x600 107 ** BX:CX = starting linear address of memory to lock 108 ** SI:DI = size of region to lock (in bytes) 109 ** - If Failure, carry flag is set. 110 */ 111 memset (®s, 0 ,sizeof(regs)); 112 segread (&sregs); 113 regs.x.eax = DPMI_LOCK_MEM; 114 regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; 115 regs.x.ecx = ((long)ptr & 0x0000ffff); 116 regs.x.esi = ((long)size & 0xffff0000) >> 16; 117 regs.x.edi = ((long)size & 0x0000ffff); 118 int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI 119 // if (regs.x.cflag) { 120 // } 121 #if(0) 122 char *temp = (char *)ptr; 123 char hold; 124 for (int lp = 0; lp < size; lp += 2048) { 125 hold = *temp; 126 temp += 2048; 127 } 128 #endif 129 130 } 131 132 /*************************************************************************** 133 * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * 134 * * 135 * INPUT: * 136 * * 137 * OUTPUT: * 138 * * 139 * WARNINGS: * 140 * * 141 * HISTORY: * 142 * 06/23/1995 PWG : Created. * 143 *=========================================================================*/ 144 void DPMI_Unlock(void const *ptr, long const size) 145 { 146 union REGS regs; 147 struct SREGS sregs; 148 149 /* 150 ** Unlock the memory 151 */ 152 memset (®s, 0 ,sizeof(regs)); 153 segread (&sregs); 154 regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call 155 regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; 156 regs.x.ecx = ((long)ptr & 0x0000ffff); 157 regs.x.esi = ((long)size & 0xffff0000) >> 16; 158 regs.x.edi = ((long)size & 0x0000ffff); 159 int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI 160 // if (regs.x.cflag) { 161 // } 162 163 } 164 165 /*************************************************************************** 166 * Alloc -- Allocates system RAM. * 167 * * 168 * This is the basic RAM allocation function. It is used for all * 169 * memory allocations needed by the system or the main program. * 170 * * 171 * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * 172 * * 173 * flags -- Memory allocation control flags. * 174 * MEM_NORMAL: No special flags. * 175 * MEM_CLEAR: Zero out memory block. * 176 * MEM_NEW: Called by a new. * 177 * * 178 * OUTPUT: Returns with pointer to allocated block. If NULL was returned * 179 * it indicates a failure to allocate. Note: NULL will never be * 180 * returned if the standard library allocation error routine is * 181 * used. * 182 * * 183 * WARNINGS: If you replace the standard memory allocation error routine * 184 * and make it so that Alloc CAN return with a NULL, be sure * 185 * and check for this in your code. * 186 * * 187 * HISTORY: * 188 * 09/03/1991 JLB : Documented. * 189 * 08/09/1993 JLB : Updated with EMS memory support. * 190 * 04/28/1994 JAW : Updated to 32bit Protected mode. * 191 * 03/09/1995 JLB : Fixed * 192 *=========================================================================*/ 193 void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) 194 { 195 union REGS regs ; 196 struct SREGS sregs ; 197 unsigned char *retval=NULL; // Pointer to allocated block. 198 unsigned long original_size; // Original allocation size. 199 unsigned long bytesfree; // Number of free bytes. 200 long *longptr=NULL; // Pointer used to store selector 201 static unsigned char _allocinit=0; 202 203 204 // 205 // Init memory system by finding largest block to alloc 206 // then allocate it to get one large heap and free it. 207 // There may be more memory available from DPMI but we only are 208 // for now allocating and freeing the first largest block. 209 // 210 if ( !_allocinit ) { 211 unsigned long largestblock = Largest_Mem_Block(); 212 largestblock -= 1024; // subtract for heap header and misc 213 largestblock &= 0xffff0000; // forcing to 64K boundary 214 215 if ( largestblock ) { 216 LargestRamBlock = MIN( largestblock, RequestedSystemRam ); 217 unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); 218 if ( lptr ) { 219 free( (void *)lptr ); 220 } 221 } 222 223 /* 224 ** Initialize the total ram available value. 225 */ 226 TotalRam = Total_Ram_Free(MEM_NORMAL); 227 228 _allocinit = 1; 229 } 230 231 /* 232 ** Save the original allocated space size so that we can clear the 233 ** exact amount of RAM if they specified MEM_CLEAR. 234 */ 235 original_size = bytes_to_alloc; 236 237 /* 238 ** Reserve one byte for the header of the memory we allocated. 239 ** We will store the flags variable there for later use. 240 */ 241 #if (LONG_ALIGNMENT) 242 bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; 243 #else 244 bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; 245 #endif 246 247 248 // Try to allocate the memory out of the protected mode memory 249 // chain if we did not require a real mode allocation. If this 250 // fails we will have to try to allocate it out of real mode memory. 251 // Real mode memory is a last resort because some types of applications 252 // require real mode memory. 253 if (!(flags & MEM_REAL)) { 254 retval = (unsigned char*)malloc(bytes_to_alloc); 255 } 256 257 // Try to allocate the memory out of the real mode memory using DPMI 258 // service 0x100. Note that retval will be null if we are requesting 259 // real mode memory so that we do not have to explicitly check for the 260 // real mode flag. Remember we need to reserve room for the dos 261 // selector value at the beginning of our allocated block so rather than 262 // adding fifteen and rounding, we need to add 19 and round. 263 if (!retval) { 264 flags = (MemoryFlagType)(flags | MEM_REAL); 265 regs.x.eax = 0x100; 266 regs.x.ebx = (bytes_to_alloc + 19) >> 4; 267 if (regs.x.ebx & 0xFFFF0000) { 268 retval = NULL; 269 } else { 270 segread ( & sregs ) ; 271 int386x ( 0x31 , & regs, & regs , & sregs ) ; 272 if (regs.x.cflag) 273 retval = NULL; 274 else { 275 #if (LONG_ALIGNMENT) 276 longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); 277 #else 278 longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); 279 #endif 280 *longptr++ = regs.x.edx & 0xFFFF; 281 retval = (unsigned char *)longptr; 282 } 283 } 284 } 285 286 // If the alloc failed then we need to signify a memory error. 287 if (retval == NULL) { 288 if (Memory_Error != NULL) 289 Memory_Error(); 290 return NULL; 291 } 292 293 // If the memory needs to be DPMI locked then we should store the 294 // original size in the header before we store the flags. 295 if (flags & MEM_LOCK) { 296 longptr = (long *)retval; 297 *longptr++ = original_size; 298 retval = (unsigned char *)longptr; 299 } 300 301 302 // Now that we know the alloc was sucessful (and for an extra byte 303 // more than the user wanted) we need to stick in the memory flags. 304 #if (LONG_ALIGNMENT) 305 if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { 306 // 307 // WARNING!!!!!!!!!! 308 // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! 309 // it reads the actual block size before the ptr returned. 310 // then eors and uses the upper word for a validation later on free. 311 // 312 longptr = (long *)retval; 313 *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; 314 *retval++ = flags; 315 *retval++ = (unsigned char)(flags ^ 0xff); 316 retval += 2; 317 } 318 else { 319 *retval++ = flags; 320 *retval++ = (unsigned char)(flags ^ 0xff); 321 *retval++ = 0; 322 *retval++ = 0; 323 } 324 #else 325 *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); 326 #endif 327 328 // If the memory needed to be DPMI locked then set it up so it 329 // is locked. 330 if (flags & MEM_LOCK) { 331 DPMI_Lock(retval, original_size); 332 } 333 334 /* Clear the space if they wanted it clear */ 335 336 if (flags & MEM_CLEAR) { 337 unsigned char *ptr; // Working memory block pointer. 338 339 ptr = retval; 340 memset(ptr, '\0', original_size); 341 } 342 343 bytesfree = Total_Ram_Free(MEM_NORMAL); 344 if (bytesfree < MinRam) { 345 MinRam = bytesfree; 346 } 347 if (TotalRam-bytesfree > MaxRam) { 348 MaxRam = TotalRam-bytesfree; 349 } 350 351 Memory_Calls++; 352 353 #if(LOGGING) 354 int val = _heapchk(); 355 356 FILE *file = fopen("mem.txt","at"); 357 fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", 358 retval, 359 original_size, 360 bytes_to_alloc, 361 flags, 362 val); 363 fclose(file); 364 #endif 365 366 return(retval); 367 } 368 369 370 /*************************************************************************** 371 * Free -- Free an Alloc'ed block of RAM. * 372 * * 373 * FUNCTION: * 374 * * 375 * INPUT: A pointer to a block of RAM from Alloc. * 376 * * 377 * OUTPUT: None. * 378 * * 379 * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * 380 * * 381 * HISTORY: * 382 * 05/25/1990 : Created. * 383 ***************************************************************************/ 384 void Free(void const *pointer) 385 { 386 union REGS regs ; 387 struct SREGS sregs ; 388 389 void const *original = pointer; 390 char string[80]; 391 392 if (pointer) { 393 /* 394 ** Get a pointer to the flags that we stored off. 395 */ 396 #if (LONG_ALIGNMENT) 397 unsigned char *byteptr = ((unsigned char *)pointer) - 4; 398 399 // 400 // validate the flags with and eor of the flags 401 // 402 if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { 403 if (Memory_Error_Exit != NULL) { 404 sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); 405 Memory_Error_Exit( string ); 406 } 407 } 408 else { 409 if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { 410 unsigned short *wordptr = (unsigned short *)(byteptr - 2); 411 412 // 413 // WARNING!!!!!!!!!! 414 // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! 415 // it reads the actual block size before the ptr to be freed. 416 // then compares with the EOR to the value stored during allocation. 417 // 418 if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { 419 if (Memory_Error_Exit != NULL) { 420 sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); 421 Memory_Error_Exit( string ); 422 } 423 } 424 } 425 else if ( *(byteptr + 2) || *(byteptr + 3) ) { 426 if (Memory_Error_Exit != NULL) { 427 sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); 428 Memory_Error_Exit( string ); 429 } 430 } 431 } 432 // if ( *byteptr != (*(byteptr + 1) ^ 0xff) || 433 // *(byteptr + 2) || *(byteptr + 3) ) { 434 // if (Memory_Error_Exit != NULL) { 435 // sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); 436 // Memory_Error_Exit( string ); 437 // } 438 // } 439 #else 440 unsigned char *byteptr = ((unsigned char *)pointer) - 1; 441 442 if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { 443 if (Memory_Error_Exit != NULL) { 444 sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); 445 Memory_Error_Exit( string ); 446 } 447 } 448 #endif 449 450 /* 451 ** Check to see if this was locked me and if it was unlock it. 452 */ 453 if (*byteptr & MEM_LOCK) { 454 long *longptr = ((long *)byteptr) - 1; 455 DPMI_Unlock(pointer, *longptr); 456 pointer = (void *)longptr; 457 } else 458 pointer = (void *)byteptr; 459 460 #if(LOGGING) 461 int val = _heapchk(); 462 FILE *file = fopen("mem.txt","at"); 463 fprintf(file, "%P Free flags = %d, Heap = %d\n", 464 original, 465 *byteptr, 466 val); 467 fclose(file); 468 #endif 469 470 // If the pointer is a real mode pointer than it will point to the 471 // first megabyte of system memory. If it does than we need to 472 // use DPMI to free it. 473 if (*byteptr & MEM_REAL) { 474 regs.x.eax = 0x101; 475 regs.x.edx = *(((long *)pointer) - 1); 476 segread ( & sregs ) ; 477 int386x(0x31, ®s, ®s, &sregs); 478 } else { 479 free((void *)pointer); 480 } 481 Memory_Calls--; 482 } 483 } 484 485 486 /*************************************************************************** 487 * Resize_Alloc -- Change the size of an allocated block. * 488 * * 489 * This routine will take a previously allocated block and change its * 490 * size without unnecessarily altering its contents. * 491 * * 492 * INPUT: pointer -- Pointer to the original memory allocation. * 493 * * 494 * new_size -- Size in bytes that it will be converted to. * 495 * * 496 * OUTPUT: Returns with a pointer to the new allocation. * 497 * * 498 * WARNINGS: ??? * 499 * * 500 * HISTORY: * 501 * 02/01/1992 JLB : Commented. * 502 *=========================================================================*/ 503 void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) 504 { 505 506 unsigned long *temp; 507 // unsigned long diff, flags; 508 509 temp = (unsigned long*)original_ptr; 510 511 /* ReAlloc the space */ 512 temp = (unsigned long *)realloc(temp, new_size_in_bytes); 513 if (temp == NULL) { 514 if (Memory_Error != NULL) 515 Memory_Error(); 516 return NULL; 517 } 518 519 return(temp); 520 } 521 522 523 /*************************************************************************** 524 * Ram_Free -- Determines the largest free chunk of RAM. * 525 * * 526 * Use this routine to determine the largest free chunk of available * 527 * RAM for allocation. It also performs a check of the memory chain. * 528 * * 529 * INPUT: none * 530 * * 531 * OUTPUT: Returns with the size of the largest free chunk of RAM. * 532 * * 533 * WARNINGS: This does not return the TOTAL memory free, only the * 534 * largest free chunk. * 535 * * 536 * HISTORY: * 537 * 09/03/1991 JLB : Commented. * 538 *=========================================================================*/ 539 long Ram_Free(MemoryFlagType) 540 { 541 return(_memmax()); 542 // return Largest_Mem_Block(); 543 } 544 545 546 /*************************************************************************** 547 * Heap_Size -- Size of the heap we have. * 548 * * 549 * * 550 * * 551 * INPUT: * 552 * * 553 * OUTPUT: * 554 * * 555 * WARNINGS: * 556 * * 557 * HISTORY: * 558 * 06/21/1994 SKB : Created. * 559 *=========================================================================*/ 560 long Heap_Size(MemoryFlagType ) 561 { 562 if (!TotalRam) { 563 TotalRam = Total_Ram_Free(MEM_NORMAL); 564 } 565 return(TotalRam); 566 } 567 568 569 /*************************************************************************** 570 * Total_Ram_Free -- Total amount of free RAM. * 571 * * 572 * * 573 * * 574 * INPUT: * 575 * * 576 * OUTPUT: * 577 * * 578 * WARNINGS: * 579 * * 580 * HISTORY: * 581 * 06/21/1994 SKB : Created. * 582 * 03/09/1995 JLB : Uses prerecorded heap size maximum. * 583 *=========================================================================*/ 584 long Total_Ram_Free(MemoryFlagType ) 585 { 586 return(_memavl()); 587 // return Largest_Mem_Block () ; 588 } 589 590 #endif