File: jmemdos.c

package info (click to toggle)
xloadimage 4.1-25
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster, sid, trixie
  • size: 4,820 kB
  • sloc: ansic: 36,084; asm: 284; makefile: 282; sh: 280
file content (634 lines) | stat: -rw-r--r-- 18,798 bytes parent folder | download | duplicates (10)
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
/*
 * jmemdos.c  (jmemsys.c)
 *
 * Copyright (C) 1992, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file provides an MS-DOS-compatible implementation of the system-
 * dependent portion of the JPEG memory manager.  Temporary data can be
 * stored in extended or expanded memory as well as in regular DOS files.
 *
 * If you use this file, you must be sure that NEED_FAR_POINTERS is defined
 * if you compile in a small-data memory model; it should NOT be defined if
 * you use a large-data memory model.  This file is not recommended if you
 * are using a flat-memory-space 386 environment such as DJGCC or Watcom C.
 * Also, this code will NOT work if struct fields are aligned on greater than
 * 2-byte boundaries.
 *
 * Based on code contributed by Ge' Weijers.
 */

/*
 * If you have both extended and expanded memory, you may want to change the
 * order in which they are tried in jopen_backing_store.  On a 286 machine
 * expanded memory is usually faster, since extended memory access involves
 * an expensive protected-mode-and-back switch.  On 386 and better, extended
 * memory is usually faster.  As distributed, the code tries extended memory
 * first (what? not everyone has a 386? :-).
 *
 * You can disable use of extended/expanded memory entirely by altering these
 * definitions or overriding them from the Makefile (eg, -DEMS_SUPPORTED=0).
 */

#ifndef XMS_SUPPORTED
#define XMS_SUPPORTED  1
#endif
#ifndef EMS_SUPPORTED
#define EMS_SUPPORTED  1
#endif


#include "jinclude.h"
#include "jmemsys.h"

#ifdef INCLUDES_ARE_ANSI
#include <stdlib.h>		/* to declare malloc(), free(), getenv() */
#else
extern void * malloc PP((size_t size));
extern void free PP((void *ptr));
extern char * getenv PP((const char * name));
#endif

#ifdef NEED_FAR_POINTERS

#ifdef __TURBOC__
/* These definitions work for Borland C (Turbo C) */
#include <alloc.h>		/* need farmalloc(), farfree() */
#define far_malloc(x)	farmalloc(x)
#define far_free(x)	farfree(x)
#else
/* These definitions work for Microsoft C and compatible compilers */
#include <malloc.h>		/* need _fmalloc(), _ffree() */
#define far_malloc(x)	_fmalloc(x)
#define far_free(x)	_ffree(x)
#endif

#endif

#ifdef DONT_USE_B_MODE		/* define mode parameters for fopen() */
#define READ_BINARY	"r"
#else
#define READ_BINARY	"rb"
#endif


/*
 * Declarations for assembly-language support routines (see jmemdosa.asm).
 *
 * The functions are declared "far" as are all pointer arguments;
 * this ensures the assembly source code will work regardless of the
 * compiler memory model.  We assume "short" is 16 bits, "long" is 32.
 */

typedef void far * XMSDRIVER;	/* actually a pointer to code */
typedef struct {		/* registers for calling XMS driver */
	unsigned short ax, dx, bx;
	void far * ds_si;
      } XMScontext;
typedef struct {		/* registers for calling EMS driver */
	unsigned short ax, dx, bx;
	void far * ds_si;
      } EMScontext;

EXTERN short far jdos_open PP((short far * handle, char far * filename));
EXTERN short far jdos_close PP((short handle));
EXTERN short far jdos_seek PP((short handle, long offset));
EXTERN short far jdos_read PP((short handle, void far * buffer,
			       unsigned short count));
EXTERN short far jdos_write PP((short handle, void far * buffer,
				unsigned short count));
EXTERN void far jxms_getdriver PP((XMSDRIVER far *));
EXTERN void far jxms_calldriver PP((XMSDRIVER, XMScontext far *));
EXTERN short far jems_available PP((void));
EXTERN void far jems_calldriver PP((EMScontext far *));


static external_methods_ptr methods; /* saved for access to error_exit */

static long total_used;		/* total FAR memory requested so far */


/*
 * Selection of a file name for a temporary file.
 * This is highly system-dependent, and you may want to customize it.
 */

static int next_file_num;	/* to distinguish among several temp files */

LOCAL void
select_file_name (char * fname)
{
  const char * env;
  char * ptr;
  FILE * tfile;

  /* Keep generating file names till we find one that's not in use */
  for (;;) {
    /* Get temp directory name from environment TMP or TEMP variable;
     * if none, use "."
     */
    if ((env = (const char *) getenv("TMP")) == NULL)
      if ((env = (const char *) getenv("TEMP")) == NULL)
	env = ".";
    if (*env == '\0')		/* null string means "." */
      env = ".";
    ptr = fname;		/* copy name to fname */
    while (*env != '\0')
      *ptr++ = *env++;
    if (ptr[-1] != '\\' && ptr[-1] != '/')
      *ptr++ = '\\';		/* append backslash if not in env variable */
    /* Append a suitable file name */
    next_file_num++;		/* advance counter */
    sprintf(ptr, "JPG%03d.TMP", next_file_num);
    /* Probe to see if file name is already in use */
    if ((tfile = fopen(fname, READ_BINARY)) == NULL)
      break;
    fclose(tfile);		/* oops, it's there; close tfile & try again */
  }
}


/*
 * Near-memory allocation and freeing are controlled by the regular library
 * routines malloc() and free().
 */

GLOBAL void *
jget_small (size_t sizeofobject)
{
  /* near data space is NOT counted in total_used */
#ifndef NEED_FAR_POINTERS
  total_used += sizeofobject;
#endif
  return (void *) malloc(sizeofobject);
}

GLOBAL void
jfree_small (void * object)
{
  free(object);
}


/*
 * Far-memory allocation and freeing
 */

#ifdef NEED_FAR_POINTERS

GLOBAL void FAR *
jget_large (size_t sizeofobject)
{
  total_used += sizeofobject;
  return (void FAR *) far_malloc(sizeofobject);
}

GLOBAL void
jfree_large (void FAR * object)
{
  far_free(object);
}

#endif


/*
 * This routine computes the total memory space available for allocation.
 * It's impossible to do this in a portable way; our current solution is
 * to make the user tell us (with a default value set at compile time).
 * If you can actually get the available space, it's a good idea to subtract
 * a slop factor of 5% or so.
 */

#ifndef DEFAULT_MAX_MEM		/* so can override from makefile */
#define DEFAULT_MAX_MEM		300000L /* for total usage about 450K */
#endif

GLOBAL long
jmem_available (long min_bytes_needed, long max_bytes_needed)
{
  return methods->max_memory_to_use - total_used;
}


/*
 * Backing store (temporary file) management.
 * Backing store objects are only used when the value returned by
 * jmem_available is less than the total space needed.  You can dispense
 * with these routines if you have plenty of virtual memory; see jmemnobs.c.
 */

/*
 * For MS-DOS we support three types of backing storage:
 *   1. Conventional DOS files.  We access these by direct DOS calls rather
 *      than via the stdio package.  This provides a bit better performance,
 *      but the real reason is that the buffers to be read or written are FAR.
 *      The stdio library for small-data memory models can't cope with that.
 *   2. Extended memory, accessed per the XMS V2.0 specification.
 *   3. Expanded memory, accessed per the LIM/EMS 4.0 specification.
 * You'll need copies of those specs to make sense of the related code.
 * The specs are available by Internet FTP from SIMTEL20 and its various
 * mirror sites; see microsoft/xms20.arc and info/limems41.zip.
 */


/*
 * Access methods for a DOS file.
 */


METHODDEF void
read_file_store (backing_store_ptr info, void FAR * buffer_address,
		 long file_offset, long byte_count)
{
  if (jdos_seek(info->handle.file_handle, file_offset))
    ERREXIT(methods, "seek failed on temporary file");
  /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */
  if (byte_count > 65535L)	/* safety check */
    ERREXIT(methods, "MAX_ALLOC_CHUNK should be less than 64K");
  if (jdos_read(info->handle.file_handle, buffer_address,
		(unsigned short) byte_count))
    ERREXIT(methods, "read failed on temporary file");
}


METHODDEF void
write_file_store (backing_store_ptr info, void FAR * buffer_address,
		  long file_offset, long byte_count)
{
  if (jdos_seek(info->handle.file_handle, file_offset))
    ERREXIT(methods, "seek failed on temporary file");
  /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */
  if (byte_count > 65535L)	/* safety check */
    ERREXIT(methods, "MAX_ALLOC_CHUNK should be less than 64K");
  if (jdos_write(info->handle.file_handle, buffer_address,
		 (unsigned short) byte_count))
    ERREXIT(methods, "write failed on temporary file --- out of disk space?");
}


METHODDEF void
close_file_store (backing_store_ptr info)
{
  jdos_close(info->handle.file_handle);	/* close the file */
  remove(info->temp_name);	/* delete the file */
/* If your system doesn't have remove(), try unlink() instead.
 * remove() is the ANSI-standard name for this function, but
 * unlink() was more common in pre-ANSI systems.
 */
  TRACEMS1(methods, 1, "Closed DOS file %d", info->handle.file_handle);
}


LOCAL boolean
open_file_store (backing_store_ptr info, long total_bytes_needed)
{
  short handle;
  char tracemsg[TEMP_NAME_LENGTH+40];

  select_file_name(info->temp_name);
  if (jdos_open((short far *) & handle, (char far *) info->temp_name)) {
    /* hack to get around TRACEMS' inability to handle string parameters */
    sprintf(tracemsg, "Failed to create temporary file %s", info->temp_name);
    ERREXIT(methods, tracemsg);	/* jopen_backing_store will fail anyway */
    return FALSE;
  }
  info->handle.file_handle = handle;
  info->read_backing_store = read_file_store;
  info->write_backing_store = write_file_store;
  info->close_backing_store = close_file_store;
  /* hack to get around TRACEMS' inability to handle string parameters */
  sprintf(tracemsg, "Opened DOS file %d  %s", handle, info->temp_name);
  TRACEMS(methods, 1, tracemsg);
  return TRUE;			/* succeeded */
}


/*
 * Access methods for extended memory.
 */

#if XMS_SUPPORTED

static XMSDRIVER xms_driver;	/* saved address of XMS driver */

typedef union {			/* either long offset or real-mode pointer */
	long offset;
	void far * ptr;
      } XMSPTR;

typedef struct {		/* XMS move specification structure */
	long length;
	XMSH src_handle;
	XMSPTR src;
	XMSH dst_handle;
	XMSPTR dst;
      } XMSspec;

#define ODD(X)	(((X) & 1L) != 0)


METHODDEF void
read_xms_store (backing_store_ptr info, void FAR * buffer_address,
		long file_offset, long byte_count)
{
  XMScontext ctx;
  XMSspec spec;
  char endbuffer[2];

  /* The XMS driver can't cope with an odd length, so handle the last byte
   * specially if byte_count is odd.  We don't expect this to be common.
   */

  spec.length = byte_count & (~ 1L);
  spec.src_handle = info->handle.xms_handle;
  spec.src.offset = file_offset;
  spec.dst_handle = 0;
  spec.dst.ptr = buffer_address;
  
  ctx.ds_si = (void far *) & spec;
  ctx.ax = 0x0b00;		/* EMB move */
  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
  if (ctx.ax != 1)
    ERREXIT(methods, "read from extended memory failed");

  if (ODD(byte_count)) {
    read_xms_store(info, (void FAR *) endbuffer,
		   file_offset + byte_count - 1L, 2L);
    ((char FAR *) buffer_address)[byte_count - 1L] = endbuffer[0];
  }
}


METHODDEF void
write_xms_store (backing_store_ptr info, void FAR * buffer_address,
		 long file_offset, long byte_count)
{
  XMScontext ctx;
  XMSspec spec;
  char endbuffer[2];

  /* The XMS driver can't cope with an odd length, so handle the last byte
   * specially if byte_count is odd.  We don't expect this to be common.
   */

  spec.length = byte_count & (~ 1L);
  spec.src_handle = 0;
  spec.src.ptr = buffer_address;
  spec.dst_handle = info->handle.xms_handle;
  spec.dst.offset = file_offset;

  ctx.ds_si = (void far *) & spec;
  ctx.ax = 0x0b00;		/* EMB move */
  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
  if (ctx.ax != 1)
    ERREXIT(methods, "write to extended memory failed");

  if (ODD(byte_count)) {
    read_xms_store(info, (void FAR *) endbuffer,
		   file_offset + byte_count - 1L, 2L);
    endbuffer[0] = ((char FAR *) buffer_address)[byte_count - 1L];
    write_xms_store(info, (void FAR *) endbuffer,
		    file_offset + byte_count - 1L, 2L);
  }
}


METHODDEF void
close_xms_store (backing_store_ptr info)
{
  XMScontext ctx;

  ctx.dx = info->handle.xms_handle;
  ctx.ax = 0x0a00;
  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
  TRACEMS1(methods, 1, "Freed XMS handle %u", info->handle.xms_handle);
  /* we ignore any error return from the driver */
}


LOCAL boolean
open_xms_store (backing_store_ptr info, long total_bytes_needed)
{
  XMScontext ctx;

  /* Get address of XMS driver */
  jxms_getdriver((XMSDRIVER far *) & xms_driver);
  if (xms_driver == NULL)
    return FALSE;		/* no driver to be had */

  /* Get version number, must be >= 2.00 */
  ctx.ax = 0x0000;
  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
  if (ctx.ax < (unsigned short) 0x0200)
    return FALSE;

  /* Try to get space (expressed in kilobytes) */
  ctx.dx = (unsigned short) ((total_bytes_needed + 1023L) >> 10);
  ctx.ax = 0x0900;
  jxms_calldriver(xms_driver, (XMScontext far *) & ctx);
  if (ctx.ax != 1)
    return FALSE;

  /* Succeeded, save the handle and away we go */
  info->handle.xms_handle = ctx.dx;
  info->read_backing_store = read_xms_store;
  info->write_backing_store = write_xms_store;
  info->close_backing_store = close_xms_store;
  TRACEMS1(methods, 1, "Obtained XMS handle %u", ctx.dx);
  return TRUE;			/* succeeded */
}

#endif /* XMS_SUPPORTED */


/*
 * Access methods for expanded memory.
 */

#if EMS_SUPPORTED

/* The EMS move specification structure requires word and long fields aligned
 * at odd byte boundaries.  Some compilers will align struct fields at even
 * byte boundaries.  While it's usually possible to force byte alignment,
 * that causes an overall performance penalty and may pose problems in merging
 * JPEG into a larger application.  Instead we accept some rather dirty code
 * here.  Note this code would fail if the hardware did not allow odd-byte
 * word & long accesses, but all 80x86 CPUs do.
 */

typedef void far * EMSPTR;

typedef union {			/* EMS move specification structure */
	long length;		/* It's easy to access first 4 bytes */
	char bytes[18];		/* Misaligned fields in here! */
      } EMSspec;

/* Macros for accessing misaligned fields */
#define FIELD_AT(spec,offset,type)  (*((type *) &(spec.bytes[offset])))
#define SRC_TYPE(spec)		FIELD_AT(spec,4,char)
#define SRC_HANDLE(spec)	FIELD_AT(spec,5,EMSH)
#define SRC_OFFSET(spec)	FIELD_AT(spec,7,unsigned short)
#define SRC_PAGE(spec)		FIELD_AT(spec,9,unsigned short)
#define SRC_PTR(spec)		FIELD_AT(spec,7,EMSPTR)
#define DST_TYPE(spec)		FIELD_AT(spec,11,char)
#define DST_HANDLE(spec)	FIELD_AT(spec,12,EMSH)
#define DST_OFFSET(spec)	FIELD_AT(spec,14,unsigned short)
#define DST_PAGE(spec)		FIELD_AT(spec,16,unsigned short)
#define DST_PTR(spec)		FIELD_AT(spec,14,EMSPTR)

#define EMSPAGESIZE	16384L	/* gospel, see the EMS specs */

#define HIBYTE(W)  (((W) >> 8) & 0xFF)
#define LOBYTE(W)  ((W) & 0xFF)


METHODDEF void
read_ems_store (backing_store_ptr info, void FAR * buffer_address,
		long file_offset, long byte_count)
{
  EMScontext ctx;
  EMSspec spec;

  spec.length = byte_count;
  SRC_TYPE(spec) = 1;
  SRC_HANDLE(spec) = info->handle.ems_handle;
  SRC_PAGE(spec)   = (unsigned short) (file_offset / EMSPAGESIZE);
  SRC_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE);
  DST_TYPE(spec) = 0;
  DST_HANDLE(spec) = 0;
  DST_PTR(spec)    = buffer_address;
  
  ctx.ds_si = (void far *) & spec;
  ctx.ax = 0x5700;		/* move memory region */
  jems_calldriver((EMScontext far *) & ctx);
  if (HIBYTE(ctx.ax) != 0)
    ERREXIT(methods, "read from expanded memory failed");
}


METHODDEF void
write_ems_store (backing_store_ptr info, void FAR * buffer_address,
		 long file_offset, long byte_count)
{
  EMScontext ctx;
  EMSspec spec;

  spec.length = byte_count;
  SRC_TYPE(spec) = 0;
  SRC_HANDLE(spec) = 0;
  SRC_PTR(spec)    = buffer_address;
  DST_TYPE(spec) = 1;
  DST_HANDLE(spec) = info->handle.ems_handle;
  DST_PAGE(spec)   = (unsigned short) (file_offset / EMSPAGESIZE);
  DST_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE);
  
  ctx.ds_si = (void far *) & spec;
  ctx.ax = 0x5700;		/* move memory region */
  jems_calldriver((EMScontext far *) & ctx);
  if (HIBYTE(ctx.ax) != 0)
    ERREXIT(methods, "write to expanded memory failed");
}


METHODDEF void
close_ems_store (backing_store_ptr info)
{
  EMScontext ctx;

  ctx.ax = 0x4500;
  ctx.dx = info->handle.ems_handle;
  jems_calldriver((EMScontext far *) & ctx);
  TRACEMS1(methods, 1, "Freed EMS handle %u", info->handle.ems_handle);
  /* we ignore any error return from the driver */
}


LOCAL boolean
open_ems_store (backing_store_ptr info, long total_bytes_needed)
{
  EMScontext ctx;

  /* Is EMS driver there? */
  if (! jems_available())
    return FALSE;

  /* Get status, make sure EMS is OK */
  ctx.ax = 0x4000;
  jems_calldriver((EMScontext far *) & ctx);
  if (HIBYTE(ctx.ax) != 0)
    return FALSE;

  /* Get version, must be >= 4.0 */
  ctx.ax = 0x4600;
  jems_calldriver((EMScontext far *) & ctx);
  if (HIBYTE(ctx.ax) != 0 || LOBYTE(ctx.ax) < 0x40)
    return FALSE;

  /* Try to allocate requested space */
  ctx.ax = 0x4300;
  ctx.bx = (unsigned short) ((total_bytes_needed + EMSPAGESIZE-1L) / EMSPAGESIZE);
  jems_calldriver((EMScontext far *) & ctx);
  if (HIBYTE(ctx.ax) != 0)
    return FALSE;

  /* Succeeded, save the handle and away we go */
  info->handle.ems_handle = ctx.dx;
  info->read_backing_store = read_ems_store;
  info->write_backing_store = write_ems_store;
  info->close_backing_store = close_ems_store;
  TRACEMS1(methods, 1, "Obtained EMS handle %u", ctx.dx);
  return TRUE;			/* succeeded */
}

#endif /* EMS_SUPPORTED */


/*
 * Initial opening of a backing-store object.
 */

GLOBAL void
jopen_backing_store (backing_store_ptr info, long total_bytes_needed)
{
  /* Try extended memory, then expanded memory, then regular file. */
#if XMS_SUPPORTED
  if (open_xms_store(info, total_bytes_needed))
    return;
#endif
#if EMS_SUPPORTED
  if (open_ems_store(info, total_bytes_needed))
    return;
#endif
  if (open_file_store(info, total_bytes_needed))
    return;
  ERREXIT(methods, "Failed to create temporary file");
}


/*
 * These routines take care of any system-dependent initialization and
 * cleanup required.  Keep in mind that jmem_term may be called more than
 * once.
 */

GLOBAL void
jmem_init (external_methods_ptr emethods)
{
  methods = emethods;		/* save struct addr for error exit access */
  emethods->max_memory_to_use = DEFAULT_MAX_MEM;
  total_used = 0;
  next_file_num = 0;
}

GLOBAL void
jmem_term (void)
{
  /* Microsoft C, at least in v6.00A, will not successfully reclaim freed
   * blocks of size > 32Kbytes unless we give it a kick in the rear, like so:
   */
#ifdef NEED_FHEAPMIN
  _fheapmin();
#endif
}