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