UNIT Cache;

{**************************************************************************
 *                   Sourcefile for Speed-386                             *
 *        (C) 1993,94 R.Nrnberger Franz-Mehring-Str.2 09112 Chemnitz     *
 *                                                                        *
 *  Cache routines for fast input/output to files via memory buffer       *
 *                                                                        *
 *                                                                        *
 **************************************************************************}

INTERFACE

USES dos;

CONST
     MaxCacheFiles=10;  {Maximal file handles to cache}

     {Cache error codes}
     cerrorok=0;
     cerrornotinfile=1;
     cerrortoomanyfiles=2;
     cerrorclosed=3;
     cerrornomemory=4;
     cerrorcacheamount=5;
     cerrorother=99;

TYPE
     CacheFile=WORD;

CONST
     corewrite =1;
     coreset   =2;
     coappend  =3;

VAR CacheIOResult:LONGWORD;

FUNCTION CacheFilePos(nr:CacheFile):LONGWORD;
FUNCTION CacheEOF(nr:CacheFile):BOOLEAN;
PROCEDURE CacheSeek(nr:CacheFile;Pos:LONGWORD);
PROCEDURE CacheRewrite(var nr:CacheFile;fname:STRING; cache:Word);
PROCEDURE CacheReset(var nr:CacheFile;fname:STRING; cache:word);
PROCEDURE CacheAppend(var nr:CacheFile;fname:STRING; cache:word);
PROCEDURE CacheErase(fname:STRING);
PROCEDURE CacheClose(var nr:CacheFile);
PROCEDURE CacheClose1(var nr:CacheFile);
PROCEDURE CacheRead(nr:CacheFile; var value:BYTE);
PROCEDURE CachereadExt(nr:CacheFile; var value; l:LongWord);
PROCEDURE CacheWrite(nr:CacheFile;value:BYTE);
PROCEDURE CacheWriteExt(nr:CacheFile; var value; l:LongWord);
PROCEDURE CacheWriteString(nr:CacheFile; var value:STRING);
PROCEDURE cacheexit;
FUNCTION CacheOpen(fname:STRING;task:BYTE;cacheamount:LongWord):BYTE;
PROCEDURE CacheGetFTime(nr:CacheFile; var year,month,day,hour,min,twosec:WORD);
FUNCTION CacheFileSize(nr:CacheFile):LONGWORD;
PROCEDURE CacheInit;

VAR
   FileData:ARRAY[1..MaxCacheFiles] OF RECORD
                                      block,lblock:LONGWORD;
                                      offset,loffset:LONGWORD;
                                      bitpos:BYTE;
                                      changed:BOOLEAN;
                                      p:^ Word;
                                      maxcachemem:LONGWORD;
                                      f:FILE;
                                      Fill:WORD; {to fill up to 128 Byte}
                                    END;

IMPLEMENTATION

PROCEDURE cacheinit;
var i:CacheFile;
BEGIN
    FOR i:=1 to MaxCachefiles DO filedata[i].p:=NIL;
    CacheIOResult:=cerrorok;
END;

PROCEDURE CacheGetFTime(nr:CacheFile; var year,month,day,hour,min,twosec:WORD);
BEGIN
     GetFTime(filedata[nr].f,year,month,day,hour,min,twosec);
     IF doserror=0 THEN CacheIOResult:=cerrorok
     ELSE CacheIOResult:=cerrorclosed;
END;

PROCEDURE expand(nr:CacheFile);
BEGIN
     {inc(filedata[nr].loffset);
     IF filedata[nr].loffset=filedata[nr].maxcachemem THEN
     BEGIN
          inc(filedata[nr].lblock);
          filedata[nr].loffset:=0;
     END;}
     ASM
        MOV ESI,OFFSET(_FILEDATA)
        MOVZXW EAX,$NR
        DEC EAX
        SHL EAX,7
        ADD ESI,EAX              ;This does not change during the run

        INCD [ESI+12]            ;Filedata[nr].loffset

        MOV EAX,[ESI+12]         ;Filedata[nr].lOffset
        CMP EAX,[ESI+22]         ;Filedata[nr].Maxcachemem
        JNE _!L8

        INCD [ESI+4]             ;Filedata[nr].lblock
        MOVD [ESI+12],0          ;Filedata[nr].loffset
_!L8:
     END; {asm}
END;

FUNCTION CacheFilePos(nr:CacheFile):LONGWORD;
BEGIN
     {CacheFilePos:=filedata[nr].block*filedata[nr].maxcachemem+filedata[nr].offset;}
     ASM
         MOV ESI,OFFSET(_FILEDATA)
         MOVZXW EAX,$NR
         DEC EAX
         SHL EAX,7
         ADD ESI,EAX

         MOV EAX,[ESI+0]   ;Filedata[nr].block
         MULD [ESI+22]     ;Filedata[nr].MaxCachemem
         ADD EAX,[ESI+8]   ;Filedata[nr].Offset
         MOV [EBP-4],EAX   ;Function result
     END;
END;

FUNCTION CacheFileSize(nr:CacheFile):LONGWORD;
BEGIN
     CacheFileSize:=filedata[nr].lblock*filedata[nr].maxcachemem+filedata[nr].loffset;
END;

PROCEDURE CacheBlockIO(nr:CacheFile;blocknr:LONGWORD;_write:BOOLEAN);
var l:LONGWORD;
    po:LONGWORD;
BEGIN
     {IF filedata[nr].changed THEN
     BEGIN
          filedata[nr].changed:=FALSE;
          cacheblockio(nr,filedata[nr].block,TRUE);
          IF CacheIOResult>cerrorok THEN Exit;
     END;
     IF blocknr=filedata[nr].lblock THEN l:=filedata[nr].loffset
     ELSE l:=filedata[nr].maxcachemem;
     po:=filedata[nr].maxcachemem;
     po:=po*blocknr;
     Seek(filedata[nr].f,po);
     CacheIOResult:=ioresult;
     IF l>0 THEN IF CacheIOResult=0 THEN
     BEGIN
          IF _write THEN BlockWrite(filedata[nr].f,filedata[nr].p^,l)
          ELSE BlockRead(filedata[nr].f,filedata[nr].p^,l);
          CacheIOResult:=ioresult;
     END;}
     ASM
         MOV ESI,OFFSET(_FILEDATA)
         MOVZXW EAX,$NR
         DEC EAX
         SHL EAX,7
         ADD ESI,EAX             ;this does not change during the run

         CMPB [ESI+17],0         ;Filedata[nr].changed
         JE _!L13                ;not changed

         ;Filedata[nr].changed
         MOVB [ESI+17],0         ;Filedata[nr].Changed=FALSE

         PUSH ESI                ;for further use

         PUSH $NR
         PUSHL [ESI+0]
         PUSH 1
         CALLN32 _CACHEBLOCKIO

         POP ESI

         CMPD _CACHEIORESULT,0
         JNE _!L19        ;exit this
_!L13:
         ;Filedata[nr] not changed
         MOV EBX,[ESI+22]  ;Filedata[nr].maxcachemem

         MOV EAX,$BLOCKNR
         CMP EAX,[ESI+4]   ;Filedata[nr].lblock
         JNE _!L17         ;otherwise its maxcachemem from above
         MOV EBX,[ESI+12]  ;Filedata[nr].loffset
_!L17:
         MOV $L,EBX
_!L18:
         MOV EAX,[ESI+22]  ;Filedata[nr].maxcachemem
         MULD $BLOCKNR
         MOV $PO,EAX

         PUSH ESI          ;for further use

         LEA EAX,[ESI+26]  ;Filedata[nr].f
         PUSH EAX
         PUSHL $PO
         CALLN32 SYSTEM._SEEK

         POP ESI
         MOV EAX,_IORESULT
         MOV _CACHEIORESULT,EAX
         CMPD $L,0
         JNA _!L19        ;exit

         CMPD _CACHEIORESULT,0
         JNE _!L19        ;exit

         CMPB $_WRITE,0
         JE _!L23         ;Read is requested

         LEA EAX,[ESI+26]    ;Filedata[nr].f
         PUSH EAX
         MOV ESI,[ESI+18]    ;Filedata[nr].p^
         PUSH ESI
         PUSHL $L
         CALLN32 SYSTEM._BLOCKWRITE  ;dont care about ESI
         JMP _!L24
_!L23:
         LEA EAX,[ESI+26]    ;Filedata[nr].f
         PUSH EAX
         MOV ESI,[ESI+18]    ;Filedata[nr].p^
         PUSH ESI
         PUSHL $L
         CALLN32 SYSTEM._BLOCKREAD   ;dont care about ESI
_!L24:
         MOV EAX,_IORESULT
         MOV _CACHEIORESULT,EAX
_!L19:
     END;
END;


FUNCTION CacheEOF(nr:CacheFile):BOOLEAN;
BEGIN
     cacheeof:=(filedata[nr].offset=filedata[nr].loffset)AND(
       filedata[nr].block=filedata[nr].lblock);
     {ASM
        MOVB [EBP-4],0       ;result is initially false
        MOV ESI,OFFSET(_FILEDATA)
        MOVZXW EAX,$NR
        DEC EAX
        SHL EAX,7
        ADD ESI,EAX            ;This does not change during the run

        MOV EAX,[ESI+8]        ;Filedata[nr].offset
        CMP EAX,[ESI+12]       ;Filedata[nr].loffset
        JNE _!L25
        MOV EAX,[ESI+0]        ;Filedata[nr].block
        CMP EAX,[ESI+4]        ;Filedata[nr].lblock
        JNE _!L25
        MOVB [EBP-4],1         ;EOF is TRUE
_!L25:
     END;} {asm}
END;


PROCEDURE CacheSeek(nr:CacheFile;Pos:LONGWORD);
var
    pblock:LONGWORD;
    poffset:LongWord;
BEGIN
  {CacheIOResult:=cerrorok;
  pblock:=Pos DIV filedata[nr].maxcachemem;
  poffset:=Pos MOD filedata[nr].maxcachemem;
  IF Pos>filedata[nr].loffset+filedata[nr].maxcachemem*filedata[nr].lblock THEN
  BEGIN
       CacheIOResult:=cerrornotinfile;
       Exit;
  END;
  IF pblock<>filedata[nr].block THEN cacheblockio(nr,pblock,FALSE);
  filedata[nr].bitpos:=0;
  filedata[nr].offset:=poffset;
  filedata[nr].block:=pblock;}
  ASM
      MOVD _CACHEIORESULT,0

      MOV ESI,OFFSET(_FILEDATA)
      MOVZXW EAX,$NR
      DEC EAX
      SHL EAX,7
      ADD ESI,EAX             ;This does not change during the run

      MOV EAX,$POS
      XOR EDX,EDX
      DIVD [ESI+22]           ;Filedata[nr].maxcachemem
      MOV $PBLOCK,EAX
      MOV $POFFSET,EDX

      MOV EAX,[ESI+22]        ;Filedata[nr].maxcachemem
      MULD [ESI+4]            ;Filedata[nr].lblock
      ADD EAX,[ESI+12]        ;Filedata[nr].lOffset
      CMP $POS,EAX
      JBE _!L27
      MOVD _CACHEIORESULT,1
      JMP _!L26                  ;exit this
_!L27:
      MOV EAX,$PBLOCK
      CMP EAX,[ESI+0]             ;Filedata[nr].block
      JE _!L29

      PUSH ESI                  ;for further use

      PUSH $NR
      PUSHL $PBLOCK
      PUSH 0                    ;read it
      CALLN32 _CACHEBLOCKIO

      POP ESI
_!L29:
      MOVB [ESI+16],0           ;filedata[nr].bitpos
      MOV EAX,$POFFSET
      MOV [ESI+8],EAX           ;filedata[nr].offset
      MOV EAX,$PBLOCK
      MOV [ESI+0],EAX           ;filedata[nr].block
_!L26:
  END; {asm}
END;

FUNCTION CacheOpen(fname:STRING;task:BYTE;cacheamount:word):BYTE;
var
   l:LONGWORD;
   nr:BYTE;
BEGIN
  nr:=1;
  CacheIOResult:=cerrorok;
  while(filedata[nr].p<>NIL)AND(nr<=maxcachefiles+1) DO inc(nr);
  IF nr>maxcachefiles THEN
  BEGIN
      CacheIOResult:=cerrortoomanyfiles;
      Exit;
  END;
  Cacheopen:=nr;

  filedata[nr].maxcachemem:=cacheamount;
  Assign(filedata[nr].f,fname);
  filedata[nr].changed:=FALSE;
  filedata[nr].bitpos:=0;
  CASE task OF
        corewrite:Rewrite(filedata[nr].f,1);
        coAppend,coreset:Reset(filedata[nr].f,1);
  END; {case}
  CacheIOResult:=ioresult;
  IF CacheIOResult>cerrorok THEN Exit;
  l:=filesize(filedata[nr].f);
  filedata[nr].lblock:=l DIV filedata[nr].maxcachemem;
  filedata[nr].loffset:=l MOD filedata[nr].maxcachemem;
  GetMem(filedata[nr].p,filedata[nr].maxcachemem);

  IF task<>coappend THEN
  BEGIN
       cacheblockio(nr,0,FALSE); {read}
       filedata[nr].block:=0;
       filedata[nr].offset:=0;
  END
  ELSE
  BEGIN
       cacheblockio(nr,filedata[nr].lblock,FALSE); {read}
       filedata[nr].block:=filedata[nr].lblock;
       filedata[nr].offset:=filedata[nr].loffset;
  END;
END;


PROCEDURE CacheRewrite(var nr:CacheFile;
fname:STRING; cache:word);
BEGIN
  nr:=cacheopen(fname,corewrite,cache);
END;


PROCEDURE CacheReset(var nr:CacheFile;fname:STRING; cache:word);
BEGIN
     nr:=cacheopen(fname,coreset,cache);
END;


PROCEDURE CacheAppend(var nr:CacheFile;fname:STRING; cache:word);
BEGIN
     nr:=cacheopen(fname,coappend,cache);
END;



PROCEDURE CacheErase(fname:STRING);
BEGIN
  Erase(fname);
  CacheIOResult:=ioresult;
END;


PROCEDURE CacheClose(var nr:CacheFile);
BEGIN
  Cacheioresult:=cerrorok;
  IF nr>=MaxCacheFiles THEN exit;
  IF filedata[nr].p=NIL THEN
  BEGIN
      CacheIOResult:=cerrorclosed;
      Exit;
  END;
  IF filedata[nr].changed THEN
  BEGIN
       filedata[nr].changed:=FALSE;
       cacheblockio(nr,filedata[nr].block,TRUE); {write}
  END;
  Close(filedata[nr].f);
  CacheIOResult:=ioresult;
  FreeMem(filedata[nr].p,filedata[nr].maxcachemem);
  filedata[nr].p:=NIL;
  nr:=maxcachefiles;
END;


PROCEDURE CacheClose1(var nr:CacheFile);
BEGIN
  CacheIoresult:=Cerrorok;
  IF nr=MaxCacheFiles THEN exit;
  IF filedata[nr].p=NIL THEN
  BEGIN
      CacheIOResult:=cerrorclosed;
      Exit;
  END;
  Close(filedata[nr].f);
  CacheIOResult:=ioresult;
  FreeMem(filedata[nr].p,filedata[nr].maxcachemem);
  filedata[nr].p:=NIL;
  nr:=maxcachefiles;
END;


PROCEDURE CacheRead(nr:CacheFile; var value:BYTE);
var v:pointer;
    o:LONGWORD;
    pp:POINTER;
BEGIN
  {IF cacheeof(nr) THEN
  BEGIN
      CacheIOResult:=cerrornotinfile;
      Exit;
  END;
  o:=filedata[nr].offset;
  pp:=filedata[nr].p;
  ASM
     MOV ESI,$pp
     ADD ESI,$o
     CLD
     LODSB
     MOV EDI,$value
     STOSB
  END;
  inc(filedata[nr].offset);
  IF filedata[nr].offset>=filedata[nr].maxcachemem THEN
  BEGIN
       cacheblockio(nr,filedata[nr].block+1,FALSE);
       filedata[nr].offset:=0;
       inc(filedata[nr].block);
  END;}
  ASM
      PUSH $NR
      CALLN32 _CACHEEOF
      CMP AL,0
      JE _!L64
      MOVD _CACHEIORESULT,1
      JMP _!L63
_!L64:
      MOV ESI,OFFSET(_FILEDATA)
      MOVZXW EAX,$NR
      DEC EAX
      SHL EAX,7
      ADD ESI,EAX              ;This does not change dzring the run

      MOV EAX,[ESI+8]          ;Filedata[nr].offset
      MOV $O,EAX
      MOV EAX,[ESI+18]         ;Filedata[nr].p
      MOV $PP,EAX

      PUSH ESI                 ;for further use

      MOV ESI,$PP
      ADD ESI,$O
      CLD
      LODSB
      MOV EDI,$VALUE
      STOSB

      POP ESI

      INCD [ESI+8]             ;Filedata[nr].offset
      MOV EAX,[ESI+8]      ;Filedata[nr].offset
      CMP EAX,[ESI+22]     ;Filedata[nr].maxcachemem
      JB _!L63             ;exit this

      PUSH ESI             ;for further use

      PUSH $NR
      MOV EAX,[ESI+0]
      INC EAX
      PUSH EAX
      PUSH 0
      CALLN32 _CACHEBLOCKIO

      POP ESI

      MOVD [ESI+8],0       ;Filedata[nr].offset
      INCD [ESI+0]         ;Filedata[nr].block
_!L63:
  END;
END;


PROCEDURE CacheReadExt(nr:CacheFile; var value; l:LongWord);
var t:LongWord;
BEGIN
     IF l>0 THEN FOR t:=0 to l-1 DO
     BEGIN
          ASM
             PUSH $nr
             MOV EDI,$value
             ADD EDI,$t
             PUSH EDI
             CALLN32 _CacheRead
          END;
     END;
END;


PROCEDURE CacheWrite(nr:CacheFile;value:BYTE);
VAR pp:POINTER;
    o:LONGWORD;
    c:BOOLEAN;
BEGIN
     {pp:=filedata[nr].p;
     o:=filedata[nr].offset;
     c:=filedata[nr].Changed;
     ASM
        PUSH ESI

        MOV ESI,$pp
        ADD ESI,$o
        MOV AL,[ESI+0]
        CMP AL,$value
        JE !nw
        MOV AL,$value
        MOV [ESI+0],AL
        MOVB $c,1
!nw:
        POP ESI
     END;
     filedata[nr].changed:=c;
     IF CacheEOF(nr) THEN
     BEGIN
          filedata[nr].changed:=true;
          expand(nr);
     END;
     inc(filedata[nr].offset);

     IF filedata[nr].offset=filedata[nr].maxcachemem THEN
     BEGIN
          filedata[nr].changed:=FALSE;
          CacheBlockIO(nr,filedata[nr].block,TRUE);
          filedata[nr].offset:=0;
          inc(filedata[nr].block);
          CacheBlockIO(nr,filedata[nr].block,FALSE);
     END;}
     ASM
         MOV ESI,OFFSET(_FILEDATA)
         MOVZXW EAX,$NR
         DEC EAX
         SHL EAX,7
         ADD ESI,EAX               ;This  does not change during the run

         MOV EAX,[ESI+18]          ;Filedata[nr].p
         MOV $PP,EAX
         MOV EAX,[ESI+8]           ;Filedata[nr].offset
         MOV $O,EAX
         MOV AL,[ESI+17]           ;Filedata[nr].changed
         MOV $C,AL

         PUSH ESI                 ;for further use

         MOV ESI,$PP
         ADD ESI,$O
         MOV AL,[ESI+0]
         CMP AL,$VALUE
         JE !NW
         MOV AL,$VALUE
         MOV [ESI+0],AL
         MOVB $C,1
!NW:
         POP ESI

         MOV AL,$C
         MOV [ESI+17],AL           ;Filedata[nr].changed

         PUSH ESI                  ;For further use

         PUSH $NR
         CALLN32 _CACHEEOF

         POP ESI

         CMP AL,0
         JE _!L72

         MOVB [ESI+17],1           ;Filedata[nr].changed

         PUSH ESI                  ;for further use

         PUSH $NR
         CALLN32 _EXPAND

         POP ESI
_!L72:
         INCD [ESI+8]              ;Filedata[nr].offset

         MOV EAX,[ESI+8]           ;Filedata[nr].offset
         CMP EAX,[ESI+22]          ;Filedata[nr].maxcachemem
         JNE _!L74

         MOVB [ESI+17],0          ;Filedata[nr].changed

         PUSH ESI                 ;for further use

         ;write old block
         PUSH $NR
         PUSHL [ESI+0]            ;Filedata[nr].block
         PUSH 1
         CALLN32 _CACHEBLOCKIO

         POP ESI
         MOVD [ESI+8],0           ;Filedata[nr].offset
         INCD [ESI+0]             ;Filedata[nr].block

         ;Read new block
         PUSH $NR
         PUSHL [ESI+0]            ;Filedata[nr].block
         PUSH 0
         CALLN32 _CACHEBLOCKIO
_!L74:
     END;
END;


PROCEDURE CacheWriteExt(nr:CacheFile; var value; l:LongWord);
var
    t:LongWord;
    v:POINTER;
BEGIN
     ASM
        MOV EAX,$value
        MOV $v,EAX
     END;
     IF l>0 THEN FOR t:=0 to l-1 DO
     BEGIN
          ASM
             PUSH $nr
             MOV EDI,$v
             ADD EDI,$t
             MOV AL,[EDI+0]
             XOR AH,AH
             PUSH AX
             CALLN32 _CacheWrite
          END;
          IF cacheioresult<>0 THEN exit;
     END;
END;


PROCEDURE CacheWriteString(nr:CacheFile; var value:STRING);
BEGIN
     ASM
        PUSH $nr         ;File
        MOV EDI,$value
        MOV AL,[EDI+0]   ;length
        INC EDI  ;value[1]
        PUSH EDI
        MOVZX EAX,AL
        PUSH EAX
        CALLN32 _CacheWriteExt
     END;
END;


var CacheExitSave:pointer;


PROCEDURE cacheexit;
var i:CacheFile;
BEGIN
  Exitproc:=CacheExitsave;
  FOR i:=1 to maxcachefiles DO
  BEGIN
       IF filedata[i].p<>NIL THEN
       BEGIN
            IF filedata[i].changed THEN
            BEGIN
                 filedata[i].changed:=FALSE;
                 cacheblockio(i,filedata[i].block,TRUE); {write}
            END;
            Close(filedata[i].f);
            CacheIOResult:=ioresult;
            FreeMem(filedata[i].p,filedata[i].maxcachemem);
            filedata[i].p:=NIL;
       END;
   END;
END;

BEGIN
  CacheExitsave:=Exitproc;
  Exitproc:=@cacheexit;
  cacheinit;
END.
