1 /*
2    "Disk-Video" (and RAM-Video and Expanded-Memory Video) routines
3 
4    Reworked with fast caching July '90 by Pieter Branderhorst.
5    (I'm proud of this cache handler, had to get my name on it!)
6    Caution when modifying any code in here:  bugs are possible which
7    slow the cache substantially but don't cause incorrect results.
8    Do timing tests for a variety of situations after any change.
9 
10 */
11 
12 #include <string.h>
13 
14   /* see Fractint.c for a description of the "include"  hierarchy */
15 #include "port.h"
16 #include "prototyp.h"
17 
18 #define BOXROW   6
19 #define BOXCOL   11
20 #define BOXWIDTH 57
21 #define BOXDEPTH 12
22 
23 int disk16bit = 0;         /* storing 16 bit values for continuous potential */
24 
25 static int timetodisplay;
26 static FILE *fp = NULL;
27 int disktarga;
28 
29 #define BLOCKLEN 2048   /* must be a power of 2, must match next */
30 #define BLOCKSHIFT 11   /* must match above */
31 #define CACHEMIN 4      /* minimum cache size in Kbytes */
32 #define CACHEMAX 64     /* maximum cache size in Kbytes */
33 #define FREEMEM  33     /* try to leave this much far memory unallocated */
34 #define HASHSIZE 1024   /* power of 2, near CACHEMAX/(BLOCKLEN+8) */
35 
36 static struct cache {   /* structure of each cache entry */
37    long offset;                    /* pixel offset in image */
38    BYTE pixel[BLOCKLEN];  /* one pixel per byte (this *is* faster) */
39    unsigned int hashlink;          /* ptr to next cache entry with same hash */
40    unsigned int dirty : 1;         /* changed since read? */
41    unsigned int lru : 1;           /* recently used? */
42    } far *cache_end, far *cache_lru, far *cur_cache;
43 
44 struct cache far *cache_start = NULL;
45 long high_offset;           /* highwater mark of writes */
46 long seek_offset;           /* what we'll get next if we don't seek */
47 long cur_offset;            /* offset of last block referenced */
48 int cur_row;
49 long cur_row_base;
50 unsigned int far *hash_ptr = NULL;
51 int pixelshift;
52 int headerlength;
53 unsigned int rowsize = 0;   /* doubles as a disk video not ok flag */
54 unsigned int colsize;       /* sydots, *2 when pot16bit */
55 
56 BYTE far *membuf;
57 U16 dv_handle = 0;
58 long memoffset = 0;
59 long oldmemoffset = 0;
60 BYTE far *membufptr;
61 
62 static void _fastcall near findload_cache(long);
63 static struct cache far * _fastcall near find_cache(long);
64 static void near write_cache_lru(void);
65 static void _fastcall near mem_putc(BYTE);
66 static BYTE near mem_getc(void);
67 static void _fastcall near mem_seek(long);
68 
startdisk()69 int startdisk()
70 {
71    if (!diskisactive)
72       return(0);
73    headerlength = disktarga = 0;
74    return (common_startdisk(sxdots,sydots,colors));
75    }
76 
pot_startdisk()77 int pot_startdisk()
78 {
79    int i;
80    if (dotmode == 11) /* ditch the original disk file */
81       enddisk();
82    else
83    {
84       static FCODE msg[] = {"clearing 16bit pot work area"};
85       showtempmsg(msg);
86    }
87    headerlength = disktarga = 0;
88    i = common_startdisk(sxdots,sydots<<1,colors);
89    cleartempmsg();
90    if (i == 0)
91       disk16bit = 1;
92    return (i);
93    }
94 
targa_startdisk(FILE * targafp,int overhead)95 int targa_startdisk(FILE *targafp,int overhead)
96 {
97    int i;
98    if (dotmode == 11) { /* ditch the original disk file, make just the targa */
99       enddisk();      /* close the 'screen' */
100       setnullvideo(); /* set readdot and writedot routines to do nothing */
101       }
102    headerlength = overhead;
103    fp = targafp;
104    disktarga = 1;
105    /*
106    i = common_startdisk(sxdots*3,sydots,colors);
107    */
108    i = common_startdisk(xdots*3,ydots,colors);
109    high_offset = 100000000L; /* targa not necessarily init'd to zeros */
110    return (i);
111 }
112 
common_startdisk(long newrowsize,long newcolsize,int colors)113 int _fastcall common_startdisk(long newrowsize, long newcolsize, int colors)
114 {
115    int i,freemem;
116    long memorysize, offset;
117    unsigned int far *fwd_link = NULL;
118    struct cache far *ptr1 = NULL;
119    long longtmp;
120    unsigned int cache_size;
121    BYTE far *tempfar = NULL;
122    char *savenameptr;
123 
124    if (diskflag)
125       enddisk();
126    if (dotmode == 11) { /* otherwise, real screen also in use, don't hit it */
127       char buf[50];
128       static FCODE fmsg1[] = {"'Disk-Video' mode"};
129       static FCODE fmsg2[] = {"Screen resolution: "};
130       static FCODE fsname[] = {"Save name: "};
131       static FCODE stat[] = {"Status:"};
132       helptitle();
133       setattr(1,0,C_DVID_BKGRD,24*80);  /* init rest to background */
134       for (i = 0; i < BOXDEPTH; ++i)
135          setattr(BOXROW+i,BOXCOL,C_DVID_LO,BOXWIDTH);  /* init box */
136       putstring(BOXROW+2,BOXCOL+4,C_DVID_HI,fmsg1);
137       putstring(BOXROW+4,BOXCOL+4,C_DVID_LO,fmsg2);
138       sprintf(buf,"%d x %d",sxdots,sydots);
139       putstring(-1,-1,C_DVID_LO,buf);
140       if (disktarga) {
141          static FCODE tarmsg[] = {"  24 bit Targa"};
142          putstring(-1,-1,C_DVID_LO,tarmsg);
143          }
144       else {
145          static FCODE clrmsg[] = {"  Colors: "};
146          putstring(-1,-1,C_DVID_LO,clrmsg);
147          sprintf(buf,"%d",colors);
148          putstring(-1,-1,C_DVID_LO,buf);
149          }
150       putstring(BOXROW+6,BOXCOL+4,C_DVID_LO,fsname);
151       savenameptr = strrchr(savename, SLASHC); /* check for full path */
152       if(savenameptr == NULL)
153          savenameptr = savename;
154       else
155          savenameptr++; /* point past slash */
156       sprintf(buf,"%s",savenameptr);
157       putstring(-1,-1,C_DVID_LO,buf);
158       putstring(BOXROW+10,BOXCOL+4,C_DVID_LO,stat);
159       {
160       static FCODE o_msg[] = {"clearing the 'screen'"};
161       char msg[sizeof(o_msg)];
162       far_strcpy(msg,o_msg);
163       dvid_status(0,msg);
164       }
165       }
166    cur_offset = seek_offset = high_offset = -1;
167    cur_row    = -1;
168    if (disktarga)
169       pixelshift = 0;
170    else {
171       pixelshift = 3;
172       i = 2;
173       while (i < colors) {
174          i *= i;
175          --pixelshift;
176          }
177       }
178    if(bf_math)
179       timetodisplay = 10;  /* time-to-display-status counter */
180    else
181       timetodisplay = 1000;  /* time-to-display-status counter */
182 
183    /* allocate cache: try for the max; leave FREEMEMk free if we can get
184       that much or more; if we can't get that much leave 1/2 of whatever
185       there is free; demand a certain minimum or nogo at all */
186    freemem = FREEMEM;
187 
188    for (cache_size = CACHEMAX; cache_size >= CACHEMIN; --cache_size) {
189       longtmp = ((int)cache_size < freemem) ? (long)cache_size << 11
190                                        : (long)(cache_size+freemem) << 10;
191       if ((tempfar = farmemalloc(longtmp)) != NULL) {
192          farmemfree(tempfar);
193          break;
194          }
195       }
196    if(debugflag==4200) cache_size = CACHEMIN;
197    longtmp = (long)cache_size << 10;
198    cache_start = (struct cache far *)farmemalloc(longtmp);
199    if (cache_size == 64)
200       --longtmp; /* safety for next line */
201    cache_end = (cache_lru = cache_start) + longtmp / sizeof(*cache_start);
202    hash_ptr  = (unsigned int far *)farmemalloc((long)(HASHSIZE<<1));
203    membuf = (BYTE far *)farmemalloc((long)BLOCKLEN);
204    if (cache_start == NULL || hash_ptr == NULL || membuf == NULL) {
205       static FCODE msg[]={"*** insufficient free memory for cache buffers ***"};
206       stopmsg(0,msg);
207       return(-1);
208       }
209    if (dotmode == 11) {
210       char buf[50];
211       sprintf(buf,"Cache size: %dK\n\n",cache_size);
212       putstring(BOXROW+8,BOXCOL+4,C_DVID_LO,buf);
213       }
214 
215    /* preset cache to all invalid entries so we don't need free list logic */
216    for (i = 0; i < HASHSIZE; ++i)
217       hash_ptr[i] = 0xffff; /* 0xffff marks the end of a hash chain */
218    longtmp = 100000000L;
219    for (ptr1 = cache_start; ptr1 < cache_end; ++ptr1) {
220       ptr1->dirty = ptr1->lru = 0;
221       fwd_link = hash_ptr
222          + (((unsigned short)(longtmp+=BLOCKLEN) >> BLOCKSHIFT) & (HASHSIZE-1));
223       ptr1->offset = longtmp;
224       ptr1->hashlink = *fwd_link;
225       *fwd_link = (char far *)ptr1 - (char far *)cache_start;
226       }
227 
228    memorysize = (long)(newcolsize) * newrowsize + headerlength;
229    if ((i = (short)memorysize & (BLOCKLEN-1)) != 0)
230       memorysize += BLOCKLEN - i;
231    memorysize >>= pixelshift;
232    memorysize >>= BLOCKSHIFT;
233    diskflag = 1;
234    rowsize = (unsigned int) newrowsize;
235    colsize = (unsigned int) newcolsize;
236 
237    if (disktarga) {
238    /* Retrieve the header information first */
239         BYTE far *tmpptr;
240       tmpptr = membuf;
241       fseek(fp, 0L,SEEK_SET);
242       for (i = 0; i < headerlength; i++)
243          *tmpptr++ = (BYTE)fgetc(fp);
244       fclose(fp);
245       dv_handle = MemoryAlloc((U16)BLOCKLEN, memorysize, DISK);
246    }
247    else
248       dv_handle = MemoryAlloc((U16)BLOCKLEN, memorysize, EXPANDED);
249    if (dv_handle == 0) {
250       static FCODE msg[]={"*** insufficient free memory/disk space ***"};
251       stopmsg(0,msg);
252       goodmode = 0;
253       rowsize = 0;
254       return(-1);
255    }
256 
257    if (dotmode == 11)
258      switch (MemoryType(dv_handle)) {
259          static FCODE fmsg1[] = {"Using no Memory, it's broke"};
260          static FCODE fmsg2[] = {"Using your Expanded Memory"};
261          static FCODE fmsg3[] = {"Using your Extended Memory"};
262          static FCODE fmsg4[] = {"Using your Disk Drive"};
263        case NOWHERE:
264        default:
265          putstring(BOXROW+2,BOXCOL+23,C_DVID_LO,fmsg1);
266          break;
267        case EXPANDED:
268          putstring(BOXROW+2,BOXCOL+23,C_DVID_LO,fmsg2);
269          break;
270        case EXTENDED:
271          putstring(BOXROW+2,BOXCOL+23,C_DVID_LO,fmsg3);
272          break;
273        case DISK:
274          putstring(BOXROW+2,BOXCOL+23,C_DVID_LO,fmsg4);
275          break;
276      }
277 
278    membufptr = membuf;
279 
280    if (!disktarga)
281       for (offset = 0; offset < memorysize; offset++) {
282            static FCODE cancel[] = {"Disk Video initialization interrupted:\n"};
283          SetMemory(0, (U16)BLOCKLEN, 1L, offset, dv_handle);
284          if (keypressed())           /* user interrupt */
285             if (stopmsg(2, cancel))  /* esc to cancel, else continue */
286             {
287                enddisk();
288                goodmode = 0;
289                return -2;            /* -1 == failed, -2 == cancel   */
290             }
291       }
292 
293    if (disktarga) { /* Put header information in the file */
294       MoveToMemory(membuf, (U16)headerlength, 1L, 0, dv_handle);
295    }
296 
297    if (dotmode == 11)
298       dvid_status(0,"");
299    return(0);
300 }
301 
enddisk()302 void enddisk()
303 {
304    if (fp != NULL) {
305       if (disktarga) /* flush the cache */
306          for (cache_lru = cache_start; cache_lru < cache_end; ++cache_lru)
307             if (cache_lru->dirty)
308                write_cache_lru();
309       fclose(fp);
310       }
311 
312    if (dv_handle != 0) {
313       MemoryRelease(dv_handle);
314       dv_handle = 0;
315    }
316    if (hash_ptr != NULL)
317       farmemfree((void far *)hash_ptr);
318    if (cache_start != NULL)
319       farmemfree((void far *)cache_start);
320    if (membuf != NULL)
321       farmemfree((void far *)membuf);
322    diskflag = rowsize = disk16bit = 0;
323    hash_ptr    = NULL;
324    cache_start = NULL;
325    fp          = NULL;
326 }
327 
readdisk(unsigned int col,unsigned int row)328 int readdisk(unsigned int col, unsigned int row)
329 {
330    int col_subscr;
331    long offset;
332    char buf[41];
333    if (--timetodisplay < 0) {  /* time to display status? */
334       if (dotmode == 11) {
335          sprintf(buf," reading line %4d",
336                 (row >= (unsigned int)sydots) ? row-sydots : row); /* adjust when potfile */
337          dvid_status(0,buf);
338          }
339       if(bf_math)
340          timetodisplay = 10;  /* time-to-display-status counter */
341       else
342          timetodisplay = 1000;  /* time-to-display-status counter */
343       }
344    if (row != (unsigned int)cur_row) { /* try to avoid ghastly code generated for multiply */
345       if (row >= colsize) /* while we're at it avoid this test if not needed  */
346          return(0);
347       cur_row_base = (long)(cur_row = row) * rowsize;
348       }
349    if (col >= rowsize)
350       return(0);
351    offset = cur_row_base + col;
352    col_subscr = (short)offset & (BLOCKLEN-1); /* offset within cache entry */
353    if (cur_offset != (offset & (0L-BLOCKLEN))) /* same entry as last ref? */
354       findload_cache(offset & (0L-BLOCKLEN));
355    return (cur_cache->pixel[col_subscr]);
356 }
357 
FromMemDisk(long offset,int size,void far * dest)358 int FromMemDisk(long offset, int size, void far *dest)
359 {
360    int col_subscr =  (int)(offset & (BLOCKLEN - 1));
361 
362    if (col_subscr + size > BLOCKLEN)            /* access violates  a */
363       return 0;                                 /*   cache boundary   */
364 
365    if (cur_offset != (offset & (0L-BLOCKLEN))) /* same entry as last ref? */
366       findload_cache (offset & (0L-BLOCKLEN));
367 
368    far_memcpy(dest, (void far *) &cur_cache->pixel[col_subscr], size);
369    cur_cache->dirty = 0;
370    return 1;
371 }
372 
373 
targa_readdisk(unsigned int col,unsigned int row,BYTE * red,BYTE * green,BYTE * blue)374 void targa_readdisk(unsigned int col, unsigned int row,
375                     BYTE *red, BYTE *green, BYTE *blue)
376 {
377    col *= 3;
378    *blue  = (BYTE)readdisk(col,row);
379    *green = (BYTE)readdisk(++col,row);
380    *red   = (BYTE)readdisk(col+1,row);
381 }
382 
writedisk(unsigned int col,unsigned int row,unsigned int color)383 void writedisk(unsigned int col, unsigned int row, unsigned int color)
384 {
385    int col_subscr;
386    long offset;
387    char buf[41];
388    if (--timetodisplay < 0) {  /* time to display status? */
389       if (dotmode == 11) {
390          sprintf(buf," writing line %4d",
391                 (row >= (unsigned int)sydots) ? row-sydots : row); /* adjust when potfile */
392          dvid_status(0,buf);
393          }
394       timetodisplay = 1000;
395       }
396    if (row != (unsigned int)cur_row)    { /* try to avoid ghastly code generated for multiply */
397       if (row >= colsize) /* while we're at it avoid this test if not needed  */
398          return;
399       cur_row_base = (long)(cur_row = row) * rowsize;
400       }
401    if (col >= rowsize)
402       return;
403    offset = cur_row_base + col;
404    col_subscr = (short)offset & (BLOCKLEN-1);
405    if (cur_offset != (offset & (0L-BLOCKLEN))) /* same entry as last ref? */
406       findload_cache(offset & (0L-BLOCKLEN));
407    if (cur_cache->pixel[col_subscr] != (color & 0xff)) {
408       cur_cache->pixel[col_subscr] = (BYTE)color;
409       cur_cache->dirty = 1;
410       }
411 }
412 
ToMemDisk(long offset,int size,void far * src)413 int ToMemDisk(long offset, int size, void far *src)
414 {
415    int col_subscr =  (int)(offset & (BLOCKLEN - 1));
416 
417    if (col_subscr + size > BLOCKLEN)            /* access violates  a */
418       return 0;                                 /*   cache boundary   */
419 
420    if (cur_offset != (offset & (0L-BLOCKLEN))) /* same entry as last ref? */
421       findload_cache (offset & (0L-BLOCKLEN));
422 
423    far_memcpy((void far *) &cur_cache->pixel[col_subscr], src, size);
424    cur_cache->dirty = 1;
425    return 1;
426 }
427 
targa_writedisk(unsigned int col,unsigned int row,BYTE red,BYTE green,BYTE blue)428 void targa_writedisk(unsigned int col, unsigned int row,
429                     BYTE red, BYTE green, BYTE blue)
430 {
431    writedisk(col*=3,row,blue);
432    writedisk(++col, row,green);
433    writedisk(col+1, row,red);
434 }
435 
findload_cache(long offset)436 static void _fastcall near findload_cache(long offset) /* used by read/write */
437 {
438 #ifndef XFRACT
439    unsigned int tbloffset;
440    int i,j;
441    unsigned int far *fwd_link;
442    BYTE far *pixelptr;
443    BYTE tmpchar;
444    cur_offset = offset; /* note this for next reference */
445    /* check if required entry is in cache - lookup by hash */
446    tbloffset = hash_ptr[ ((unsigned short)offset >> BLOCKSHIFT) & (HASHSIZE-1) ];
447    while (tbloffset != 0xffff) { /* follow the hash chain */
448       cur_cache = (struct cache far *)((char far *)cache_start + tbloffset);
449       if (cur_cache->offset == offset) { /* great, it is in the cache */
450          cur_cache->lru = 1;
451          return;
452          }
453       tbloffset = cur_cache->hashlink;
454       }
455    /* must load the cache entry from backing store */
456    for(;;) { /* look around for something not recently used */
457       if (++cache_lru >= cache_end)
458          cache_lru = cache_start;
459       if (cache_lru->lru == 0)
460          break;
461       cache_lru->lru = 0;
462       }
463    if (cache_lru->dirty) /* must write this block before reusing it */
464       write_cache_lru();
465    /* remove block at cache_lru from its hash chain */
466    fwd_link = hash_ptr
467             + (((unsigned short)cache_lru->offset >> BLOCKSHIFT) & (HASHSIZE-1));
468    tbloffset = (char far *)cache_lru - (char far *)cache_start;
469    while (*fwd_link != tbloffset)
470       fwd_link = &((struct cache far *)((char far *)cache_start+*fwd_link))->hashlink;
471    *fwd_link = cache_lru->hashlink;
472    /* load block */
473    cache_lru->dirty  = 0;
474    cache_lru->lru    = 1;
475    cache_lru->offset = offset;
476    pixelptr = &cache_lru->pixel[0];
477    if (offset > high_offset) { /* never been this high before, just clear it */
478       high_offset = offset;
479       for (i = 0; i < BLOCKLEN; ++i)
480          *(pixelptr++) = 0;
481       }
482    else {
483       if (offset != seek_offset)
484          mem_seek(offset >> pixelshift);
485       seek_offset = offset + BLOCKLEN;
486       switch (pixelshift) {
487          case 0:
488             for (i = 0; i < BLOCKLEN; ++i)
489                *(pixelptr++) = mem_getc();
490             break;
491          case 1:
492             for (i = 0; i < BLOCKLEN/2; ++i) {
493                tmpchar = mem_getc();
494                *(pixelptr++) = (BYTE)(tmpchar >> 4);
495                *(pixelptr++) = (BYTE)(tmpchar & 15);
496                }
497             break;
498          case 2:
499             for (i = 0; i < BLOCKLEN/4; ++i) {
500                tmpchar = mem_getc();
501                for (j = 6; j >= 0; j -= 2)
502                   *(pixelptr++) = (BYTE)((tmpchar >> j) & 3);
503                }
504             break;
505          case 3:
506             for (i = 0; i < BLOCKLEN/8; ++i) {
507                tmpchar = mem_getc();
508                for (j = 7; j >= 0; --j)
509                   *(pixelptr++) = (BYTE)((tmpchar >> j) & 1);
510                }
511             break;
512          }
513       }
514    /* add new block to its hash chain */
515    fwd_link = hash_ptr + (((unsigned short)offset >> BLOCKSHIFT) & (HASHSIZE-1));
516    cache_lru->hashlink = *fwd_link;
517    *fwd_link = (char far *)cache_lru - (char far *)cache_start;
518    cur_cache = cache_lru;
519 #endif
520    }
521 
find_cache(long offset)522 static struct cache far * _fastcall near find_cache(long offset)
523 /* lookup for write_cache_lru */
524 {
525 #ifndef XFRACT
526    unsigned int tbloffset;
527    struct cache far *ptr1;
528    tbloffset = hash_ptr[ ((unsigned short)offset >> BLOCKSHIFT) & (HASHSIZE-1) ];
529    while (tbloffset != 0xffff) {
530       ptr1 = (struct cache far *)((char far *)cache_start + tbloffset);
531       if (ptr1->offset == offset)
532          return (ptr1);
533       tbloffset = ptr1->hashlink;
534       }
535    return (NULL);
536 #endif
537 }
538 
write_cache_lru()539 static void near write_cache_lru()
540 {
541    int i,j;
542    BYTE far *pixelptr;
543    long offset;
544    BYTE tmpchar = 0;
545    struct cache far *ptr1, far *ptr2;
546 #define WRITEGAP 4 /* 1 for no gaps */
547    /* scan back to also write any preceding dirty blocks, skipping small gaps */
548    ptr1 = cache_lru;
549    offset = ptr1->offset;
550    i = 0;
551    while (++i <= WRITEGAP) {
552       if ((ptr2 = find_cache(offset -= BLOCKLEN)) != NULL && ptr2->dirty) {
553          ptr1 = ptr2;
554          i = 0;
555          }
556       }
557    /* write all consecutive dirty blocks (often whole cache in 1pass modes) */
558    /* keep going past small gaps */
559 write_seek:
560    mem_seek(ptr1->offset >> pixelshift);
561 write_stuff:
562    pixelptr = &ptr1->pixel[0];
563    switch (pixelshift) {
564       case 0:
565          for (i = 0; i < BLOCKLEN; ++i)
566             mem_putc(*(pixelptr++));
567          break;
568       case 1:
569          for (i = 0; i < BLOCKLEN/2; ++i) {
570             tmpchar = (BYTE)(*(pixelptr++) << 4);
571             tmpchar = (BYTE)(tmpchar + *(pixelptr++));
572             mem_putc(tmpchar);
573             }
574          break;
575       case 2:
576          for (i = 0; i < BLOCKLEN/4; ++i) {
577             for (j = 6; j >= 0; j -= 2)
578                tmpchar = (BYTE)((tmpchar << 2) + *(pixelptr++));
579             mem_putc(tmpchar);
580             }
581          break;
582       case 3:
583          for (i = 0; i < BLOCKLEN/8; ++i) {
584             mem_putc((BYTE)
585                         ((((((((((((((*pixelptr
586                         << 1)
587                         | *(pixelptr+1) )
588                         << 1)
589                         | *(pixelptr+2) )
590                         << 1)
591                         | *(pixelptr+3) )
592                         << 1)
593                         | *(pixelptr+4) )
594                         << 1)
595                         | *(pixelptr+5) )
596                         << 1)
597                         | *(pixelptr+6) )
598                         << 1)
599                         | *(pixelptr+7)));
600             pixelptr += 8;
601             }
602          break;
603       }
604    ptr1->dirty = 0;
605    offset = ptr1->offset + BLOCKLEN;
606    if ((ptr1 = find_cache(offset)) != NULL && ptr1->dirty != 0)
607       goto write_stuff;
608    i = 1;
609    while (++i <= WRITEGAP) {
610       if ((ptr1 = find_cache(offset += BLOCKLEN)) != NULL && ptr1->dirty != 0)
611          goto write_seek;
612       }
613    seek_offset = -1; /* force a seek before next read */
614 }
615 
616 /* Seek, mem_getc, mem_putc routines follow.
617    Note that the calling logic always separates mem_getc and mem_putc
618    sequences with a seek between them.  A mem_getc is never followed by
619    a mem_putc nor v.v. without a seek between them.
620    */
621 
mem_seek(long offset)622 static void _fastcall near mem_seek(long offset)        /* mem seek */
623 {
624    offset += headerlength;
625    memoffset = offset >> BLOCKSHIFT;
626    if (memoffset != oldmemoffset) {
627       MoveToMemory(membuf, (U16)BLOCKLEN, 1L, oldmemoffset, dv_handle);
628       MoveFromMemory(membuf, (U16)BLOCKLEN, 1L, memoffset, dv_handle);
629       }
630    oldmemoffset = memoffset;
631    membufptr = membuf + (offset & (BLOCKLEN - 1));
632    }
633 
mem_getc()634 static BYTE near mem_getc()                     /* memory get_char */
635 {
636    if (membufptr - membuf >= BLOCKLEN) {
637       MoveToMemory(membuf, (U16)BLOCKLEN, 1L, memoffset, dv_handle);
638       memoffset++;
639       MoveFromMemory(membuf, (U16)BLOCKLEN, 1L, memoffset, dv_handle);
640       membufptr = membuf;
641       oldmemoffset = memoffset;
642       }
643    return (*(membufptr++));
644    }
645 
mem_putc(BYTE c)646 static void _fastcall near mem_putc(BYTE c)     /* memory get_char */
647 {
648    if (membufptr - membuf >= BLOCKLEN) {
649       MoveToMemory(membuf, (U16)BLOCKLEN, 1L, memoffset, dv_handle);
650       memoffset++;
651       MoveFromMemory(membuf, (U16)BLOCKLEN, 1L, memoffset, dv_handle);
652       membufptr = membuf;
653       oldmemoffset = memoffset;
654       }
655    *(membufptr++) = c;
656    }
657 
658 
dvid_status(int line,char far * msg)659 void dvid_status(int line,char far *msg)
660 {
661    char buf[41];
662    int attrib;
663    memset(buf,' ',40);
664    far_memcpy(buf,msg,far_strlen(msg));
665    buf[40] = 0;
666    attrib = C_DVID_HI;
667    if (line >= 100) {
668       line -= 100;
669       attrib = C_STOP_ERR;
670       }
671    putstring(BOXROW+10+line,BOXCOL+12,attrib,buf);
672    movecursor(25,80);
673 }
674 
675