source: trunk/third/jpeg/jmemdos.c @ 15227

Revision 15227, 18.5 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15226, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * jmemdos.c
3 *
4 * Copyright (C) 1992-1997, Thomas G. Lane.
5 * This file is part of the Independent JPEG Group's software.
6 * For conditions of distribution and use, see the accompanying README file.
7 *
8 * This file provides an MS-DOS-compatible implementation of the system-
9 * dependent portion of the JPEG memory manager.  Temporary data can be
10 * stored in extended or expanded memory as well as in regular DOS files.
11 *
12 * If you use this file, you must be sure that NEED_FAR_POINTERS is defined
13 * if you compile in a small-data memory model; it should NOT be defined if
14 * you use a large-data memory model.  This file is not recommended if you
15 * are using a flat-memory-space 386 environment such as DJGCC or Watcom C.
16 * Also, this code will NOT work if struct fields are aligned on greater than
17 * 2-byte boundaries.
18 *
19 * Based on code contributed by Ge' Weijers.
20 */
21
22/*
23 * If you have both extended and expanded memory, you may want to change the
24 * order in which they are tried in jopen_backing_store.  On a 286 machine
25 * expanded memory is usually faster, since extended memory access involves
26 * an expensive protected-mode-and-back switch.  On 386 and better, extended
27 * memory is usually faster.  As distributed, the code tries extended memory
28 * first (what? not everyone has a 386? :-).
29 *
30 * You can disable use of extended/expanded memory entirely by altering these
31 * definitions or overriding them from the Makefile (eg, -DEMS_SUPPORTED=0).
32 */
33
34#ifndef XMS_SUPPORTED
35#define XMS_SUPPORTED  1
36#endif
37#ifndef EMS_SUPPORTED
38#define EMS_SUPPORTED  1
39#endif
40
41
42#define JPEG_INTERNALS
43#include "jinclude.h"
44#include "jpeglib.h"
45#include "jmemsys.h"            /* import the system-dependent declarations */
46
47#ifndef HAVE_STDLIB_H           /* <stdlib.h> should declare these */
48extern void * malloc JPP((size_t size));
49extern void free JPP((void *ptr));
50extern char * getenv JPP((const char * name));
51#endif
52
53#ifdef NEED_FAR_POINTERS
54
55#ifdef __TURBOC__
56/* These definitions work for Borland C (Turbo C) */
57#include <alloc.h>              /* need farmalloc(), farfree() */
58#define far_malloc(x)   farmalloc(x)
59#define far_free(x)     farfree(x)
60#else
61/* These definitions work for Microsoft C and compatible compilers */
62#include <malloc.h>             /* need _fmalloc(), _ffree() */
63#define far_malloc(x)   _fmalloc(x)
64#define far_free(x)     _ffree(x)
65#endif
66
67#else /* not NEED_FAR_POINTERS */
68
69#define far_malloc(x)   malloc(x)
70#define far_free(x)     free(x)
71
72#endif /* NEED_FAR_POINTERS */
73
74#ifdef DONT_USE_B_MODE          /* define mode parameters for fopen() */
75#define READ_BINARY     "r"
76#else
77#define READ_BINARY     "rb"
78#endif
79
80#ifndef USE_MSDOS_MEMMGR        /* make sure user got configuration right */
81  You forgot to define USE_MSDOS_MEMMGR in jconfig.h. /* deliberate syntax error */
82#endif
83
84#if MAX_ALLOC_CHUNK >= 65535L   /* make sure jconfig.h got this right */
85  MAX_ALLOC_CHUNK should be less than 64K. /* deliberate syntax error */
86#endif
87
88
89/*
90 * Declarations for assembly-language support routines (see jmemdosa.asm).
91 *
92 * The functions are declared "far" as are all their pointer arguments;
93 * this ensures the assembly source code will work regardless of the
94 * compiler memory model.  We assume "short" is 16 bits, "long" is 32.
95 */
96
97typedef void far * XMSDRIVER;   /* actually a pointer to code */
98typedef struct {                /* registers for calling XMS driver */
99        unsigned short ax, dx, bx;
100        void far * ds_si;
101      } XMScontext;
102typedef struct {                /* registers for calling EMS driver */
103        unsigned short ax, dx, bx;
104        void far * ds_si;
105      } EMScontext;
106
107extern short far jdos_open JPP((short far * handle, char far * filename));
108extern short far jdos_close JPP((short handle));
109extern short far jdos_seek JPP((short handle, long offset));
110extern short far jdos_read JPP((short handle, void far * buffer,
111                                unsigned short count));
112extern short far jdos_write JPP((short handle, void far * buffer,
113                                 unsigned short count));
114extern void far jxms_getdriver JPP((XMSDRIVER far *));
115extern void far jxms_calldriver JPP((XMSDRIVER, XMScontext far *));
116extern short far jems_available JPP((void));
117extern void far jems_calldriver JPP((EMScontext far *));
118
119
120/*
121 * Selection of a file name for a temporary file.
122 * This is highly system-dependent, and you may want to customize it.
123 */
124
125static int next_file_num;       /* to distinguish among several temp files */
126
127LOCAL(void)
128select_file_name (char * fname)
129{
130  const char * env;
131  char * ptr;
132  FILE * tfile;
133
134  /* Keep generating file names till we find one that's not in use */
135  for (;;) {
136    /* Get temp directory name from environment TMP or TEMP variable;
137     * if none, use "."
138     */
139    if ((env = (const char *) getenv("TMP")) == NULL)
140      if ((env = (const char *) getenv("TEMP")) == NULL)
141        env = ".";
142    if (*env == '\0')           /* null string means "." */
143      env = ".";
144    ptr = fname;                /* copy name to fname */
145    while (*env != '\0')
146      *ptr++ = *env++;
147    if (ptr[-1] != '\\' && ptr[-1] != '/')
148      *ptr++ = '\\';            /* append backslash if not in env variable */
149    /* Append a suitable file name */
150    next_file_num++;            /* advance counter */
151    sprintf(ptr, "JPG%03d.TMP", next_file_num);
152    /* Probe to see if file name is already in use */
153    if ((tfile = fopen(fname, READ_BINARY)) == NULL)
154      break;
155    fclose(tfile);              /* oops, it's there; close tfile & try again */
156  }
157}
158
159
160/*
161 * Near-memory allocation and freeing are controlled by the regular library
162 * routines malloc() and free().
163 */
164
165GLOBAL(void *)
166jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
167{
168  return (void *) malloc(sizeofobject);
169}
170
171GLOBAL(void)
172jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
173{
174  free(object);
175}
176
177
178/*
179 * "Large" objects are allocated in far memory, if possible
180 */
181
182GLOBAL(void FAR *)
183jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
184{
185  return (void FAR *) far_malloc(sizeofobject);
186}
187
188GLOBAL(void)
189jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
190{
191  far_free(object);
192}
193
194
195/*
196 * This routine computes the total memory space available for allocation.
197 * It's impossible to do this in a portable way; our current solution is
198 * to make the user tell us (with a default value set at compile time).
199 * If you can actually get the available space, it's a good idea to subtract
200 * a slop factor of 5% or so.
201 */
202
203#ifndef DEFAULT_MAX_MEM         /* so can override from makefile */
204#define DEFAULT_MAX_MEM         300000L /* for total usage about 450K */
205#endif
206
207GLOBAL(long)
208jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed,
209                    long max_bytes_needed, long already_allocated)
210{
211  return cinfo->mem->max_memory_to_use - already_allocated;
212}
213
214
215/*
216 * Backing store (temporary file) management.
217 * Backing store objects are only used when the value returned by
218 * jpeg_mem_available is less than the total space needed.  You can dispense
219 * with these routines if you have plenty of virtual memory; see jmemnobs.c.
220 */
221
222/*
223 * For MS-DOS we support three types of backing storage:
224 *   1. Conventional DOS files.  We access these by direct DOS calls rather
225 *      than via the stdio package.  This provides a bit better performance,
226 *      but the real reason is that the buffers to be read or written are FAR.
227 *      The stdio library for small-data memory models can't cope with that.
228 *   2. Extended memory, accessed per the XMS V2.0 specification.
229 *   3. Expanded memory, accessed per the LIM/EMS 4.0 specification.
230 * You'll need copies of those specs to make sense of the related code.
231 * The specs are available by Internet FTP from the SIMTEL archives
232 * (oak.oakland.edu and its various mirror sites).  See files
233 * pub/msdos/microsoft/xms20.arc and pub/msdos/info/limems41.zip.
234 */
235
236
237/*
238 * Access methods for a DOS file.
239 */
240
241
242METHODDEF(void)
243read_file_store (j_common_ptr cinfo, backing_store_ptr info,
244                 void FAR * buffer_address,
245                 long file_offset, long byte_count)
246{
247  if (jdos_seek(info->handle.file_handle, file_offset))
248    ERREXIT(cinfo, JERR_TFILE_SEEK);
249  /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */
250  if (byte_count > 65535L)      /* safety check */
251    ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK);
252  if (jdos_read(info->handle.file_handle, buffer_address,
253                (unsigned short) byte_count))
254    ERREXIT(cinfo, JERR_TFILE_READ);
255}
256
257
258METHODDEF(void)
259write_file_store (j_common_ptr cinfo, backing_store_ptr info,
260                  void FAR * buffer_address,
261                  long file_offset, long byte_count)
262{
263  if (jdos_seek(info->handle.file_handle, file_offset))
264    ERREXIT(cinfo, JERR_TFILE_SEEK);
265  /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */
266  if (byte_count > 65535L)      /* safety check */
267    ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK);
268  if (jdos_write(info->handle.file_handle, buffer_address,
269                 (unsigned short) byte_count))
270    ERREXIT(cinfo, JERR_TFILE_WRITE);
271}
272
273
274METHODDEF(void)
275close_file_store (j_common_ptr cinfo, backing_store_ptr info)
276{
277  jdos_close(info->handle.file_handle); /* close the file */
278  remove(info->temp_name);      /* delete the file */
279/* If your system doesn't have remove(), try unlink() instead.
280 * remove() is the ANSI-standard name for this function, but
281 * unlink() was more common in pre-ANSI systems.
282 */
283  TRACEMSS(cinfo, 1, JTRC_TFILE_CLOSE, info->temp_name);
284}
285
286
287LOCAL(boolean)
288open_file_store (j_common_ptr cinfo, backing_store_ptr info,
289                 long total_bytes_needed)
290{
291  short handle;
292
293  select_file_name(info->temp_name);
294  if (jdos_open((short far *) & handle, (char far *) info->temp_name)) {
295    /* might as well exit since jpeg_open_backing_store will fail anyway */
296    ERREXITS(cinfo, JERR_TFILE_CREATE, info->temp_name);
297    return FALSE;
298  }
299  info->handle.file_handle = handle;
300  info->read_backing_store = read_file_store;
301  info->write_backing_store = write_file_store;
302  info->close_backing_store = close_file_store;
303  TRACEMSS(cinfo, 1, JTRC_TFILE_OPEN, info->temp_name);
304  return TRUE;                  /* succeeded */
305}
306
307
308/*
309 * Access methods for extended memory.
310 */
311
312#if XMS_SUPPORTED
313
314static XMSDRIVER xms_driver;    /* saved address of XMS driver */
315
316typedef union {                 /* either long offset or real-mode pointer */
317        long offset;
318        void far * ptr;
319      } XMSPTR;
320
321typedef struct {                /* XMS move specification structure */
322        long length;
323        XMSH src_handle;
324        XMSPTR src;
325        XMSH dst_handle;
326        XMSPTR dst;
327      } XMSspec;
328
329#define ODD(X)  (((X) & 1L) != 0)
330
331
332METHODDEF(void)
333read_xms_store (j_common_ptr cinfo, backing_store_ptr info,
334                void FAR * buffer_address,
335                long file_offset, long byte_count)
336{
337  XMScontext ctx;
338  XMSspec spec;
339  char endbuffer[2];
340
341  /* The XMS driver can't cope with an odd length, so handle the last byte
342   * specially if byte_count is odd.  We don't expect this to be common.
343   */
344
345  spec.length = byte_count & (~ 1L);
346  spec.src_handle = info->handle.xms_handle;
347  spec.src.offset = file_offset;
348  spec.dst_handle = 0;
349  spec.dst.ptr = buffer_address;
350 
351  ctx.ds_si = (void far *) & spec;
352  ctx.ax = 0x0b00;              /* EMB move */
353  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
354  if (ctx.ax != 1)
355    ERREXIT(cinfo, JERR_XMS_READ);
356
357  if (ODD(byte_count)) {
358    read_xms_store(cinfo, info, (void FAR *) endbuffer,
359                   file_offset + byte_count - 1L, 2L);
360    ((char FAR *) buffer_address)[byte_count - 1L] = endbuffer[0];
361  }
362}
363
364
365METHODDEF(void)
366write_xms_store (j_common_ptr cinfo, backing_store_ptr info,
367                 void FAR * buffer_address,
368                 long file_offset, long byte_count)
369{
370  XMScontext ctx;
371  XMSspec spec;
372  char endbuffer[2];
373
374  /* The XMS driver can't cope with an odd length, so handle the last byte
375   * specially if byte_count is odd.  We don't expect this to be common.
376   */
377
378  spec.length = byte_count & (~ 1L);
379  spec.src_handle = 0;
380  spec.src.ptr = buffer_address;
381  spec.dst_handle = info->handle.xms_handle;
382  spec.dst.offset = file_offset;
383
384  ctx.ds_si = (void far *) & spec;
385  ctx.ax = 0x0b00;              /* EMB move */
386  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
387  if (ctx.ax != 1)
388    ERREXIT(cinfo, JERR_XMS_WRITE);
389
390  if (ODD(byte_count)) {
391    read_xms_store(cinfo, info, (void FAR *) endbuffer,
392                   file_offset + byte_count - 1L, 2L);
393    endbuffer[0] = ((char FAR *) buffer_address)[byte_count - 1L];
394    write_xms_store(cinfo, info, (void FAR *) endbuffer,
395                    file_offset + byte_count - 1L, 2L);
396  }
397}
398
399
400METHODDEF(void)
401close_xms_store (j_common_ptr cinfo, backing_store_ptr info)
402{
403  XMScontext ctx;
404
405  ctx.dx = info->handle.xms_handle;
406  ctx.ax = 0x0a00;
407  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
408  TRACEMS1(cinfo, 1, JTRC_XMS_CLOSE, info->handle.xms_handle);
409  /* we ignore any error return from the driver */
410}
411
412
413LOCAL(boolean)
414open_xms_store (j_common_ptr cinfo, backing_store_ptr info,
415                long total_bytes_needed)
416{
417  XMScontext ctx;
418
419  /* Get address of XMS driver */
420  jxms_getdriver((XMSDRIVER far *) & xms_driver);
421  if (xms_driver == NULL)
422    return FALSE;               /* no driver to be had */
423
424  /* Get version number, must be >= 2.00 */
425  ctx.ax = 0x0000;
426  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
427  if (ctx.ax < (unsigned short) 0x0200)
428    return FALSE;
429
430  /* Try to get space (expressed in kilobytes) */
431  ctx.dx = (unsigned short) ((total_bytes_needed + 1023L) >> 10);
432  ctx.ax = 0x0900;
433  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
434  if (ctx.ax != 1)
435    return FALSE;
436
437  /* Succeeded, save the handle and away we go */
438  info->handle.xms_handle = ctx.dx;
439  info->read_backing_store = read_xms_store;
440  info->write_backing_store = write_xms_store;
441  info->close_backing_store = close_xms_store;
442  TRACEMS1(cinfo, 1, JTRC_XMS_OPEN, ctx.dx);
443  return TRUE;                  /* succeeded */
444}
445
446#endif /* XMS_SUPPORTED */
447
448
449/*
450 * Access methods for expanded memory.
451 */
452
453#if EMS_SUPPORTED
454
455/* The EMS move specification structure requires word and long fields aligned
456 * at odd byte boundaries.  Some compilers will align struct fields at even
457 * byte boundaries.  While it's usually possible to force byte alignment,
458 * that causes an overall performance penalty and may pose problems in merging
459 * JPEG into a larger application.  Instead we accept some rather dirty code
460 * here.  Note this code would fail if the hardware did not allow odd-byte
461 * word & long accesses, but all 80x86 CPUs do.
462 */
463
464typedef void far * EMSPTR;
465
466typedef union {                 /* EMS move specification structure */
467        long length;            /* It's easy to access first 4 bytes */
468        char bytes[18];         /* Misaligned fields in here! */
469      } EMSspec;
470
471/* Macros for accessing misaligned fields */
472#define FIELD_AT(spec,offset,type)  (*((type *) &(spec.bytes[offset])))
473#define SRC_TYPE(spec)          FIELD_AT(spec,4,char)
474#define SRC_HANDLE(spec)        FIELD_AT(spec,5,EMSH)
475#define SRC_OFFSET(spec)        FIELD_AT(spec,7,unsigned short)
476#define SRC_PAGE(spec)          FIELD_AT(spec,9,unsigned short)
477#define SRC_PTR(spec)           FIELD_AT(spec,7,EMSPTR)
478#define DST_TYPE(spec)          FIELD_AT(spec,11,char)
479#define DST_HANDLE(spec)        FIELD_AT(spec,12,EMSH)
480#define DST_OFFSET(spec)        FIELD_AT(spec,14,unsigned short)
481#define DST_PAGE(spec)          FIELD_AT(spec,16,unsigned short)
482#define DST_PTR(spec)           FIELD_AT(spec,14,EMSPTR)
483
484#define EMSPAGESIZE     16384L  /* gospel, see the EMS specs */
485
486#define HIBYTE(W)  (((W) >> 8) & 0xFF)
487#define LOBYTE(W)  ((W) & 0xFF)
488
489
490METHODDEF(void)
491read_ems_store (j_common_ptr cinfo, backing_store_ptr info,
492                void FAR * buffer_address,
493                long file_offset, long byte_count)
494{
495  EMScontext ctx;
496  EMSspec spec;
497
498  spec.length = byte_count;
499  SRC_TYPE(spec) = 1;
500  SRC_HANDLE(spec) = info->handle.ems_handle;
501  SRC_PAGE(spec)   = (unsigned short) (file_offset / EMSPAGESIZE);
502  SRC_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE);
503  DST_TYPE(spec) = 0;
504  DST_HANDLE(spec) = 0;
505  DST_PTR(spec)    = buffer_address;
506 
507  ctx.ds_si = (void far *) & spec;
508  ctx.ax = 0x5700;              /* move memory region */
509  jems_calldriver((EMScontext far *) & ctx);
510  if (HIBYTE(ctx.ax) != 0)
511    ERREXIT(cinfo, JERR_EMS_READ);
512}
513
514
515METHODDEF(void)
516write_ems_store (j_common_ptr cinfo, backing_store_ptr info,
517                 void FAR * buffer_address,
518                 long file_offset, long byte_count)
519{
520  EMScontext ctx;
521  EMSspec spec;
522
523  spec.length = byte_count;
524  SRC_TYPE(spec) = 0;
525  SRC_HANDLE(spec) = 0;
526  SRC_PTR(spec)    = buffer_address;
527  DST_TYPE(spec) = 1;
528  DST_HANDLE(spec) = info->handle.ems_handle;
529  DST_PAGE(spec)   = (unsigned short) (file_offset / EMSPAGESIZE);
530  DST_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE);
531 
532  ctx.ds_si = (void far *) & spec;
533  ctx.ax = 0x5700;              /* move memory region */
534  jems_calldriver((EMScontext far *) & ctx);
535  if (HIBYTE(ctx.ax) != 0)
536    ERREXIT(cinfo, JERR_EMS_WRITE);
537}
538
539
540METHODDEF(void)
541close_ems_store (j_common_ptr cinfo, backing_store_ptr info)
542{
543  EMScontext ctx;
544
545  ctx.ax = 0x4500;
546  ctx.dx = info->handle.ems_handle;
547  jems_calldriver((EMScontext far *) & ctx);
548  TRACEMS1(cinfo, 1, JTRC_EMS_CLOSE, info->handle.ems_handle);
549  /* we ignore any error return from the driver */
550}
551
552
553LOCAL(boolean)
554open_ems_store (j_common_ptr cinfo, backing_store_ptr info,
555                long total_bytes_needed)
556{
557  EMScontext ctx;
558
559  /* Is EMS driver there? */
560  if (! jems_available())
561    return FALSE;
562
563  /* Get status, make sure EMS is OK */
564  ctx.ax = 0x4000;
565  jems_calldriver((EMScontext far *) & ctx);
566  if (HIBYTE(ctx.ax) != 0)
567    return FALSE;
568
569  /* Get version, must be >= 4.0 */
570  ctx.ax = 0x4600;
571  jems_calldriver((EMScontext far *) & ctx);
572  if (HIBYTE(ctx.ax) != 0 || LOBYTE(ctx.ax) < 0x40)
573    return FALSE;
574
575  /* Try to allocate requested space */
576  ctx.ax = 0x4300;
577  ctx.bx = (unsigned short) ((total_bytes_needed + EMSPAGESIZE-1L) / EMSPAGESIZE);
578  jems_calldriver((EMScontext far *) & ctx);
579  if (HIBYTE(ctx.ax) != 0)
580    return FALSE;
581
582  /* Succeeded, save the handle and away we go */
583  info->handle.ems_handle = ctx.dx;
584  info->read_backing_store = read_ems_store;
585  info->write_backing_store = write_ems_store;
586  info->close_backing_store = close_ems_store;
587  TRACEMS1(cinfo, 1, JTRC_EMS_OPEN, ctx.dx);
588  return TRUE;                  /* succeeded */
589}
590
591#endif /* EMS_SUPPORTED */
592
593
594/*
595 * Initial opening of a backing-store object.
596 */
597
598GLOBAL(void)
599jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info,
600                         long total_bytes_needed)
601{
602  /* Try extended memory, then expanded memory, then regular file. */
603#if XMS_SUPPORTED
604  if (open_xms_store(cinfo, info, total_bytes_needed))
605    return;
606#endif
607#if EMS_SUPPORTED
608  if (open_ems_store(cinfo, info, total_bytes_needed))
609    return;
610#endif
611  if (open_file_store(cinfo, info, total_bytes_needed))
612    return;
613  ERREXITS(cinfo, JERR_TFILE_CREATE, "");
614}
615
616
617/*
618 * These routines take care of any system-dependent initialization and
619 * cleanup required.
620 */
621
622GLOBAL(long)
623jpeg_mem_init (j_common_ptr cinfo)
624{
625  next_file_num = 0;            /* initialize temp file name generator */
626  return DEFAULT_MAX_MEM;       /* default for max_memory_to_use */
627}
628
629GLOBAL(void)
630jpeg_mem_term (j_common_ptr cinfo)
631{
632  /* Microsoft C, at least in v6.00A, will not successfully reclaim freed
633   * blocks of size > 32Kbytes unless we give it a kick in the rear, like so:
634   */
635#ifdef NEED_FHEAPMIN
636  _fheapmin();
637#endif
638}
Note: See TracBrowser for help on using the repository browser.