1 //--------------------------------------------------------------------------
3 // XMSARRAY.CPP: body of XMSarray interface library.
4 // Copyright (c) J.English 1993.
5 // Author's address: je@unix.brighton.ac.uk
7 // Permission is granted to use copy and distribute the
8 // information contained in this file provided that this
9 // copyright notice is retained intact and that any software
10 // or other document incorporating this file or parts thereof
11 // makes the source code for the library of which this file
12 // is a part freely available.
14 //--------------------------------------------------------------------------
17 // 1.0 Jun 1993 Initial coding
18 // 1.01 Sep 1993 Filenames changed from XMS.* to XMSARRAY.*
19 // 2.0 Nov 1993 Revised to use general purpose XMS class
21 //--------------------------------------------------------------------------
28 //--------------------------------------------------------------------------
32 const int USED = 1, // cache line is in use
33 DIRTY = 2; // cache line has been written to
36 //--------------------------------------------------------------------------
40 struct XMSbuffer // cache structure
42 int mask; // ... mask to extract item number in buffer
43 long where; // ... offset to start of XMS block
44 unsigned width; // ... unit for XMS transfers in bytes
45 char* cache; // ... buffer for XMS transfers
46 long tag; // ... current index held in buffer
47 char flags; // ... buffer state (in use, dirty)
50 struct XMSheader // header for XMS block
52 long size; // ... block size in bytes
53 int free; // ... is it a free block?
56 struct XMSmove // descriptor for XMS moves
58 long count; // ... number of bytes to move
59 int srchandle; // ... source handle (0 for real memory)
60 long srcoffset; // ... source offset (or far pointer)
61 int dsthandle; // ... destination handle (0 for real memory)
62 long dstoffset; // ... destination offset (or far pointer)
66 //--------------------------------------------------------------------------
70 static XMS* arrayBlock = 0; // block used to hold XMS arrays
71 static int arrayCount = 0; // number of allocated XMS arrays
72 static long XMSsize = 0; // current allocation size, internal reckoning
73 // (XMS driver rounds up to nearest 4K or 16K)
75 //--------------------------------------------------------------------------
77 // Function to return the number of bits required to represent "n",
78 // used for rounding up to powers of 2 and generating masks.
80 static int bits (unsigned n)
83 for (long i = 1; i > 0 && i < n; i <<= 1)
88 //--------------------------------------------------------------------------
90 // XMSlocate: locate a free block of XMS.
92 // This is used by XMSalloc to check if there is a block of suitable
93 // size which has been returned to the free list. If not, it returns
94 // 0 (which is never a legitimate offset) and if so, it divides the
95 // block as necessary, marks the allocated block as in use and returns
96 // its offset. XMS blocks are prefaced by an XMSheader giving their
97 // size and status (free/in use). Offsets refer to the first byte
98 // of the allocation (i.e. the byte after the header); the size then
99 // gives the address of the next block. Adjacent free blocks are merged
100 // while scanning the list. If the last block is free, it can't be
101 // merged with anything and so XMSsize is adjusted to remove it from
102 // the internal record of the allocation size.
104 static long XMSlocate (long size)
106 long p = 0, oldp = 0;
107 XMSheader h, oldh = {0,0};
109 //--- scan block list from offset 0
112 //--- get header of next block
113 XMS::copy (&h, (*arrayBlock)[p], sizeof(h));
115 //--- merge adjacent free blocks by changing size of previous block
116 if (oldh.free && h.free)
117 { oldh.size += h.size + sizeof(h);
119 XMS::copy ((*arrayBlock)[p], &h, sizeof(h));
122 //--- check if block (after possible merging) is big enough
123 if (h.free && h.size >= size + sizeof(XMSheader))
126 //--- keep value and offset for block and get offset of next header
128 p += h.size + sizeof(h);
131 //--- no suitable block available: remove last block if it is free
138 //--- divide block if necessary
139 if (h.free && h.size >= size + sizeof(XMSheader))
141 XMSheader t = {h.size - size - sizeof(t), 1};
142 XMS::copy (arrayBlock->at(p+sizeof(h)+size), &t, sizeof(t));
146 //--- write new block's header and return offset of first usable byte
148 XMS::copy (arrayBlock->at(p), &h, sizeof(h));
149 return p + sizeof(h);
152 //--------------------------------------------------------------------------
154 // XMSalloc: allocate a block of XMS.
156 // This function tries to allocate "size" bytes of XMS and returns 0
159 static long XMSalloc (long size)
161 //--- check XMS initialised before proceeding
162 if (arrayCount++ == 0)
163 { arrayBlock = new XMS (sizeof(XMSheader) + size);
164 if (arrayBlock == 0 || !arrayBlock->valid())
170 { XMSheader h = {0,0};
171 XMS::copy (arrayBlock->at(0), &h, sizeof(XMSheader));
177 //--- try to find a suitable free block in the current allocation
178 long pos = XMSlocate (size);
182 //--- try to extend current allocation if none found
183 if (arrayBlock->resize (XMSsize + size + sizeof(XMSheader)) != XMS::SUCCESS)
186 //--- write the header for the new block at the end of current allocation
187 XMSheader h = {size,0};
188 XMS::copy (arrayBlock->at(XMSsize), &h, sizeof(h));
190 //--- get offset of first usable byte of new block
191 pos = XMSsize + sizeof(h);
193 //--- increase allocation size to accomodate it and return its offset
194 XMSsize += size + sizeof(h);
199 //--------------------------------------------------------------------------
201 // XMSfree: mark a block of XMS as free.
203 // This returns a block to the free list by rewriting its header.
205 static void XMSfree (long offset)
207 if (--arrayCount == 0)
212 XMS::copy (&h, arrayBlock->at(offset-sizeof(h)), sizeof(h));
214 XMS::copy (arrayBlock->at(offset-sizeof(h)), &h, sizeof(h));
217 //--------------------------------------------------------------------------
219 // XMSarrayBase::XMSarrayBase.
221 // Constructor for base class XMS, called by constructor for XMSarray.
222 // Constructs an XMS array containing the specified number of items of
223 // the specified size and also a one-line cache of the specified size.
225 XMSarrayBase::XMSarrayBase (long items, unsigned size, unsigned cachesize)
227 //--- initial state is "unallocated"
230 //--- create the cache
231 buffer = new XMSbuffer;
235 //--- set up the item offset mask (items in line - 1)
236 buffer->mask = (size > cachesize) ? 0 :
237 (1 << (bits(cachesize) - bits(size))) - 1;
239 //--- calculate number of items needed (1 extra rounded up to nearest line)
242 items += buffer->mask;
243 items &= ~buffer->mask;
245 //--- calculate cache size in bytes and allocate it
246 buffer->width = (buffer->mask + 1) * size;
247 buffer->cache = new char [buffer->width];
248 if (buffer->cache == 0)
251 //--- try to allocate the XMS block
252 buffer->where = XMSalloc (items * size);
254 //--- mark cache line as unused
258 //--- record if allocation succeeded
259 state = (buffer->where != 0);
263 //--------------------------------------------------------------------------
265 // XMSarrayBase::XMSarrayBase.
267 // Copy constructor used by XMSitem to clone a reference to the
270 XMSarrayBase::XMSarrayBase (const XMSarrayBase& base)
273 buffer = base.buffer;
276 //--------------------------------------------------------------------------
278 // XMSarrayBase::free.
280 // Try to free an array. Check that it's there before doing anything,
281 // then free the XMS block and delete the cache. This is not a written
282 // as a destructor so that XMSitem doesn't call it. Unfortunately
283 // Borland C++ won't let you nominate all possible instantiations of
284 // a template as friends of a non-template class, i.e.
285 // class X { template<class T> friend class Y<T>; ... };
286 // gives a compilation error.
288 void XMSarrayBase::free ()
292 if (buffer->where != 0)
293 XMSfree (buffer->where);
294 delete buffer->cache;
299 //--------------------------------------------------------------------------
301 // XMSarrayBase::mask.
303 // Return the item number within the cache line for a given subscript.
305 int XMSarrayBase::mask (int n)
309 return n & buffer->mask;
312 //--------------------------------------------------------------------------
314 // XMSarrayBase::get.
316 // Get a block from XMS into conventional memory. If it's not in the
317 // cache, load the relevant line (writing back the old line if it was
318 // dirty). Return a pointer to the cached copy of the item. "Base"
319 // is the base address of the line to load and "offset" is the offset
320 // into the cache buffer.
322 void* XMSarrayBase::get (long base, int offset)
326 if (buffer->tag != base || !(buffer->flags & USED))
327 { if (buffer->flags & DIRTY)
328 XMS::copy (arrayBlock->at (buffer->where + buffer->tag),
329 buffer->cache, buffer->width);
330 XMS::copy (buffer->cache, arrayBlock->at (buffer->where + base),
332 buffer->flags = USED;
335 return buffer->cache + offset;
339 //--------------------------------------------------------------------------
341 // XMSarrayBase::put.
343 // Gets a copy of the relevant cache line into memory and then marks
344 // the line as dirty (about to be written to).
346 void* XMSarrayBase::put (long base, int offset)
350 void* addr = get (base, offset);
351 buffer->flags |= DIRTY;