
/*
 * Copyright 1987 Alan Kent
 *
 * Permission is granted to redistribute this code as long
 * as this message is retained in the code and the code is
 * not sold without written permission from the author.
 *
 * UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
 * ACSnet: ajk@goanna.oz
 * ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
 */


#include "hd.h"


extern struct Node *RemTail ();


#define CACHE_SIZE		10


static struct cache {

	struct Node node;
	struct posn posn;
	UBYTE *		buf;

} cache[ CACHE_SIZE ];


static struct List history;


void
clear_all ()
{
	register int i;
	register struct cache *c;

	NewList ( &history );
	for ( i = 0; i < CACHE_SIZE; i++ ) {
		c = &cache[i];
		c->posn.cylinder = -1;
		c->node.ln_Pri = 0;
		AddTail ( &history , c );
	}
}


void
flush_all ()
{
}


int
init_cache ()
{
	int i;

	for ( i = 0; i < CACHE_SIZE; i++ ) {
		if ( ( cache[i].buf = (UBYTE *) AllocMem (
		(LONG)HD_SECTOR * first.sectors , (LONG)MEMF_CLEAR ) ) == NULL ) {
			while ( --i >= 0 )
				FreeMem ( cache[i].buf , (LONG)HD_SECTOR * first.sectors );
			return ( -1 );
		}
	}
	clear_all ();
	return ( 0 );
}


void
free_cache ()
{
	register int i;

	for ( i = 0; i < CACHE_SIZE; i++ )
		FreeMem ( cache[i].buf , (LONG)HD_SECTOR * first.sectors );
}


UBYTE *
read_cache ( posn )
struct posn *posn;
{
	register struct cache *c;
	int status;
	static UBYTE single_buf[ HD_SECTOR ];


	/* see if track is in the cache */

	for ( c = (struct cache *) history.lh_Head;
	c->node.ln_Succ != NULL;
	c = (struct cache *) c->node.ln_Succ ) {

		if ( posn->cylinder == c->posn.cylinder
		&&   posn->surface == c->posn.surface ) {

			/* recalculate priority so is not pushed out of list */

			Remove ( c );
			calc_priority ( c );
			Enqueue ( &history , c );
			return ( &c->buf[ posn->sector * (LONG)HD_SECTOR ] );
		}
	}

	/* ok, decrease cache weights so new tracks have better chance */

	for ( c = (struct cache *) history.lh_Head;
	c->node.ln_Succ != NULL;
	c = (struct cache *) c->node.ln_Succ ) {
		if ( c->node.ln_Pri > 0 )
			c->node.ln_Pri--;
	}

	/* not in cache */

	c = (struct cache *) RemTail ( &history );
	c->posn = *posn;
	status = read_track ( &c->posn , c->buf );
	if ( status != 0 ) {
		AddTail ( &history , c );
		c->posn.cylinder = -1;
		status = read_sector ( &c->posn , &single_buf[0] );
		if ( status != 0 )
			status = read_sector ( &c->posn , &single_buf[0] );
			if ( status != 0 )
				status = read_sector ( &c->posn , &single_buf[0] );
				if ( status != 0 )
					return ( NULL );
		return ( &single_buf[0] );
	}
	calc_priority ( c );
	Enqueue ( &history , c );
	return ( &c->buf[ posn->sector * (LONG)HD_SECTOR ] );
}


void
write_cache ( posn , buf )
register struct posn *posn;
UBYTE *buf;
{
	register struct cache *c;


	/* if in cache, make sure updated in cache */

	for ( c = (struct cache *) history.lh_Head;
	c->node.ln_Succ != NULL;
	c = (struct cache *) c->node.ln_Succ ) {

		if ( posn->cylinder == c->posn.cylinder
		&&   posn->surface == c->posn.surface ) {

			/* update cache */

			copy_sector ( buf , &c->buf[ posn->sector * (LONG)HD_SECTOR ] );
			break;
		}
	}

	write_sector ( posn , buf );
}


static
calc_priority ( c )
struct cache *c;
{
	register UBYTE *p;
	register LONG type , subtype;
	register int sector;
	register int priority;


	/* ok, now lets peek inside the sectors to see if this track */
	/* contains anything juicy. Directory entries get extra points */
	/* because of the way they are scattered all over the place. */
	/* Data blocks get less because they are only likely to be used */
	/* once. */

	priority = 0;

	for ( sector = 0, p = c->buf;
	sector < first.sectors;
	sector++, p += HD_SECTOR ) {

		type = ((LONG*)p) [ 0 ];
		subtype = ((LONG*)p) [ 127 ];

		if ( type == 2  &&  subtype == 2		/* File Header */
		||   type == 2  &&  subtype == -3		/* User Directory */
		||   type == 16 &&  subtype == -3 )		/* file extension */
			priority += 2;

		if ( type == 8 )						/* data block */
			priority += 1;
	}

	c->node.ln_Pri = priority;
}
