]> 4ch.mooo.com Git - 16.git/blob - 16/XMSARRAY.CPP
added more ems xms junk to the 16/ dir for reference
[16.git] / 16 / XMSARRAY.CPP
1 //--------------------------------------------------------------------------
2 //
3 //      XMSARRAY.CPP: body of XMSarray interface library.
4 //      Copyright (c) J.English 1993.
5 //      Author's address: je@unix.brighton.ac.uk
6 //
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.
13 //
14 //--------------------------------------------------------------------------
15 //
16 //      Revision history:
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
20 //
21 //--------------------------------------------------------------------------
22
23 #include "xms.h"
24 #include "xmsarray.h"
25 #include <dos.h>
26 #include <stdlib.h>
27 \f
28 //--------------------------------------------------------------------------
29 //
30 //      Constants.
31 //
32 const int USED      = 1,    // cache line is in use
33           DIRTY     = 2;    // cache line has been written to
34
35
36 //--------------------------------------------------------------------------
37 //
38 //      Data structures.
39 //
40 struct XMSbuffer            // cache structure
41 {
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)
48 };
49
50 struct XMSheader            // header for XMS block
51 {
52     long size;              // ... block size in bytes
53     int  free;              // ... is it a free block?
54 };
55
56 struct XMSmove              // descriptor for XMS moves
57 {
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)
63 };
64
65
66 //--------------------------------------------------------------------------
67 //
68 //      Globals (statics).
69 //
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)
74
75 //--------------------------------------------------------------------------
76 //
77 //      Function to return the number of bits required to represent "n",
78 //      used for rounding up to powers of 2 and generating masks.
79 //
80 static int bits (unsigned n)
81 {
82     int b = 0;
83     for (long i = 1; i > 0 && i < n; i <<= 1)
84         b++;
85     return b;
86 }
87 \f
88 //--------------------------------------------------------------------------
89 //
90 //      XMSlocate: locate a free block of XMS.
91 //
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.
103 //
104 static long XMSlocate (long size)
105 {
106     long p = 0, oldp = 0;
107     XMSheader h, oldh = {0,0};
108
109     //--- scan block list from offset 0
110     while (p < XMSsize)
111     {
112         //--- get header of next block
113         XMS::copy (&h, (*arrayBlock)[p], sizeof(h));
114
115         //--- merge adjacent free blocks by changing size of previous block
116         if (oldh.free && h.free)
117         {   oldh.size += h.size + sizeof(h);
118             h = oldh, p = oldp;
119             XMS::copy ((*arrayBlock)[p], &h, sizeof(h));
120         }
121
122         //--- check if block (after possible merging) is big enough
123         if (h.free && h.size >= size + sizeof(XMSheader))
124             break;
125
126         //--- keep value and offset for block and get offset of next header
127         oldh = h, oldp = p;
128         p += h.size + sizeof(h);
129     }
130
131     //--- no suitable block available: remove last block if it is free
132     if (p >= XMSsize)
133     {   if (oldh.free)
134             XMSsize = oldp;
135         return 0;
136     }
137
138     //--- divide block if necessary
139     if (h.free && h.size >= size + sizeof(XMSheader))
140     {
141         XMSheader t = {h.size - size - sizeof(t), 1};
142         XMS::copy (arrayBlock->at(p+sizeof(h)+size), &t, sizeof(t));
143         h.size = size;
144     }
145
146     //--- write new block's header and return offset of first usable byte
147     h.free = 0;
148     XMS::copy (arrayBlock->at(p), &h, sizeof(h));
149     return p + sizeof(h);
150 }
151 \f
152 //--------------------------------------------------------------------------
153 //
154 //      XMSalloc: allocate a block of XMS.
155 //
156 //      This function tries to allocate "size" bytes of XMS and returns 0
157 //      if it fails.
158 //
159 static long XMSalloc (long size)
160 {
161     //--- check XMS initialised before proceeding
162     if (arrayCount++ == 0)
163     {   arrayBlock = new XMS (sizeof(XMSheader) + size);
164         if (arrayBlock == 0 || !arrayBlock->valid())
165         {   delete arrayBlock;
166             arrayCount--;
167             arrayBlock = 0;
168         }
169         else
170         {   XMSheader h = {0,0};
171             XMS::copy (arrayBlock->at(0), &h, sizeof(XMSheader));
172         }
173     }
174     if (arrayBlock == 0)
175         return 0;
176
177     //--- try to find a suitable free block in the current allocation
178     long pos = XMSlocate (size);
179     if (pos != 0)
180         return pos;
181
182     //--- try to extend current allocation if none found
183     if (arrayBlock->resize (XMSsize + size + sizeof(XMSheader)) != XMS::SUCCESS)
184         return 0;
185
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));
189
190     //--- get offset of first usable byte of new block
191     pos = XMSsize + sizeof(h);
192
193     //--- increase allocation size to accomodate it and return its offset
194     XMSsize += size + sizeof(h);
195     return pos;
196 }
197
198
199 //--------------------------------------------------------------------------
200 //
201 //      XMSfree: mark a block of XMS as free.
202 //
203 //      This returns a block to the free list by rewriting its header.
204 //
205 static void XMSfree (long offset)
206 {
207     if (--arrayCount == 0)
208     {   delete arrayBlock;
209         return;
210     }
211     XMSheader h;
212     XMS::copy (&h, arrayBlock->at(offset-sizeof(h)), sizeof(h));
213     h.free = 1;
214     XMS::copy (arrayBlock->at(offset-sizeof(h)), &h, sizeof(h));
215 }
216 \f
217 //--------------------------------------------------------------------------
218 //
219 //      XMSarrayBase::XMSarrayBase.
220 //
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.
224 //
225 XMSarrayBase::XMSarrayBase (long items, unsigned size, unsigned cachesize)
226 {
227     //--- initial state is "unallocated"
228     state = 0;
229
230     //--- create the cache
231     buffer = new XMSbuffer;
232     if (buffer == 0)
233         return;
234
235     //--- set up the item offset mask (items in line - 1)
236     buffer->mask = (size > cachesize) ? 0 : 
237                    (1 << (bits(cachesize) - bits(size))) - 1;
238
239     //--- calculate number of items needed (1 extra rounded up to nearest line)
240     if (items < 0)
241         items = 0;
242     items += buffer->mask;
243     items &= ~buffer->mask;
244
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)
249         return;
250
251     //--- try to allocate the XMS block
252     buffer->where = XMSalloc (items * size);
253
254     //--- mark cache line as unused
255     buffer->tag   = 0;
256     buffer->flags = 0;
257
258     //--- record if allocation succeeded
259     state = (buffer->where != 0);
260 }
261
262
263 //--------------------------------------------------------------------------
264 //
265 //      XMSarrayBase::XMSarrayBase.
266 //
267 //      Copy constructor used by XMSitem to clone a reference to the
268 //      base array.
269 //
270 XMSarrayBase::XMSarrayBase (const XMSarrayBase& base)
271 {
272     state  = base.state;
273     buffer = base.buffer;
274 }
275 \f
276 //--------------------------------------------------------------------------
277 //
278 //      XMSarrayBase::free.
279 //
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.
287 //
288 void XMSarrayBase::free ()
289 {
290     if (buffer == 0)
291         return;
292     if (buffer->where != 0)
293         XMSfree (buffer->where);
294     delete buffer->cache;
295     delete buffer;
296 }
297
298
299 //--------------------------------------------------------------------------
300 //
301 //      XMSarrayBase::mask.
302 //
303 //      Return the item number within the cache line for a given subscript.
304 //
305 int XMSarrayBase::mask (int n)
306 {
307     if (!state)
308         return 0;
309     return n & buffer->mask;
310 }
311 \f
312 //--------------------------------------------------------------------------
313 //
314 //      XMSarrayBase::get.
315 //
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.
321 //
322 void* XMSarrayBase::get (long base, int offset)
323 {
324     if (!state)
325         return 0;
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),
331                    buffer->width);
332         buffer->flags = USED;
333         buffer->tag = base;
334     }
335     return buffer->cache + offset;
336 }
337
338
339 //--------------------------------------------------------------------------
340 //
341 //      XMSarrayBase::put.
342 //
343 //      Gets a copy of the relevant cache line into memory and then marks
344 //      the line as dirty (about to be written to).
345 //
346 void* XMSarrayBase::put (long base, int offset)
347 {
348     if (!state)
349         return 0;
350     void* addr = get (base, offset);
351     buffer->flags |= DIRTY;
352     return addr;
353 }