static int vdrive_command_scratch(vdrive_t *vdrive, BYTE *name, int length) { int status, rc; BYTE *slot; cbmdos_cmd_parse_t cmd_parse; /* XXX * Wrong name parser -- s0:file1,0:file2 means scratch * those 2 files. (It's similar to the copy command.) */ cmd_parse.cmd = name; cmd_parse.cmdlength = length; cmd_parse.readmode = 0; rc = cbmdos_command_parse(&cmd_parse); if (rc != SERIAL_OK) { status = CBMDOS_IPE_NO_NAME; } else if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) { status = CBMDOS_IPE_WRITE_PROTECT_ON; } else { /*#ifdef DEBUG_DRIVE*/ log_debug("remove name='%s', len=%d (%d), type= %d.", cmd_parse.parsecmd, cmd_parse.parselength, length, cmd_parse.filetype); /*#endif*/ vdrive->deleted_files = 0; /* Since vdrive_dir_remove_slot() uses * vdrive_dir_find_first_slot() too, we cannot find the * matching files by simply repeating * vdrive_dir find_next_slot() calls alone; we have to re-call * vdrive_dir_find_first_slot() each time... EP 1996/04/07 */ vdrive_dir_find_first_slot(vdrive, cmd_parse.parsecmd, cmd_parse.parselength, 0); while ((slot = vdrive_dir_find_next_slot(vdrive))) { vdrive_dir_remove_slot(vdrive, slot); vdrive->deleted_files++; vdrive_dir_find_first_slot(vdrive, cmd_parse.parsecmd, cmd_parse.parselength, 0); } if (vdrive->deleted_files) status = CBMDOS_IPE_DELETED; else status = CBMDOS_IPE_NOT_FOUND; vdrive_command_set_error(vdrive, status, 1, 0); } lib_free(cmd_parse.parsecmd); return status; }
int vdrive_dir_first_directory(vdrive_t *vdrive, const char *name, int length, int filetype, bufferinfo_t *p) { BYTE *l; if (length) { if (*name == '$') { ++name; --length; } if (*name == ':') { ++name; --length; } } if (!*name || length < 1) { name = "*\0"; length = 1; } filetype = vdrive_dir_filetype(name, length); vdrive_dir_find_first_slot(vdrive, name, length, filetype, &p->dir); /* * Start Address, Line Link and Line number 0 */ l = p->buffer; *l++ = 1; *l++ = 4; *l++ = 1; *l++ = 1; *l++ = 0; *l++ = 0; l[25] = 0; *l++ = (BYTE)0x12; /* Reverse on */ *l++ = '"'; memcpy(l, &p->dir.buffer[vdrive->bam_name], 16); vdrive_dir_no_a0_pads(l, 16); l += 16; *l++ = '"'; *l++ = ' '; memcpy(l, &p->dir.buffer[vdrive->bam_id], 5); vdrive_dir_no_a0_pads(l, 5); p->bufptr = 32; return vdrive_dir_next_directory(vdrive, p); }
/* CMD style subdir support (using DIR filetype) */ static int vdrive_command_chdir(vdrive_t *vdrive, BYTE *name, int length) { int status, rc; BYTE *slot, buffer[256]; cbmdos_cmd_parse_t cmd_parse; cmd_parse.cmd = name; cmd_parse.cmdlength = length; cmd_parse.readmode = 0; rc = cbmdos_command_parse(&cmd_parse); if (rc != SERIAL_OK) { status = CBMDOS_IPE_NO_NAME; } else { /*#ifdef DEBUG_DRIVE*/ log_debug("chdir name='%s', len=%d (%d), type= %d.", cmd_parse.parsecmd, cmd_parse.parselength, length, cmd_parse.filetype); /*#endif*/ vdrive_dir_find_first_slot(vdrive, cmd_parse.parsecmd, cmd_parse.parselength, CBMDOS_FT_DIR); slot = vdrive_dir_find_next_slot(vdrive); if (slot) { slot = &vdrive->Dir_buffer[vdrive->SlotNumber * 32]; rc = vdrive_read_sector(vdrive, buffer, slot[SLOT_FIRST_TRACK], slot[SLOT_FIRST_SECTOR]); if (rc > 0) { return rc; } if (rc < 0) { return CBMDOS_IPE_NOT_READY; } vdrive->Header_Track = slot[SLOT_FIRST_TRACK]; vdrive->Header_Sector = slot[SLOT_FIRST_SECTOR]; vdrive->Dir_Track = buffer[0]; vdrive->Dir_Sector = buffer[1]; status = CBMDOS_IPE_OK; } else { status = CBMDOS_IPE_PATH_NOT_FOUND; } vdrive_command_set_error(vdrive, status, 0, 0); } lib_free(cmd_parse.parsecmd); return status; }
void vdrive_dir_remove_slot(vdrive_t *vdrive, BYTE *slot) { unsigned int tmp; int t, s; /* Find slot. */ for (tmp = 0; (tmp < 16) && slot[SLOT_NAME_OFFSET + tmp] != 0xa0; tmp++); vdrive_dir_find_first_slot(vdrive, (char *)&slot[SLOT_NAME_OFFSET], tmp, slot[SLOT_TYPE_OFFSET] & 0x07); /* If slot found, remove. */ if (vdrive_dir_find_next_slot(vdrive)) { /* Free all sector this file is using. */ t = (int) vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_FIRST_TRACK]; s = (int) vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_FIRST_SECTOR]; vdrive_dir_free_chain(vdrive, t, s); /* Free side sectors. */ t = (int) vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_SIDE_TRACK]; s = (int) vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_SIDE_SECTOR]; vdrive_dir_free_chain(vdrive, t, s); /* Update bam */ vdrive_bam_write_bam(vdrive); /* Update directory entry */ vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_TYPE_OFFSET] = 0; disk_image_write_sector(vdrive->image, vdrive->Dir_buffer, vdrive->Curr_track, vdrive->Curr_sector); } }
int vdrive_iec_open(vdrive_t *vdrive, const BYTE *name, unsigned int length, unsigned int secondary, cbmdos_cmd_parse_t *cmd_parse_ext) { bufferinfo_t *p = &(vdrive->buffers[secondary]); BYTE *slot; /* Current directory entry */ int rc, status = SERIAL_OK; /* FIXME: This should probably be set to zero */ cbmdos_cmd_parse_t cmd_parse_stat; cbmdos_cmd_parse_t *cmd_parse; BYTE name_stat[17]; unsigned int opentype; if (cmd_parse_ext != NULL) { cmd_parse = cmd_parse_ext; memset(name_stat, 0, sizeof(name_stat)); strncpy((char *)name_stat, cmd_parse->parsecmd, sizeof(name_stat) - 1); name = name_stat; length = (unsigned int)strlen((char *)name); secondary = cmd_parse->secondary; } else { cmd_parse = &cmd_parse_stat; } if (cmd_parse_ext == NULL && (!name || !*name) && p->mode != BUFFER_COMMAND_CHANNEL) { return SERIAL_NO_DEVICE; } /* No floppy in drive? */ if (vdrive->image == NULL && p->mode != BUFFER_COMMAND_CHANNEL && secondary != 15 && *name != '#') { vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_READY, 18, 0); log_message(vdrive_iec_log, "Drive not ready."); return SERIAL_ERROR; } #ifdef DEBUG_DRIVE log_debug("VDRIVE#%i: OPEN: Name '%s' (%d) on ch %d.", vdrive->unit, 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) { unsigned int n; for (n = 0; n < length; n++) { status = vdrive_iec_write(vdrive, name[n], secondary); } if (length) { p->readmode = CBMDOS_FAM_WRITE; } else { p->readmode = CBMDOS_FAM_READ; } return status; } /* * Clear error flag */ vdrive_command_set_error(vdrive, CBMDOS_IPE_OK, 0, 0); /* * In use ? */ if (p->mode != BUFFER_NOT_IN_USE) { #ifdef DEBUG_DRIVE log_debug("Cannot open channel %d. Mode is %d.", secondary, p->mode); #endif vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_CHANNEL, 0, 0); return SERIAL_ERROR; } if (cmd_parse_ext == NULL) { cmd_parse->cmd = name; cmd_parse->cmdlength = length; cmd_parse->secondary = secondary; /* make sure this is zero, since it isn't set below */ cmd_parse->recordlength = 0; cmd_parse->drive = -1; rc = cbmdos_command_parse(cmd_parse); if (rc != CBMDOS_IPE_OK) { status = SERIAL_ERROR; goto out; } #ifdef DEBUG_DRIVE log_debug("Raw file name: `%s', length: %i.", name, length); log_debug("Parsed file name: `%s', reallength: %i. drive: %i", cmd_parse->parsecmd, cmd_parse->parselength, cmd_parse->drive); #endif if (cmd_parse->drive != -1) { /* a drive number was specified in the filename */ if ((vdrive->image_format == VDRIVE_IMAGE_FORMAT_8050) || (vdrive->image_format == VDRIVE_IMAGE_FORMAT_8250) || (vdrive->image_format == VDRIVE_IMAGE_FORMAT_2040)) { /* FIXME: dual disk drives not supported */ if (cmd_parse->drive == 0) { /* FIXME: use drive 0 */ } else if (cmd_parse->drive == 1) { /* FIXME: use drive 1 */ /* since some software gets confused when it sees the same disk in both drives, we bail out with an error instead. */ log_warning(LOG_DEFAULT, "second drive of dual disk drive is not supported"); vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_READY, 18, 0); status = SERIAL_ERROR; goto out; } else { /* FIXME: what exactly does the drive do if drivenumber is > 1, look up the file on both drives perhaps ? */ } } else { /* single disk drives seem to ignore the drive number, *except* if it is 1, which will result in an error. */ if (cmd_parse->drive == 1) { vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_READY, 18, 0); status = SERIAL_ERROR; goto out; } } } } /* Limit file name to 16 chars. */ if (cmd_parse->parselength > 16) { cmd_parse->parselength = 16; } /* * Internal buffer ? */ if (*name == '#') { vdrive_alloc_buffer(p, BUFFER_MEMORY_BUFFER); /* the pointer is actually 1 on the real drives. */ /* this probably relates to the B-R and B-W commands. */ /* 1541 firmware: $cb84 - open channel, $cc0f bp = 1 */ p->bufptr = 1; /* we need a length to support the original B-R and B-W commands. */ p->length = 256; status = SERIAL_OK; goto out; } /* Clear update flag */ p->needsupdate = 0; /* * 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 == '$') { p->readmode = CBMDOS_FAM_READ; status = iec_open_read_directory(vdrive, secondary, cmd_parse); goto out; } /* * Check that there is room on directory. */ if (cmd_parse->readmode == CBMDOS_FAM_READ || cmd_parse->readmode == CBMDOS_FAM_APPEND) { opentype = cmd_parse->filetype; } else { opentype = CBMDOS_FT_DEL; } vdrive_dir_find_first_slot(vdrive, cmd_parse->parsecmd, cmd_parse->parselength, opentype, &p->dir); /* * Find the first non-DEL entry in the directory (if it exists). */ do { slot = vdrive_dir_find_next_slot(&p->dir); } while (slot && ((slot[SLOT_TYPE_OFFSET] & 0x07) == CBMDOS_FT_DEL)); p->readmode = cmd_parse->readmode; p->slot = slot; /* Call REL function if we are creating OR opening one */ if (cmd_parse->filetype == CBMDOS_FT_REL || ( slot && (slot[SLOT_TYPE_OFFSET] & 0x07) == CBMDOS_FT_REL)) { /* Make sure the record length of the opening command is the same as the record length in the directory slot, if not DOS ERROR 50 */ if (slot && cmd_parse->recordlength > 0 && slot[SLOT_RECORD_LENGTH] != cmd_parse->recordlength) { vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_RECORD, 0, 0); status = SERIAL_ERROR; goto out; } /* At this point the record lengths are the same (or will be), so set them equal. */ if (slot) { cmd_parse->recordlength = slot[SLOT_RECORD_LENGTH]; } status = vdrive_rel_open(vdrive, secondary, cmd_parse, name); goto out; } if (cmd_parse->readmode == CBMDOS_FAM_READ) { status = iec_open_read(vdrive, secondary); } else { status = iec_open_write(vdrive, secondary, cmd_parse, name); } out: lib_free(cmd_parse->parsecmd); return status; }
static int iec_open_write(vdrive_t *vdrive, unsigned int secondary, cbmdos_cmd_parse_t *cmd_parse, const BYTE *name) { bufferinfo_t *p = &(vdrive->buffers[secondary]); unsigned int track, sector; BYTE *slot = p->slot, *e; if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) { vdrive_command_set_error(vdrive, CBMDOS_IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } /* set flag for overwrite mode */ p->needsupdate = 0; if (slot) { /* file exists */ if (*name == '@') { /* replace mode: we don't want the dirent updated at all until close */ /* allocate buffers */ vdrive_alloc_buffer(p, BUFFER_SEQUENTIAL); p->bufptr = 2; /* Create our own slot, since the one passed is static */ p->slot = lib_calloc(1, 32); /* Copy the static on to the new one. */ memcpy(p->slot, slot, 32); slot = p->slot; /* set flag for replace mode */ p->needsupdate = 1; /* find a new track and sector when writing */ p->track = p->sector = 0; } else { if (p->readmode == CBMDOS_FAM_APPEND) { /* append mode */ /* allocate buffers */ vdrive_alloc_buffer(p, BUFFER_SEQUENTIAL); /* Create our own slot, since the one passed is static */ p->slot = lib_calloc(1, 32); /* Copy the static on to the new one. */ memcpy(p->slot, slot, 32); slot = p->slot; /* set file unclosed */ p->slot[SLOT_TYPE_OFFSET] &= 0x7f; /* get the starting track and sector */ p->track = track = slot[SLOT_FIRST_TRACK]; p->sector = sector = slot[SLOT_FIRST_SECTOR]; /* update block count as we find the end of the file */ /* the real drives actually don't do this, so each time you append to a file, the block count increases by 1. I think it is safer to correct the block count. */ slot[SLOT_NR_BLOCKS] = 255; slot[SLOT_NR_BLOCKS + 1] = 255; /* scan to the end of the file */ while (track) { p->track = track; p->sector = sector; if (vdrive_read_sector(vdrive, p->buffer, p->track, p->sector)) { /* couldn't read sector, report error and leave */ vdrive_free_buffer(p); vdrive_command_set_error(vdrive, CBMDOS_IPE_ILLEGAL_TRACK_OR_SECTOR, p->track, p->sector); return SERIAL_ERROR; } /* setup next link */ track = p->buffer[0]; sector = p->buffer[1]; /* Increment block count. */ if (!(++slot[SLOT_NR_BLOCKS])) { ++slot[SLOT_NR_BLOCKS + 1]; } } /* compensate if the dir link is 0 (rare possibility) */ if (!p->track) { /* Our loop didn't even execute once, set the block size to 0 */ slot[SLOT_NR_BLOCKS] = 0; slot[SLOT_NR_BLOCKS + 1] = 0; /* set buffer pointer to 2 */ sector = 1; } /* set the buffer pointer */ p->bufptr = sector + 1; } else { /* can't overwrite an existing file */ vdrive_iec_close(vdrive, secondary); vdrive_command_set_error(vdrive, CBMDOS_IPE_FILE_EXISTS, 0, 0); return SERIAL_ERROR; } } } else { /* new file... */ /* create a slot based on the opening name */ vdrive_dir_create_slot(p, cmd_parse->parsecmd, cmd_parse->parselength, cmd_parse->filetype); /* Write the directory entry to disk as an UNCLOSED file. */ vdrive_dir_find_first_slot(vdrive, NULL, -1, 0, &p->dir); e = vdrive_dir_find_next_slot(&p->dir); /* If there is not space for the slot, disk is full */ if (!e) { vdrive_free_buffer(p); vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return SERIAL_ERROR; } /* find a new track and sector when writing */ p->track = p->sector = 0; } if (!p->needsupdate) { /* copy the slot information into the sector. */ memcpy(&p->dir.buffer[p->dir.slot * 32 + 2], p->slot + 2, 30); /* Write the sector. */ vdrive_write_sector(vdrive, p->dir.buffer, p->dir.track, p->dir.sector); } return SERIAL_OK; }
static int vdrive_rel_open_new(vdrive_t *vdrive, unsigned int secondary, cbmdos_cmd_parse_t *cmd_parse, const BYTE *name) { bufferinfo_t *p = &(vdrive->buffers[secondary]); BYTE *slot; /* Allocate a directory slot */ vdrive_dir_find_first_slot(vdrive, NULL, -1, 0); slot = vdrive_dir_find_next_slot(vdrive); /* If we can't get one the directory is full - disk full */ if (!slot) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return 1; } /* Create our own slot */ p->slot = lib_calloc(1, 32); /* Populate it */ memset(p->slot + SLOT_NAME_OFFSET, 0xa0, 16); memcpy(p->slot + SLOT_NAME_OFFSET, cmd_parse->parsecmd, cmd_parse->parselength); #ifdef DEBUG_DRIVE log_debug("DIR: Created dir slot. Name (%d) '%s'\n", cmd_parse->parsecmd, cmd_parse->parselength); #endif p->slot[SLOT_TYPE_OFFSET] = cmd_parse->filetype | 0x80; /* closed */ p->slot[SLOT_RECORD_LENGTH] = cmd_parse->recordlength; /* Store in buffer */ memcpy(&(vdrive->Dir_buffer[vdrive->SlotNumber * 32 + 2]), p->slot + 2, 30); #ifdef DEBUG_DRIVE log_debug("DEBUG: write DIR slot (%d %d).", vdrive->Curr_track, vdrive->Curr_sector); #endif /* Write the sector */ disk_image_write_sector(vdrive->image, vdrive->Dir_buffer, vdrive->Curr_track, vdrive->Curr_sector); /* Allocate memory for super side sector */ p->super_side_sector = lib_malloc(256); /* clear out block */ memset(p->super_side_sector, 0, 256); /* build valid super side sector block */ p->super_side_sector[OFFSET_SUPER_254] = 254; p->super_side_sector_track = 0; p->super_side_sector_sector = 0; /* Clear dirty flag */ p->super_side_sector_needsupdate = 0; /* allocate memory for side sectors */ p->side_sector = lib_malloc(SIDE_SECTORS_MAX * 256); memset(p->side_sector, 0, SIDE_SECTORS_MAX * 256); /* Also clear out track and sectors locations and dirty flag of each side sector */ p->side_sector_track = lib_malloc(SIDE_SECTORS_MAX); p->side_sector_sector = lib_malloc(SIDE_SECTORS_MAX); p->side_sector_needsupdate = lib_malloc(SIDE_SECTORS_MAX); memset(p->side_sector_track, 0, SIDE_SECTORS_MAX); memset(p->side_sector_sector, 0, SIDE_SECTORS_MAX); memset(p->side_sector_needsupdate, 0, SIDE_SECTORS_MAX); /* Remember the directory information incase our REL file grows. */ p->dir_track = vdrive->Curr_track; p->dir_sector = vdrive->Curr_sector; p->dir_slot = vdrive->SlotNumber; /* Everything okay */ return 0; }
/* FIXME: partition support */ int vdrive_command_validate(vdrive_t *vdrive) { unsigned int t, s; int status, max_sector; BYTE *b, oldbam[BAM_MAXSIZE]; status = vdrive_command_initialize(vdrive); if (status != CBMDOS_IPE_OK) return status; if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) return CBMDOS_IPE_WRITE_PROTECT_ON; memcpy(oldbam, vdrive->bam, vdrive->bam_size); vdrive_bam_clear_all(vdrive->image_format, vdrive->bam); for (t = 1; t <= vdrive->num_tracks; t++) { max_sector = vdrive_get_max_sectors(vdrive, t); for (s = 0; s < (unsigned int)max_sector; s++) { vdrive_bam_free_sector(vdrive->image_format, vdrive->bam, t, s); } } /* First, map out the header (BAM) and the directory, themselves. */ status = vdrive_bam_allocate_chain(vdrive, vdrive->Bam_Track, vdrive->Bam_Sector); if (status != CBMDOS_IPE_OK) { memcpy(vdrive->bam, oldbam, vdrive->bam_size); return status; } switch (vdrive->image_format) { case VDRIVE_IMAGE_FORMAT_1571: /* Map the opposite side of the directory cylinder. */ max_sector = vdrive_get_max_sectors(vdrive, 53); for (s = 0; s < (unsigned int)max_sector; s++) { vdrive_bam_allocate_sector(vdrive->image_format, vdrive->bam, 53, s); } break; case VDRIVE_IMAGE_FORMAT_1581: /* Map the BAM sectors. */ vdrive_bam_allocate_sector(vdrive->image_format, vdrive->bam, vdrive->Bam_Track, vdrive->Bam_Sector + 1); vdrive_bam_allocate_sector(vdrive->image_format, vdrive->bam, vdrive->Bam_Track, vdrive->Bam_Sector + 2); break; case VDRIVE_IMAGE_FORMAT_4000: /* Map the boot sector. */ vdrive_bam_allocate_sector(vdrive->image_format, vdrive->bam, 1, 0); /* Map the BAM sectors. */ for (s = 2; s < 34; s++) vdrive_bam_allocate_sector(vdrive->image_format, vdrive->bam, 1, s); break; } vdrive_dir_find_first_slot(vdrive, "*", 1, 0); while ((b = vdrive_dir_find_next_slot(vdrive))) { char *filetype = (char *) &vdrive->Dir_buffer[vdrive->SlotNumber * 32 + SLOT_TYPE_OFFSET]; if (*filetype & CBMDOS_FT_CLOSED) { status = vdrive_bam_allocate_chain(vdrive, b[SLOT_FIRST_TRACK], b[SLOT_FIRST_SECTOR]); if (status != CBMDOS_IPE_OK) { memcpy(vdrive->bam, oldbam, vdrive->bam_size); return status; } /* The real drive always validates side sectors even if the file type is not REL. */ status = vdrive_bam_allocate_chain(vdrive, b[SLOT_SIDE_TRACK], b[SLOT_SIDE_SECTOR]); if (status != CBMDOS_IPE_OK) { memcpy(vdrive->bam, oldbam, vdrive->bam_size); return status; } } else { /* Delete an unclosed file. */ *filetype = CBMDOS_FT_DEL; if (vdrive_write_sector(vdrive, vdrive->Dir_buffer, vdrive->Curr_track, vdrive->Curr_sector) < 0) return CBMDOS_IPE_WRITE_ERROR_VER; } } /* Write back BAM only if validate was successful. */ vdrive_bam_write_bam(vdrive); return status; }
/* CBM style sub partition support (using CBM filetype) on 1581 dos command "/dirname" enters a partition, "i" will go back to root FIXME: this works only for .d81 */ static int vdrive_command_chpart(vdrive_t *vdrive, BYTE *name, int length) { int status, rc; int ts,ss,te,len; BYTE *slot, buffer[256]; cbmdos_cmd_parse_t cmd_parse; cmd_parse.cmd = name; cmd_parse.cmdlength = length; cmd_parse.readmode = 0; rc = cbmdos_command_parse(&cmd_parse); if (rc != SERIAL_OK) { status = CBMDOS_IPE_NO_NAME; } else { /*#ifdef DEBUG_DRIVE*/ log_debug("chpart name='%s', len=%d (%d), type= %d.", cmd_parse.parsecmd, cmd_parse.parselength, length, cmd_parse.filetype); /*#endif*/ vdrive_dir_find_first_slot(vdrive, cmd_parse.parsecmd, cmd_parse.parselength, CBMDOS_FT_CBM); slot = vdrive_dir_find_next_slot(vdrive); status = CBMDOS_IPE_BAD_PARTN; /* FIXME: is this correct ? */ if (slot) { slot = &vdrive->Dir_buffer[vdrive->SlotNumber * 32]; /* In order to use a partition as a sub-directory, it must adhere to the following four rules: 1. It must start on sector 0 2. It's size must be in multiples of 40 sectors (which means the last sector is 39) 3. It must be a minimum of 120 sectors long (3 tracks) 4. It must not start on or cross track 40 */ ts = slot[SLOT_FIRST_TRACK]; ss = slot[SLOT_FIRST_SECTOR]; len = slot[SLOT_NR_BLOCKS] + (slot[SLOT_NR_BLOCKS + 1] * 256); if ((ss == 0) && ((len % 40) == 0) && (len >= 120) && (ts != 40)) { te = ts + (len / 40); if (((ts < 40) && (te >= 40)) || (te >= (int)vdrive->num_tracks)) { return CBMDOS_IPE_BAD_PARTN; /* FIXME: is this correct ? */ } /* read the first BAM sector to get the DIR start The BAM track for the sub-directory exists on the first track of the partition, and has the same layout as the disk BAM on track 40. */ rc = vdrive_read_sector(vdrive, buffer, ts, 0); if (rc > 0) { return rc; } if (rc < 0) { return CBMDOS_IPE_NOT_READY; } /* more sanity checks */ if ((buffer[0] < ts) || (buffer[1] > 39)) { return CBMDOS_IPE_BAD_PARTN; /* FIXME: is this correct ? */ } /*#ifdef DEBUG_DRIVE*/ log_debug("Partition Trk %d Sec %d - Trk %d len: %d", ts, ss, te, len); /*#endif*/ /* setup BAM location */ vdrive->Header_Track = ts; vdrive->Header_Sector = 0; vdrive->Bam_Track = ts; vdrive->Bam_Sector = 0; /* set area for active partition */ vdrive->Part_Start = ts; vdrive->Part_End = te; /* start of directory */ vdrive->Dir_Track = buffer[0]; vdrive->Dir_Sector = buffer[1]; status = CBMDOS_IPE_OK; } } } vdrive_command_set_error(vdrive, status, 0, 0); lib_free(cmd_parse.parsecmd); return status; }
static int vdrive_command_rename(vdrive_t *vdrive, BYTE *dest, int length) { BYTE *src; BYTE *slot; int status = CBMDOS_IPE_OK, rc; cbmdos_cmd_parse_t cmd_parse_dst, cmd_parse_src; if (!dest || !(src = memchr((char *)++dest, '=', length)) ) { return CBMDOS_IPE_SYNTAX; } *src++ = 0; #ifdef DEBUG_DRIVE log_debug("RENAME: dest= '%s', orig= '%s'.", dest, src); #endif cmd_parse_dst.cmd = dest; cmd_parse_dst.cmdlength = (unsigned int)strlen((char *)dest); cmd_parse_dst.readmode = CBMDOS_FAM_READ; rc = cbmdos_command_parse(&cmd_parse_dst); if (rc == FLOPPY_ERROR) { status = CBMDOS_IPE_SYNTAX; goto out1; } cmd_parse_src.cmd = src; cmd_parse_src.cmdlength = (unsigned int)strlen((char *)src); cmd_parse_src.readmode = CBMDOS_FAM_READ; rc = cbmdos_command_parse(&cmd_parse_src); if (rc == FLOPPY_ERROR) { status = CBMDOS_IPE_SYNTAX; goto out2; } if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) { status = CBMDOS_IPE_WRITE_PROTECT_ON; goto out2; } /* Check if the destination name is already in use. */ vdrive_dir_find_first_slot(vdrive, cmd_parse_dst.parsecmd, cmd_parse_dst.parselength, cmd_parse_dst.filetype); slot = vdrive_dir_find_next_slot(vdrive); if (slot) { status = CBMDOS_IPE_FILE_EXISTS; goto out2; } /* Find the file to rename. */ vdrive_dir_find_first_slot(vdrive, cmd_parse_src.parsecmd, cmd_parse_src.parselength, cmd_parse_src.filetype); slot = vdrive_dir_find_next_slot(vdrive); if (!slot) { status = CBMDOS_IPE_NOT_FOUND; goto out2; } /* Now we can replace the old file name... */ /* We write directly to the Dir_buffer. */ slot = &vdrive->Dir_buffer[vdrive->SlotNumber * 32]; memset(slot + SLOT_NAME_OFFSET, 0xa0, 16); memcpy(slot + SLOT_NAME_OFFSET, cmd_parse_dst.parsecmd, cmd_parse_dst.parselength); /* FIXME: is this right? */ if (cmd_parse_dst.filetype) slot[SLOT_TYPE_OFFSET] = cmd_parse_dst.filetype; /* Update the directory. */ if (vdrive_write_sector(vdrive, vdrive->Dir_buffer, vdrive->Curr_track, vdrive->Curr_sector) < 0) status = CBMDOS_IPE_WRITE_ERROR_VER; out2: lib_free(cmd_parse_src.parsecmd); out1: lib_free(cmd_parse_dst.parsecmd); return status; }
int vdrive_dir_create_directory(vdrive_t *vdrive, const char *name, int length, int filetype, BYTE *outputptr) { BYTE *l, *p; BYTE *origptr = outputptr; int blocks, 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; *l++ = 1; *l++ = 4; l += 2; /* Leave space for Next Line Link */ *l++ = 0; *l++ = 0; *l++ = (BYTE)0x12; /* Reverse on */ *l++ = '"'; memcpy(l, &vdrive->bam[vdrive->bam_name], 16); vdrive_dir_no_a0_pads(l, 16); l += 16; *l++ = '"'; *l++ = ' '; memcpy(l, &vdrive->bam[vdrive->bam_id], 5); vdrive_dir_no_a0_pads(l, 5); l += 5; *l++ = 0; /* * Pointer to the next line */ outputptr[2] = 1; outputptr[3] = 1; outputptr = l; /* * Now, list files that match the pattern. * Wildcards can be used too. */ vdrive_dir_find_first_slot(vdrive, name, length, filetype); while ((p = vdrive_dir_find_next_slot(vdrive))) { 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) { log_error(vdrive_dir_log, "Directory too long: giving up."); 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++; vdrive_dir_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] & CBMDOS_FT_CLOSED ? ' ' : '*'), cbmdos_filetype_get(p[SLOT_TYPE_OFFSET] & 0x07), (p[SLOT_TYPE_OFFSET] & CBMDOS_FT_LOCKED ? '<' : ' '), 0); l += 5; i = (int)(l - tl); while (i < 31) { *l++ = ' '; i++; } *l++ = '\0'; /* * New address */ outputptr[0] = 1; outputptr[1] = 1; outputptr = l; } } blocks = vdrive_bam_free_block_count(vdrive); *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 */ outputptr[0] = 1; outputptr[1] = 1; /* * end */ *l++ = (char) 0; *l++ = (char) 0; *l = (char) 0; return (int)(l - origptr); }