This is drive.c in view mode; [Download] [Up]
/* * drive.c - Disk-drive implementation. * * Written by * Teemu Rantanen (tvr@cs.hut.fi) * Jarkko Sonninen (sonninen@lut.fi) * Jouko Valta (jopi@stekt.oulu.fi) * Olaf Seibert (rhialto@mbfys.kun.nl) * André Fachat (a.fachat@physik.tu-chemnitz.de) * Ettore Perazzoli (ettore@comm2000.it) * Martin Pottendorfer (Martin.Pottendorfer@aut.alcatel.at) * Andreas Boose (boose@unixserv.rz.fh-hannover.de) * * Patches by * Dan Miner (dminer@nyx10.cs.du.edu) * Germano Caronni (caronni@tik.ethz.ch) * Daniel Fandrich (dan@fch.wimsey.bc.ca) /DF/ * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * This program 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 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. * */ #include "vice.h" /* #define DEBUG_DRIVE */ /* #define DEBUG_FS */ /* Unix FS driver command/error channel */ #ifdef __hpux #ifndef _POSIX_SOURCE #define _POSIX_SOURCE #endif #endif #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <memory.h> #include <assert.h> #include <errno.h> #include <unistd.h> #include <dirent.h> #include "serial.h" #include "drive.h" #include "file.h" #include "zfile.h" #include "utils.h" #include "charsets.h" #include "fsdevice.h" #ifdef STANDALONE_1541 #include "c1541.h" #endif /* ------------------------------------------------------------------------- */ #define DIR_MAXBUF 8192 char sector_map[43] = { 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* 1 - 10 */ 21, 21, 21, 21, 21, 21, 21, 19, 19, 19, /* 11 - 20 */ 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, /* 21 - 30 */ 17, 17, 17, 17, 17, /* 31 - 35 */ 17, 17, 17, 17, 17, 17, 17 /* Tracks 36 - 42 are non-standard. */ }; int speed_map[42] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static char pet_sector_map[78] = { 0, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, /* 1 - 10 */ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, /* 11 - 20 */ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, /* 21 - 30 */ 29, 29, 29, 29, 29, 29, 29, 29, 29, 27, /* 31 - 40 */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 41 - 50 */ 27, 27, 27, 25, 25, 25, 25, 25, 25, 25, /* 51 - 60 */ 25, 25, 25, 25, 23, 23, 23, 23, 23, 23, /* 61 - 70 */ 23, 23, 23, 23, 23, 23, 23 /* 71 - 77 */ }; int deleted_files;/* Keeps the number of entries deleted with the `S' command */ /* PC64 files need this too */ char *slot_type[] = { "DEL", "SEQ", "PRG", "USR", "REL", "CBM", "DJJ", "FAB" }; static int set_error_data(DRIVE *floppy, int flags); static int do_block_command ( DRIVE *floppy, char command, char *buffer ); static int do_memory_command ( DRIVE *floppy, BYTE *buffer, int length ); static int do_initialize ( DRIVE *floppy ); static int do_format ( DRIVE *floppy, char *name, BYTE *id, BYTE *minus ); #if defined(__GNUC__) && defined(DEBUG_DRIVE) static void floppy_error_ ( bufferinfo_t *p, int code, int track, int sector, char *fromfunc ); #define floppy_error(a,b,c,d) floppy_error_((a),(b),(c),(d),__FUNCTION__) #else static void floppy_error ( bufferinfo_t *p, int code, int track, int sector ); #endif static void floppy_close_all_channels ( DRIVE * ); static int floppy_create_directory ( DRIVE *floppy, char *name, int length, int filetype, int secondary, BYTE *outputptr ); static int do_copy ( DRIVE *floppy, char *dest, int length ); static int do_rename ( DRIVE *floppy, char *dest, int length ); static void set_find_first_slot ( DRIVE *floppy, char *name, int length, int type ); static BYTE *find_next_slot ( DRIVE *floppy ); static void remove_slot ( DRIVE *floppy, BYTE *slot ); static int write_sequential_buffer ( DRIVE *floppy, bufferinfo_t *bi, int length ); static int alloc_first_free_sector ( BYTE *bam, int *track, int *sector ); static int alloc_next_free_sector ( BYTE *bam, int *track, int *sector ); static int floppy_name_match ( BYTE *slot, char *name, int length, int type ); static int floppy_read_bam (DRIVE *floppy); static int floppy_write_bam (DRIVE *floppy); static int mystrncpy ( BYTE *d, BYTE *s, int n ); static int import_GCR_image(BYTE *header, hdrinfo *hdr); /* ------------------------------------------------------------------------- */ /* * Error messages */ errortext_t floppy_error_messages[] = { { 0, "OK"}, { 1, "FILES SCRATCHED"}, { 2, "SELECTED PARTITION"}, /* 1581 */ { 3, "UNIMPLEMENTED"}, {26, "WRITE PROTECT ON"}, {30, "SYNTAX ERROR"}, {31, "SNERR INVALID COMMAND"}, {32, "SNERR LINE TOO LONG"}, {33, "SNERR INVAL FILE NAME"}, {34, "SNERR NO FILE NAME"}, {60, "WRITE FILE OPEN"}, {61, "FILE NOT OPEN"}, {62, "FILE NOT FOUND"}, {63, "FILE EXISTS"}, {64, "FILE TYPE MISMATCH"}, {65, "NO BLOCK"}, {66, "ILLEGAL TRACK OR SECTOR"}, {67, "ILLEGAL SYSTEM T OR S"}, {70, "NO CHANNEL"}, {72, "DISK FULL"}, {73, "TVR 1541 EMULATOR V2.1"}, /* The program version */ {74, "DRIVE NOT READY"}, {77, "SELECTED PARTITION ILLEGAL"}, /* 1581 */ {80, "DIRECTORY NOT EMPTY"}, {81, "PERMISSION DENIED"}, {-1, 0} }; /* ------------------------------------------------------------------------- */ int initialize_1541(int dev, int type, drive_attach_func_t attach_func, drive_detach_func_t detach_func, DRIVE *oldinfo) { DRIVE *floppy; int i; floppy = oldinfo; /* Create instances of the disk drive. */ if (!floppy) { floppy = (DRIVE *)malloc(sizeof(DRIVE)); assert(floppy); } memset (floppy, 0, sizeof(DRIVE)); /* init all pointers */ floppy->type = type; floppy->ActiveFd = -1; floppy->unit = dev; for (i = 0; i < 15; i++) floppy->buffers[i].mode = BUFFER_NOT_IN_USE; floppy->buffers[15].mode = BUFFER_COMMAND_CHANNEL; floppy->buffers[15].buffer = (BYTE *) malloc(256); /* Initialise format constants. */ set_disk_geometry(floppy, type); /* * 'type' specifies what kind of emulation is selected */ if (!(type & DT_FS)) { if (serial_attach_device(dev, (char *)floppy, "1541 Disk Drive", read_1541, write_1541, open_1541, close_1541, flush_1541)) { printf("could not initialize 1541 ????\n"); return(-1); } } else { if (attach_fsdevice(dev, (char *)floppy, "1541 FS Drive")) { printf("could not initialize FS 1541 ????\n"); return(-1); } } floppy->attach_func = attach_func; floppy->detach_func = detach_func; floppy_error(&floppy->buffers[15], IPE_DOS_VERSION, 0, 0); return 0; } /* * This routine is used by the standalone version and ML monitor * in order to get around standard serial bus connection. * Device number is searched in filename if it's not NULL. * 'dev' contains the default in range 0...3 or 8...11. */ int find_devno(int dev, const char *name) { int num, len; const char *p; if (name && *name) { p = name; while (isspace((int)*p)) ++p; if (sscanf(p, "%d%n", &num, &len) == 1) { p += len; while (isspace((int)*p)) ++p; if (*p == ':') return (num); } } /* name */ return (dev); /* Default drive */ } /* ------------------------------------------------------------------------- */ /* * Serial Bus Interface */ /* * Open a file on the disk image, and store the information on the * directory slot. */ int open_1541(void *flp, char *name, int length, int secondary) { DRIVE *floppy = (DRIVE *)flp; bufferinfo_t *p = &(floppy->buffers[secondary]); char realname[256]; int reallength; int readmode; int filetype, rl; int track, sector; BYTE *slot; /* Current directory entry */ if ( (!name || !*name) && p->mode != BUFFER_COMMAND_CHANNEL ) /* EP */ return SERIAL_NO_DEVICE; /* Routine was called incorrectly. */ /* * No floppy in drive ? * * On systems with limited memory it may save resources not to keep * the image file open all the time. * If ActiveName exists, the file may have been temporarily closed. */ if (floppy->ActiveFd < 0 && p->mode != BUFFER_COMMAND_CHANNEL && secondary != 15 && *name != '#') { floppy_error(&floppy->buffers[15], IPE_NOT_READY, 18, 0); printf("Drive not ready.\n"); return SERIAL_ERROR; } #ifdef DEBUG_DRIVE printf("OPEN 1541: FD = %d - Name '%s' (%d) on ch %d\n", floppy->ActiveFd, name, length, secondary); #endif /* * If channel is command channel, name will be used as write. Return only * status of last write ... */ if (p->mode == BUFFER_COMMAND_CHANNEL) { int n; int status = SERIAL_OK; for (n = 0; n < length; n++) status = write_1541(floppy, name[n], secondary); if (length) p->readmode = FAM_WRITE; else p->readmode = FAM_READ; return status; } /* * Clear error flag */ floppy_error(&floppy->buffers[15], IPE_OK, 0, 0); /* * In use ? */ if (p->mode != BUFFER_NOT_IN_USE) { #ifdef DEBUG_DRIVE printf ("Cannot open channel %d. Mode is %d.\n", secondary, p->mode); #endif floppy_error(&floppy->buffers[15], IPE_NO_CHANNEL, 0, 0); return SERIAL_ERROR; } /* * Filemode / type */ if (secondary == 1) readmode = FAM_WRITE; else readmode = FAM_READ; filetype = 0; rl = 0; /* REL */ if (floppy_parse_name(name, length, realname, &reallength, &readmode, &filetype, &rl) != SERIAL_OK) return SERIAL_ERROR; /* Limit file name to 16 chars. */ reallength = (reallength > 16) ? 16 : reallength; /* * Internal buffer ? */ if (*name == '#') { p->mode = BUFFER_MEMORY_BUFFER; p->buffer = (BYTE *) malloc(256); p->bufptr = 0; return SERIAL_OK; } /* * Directory read * A little-known feature of the 1541: open 1,8,2,"$" (or even 1,8,1). * It gives you the BAM+DIR as a sequential file, containing the data * just as it appears on disk. -Olaf Seibert */ if (*name == '$') { if (secondary > 0) { track = floppy->Dir_Track; sector = 0; goto as_if_sequential; } p->mode = BUFFER_DIRECTORY_READ; /* XXX assumes this is enough -Rhialto */ p->buffer = (BYTE *) xmalloc(DIR_MAXBUF); p->length = floppy_create_directory(floppy, realname, reallength, filetype, secondary, p->buffer); if (p->length < 0) { /* Directory not valid. */ p->mode = BUFFER_NOT_IN_USE; free(p->buffer); p->length = 0; floppy_error(&floppy->buffers[15], IPE_NOT_FOUND, 0, 0); return SERIAL_ERROR; } p->bufptr = 0; return SERIAL_OK; } /* * Now, set filetype according secondary address, if it was not specified * on filename */ if (!filetype) filetype = (secondary < 2) ? FT_PRG : FT_SEQ; /* * Check that there is room on directory. */ set_find_first_slot(floppy, realname, reallength, 0); /* * Find the first non-DEL entry in the directory (if it exists). */ do slot = find_next_slot(floppy); while (slot && ((slot[SLOT_TYPE_OFFSET] & 0x07) == FT_DEL)); p->readmode = readmode; p->slot = slot; /* * Open file for reading */ if (readmode == FAM_READ) { int type; int status; if (!slot) { close_1541(floppy, secondary); floppy_error(&floppy->buffers[15], IPE_NOT_FOUND, 0, 0); return SERIAL_ERROR; } type = slot[SLOT_TYPE_OFFSET] & 0x07; /* I don't think that this one is needed - EP if (filetype && type != filetype) { floppy_error(&floppy->buffers[15], IPE_BAD_TYPE, 0, 0); return SERIAL_ERROR; } */ filetype = type; /* EP */ track = (int) slot[SLOT_FIRST_TRACK]; sector = (int) slot[SLOT_FIRST_SECTOR]; /* * Del, Seq, Prg, Usr (Rel not yet supported) */ if (type != FT_REL) { as_if_sequential: p->mode = BUFFER_SEQUENTIAL; p->bufptr = 2; p->buffer = (BYTE *) malloc(256); status = floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, p->buffer, track, sector, floppy->D64_Header); if (status < 0) { close_1541(floppy, secondary); return SERIAL_ERROR; } return SERIAL_OK; } /* * Unsupported filetype */ return SERIAL_ERROR; } /* * Write */ if (floppy->ReadOnly) { floppy_error(&floppy->buffers[15], IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } if (slot) { if (*name == '@') remove_slot(floppy, slot); else { close_1541(floppy, secondary); floppy_error(&floppy->buffers[15], IPE_FILE_EXISTS, 0, 0); return SERIAL_ERROR; } } /* * Create slot information. * Different front-ends can fetch the real filename from slot info. */ p->slot = (BYTE *) malloc(32); memset(p->slot, 0, 32); memset(p->slot + SLOT_NAME_OFFSET, 0xa0, 16); memcpy(p->slot + SLOT_NAME_OFFSET, realname, reallength); #ifdef DEBUG_DRIVE printf("DIR: name (%d) '%s'\n", reallength, realname); #endif p->slot[SLOT_TYPE_OFFSET] = filetype; /* unclosed */ p->buffer = (BYTE *) malloc(256); p->mode = BUFFER_SEQUENTIAL; p->bufptr = 2; #if 0 /* XXX keeping entry until close not implemented */ /* Write the directory entry to disk as an UNCLOSED file. */ #ifdef DEBUG_DRIVE printf("DEBUG: find empty DIR slot.\n"); #endif set_find_first_slot(floppy, NULL, -1, 0); e = find_next_slot(floppy); if (!e) { p->mode = BUFFER_NOT_IN_USE; free((char *)p->buffer); p->buffer = NULL; floppy_error(&floppy->buffers[15], IPE_DISK_FULL, 0, 0); return SERIAL_ERROR; } memcpy(&floppy->Dir_buffer[floppy->SlotNumber * 32 + 2], p->slot + 2, 30); #ifdef DEBUG_DRIVE printf("DEBUG: create, write DIR slot (%d %d).\n", floppy->Curr_track, floppy->Curr_sector); #endif floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); /*floppy_write_bam(floppy);*/ #endif /* BAM write */ return SERIAL_OK; } int close_1541(void *flp, int secondary) { DRIVE *floppy = (DRIVE *)flp; BYTE *e; bufferinfo_t *p = &(floppy->buffers[secondary]); #ifdef DEBUG_DRIVE printf("DEBUG: closing file #%d.\n", secondary); #endif switch (p->mode) { case BUFFER_NOT_IN_USE: return SERIAL_OK; /* FIXME: Is this correct? */ case BUFFER_MEMORY_BUFFER: case BUFFER_DIRECTORY_READ: free((char *)p->buffer); p->mode = BUFFER_NOT_IN_USE; p->buffer = NULL; p->slot = NULL; break; case BUFFER_SEQUENTIAL: if (p->readmode & (FAM_WRITE | FAM_APPEND)) { /* * Flush bytes and write slot to directory */ if (floppy->ReadOnly) { floppy_error(&floppy->buffers[15], IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } #ifdef DEBUG_DRIVE printf("DEBUG: flush.\n"); #endif write_sequential_buffer(floppy, p, p->bufptr); #ifdef DEBUG_DRIVE printf("DEBUG: find empty DIR slot.\n"); #endif set_find_first_slot(floppy, NULL, -1, 0); e = find_next_slot(floppy); if (!e) { p->mode = BUFFER_NOT_IN_USE; free((char *)p->buffer); p->buffer = NULL; floppy_error(&floppy->buffers[15], IPE_DISK_FULL, 0, 0); return SERIAL_ERROR; } p->slot[SLOT_TYPE_OFFSET] |= 0x80; /* Closed */ memcpy(&floppy->Dir_buffer[floppy->SlotNumber * 32 + 2], p->slot + 2, 30); #ifdef DEBUG_DRIVE printf("DEBUG: closing, write DIR slot (%d %d) and BAM.\n", floppy->Curr_track, floppy->Curr_sector); #endif floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); floppy_write_bam(floppy); } p->mode = BUFFER_NOT_IN_USE; free((char *)p->buffer); p->buffer = NULL; break; case BUFFER_COMMAND_CHANNEL: /* I'm not sure if this is correct, but really closing the buffer should reset the read pointer to the beginning for the next write! */ floppy_error(&floppy->buffers[15], IPE_OK, 0, 0); floppy_close_all_channels(floppy); break; default: fprintf (stderr, "\nFatal: unknown floppy-close-mode\n"); exit(-1); } return SERIAL_OK; } int read_1541(void *flp, BYTE *data, int secondary) { DRIVE *floppy = (DRIVE *)flp; bufferinfo_t *p = &(floppy->buffers[secondary]); #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) printf("Disk read %d [%02d %02d]\n", p->mode, 0, 0); #endif switch (p->mode) { case BUFFER_NOT_IN_USE: floppy_error(&floppy->buffers[15], IPE_NOT_OPEN, 0, 0); return SERIAL_ERROR; case BUFFER_DIRECTORY_READ: /*printf("read %d/%d [%02X]\n", p->bufptr, p->length, p->buffer[p->bufptr]);*/ if (p->bufptr >= p->length) { *data = 0xc7; return SERIAL_EOF; } *data = p->buffer[p->bufptr]; p->bufptr++; break; case BUFFER_MEMORY_BUFFER: if (p->bufptr >= 256) { *data = 0xc7; return SERIAL_EOF; } *data = p->buffer[p->bufptr]; p->bufptr++; break; case BUFFER_SEQUENTIAL: if (p->readmode != FAM_READ) return SERIAL_ERROR; /* * Read next block if needed */ if (p->buffer[0]) { if (p->bufptr >= 256) { floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, p->buffer, (int) p->buffer[0], (int) p->buffer[1], floppy->D64_Header); p->bufptr = 2; } } else { if (p->bufptr > p->buffer[1]) { *data = 0xc7; return SERIAL_EOF; } } *data = p->buffer[p->bufptr]; p->bufptr++; break; case BUFFER_COMMAND_CHANNEL: if (p->bufptr > p->length) { floppy_error(&floppy->buffers[15], IPE_OK, 0, 0); #ifdef DEBUG_DRIVE printf( "end of buffer in command channel\n" ); #endif *data = 0xc7; return SERIAL_EOF; } *data = p->buffer[p->bufptr]; p->bufptr++; break; default: fprintf (stderr, "\nFatal: unknown buffermode on floppy-read\n"); exit(-1); } return SERIAL_OK; } int write_1541(void *flp, BYTE data, int secondary) { DRIVE *floppy = (DRIVE *)flp; bufferinfo_t *p = &(floppy->buffers[secondary]); if (floppy->ReadOnly && p->mode != BUFFER_COMMAND_CHANNEL) { floppy_error(&floppy->buffers[15], IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } #ifdef DEBUG_DRIVE if (p -> mode == BUFFER_COMMAND_CHANNEL) printf("Disk write %d [%02d %02d] data %02x (%c)\n", p->mode, 0, 0, data, (isprint(data) ? data : '.') ); #endif switch (p->mode) { case BUFFER_NOT_IN_USE: floppy_error(&floppy->buffers[15], IPE_NOT_OPEN, 0, 0); return SERIAL_ERROR; case BUFFER_DIRECTORY_READ: floppy_error(&floppy->buffers[15], IPE_NOT_WRITE, 0, 0); return SERIAL_ERROR; case BUFFER_MEMORY_BUFFER: if (p->bufptr >= 256) return SERIAL_ERROR; p->buffer[p->bufptr] = data; p->bufptr++; return SERIAL_OK; case BUFFER_SEQUENTIAL: if (p->readmode == FAM_READ) return SERIAL_ERROR; if (p->bufptr >= 256) { p->bufptr = 2; if (write_sequential_buffer(floppy, p, WRITE_BLOCK) < 0) return SERIAL_ERROR; } p->buffer[p->bufptr] = data; p->bufptr++; break; case BUFFER_COMMAND_CHANNEL: if (p->readmode == FAM_READ) { p->bufptr = 0; p->readmode = FAM_WRITE; } if (p->bufptr >= 256) /* Limits checked later */ return SERIAL_ERROR; p->buffer[p->bufptr] = data; p->bufptr++; break; default: fprintf (stderr, "\nFatal: Unknown write mode\n"); exit(-1); } return SERIAL_OK; } void flush_1541(void *flp, int secondary) { DRIVE *floppy = (DRIVE *)flp; bufferinfo_t *p = &(floppy->buffers[secondary]); int status; #ifdef DEBUG_DRIVE printf("flush_1541, secondary = %d, buffer=%s\n bufptr=%d, length=%d, " "read?=%d\n", secondary, p->buffer, p->bufptr, p->length, p->readmode==FAM_READ); #endif if (p->mode != BUFFER_COMMAND_CHANNEL) return; if (p->readmode == FAM_READ) return; if (p->length) { /* if no command, do nothing - esp. keep error code. */ status = ip_execute(floppy, p->buffer, p->bufptr); p->bufptr = 0; if (status == IPE_OK) floppy_error(&floppy->buffers[15], IPE_OK, 0, 0); } } /* ------------------------------------------------------------------------- */ /* * Should set values to somewhere so that they could be read from * command channel */ #if defined(__GNUC__) && defined(DEBUG_DRIVE) static void floppy_error_(bufferinfo_t *p, int code, int track, int sector, char *fromfunc) #else static void floppy_error(bufferinfo_t *p, int code, int track, int sector) #endif { char *message; errortext_t *e; static int last_code; #if defined(__GNUC__) && defined(DEBUG_DRIVE) printf("floppy_error: code =%d, last_code =%d, trck =%d, sctr =%d, from=%s\n", code, last_code, track, sector, fromfunc); #else #ifdef DEBUG_DRIVE printf("floppy_error: code =%d, last_code =%d, track =%d, sector =%d\n", code, last_code, track, sector); #endif #endif /* Only set an error once per command */ if (code != IPE_OK && last_code != IPE_OK) return; last_code = code; e = &(floppy_error_messages[0]); while (e->nr >= 0 && e->nr != code) e++; if (e->nr >= 0) message = e->text; else message = "UNKNOWN ERROR NUMBER"; sprintf((char *)p->buffer, "%02d,%s,%02d,%02d\015", code == IPE_DELETED ? deleted_files : code, message, track, sector); /* length points to the last byte, and doesn't give the length... */ p->length = strlen((char *)p->buffer)-1; p->bufptr = 0; if (code && code != IPE_DOS_VERSION) fprintf(stderr, "1541: ERR = %02d, %s, %02d, %02d\n", code == IPE_DELETED ? deleted_files : code, message, track, sector); p->readmode = FAM_READ; } /* ------------------------------------------------------------------------- */ /* * Input Processor Simulator. IP interprets incoming commands and generates * the error messages according to return values. */ int ip_execute(DRIVE *floppy, BYTE *buf, int length) { int status = IPE_OK; BYTE *p, *p2; char *name; BYTE *minus, *id; if (!length) return IPE_OK; if (length > IP_MAX_COMMAND_LEN) { floppy_error(&floppy->buffers[15], IPE_LONG_LINE, 0, 0); return IPE_LONG_LINE; } p = (BYTE *) malloc(length + 1); memcpy(p, buf, length); if (p[length-1] == 0x0d) --length; /* chop CR character */ p[length] = 0; name = (char *)memchr(p, ':', length); id = (BYTE *)memchr(p, ',', length); minus= (BYTE *)memchr(p, '-', length); if (name) /* Fix name length */ for (p2 = p; *p2 && *p2 != ':' && length > 0; p2++, length--); switch (*p) { case 'C': /* Copy */ status = do_copy(floppy, (char *)name, length); break; case 'D': /* Backup unused */ status = IPE_INVAL; break; case 'R': /* Rename */ status = do_rename(floppy, (char *)name, length); break; case 'S': /* Scratch */ { BYTE *slot; char *realname = name; int reallength = 0, filetype = 0, readmode = 0; /* XXX * Wrong name parser - s0:file1,file2 means scratch * those 2 files. */ if (floppy_parse_name(name, length, realname, &reallength, &readmode, &filetype, NULL) != SERIAL_OK) { status = IPE_NO_NAME; } else if (floppy->ReadOnly) { status = IPE_WRITE_PROTECT_ON; } else { #ifdef DEBUG_DRIVE printf("remove name= '%s' len=%d (%d) type= %d.\n", realname, reallength, length, filetype); #endif deleted_files = 0; /* since remove_slot() uses set_find_first_slot() too, we * cannot find the matching files by simply repeating * find_next_slot() calls alone; we have to re-call * set_find_first_slot() each time... EP 1996/04/07 */ set_find_first_slot(floppy, realname, reallength, 0); while ((slot = find_next_slot(floppy))) { remove_slot(floppy, slot); deleted_files++; set_find_first_slot(floppy, realname, reallength, 0); } if (deleted_files) status = IPE_DELETED; else status = IPE_NOT_FOUND; floppy_error(&floppy->buffers[15], status, 1, 0); } /* else */ } break; case 'I': status = do_initialize(floppy); break; case 'N': status = do_format(floppy, name, id, minus); break; case 'V': status = do_validate(floppy); break; case 'B': /* Block, Buffer */ if (!name) /* B-x does not require a : */ name = (char *)(p + 2); if (!minus) status = IPE_INVAL; else status = do_block_command(floppy, minus[1], name + 1); break; case 'M': /* Memory */ if (!minus) /* M-x does not allow a : */ status = IPE_INVAL; else status = do_memory_command(floppy, minus +1, length); break; case 'P': /* Position */ /* 4 byte parameters: channel, rec_lo, rec_hi, pos */ break; case 'U': /* User */ if (!name) name = (char *)(p + 1); if (p[1] == '0') { status = IPE_OK; } else { switch ((p[1] - 1) & 0x0f) { case 0: /* UA */ /* XXX incorrect: U1 is not exactly the same as B-R */ /* -- should store the buffer pointer */ if (name) status = do_block_command(floppy, 'R', name + 1); break; case 1: /* UB */ /* XXX incorrect: U2 is not exactly the same as B-W */ /* -- should store the buffer pointer */ if (name) status = do_block_command(floppy, 'W', name + 1); break; case 2: /* Jumps */ case 3: case 4: case 5: case 6: case 7: status = IPE_NOT_READY; break; case 8: /* UI */ if (p[2] == '-' || p[2] == '+') { status = IPE_OK; /* Set IEC bus speed */ } else { floppy_close_all_channels(floppy); /* Warm reset */ status = IPE_DOS_VERSION; } break; case 9: /* UJ */ floppy_close_all_channels(floppy); /* Cold reset */ status = IPE_DOS_VERSION; break; case 10: /* UK..UP */ case 11: case 12: case 13: case 14: case 15: status = IPE_NOT_READY; break; } } /* Un */ break; default: status = IPE_INVAL; break; } /* commands */ if (status == IPE_INVAL) fprintf(stderr, "1541: wrong command `%s'\n", p); floppy_error(&floppy->buffers[15], status, 0, 0); free((char *)p); return status; } /* * This function is modeled after BLKPAR, in the 1541 ROM at CC6F. */ static int get_block_parameters(char *buf, int *p1, int *p2, int *p3, int *p4) { int ip; char *bp; int *p[4]; /* This is a kludge */ p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; bp = buf; for (ip = 0; ip < 4; ip++) { while (*bp == ' ' || *bp == ')' || *bp == ',') bp++; if (*bp == 0) break; /* convert and skip over decimal number */ *p[ip] = strtol(bp, &bp, 10); } if ((*bp != 0) && (ip == 4)) return IPE_SYNTAX; return -ip; /* negative of # arguments found */ } static int do_block_command(DRIVE *floppy, char command, char *buffer) { int channel = 0; int drive = 0; int track = 0; int sector = 0; int position = 0; int l; switch (command) { case 'R': case 'W': l = get_block_parameters(buffer, &channel, &drive, &track, §or); if (l < 0) { #ifdef DEBUG_DRIVE printf("B-R/W parsed ok. (l=%d) channel %d mode %d, drive=%d, track=%d sector=%d\n", l, channel, floppy->buffers[channel].mode, drive, track, sector); #endif if (floppy->buffers[channel].mode != BUFFER_MEMORY_BUFFER) { return IPE_NO_CHANNEL; } if (command == 'W') { if (floppy->ReadOnly) return IPE_WRITE_PROTECT_ON; if (floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->buffers[channel].buffer, track, sector, floppy->D64_Header)) return IPE_NOT_READY; } else { if (floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, floppy->buffers[channel].buffer, track, sector, floppy->D64_Header) < 0) return IPE_NOT_READY; } floppy->buffers[channel].bufptr = 0; } break; case 'A': case 'F': l = get_block_parameters(buffer, &drive, &track, §or, &channel); if (l > 0) /* just 3 args used */ return l; if (command == 'A') { if (!allocate_sector(floppy->bam, track, sector)) { /* * Desired sector not free. Suggest another. XXX The 1541 * uses an inferior search function that only looks on * higher tracks and can return sectors in the directory * track. */ if (alloc_next_free_sector(floppy->bam, &track, §or) >= 0) { /* Deallocate it and merely suggest it */ free_sector(floppy->bam, track, sector); } else { /* Found none */ track = 0; sector = 0; } floppy_error(&floppy->buffers[15], IPE_NO_BLOCK, track, sector); return IPE_NO_BLOCK; } } else free_sector(floppy->bam, track, sector); break; case 'P': l = get_block_parameters(buffer, &channel, &position, &track, §or); if (l > 0) /* just 2 args used */ return l; if (floppy->buffers[channel].mode != BUFFER_MEMORY_BUFFER) { return IPE_NO_CHANNEL; } floppy->buffers[channel].bufptr = position; break; default: return IPE_INVAL; } return IPE_OK; } static int do_memory_command(DRIVE *floppy, BYTE *buffer, int length) { #if 0 int addr = 0; if (length < 3) /* All commands at least 3 bytes */ return IPE_SYNTAX; addr = (buffer[1] & 0xff) | ((buffer[2] & 0xff) << 8); switch (*buffer) { case 'W': if (length < 5) return IPE_SYNTAX; count = buffer[3]; /* data= buffer[4 ... 4+34]; */ if (floppy->buffers[addrlo].mode != BUFFER_MEMORY_BUFFER) { return IPE_SYNTAX; memcpy ( ... , buffer + 4, buffer[3]); } break; case 'R': floppy->buffers[channel].bufptr = addr; break; case 'E': floppy->buffers[channel].bufptr = addr; break; default: return IPE_SYNTAX; } return IPE_OK; #else return IPE_UNIMPL; #endif } /* ------------------------------------------------------------------------- */ /* * Parse name realname, type and read/write mode. '@' on write must * be checked elsewhere */ int floppy_parse_name(char *name, int length, char *ptr, int *reallength, int *readmode, int *filetype, int *rl) { char *c, *p; int t; if (!name || !*name) return FLOPPY_ERROR; p = (char *)memchr(name, ':', length); if (p) p++; else { /* no colon found */ if (*name != '$') p = name; else p = name + strlen(name); /* set to null byte */ } #ifdef DEBUG_DRIVE printf("Name (%d): '%s'\n", length, p); #endif #if 0 if (*name == '@' && p == name) p++; #endif t = strlen(p); *reallength = 0; while (*p != ',' && t-- > 0) { (*reallength)++; *(ptr++) = *(p++); /* realname pointer */ #ifdef DEBUG_DRIVE printf("parsing... [%d] %02x t=%d\n", *reallength, *(ptr-1), t); #endif } /* while */ *filetype = 0; /* EP */ /* * Change modes ? */ while (t > 0) { t--; p++; if (t == 0) { #ifdef DEBUG_DRIVE printf("done. [%d] %02x t=%d\n", *reallength, *p, t); printf("No type.\n"); #endif return FLOPPY_ERROR; } switch (*p) { case 'S': *filetype = FT_SEQ; break; case 'P': *filetype = FT_PRG; break; case 'U': *filetype = FT_USR; break; case 'L': /* L,(#record length) max 254 */ if (rl && p[1] == ',') { *rl = p[2]; /* Changing RL causes error */ if (*rl > 254) return FLOPPY_ERROR; } *filetype = FT_REL; break; /* 1581 types */ case 'C': *filetype = FT_CBM; /* 1581 partition */ break; case 'J': *filetype = FT_DJJ; break; case 'F': *filetype = FT_FAB; /* Fred's format */ break; /* Access Control Methods */ case 'R': *readmode = FAM_READ; break; case 'W': *readmode = FAM_WRITE; break; case 'A': if (*filetype != FT_SEQ) return FLOPPY_ERROR; *readmode = FAM_APPEND; /* Append on a SEQ file */ break; default: #ifdef DEBUG_DRIVE printf("No way. p='%s'\n", p); #endif return FLOPPY_ERROR; } c = (char *)memchr(p, ',', t); if (c) { t -= (c - p); p = c; } else t = 0; } /* while (t) */ #ifdef DEBUG_DRIVE printf ("Type = %s %s\n", slot_type[*filetype], (*readmode == FAM_READ ? "read" : "write")); #endif return FLOPPY_COMMAND_OK; } /* * Find next slot with the same name for match. * If type is 0, check the file name only. * If type is nonzero, check that it matches the id of the file found. * Note: If the type is not given on command line, the drive adds it * itself, according to the channel number (secondary address) used. * * Just like MSDOS, the CBM drive DOS also uses '?' for any character * and '*' to skip all the remainder. */ static int floppy_name_match (BYTE *slot, char *name, int length, int type) { int i; if (length < 0) { if (slot[SLOT_TYPE_OFFSET]) return 0; else return 1; } if (!slot[SLOT_TYPE_OFFSET]) return 0; if (length > 16) length = 16; for (i = 0; i < length; i++) { switch (name[i]) { case '?': /* Match any character */ break; case '*': /* Make a match */ i = 16; break; default: if ((BYTE)name[i] != slot[i + SLOT_NAME_OFFSET]) return 0; } } /* * Got name match ? */ if (i < 16 && slot[SLOT_NAME_OFFSET + i] != 0xa0) return 0; if (type && type != (slot[SLOT_TYPE_OFFSET] & 0x07)) return 0; return 1; } /* ------------------------------------------------------------------------- */ /* * Return the number of free blocks on disk. */ static int floppy_free_block_count (DRIVE *floppy) { int blocks, i; for (blocks = 0, i = 1; i <= floppy->NumTracks; i++) { if (i != floppy->Dir_Track) blocks += (i <= NUM_TRACKS_1541) ? floppy->bam[BAM_BIT_MAP + 4 * (i - 1)] : floppy->bam[BAM_EXT_BIT_MAP + 4 * (i - NUM_TRACKS_1541 - 1)]; } return blocks; } /* * Create directory listing. (called from open_1541) * If filetype is 0, match for all files. * Return the length in bytes if successful, -1 if the directory is not * valid. */ static int floppy_create_directory (DRIVE *floppy, char *name, int length, int filetype, int secondary, BYTE *outputptr) { BYTE *l, *p; BYTE *origptr = outputptr; int blocks; int addr, i; if (length) { if (*name == '$') { ++name; --length; } if (*name == ':') { ++name; --length; } } if (!*name || length < 1) { name = "*\0"; length = 1; } /* * Start Address, Line Link and Line number 0 */ l = outputptr; addr = 0x401; SET_LO_HI(l, addr); l += 2; /* Leave space for Next Line Link */ *l++ = 0; *l++ = 0; *l++ = (BYTE) 0x12; /* Reverse on */ *l++ = '"'; memcpy(l, &floppy->bam[BAM_DISK_NAME], 16); no_a0_pads(l, 16); l += 16; *l++ = '"'; *l++ = ' '; memcpy(l, &floppy->bam[BAM_DISK_ID], 5); no_a0_pads(l, 5); l += 5; *l++ = 0; /* * Pointer to the next line */ addr += ((l - outputptr) - 2); outputptr[2] = 1; /* addr & 0xff; */ outputptr[3] = 1; /* (addr >>8) & 0xff; */ outputptr = l; /* * Now, list files that match the pattern. * Wildcards can be used too. */ set_find_first_slot(floppy, name, length, filetype); while ((p = find_next_slot(floppy))) { BYTE *tl; /* Check whether the directory exceeds the malloced memory. We make sure there is enough space for two lines because we also have to add the final ``...BLOCKS FREE'' line. */ if ((l - origptr) >= DIR_MAXBUF - 64) { fprintf(stderr, "Directory too long: giving up.\n"); return -1; } if (p[SLOT_TYPE_OFFSET]) { tl = l; l += 2; /* * Length and spaces */ blocks = p[SLOT_NR_BLOCKS] + p[SLOT_NR_BLOCKS + 1] * 256; SET_LO_HI(l, blocks); if (blocks < 10) *l++ = ' '; if (blocks < 100) *l++ = ' '; /* * Filename */ *l++ = ' '; *l++ = '"'; memcpy(l, &p[SLOT_NAME_OFFSET], 16); for (i = 0; (i < 16) && (p[SLOT_NAME_OFFSET + i] != 0xa0);) i++; no_a0_pads(l, 16); l[16] = ' '; l[i] = '"'; l += 17; /* * Type + End * There are 3 spaces or < and 2 spaces after the filetype. * Well, not exactly - the whole directory entry is 32 byte long * (including nullbyte). * Depending on the file size, there are more or less spaces */ sprintf ((char *)l, "%c%s%c%c", (p[SLOT_TYPE_OFFSET] & FT_CLOSED ? ' ' : '*'), slot_type[p[SLOT_TYPE_OFFSET] & 0x07], (p[SLOT_TYPE_OFFSET] & FT_LOCKED ? '<' : ' '), 0); l += 5; i = l - tl; while (i < 31) { *l++ = ' '; i++; } *l++ = '\0'; /* * New address */ addr += l - outputptr; outputptr[0] = 1; /* addr & 0xff; */ outputptr[1] = 1; /* (addr >> 8) & 0xff; */ outputptr = l; } } blocks = floppy_free_block_count(floppy); *l++ = 0; *l++ = 0; SET_LO_HI(l, blocks); memcpy(l, "BLOCKS FREE.", 12); l += 12; memset(l, ' ', 13); l += 13; *l++ = (char) 0; /* Line Address */ addr += l - outputptr; outputptr[0] = 1; /* addr & 0xff; */ outputptr[1] = 1; /* (addr / 256) & 0xff; */ /* * end */ *l++ = (char) 0; *l++ = (char) 0; *l = (char) 0; return (l - origptr); } /* ------------------------------------------------------------------------- */ static int do_copy( DRIVE *floppy, char *dest, int length) { char *name, *files, *p, c; /* Split command line */ if (!dest || !(files = memchr(dest, '=', length)) ) return (IPE_SYNTAX); *files++ = 0; if (strchr (dest, ':')) dest = strchr (dest, ':') +1; #ifdef DEBUG_DRIVE printf("COPY: dest= '%s'\n orig= '%s'\n", dest, files); #endif if (open_1541(floppy, dest, strlen(dest), 1)) return (IPE_FILE_EXISTS); p = name = files; while (*name) { /* loop for given files */ for (; *p && *p != ','; p++); *p++ = 0; if (strchr (name, ':')) name = strchr (name, ':') +1; #ifdef DEBUG_DRIVE printf("searching for file '%s'\n", name); #endif if (open_1541(floppy, name, strlen(name), 0)) { close_1541(floppy, 1); return (IPE_NOT_FOUND); } while (!read_1541(floppy, (BYTE *)&c, 0)) { if (write_1541(floppy, c, 1)) { close_1541(floppy, 0); /* no space on disk */ close_1541(floppy, 1); return (IPE_DISK_FULL); } } close_1541(floppy, 0); name = p; /* next file */ } /* while */ close_1541(floppy, 1); return(IPE_OK); } /* * Rename disk entry */ static int do_rename( DRIVE *floppy, char *dest, int length) { char *src; char dest_name[256], src_name[256]; int dest_reallength, dest_readmode, dest_filetype, dest_rl; int src_reallength, src_readmode, src_filetype, src_rl; BYTE *slot; if (!dest || !(src = memchr(dest, '=', length)) ) return (IPE_SYNTAX); *src++ = 0; if (strchr (dest, ':')) dest = strchr (dest, ':') +1; #ifdef DEBUG_DRIVE printf("RENAME: dest= '%s'\n orig= '%s'\n", dest, src); #endif if (!floppy_parse_name(dest, strlen(dest), dest_name, &dest_reallength, &dest_readmode, &dest_filetype, &dest_rl) == FLOPPY_ERROR) return IPE_SYNTAX; if (!floppy_parse_name(src, strlen(src), src_name, &src_reallength, &src_readmode, &src_filetype, &src_rl) == FLOPPY_ERROR) return IPE_SYNTAX; if (floppy->ReadOnly) return IPE_WRITE_PROTECT_ON; /* check if the destination name is already in use */ set_find_first_slot(floppy, dest_name, dest_reallength, dest_filetype); slot = find_next_slot(floppy); if (slot) return (IPE_FILE_EXISTS); /* find the file to rename */ set_find_first_slot(floppy, src_name, src_reallength, src_filetype); slot = find_next_slot(floppy); if (!slot) return (IPE_NOT_FOUND); /* now we can replace the old file name... */ /* we write directly to the Dir_buffer */ slot = &floppy->Dir_buffer[floppy->SlotNumber * 32]; memset(slot + SLOT_NAME_OFFSET, 0xa0, 16); memcpy(slot + SLOT_NAME_OFFSET, dest_name, dest_reallength); if (dest_filetype) slot[SLOT_TYPE_OFFSET] = dest_filetype; /* XXX: is this right? */ /* update the directory */ floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); return(IPE_OK); } /* ------------------------------------------------------------------------- */ /* * Close all channels. This happens on 'I' -command and on command- * channel close. */ static void floppy_close_all_channels(DRIVE *floppy) { int i; bufferinfo_t *p; for (i = 0; i <= 15; i++) { p = &(floppy->buffers[i]); if (p->mode != BUFFER_NOT_IN_USE && p->mode != BUFFER_COMMAND_CHANNEL) close_1541(floppy, i); } } static int do_initialize(DRIVE *floppy) { floppy_close_all_channels(floppy); /* Update BAM in memory */ if (floppy->ActiveFd > 0) floppy_read_bam(floppy); if (floppy->ErrFlg) set_error_data(floppy, 3); /* clear or read error data */ return IPE_OK; } static int allocate_chain(DRIVE *floppy, int t, int s) { BYTE tmp[256]; while (t) { if (check_track_sector(floppy->ImageFormat, t, s) < 0) { floppy_error(&floppy->buffers[15], IPE_ILLEGAL_TRACK_OR_SECTOR, s, t); return IPE_ILLEGAL_TRACK_OR_SECTOR; } if (!allocate_sector(floppy->bam, t, s)) { /* The 1541 does not seem to catch this error... */ floppy_error(&floppy->buffers[15], IPE_NO_BLOCK, s, t); return IPE_NO_BLOCK; } floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, tmp, t, s, floppy->D64_Header); t = (int) tmp[0]; s = (int) tmp[1]; } return IPE_OK; } int do_validate(DRIVE *floppy) { int t, s; BYTE *b; int status; BYTE oldbam[256]; status = do_initialize(floppy); if (status != IPE_OK) return status; if (floppy->ReadOnly) return IPE_WRITE_PROTECT_ON; memcpy(oldbam, floppy->bam, 256); for (t = 1; t <= floppy->NumTracks; t++) { b = (t <= NUM_TRACKS_1541) ? (BYTE *) floppy->bam + BAM_BIT_MAP + 4 * (t - 1) : (BYTE *) floppy->bam + BAM_EXT_BIT_MAP + 4 * (t - NUM_TRACKS_1541 - 1); *b++ = 0; *b++ = 0; *b++ = 0; *b++ = 0; for (s = 0; s < sector_map[t]; s++) free_sector(floppy->bam, t, s); } /* * First map out the BAM and directory itself. */ /* XXX -- advanced drives ... */ status = allocate_chain(floppy, DSK_BAM_TRACK, DSK_BAM_SECTOR); if (status != IPE_OK) { memcpy(floppy->bam, oldbam, 256); return status; } set_find_first_slot(floppy, "*", 1, 0); while ((b = (BYTE *) find_next_slot(floppy))) { char *filetype = (char *) &floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_TYPE_OFFSET]; if (*filetype & FT_CLOSED) { status = allocate_chain(floppy, b[SLOT_FIRST_TRACK], b[SLOT_FIRST_SECTOR]); if (status != IPE_OK) { memcpy(floppy->bam, oldbam, 256); return status; } /* The 1541 drive always validates side sectors even if the file type is not REL. */ status = allocate_chain(floppy, b[SLOT_SIDE_TRACK], b[SLOT_SIDE_SECTOR]); if (status != IPE_OK) { memcpy(floppy->bam, oldbam, 256); return status; } } else { *filetype = FT_DEL; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); } } /* Write back BAM only if validate was successful. */ floppy_write_bam(floppy); return status; } static int do_format(DRIVE *floppy, char *name, BYTE *id, BYTE *minus) { BYTE tmp[256]; int status; BYTE null = 0; if (!name) return IPE_SYNTAX; if (floppy->ReadOnly) return IPE_WRITE_PROTECT_ON; /* * If id, skip comma */ if (id) *id++ = 0; else id = &null; if (floppy->ErrFlg) set_error_data(floppy, 5); /* clear and write error data */ /* * Make the first dir-entry */ memset(tmp, 0, 256); tmp[1] = 255; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, tmp, floppy->Dir_Track, floppy->Dir_Sector, floppy->D64_Header); /* * Create Disk Format of type '2A' */ memset(floppy->bam, 0, 256); floppy->bam[0] = DSK_DIR_TRACK; floppy->bam[1] = DSK_DIR_SECTOR; floppy->bam[2] = 65; memset(floppy->bam + BAM_DISK_NAME, 0xa0, 27); mystrncpy(floppy->bam + BAM_DISK_NAME, (BYTE *)name + 1, 16); mystrncpy(floppy->bam + BAM_DISK_ID, id, 2); floppy->bam[BAM_VERSION] = 50; floppy->bam[BAM_VERSION + 1] = 65; /* Write BAM so that Initialise during Validate will work -Olaf Seibert */ floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->bam, DSK_BAM_TRACK, DSK_BAM_SECTOR, floppy->D64_Header); /* * Validate is called to clear the BAM. */ status = do_validate(floppy); return status; } /* ------------------------------------------------------------------------- */ /* * Floppy Disc Controller Simulator. FDC executes tasks in the work queue * and puts return values there. */ /* * Sync (update) the Error Block image in memory */ static int set_error_data(DRIVE *floppy, int flags) { int size; off_t offset; if (!(floppy->ErrData)) { floppy->ErrData = (char *)malloc((size_t)MAX_BLOCKS_ANY); assert (floppy->ErrData); memset(floppy->ErrData, 0x01, MAX_BLOCKS_ANY); } else if (flags & 1) /* clear error data */ memset(floppy->ErrData, 0x01, MAX_BLOCKS_ANY); size = num_blocks (floppy->ImageFormat, floppy->NumTracks); offset = ((off_t)size << 8) + (off_t)(floppy->D64_Header?0:HEADER_LENGTH); lseek(floppy->ActiveFd, offset, SEEK_SET); if (flags & 2) { /* read from file */ if (read(floppy->ActiveFd, (char *)floppy->ErrData, size) < size) { fprintf(stderr, "\nfloppy image read error\n"); return(-2); } } if (flags & 4) { /* write to file */ if (write(floppy->ActiveFd, (char *)floppy->ErrData, size) < size) { fprintf(stderr, "\nfloppy image write error\n"); return(-2); } } return (0); } #if 0 /* unused... */ /* * Return 'error' according to track and sector */ static int get_disk_error(DRIVE *floppy, int track, int sector) { int sectors, i; if ((sectors = check_track_sector(floppy->ImageFormat, track, sector)) < 0) return -1; if (floppy->ErrFlg) { i = floppy->ErrData[sectors]; } else i = 0x01; return (i); } #endif /* ------------------------------------------------------------------------- */ /* * Check that requested Track and Sector are within limits. * Return the index number of the block. */ int check_track_sector(int format, int track, int sector) { int sectors = 0, i; if (track < 1 || sector < 0) return (-1); switch (format) { case 1541: if (track > MAX_TRACKS_1541 || sector >= sector_map[track]) return (-1); for (i = 1; i < track; i++) { sectors += sector_map[i]; } sectors += sector; break; case 1571: if (track > MAX_TRACKS_1571) return (-1); if (track > NUM_TRACKS_1541) { /* The second side */ track -= NUM_TRACKS_1541; sectors = NUM_BLOCKS_1541; } if (sector >= sector_map[track]) return (-1); for (i = 1; i < track; i++) { sectors += sector_map[i]; } sectors += sector; break; case 1581: if (track > MAX_TRACKS_1581 || sector >= NUM_SECTORS_1581) return (-1); sectors = track * NUM_SECTORS_1581 + sector; break; /* PET IEEE488 Drives. */ case 8050: if (track > MAX_TRACKS_8050 || sector >= pet_sector_map[track]) return (-1); for (i = 1; i < track; i++) { sectors += pet_sector_map[i]; } sectors += sector; break; case 8250: if (track > MAX_TRACKS_8250) return (-1); if (track > NUM_TRACKS_8050) { /* The second side */ track -= NUM_TRACKS_8050; sectors = NUM_BLOCKS_8050; } if (sector >= pet_sector_map[track]) return (-1); for (i = 1; i < track; i++) { sectors += pet_sector_map[i]; } sectors += sector; break; default: sectors = -1; } return (sectors); } /* * Return block offset on 'disk file' according to track and sector */ static off_t offset_from_track_and_sector(int format, int track, int sector, int d64) { int sectors; if ((sectors = check_track_sector(format, track, sector)) < 0) return -1; #ifdef DEBUG_DRIVE printf("DEBUG SECTOR %3d (%2d,%2d) OFF_T: %ld\n", sectors, track, sector, ((off_t)sectors << 8) /* + (off_t)HEADER_LENGTH*/ ); #endif return ( ((off_t)sectors << 8) + (off_t)(d64?0:HEADER_LENGTH) ); } /* * Read one block */ int floppy_read_block(int fd, int format, BYTE *buf, int track, int sector, int d64) { off_t offset; offset = offset_from_track_and_sector(format, track, sector, d64); if (offset < 0) return -1; lseek(fd, offset, SEEK_SET); if (read(fd, (char *)buf, 256) < 256) { fprintf (stderr, "Floppy image file read failed.\n"); return(-2); } return 0; } /* * Write one block */ int floppy_write_block(int fd, int format, BYTE *buf, int track, int sector, int d64) { off_t offset; offset = offset_from_track_and_sector(format, track, sector, d64); if (offset < 0) return -1; if (lseek(fd, offset, SEEK_SET) < 0 || write(fd, (char *)buf, 256) < 0) { perror("floppy_write_block"); return(-2); } return 0; } static int write_sequential_buffer(DRIVE *floppy, bufferinfo_t *bi, int length ) { int t_new, s_new; int e; BYTE *buf = bi->buffer; BYTE *slot = bi->slot; /* * First block of a file ? */ if (slot[SLOT_FIRST_TRACK] == 0) { e = alloc_first_free_sector(floppy->bam, &t_new, &s_new); if (e < 0) { floppy_error(&floppy->buffers[15], IPE_DISK_FULL, 0, 0); return -1; } slot[SLOT_FIRST_TRACK] = bi->track = t_new; slot[SLOT_FIRST_SECTOR] = bi->sector = s_new; } if (length == WRITE_BLOCK) { /* * Write current sector and allocate next */ t_new = bi->track; s_new = bi->sector; e = alloc_next_free_sector(floppy->bam, &t_new, &s_new); if (e < 0) { floppy_error(&floppy->buffers[15], IPE_DISK_FULL, 0, 0); return -1; } buf[0] = t_new; buf[1] = s_new; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, buf, bi->track, bi->sector, floppy->D64_Header); bi->track = t_new; bi->sector = s_new; } else { /* * Write last block */ buf[0] = 0; buf[1] = length - 1; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, buf, bi->track, bi->sector, floppy->D64_Header); } if (!(++slot[SLOT_NR_BLOCKS])) ++slot[SLOT_NR_BLOCKS + 1]; return 0; } /* ------------------------------------------------------------------------- */ /* * Disk Block availability management */ /* * Initialize Directory Slot find */ static void set_find_first_slot(DRIVE *floppy, char *name, int length, int type) { floppy->find_name = name; floppy->find_type = type; floppy->find_length = length; #ifdef DEBUG_DRIVE if (name) printf ("name = >%s< *name = $%02x\n", name, *name); printf("Prepare seeking entry for '%s' of type %d. (len =%d)\n", (name ? name : "<noname>"), type, length); #endif /* * Make sure find_next_slot() loads first directory block ... * Emulate chaining behaviour of the 1541 -- GEC */ floppy->Curr_track = floppy->Dir_Track; floppy->Curr_sector = floppy->Dir_Sector; floppy->SlotNumber = -1; floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Dir_Track, floppy->Dir_Sector, floppy->D64_Header); } static BYTE *find_next_slot(DRIVE *floppy) { static BYTE return_slot[32]; int status; floppy->SlotNumber++; #ifdef DEBUG_DRIVE printf("find next slot for '%s' of type %d. slot: %d\n", (floppy->find_name ? floppy->find_name : "<noname>"), floppy->find_type, floppy->SlotNumber); #endif /* * Loop all directory blocks starting from track 18, sector 1 (1541). */ do { /* * Load next(first) directory block ? */ if (floppy->SlotNumber >= 8) { if (!(*(floppy->Dir_buffer))) { #ifdef DEBUG_DRIVE printf("error.\n"); #endif return NULL; } floppy->SlotNumber = 0; floppy->Curr_track = (int) floppy->Dir_buffer[0]; floppy->Curr_sector = (int) floppy->Dir_buffer[1]; status = floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); } while (floppy->SlotNumber < 8) { if (floppy_name_match(&floppy->Dir_buffer[floppy->SlotNumber * 32], floppy->find_name, floppy->find_length, floppy->find_type)) { memcpy(return_slot, &floppy->Dir_buffer[floppy->SlotNumber * 32], 32); #ifdef DEBUG_DRIVE printf("found slot %d on %d %d.\n", floppy->SlotNumber, floppy->Curr_track, floppy->Curr_sector); #endif return return_slot; } floppy->SlotNumber++; } } while (*(floppy->Dir_buffer)); /* * If length < 0, create new directory-entry if possible */ if (floppy->find_length < 0) { int s; #ifdef DEBUG_DRIVE printf("create a new entry.\n"); #endif for (s = 1; s < sector_map[DSK_DIR_TRACK]; s++) { if (allocate_sector(floppy->bam, DSK_DIR_TRACK, s)) { floppy->Dir_buffer[0] = DSK_DIR_TRACK; floppy->Dir_buffer[1] = s; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); #ifdef DEBUG_DRIVE printf("Found (%d %d) TR = %d SE = %d.\n", DSK_DIR_TRACK, s, floppy->Curr_track, floppy->Curr_sector); #endif floppy->SlotNumber = 0; memset(floppy->Dir_buffer, 0, 256); floppy->Dir_buffer[1] = 0xff; floppy->Curr_sector = s; return floppy->Dir_buffer; } } } /* length */ return NULL; } static void free_chain(DRIVE *floppy, int t, int s) { BYTE buf[256]; while (t) { #ifdef DEBUG_DRIVE printf("free_chain free %d,%d\n", t, s); #endif if (check_track_sector(floppy->ImageFormat, t, s) < 0) { /* ILLEGAL TRACK OR SECTOR */ break; } if (!free_sector(floppy->bam, t, s)) { /* NO BLOCK */ break; } free_sector(floppy->bam, t, s); floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, buf, t, s, floppy->D64_Header); t = (int) buf[0]; s = (int) buf[1]; } } /* * remove_slot() is called from open_1541() (in 'save and replace') * and from ip_execute() for 'SCRATCH'. */ static void remove_slot(DRIVE *floppy, BYTE *slot) { int tmp; int t, s; /* * Find slot */ for (tmp = 0; (tmp < 16) && slot[SLOT_NAME_OFFSET + tmp] != 0xa0; tmp++) ; set_find_first_slot(floppy, (char *)&slot[SLOT_NAME_OFFSET], tmp, slot[SLOT_TYPE_OFFSET] & 0x07); /* * If slot slot found, remove */ if (find_next_slot(floppy)) { /* * Free all buffers from bam */ t = (int) floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_FIRST_TRACK]; s = (int) floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_FIRST_SECTOR]; free_chain(floppy, t, s); t = (int) floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_SIDE_TRACK]; s = (int) floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_SIDE_SECTOR]; free_chain(floppy, t, s); /* Update bam */ floppy_write_bam(floppy); /* Update directory entry */ floppy->Dir_buffer[floppy->SlotNumber * 32 + SLOT_TYPE_OFFSET] = 0; floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->Dir_buffer, floppy->Curr_track, floppy->Curr_sector, floppy->D64_Header); } } /* ------------------------------------------------------------------------- */ #ifndef MAX #define MAX(a, b) ((a) > (b)? (a) : (b)) #endif /* XXX warning XXX * These BAM functions ignore formats other than 1541 because * in their current form they could not possibly support the * 1571/8050/8250/etc: DSK_DIR_TRACK is different, they have multiple * BAM sectors, 8250 allocation strategy is different, etc. */ /* How many tracks there can be at one side of the directory track */ #define DSK_HALF_MAX_TRACKS MAX(DSK_DIR_TRACK-1, MAX_TRACKS_1541-DSK_DIR_TRACK) /* * This algorithm is used to select a free first sector */ static int alloc_first_free_sector(BYTE *bam, int *track, int *sector) { int t, s; int d; for (d = 1; d <= DSK_HALF_MAX_TRACKS; d++) { t = DSK_DIR_TRACK - d; #ifdef DEBUG_DRIVE printf("alloc_first_free_sector on track %d?\n", t); #endif if (t < 1) continue; for (s = 0; s < sector_map[t]; s++) { if (allocate_sector(bam, t, s)) { *track = t; *sector = s; #ifdef DEBUG_DRIVE printf("alloc_first_free_sector: %d,%d\n", t, s); #endif return 0; } } t = DSK_DIR_TRACK + d; #ifdef DEBUG_DRIVE printf("alloc_first_free_sector on track %d?\n", t); #endif if (t > MAX_TRACKS_1541) continue; for (s = 0; s < sector_map[t]; s++) { if (allocate_sector(bam, t, s)) { *track = t; *sector = s; #ifdef DEBUG_DRIVE printf("alloc_first_free_sector: %d,%d\n", t, s); #endif return 0; } } } return -1; } /* * This algorithm is used to select a continuation sector. * track and sector must be given. * XXX the interleave is not taken into account yet. */ static int alloc_next_free_sector(BYTE *bam, int *track, int *sector) { int t, s; int d; int dir; int diskhalf; if (*track < DSK_DIR_TRACK) { dir = -1; d = DSK_DIR_TRACK - *track; } else { dir = 1; d = *track - DSK_DIR_TRACK; } for (diskhalf = 0; diskhalf < 2; diskhalf++) { for (; d <= DSK_HALF_MAX_TRACKS; d++) { t = DSK_DIR_TRACK + dir * d; #ifdef DEBUG_DRIVE printf("alloc_next_free_sector on track %d?\n", t); #endif if (t < 1 || t > MAX_TRACKS_1541) { dir = -dir; d = 1; break; } for (s = 0; s < sector_map[t]; s++) { if (allocate_sector(bam, t, s)) { *track = t; *sector = s; #ifdef DEBUG_DRIVE printf("alloc_next_free_sector: %d,%d\n", t, s); #endif return 0; } } } } return -1; } int allocate_sector(BYTE *bam, int track, int sector) { BYTE *bamp; /* Macros use this */ bamp = (track <= NUM_TRACKS_1541) ? &bam[BAM_BIT_MAP + 4 * (track - 1)] : &bam[BAM_EXT_BIT_MAP + 4 * (track - NUM_TRACKS_1541 - 1)]; if (BAM_ISSET(sector)) { (*bamp)--; BAM_CLR(sector); return 1; } return 0; } int free_sector(BYTE *bam, int track, int sector) { BYTE *bamp; bamp = (track <= NUM_TRACKS_1541) ? &bam[BAM_BIT_MAP + 4 * (track - 1)] : &bam[BAM_EXT_BIT_MAP + 4 * (track - NUM_TRACKS_1541 - 1)]; if (!(BAM_ISSET(sector))) { BAM_SET(sector); (*bamp)++; return 1; } return 0; } /* ------------------------------------------------------------------------- */ /* * Load/Store BAM Image. */ static int floppy_read_bam (DRIVE *floppy) { return floppy_read_block(floppy->ActiveFd, floppy->ImageFormat, floppy->bam, DSK_BAM_TRACK, DSK_BAM_SECTOR, floppy->D64_Header); } static int floppy_write_bam (DRIVE *floppy) { return floppy_write_block(floppy->ActiveFd, floppy->ImageFormat, floppy->bam, DSK_BAM_TRACK, DSK_BAM_SECTOR, floppy->D64_Header); } /* ------------------------------------------------------------------------- */ /* * Functions to attach the disk image files. */ void detach_floppy_image(DRIVE *floppy) { floppy_close_all_channels(floppy); if (floppy->ActiveFd >= 0) { printf("Detaching disk image %s\n", floppy->ActiveName); if (floppy->detach_func != NULL) floppy->detach_func(floppy); zclose(floppy->ActiveFd); floppy->ActiveFd = -1; floppy->ActiveName[0] = 0; /* Name is used as flag */ } } int attach_floppy_image(DRIVE *floppy, const char *name, int mode) { int DType = -1; int fd; hdrinfo hdr; if ( !(floppy -> type & DT_DISK)) { printf("Incompatible emulator mode: FS 1541 Drive.\n"); return (-1); } if (!name) { printf("No name, detaching floppyimage\n"); return (-1); } fd = zopen(name, O_RDWR, 0); /* If we cannot open the image read/write, try to open it read only. */ if (fd < 0) { fd = zopen(name, O_RDONLY, 0); floppy->ReadOnly = 1; } else { floppy->ReadOnly = 0; } if (fd >= 0) { if (check_header(fd, &hdr)) { printf("File '%s' was not recognized as a disk image\n", name); zclose(fd); return (-2); } if (hdr.v_major > HEADER_VERSION_MAJOR || (hdr.v_major == HEADER_VERSION_MAJOR && hdr.v_minor > HEADER_VERSION_MINOR)) { printf("Disk image file %s (V %d.%02d) version higher than emulator (V %d.%02d)\n", name, hdr.v_major, hdr.v_minor, HEADER_VERSION_MAJOR, HEADER_VERSION_MINOR); zclose(fd); return (-2); } /* * Check that the disk format on the image is compatible. */ DType = get_diskformat (hdr.devtype); if (DType < 0 || DType != get_diskformat (hdr.devtype)) { fprintf(stderr, "\nError: Disk drive type mismatch.\n"); /* * mode defines what to do if the attached disk image is * altered or incompatible */ return (-2); } detach_floppy_image(floppy); floppy->ImageFormat = DType; floppy->ActiveFd = fd; strcpy(floppy->ActiveName, name); /* floppy->changed = 0; */ floppy->NumTracks = hdr.tracks; floppy->NumBlocks = num_blocks (DType, hdr.tracks); floppy->ErrFlg = hdr.errblk; floppy->D64_Header = hdr.d64; floppy->GCR_Header = hdr.gcr; if (!hdr.gcr) { /* Initialise format constants */ set_disk_geometry(floppy, DType); /* Initialize */ if (hdr.errblk) set_error_data(floppy, 3); /* clear or read error data */ floppy_read_bam (floppy); } if (hdr.d64) printf ("D64 disk image attached: %s%s\n", name, floppy->ReadOnly ? " (read only)" : ""); if (hdr.gcr) printf ("GCR disk image attached: %s%s\n", name, floppy->ReadOnly ? " (read only)" : ""); else printf ("VICE disk image version %d.%02d attached (CBM%d format%s): %s\n", hdr.v_major, hdr.v_minor, DType, floppy->ReadOnly ? ", read only" : "", name); } else { fprintf(stderr, "Couldn't open file %s\n",name); return -1; } if (floppy->attach_func != NULL) floppy->attach_func(floppy); return (0); } /* ------------------------------------------------------------------------- */ /* * Information Acquisition routines. */ int get_std64_header(int fd, BYTE *header) { int devtype = DEFAULT_DEVICE_TYPE; int tracks = NUM_TRACKS_1541; int blk = NUM_BLOCKS_1541-1; int len, errblk; char block[256]; memset(header, 0, HEADER_LENGTH); /* Check values */ if (check_track_sector(get_diskformat (devtype), tracks, 1) < 0) exit (-1); header[HEADER_MAGIC_OFFSET + 0] = HEADER_MAGIC_1; header[HEADER_MAGIC_OFFSET + 1] = HEADER_MAGIC_2; header[HEADER_MAGIC_OFFSET + 2] = HEADER_MAGIC_3; header[HEADER_MAGIC_OFFSET + 3] = HEADER_MAGIC_4; header[HEADER_VERSION_OFFSET + 0] = HEADER_VERSION_MAJOR; header[HEADER_VERSION_OFFSET + 1] = HEADER_VERSION_MINOR; header[HEADER_FLAGS_OFFSET + 0] = devtype; header[HEADER_FLAGS_OFFSET + 1] = tracks; if (lseek(fd, 256*blk, SEEK_SET)<0) return FD_BADIMAGE; while ((len = read(fd, block, 256)) == 256) { if (++blk > 771) { printf("Nice try.\n"); break; } } if (blk < NUM_BLOCKS_1541) { printf("Cannot read block %d\n", blk); return (FD_NOTRD); } switch (blk) { case 683: tracks = NUM_TRACKS_1541; errblk = 0; break; case 685: if (len != 171) { /* check if 683 error bytes */ printf("cannot read block %d\n", blk); return (FD_NOTRD); } tracks = NUM_TRACKS_1541; errblk = 1; break; case 768: tracks = EXT_TRACKS_1541; errblk = 0; break; case 771: tracks = EXT_TRACKS_1541; errblk = 1; break; default: return (FD_BADIMAGE); } header[HEADER_FLAGS_OFFSET+0] = DEFAULT_DEVICE_TYPE; header[HEADER_FLAGS_OFFSET+1] = tracks; header[HEADER_FLAGS_OFFSET+2] = 0; header[HEADER_FLAGS_OFFSET+3] = errblk; return(0); } int check_header(int fd, hdrinfo *hdr) { BYTE header[HEADER_LENGTH]; lseek (fd, (off_t) 0, SEEK_SET); if (read(fd, (BYTE *)header, sizeof (header)) != sizeof (header)) { fprintf(stderr, "\nCannot read image header\n"); return FD_RDERR; } memset (hdr, 0, sizeof(hdrinfo)); hdr->d64 = 0; hdr->gcr = 0; if (header[HEADER_MAGIC_OFFSET + 0] != HEADER_MAGIC_1 || header[HEADER_MAGIC_OFFSET + 1] != HEADER_MAGIC_2 || header[HEADER_MAGIC_OFFSET + 2] != HEADER_MAGIC_3 || header[HEADER_MAGIC_OFFSET + 3] != HEADER_MAGIC_4) { struct stat s; if (!fstat (fd, &s)) { if (IS_D64_LEN(s.st_size)) { if (get_std64_header(fd, header)) return FD_BADIMAGE; hdr->d64 = 1; } else { if (import_GCR_image(header, hdr)) return FD_OK; else return FD_BADIMAGE; } } else { perror ("stat"); return FD_BADIMAGE; } } hdr-> v_major = header[HEADER_VERSION_OFFSET + 0]; hdr-> v_minor = header[HEADER_VERSION_OFFSET + 1]; /* Disk type flags: Device Type, Max Tracks, Side, and Error Flag. */ hdr-> devtype = header[HEADER_FLAGS_OFFSET]; hdr-> tracks = header[HEADER_FLAGS_OFFSET + 1]; hdr-> sides = (header[HEADER_FLAGS_OFFSET + 2] ? 2 : 1); hdr-> errblk = header[HEADER_FLAGS_OFFSET + 3]; hdr-> format = (get_diskformat(hdr-> devtype)) == 1581 ? 'D' : 'A'; if (hdr-> tracks == 0) hdr-> tracks = 35; if (check_track_sector(get_diskformat(hdr-> devtype), hdr-> tracks, 1) < 0) return FD_BADIMAGE; strncpy (hdr->description, (char *)header + HEADER_LABEL_OFFSET, HEADER_LABEL_LEN); return FD_OK; } int import_GCR_image(BYTE *header, hdrinfo *hdr) { int trackfield; if (strncmp("GCR-1541",header,8)) return 0; if (header[8] != 0) { printf("Import GCR: Wrong GCR image version.\n"); return 0; } if ((header[9] < (NUM_TRACKS_1541 * 2)) || (header[9] > (MAX_TRACKS_1541 * 2))) { printf("Import GCR: invalid number of tracks.\n"); return 0; } trackfield = header[10] + header[11] * 256; if (trackfield != 7928) { printf("Import GCR: invalid track field number.\n"); return 0; } hdr->tracks = header[9] / 2; hdr->format = 1541; hdr->gcr = 1; hdr->v_major = HEADER_VERSION_MAJOR; hdr->v_minor = HEADER_VERSION_MINOR; hdr->devtype = DEFAULT_DEVICE_TYPE; return 1; } /* * This routine is from fvcbm by Dan Fandrich. */ int get_diskformat (int devtype) { int DiskType = -1; /* * Get the group of compatible disk formats. */ switch (devtype & DT_MASK) { case DT_2031: /*case DT_4031:*/ case DT_2040: /*case DT_3040:*/ case DT_2041: case DT_4040: case DT_1540: case DT_1541: case DT_1542: case DT_1551: case DT_1570: DiskType = 1541; break; case DT_1571: case DT_1572: DiskType = 1571; break; case DT_1581: DiskType = 1581; break; case DT_8050: DiskType = 8050; break; case DT_8060: case DT_8061: break; case DT_SFD1001: case DT_8250: DiskType = 8250; break; /*case DT_8280:*/ } return (DiskType); } /* * Calculate and return the total number of blocks available on a disk. */ int num_blocks (int format, int tracks) { int blocks = -1; switch (format) { case 1541: if (tracks > MAX_TRACKS_1541) tracks = MAX_TRACKS_1541; blocks = NUM_BLOCKS_1541 + (tracks - 35) * 17; break; case 1571: if (tracks > MAX_TRACKS_1571) tracks = MAX_TRACKS_1571; blocks = NUM_BLOCKS_1571 + (tracks - 70) * 17; break; case 1581: blocks = NUM_BLOCKS_1581; break; case 8050: blocks = NUM_BLOCKS_8050; break; case 8250: blocks = NUM_BLOCKS_8250; break; } return (blocks); } /* ------------------------------------------------------------------------- */ /* * Misc functions */ static int mystrncpy(BYTE *d, BYTE *s, int n) { while (n-- && *s) *d++ = *s++; return (n); } void no_a0_pads(BYTE *ptr, int l) { while (l--) { if (*ptr == 0xa0) *ptr = 0x20; ptr++; } } /* ------------------------------------------------------------------------- */ /* * Initialise format constants */ void set_disk_geometry(DRIVE *floppy, int type) { if (get_diskformat(type) == 1581) { floppy->Bam_Track = 38; floppy->Bam_Sector = 1; floppy->Dir_Track = 38; floppy->Dir_Sector = 3; } else { floppy->Bam_Track = 18; floppy->Bam_Sector = 0; floppy->Dir_Track = 18; floppy->Dir_Sector = 1; } } /* ------------------------------------------------------------------------- */ #define MAXLEVELS 1 struct { int level; struct { DIR *dp; struct dirent *dirp; } current[MAXLEVELS+1]; } ITERATION; /* * Returns -1 if an error occurred, 0 if no files match, and positive * integer for matches. */ #if 0 int find_name (char *fsname) { char buf[MAXPATHLEN], dirname[MAXPATHLEN]; char *p; DIR *dp; struct dirent *dirp; /* dp ja dirp sailytettava tai kaikki nimet palautettava kerralla */ /* Directory read */ #if 0 if (fsname[1]) { if (!(dp = opendir((char *)fsname + 1))) { for (p = fsname; *p; p++) if (isupper(*p)) *p = tolower(*p); if (!(dp = opendir((char *)fsname + 1))) return (-1); } strcpy(dirname, fsname + 1); } else { if (!(dp = opendir("."))) return (-1); strcpy(dirname, "."); } #else if (!(dp = opendir("."))) return (-1); strcpy(dirname, "."); #endif #ifdef DEBUG_FSDRIVE printf ("reading\n"); #endif /* * Find the next directory entry and try if it matches * given pattern. */ if ((dirp = readdir(dp))) { strcpy(buf, dirname); strcat(buf, "/"); strcat(buf, dirp->d_name); if (stat(buf, &statbuf) < 0) return (-1); p = buf; for (i = 0; i < dirp->d_namlen && (*p = p_topetcii(dirp->d_name[i])); ++i, ++p); #ifdef DEBUG_FSDRIVE printf ("found >%s< (%d/%d) buf:>%s< (%d)\n", dirp->d_name, i, dirp->d_namlen, info->name +4, info->buflen); #endif } closedir(dp); } #endif /* Wild-card match routine * * This routine takes two string parameters, the path to be macthed and * a pattern to match it with. There are two wild-cards - the ? and *. * A question mark will match exactly one character. An asterisk will * match any number (or zero) characters. Each '*' introduces one level * of recursion. A backslash forces the next character to be taken verbatim. * However, a null character cannot be protected. */ int compare_filename (char *name, char *pattern) { char *p, *q; int literal = 0; p = pattern; q = name; while (*p && *q) { if (!literal) { if (*p == '?') { ++p; ++q; continue; } if (*p == '*') { while (*++p == '*'); if (!*p) return (1); /* End of pattern -> matches */ while (!compare_filename (q, p) && *++q); return (*q); /* if *q > 0 it must have matched */ } if (*p == '\\') { ++p; ++literal; continue; } } /* literal */ else { literal = 0; } if (*p == *q) { ++p; ++q; } else return (0); /* No match */ } /* while */ return (!*p && !*q); /* Match */ } /* ------------------------------------------------------------------------- */ /* Read the directory and return it as a long malloc'ed ASCII string. FIXME: Should probably be made more robust. */ char *floppy_read_directory(DRIVE *floppy, const char *pattern) { BYTE *p; int outbuf_size, max_outbuf_size, len; char line[256], *outbuf; /* Open the directory. */ if (pattern != NULL) sprintf(line, "$:%s", pattern); else sprintf(line, "$"); if (open_1541(floppy, line, 1, 0) != SERIAL_OK) return NULL; /* Allocate a buffer. */ max_outbuf_size = 4096; outbuf = xmalloc(max_outbuf_size); outbuf_size = 0; p = floppy->buffers[0].buffer + 2; /* Skip load address. */ while ((p[0] | (p[1] << 8)) != 0) { len = sprintf(line, "%d ", p[2] | (p[3] << 8)); p += 4; while (*p != '\0') { line[len++] = p_toascii(*p, 0); p++; } p++; line[len++] = '\n'; bufcat(outbuf, &outbuf_size, &max_outbuf_size, line, len); } close_1541(floppy, 0); bufcat(outbuf, &outbuf_size, &max_outbuf_size, "\n", 1); /* Add trailing zero. */ *(outbuf + outbuf_size) = '\0'; return outbuf; } /* Read the directory of a named disk image and return it as a long malloc'ed ASCII string. */ char *read_disk_image_contents(const char *fname) { static BYTE fake_command_buffer[256]; DRIVE floppy; char *buf; hdrinfo hdr; int fd; fd = zopen(fname, O_RDONLY, 0); if (fd < 0) return NULL; if (check_header(fd, &hdr)) return NULL; if (hdr.v_major > HEADER_VERSION_MAJOR || (hdr.v_major == HEADER_VERSION_MAJOR && hdr.v_minor > HEADER_VERSION_MINOR)) { printf("Disk image file %s (V %d.%02d) version higher than emulator (V %d.%02d)\n", fname, hdr.v_major, hdr.v_minor, HEADER_VERSION_MAJOR, HEADER_VERSION_MINOR); zclose(fd); return 0; } /* Initialize floppy. */ floppy.ImageFormat = get_diskformat(hdr.devtype); if (floppy.ImageFormat < 0) { fprintf(stderr, "Error: unknown image format.\n"); zclose(fd); return NULL; } floppy.ActiveFd = fd; floppy.NumTracks = hdr.tracks; floppy.NumBlocks = num_blocks(floppy.ImageFormat, hdr.tracks); floppy.ErrFlg = hdr.errblk; floppy.D64_Header= hdr.d64; floppy.ReadOnly = 1; /* Just to be sure... */ if (floppy_read_bam(&floppy) < 0) { fprintf(stderr, "Error: cannot read BAM.\n"); zclose(fd); return NULL; } /* This fake is necessary to `open_1541'... Ugly, but that is the only way I know with the existing functions. */ floppy.buffers[15].mode = BUFFER_COMMAND_CHANNEL; floppy.buffers[15].buffer = fake_command_buffer; floppy.buffers[0].mode = BUFFER_NOT_IN_USE; /* Initialize format constants. */ set_disk_geometry(&floppy, floppy.ImageFormat); /* Now we can actually read. */ buf = floppy_read_directory(&floppy, NULL); zclose(fd); return buf; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.