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.