File: common\diskvid.c

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