home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / progc / jpeg3.arj / JMEMDOS.C < prev    next >
C/C++ Source or Header  |  1992-03-13  |  17KB  |  609 lines

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