This is c1541.c in view mode; [Download] [Up]
/*
* c1541.c - Stand-alone disk image maintenance program.
*
* Written by
* Teemu Rantanen (tvr@cs.hut.fi)
* Jouko Valta (jopi@zombie.oulu.fi)
* Gerhard Wesp (gwesp@cosy.sbg.ac.at)
* Daniel Sladic (sladic@eecg.toronto.edu)
* Ricardo Ferreira (storm@esoterica.pt)
* Andreas Boose (boose@unixserv.rz.fh-hannover.de)
*
* Patches by
* Olaf Seibert (rhialto@mbfys.kun.nl)
* Ettore Perazzoli (ettore@comm2000.it)
*
* Zipcode implementation based on `zip2disk' by
* Paul David Doherty (h0142kdd@rz.hu-berlin.de)
*
* 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.
*
*/
#ifdef __hpux
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#endif
#include "vice.h" /* for convenience */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
#include <assert.h>
#include "c1541.h"
#include "mshell.h"
#include "serial.h"
#include "drive.h"
#include "gcr.h"
#include "file.h"
#include "charsets.h"
#include "tape.h"
#include "utils.h"
#include "zipcode.h"
#define MAXARG 16
#define MAXVAL 0xffff
#define MAXDRIVE 1
#define C1541_VERSION_MAJOR 2
#define C1541_VERSION_MINOR 4
/* Global */
DRIVE *DriveData[4] = { NULL, NULL, NULL, NULL };
static int DriveNum = 0;
static char *args[MAXARG+1];
static int values[MAXARG+1];
static int types[MAXARG+1];
static int nargs;
char newname[256];
/* Local functions */
static int check_drive ( int dev, int mode );
static int open_image ( int dev, char *name, int create );
static int create_image ( int fd, int devtyp, int tracks, int errb, char *label );
static void usage( void );
static int disk_attach ( void );
static int disk_cdd ( void );
static int disk_format ( void );
static int disk_gcrformat ( void );
static int disk_delete ( void );
static int disk_list ( void );
static int disk_validate ( void );
static int disk_copy ( void );
static int disk_rename ( void );
static int disk_read ( void );
static int disk_write ( void );
static int disk_import ( void );
static int disk_import_zipfile ( void );
static int disk_unlynx ( void );
static int disk_copy_tape ( void );
static int disk_sectordump ( void );
static int disk_extract ( void );
static int disk_raw_command ( void );
static int disk_info ( void );
static int disk_help ( void );
static int disk_quit ( void );
static int disk_system ( void );
extern char sector_map[43]; /* Ugly: FIXME! */
extern int speed_map[42];
struct ms_table disk_cmds[] = {
{"format", 1, 2, disk_format,
"format [imagename] 'diskname,id'\t(create X64 disk & format)\n"},
{"gcrformat", 1, 2, disk_gcrformat,
"gcrformat imagename ['diskname,id']\t(create GCR disk & format)\n"},
{"delete", 1, MAXARG, disk_delete,
"delete files\n"},
{"list", 0, MAXARG, disk_list,
"list [pattern]\n"},
{"validate", 0, 1, disk_validate,
"validate\n"},
{"copy", 2, MAXARG, disk_copy,
#ifdef COPY_TO_LEFT
"copy newname oldname [oldname2 ...]\n"},
#else
"copy oldname [oldname2 ...] newname\n"},
#endif
{"rename", 2, 2, disk_rename, /* reintroduced 1996/04/08 EP */
#ifdef COPY_TO_LEFT
"rename newname oldname\n"},
#else
"rename oldname newname\n"},
#endif
{"attach", 0, 2, disk_attach,
"attach [drive] imagename\t\t(attaches floppy image for use)\n"},
{"unit", 1, 1, disk_cdd,
"unit\t drive\t\t\t\t(changes default device)\n"},
{"create", 2, 3, disk_import,
"create imagename dumpname ['descr']\t(create disk & import a raw diskdump)\n"},
{"zcreate", 2, 3, disk_import_zipfile,
"zcreate imagename zipname ['descr']\t(create disk & import zipfile images)\n"},
{"tape", 1, MAXARG, disk_copy_tape,
"tape\t tapeimage [files]\t\t(copy files from tape image)\n"},
{"unlynx", 1, 1, disk_unlynx,
"unlynx lynxname\t\t\t(unarchive an LNX file to disk image)\n"},
{"read", 1, 2, disk_read,
"read\t 1541name [FSname]\t\t(copy file from disk image)\n"},
{"write", 1, 2, disk_write,
"write\t FSname [1541name]\t\t(write CBM or P00 file to disk image)\n"},
#if 0
{"memory", 0, 2, disk_memdump,
"memory [addr] [end|+len]\t\t(show memory in hex format)\n"},
{"hunt", 3, MAXARG, disk_hunt,
"hunt start end|+len bytes|'string'\n"},
#endif
{"block", 0, 4, disk_sectordump,
"block [drive:] [track] [sector] [disp] (show disk blocks in hex format)\n"},
{"extract", 0, 0, disk_extract,
"extract\t\t\t\t(extract all files to filesystem)\n"},
{"info", 0, 1, disk_info,
"info\t ['description']\t\t(show disk image version and info)\n"},
{"@", 0, 1, disk_raw_command,
"@\t[disk command]\t\t\t(command for channel 15)\n"},
{"system", 1, 1, disk_system,
"system command\t\t\t(execute a system command)\n"},
{"help", 0, 1, disk_help,
"help\t [topic]\t\t\t(Describe command)\n"},
{"?", 0, 1, disk_help,
"?\t [topic]\t\t\t(Same as help)\n"},
{"quit", 0, 0, disk_quit,
"quit\t\t\t\t\t(exits c1541)\n"},
{"x", 0, 0, disk_quit,
"x\t\t\t\t\t(exits c1541)\n"},
{NULL, 0, 0, 0, NULL}
};
/*
* Create Floppy Image
*/
static int create_image (int fd, int devtype, int tracks, int errb, char *label)
{
BYTE header[HEADER_LENGTH];
BYTE block[256];
int blks;
int i;
memset(header, 0, sizeof (header));
memset(block, 0, sizeof (block));
/* Check values */
if (check_track_sector(get_diskformat (devtype), tracks, 1) < 0)
exit (-1);
blks = num_blocks (get_diskformat (devtype), tracks);
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 (label)
strncpy((char *)header + HEADER_LABEL_OFFSET, label, HEADER_LABEL_LEN);
header[HEADER_LABEL_OFFSET + HEADER_LABEL_LEN] = 0; /* terminator */
printf("writing header\n");
if (write(fd, (char *)header, sizeof (header)) != sizeof (header)) {
printf("cannot write header\n");
exit(1);
}
printf("creating blocks\n");
for (i = 0; i < blks; i++) {
if (write(fd, (char *)block, sizeof (block)) != sizeof (block)) {
printf("cannot write block %d\n", i);
exit(1);
}
}
if (errb) {
printf("creating error data\n");
#if 0
if (set_error_data(floppy, 5) < 0) { /* clear and write */
printf("cannot write error data block.\n");
exit(1);
}
#endif
}
return (0);
}
static int set_label(int fd, char *label)
{
int siz = HEADER_LABEL_LEN +1;
char buf[HEADER_LABEL_LEN +2];
memset(buf, 0, siz);
if (label)
strncpy(buf, label, HEADER_LABEL_LEN);
lseek(fd, (off_t)HEADER_LABEL_OFFSET, SEEK_SET);
if (write(fd, (char *)buf, siz) != siz) {
return (FD_WRTERR);
}
return (FD_OK);
}
/*
* These 4 bytes are disk type flags (set upon create or format)
* They contain: Device Type, Max Tracks, Side, and Error Flag.
*/
static int set_disk_size(int fd, int tracks, int sides, int errblk)
{
int siz = HEADER_FLAGS_LEN;
char buf[HEADER_FLAGS_LEN +1];
buf[0] = DEFAULT_DEVICE_TYPE;
buf[1] = tracks;
buf[2] = sides;
buf[3] = errblk;
lseek(fd, (off_t)HEADER_FLAGS_OFFSET, SEEK_SET);
if (write(fd, (char *)buf, siz) != siz) {
return (FD_WRTERR);
}
return (FD_OK);
}
/*
* Open image or create a new one.
* If the file exists, it must have valid header.
*/
static int open_image(int dev, char *name, int create)
{
DRIVE *floppy;
int fd;
if (dev < 0 || dev > MAXDRIVE)
return (-1); /* FD_BADDEV */
floppy = DriveData[dev & 3];
if (create) {
if ((fd = open(name, O_RDWR | O_CREAT, 0666)) < 0) {
printf("could not create image %s\n", name);
return (-1);
}
/*
* Get default geometry
* make a new image file and format it
*/
create_image(fd, DEFAULT_DEVICE_TYPE, NUM_TRACKS_1541, 0, NULL);
close(fd);
}
attach_floppy_image(floppy, name, 0);
if (floppy->ActiveFd < 0)
return (-1);
return (0);
}
/*
* This routine is used in order to get around standard serial bus
* connection.
* It checks privileges for the requested operation on the specified
* disk unit. Return value < 0 means requested action is not allowed,
* i.e. there is no disk in the drive, or that drive is not available.
*/
static int check_drive(int dev, int flags)
{
DRIVE *floppy;
dev &= 7;
if (dev < 0 || dev > 3)
return (FD_BADDEV);
floppy = DriveData[dev & 3];
if (!floppy || (flags != CHK_NUM && floppy->ActiveFd < 0)) {
return (FD_NOTREADY);
}
return (FD_OK);
}
/* ------------------------------------------------------------------------- */
static int disk_attach(void)
{
char *name = NULL;
int dev = DriveNum;
switch (nargs) {
case 3:
dev = values[1];
name = args[2];
break;
case 2:
name = args[1];
}
if (check_drive(dev, CHK_NUM) < 0)
return (FD_BADDEV);
attach_floppy_image(DriveData[dev & 3], name, 0);
return (0);
}
static int disk_cdd(void)
{
int dev = values[1];
if (check_drive(dev, CHK_NUM) < 0)
return (FD_BADDEV);
DriveNum = (dev & 3);
return (0);
}
/*
* Creates a raw GCR disk image.
*/
static int disk_gcrformat (void)
{
int fd, track, sector;
BYTE gcr_header[12], id[2];
char name[16], *idptr;
DWORD gcr_track_p[MAX_TRACKS_1541 * 2];
DWORD gcr_speed_p[MAX_TRACKS_1541 * 2];
BYTE gcr_track[7930], rawdata[260];
BYTE *gcrptr;
if (nargs < 2)
return 0;
memset(name, 0xa0, 16);
id[0] = id[1] = 0xa0;
if (nargs > 2) {
strcpy(newname, args[2]);
petconvstring(newname, 1);
idptr = memchr(newname, ',', strlen(newname));
if (idptr == NULL) {
int i;
for (i = 0; i < 16 && newname[i] != '\0'; i++)
name[i] = newname[i];
} else {
int i;
*idptr = '\0';
for (i = 0; i < 16 && newname[i] != '\0'; i++)
name[i] = newname[i];
strncpy(id, ++idptr, 2);
if (idptr[1] != '\0') {
id[0] = idptr[1];
if (idptr[2] != '\0')
id[1] = idptr[2];
}
}
}
if ((fd = open(args[1], O_RDWR | O_CREAT, 0666)) < 0) {
printf("could not create image %s\n", args[1]);
return (FD_BADIMAGE);
}
strcpy(gcr_header, "GCR-1541");
gcr_header[8] = 0;
gcr_header[9] = MAX_TRACKS_1541 * 2;
gcr_header[10] = 7928 % 256;
gcr_header[11] = 7928 / 256;
if (write(fd, (char *)gcr_header, sizeof(gcr_header))
!= sizeof(gcr_header)) {
printf("Cannot write header.\n");
close(fd);
return 0;
}
for (track = 0; track < MAX_TRACKS_1541; track++) {
gcr_track_p[track * 2] = 12 + MAX_TRACKS_1541 * 16 + track * 7930;
gcr_track_p[track * 2 + 1] = 0;
gcr_speed_p[track * 2] = speed_map[track];
gcr_speed_p[track * 2 + 1] = 0;
}
if (write_dword(fd, gcr_track_p, sizeof(gcr_track_p)) < 0) {
printf("Cannot write track header.\n");
close(fd);
return 0;
}
if (write_dword(fd, gcr_speed_p, sizeof(gcr_speed_p)) < 0) {
printf("Cannot write speed header.\n");
close(fd);
return 0;
}
for (track = 0; track < MAX_TRACKS_1541; track++) {
int raw_track_size[4] = { 6250, 6666, 7142, 7692 };
memset(&gcr_track[2], 0xff, 7928);
gcr_track[0] = raw_track_size[speed_map[track]] % 256;
gcr_track[1] = raw_track_size[speed_map[track]] / 256;
gcrptr = &gcr_track[2];
for (sector = 0; sector < sector_map[track+1]; sector++) {
BYTE chksum;
int i;
memset(rawdata, 0, 260);
if (track == 17 && sector == 0) {
BYTE *rdat = &rawdata[1];
int s, t;
memset(rdat + BAM_DISK_NAME, 0xa0, 27);
rdat[0] = DSK_DIR_TRACK;
rdat[1] = DSK_DIR_SECTOR;
rdat[2] = 65;
rdat[BAM_VERSION] = 50;
rdat[BAM_VERSION + 1] = 65;
memcpy(rdat + BAM_DISK_NAME, (BYTE *)name, 16);
rdat[BAM_DISK_ID] = id[0];
rdat[BAM_DISK_ID + 1] = id[1];
for (t = 1; t <= 35; t++)
for (s = 0; s < sector_map[t]; s++)
free_sector(rdat, t, s);
allocate_sector(rdat, DSK_BAM_TRACK, 0);
allocate_sector(rdat, DSK_BAM_TRACK, 1);
}
rawdata[0] = 7;
chksum = rawdata[1];
for (i = 1; i < 256; i++)
chksum ^= rawdata[i + 1];
rawdata[257] = chksum;
convert_sector_to_GCR(rawdata, gcrptr, track+1, sector, id[0], id[1]);
gcrptr += 360;
}
if (write(fd, (char *)gcr_track, sizeof(gcr_track))
!= sizeof(gcr_track)) {
printf("Cannot write track data.\n");
close(fd);
return 0;
}
}
close(fd);
return 0;
}
/*
* Simulate the 1541 DOS commands
*/
static int disk_format (void)
{
strcpy(newname, "N:");
strcpy(newname + 2, ((nargs > 2) ? args[2] : args[1]) );
if (!memchr(newname, ',', strlen(newname))) {
printf("There must be ID on the name\n");
return (0);
}
/* Open image or create a new one.
* If the file exists, it must have valid header.
* Note: Syntax check from command line is NOT passed
* without the image name.
*/
/*printf("DriveNum = %d\n", DriveNum);*/
if (nargs > 2) {
if (open_image(DriveNum, args[1], 1) < 0)
return (FD_BADIMAGE);
}
printf("formatting image\n");
petconvstring(newname +2, 1);
ip_execute(DriveData[DriveNum], (BYTE *)newname, strlen(newname));
return(0);
}
static int disk_delete (void)
{
DRIVE *floppy;
int dev, err, i = 1;
char *name, tmp[20];
do {
name = args[i];
dev = find_devno(DriveNum, name);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy = DriveData[dev & 3];
petconvstring(name, 1);
strcpy(tmp, "S:");
strcat(tmp, name);
ip_execute(floppy, (BYTE *)tmp, strlen(tmp));
} while (++i < nargs);
return(0);
}
static int disk_validate (void)
{
int dev, err;
dev = (nargs > 1 ? values[1] : DriveNum);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
printf("validating\n");
do_validate(DriveData[dev & 3]);
return(0);
}
static int disk_list (void)
{
DRIVE *floppy;
int dev, err, i = 1;
char *name, *listing;
do {
name = args[i];
dev = find_devno(DriveNum, name);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy = DriveData[dev & 3];
if (name) {
if (*name && strchr (name, ':'))
name = strchr (name, ':') +1;
petconvstring(name, 1);
listing = floppy_read_directory(floppy, name);
}
else
listing = floppy_read_directory(floppy, NULL);
if (listing != NULL) {
printf("\n === Listing for drive %d ===\n\n%s", dev | 8,
listing);
free(listing);
}
} while (++i < nargs);
return(0);
}
static int disk_copy (void)
{
DRIVE *floppy1, *floppy2; /* may point to the same data */
int dev, err, i;
char *name, *dest;
char c;
#ifdef COPY_TO_LEFT
/* copy newname oldname [oldname2 ...] */
dest = args[1];
#else
/* copy oldname [oldname2 ...] newname */
dest = args[nargs -1]; /* EP */
#endif
dev = find_devno(DriveNum, dest);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy2 = DriveData[dev & 3];
if (strchr (dest, ':'))
dest = strchr (dest, ':') +1;
strcpy((char *)newname, dest);
petconvstring(newname, 1);
if (open_1541(floppy2, newname, (int)strlen(newname), 1)) {
printf("cannot open `%s' for writing on image.\n", dest);
return (FD_WRTERR);
}
#ifdef COPY_TO_LEFT
for (i = 2; i < nargs; i++) {
#else
for (i = 1; i < nargs -1; i++) {
#endif
dev = find_devno(DriveNum, args[i]);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy1 = DriveData[dev & 3];
if (strchr (args[i], ':'))
name = strchr (args[i], ':') +1;
else
name = args[i];
strcpy((char *)newname, name);
petconvstring(newname, 1);
if (open_1541(floppy1, newname, strlen(newname), 0)) {
printf("u%d: cannot open `%s' on image.\n", dev, name);
close_1541(floppy2, 1);
return (FD_RDERR);
}
printf("copying %s\n", args[i]);
while (!read_1541(floppy1, (BYTE *)&c, 0)) {
if (write_1541(floppy2, c, 1)) {
printf("No space on image ?\n");
break;
}
}
close_1541(floppy1, 0);
} /* for */
close_1541(floppy2, 1);
return(0);
}
static int disk_rename (void)
{
DRIVE *floppy2;
int dev, err;
char *src, *dest;
#if defined (COPY_TO_LEFT)
/* rename newname oldname */
dest = args[1];
src = args[nargs - 1];
#else
/* rename oldname newname */
src = args[1];
dest = args[nargs - 1];
#endif
dev = find_devno(DriveNum, dest);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy2 = DriveData[dev & 3];
if (strchr (dest, ':'))
dest = strchr (dest, ':') +1;
printf("renaming `%s' to `%s'\n", src, dest);
sprintf (newname, "R:%s=%s", dest, src); /* Rename File */
petconvstring(newname +2, 1);
ip_execute(floppy2, (BYTE *)newname, strlen(newname));
return(0);
}
/*
* Additional Disk Maintenance commands
*/
static int disk_read (void)
{
DRIVE *floppy;
FILE *f;
char *name, *fsname;
int dev, err;
char c;
if (strchr (args[1], ':'))
name = strchr (args[1], ':') +1;
else
name = args[1];
dev = find_devno(DriveNum, args[1]);
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy = DriveData[dev & 3];
strcpy((char *)newname, name);
petconvstring(newname, 0); /* To PETscii */
if (open_1541(floppy, newname, strlen(newname), 0)) {
printf("u%d: cannot open `%s' on image.\n", dev, name);
return (FD_BADNAME);
}
/* Get real filename from the disk file.
* slot must be defined by open_1541().
*/
memcpy (newname, floppy->buffers[0].slot + SLOT_NAME_OFFSET, 16);
if (nargs > 2)
fsname = args[2];
else {
int l;
fsname = newname;
no_a0_pads((BYTE *)fsname, 16);
l = strlen (fsname) - 1; /* EP */
while (fsname[l] == ' ') {
fsname[l] = '\0';
l--;
}
petconvstring(fsname, 1);
}
if (*fsname == '-')
f = stdout;
else {
if (!(f = fopen(fsname, WRITE))) {
close_1541(floppy, 0);
printf("cannot open %s for writing.\n", fsname);
return (FD_NOTWRT);
}
/* Write P00 header ? */
if (nargs > 2 && is_pc64name(fsname) >= 0) {
printf("writing PC64 header.\n");
write_pc64header(f, newname, 0);
}
} /* stdout */
fprintf(stderr, "reading file `%s' from image.\n", name);
while (!read_1541(floppy, (BYTE *)&c, 0))
fputc(c, f);
fclose(f);
close_1541(floppy, 0);
return(0);
}
static int disk_write (void)
{
DRIVE *floppy;
FILE *f;
int dev, err, c;
int realtype, reclen = 0;
char *p, realname[20];
if (!(f = fopen(args[1], READ))) {
printf("cannot open %s for reading\n", args[1]);
return (FD_NOTRD);
}
if (nargs > 2)
p = args[2];
else if ((p = strrchr(args[1], '/')) == NULL || !p++)
p = args[1];
if (!p || !*p)
return (FD_BADNAME);
strcpy((char *)newname, p);
dev = find_devno(DriveNum, newname);
petconvstring(newname, 1);
/* Check if P00 header and read the actual filename and type */
if ((realtype = is_pc64name(args[1])) >= 0 &&
read_pc64header(f, realname, &reclen) == FD_OK) {
if (nargs < 3)
strcpy (newname, pc_get_cbmname (f, args[1]));
}
else
rewind (f); /* There is no P00 header */
if ((err = check_drive(dev, CHK_RDY)) < 0)
return (err);
floppy = DriveData[dev & 3];
if (open_1541(floppy, newname, strlen(newname), 1)) {
printf("cannot open %s for writing on image.\n", newname);
return (FD_WRTERR);
}
printf("writing file `%s' to image\n", newname);
while (EOF != (c = fgetc(f))) {
if (write_1541(floppy, c, 1)) {
printf("No space on image ?\n");
break;
}
}
fclose(f);
close_1541(floppy, 1);
return(0);
}
typedef struct {
char ImageFormat; /* 1541/71/81 */
int TracksSide;
int Sides;
int TotalBks;
} DiskFormats;
static DiskFormats Legal_formats[] = {
{DT_1541, 35, 0, 683 },
{DT_1541, 40, 0, 768 },
/*{DT_1541, 42, 0, 785 }, */
{DT_1571, 35, 1, 1366 },
{DT_1581, 80, 0, 3200 },
{DT_8050, 77, 0, 2088 },
{DT_8250, 72, 1, 4166 },
{ -1, 0, 0, 0 }
};
static int disk_import_zipfile (void)
{
DRIVE *floppy = DriveData[DriveNum];
DiskFormats *format = Legal_formats;
char tmp[256];
int fsfd=-1, errblk = 0;
int track, sector, count;
char *fname;
int channel = 2;
BYTE str[20];
static int drive = 8;
int singlefilemode = 0;
/*
* Open image or create a new one.
* If the file exists, it must have valid header.
*/
if (open_image(DriveNum, args[1], 1) < 0) {
return (FD_BADIMAGE);
}
fname = (char *) malloc (sizeof(char) * strlen(args[2]) + 3);
/* ignore '[0-4]!' if found */
if (args[2][0] >= '1' && args[2][0] <= '4' && args[2][1] == '!')
strcpy(fname+2, args[2]+2);
else
strcpy(fname+2, args[2]);
fname[0] = '0';
fname[1] = '!';
set_label(floppy->ActiveFd, "*** Truncated image."); /* Notify of errors */
printf("copying blocks to image\n");
lseek(floppy->ActiveFd, HEADER_LENGTH, SEEK_SET);
/* Write out all the sectors */
for(count=0;count<format->TotalBks;count++) {
if (write(floppy->ActiveFd, tmp, 256) != 256) {
printf("cannot write block %d of %s\n", count, args[2]);
return (FD_WRTERR);
}
}
if (open_1541(floppy, "#", 1, channel)) {
printf("u%d: cannot open buffer #%d.\n", drive, channel);
return (FD_RDERR);
}
for (track = 1; track <= 35; track++) {
if (singlefilemode || track == 1) {
if (track == 1) {
fsfd = open(fname + 2, O_RDONLY);
if (fsfd >= 0) {
printf("reading zipfile on onefile -mode\n");
singlefilemode = 1;
lseek(fsfd, 4, SEEK_SET);
}
} else if (track == 9 || track == 17 || track == 26) {
lseek(fsfd, 2, SEEK_CUR);
}
}
if (!singlefilemode) {
switch (track) {
case 1:
case 9:
case 17:
case 26:
fname[0]++;
if (fsfd >= 0)
close(fsfd);
if ((fsfd = open(fname, O_RDONLY)) < 0) {
printf("cannot open %s\n", fname);
perror (fname);
return (FD_NOTRD);
}
lseek(fsfd, (track == 1) ? 4 : 2, SEEK_SET);
break;
}
}
for (count = 0; count < sector_map[track]; count++) {
if ( (zipcode_read_sector(fsfd, track, §or, floppy->buffers[channel].buffer)) != 0) {
close(fsfd);
return (FD_BADIMAGE);
}
/* Write one block */
sprintf((char *)str, "B-W:%d 0 %d %d", channel, track, sector);
if (ip_execute(floppy, (BYTE *)str, strlen((char *)str)) != 0) {
track = DSK_DIR_TRACK;
sector= 0;
close(fsfd);
return (FD_RDERR);
}
}
}
close_1541(floppy, channel);
/* Update Format and Label information on Disk Header */
lseek(floppy->ActiveFd, (off_t)HEADER_LABEL_OFFSET + 0, SEEK_SET);
if (write(floppy->ActiveFd, &(format->ImageFormat), 1) != 1) {
return (FD_WRTERR);
}
set_disk_size(floppy->ActiveFd, format->TracksSide, format->Sides, errblk);
set_label(floppy->ActiveFd, (args[3] ? args[3] : NULL)); /* Fix the note */
close(fsfd);
ip_execute(floppy, (BYTE *)"I", 1);
return(0);
}
static int disk_import (void)
{
DRIVE *floppy = DriveData[DriveNum];
DiskFormats *format;
char tmp[256];
int fsfd, len, blk = 0, errblk = 0;
/*
* Open image or create a new one.
* If the file exists, it must have valid header.
*/
if (open_image(DriveNum, args[1], 1) < 0)
return (FD_BADIMAGE);
if ((fsfd = open(args[2], O_RDONLY)) < 0) {
printf("cannot open %s\n", args[2]);
perror (args[2]);
return (FD_NOTRD);
}
set_label(floppy->ActiveFd, "*** Truncated image."); /* Notify of errors */
/* First copy all available blocks and then check existence of
* Error Data Block */
printf("copying blocks to image\n");
lseek(floppy->ActiveFd, HEADER_LENGTH, SEEK_SET);
while ((len = read(fsfd, tmp, 256)) == 256) {
if (++blk > MAX_BLOCKS_ANY) {
printf("\nNice try.\n");
break;
}
if (write(floppy->ActiveFd, tmp, 256) != 256) {
printf("cannot write block %d of %s\n", blk, args[2]);
return (FD_WRTERR);
}
}
/* Now recognize the format and verify block count on it. */
if (blk < NUM_BLOCKS_1541) {
printf("cannot read block %d of %s\n", blk, args[2]);
return (FD_NOTRD);
}
for (format = Legal_formats; format->ImageFormat >= 0; ++format) {
if (blk == format->TotalBks) {
errblk = 0;
break;
}
if (blk == (format->TotalBks + (format->TotalBks >> 8)) ) {
errblk = 1;
break;
}
}
if (format->ImageFormat < 0)
return (FD_BADIMAGE);
/* Check and write the last (short) sector of error bytes */
if (len) {
if (len != (format->TotalBks % 256)) {
printf("cannot read block %d of %s\n", blk, args[2]);
return (FD_NOTRD);
}
if (write(floppy->ActiveFd, tmp, len) != len) {
printf("cannot write block %d of %s\n", blk, args[2]);
return (FD_WRTERR);
}
}
/* Update Format and Label information on Disk Header */
lseek(floppy->ActiveFd, (off_t)HEADER_LABEL_OFFSET + 0, SEEK_SET);
if (write(floppy->ActiveFd, &(format->ImageFormat), 1) != 1) {
return (FD_WRTERR);
}
set_disk_size(floppy->ActiveFd, format->TracksSide, format->Sides, errblk);
set_label(floppy->ActiveFd, (args[3] ? args[3] : NULL)); /* Fix the note */
close(fsfd);
ip_execute(floppy, (BYTE *)"I", 1);
return(0);
}
/*
* Copy files from *.t64 tape image. An already formatted
* X64 disk image is required
*/
#define TAPE_HDR_SIZE 64
#define TAPE_DIR_SIZE 32
static int disk_copy_tape (void)
{
DRIVE *floppy = DriveData[DriveNum];
FILE *f;
BYTE *dirp, *tapebuf = NULL;
char asciiname[20], *p;
int maxentries = 0, ccount = 0;
int c, i, len, loc;
if (!(f = fopen(args[1], READ))) {
printf("cannot open %s for reading\n", args[1]);
return (FD_NOTRD);
}
if ((maxentries = check_t64_header (f)) <= 0)
return (maxentries);
/* Read the tape directory entries into memory */
tapebuf = (BYTE *)malloc(TAPE_DIR_SIZE * maxentries);
assert(tapebuf);
fread(tapebuf, TAPE_DIR_SIZE, maxentries, f);
dirp = tapebuf;
while (maxentries--) {
if (*dirp == 1) { /* found a file */
/* Get filename and remove trailing spaces */
memccpy(newname, &dirp[16], 0xa0, 16);
newname[16] = 0;
{
char *cf = &newname[15];
while (*cf == 0x20) *cf-- = 0;
}
strcpy(asciiname, newname);
petconvstring(asciiname, 1); /* to ascii */
/* Check filename match against [files] list */
/* if (nargs > 4) { */
if (nargs > 2) {
for (i = 2; i < nargs &&
!compare_filename(asciiname, args[i]); ++i);
if (i >= nargs) {
dirp += TAPE_DIR_SIZE; /* No match, skip the file */
continue;
}
}
else {
if (!strcmp(asciiname, "file")) {
/* Invalid filename, try the imagename instead. */
strncpy(asciiname, args[1], 16);
asciiname[16] = 0;
if ((p = strstr(asciiname, ".t64")) != NULL)
*p = 0;
strcpy(newname, asciiname);
petconvstring(newname, 0); /* to petcii */
}
}
printf(" \"%-16s\"\t%02x%02x to %02x%02x\n",
asciiname, dirp[3], dirp[2], dirp[5], dirp[4]);
loc = dirp[8] | (dirp[9]<<8);
len = (dirp[4] | (dirp[5]<<8)) - (dirp[2] | (dirp[3]<<8));
if (open_1541(floppy, newname, strlen(newname), 1)) {
printf("Cannot open `%s' for writing on image.\n", asciiname);
free(tapebuf);
return (FD_WRTERR);
}
printf("Writing file to image. %d bytes\n", len);
/* PRG file */
write_1541(floppy, dirp[2], 1);
write_1541(floppy, dirp[3], 1);
fseek(f, loc, 0);
while ((len--) > 0 && (EOF != (c = fgetc(f))))
if (write_1541(floppy, c, 1)) {
printf("No space on image ?\n");
close_1541(floppy, 1);
free(tapebuf);
return (FD_WRTERR);
}
close_1541(floppy, 1);
if (len > 0)
fprintf(stderr,
"T64 Error: Unexpected end of tape. File may be truncated.\n");
++ccount;
} /* if dirp */
dirp += TAPE_DIR_SIZE;
}
printf("\n%d files copied.\n", ccount);
fclose(f);
free(tapebuf);
return(0);
}
/*
* Execute selection of BLOCK commands
*/
static int disk_sectordump(void)
{
static int drive = 8, track = DSK_DIR_TRACK, sector = DSK_DIR_SECTOR;
DRIVE *floppy;
BYTE *buf, str[20];
int err, cnt;
int channel = 2, disp = 0;
/* block [drive:] [track] [sector] [disp] show disk blocks in hex form */
if ((nargs > 1) && (types[1] != T_NUMBER)) {
switch (nargs) {
case 5:
disp = values[4];
case 4:
sector = values[3];
case 3:
track = values[2];
}
drive = find_devno(drive, args[1]);
}
else
switch (nargs) {
case 4:
disp = values[3];
case 3:
sector = values[2];
case 2:
track = values[1];
}
if ((err = check_drive(drive, CHK_RDY)) < 0)
return (err);
floppy = DriveData[drive & 3];
if (check_track_sector(floppy->ImageFormat, track, sector) < 0) {
sector = 0;
track = DSK_DIR_TRACK;
return FD_BAD_TS;
}
/* Read one block */
if (open_1541(floppy, "#", 1, channel)) {
printf("u%d: cannot open buffer #%d.\n", drive, channel);
return (FD_RDERR);
}
sprintf((char *)str, "B-R:%d 0 %d %d", channel, track, sector);
if (ip_execute(floppy, (BYTE *)str, strlen((char *)str)) != 0) {
track = DSK_DIR_TRACK;
sector= 0;
return (FD_RDERR);
}
buf = floppy->buffers[channel].buffer;
/* Show block */
printf("<%2d: %2d %2d>\n", drive, track, sector);
str[16] = 0;
for (; disp < 256;) {
printf("> %02X ", disp &255);
for (cnt = 0; cnt < 16; cnt++, disp++) {
printf(" %02X", buf[disp &255]);
str[cnt] = (buf[disp &255] < ' ' ?
'.' : p_toascii(buf[disp &255], 0));
}
printf(" ;%s\n", str);
}
/* Find next sector for the file being traced */
if (buf[0] && buf[1]) {
track = buf[0];
sector = buf[1];
}
else if (check_track_sector(floppy->ImageFormat, track, ++sector) < 0) {
sector = 0;
if (++track > floppy->NumTracks)
track = DSK_DIR_TRACK;
}
close_1541(floppy, channel);
return (FD_OK);
}
/*
* Extract all files (gwesp@cosy.sbg.ac.at)
*/
static int disk_extract(void)
{
int drive = 8, track = DSK_DIR_TRACK, sector = DSK_DIR_SECTOR;
DRIVE *floppy;
BYTE *buf, str[20];
int err;
int channel = 2;
if ((err = check_drive(drive, CHK_RDY)) < 0)
return (err);
floppy = DriveData[drive & 3];
if (open_1541(floppy, "#", 1, channel)) {
printf("u%d: cannot open buffer #%d.\n", drive, channel);
return (FD_RDERR);
}
while(1)
{
int i;
/* Read one block */
sprintf((char *)str, "B-R:%d 0 %d %d", channel, track, sector);
if (ip_execute(floppy, (BYTE *)str, strlen((char *)str))) {
return (FD_RDERR);
}
buf = floppy->buffers[channel].buffer;
for(i=0; i < 256; i+= 32) {
dirslot *entry= (dirslot *) (buf + i);
if ((((entry->FileType & 7) == (FT_SEQ))
|| ((entry->FileType & 7) == (FT_PRG))
|| ((entry->FileType & 7) == (FT_USR))
) && (entry->FileType & FT_CLOSED)) {
int len;
BYTE c;
FILE *fd;
BYTE name[17];
BYTE *l= memccpy(name,entry->FileName,0xa0,16);
if (l) {
*(l - 1)= 0; /* terminate by 0 */
len= l - 1 - name;
} else {
name[16]= 0;
len= 16;
}
petconvstring(name,1);
printf("%s\n",name);
unix_filename(name); /* for now, convert '/' to '_' */
if (open_1541(floppy,entry->FileName,len,0)) {
printf("u%d: cannot open `%s' on image.\n", drive, name);
continue;
}
if (!(fd= fopen(name,"wb"))) {
perror("Couldn't open unix file");
close_1541(floppy,0);
continue;
}
while (!read_1541(floppy,&c,0))
fputc(c,fd);
close_1541(floppy,0);
if (fclose(fd)) {
perror("fclose");
return FD_RDERR;
}
}
}
if (buf[0] && buf[1]) {
track = buf[0];
sector = buf[1];
}
else break;
}
close_1541(floppy, channel);
return (FD_OK);
}
#define OPEN15 0
static int disk_raw_command(void)
{
DRIVE *floppy = DriveData[DriveNum & 3];
#if OPEN15
char c;
#endif
#if OPEN15
if (open_1541(floppy, "", 0, 15)) {
printf("Can't open channel 15\n");
return (FD_NOTREADY);
}
#endif
/* Write to the command channel */
if (nargs >= 2) {
#if OPEN15
char *cp;
#endif
strcpy((char *)newname, args[1]);
petconvstring(newname, 1);
#if OPEN15
for (cp = newname; *cp; cp++) {
if (write_1541(floppy, *cp, 15)) {
printf("Error writing to channel 15\n");
break;
}
}
flush_1541(floppy, 15);
#else
ip_execute(floppy, (BYTE *)newname, strlen(newname));
#endif
}
/* Print the error now */
#if OPEN15
while (!read_1541(floppy, (BYTE *)&c, 15)) {
putchar(c);
}
close_1541(floppy, 15);
#else
printf("%s\n", floppy->buffers[15].buffer);
#endif
return FD_OK;
}
static int disk_info (void)
{
DRIVE *floppy;
hdrinfo hdr;
int err;
if ((err = check_drive(DriveNum, CHK_RDY)) < 0)
return (err);
floppy = DriveData[DriveNum & 3];
if ((err = check_header(floppy -> ActiveFd, &hdr)) < 0)
return (err);
/* Check if there's a new description provided. */
if (args[1]) {
return (set_label(floppy -> ActiveFd, args[1]));
}
else {
printf ("\nDescription: %s\n",
(*hdr.description ? hdr.description : "None."));
printf ("Drive Type : %d.\n", floppy->ImageFormat); /* Compatible drive */
printf ("Disk Format: %c.\n", hdr.format);
printf ("Sides\t : %d.\n", hdr.sides);
printf ("Tracks\t : %d.\n", hdr.tracks);
printf ((hdr.errblk ? "Error Block present.\n" : "No Error Block.\n"));
printf ("Write protect: %s.\n", hdr.wprot ? "On" : "Off");
}
return(0);
}
static void usage()
{
fprintf (stderr, "\nUsage: following commands are supported on c1541\n\
-format image 'name,id' Format image. Create if needed\n\
-delete image files Delete files from image\n\
-list image List files on image\n\
-validate image Validate disk\n %s\n\
-create image dump ['descr'] Build image out of raw blockdump\n\
(683 256-byte blocks, e.g. *.d64 file)\n\
-zcreate image zipfile Create image out of a ZIPcoded file\n\
-unlynx image lynxfile Extract contents of a LYNX file\n\
-tape image tapeimage [files] Transfer files from *.t64 image\n\
-read image 1541name [FSname] Read 1541name on image to file FSname\n\
-extract image Extract all files on image\n\
-write image FSname [1541name] Write bin or P00 file FSname to image\n\
-block [drive:] [track] [sector] [disp] \n\
Show disk blocks in hex format\n\
-info image ['description'] Show image and 1541 version\n\
[image list] Enter interactive mode\n\n\
\tA '-' in place of FSname stands for stdin/stdout.\n\n",
#ifdef COPY_TO_LEFT
"-copy image newname oldname [oldname2 ...]"
#else
"-copy image oldname [oldname2 ...] newname"
#endif
);
exit(1);
}
/*
* Help
*/
static int disk_help(void)
{
int i,n;
if (nargs == 2) {
n = strlen(args[1]);
for (i = 0; disk_cmds[i].command; i++)
if (!strncmp(disk_cmds[i].command, args[1], n)) {
printf("\n %s\n", disk_cmds[i].help_line);
/* add help page here */
break;
}
}
else {
printf("\nFollowing commands are available:\n\n");
for (i = 0; disk_cmds[i].command; i++)
printf(" %s", disk_cmds[i].help_line);
printf("\n\tAll commands may be abbreviated\n");
}
return (0);
}
static int disk_quit(void)
{
return 1;
}
/* Lynx support added by Riccardo Ferreira (storm@esoterica.pt) --
1998-02-07. */
static int disk_unlynx(void)
{
DRIVE *floppy;
FILE *f, *f2;
int err, dev, cnt=0;
long dentries, lbsize, bsize, dirsize;
BYTE val;
char buff[256] = { 0 }, cname[20] = { 0 }, ftype;
if (!(f=fopen(args[1], READ))) {
printf("cannot open %s for reading\n", args[1]);
return (FD_NOTRD);
}
/* Look for the 0, 0, 0 sign of the end of BASIC */
while (1) {
fread(&val, 1, 1, f);
if (val == 0)
cnt++;
else
cnt = 0;
if (cnt == 3)
break;
}
/* Bypass the 1st return in the file */
fgetc(f);
/* Get the directory block size */
cnt = 0;
while (1) {
fread(&val, 1, 1, f);
if (val != 13)
buff[cnt++] = val;
else
break;
}
buff[cnt] = 0;
if (string_to_long(buff, NULL, 10, &dirsize) < 0
|| dirsize <= 0) {
printf("invalid LyNX file.\n");
return (FD_RDERR);
}
/* Get the number of dir entries */
cnt = 0;
while (1) {
fread(&val, 1, 1, f);
if (val != 13)
buff[cnt++] = val;
else
break;
}
buff[cnt] = 0;
if (string_to_long(buff, NULL, 10, &dentries) < 0
|| dentries <= 0) {
printf("invalid LyNX file.\n");
return (FD_RDERR);
}
/* Open the file for reading of the chained data */
f2 = fopen(args[1], READ);
fseek(f2, (dirsize*254), SEEK_SET);
/* Loop */
while (dentries != 0) {
/* Read CBM filename */
cnt = 0;
while (1) {
fread(&val, 1, 1, f);
if (val != 13) cname[cnt++] = val; else break;
}
cname[cnt] = 0;
/* Read the block size */
cnt = 0;
while (1) {
fread(&val, 1, 1, f);
if (val != 13) buff[cnt++] = val; else break;
}
buff[cnt] = 0;
if (string_to_long(buff, NULL, 10, &bsize) < 0) {
printf("invalid LyNX file.\n");
return (FD_RDERR);
}
/* Get the file type (P[RG], S[EQ], R[EL], U[SR]) */
ftype = fgetc(f);
fgetc(f);
/* FIXME: REL type files unsupported */
if (ftype == 'R') {
printf("REL not supported.\n");
return (FD_RDERR);
}
/* FIXME: This is a temporary hack! How can we persuade `open_1541()'
to write `DEL' files without breaking compatibility with CBM
DOS? -- [EP] 98.04.17 */
if (ftype == 'D')
ftype = 'P';
/* Add the file type to the name */
cnt = strlen(cname);
cname[cnt++] = ',';
cname[cnt++] = ftype;
cname[cnt] = 0;
/* Get the byte size of the last block +1 */
cnt = 0;
while (1) {
fread(&val, 1, 1, f);
if (val != 13)
buff[cnt++] = val;
else
break;
}
buff[cnt] = 0;
if (string_to_long(buff, NULL, 10, &lbsize) < 0) {
printf("invalid LyNX file.\n");
return (FD_RDERR);
}
/* Calculate byte size of file */
cnt = bsize*254;
/* Get the device number */
dev = find_devno(DriveNum, (char *)NULL);
if ((err = check_drive(dev, CHK_RDY))<0)
return(err);
floppy = DriveData[dev & 3];
if (open_1541(floppy, cname, strlen(cname), 1)) {
printf("couldn't open '%s' for writing.\n", cname);
return (FD_WRTERR);
}
printf("writing file '%s' to image.\n", cname);
while (cnt != 0) {
fread(&val, 1, 1, f2);
if (write_1541(floppy, val, 1)) {
printf("no space on image ?\n");
break;
}
cnt--;
}
close_1541(floppy, 1);
/* Adjust for the last block */
fread(buff, 1, 254 - lbsize, f2);
dentries--;
}
fclose(f);
fclose(f2);
return(0);
}
/* `system' command added by Riccardo Ferreira (storm@esoterica.pt) --
1998-02-07. */
int disk_system(void)
{
return system(args[1]);
}
/*
* Disk Maintenance main program
*/
int main(argc, argv)
int argc;
char *argv[];
{
char *progname, *line;
int flg = 0, i;
progname = argv[0];
nargs = 0;
initialize_1541(8, DT_DISK | DT_1541, NULL, NULL, NULL);
initialize_1541(9, DT_DISK | DT_1541, NULL, NULL, NULL);
/* Direct mode */
if (argc > 1 && argv[1][0] == '-') {
char *ImageName;
if (argc < 3)
usage();
args[0] = argv[1] + 1; /* Command */
ImageName = argv[2];
if (!strncmp(args[0], "f", 1) || !strncmp(args[0], "cr", 2) ||
!strncmp(args[0], "zcr", 3)) {
--argc;
++argv;
} else {
argc -= 2;
argv += 2;
}
for (nargs = 1; nargs < argc; nargs++) { /* Parameters */
if (nargs >= MAXARG) {
fprintf(stderr, "%s: Bailing out: Too many arguments.\n\n",
progname);
usage();
}
args[nargs] = argv[nargs];
values[nargs] = strlen(args[nargs]);
}
if (strncmp(args[0], "f", 1) && strncmp(args[0], "cr", 2))
if (open_image(0, ImageName, 0) < 0) {
printf ("Cannot open image\n");
exit (1);
}
printf("\n");
if (eval_command(args[0], nargs, disk_cmds) < 0) { /* any error */
printf(" I won't do that.\n");
usage();
}
}
/* Mainloop: parse & execute */
else {
char buf[16];
fprintf (stderr, "\nC1541 V %d.%02d Image %d.%02d\n\n",
C1541_VERSION_MAJOR, C1541_VERSION_MINOR,
HEADER_VERSION_MAJOR, HEADER_VERSION_MINOR);
while (--argc && ++argv && nargs <= MAXDRIVE) { /* Images */
if (*argv[0])
if (open_image(nargs, argv[0], 0) < 0) { /* open each */
printf ("Invalid image\n");
exit (1);
}
++nargs;
}
while (!flg) {
sprintf (buf, "c1541 #%d> ", (DriveNum | 8) );
if ((line = read_line(buf, MODE_MON)) == NULL)
break;
if (*line) {
nargs = split_args(line, 0,
MAXARG, MAXVAL, args, values, types);
switch ( eval_command(args[0], nargs, disk_cmds) ) {
case FD_EXIT:
++flg;
case FD_OK:
break;
case FD_NOTREADY:
printf("Drive not ready.\n");
break;
case FD_CHANGED:
printf("Image file has changed on disk.\n");
break;
case FD_NOTRD:
printf("Cannot read file.\n");
break;
case FD_NOTWRT:
printf("Cannot write file.\n");
break;
case FD_WRTERR:
printf("Floppy write failed.\n");
break;
case FD_RDERR:
printf("Floppy read failed.\n");
break;
case FD_INCOMP:
printf("Incompatible DOS version.\n");
break;
case FD_BADIMAGE:
printf("Invalid image.\n"); /* Disk or tape */
break;
case FD_BADNAME:
printf("Invalid filename.\n");
break;
case FD_BADVAL:
printf("Illegal value.\n");
break;
case FD_BADDEV:
printf("Illegal device number.\n");
break;
case FD_BAD_TS:
printf("Inaccessible Track or Sector.\n");
break;
default:
printf(" Unknown command. Try 'help'\n");
}
printf("\n");
} /* line */
} /* while */
} /* Mainloop */
for (i = 0; i <= MAXDRIVE; i++) {
if (DriveData[i])
detach_floppy_image(DriveData[i]);
}
printf("\n");
return (0);
}
int attach_fsdevice(int device, char *var, char *name)
{
DriveData[device & 3] = (DRIVE *)var;
return 0;
}
int serial_attach_device(int device, char *var, char *name,
int (*getf)(void *, BYTE * , int),
int (*putf)(void *, BYTE , int),
int (*openf)(void *, char *, int , int),
int (*closef)(void *, int),
void (*flushf)(void *, int))
{
DriveData[device & 3] = (DRIVE *)var;
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.