/****************************************************************/ /* */ /* blockio.c */ /* DOS-C */ /* */ /* Block cache functions and device driver interface */ /* */ /* Copyright (c) 1995 */ /* Pasquale J. Villani */ /* All Rights Reserved */ /* */ /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */ /* */ /****************************************************************/ #include "portab.h" #include "globals.h" #ifdef VERSION_STRINGS static BYTE *blockioRcsId = "$Id: blockio.c 1702 2012-02-04 08:46:16Z perditionc $"; #endif #define b_next(bp) ((struct buffer FAR *)(MK_FP(FP_SEG(bp), bp->b_next))) #define b_prev(bp) ((struct buffer FAR *)(MK_FP(FP_SEG(bp), bp->b_prev))) #define bufptr(fbp) ((struct buffer FAR *)(MK_FP(FP_SEG(bp), fbp))) /************************************************************************/ /* */ /* block cache routines */ /* */ /************************************************************************/ /* #define DISPLAY_GETBLOCK */ STATIC BOOL flush1(struct buffer FAR * bp); /* this searches the buffer list for the given disk/block. returns: a far pointer to the buffer. If the buffer is found the UNCACHE bit is not set and else it is set. new: upper layer may set UNCACHE attribute UNCACHE buffers are recycled first. intended to be used for full sector reads into application buffer resets UNCACHE upon a "HIT" -- so then this buffer will not be recycled anymore. */ STATIC void move_buffer(struct buffer FAR *bp, size_t firstbp) { /* connect bp->b_prev and bp->b_next */ b_next(bp)->b_prev = bp->b_prev; b_prev(bp)->b_next = bp->b_next; /* insert bp between firstbp and firstbp->b_prev */ bp->b_prev = bufptr(firstbp)->b_prev; bp->b_next = firstbp; b_next(bp)->b_prev = FP_OFF(bp); b_prev(bp)->b_next = FP_OFF(bp); } STATIC struct buffer FAR *searchblock(ULONG blkno, COUNT dsk) { int fat_count = 0; struct buffer FAR *bp; size_t lastNonFat = 0; size_t uncacheBuf = 0; seg bufseg = FP_SEG(firstbuf); size_t firstbp = FP_OFF(firstbuf); #ifdef DISPLAY_GETBLOCK printf("[searchblock %d, blk %ld, buf ", dsk, blkno); #endif /* Search through buffers to see if the required block */ /* is already in a buffer */ bp = MK_FP(bufseg, firstbp); do { if ((bp->b_blkno == blkno) && (bp->b_flag & BFR_VALID) && (bp->b_unit == dsk)) { /* found it -- rearrange LRU links */ #ifdef DISPLAY_GETBLOCK printf("HIT %04x:%04x]\n", FP_SEG(bp), FP_OFF(bp)); #endif bp->b_flag &= ~BFR_UNCACHE; /* reset uncache attribute */ if (FP_OFF(bp) != firstbp) { *(UWORD *)&firstbuf = FP_OFF(bp); move_buffer(bp, firstbp); } return bp; } if (bp->b_flag & BFR_UNCACHE) uncacheBuf = FP_OFF(bp); if (bp->b_flag & BFR_FAT) fat_count++; else lastNonFat = FP_OFF(bp); bp = b_next(bp); } while (FP_OFF(bp) != firstbp); /* now take either the last buffer in chain (not used recently) or, if we are low on FAT buffers, the last non FAT buffer */ if (uncacheBuf) { bp = bufptr(uncacheBuf); } else if (bp->b_flag & BFR_FAT && fat_count < 3 && lastNonFat) { bp = bufptr(lastNonFat); } else { bp = b_prev(bufptr(firstbp)); } bp->b_flag |= BFR_UNCACHE; /* set uncache attribute */ #ifdef DISPLAY_GETBLOCK printf("MISS, replace %04x:%04x]\n", FP_SEG(bp), FP_OFF(bp)); #endif if (FP_OFF(bp) != firstbp) /* move to front */ { move_buffer(bp, firstbp); *(UWORD *)&firstbuf = FP_OFF(bp); } return bp; } BOOL DeleteBlockInBufferCache(ULONG blknolow, ULONG blknohigh, COUNT dsk, int mode) { struct buffer FAR *bp = firstbuf; /* Search through buffers to see if the required block */ /* is already in a buffer */ do { if (blknolow <= bp->b_blkno && bp->b_blkno <= blknohigh && (bp->b_flag & BFR_VALID) && (bp->b_unit == dsk)) { if (mode == XFR_READ) flush1(bp); else bp->b_flag = 0; } bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); return FALSE; } #if TOM void dumpBufferCache(void) { struct buffer FAR *bp = firstbuf; int printed = 0; /* Search through buffers to see if the required block */ /* is already in a buffer */ do { printf("%8lx %02x ", bp->b_blkno, bp->b_flag); if (++printed % 6 == 0) printf("\n"); bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); printf("\n"); } #endif /* */ /* Return the address of a buffer structure containing the */ /* requested block. */ /* if overwrite is set, then no need to read first */ /* */ /* returns: */ /* requested block with data */ /* failure: */ /* returns NULL */ /* */ struct buffer FAR *getblk(ULONG blkno, COUNT dsk, BOOL overwrite) { /* Search through buffers to see if the required block */ /* is already in a buffer */ struct buffer FAR *bp = searchblock(blkno, dsk); if (!(bp->b_flag & BFR_UNCACHE)) { return bp; } /* The block we need is not in a buffer, we must make a buffer */ /* available, and fill it with the desired block */ /* take the buffer that lbp points to and flush it, then read new block. */ if (!flush1(bp)) return NULL; /* Fill the indicated disk buffer with the current track and sector */ if (!overwrite && dskxfer(dsk, blkno, bp->b_buffer, 1, DSKREAD)) { return NULL; } bp->b_flag = BFR_VALID | BFR_DATA; bp->b_unit = dsk; bp->b_blkno = blkno; return bp; } /* */ /* Mark all buffers for a disk as not valid */ /* */ VOID setinvld(REG COUNT dsk) { struct buffer FAR *bp = firstbuf; do { if (bp->b_unit == dsk) bp->b_flag = 0; bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); } /* Check if there is at least one dirty buffer */ /* */ BOOL dirty_buffers(REG COUNT dsk) { struct buffer FAR *bp = firstbuf; do { if (bp->b_unit == dsk && (bp->b_flag & (BFR_VALID | BFR_DIRTY)) == (BFR_VALID | BFR_DIRTY)) return TRUE; bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); return FALSE; } /* */ /* */ /* Flush all buffers for a disk */ /* */ /* returns: */ /* TRUE on success */ /* */ BOOL flush_buffers(REG COUNT dsk) { struct buffer FAR *bp = firstbuf; REG BOOL ok = TRUE; bp = firstbuf; do { if (bp->b_unit == dsk) if (!flush1(bp)) ok = FALSE; bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); return ok; } /* */ /* Write one disk buffer */ /* */ STATIC BOOL flush1(struct buffer FAR * bp) { BOOL ok = TRUE; if ((bp->b_flag & (BFR_VALID | BFR_DIRTY)) == (BFR_VALID | BFR_DIRTY)) { #ifdef WITHFAT32 ULONG b_offset = 0; #else UWORD b_offset = 0; #endif UBYTE b_copies = 1; ULONG blkno = bp->b_blkno; if (bp->b_flag & BFR_FAT) { b_copies = bp->b_copies; b_offset = bp->b_offset; #ifdef WITHFAT32 if (b_offset == 0) /* FAT32 FS */ b_offset = bp->b_dpbp->dpb_xfatsize; #endif } while (b_copies--) { if (dskxfer(bp->b_unit, blkno, bp->b_buffer, 1, DSKWRITE)) ok = FALSE; blkno += b_offset; } } bp->b_flag &= ~BFR_DIRTY; /* even if error, mark not dirty */ if (!ok) /* otherwise system has trouble */ bp->b_flag &= ~BFR_VALID; /* continuing. */ return ok; } /* */ /* Write all disk buffers */ /* */ BOOL flush(void) { REG struct buffer FAR *bp = firstbuf; REG BOOL ok; ok = TRUE; do { if (!flush1(bp)) ok = FALSE; bp->b_flag &= ~BFR_VALID; bp = b_next(bp); } while (FP_OFF(bp) != FP_OFF(firstbuf)); network_redirector(REM_FLUSHALL); return (ok); } /************************************************************************/ /* */ /* Device Driver Interface Functions */ /* */ /************************************************************************/ /* */ /* Transfer one or more blocks to/from disk */ /* */ UWORD dskxfer(COUNT dsk, ULONG blkno, VOID FAR * buf, UWORD numblocks, COUNT mode) { register struct dpb FAR *dpbp = get_dpb(dsk); if (dpbp == NULL) { return 0x0201; /* illegal command */ } #if TOM #define KeyboardShiftState() (*(BYTE FAR *)(MK_FP(0x40,0x17))) if (KeyboardShiftState() & 0x01) { printf("dskxfer:%s %x - %lx %u\n", mode == DSKWRITE ? "write" : "read", dsk, blkno, numblocks); if ((KeyboardShiftState() & 0x03) == 3) dumpBufferCache(); } #endif for (;;) { IoReqHdr.r_length = sizeof(request); IoReqHdr.r_unit = dpbp->dpb_subunit; switch (mode) { case DSKWRITE: if (verify_ena) { IoReqHdr.r_command = C_OUTVFY; break; } /* else fall through */ case DSKWRITEINT26: IoReqHdr.r_command = C_OUTPUT; break; case DSKREADINT25: case DSKREAD: IoReqHdr.r_command = C_INPUT; break; default: return 0x0100; /* illegal command */ } IoReqHdr.r_status = 0; IoReqHdr.r_meddesc = dpbp->dpb_mdb; IoReqHdr.r_count = numblocks; if ((dpbp->dpb_device->dh_attr & ATTR_HUGE) || blkno >= MAXSHORT) { IoReqHdr.r_start = HUGECOUNT; IoReqHdr.r_huge = blkno; } else IoReqHdr.r_start = (UWORD)blkno; /* * Some drivers normalise transfer address so HMA transfers are disastrous! * Then transfer block through xferbuf (DiskTransferBuffer doesn't work!) * (But this won't work for multi-block HMA transfers... are there any?) */ if (FP_SEG(buf) >= 0xa000 && numblocks == 1 && bufloc != LOC_CONV) { IoReqHdr.r_trans = deblock_buf; if (mode == DSKWRITE) fmemcpy(deblock_buf, buf, dpbp->dpb_secsize); execrh((request FAR *) & IoReqHdr, dpbp->dpb_device); if (mode == DSKREAD) fmemcpy(buf, deblock_buf, dpbp->dpb_secsize); } else { IoReqHdr.r_trans = (BYTE FAR *) buf; execrh((request FAR *) & IoReqHdr, dpbp->dpb_device); } if ((IoReqHdr.r_status & (S_ERROR | S_DONE)) == S_DONE) break; /* INT25/26 (_SEEMS_ TO) return immediately with 0x8002, if drive is not online,... normal operations (DIR) wait for ABORT/RETRY other condition codes not tested */ if (mode >= DSKWRITEINT26) return (IoReqHdr.r_status); loop: switch (block_error(&IoReqHdr, dpbp->dpb_unit, dpbp->dpb_device, mode)) { case ABORT: case FAIL: return (IoReqHdr.r_status); case RETRY: continue; case CONTINUE: break; default: goto loop; } break; } /* retry loop */ /* *** Changed 9/4/00 BER */ return 0; /* Success! Return 0 for a successful operation. */ /* End of change */ } /* this removes any (additionally allocated) buffers from the HMA buffer chain, because they get allocated to the 'user' */ void AllocateHMASpace (size_t lowbuffer, size_t highbuffer) { REG struct buffer FAR *bp = firstbuf; int n; if (FP_SEG(bp) != 0xffff) return; n = LoL_nbuffers; do { /* check if buffer intersects with requested area */ if (FP_OFF(bp) < highbuffer && FP_OFF(bp+1) > lowbuffer) { flush1(bp); /* unlink bp from buffer chain */ b_prev(bp)->b_next = bp->b_next; b_next(bp)->b_prev = bp->b_prev; if (FP_OFF(bp) == FP_OFF(firstbuf)) firstbuf = b_next(bp); LoL_nbuffers--; } bp = b_next(bp); } while (--n); }