/** * \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 * */ /* * 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 2007/05/23 23:11:28 oliverschmidt Exp $ * */ #include "c64-dio.h" #include "c64-dio-asm.h" #include "c64-fs.h" #include #include 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) { } /*-----------------------------------------------------------------------------------*/ /** @} */