summaryrefslogblamecommitdiffstats
path: root/private/mvdm/softpc.new/host/src/nt_mem.c
blob: 73578a04bfe4af58e0ee8d155a975797e4e78d61 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                 
/*[
 *
 *  Name:	   nt_mem.c
 *
 *  Derived From:   (original)
 *
 *  Author:         Jerry Sexton
 *
 *  Created On:     7 December 1994
 *
 *  Coding Stds:    2.4
 *
 *  Purpose:        This module implements the memory management functions
 *                  required for 486 NT.
 *
 *  Include File:   nt_mem.h
 *
 *  Copyright Insignia Solutions Ltd., 1994. All rights reserved.
 *
]*/

#ifdef CPU_40_STYLE

#if defined(DBG)
#define DEBUG_MEM YES_PLEASE
//#define DEBUG_MEM_DUMP 1
#endif

/* Need all of the following to include nt.h and windows.h in the same file. */
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>

#include "insignia.h"
#include "host_def.h"

#include <string.h>
#include <stdlib.h>

#include "nt_mem.h"
#include "debug.h"
#include "sas.h"

/* Make local symbols visible if debugging. */
#ifdef DEBUG_MEM
#define LOCAL

GLOBAL VOID DumpAllocationHeaders IFN1();

#endif /* DEBUG_MEM */

/* Macros and typedefs. */

/* Entry in header table describing sections of memory. */
typedef struct _SECTION_HEADER
{
    struct _SECTION_HEADER   *prev;     /* Previous header in linked list. */
    IU8                       flags;    /* Is header valid/allocated? */
    IU8                      *address;  /* Address of corresponding section. */
    IU32                      size;     /* Size of corresponding section. */
    struct _SECTION_HEADER   *next;     /* Next header in linked list. */
} SECTION_HEADER;

/* Possible values for 'flags' field of SECTION_HEADER structure. */
#define HDR_VALID_FLAG  0x1     /* Header points to the beginning of a chunk
                                   or free section. */
#define HDR_ALLOC_FLAG	0x2	/* Header points to an allocated chunk. */
#define HDR_COMMITTED_FLAG 0x4	/* Header points to committed chunk, not moveable */
#define HDR_REMAP_FLAG	0x8	/* chunk is remapped by "AddVirtualMemory" */

#define HDR_VALID_ALLOC (HDR_VALID_FLAG|HDR_ALLOC_FLAG)
                                /* Header points to a section that is valid
                                   and allocated. */

#define SECTION_IS_FREE(ptr)    (!((ptr)->flags & HDR_ALLOC_FLAG))
                                /* Is a section's allocated flag set? */

#define SECTION_IS_UNCOMMITTED(ptr)    (!((ptr)->flags & HDR_COMMITTED_FLAG))
                                /* Is a section's committed flag set? */

/* Enumerated type to pass to header management functions. */
typedef enum
{
    SECT_ALLOC,
    SECT_FREE
} SECT_TYPE;

#define ONE_K           (1<<10)         /* Speaks for itself. */
#define ONE_MEG         (1<<20)         /* Ditto. */
#define PAGE_SIZE       (4 * ONE_K)     /* Intel memory page granularity. */
#define PAGE_MASK       (PAGE_SIZE - 1) /* Mask used in rounding to boundary. */
#define PAGE_SHIFT	12		/* Amount to shift by to get page. */

// The following two defines have been replace with MaxIntelMemorySize and
//MaxIntelMemorySize/PAGE_SIZE
//#define MAX_XMS_SIZE	 (128 * ONE_MEG) /* Maximum memory size. */
//#define MAX_HEADER_SIZE (MAX_XMS_SIZE / PAGE_SIZE)

#define A20_WRAP_SIZE   (0xfff0)        /* Size of A20 wrap area beyond 1M. */

/* Convert header table entries to addresses. */
#define HEADER_TO_ADDRESS(header)   \
    (intelMem + (((header) - headerTable) << PAGE_SHIFT))

/* Convert addresses to header table entries. */
#define ADDRESS_TO_HEADER(address)   \
    (headerTable + (((address) - intelMem) >> PAGE_SHIFT))

/* Local variables. */
LOCAL IBOOL              memInit = FALSE;
                                        /* Is memory system initialised? */
LOCAL IU8               *intelMem;      /* Pointer to intel memory block. */
LOCAL SECTION_HEADER    *headerTable;   /* Table of memory section headers. */
LOCAL IU32               totalFree;     /* Total amount of free memory. */
LOCAL IU32		 commitShift;	/* Shift to get commitment granularity */
LOCAL int		 ZapValue;	/* Value to set allocated memory to */
LOCAL IU32       WOWforceIncrAlloc = 0; /* Force increasing linear address */
LOCAL SECTION_HEADER  *lastAllocPtr = NULL; /* Last chunk allocated. */

/* Prototypes for local functions. */
LOCAL SECTION_HEADER *addHeaderEntry IPT5(
    SECTION_HEADER *, prevHeader,
    SECTION_HEADER *, nextHeader,
    SECT_TYPE, allocFree,
    IU8 *, intelAddr,
    IU32, size);
LOCAL IBOOL deleteHeaderEntry IPT1(
    SECTION_HEADER *, header);
LOCAL void exclusiveHeaderPages IPT5(
    IHPE, tableAddress,
    IHPE, prev,
    IHPE, next,
    IHP *, allocAddr,
    IU32 *, allocSize);
LOCAL void exclusiveChunkPages IPT4(
    SECTION_HEADER *, chunkHeader,
    IHP *, allocAddr,
    IU32 *, allocSize,
    BOOL, Commit);
LOCAL void exclusiveAllocPages IPT6(
    IHPE, address,
    IU32, size,
    IHPE, prevAllocLastAddr,
    IHPE, nextAllocFirstAddr,
    IHP *, allocAddr,
    IU32 *, allocSize);

/* Global Functions. */

/*(
============================== InitIntelMemory =================================
PURPOSE:
        This function reserves the entire Intel address space, committing the
        first 1 meg and leaving the rest to be committed as and when required
        when new chunks are allocated.
INPUT:
        None.
OUTPUT:
        Return value - pointer to beginning of intel memory.
================================================================================
)*/
GLOBAL IU8 *InitIntelMemory IFN1(IU32, MaxIntelMemorySize)
{
    SYSTEM_INFO  SystemInfo;        /* Passed to GetSystemInfo API. */
    DWORD   tabSize;                /* Max. size of header table in bytes. */
    IS32    commitGran;		    /* Commitment granularity. */
    IU32    temp,		    /* Used in computing commitShift. */
            initialCommitSize;      /* Size of real mode area to commit. */
    SECTION_HEADER  *entryPtr;      /* Header entry for real mode area. */

#ifdef DEBUG_MEM
    printf("NTVDM:InitIntelMemory(%lx [%dK])\n",
	   MaxIntelMemorySize, MaxIntelMemorySize/ONE_K);
#endif

    /*
     * Setup the value to initalise allocated memory with
     */
    {
	char *env;

	if((env = getenv("CPU_INITIALISE_MEMORY")) != 0)
	    ZapValue = strtol(env, 0, 16);
	else
	    ZapValue = 0xf4;	   /* HLT instruction */

    }

    /*
     * Find out machine's commitment granularity, and store it as a number of
     * bits to shift an address right to give the allocation page it is in.
     * This assumes the allocation granularity is a power of two so complain
     * if it isn't.
     */
    GetSystemInfo(&SystemInfo);
    commitGran = (IS32) SystemInfo.dwPageSize;
    if ((-commitGran & commitGran) != commitGran)
    {
	always_trace1("Commitment granularity %#x not a power of two",
		      commitGran);
        return((IU8 *) NULL);
    }
#ifdef DEBUG_MEM
    printf("NTVDM:Commitment granularity is %lx\n", commitGran);
#endif
    commitShift = 0;
    for (temp = commitGran; temp > 1; temp >>= 1)
	commitShift++;

    /* Reserve the entire memory space. */
    intelMem = (IU8 *) VirtualAlloc((LPVOID) NULL,
				    (DWORD) MaxIntelMemorySize,
                                    (DWORD) MEM_RESERVE,
                                    (DWORD) PAGE_READWRITE);
    if (!intelMem)
    {
	always_trace1("Failed to reserve %dM of memory", MaxIntelMemorySize >> 20);
        return((IU8 *) NULL);
    }

    /* 
     * Allocate the bottom 1 megabyte plus the 20-bit wrap area. Round this
     * to an Intel page boundary as this is the granularity of this system.
     */
    initialCommitSize = (ONE_MEG + A20_WRAP_SIZE + PAGE_MASK) &
                        (IU32) ~PAGE_MASK;
    if (VirtualAlloc((LPVOID) intelMem,
                     (DWORD) initialCommitSize,
                     (DWORD) MEM_COMMIT,
                     (DWORD) PAGE_READWRITE) == NULL)
    {
	always_trace0("Could not commit real mode area");
	VirtualFree (intelMem, 0, MEM_RELEASE);    /* Free Intel memory */
        return((IU8 *) NULL);
    }

    /* Reserve enough space for the entire header table. */
    tabSize = (MaxIntelMemorySize/PAGE_SIZE) * sizeof(SECTION_HEADER);
    headerTable = (SECTION_HEADER *) VirtualAlloc((LPVOID) NULL,
                                                  tabSize,
                                                  (DWORD) MEM_RESERVE,
						  (DWORD) PAGE_READWRITE);

    if (!headerTable)
    {
	always_trace0("Failed to reserve header table");
	VirtualFree (intelMem, 0, MEM_RELEASE);    /* Free Intel memory */
        return((IU8 *) NULL);
    }

    /*
     * Initialise linked list with pointers to initial 1M and remaining free
     * space and store initial size of free space.
     */
    entryPtr = addHeaderEntry((SECTION_HEADER *) NULL, (SECTION_HEADER *) NULL,
			      SECT_ALLOC, intelMem, initialCommitSize);

    entryPtr->flags |= HDR_COMMITTED_FLAG;
    totalFree = MaxIntelMemorySize - initialCommitSize;

    (void) addHeaderEntry(entryPtr, (SECTION_HEADER *) NULL, SECT_FREE,
                          intelMem + initialCommitSize, totalFree);

    /* Return the address of the start of memory. */
    memInit = TRUE;
    return(intelMem);
}

/*(
============================== FreeIntelMemory =================================
PURPOSE:
	This function frees the entire Intel address space
INPUT:
        None.
OUTPUT:
	None.
================================================================================
)*/

GLOBAL	VOID FreeIntelMemory IFN0()
{
#ifdef DEBUG_MEM
    printf("NTVDM:FreeIntelMemory\n");
#endif

    /* Free Intel memory */
    VirtualFree (intelMem, 0, MEM_RELEASE);

    /* Free allocation control structure */
    VirtualFree (headerTable, 0, MEM_RELEASE);
}

/*(
============================ SetWOWforceIncrAlloc ===============================
PURPOSE:
    When TRUE is passed, this function sets the WOWforceIncrAlloc variable to
    the current thread ID. This will force VdmAllocateVirtualMemory to allocate
    blocks of memory with ever increasing linear address's for this thread only.
    When FALSE is passed, WOWforceIncrAlloc is cleared and the default
    allocation scheme is used. This function is called by WOW based on an app
    compatibility flag at start task time and end task time. Power Builder 4
    depends on this. This is not a general solution for multiple threads. Since
    the problem we're trying to work around occurs at load time, the allocation
    strategy is applied to the most recent thread only. This might not work if
    multiple instances of the app are started at the same time.

INPUT:
    None.
OUTPUT:
    None.
================================================================================
)*/

GLOBAL  VOID SetWOWforceIncrAlloc IFN1(IBOOL, iEnable)
{
    if (iEnable) {
        WOWforceIncrAlloc = GetCurrentThreadId();
    }
    else {
        WOWforceIncrAlloc = 0;
    }
#ifdef DEBUG_MEM
    printf("NTVDM:SetWOWforceIncrAlloc, WOWforceIncrAlloc: %X\n", WOWforceIncrAlloc);
#endif
}


/*(
========================= VdmAllocateVirtualMemory =============================
PURPOSE:
        This interface will allocate the specified number of bytes of virtual
        memory, returning the Intel linear address in the variable pointed to
        by the Address parameter.  The Intel linear address will be page
        aligned (this is important).  In the event that the memory is
        allocated, STATUS_SUCCESS will be returned.  In the event of failure,
        an appropriate NTSTATUS code will be returned.  (In the event that
        there is a failure in the cpu for which there is not an appropriate
        code, STATUS_UNSUCCESSFUL can be returned.  I just want to have an
        opportunity to get more information.)
INPUT:
        Size            - size of memory chunk required in bytes (must be a
			  multiple of 4K).

	Commit		- Commit virtual memory ?
OUTPUT:
	Address 	- INTEL linear address of allocated chunk.
        Return value    - STATUS_SUCCESS, if the memory is allocated, or an
                          appropriate NTSTATUS code if not (if there is no
                          appropriate code STATUS_UNSUCCESSFUL is returned).
================================================================================
)*/
GLOBAL NTSTATUS VdmAllocateVirtualMemory IFN3(PULONG, INTELAddress,
					      ULONG, Size,
					      BOOL, Commit)
{
    SECTION_HEADER  *headerPtr,
                    *newHeader;
    IHP     retAddr,
            commitAddr;
    IU32    commitSize;

#ifdef DEBUG_MEM
    printf("NTVDM:VdmAllocateVirtualMemory(%lx [%dK],%s)\n",
	   Size, Size/ONE_K, Commit ? "COMMIT" : "DONOT_COMMIT");
#endif

    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmAllocateVirtualMemory before initialisation");

    /* Round Size up to a multiple of 4K. */

    if (Size & PAGE_MASK)
	Size = (Size + PAGE_MASK) & (~PAGE_MASK);


    /* Search for a chunk of the required size. If WOWforceIncrAlloc is our */
    /* current thread ID and lastAllocPtr is intialized, force returned     */
    /* chunks to have ever increasing linear address's.                     */
    if ((WOWforceIncrAlloc != 0) && lastAllocPtr
                             && (GetCurrentThreadId() == WOWforceIncrAlloc)) {
#ifdef DEBUG_MEM
    printf("\nNTVDM:VdmAllocateVirtualMemory, using increasing linear address strategy.\n");
#endif
        headerPtr = lastAllocPtr;
    } else {
        headerPtr = &headerTable[0];
    }
    while (headerPtr != NULL)
    {
        if (SECTION_IS_FREE(headerPtr) && (headerPtr->size >= Size))
            break;
        headerPtr = headerPtr->next;
    }

    /* Return failure if there is no chunk of the required size. */
    if (headerPtr == NULL)
        return(STATUS_NO_MEMORY);

    /* Mark the header as an allocated chunk */
    headerPtr->flags |= HDR_ALLOC_FLAG;
    lastAllocPtr = headerPtr;

    /* Add committed status to header. */
    if(Commit)
	headerPtr->flags |= HDR_COMMITTED_FLAG;
    else
	headerPtr->flags &= ~HDR_COMMITTED_FLAG;

    /* Create a new header if there is any space left below the new chunk. */
    if (headerPtr->size > Size)
    {
        newHeader = addHeaderEntry(headerPtr,
                                   headerPtr->next,
                                   SECT_FREE,
                                   headerPtr->address + Size,
                                   headerPtr->size - Size);
        headerPtr->size = Size;
    }

    /* Commit any pages that are unique to this chunk. */
    exclusiveChunkPages(headerPtr, &commitAddr, &commitSize, TRUE);
    if (commitSize && Commit)
    {
        retAddr = VirtualAlloc((LPVOID) commitAddr,
                               (DWORD) commitSize,
                               (DWORD) MEM_COMMIT,
			       (DWORD) PAGE_READWRITE);

        if (retAddr != commitAddr)
        {
            always_trace2("Could not commit %dK at addr %#x",
                          commitSize / ONE_K, commitAddr);
            return(STATUS_NOT_COMMITTED);
        }
    }

    /*
     * Success, so update total free space store and return the address of
     * the new chunk to the caller.
     */
    totalFree -= Size;
    *INTELAddress = ((ULONG) (IHPE) headerPtr->address) - (ULONG) intelMem;

#ifdef DEBUG_MEM
    printf(" => alloc %lxh, commit %lxh\n",
        ((ULONG) (IHPE)headerPtr->address) - (ULONG)intelMem,
        ((ULONG) (IHPE)commitAddr) - (ULONG)intelMem);

#ifdef DEBUG_MEM_DUMP
    DumpAllocationHeaders("after allocate");
#endif
#endif

    return(STATUS_SUCCESS);
}

/*(
========================== VdmCommitVirtualMemory =============================
PURPOSE:
	Commit memory within a previously allocated chunk
INPUT:
	Address     Intel linear address of memory to commit
	Size	    Size of memory to commit
OUTPUT:
        Return value    - STATUS_SUCCESS, if the memory is allocated, or an
			  appropriate NTSTATUS code if not.
===============================================================================
)*/

GLOBAL NTSTATUS VdmCommitVirtualMemory IFN2(ULONG, INTELAddress,
					     ULONG, Size)
{
    IHP     retAddr;

#ifdef DEBUG_MEM
    printf("NTVDM:VdmCommitVirtualMemory(%lxh,%lxh)\n",INTELAddress, Size);
#endif

    retAddr = VirtualAlloc((LPVOID) (intelMem + INTELAddress),
			   (DWORD) Size,
			   (DWORD) MEM_COMMIT,
			   (DWORD) PAGE_READWRITE);

    if(retAddr != (intelMem + INTELAddress))
    {
	always_trace2("Could not commit %dK at addr %#x",
		      Size / ONE_K, INTELAddress);

	return(STATUS_NOT_COMMITTED);
    }

    return(STATUS_SUCCESS);
}

/*(
======================== VdmDeCommitVirtualMemory =============================
PURPOSE:
	Decommit memory within a previously allocated chunk
INPUT:
	Address     Intel linear address of memory to decommit
	Size	    Size of memory to commit
OUTPUT:
        Return value    - STATUS_SUCCESS, if the memory is allocated, or an
			  appropriate NTSTATUS code if not.
===============================================================================
)*/

GLOBAL NTSTATUS VdmDeCommitVirtualMemory IFN2(ULONG, INTELAddress,
					     ULONG, Size)
{
#ifdef DEBUG_MEM
    printf("NTVDM:VdmDeCommitVirtualMemory(%lxh,%lxh)\n",INTELAddress, Size);
#endif


    sas_overwrite_memory(INTELAddress, Size);
	if (!VirtualFree((LPVOID) (intelMem + INTELAddress),
			 (DWORD) Size,
                         (DWORD) MEM_DECOMMIT))
        {
            always_trace2("Could not decommit %dK at addr %#x",
			  Size / ONE_K, INTELAddress);

	    return(STATUS_UNSUCCESSFUL);
        }

    return(STATUS_SUCCESS);
}

/*(
============================ VdmFreeVirtualMemory ==============================
PURPOSE:
        This interface will free the allocation at the specified Intel linear
        address.  The above notes on return value apply.
INPUT:
	INTELAddress	- INTEL address to be freed.
OUTPUT:
        Return value    - STATUS_SUCCESS, if the memory is allocated, or an
                          appropriate NTSTATUS code if not (if there is no
                          appropriate code STATUS_UNSUCCESSFUL is returned).
================================================================================
)*/
GLOBAL NTSTATUS VdmFreeVirtualMemory IFN1(ULONG, INTELAddress)
{
    SECTION_HEADER  *headerPtr;
    IU32    size,
            decommitSize;
    IHP     decommitAddr;
    ULONG   Address;


    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmFreeVirtualMemory before initialisation");

#ifdef DEBUG_MEM
    printf("NTVDM:VdmFreeVirtualMemory(%lxh)\n",INTELAddress);
#endif

    /* Calculate chunk address */
    Address = INTELAddress + (ULONG)intelMem;

    /* Get header table entry for address. */
    headerPtr = ADDRESS_TO_HEADER((IU8 *) Address);

    /*
     * Check address is correctly aligned and at the top of an allocated
     * chunk.
     */
    if ((Address & PAGE_MASK) ||
        (headerPtr->flags & HDR_VALID_ALLOC != HDR_VALID_ALLOC))
    {
        always_trace0("Tried to free invalid address");
        return(STATUS_MEMORY_NOT_ALLOCATED);
    }

    /* Don't allow freeing of bottom 1 Meg. */
    if (headerPtr == &headerTable[0])
    {
        always_trace0("Tried to free real mode area");
        return(STATUS_UNSUCCESSFUL);
    }

    /* DON'T free if still mapped to another area of host memory by
	 * VdmAddVirtualMemory() - ie the PhysRecStructs are in a munged
	 * state. Free can get called before Remove, and Remove will
	 * unflag it and call here later.
     */
	if (headerPtr->flags & HDR_REMAP_FLAG)
	{
        always_trace0("Tried to free remapped area");
        return(STATUS_SUCCESS);
    }


    /* Save the size of the chunk before decommitting it. */
    size = headerPtr->size;

    /* Tell the CPU that the contents of the memory are no longer valid. */
    sas_overwrite_memory(INTELAddress, size);

    /* Decommit any memory that is unique to this chunk. */
    exclusiveChunkPages(headerPtr, &decommitAddr, &decommitSize, FALSE);
    if (decommitSize)
    {
        if (!VirtualFree((LPVOID) decommitAddr,
                         (DWORD) decommitSize,
                         (DWORD) MEM_DECOMMIT))
        {
            always_trace2("Could not decommit %dK at addr %#x",
                          decommitSize / ONE_K, decommitAddr);
            return(STATUS_UNABLE_TO_DECOMMIT_VM);
        }
    }

    /*
     * If chunk is adjacent to a free section absorb this chunk into it.
     * Start with next chunk so we don't trash current one before we are
     * finished with it.
     */
    if (headerPtr->next && SECTION_IS_FREE(headerPtr->next))
    {
        headerPtr->size += headerPtr->next->size;
        deleteHeaderEntry(headerPtr->next);
    }

    /* Absorb into previous section if that is free. */
    if (headerPtr->prev && SECTION_IS_FREE(headerPtr->prev))
    {
        headerPtr->prev->size += headerPtr->size;
        deleteHeaderEntry(headerPtr);
    }
    else
    {

        /* Otherwise just mark this chunk as free. */
        headerPtr->flags &= (IU32) ~HDR_ALLOC_FLAG;
    }

    /* Success, so update total free space store and return. */
    totalFree += size;


#ifdef DEBUG_MEM
#ifdef DEBUG_MEM_DUMP
    DumpAllocationHeaders("after free");
#endif
#endif

    return(STATUS_SUCCESS);
}

/*(
========================== VdmQueryFreeVirtualMemory ===========================
PURPOSE:
        This interface returns information about free memory.  The total number
        of allocatable free bytes is returned in the variable pointed to by
        FreeBytes. LargestFreeBlock returns the size of the largest contiguous
        block that can be allocated at the present time.  This value is
        potentially all of the available virtual memory. It may change over
        time due to other activities in the system.  The above notes on return
        value apply.
INPUT:
        None.
OUTPUT:
        FreeBytes           - the total number of allocatable free bytes.
        LargestFreeBlock    - the size of the largest contiguous block that can
                              be allocated at the present time.
        Return value        - STATUS_SUCCESS, if the memory is allocated, or an
                              appropriate NTSTATUS code if not (if there is no
                              appropriate code STATUS_UNSUCCESSFUL is returned).
================================================================================
)*/
GLOBAL NTSTATUS VdmQueryFreeVirtualMemory IFN2(PULONG, FreeBytes,
                                               PULONG, LargestFreeBlock)
{
    IU32    maxFree = 0;        /* Local storage for maximum. */
    SECTION_HEADER  *headerPtr; /* Pointer for searching through linked list. */

    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmQueryFreeVirtualMemory before initialisation");

    /*
     * Use a linear search through the linked list to find the largest
     * contiguous free space. (This information could be updated as chunks are
     * allocated and freed but do it this way for now.)
     */

    for(headerPtr = &headerTable[0] ;
	headerPtr != NULL ;
	headerPtr = headerPtr->next)
    {
        if (SECTION_IS_FREE(headerPtr) && (headerPtr->size > maxFree))
            maxFree = headerPtr->size;
    }

    *FreeBytes = (ULONG) totalFree;
    *LargestFreeBlock = (ULONG) maxFree;

#ifdef DEBUG_MEM
    printf("NTVDM:VdmQueryFreeVirtualMemory() Total %lx [%dK], Largest %lx[%dK]\n",
	    *FreeBytes, *FreeBytes/ONE_K, *LargestFreeBlock, *LargestFreeBlock/ONE_K);
#endif

    return(STATUS_SUCCESS);
}

/*(
========================== VdmReallocateVirtualMemory ==========================
PURPOSE:
        This interface will reallocate the block of memory at the specified
        Intel linear address.  The size of the new block is specified by
        NewSize.  The new address is returned in the variable pointed to by
        NewAddress.  The new address must be page aligned (this is important).
        If the new size is smaller than the old size, the new address must be
        the same as the old address (this is also important).  The original
        data from the reallocated memory must be preserved to
        min(old size, new size). The state of any data beyond NewSize is
        indeterminate.
INPUT:
	INTELOriginalAddress - INTEL address of the chunk to be reallocated.
	NewSize 	     - size in bytes the chunk needs to be changed to.
OUTPUT:
	INTELNewAddress	 - new INTEL address of the chunk.
================================================================================
)*/
GLOBAL NTSTATUS VdmReallocateVirtualMemory IFN3(ULONG, INTELOriginalAddress,
						PULONG, INTELNewAddress,
                                                ULONG, NewSize)
{
    SECTION_HEADER  *headerPtr;
    IBOOL   nextSectIsFree;
    IU32    oldSize,
            maxSize;
    ULONG   newAddr;
    NTSTATUS	status;
    ULONG   OriginalAddress;


#ifdef DEBUG_MEM
    printf("NTVDM:VdmReallocateVirtualMemory(%lx [%dK] at %lx)\n",
	   NewSize, NewSize/ONE_K, INTELOriginalAddress);
#endif

    /* Round NewSize up to a multiple of 4K. */
    if (NewSize & PAGE_MASK)
	NewSize = (NewSize + PAGE_MASK) & (~PAGE_MASK);

    /* Calculate chunk address */
    OriginalAddress = INTELOriginalAddress + (ULONG)intelMem;

    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmReallocateVirtualMemory before initialisation");

    /* Get header table entry for address. */
    headerPtr = ADDRESS_TO_HEADER((IU8 *) OriginalAddress);


    /* Unable to reallocate sparsely commit chunk */
    if(!(headerPtr->flags & HDR_COMMITTED_FLAG))
    {
	always_trace0("Tried to reallocate sparsely committed chunk");
        return(STATUS_MEMORY_NOT_ALLOCATED);
    }


    /*
     * Check address is correctly aligned and at the top of an allocated
     * chunk.
     */
    if ((OriginalAddress & PAGE_MASK) ||
        (headerPtr->flags & HDR_VALID_ALLOC != HDR_VALID_ALLOC))
    {
        always_trace0("Tried to reallocate invalid address");
        return(STATUS_MEMORY_NOT_ALLOCATED);
    }

    /* If size is the same there is nothing to do. */
    if (NewSize == headerPtr->size)
    {
        always_trace0("New size equals old size in VdmReallocateVirtualMemory");
        *INTELNewAddress = INTELOriginalAddress;
        return(STATUS_SUCCESS);
    }

    /* Don't allow reallocation of bottom 1 Meg. */
    if (headerPtr == &headerTable[0])
    {
        always_trace0("Tried to reallocate real mode area");
        return(STATUS_UNSUCCESSFUL);
    }

    /* ... or if still mapped to another area of host memory by
	 * VdmAddVirtualMemory() - ouch!
     */
	if (headerPtr->flags & HDR_REMAP_FLAG)
	{
        always_trace0("Tried to reallocate remapped area");
        return(STATUS_UNSUCCESSFUL);
    }

    /* Save old size for later. */
    oldSize = headerPtr->size;

    /* Work out whether the chunk needs to be moved. */
    maxSize = headerPtr->size;
    if (headerPtr->next && SECTION_IS_FREE(headerPtr->next))
    {
        maxSize += headerPtr->next->size;
        nextSectIsFree = TRUE;
    }
    else
    {
        nextSectIsFree = FALSE;
    }
    if (NewSize > maxSize)
    {

        /* Chunk must move, so allocate a new one. */
	status = VdmAllocateVirtualMemory(&newAddr, NewSize, TRUE);
        if (status != STATUS_SUCCESS)
            return(status);

        /* Copy old chunk. */
        memcpy((void *) (newAddr + intelMem), (void *) OriginalAddress,
                (size_t) oldSize);

        /* Free old chunk. */
        sas_overwrite_memory(INTELOriginalAddress, oldSize);
        status = VdmFreeVirtualMemory(INTELOriginalAddress);
        if (status != STATUS_SUCCESS)
            return(status);

        /* Inform caller of new address. */
	*INTELNewAddress = newAddr;
    }
    else
    {
        IHP commitAddr;
        IU32 commitSize;

        /* Adjust size of current chunk. */
        headerPtr->size = NewSize;

        /* Remove old pointer to free space if there is one. */
        if (nextSectIsFree)
            deleteHeaderEntry(headerPtr->next);

        /* Add new pointer to free space if one is required. */
        if (NewSize < maxSize)
        {
            (void) addHeaderEntry(headerPtr,
                                  headerPtr->next,
                                  SECT_FREE,
				  (IU8 *) (OriginalAddress + NewSize),
                                  maxSize - NewSize);
        }

        /* If this chunk is committed, commit the memory now covered by it */
        /* in case its size has increased, or decommit the freed up memory */
        if (headerPtr->flags & HDR_COMMITTED_FLAG)
        {
            if (oldSize < NewSize) {
                exclusiveChunkPages(headerPtr, &commitAddr, &commitSize, TRUE);
                if (commitSize)
                    (void) VirtualAlloc((LPVOID) commitAddr,
                                    (DWORD) commitSize,
                                    (DWORD) MEM_COMMIT,
                                    (DWORD) PAGE_READWRITE);
            } else {
                /* Chunk has shrunk, so free up excess */
                exclusiveChunkPages(headerPtr->next, &commitAddr, &commitSize, FALSE);
                if (commitSize)
                    (void) VirtualFree((LPVOID) commitAddr,
                                    (DWORD) commitSize,
                                    (DWORD) MEM_DECOMMIT);
            }
        }

        /* Inform caller address has not changed. */
	*INTELNewAddress = OriginalAddress - (ULONG)intelMem;

        /* Update total free space store. */
        totalFree += NewSize - oldSize;
    }

#ifdef DEBUG_MEM
    printf("to %lx\n", *INTELNewAddress);
#ifdef DEBUG_MEM_DUMP
    DumpAllocationHeaders("after realloc");
#endif
#endif
    /* Success. */
    return(STATUS_SUCCESS);
}

/*(
============================ VdmAddVirtualMemory ===============================
PURPOSE:
 In investigating the things we need to support with the 386, I've come
 across an interesting one called dib.drv. Support for this involves calling
 CreateDibSection, which returns a pointer to a DIB. The applications then
 edit the bits in the dib directly, as well as operating on it using GDI
 calls. At least that's my understanding. In view of this, and the potential
 for people to create other api with similar properties, it appears that we
 need to be able to notify the cpu that a particular region of memory needs
 to be added to the intel address space.

 This interface adds virtual memory allocated by the system to the intel
 address space.  The host linear address of the block to be added is
 specified by HostAddress.  The pages at this address have already been
 allocated and initialized.  The CPU should not modify the contents of these
 pages, except as part of executing emulated code.  The Intel linear address
 may be specified by IntelAddress. If IntelAddress is non-NULL, it specifies
 the Intel address that the memory should be added at. If IntelAddress is
 NULL, the CPU may select the Intel address the memory is at.  In all events,
 upon return from VdmAddVirtualMemory, IntelAddress will contain the Intel
 address of the block of memory.  If the function cannot be performed, and
 appropriate NTSTATUS code should be returned.

 / The ability to specify Intel Address may be unecessary.  I included it for
 completeness /
INPUT:
    HostAddress     - the host linear address of the block to be added.
    Size            - the size of the block in bytes.
OUTPUT:
    IntelAddress    - the intel address the block is mapped to.
================================================================================
)*/
GLOBAL NTSTATUS VdmAddVirtualMemory IFN3(ULONG, HostAddress,
                                         ULONG, Size,
                                         PULONG, IntelAddress)
{
    IU32 alignfix;

#ifdef DEBUG_MEM
    printf("NTVDM:VdmAddVirtualMemory (%lx [%dK]) at %lx)\n",
	   Size, Size/ONE_K, HostAddress);
#endif

    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmAddVirtualMemory before initialisation");

    /* Calculate shift required to DWORD align HostAddress */
    if ((alignfix = HostAddress & 0x3) != 0) {
        Size += alignfix;
        HostAddress -= alignfix;
    }

    /* Round Size up to a multiple of 4K. */

    if (Size & PAGE_MASK)
	Size = (Size + PAGE_MASK) & (~PAGE_MASK);

    /* step 1 - reserve the intel address space */

    if (VdmAllocateVirtualMemory(IntelAddress,Size,FALSE) != STATUS_SUCCESS)
        return (STATUS_NO_MEMORY);

    /* step 2 - flush the caches */

    sas_overwrite_memory(*IntelAddress, Size);

    /* step 3 - replace the PhysicalPageREC.translation entries */

    VdmSetPhysRecStructs(HostAddress, *IntelAddress, Size);
    ADDRESS_TO_HEADER(*IntelAddress+intelMem)->flags |= HDR_REMAP_FLAG;

    /* adjust IntelAddress if HostAddress not DWORD aligned */
    
    *IntelAddress += alignfix;

#ifdef DEBUG_MEM
#ifdef DEBUG_MEM_DUMP
    DumpAllocationHeaders("after Add");
#endif
    printf("NTVDM:VdmAddVirtualMemory => *IntelAddress=%lx\n", *IntelAddress);
#endif

    return(STATUS_SUCCESS);
}

/*(
========================== VdmRemoveVirtualMemory ==============================
PURPOSE:
        This interface undoes an address mapping that was performed using
        VdmAddVirtualMemory.
INPUT:
        IntelAddress    - address of block to be removed.
================================================================================
)*/
GLOBAL NTSTATUS VdmRemoveVirtualMemory IFN1(ULONG, IntelAddress)
{
    SECTION_HEADER * headerPtr;
    ULONG   HostAddress,Size;
    NTSTATUS status;

#ifdef DEBUG_MEM
    printf("NTVDM:VdmRemoveVirtualMemory at %lx)\n", IntelAddress);
#ifdef DEBUG_MEM_DUMP
    DumpAllocationHeaders("before remove");
#endif
#endif

    /* Make sure memory system is initialised. */
    assert0(memInit, "Called VdmRemoveVirtualMemory before initialisation");

    /* Make sure IntelAddress is page aligned */
    IntelAddress &= ~PAGE_MASK;

    HostAddress = IntelAddress + (ULONG)intelMem;

    /* Get header table entry for address. */
    headerPtr = ADDRESS_TO_HEADER((IU8 *) HostAddress);

	Size = headerPtr->size;

    /* step 1 - flush the caches */

    sas_overwrite_memory(IntelAddress, Size);

    /* step 2 - reset the PhysicalPageREC.translation entries */

#ifdef DEBUG_MEM
    if (Size==0) {
        printf("NTVDM:VdmRemoveVirtualMemory WARNING, Size==0\n");
    }
#endif
    VdmSetPhysRecStructs(HostAddress, IntelAddress, Size);
    ADDRESS_TO_HEADER(IntelAddress+intelMem)->flags &= ~HDR_REMAP_FLAG;

    /* step 3 - free the reserved intel address space */

#ifdef DEBUG_MEM
#ifdef DEBUG_MEM_DUMP
        DumpAllocationHeaders("after remove (now calling free)");
#endif
#endif
    return VdmFreeVirtualMemory(IntelAddress);
}

/* Local Functions. */

/*
=============================== addHeaderEntry =================================
PURPOSE:
        Add an entry in the header table, corresponding to 'intelAddr'. The
        entry  will sit between 'prevHeader' and 'nextHeader' in the linked
        list. If 'prevHeader' is NULL, the new entry will be the first in the
        list. If 'nextHeader' is NULL, the new entry will be the last in the
        list. The 'allocFree' parameter states whether the section is to be
        marked as allocated or free. The 'size' parameter gives the new
        section's size. Returns a pointer to the new header on success, NULL
        on failure.
INPUT:
        prevHeader  - previous header in linked list - may be NULL if the new
                      header is to be the first in the list.
        nextHeader  - next header in linked list - may be NULL if the new
                      header is to be the last in the list.
        allocFree   - is the new header to be marked as allocated or free?
        intelAddr   - address of the new entry.
        size        - size of the new section in bytes.
OUTPUT:
        return val  - pointer to new section or NULL if there is a problem.
================================================================================
 */
LOCAL SECTION_HEADER *addHeaderEntry IFN5(SECTION_HEADER *, prevHeader,
                                          SECTION_HEADER *, nextHeader,
                                          SECT_TYPE, allocFree,
                                          IU8 *, intelAddr,
                                          IU32, size)
{
    SECTION_HEADER  *newHeader = ADDRESS_TO_HEADER(intelAddr);
                                    /* New header table entry. */
    IHP      retAddr,
             commitAddr;
    IU32     commitSize;

#ifndef PROD
    if (prevHeader)
        assert0(newHeader > prevHeader, "prevHeader invalid");
    if (nextHeader)
        assert0(newHeader < nextHeader, "nextHeader invalid");
#endif /* !PROD */

    /* Commit and zero table header entries if necessary. */
    exclusiveHeaderPages((IHPE) newHeader, (IHPE) prevHeader,
                         (IHPE) nextHeader, &commitAddr, &commitSize);
    if (commitSize)
    {
        retAddr = VirtualAlloc((LPVOID) commitAddr,
                               (DWORD) commitSize,
                               (DWORD) MEM_COMMIT,
			       (DWORD) PAGE_READWRITE);

	if(retAddr != commitAddr)
	{
	    printf("V.Allocate failed (%xh) [%lxh :%xh]\n",GetLastError(),commitAddr,commitSize);
	}


        if (retAddr == commitAddr)
	    memset((void *) commitAddr, ZapValue, (size_t) commitSize);
        else
            return((SECTION_HEADER *) NULL);
    }
    
    /* Fill in header's fields. */
    newHeader->flags = HDR_VALID_FLAG;
    if (allocFree == SECT_ALLOC)
        newHeader->flags |= HDR_ALLOC_FLAG;
    newHeader->address = intelAddr;
    newHeader->size = size;

    /* Add it to linked list. */
    if (prevHeader)
        prevHeader->next = newHeader;
    if (nextHeader)
        nextHeader->prev = newHeader;
    newHeader->prev = prevHeader;
    newHeader->next = nextHeader;

    /* Success. */
    return(newHeader);
}

/*
=========================== deleteHeaderEntry ==================================
PURPOSE:
        Delete an entry in the header table and remove it from the linked list.
        If this entry is in an allocation page on its own, decommit the whole
        page, otherwise zero the entry. Return TRUE on success, FALSE on
        failure.
INPUT:
        header      - pointer to entry to be removed.
OUTPUT:
        return val  - TRUE on success, FALSE on failure.
================================================================================
 */
LOCAL IBOOL deleteHeaderEntry IFN1(SECTION_HEADER *, header)
{
    IHP     decommitAddr;
    IU32    decommitSize;

    /* If trying to delete the last allocated chunk, invalidate lastAllocPtr. */
    if (header == lastAllocPtr) {
        lastAllocPtr = NULL;
    }

    /* Find out which pages can be decommitted after this header is freed. */
    exclusiveHeaderPages((IHPE) header, (IHPE) header->prev,
                         (IHPE) header->next, &decommitAddr, &decommitSize);

    /* Remove header from linked list. */
    if (header->prev)
        header->prev->next = header->next;
    if (header->next)
        header->next->prev = header->prev;

    if (decommitSize)
    {

        /* Decommit any allocation pages exclusively covered by 'header'. */
        if (!VirtualFree((LPVOID) decommitAddr,
                         (DWORD) decommitSize,
                         (DWORD) MEM_DECOMMIT))
        {
            always_trace2("Could not decommit %dK at addr %#x",
                          decommitSize / ONE_K, decommitAddr);
            return(FALSE);
        }
    }
    else
    {

        /* Zero header's fields. */
        header->prev = (SECTION_HEADER *) NULL;
        header->flags = 0;
        header->address = 0;
        header->size = 0;
        header->next = (SECTION_HEADER *) NULL;
    }
}

/*
========================== exclusiveHeaderPages ================================
PURPOSE:
        Find the allocation pages EXCLUSIVELY covered by the table entry
        pointed to by 'header'. The previous and next headers in the table
        are pointed to by 'prev' and 'next', which may be NULL if there is
        no corresponding table entry. The address of the first page exclusive
        to the entry is returned in 'allocAddr', the size in bytes of exclusive
        pages is returned in 'allocSize'. If 'allocSize' is zero 'allocAddr'
        is undefined.
INPUT:
        tableAddress    - address of table entry about to be used or removed.
        prevAddress     - address of previous entry in list.
        nextAddress     - address of next entry in list.
OUTPUT:
        allocAddr       - pointer to first byte that needs to be
                          committed/decommitted (undefined if allocSize is 0).
        allocSize       - size in bytes that needs to be committed/decommitted.
================================================================================
 */
LOCAL void exclusiveHeaderPages IFN5(IHPE, tableAddress,
                                     IHPE, prevAddress,
                                     IHPE, nextAddress,
                                     IHP *, allocAddr,
                                     IU32 *, allocSize)
{
    IHPE    prevHeaderLastAddr,
            nextHeaderFirstAddr;

    /*
     * Find out which allocation pages are exclusively covered by the table
     * entry pointed to by 'header'.
     */
    if (prevAddress)
        prevHeaderLastAddr = prevAddress + sizeof(SECTION_HEADER) - 1;
    else
        prevHeaderLastAddr = (IHPE) 0;
    nextHeaderFirstAddr = nextAddress;
    exclusiveAllocPages(tableAddress,
                        (IU32) sizeof(SECTION_HEADER),
                        prevHeaderLastAddr,
                        nextHeaderFirstAddr,
                        allocAddr,
                        allocSize);
}

/*
=========================== exclusiveChunkPages ================================
PURPOSE:
        Return any allocation pages EXCLUSIVELY covered by the section of
        memory pointed to by 'chunkHeader'. The 'allocAddr' parameter points
        at the variable in which to store the address the first allocation
        page so covered. The 'allocSize' parameter points at the variable in
        which to store the size in bytes of these these pages. This routine
        calls 'exclusiveAllocPages' which returns zero in 'allocSize' if there
        are no exclusive pages, 'allocAddr' being undefined. The same is
        therefore true of this routine.
        If we are committing, we must commit any (potentially) uncommitted
        pages. If uncommitting, we must not uncommit any pages that are alloced,
        as they may be committed also.
INPUT:
        chunkHeader - The header table entry pointing at the section of memory
                      that may need committing/decommitting.
OUTPUT:
        allocAddr   - pointer to first byte that needs to be
                      committed/decommitted (undefined if allocSize is 0).
        allocSize   - size in bytes that needs to be committed/decommitted.
        Commit      - are we going to commit (true) or decommit this header.
================================================================================
 */
LOCAL void exclusiveChunkPages IFN4(SECTION_HEADER *, chunkHeader,
                                    IHP *, allocAddr,
                                    IU32 *, allocSize,
                                    BOOL, Commit)
{
    IHPE    prevChunkLastAddr,      /* Last page previous chunk touches. */
            nextChunkFirstAddr;     /* First page next chunk touches. */
    SECTION_HEADER  *prevHeader,    /* Pointer to previous allocated chunk. */
                    *nextHeader;    /* Pointer to next allocated chunk. */

    /* Find previous allocated chunk. */
    prevHeader = chunkHeader->prev;
    while ((prevHeader != NULL) &&
     (Commit?SECTION_IS_UNCOMMITTED(prevHeader):SECTION_IS_FREE(prevHeader)))
        prevHeader = prevHeader->prev;

    /* Work out end address of previous chunk. */
    if (prevHeader)
        prevChunkLastAddr = (IHPE) prevHeader->address + prevHeader->size - 1;
    else
        prevChunkLastAddr = (IHPE) 0;

    /* Find next allocated chunk. */
    nextHeader = chunkHeader->next;
    while ((nextHeader != NULL) && 
     (Commit?SECTION_IS_UNCOMMITTED(nextHeader):SECTION_IS_FREE(nextHeader)))
        nextHeader = nextHeader->next;

    /* Work out start address of next chunk. */
    if (nextHeader)
        nextChunkFirstAddr = (IHPE) nextHeader->address;
    else
        nextChunkFirstAddr = (IHPE) 0;

    /*
     * Find the address range of pages that need to be committed and pass them
     * straight up to the caller.
     */
    exclusiveAllocPages((IHPE) chunkHeader->address,
                        chunkHeader->size,
                        prevChunkLastAddr,
                        nextChunkFirstAddr,
                        allocAddr,
                        allocSize);
#ifdef DEBUG_MEM
    printf("NTVDM:Exclusive range to %s %lx+%lx is %lx+%lx\n",
    Commit ? "COMMIT" : "DECOMMIT", chunkHeader->address,chunkHeader->size,
    *allocAddr, *allocSize);
#endif
}

/*
============================= exclusiveAllocPages ==============================
PURPOSE:
        For a given memory range, find out which allocation pages need to be
        committed in order for memory accesses to be allowed across the entire
        range. To do this we need to know the address and size of the memory
        range. These are passed in 'address' and 'size'. We also need to know
        the addresses of the previous and next allocated memory ranges to find
        out which allocation pages are already committed. This information is
        passed to the function in 'prevAllocLastAddr' and 'nextAllocFirstAddr'.
        If there is no previous or next chunk, 'prevAllocLastAddr' or
        'nextAllocFirstAddr' is zero. The address and size that need to be
        committed are returned in 'commitAddr' and 'commitSize'. Note that if
        'commitSize' is zero, no memory needs to be committed and 'commitAddr'
        is undefined.
INPUT:
        address             - address of object being checked.
        size                - size of object being checked.
        prevAllocLastAddr   - address of last byte of previous allocated
                              object (or zero if there is none).
        nextAllocFirstAddr  - address of first byte of next allocated object
                              (or zero if there is none).
OUTPUT:
        allocAddr           - pointer to first byte that needs to be
                              committed/decommitted (undefined if allocSize
                              is 0).
        allocSize           - size in bytes that needs to be
                              committed/decommitted.
================================================================================
 */
LOCAL void exclusiveAllocPages IFN6(IHPE, address,
                                    IU32, size,
                                    IHPE, prevAllocLastAddr,
                                    IHPE, nextAllocFirstAddr,
                                    IHP *, allocAddr,
                                    IU32 *, allocSize)
{
    IU32    prevAllocLastPage,      /* Last page previous chunk touches. */
            currentAllocFirstPage,  /* First page current chunk touches. */
            currentAllocLastPage,   /* Last page current chunk touches. */
            nextAllocFirstPage,     /* First page next chunk touches. */
            firstPage,              /* First page that needs committing. */
            lastPage;               /* Last page that needs committing. */

#ifndef PROD

    /* Check for sensible parameters. */
    if (prevAllocLastAddr)
        assert0(address > prevAllocLastAddr, "address out of range");
    if (nextAllocFirstAddr)
        assert0(address < nextAllocFirstAddr, "address out of range");
#endif /* !PROD */

    /*
     * Work out first and last pages of new memory block that need to be
     * committed.
     */
    currentAllocFirstPage = address >> commitShift;
    currentAllocLastPage = (address + size - 1) >> commitShift;
    firstPage = currentAllocFirstPage;

/* Fix horrid nano lookahead bug, but leaves memory leak ?
 * Also insufficient anyway in general case
 */
#ifdef PIG
    lastPage = currentAllocLastPage+1;
#else
    lastPage = currentAllocLastPage;
#endif

    /*
     * Now check to see if first or last pages of this allocation are already
     * committed by adjacent allocations.
     */
    if (prevAllocLastAddr)
    {

        /* See if first page of current allocation is already committed. */
	prevAllocLastPage = prevAllocLastAddr >> commitShift;
        if (prevAllocLastPage == currentAllocFirstPage)
            firstPage++;
    }
    if (nextAllocFirstAddr)
    {

        /* See if last page of current chunk is already committed. */
	nextAllocFirstPage = nextAllocFirstAddr >> commitShift;
        if (nextAllocFirstPage == currentAllocLastPage)
            lastPage--;
    }

    /*
     * If first page is less than or equal to last page we have some pages to
     * allocate. Return the addrees and size to caller (zero size if nothing
     * do.
     */
    if (firstPage <= lastPage)
    {
	*allocAddr = (void *) (firstPage << commitShift);
	*allocSize = (lastPage - firstPage + 1) << commitShift;
    }
    else
        *allocSize = 0;
}


#ifdef DEBUG_MEM


/*
=========================== Dump Header Linked List ===========================
PURPOSE:
	Dump the headers linked list controlling allocated blocks


INPUT:	    None
OUTPUT:     Via printf

================================================================================
*/

GLOBAL VOID DumpAllocationHeaders IFN1(char*, where)
{

    SECTION_HEADER  *headerPtr; /* Pointer for searching through linked list. */

    /* Dump headers */
    printf("NTVDM: Dump Allocation Headers %s\n", where);
    printf("ptr        address   status size     (k)     commit\n");

    for(headerPtr = &headerTable[0] ;
	headerPtr != NULL ;
	headerPtr = headerPtr->next)
    {
	printf("%08lxh: %08lxh [%s] %08lxh (%05dK)%s%s\n",
               headerPtr,
	       headerPtr->address - intelMem,
	       SECTION_IS_FREE(headerPtr) ? "FREE" : "USED",
	       headerPtr->size, headerPtr->size / ONE_K,
	       headerPtr->flags & HDR_COMMITTED_FLAG ? " COMMITTED" : "",
	       headerPtr->flags & HDR_REMAP_FLAG ? " REMAPPED" : "");
    }

    printf("\n");
}


#endif DEBUG_MEM


#endif /* CPU_40_STYLE */