blob: d0b8f47bb2f5147664cb412a3160ef76102d2cf5 [file] [log] [blame]
/**
* \defgroup c64fs C64 file system and disk functions.
* @{
*
* The C64 file system functions are divided into two categories:
* those that deal with C64 files and the C64 disk directory, and
* those that allow direct block access to the disk. The former
* functions can be used for accessing regular files, whereas the
* latter functions are used e.g. to download D64 files onto 1541
* disks.
*
* \note The C64 filesystem functions currently only work with the
* 1541/1541-II/1571 and compatible drives, and not with the IDE64
* hard disks or the 1581/FD2000 3.5" drives.
*
*
*/
/**
* \file
* C64 file system operations interface for Contiki.
* \author Adam Dunkels <adam@dunkels.com>
*
*/
/*
* Copyright (c) 2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the Contiki desktop environment
*
* $Id: c64-fs.c,v 1.1 2006/04/17 15:11:57 kthacker Exp $
*
*/
#include "c64-dio.h"
#include "c64-dio-asm.h"
#include "c64-fs.h"
#include <string.h>
#include <stdio.h>
struct directory_entry {
unsigned char type;
unsigned char track, sect;
unsigned char name[16];
unsigned char reltrack, relsect, relreclen;
unsigned char unused1, unused2, unused3, unused4;
unsigned char tmptrack, tmpsect;
unsigned char blockslo, blockshi;
};
unsigned char _c64_fs_dirbuf[256];
unsigned char _c64_fs_dirbuftrack = 0, _c64_fs_dirbufsect = 0;
unsigned char _c64_fs_filebuf[256];
unsigned char _c64_fs_filebuftrack = 0, _c64_fs_filebufsect = 0;
static struct c64_fs_dirent lastdirent;
static struct c64_fs_dir opendir;
static struct c64_fs_dirent opendirent;
/*-----------------------------------------------------------------------------------*/
/**
* Open a file.
*
* The file description must be allocated by the caller and a pointer
* to it is passed to this function.
*
* \param name A pointer to the name of the file to be opened.
* \param f A pointer to the file descriptor struct.
*
* \retval 0 If the file was successfully opened.
* \retval -1 If the file does not exist.
*/
/*-----------------------------------------------------------------------------------*/
int
c64_fs_open(const char *name, register struct c64_fs_file *f)
{
/* First check if we already have the file cached. If so, we don't
need to do an expensive directory lookup. */
if(strncmp(lastdirent.name, name, 16) == 0) {
f->track = lastdirent.track;
f->sect = lastdirent.sect;
f->ptr = 2;
return 0;
}
/* Not in cache, so we walk through directory instead. */
c64_fs_opendir(&opendir);
do {
c64_fs_readdir_dirent(&opendir, &opendirent);
if(strncmp(opendirent.name, name, 16) == 0) {
f->track = opendirent.track;
f->sect = opendirent.sect;
f->ptr = 2;
return 0;
}
} while(c64_fs_readdir_next(&opendir) == 0);
/* The file was not found in the directory. We flush the directory
buffer cache now in order to prevent a nasty problem from
happening: If the first directory block of an empty disk was
cached, *all* subsequent file opens would return "file not
found". */
_c64_fs_dirbuftrack = 0; /* There are no disk blocks on track 0. */
return -1;
}
/*-----------------------------------------------------------------------------------*/
/**
* Read data from an open file.
*
* This function reads data from an open file into a buffer than must
* be allocated by the caller.
*
* \param f A pointer to a file descriptor structure that must have
* been opened with c64_fs_open().
*
* \param buf A pointer to the buffer in which the data should be placed.
*
* \param len The maxiumum amount of bytes to read.
*
* \return The number of bytes that actually was read, or 0 if an end
* of file was encountered.
*/
/*-----------------------------------------------------------------------------------*/
#if !NOASM
#pragma optimize(push, off)
#endif /* !NOASM */
int __fastcall__
c64_fs_read(register struct c64_fs_file *f, char *buf, int len)
{
static int i;
/* Check if current block is already in buffer, and if not read it
from disk. */
#if NOASM
if(f->track != _c64_fs_filebuftrack ||
_c64_fs_filebufsect != f->sect) {
_c64_fs_filebuftrack = f->track;
_c64_fs_filebufsect = f->sect;
c64_dio_read_block(_c64_fs_filebuftrack, _c64_fs_filebufsect,
_c64_fs_filebuf);
}
#else /* NOASM */
asm("ldy #%b", offsetof(struct c64_fs_file, track));
asm("lda (regbank+%b),y", 4);
asm("cmp %v", _c64_fs_filebuftrack);
asm("bne doblock");
asm("ldy #%b", offsetof(struct c64_fs_file, sect));
asm("lda (regbank+%b),y", 4);
asm("cmp %v", _c64_fs_filebufsect);
asm("bne doblock");
asm("jmp noblock");
asm("doblock:");
asm("ldy #%b", offsetof(struct c64_fs_file, track));
asm("lda (regbank+%b),y", 4);
asm("sta %v", _c64_fs_filebuftrack);
asm("sta %v", c64_dio_asm_track);
asm("ldy #%b", offsetof(struct c64_fs_file, sect));
asm("lda (regbank+%b),y", 4);
asm("sta %v", _c64_fs_filebufsect);
asm("sta %v", c64_dio_asm_sector);
asm("lda #<(%v)", _c64_fs_filebuf);
asm("sta %v", c64_dio_asm_ptr);
asm("lda #>(%v)", _c64_fs_filebuf);
asm("sta %v+1", c64_dio_asm_ptr);
asm("jsr %v", c64_dio_asm_read_block);
asm("noblock:");
#endif /* NOASM */
if(_c64_fs_filebuf[0] == 0 &&
f->ptr == _c64_fs_filebuf[1]) {
return 0; /* EOF */
}
for(i = 0; i < len; ++i) {
#if NOASM
*buf = _c64_fs_filebuf[f->ptr];
++f->ptr;
#else /* NOASM */
asm("ldy #%o+1", buf);
asm("jsr ldaxysp");
asm("sta ptr2");
asm("stx ptr2+1");
asm("ldy #%b", offsetof(struct c64_fs_file, ptr));
asm("lda (regbank+%b),y", 4);
asm("tax");
asm("ldy #0");
asm("lda %v,x", _c64_fs_filebuf);
asm("sta (ptr2),y");
asm("inx");
asm("txa");
asm("ldy #%b", offsetof(struct c64_fs_file, ptr));
asm("sta (regbank+%b),y", 4);
#endif /* NOASM */
if(_c64_fs_filebuf[0] == 0) {
if(f->ptr == _c64_fs_filebuf[1]) {
/* End of file reached, we return the amount of bytes read so
far. */
return i + 1;
}
} else if(f->ptr == 0) {
/* Read new block into buffer and set buffer state
accordingly. */
_c64_fs_filebuftrack = f->track = _c64_fs_filebuf[0];
_c64_fs_filebufsect = f->sect = _c64_fs_filebuf[1];
f->ptr = 2;
c64_dio_read_block(_c64_fs_filebuftrack,
_c64_fs_filebufsect, _c64_fs_filebuf);
}
++buf;
}
return i;
}
#if !NOASM
#pragma optimize(pop)
#endif /* !NOASM */
/*-----------------------------------------------------------------------------------*/
/**
* Close an open file.
*
* \param f A pointer to a file descriptor struct that previously has
* been opened with c64_fs_open().
*/
/*-----------------------------------------------------------------------------------*/
void
c64_fs_close(struct c64_fs_file *f)
{
}
/*-----------------------------------------------------------------------------------*/
/**
* \internal
* Read a directory buffer into the _c64_fs_dirbuf buffer.
*
* This function is shared between this and the c64-fs-raw module.
*
* \param track The track of the directory block.
* \param sect The sector of the directory block.
*/
/*-----------------------------------------------------------------------------------*/
void
_c64_fs_readdirbuf(unsigned char track, unsigned char sect)
{
if(_c64_fs_dirbuftrack == track &&
_c64_fs_dirbufsect == sect) {
/* Buffer already contains requested block, return. */
return;
}
c64_dio_read_block(track, sect, _c64_fs_dirbuf);
_c64_fs_dirbuftrack = track;
_c64_fs_dirbufsect = sect;
}
/*-----------------------------------------------------------------------------------*/
/**
* Open the disk directory for reading.
*
* The caller must supply a pointer to a directory descriptor.
*
* \param d A pointer to a directory description that must be
* allocated by the caller.
*/
/*-----------------------------------------------------------------------------------*/
unsigned char
c64_fs_opendir(register struct c64_fs_dir *d)
{
d->track = 18;
d->sect = 1;
d->ptr = 2;
return 0;
}
/*-----------------------------------------------------------------------------------*/
/**
* Read the current directory entry.
*
* This function reads the directory entry to which the directory
* descriptor currently points into a struct c64_fs_dirent supplied by
* the caller.
*
* The function c64_fs_readdir_next() is used to move the directory
* entry pointer forward in the directory.
*
* \param d A pointer to a directory descriptor previously opened with c64_fs_opendir().
*
* \param f A pointer to a directory entry that must have been
* previously allocated by the caller.
*/
/*-----------------------------------------------------------------------------------*/
void
c64_fs_readdir_dirent(register struct c64_fs_dir *d,
register struct c64_fs_dirent *f)
{
struct directory_entry *de;
int i;
register char *nameptr;
_c64_fs_readdirbuf(d->track, d->sect);
de = (struct directory_entry *)&_c64_fs_dirbuf[d->ptr];
nameptr = de->name;
for(i = 0; i < 16; ++i) {
if(*nameptr == 0xa0) {
*nameptr = 0;
break;
}
++nameptr;
}
strncpy(f->name, de->name, 16);
f->track = de->track;
f->sect = de->sect;
f->size = de->blockslo + (de->blockshi >> 8);
memcpy(&lastdirent, f, sizeof(struct c64_fs_dirent));
}
/*-----------------------------------------------------------------------------------*/
/**
* Move the directory pointer forward.
*
* This function moves the directory entry pointer in the directory
* descriptor forward so that it points to the next file.
*
* \param d A pointer to a directory descriptor previously opened with
* c64_fs_opendir().
*
* \retval 1 If there are no more directory entried in the directory.
* \retval 0 There were more directory entries and the pointer has
* been moved to point to the next one.
*/
/*-----------------------------------------------------------------------------------*/
unsigned char
c64_fs_readdir_next(struct c64_fs_dir *d)
{
struct directory_entry *de;
again:
_c64_fs_readdirbuf(d->track, d->sect);
if(d->ptr == 226) {
if(_c64_fs_dirbuf[0] == 0) {
return 1;
}
d->track = _c64_fs_dirbuf[0];
d->sect = _c64_fs_dirbuf[1];
d->ptr = 2;
} else {
d->ptr += 32;
}
de = (struct directory_entry *)&_c64_fs_dirbuf[d->ptr];
if(de->type == 0) {
goto again;
}
return 0;
}
/*-----------------------------------------------------------------------------------*/
/**
* Close a directory descriptor previously opened by c64_fs_opendir().
*
* \param d A poitner to a directory descriptor previously opened with
* c64_fs_opendir().
*/
/*-----------------------------------------------------------------------------------*/
void
c64_fs_closedir(struct c64_fs_dir *d)
{
}
/*-----------------------------------------------------------------------------------*/
/** @} */