int vdrive_bam_allocate_chain(vdrive_t *vdrive, unsigned int t, unsigned int s) { BYTE tmp[256]; int rc; while (t) { /* Check for illegal track or sector. */ if (disk_image_check_sector(vdrive->image, t, s) < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_ILLEGAL_TRACK_OR_SECTOR, s, t); return CBMDOS_IPE_ILLEGAL_TRACK_OR_SECTOR; } if (!vdrive_bam_allocate_sector(vdrive, t, s)) { /* The real drive does not seem to catch this error. */ vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_BLOCK, s, t); return CBMDOS_IPE_NO_BLOCK; } rc = vdrive_read_sector(vdrive, tmp, t, s); if (rc > 0) { return rc; } if (rc < 0) { return CBMDOS_IPE_NOT_READY; } t = (int)tmp[0]; s = (int)tmp[1]; } return CBMDOS_IPE_OK; }
int vdrive_iec_close(vdrive_t *vdrive, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); int status = SERIAL_OK; switch (p->mode) { case BUFFER_NOT_IN_USE: return SERIAL_OK; /* FIXME: Is this correct? */ case BUFFER_MEMORY_BUFFER: case BUFFER_DIRECTORY_READ: vdrive_free_buffer(p); p->slot = NULL; break; case BUFFER_SEQUENTIAL: status = iec_close_sequential(vdrive, secondary); break; case BUFFER_RELATIVE: status = vdrive_rel_close(vdrive, secondary); 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! */ vdrive_command_set_error(vdrive, CBMDOS_IPE_OK, 0, 0); /* this breaks any rel file access if the command channel is closed when the record is changed. Removed for now.*/ /* vdrive_close_all_channels(vdrive); */ break; default: log_error(vdrive_iec_log, "Fatal: unknown floppy-close-mode: %i.", p->mode); } return status; }
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; }
static int iec_close_sequential(vdrive_t *vdrive, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); unsigned int track = 0, sector = 0; if (p->readmode & (CBMDOS_FAM_WRITE | CBMDOS_FAM_APPEND)) { /* * Flush bytes and write slot to directory */ 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; } #ifdef DEBUG_DRIVE log_debug("DEBUG: flush."); #endif /* Flush remained of file */ iec_write_sequential(vdrive, p, p->bufptr); /* Set the file as closed */ p->slot[SLOT_TYPE_OFFSET] |= 0x80; /* Closed */ /* is this a save and replace? */ if (p->needsupdate) { /* remember the original track and sector */ track = p->slot[SLOT_FIRST_TRACK]; sector = p->slot[SLOT_FIRST_SECTOR]; /* move over the replacement track and sector */ p->slot[SLOT_FIRST_TRACK] = p->slot[SLOT_REPLACE_TRACK]; p->slot[SLOT_FIRST_SECTOR] = p->slot[SLOT_REPLACE_SECTOR]; /* set replacement track and sector to 0 */ p->slot[SLOT_REPLACE_TRACK] = 0; p->slot[SLOT_REPLACE_SECTOR] = 0; } /* Update the directory entry (block count, closed) */ vdrive_iec_update_dirent(vdrive, secondary); /* if we have a track and sector saved */ if (track) { /* remove the original file */ vdrive_dir_free_chain(vdrive, track, sector); } /* Update BAM */ vdrive_bam_write_bam(vdrive); /* Free up the slot */ lib_free(p->slot); } /* Release buffers */ vdrive_free_buffer(p); return SERIAL_OK; }
int vdrive_rel_open(vdrive_t *vdrive, unsigned int secondary, cbmdos_cmd_parse_t *cmd_parse, const BYTE *name) { bufferinfo_t *p = &(vdrive->buffers[secondary]); int newrelfile = 0; if (p->slot) { //log_debug("Open existing REL file '%s' with record length %i on channel %d.", name, cmd_parse->recordlength, secondary); /* Follow through to function. */ vdrive_rel_open_existing(vdrive, secondary); } else { //log_debug("Open new REL file '%s' with record length %i on channel %d.", name, cmd_parse->recordlength, secondary); /* abort if we are in read only mode */ if (vdrive->image->read_only) { vdrive_command_set_error(vdrive, CBMDOS_IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } /* Call function to open a new REL file, leave if error exists. */ if (vdrive_rel_open_new(vdrive, secondary, cmd_parse, name)) { return SERIAL_ERROR; } /* set a flag so we can expand the rel file to 1 record later. */ newrelfile++; } /* Allocate dual buffers to improve performance */ p->mode = BUFFER_RELATIVE; p->bufptr = 0; p->buffer = lib_malloc(256); p->record = 0; p->track = 0; p->sector = 0; p->buffer_next = lib_malloc(256); p->track_next = 0; p->sector_next = 0; /* Determine maximum record */ p->record_max = vdrive_rel_record_max(vdrive, secondary); /* If this is a new REL file, have atleast 1 record. */ if (newrelfile) { vdrive_rel_grow(vdrive, secondary, 0); } /* Move to first record, no offset */ vdrive_rel_position(vdrive, secondary, 1, 0, 1); return SERIAL_OK; }
/* 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; }
int vdrive_device_setup(vdrive_t *vdrive, unsigned int unit) { unsigned int i; vdrive->unit = unit; for (i = 0; i < 15; i++) vdrive->buffers[i].mode = BUFFER_NOT_IN_USE; vdrive->buffers[15].mode = BUFFER_COMMAND_CHANNEL; if (vdrive->buffers[15].buffer == NULL) vdrive->buffers[15].buffer = lib_malloc(256); memset(vdrive->buffers[15].buffer, 0, 256); vdrive_command_set_error(vdrive, CBMDOS_IPE_DOS_VERSION, 0, 0); return 0; }
int vdrive_device_setup(vdrive_t *vdrive, unsigned int unit) { unsigned int i; vdrive->unit = unit; /* init buffers */ for (i = 0; i < 15; i++) { vdrive->buffers[i].mode = BUFFER_NOT_IN_USE; vdrive->buffers[i].buffer = NULL; } /* init command channel */ vdrive_alloc_buffer(&(vdrive->buffers[15]), BUFFER_COMMAND_CHANNEL); vdrive_command_set_error(vdrive, CBMDOS_IPE_DOS_VERSION, 0, 0); return 0; }
static int iec_open_read(vdrive_t *vdrive, unsigned int secondary) { int type; unsigned int track, sector; bufferinfo_t *p = &(vdrive->buffers[secondary]); BYTE *slot = p->slot; if (!slot) { vdrive_iec_close(vdrive, secondary); vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_FOUND, 0, 0); return SERIAL_ERROR; } type = slot[SLOT_TYPE_OFFSET] & 0x07; track = (unsigned int)slot[SLOT_FIRST_TRACK]; sector = (unsigned int)slot[SLOT_FIRST_SECTOR]; /* Del, Seq, Prg, Usr (Rel not supported here). */ if (type != CBMDOS_FT_REL) return iec_open_read_sequential(vdrive, secondary, track, sector); return SERIAL_ERROR; }
static int vdrive_rel_add_sector(vdrive_t *vdrive, unsigned int secondary, unsigned int *track, unsigned int *sector) { bufferinfo_t *p = &(vdrive->buffers[secondary]); unsigned int i, j, k, l, m, side, o; unsigned int t_new, s_new, t_super, s_super, current; int retval; BYTE *slot = p->slot; /* Find the total blocks this REL file uses. */ i = slot[SLOT_NR_BLOCKS] + (slot[SLOT_NR_BLOCKS + 1] << 8); /* Compare it with the maximum for the particular disk image. */ if (i >= vdrive_rel_blocks_max(vdrive)) { /* If we are equal or over, report error 52 */ vdrive_command_set_error(vdrive, CBMDOS_IPE_TOOLARGE, 0, 0); return 1; } /* find the number of side sector groups */ o = OFFSET_SUPER_POINTER; for (side = 0; side < SIDE_SUPER_MAX && p->super_side_sector[o] != 0 ; side++, o+=2 ); /* If the file isn't new, find the last record */ if ( side ) { side--; /* Find last side sector; guaranteed find. */ o = 256 * ( side * SIDE_SECTORS_MAX ) + OFFSET_NEXT_TRACK; for (i = 0; i < SIDE_SECTORS_MAX; i++, o+=256 ) { if (p->side_sector[o] == 0) break; } /* obtain the last byte of the sector according to the index */ j = ( p->side_sector[256 * ( i + side * SIDE_SECTORS_MAX) + OFFSET_NEXT_SECTOR ] + 1 - OFFSET_POINTER ) / 2; /* Get the track and sector of the last REL block */ o = 256 * ( i + side * SIDE_SECTORS_MAX) + OFFSET_POINTER + 2 * (j-1); *track = p->side_sector[o]; *sector = p->side_sector[o + 1]; /* Find a new sector */ retval = vdrive_bam_alloc_next_free_sector(vdrive, vdrive->bam, track, sector); } else { /* New REL file, get ready to create... */ i = 0; j = 0; *track = 0; *sector = 0; /* Find the first new sector */ retval = vdrive_bam_alloc_first_free_sector(vdrive, vdrive->bam, track, sector); } /* Leave if no space left */ if (retval < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return 1; } /* Check if this side sector is full or if we need on in general. */ if ( j == SIDE_INDEX_MAX || j == 0 ) { /* Allocate a new sector for the new side sector. */ t_new = *track; s_new = *sector; retval = vdrive_bam_alloc_next_free_sector(vdrive, vdrive->bam, &t_new, &s_new); /* If no space, leave with no changes */ if (retval < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return 1; } /* We will adjust the side sector later, but atleast we know we have a place to make adjustments. */ } /* setup for later */ k = 0; m = p->slot[SLOT_RECORD_LENGTH]; /* remember old current record, we will need it for later. */ current = p->record + 1; /* If this is a unallocated file... */ if ( j == 0 ) { /* Update slot information if this is our first side sector. */ p->slot[SLOT_FIRST_TRACK] = *track; p->slot[SLOT_FIRST_SECTOR] = *sector; /* Update the super side sector */ p->super_side_sector[OFFSET_NEXT_TRACK] = t_new; p->super_side_sector[OFFSET_NEXT_SECTOR] = s_new; p->super_side_sector[OFFSET_SUPER_254] = 254; p->super_side_sector[OFFSET_SUPER_POINTER] = t_new; p->super_side_sector[OFFSET_SUPER_POINTER+1] = s_new; p->super_side_sector_needsupdate = 1; t_super = t_new; s_super = s_new; /* Does this image require a real super side sector? */ if (vdrive_rel_has_super(vdrive)) { retval = vdrive_bam_alloc_next_free_sector(vdrive, vdrive->bam, &t_super, &s_super); /* If no space, leave with no changes */ if (retval < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return 1; } p->super_side_sector_track = t_super; p->super_side_sector_sector = s_super; } else { /* No super side sector required for this image */ p->super_side_sector_track = 0; p->super_side_sector_sector = 0; } p->slot[SLOT_SIDE_TRACK] = t_super; p->slot[SLOT_SIDE_SECTOR] = s_super; /* set up first data block */ p->track_next = *track; p->sector_next = *sector; /* Update the directory entry */ vdrive_iec_update_dirent(vdrive, secondary); } else { /* Existing... */ /* Move to last sector, use position command - this will flush any dirty buffers too. */ vdrive_rel_position(vdrive, secondary, p->record_max & 255, p->record_max >> 8, 1); /* Modify this sector to connect to the next one. */ /* We won't use the pointer in this sector for the last used byte as it could very well be wrong. The 1541/71/81 had a tendency to mess the last sector when making REL files by expanding in small chunks. This generally would happen when a new side sector was created. I can generate a case where this bug happens, but I can't seem to figure out why - yet. */ p->buffer[OFFSET_NEXT_TRACK] = p->track_next = *track; p->buffer[OFFSET_NEXT_SECTOR] = p->sector_next = *sector; /* Fill the new records up with the default 0xff 0x00 ... */ o = p->bufptr + m; while (o < 256) { if (k==0) p->buffer[o] = 0xff; else p->buffer[o] = 0x00; k = ( k + 1 ) % m; /* increment the maximum records each time we complete a full record. */ if (k==0) p->record_max++; o++; } /* flag the current sector as dirty - the other functions will update it. */ p->needsupdate = DIRTY_SECTOR; } /* Fill new sector with maximum records */ o = 2; while (o < 256) { if (k==0) p->buffer_next[o] = 0xff; else p->buffer_next[o] = 0x00; k = ( k + 1 ) % m; /* increment the maximum records each time we complete a full record. */ if (k==0) p->record_max++; o++; } /* set as last sector in REL file */ p->buffer_next[OFFSET_NEXT_TRACK] = 0; /* Update the last byte based on how much of the last record we filled. */ p->buffer_next[OFFSET_NEXT_SECTOR] = 255 - k; /* update the "next" buffer to disk, we don't have a dirty flag for it. */ disk_image_write_sector(vdrive->image, p->buffer_next, p->track_next, p->sector_next); /* If this is the first side sector being made */ if ( j == 0 ) { /* Build some of the structure */ p->side_sector[OFFSET_NEXT_TRACK] = 0; p->side_sector[OFFSET_RECORD_LEN] = m; p->side_sector[OFFSET_SIDE_SECTOR] = t_new; p->side_sector[OFFSET_SIDE_SECTOR + 1] = s_new; p->side_sector_track[0] = t_new; p->side_sector_sector[0] = s_new; /* Follow through with the "else" of the next if */ } /* If this side sector is full... */ if ( j == SIDE_INDEX_MAX ) { /* Update previous side sector */ o = ( i + side * SIDE_SECTORS_MAX); /* dirty the side sector. */ p->side_sector_needsupdate[ o ] = 1; o *= 256; /* Update the link. */ p->side_sector[o + OFFSET_NEXT_TRACK] = t_new; p->side_sector[o + OFFSET_NEXT_SECTOR] = s_new; /* Is this the last side sector of a group in a super side sector? */ if ( i == SIDE_SECTORS_MAX - 1 ) { /* Yes, create a new group. */ /* correct side reference. */ side++; /* reallocate memory for another side sector group */ o = SIDE_SECTORS_MAX * 256; p->side_sector = lib_realloc(p->side_sector, ( side + 1 ) * o ); /* clear out new portion, the function may not do this */ memset(&(p->side_sector[side * o]), 0, o); /* Also reallocate and clear out new sections of track and sectors locations and dirty flag of each side sector */ o = ( side + 1 ) * SIDE_SECTORS_MAX; p->side_sector_track = lib_realloc(p->side_sector_track, o); p->side_sector_sector = lib_realloc(p->side_sector_sector, o); p->side_sector_needsupdate = lib_realloc(p->side_sector_needsupdate, o); o = side * SIDE_SECTORS_MAX; memset(&(p->side_sector_track[o]), 0, SIDE_SECTORS_MAX); memset(&(p->side_sector_sector[o]), 0, SIDE_SECTORS_MAX); memset(&(p->side_sector_needsupdate[o]), 0, SIDE_SECTORS_MAX); /* Create first side sector of new group */ o *=256; p->side_sector[o + OFFSET_SIDE_SECTOR ] = t_new; p->side_sector[o + OFFSET_SIDE_SECTOR + 1] = s_new; p->side_sector[o + OFFSET_SECTOR_NUM] = 0; /* Adjust the super side sector */ o = OFFSET_SUPER_POINTER + side * 2; p->super_side_sector[o] = t_new; p->super_side_sector[o+1] = s_new; p->super_side_sector_needsupdate = 1; /* Set up reference to the first side sector. */ o = ( side * SIDE_SECTORS_MAX); } else { /* Nope, update old group. */ /* Update side sector indices. */ l = o = 256 * ( side * SIDE_SECTORS_MAX ); for (k=0; k<=i; k++, o+=256) { p->side_sector[o + OFFSET_SIDE_SECTOR + ( i + 1 ) * 2 ] = t_new; p->side_sector[o + OFFSET_SIDE_SECTOR + ( i + 1 ) * 2 + 1] = s_new; } /* Update the new sector */ p->side_sector[o + OFFSET_SECTOR_NUM] = i+1; /* Copy side sector track and sectors from first side sector. */ for ( k=0;k<SIDE_SECTORS_MAX * 2;k++ ) { p->side_sector[o + OFFSET_SIDE_SECTOR + k ] = p->side_sector[l + OFFSET_SIDE_SECTOR + k ]; } o = ( side * SIDE_SECTORS_MAX ); /* Mark all of other side sectors as dirty. */ for ( k=0; k<=i; k++, o++) { p->side_sector_needsupdate[o] = 1; } /* Set up the reference to the new side sector. */ o = ( (i+1) + side * SIDE_SECTORS_MAX); } /* dirty the side sector. */ p->side_sector_needsupdate[ o ] = 1; /* update the internal references. */ p->side_sector_track[ o ] = t_new; p->side_sector_sector[ o ] = s_new; /* Update the side sector contents. */ o *= 256; p->side_sector[o + OFFSET_NEXT_TRACK] = 0; p->side_sector[o + OFFSET_NEXT_SECTOR] = OFFSET_POINTER + 1; p->side_sector[o + OFFSET_RECORD_LEN] = m; p->side_sector[o + OFFSET_POINTER] = *track; p->side_sector[o + OFFSET_POINTER + 1] = *sector; } else { /* Update last side sector with new data. */ o = ( i + side * SIDE_SECTORS_MAX ); /* set sector dirty. */ p->side_sector_needsupdate[ o ] = 1; /* update last byte used. */ o *= 256; p->side_sector[ o + OFFSET_NEXT_SECTOR ] = OFFSET_POINTER + 2 * j + 1; o+= OFFSET_POINTER + 2 * j; /* update track and sector of data. */ p->side_sector[o] = *track; p->side_sector[o + 1] = *sector; } /* Move back to original record - it may not even exist. */ vdrive_rel_position(vdrive, secondary, current & 255, current >> 8, 1); /* everything is okay. */ return 0; }
/* 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 iec_write_sequential(vdrive_t *vdrive, bufferinfo_t *bi, int length) { unsigned int t_new, s_new; int retval; BYTE *buf = bi->buffer; BYTE *slot = bi->slot; /* * First block of a file ? */ if (bi->track == 0) { /* allocate the first sector */ retval = vdrive_bam_alloc_first_free_sector(vdrive, &t_new, &s_new); if (retval < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return -1; } /* remember track and sector */ bi->track = t_new; bi->sector = s_new; /* use update flag to indicate replace mode */ if (bi->needsupdate) { /* save and replace */ slot[SLOT_REPLACE_TRACK] = t_new; slot[SLOT_REPLACE_SECTOR] = s_new; } else { /* new file */ slot[SLOT_FIRST_TRACK] = t_new; slot[SLOT_FIRST_SECTOR] = s_new; } /* reset block counter */ slot[SLOT_NR_BLOCKS] = 0; slot[SLOT_NR_BLOCKS + 1] = 0; } if (length == WRITE_BLOCK) { /* * Write current sector and allocate next */ t_new = bi->track; s_new = bi->sector; retval = vdrive_bam_alloc_next_free_sector(vdrive, &t_new, &s_new); if (retval < 0) { vdrive_command_set_error(vdrive, CBMDOS_IPE_DISK_FULL, 0, 0); return -1; } buf[0] = t_new; buf[1] = s_new; vdrive_write_sector(vdrive, buf, bi->track, bi->sector); bi->track = t_new; bi->sector = s_new; } else { /* * Write last block */ buf[0] = 0; buf[1] = length - 1; vdrive_write_sector(vdrive, buf, bi->track, bi->sector); } /* Increment block count. */ if (!(++slot[SLOT_NR_BLOCKS])) { ++slot[SLOT_NR_BLOCKS + 1]; } return 0; }
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 unsigned int vdrive_rel_record_max(vdrive_t *vdrive, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); unsigned int i, j, k, l, side, o; unsigned int track, sector; /* The original 1541 documentation states that there can only be 720 (6*120) records in a REL file - this is incorrect. It is actually blocks. The side sectors point to each block in the REL file. It is up to the drive to determine the where the buffer pointer should be when the record changes. */ /* To find the maximum records we have to check the last block of the REL file. The last block is referenced in the last side sector group by the OFFSET_NEXT_SECTOR byte. The next sector pointer after this one should be zero - we will check anyways to make sure. Once the last block is found, we can move to that sector and check the used bytes there. */ /* The maximum REL file size for a 1541/1571 is 700 blocks, although it is documented to be 720. The 1581 is limited to 3000 blocks, although it is documented to be 90720 (126*6*120). You think they would have allowed it to be atleast the maximum amount of disk space. */ /* find the number of side sector groups */ o = OFFSET_SUPER_POINTER; for (side = 0; side < SIDE_SUPER_MAX && p->super_side_sector[o] != 0 ; side++, o+=2 ); /* check if this is a NEW rel file - no data in super side sector. */ if (!side) { /* return a maximum of 0. */ return 0; } side--; /* Find last side sector; guaranteed find. */ o = 256 * ( side * SIDE_SECTORS_MAX ) + OFFSET_NEXT_TRACK; for (i = 0; i < SIDE_SECTORS_MAX; i++, o+=256 ) { if (p->side_sector[o] == 0) break; } /* obtain the last byte of the sector according to the index */ j = ( p->side_sector[256 * ( i + side * SIDE_SECTORS_MAX) + OFFSET_NEXT_SECTOR ] + 1 - OFFSET_POINTER ) / 2; /* Get the track and sector of the last block */ j--; o = 256 * ( i + side * SIDE_SECTORS_MAX ) + OFFSET_POINTER + 2 * j; track = p->side_sector[o]; sector = p->side_sector[o + 1]; /* read it in */ if (disk_image_read_sector(vdrive->image, p->buffer, track, sector) != 0) { #ifdef CELL_DEBUG printf("ERROR: Cannot read relative file data sector.\n"); #endif vdrive_command_set_error(vdrive, CBMDOS_IPE_ILLEGAL_TRACK_OR_SECTOR, track, sector); return 0; } /* calculate the total bytes based on the number of super side, side sectors, and last byte index */ k = ( ( side * SIDE_SECTORS_MAX + i ) * SIDE_INDEX_MAX + j ) * 254 + p->buffer[OFFSET_NEXT_SECTOR] - 1; /* divide by the record length, and get the maximum records */ l = k / p->slot[SLOT_RECORD_LENGTH]; return l; }
int vdrive_command_execute(vdrive_t *vdrive, const BYTE *buf, unsigned int length) { int status = CBMDOS_IPE_INVAL; BYTE *p, *minus; char *name; if (!length) return CBMDOS_IPE_OK; if (length > IP_MAX_COMMAND_LEN) { vdrive_command_set_error(vdrive, CBMDOS_IPE_LONG_LINE, 0, 0); return CBMDOS_IPE_LONG_LINE; } if (buf[length - 1] == 0x0d) { --length; /* chop CR character */ } p = lib_malloc(length + 1); memcpy(p, buf, length); p[length] = 0; minus = (BYTE *)memchr(p, '-', length); name = (char *)memchr(p, ':', length); #ifdef DEBUG_DRIVE log_debug("Command '%c' (%s).", *p, p); #endif switch (*p) { case 'M': case 'P': break; /* In binary commands, colons are data */ default: if (name) { /* Fix name length */ length -= (BYTE *)name - p; } } switch (*p) { case 'C': /* Copy */ if (p[1] == 'D' && vdrive->image_format == VDRIVE_IMAGE_FORMAT_4000) { if (!name) { /* CD_ doesn't allow a : */ name = (char *)(p + 1); } status = vdrive_command_chdir(vdrive, (BYTE *)name, length); } else { status = vdrive_command_copy(vdrive, (char *)name, length); } break; case '/': /* change partition */ if ((vdrive->image_format == VDRIVE_IMAGE_FORMAT_1581) || (vdrive->image_format == VDRIVE_IMAGE_FORMAT_4000)) { if (!name) { /* handle "/dir" */ name = (char *)(p + 1); --length; } status = vdrive_command_chpart(vdrive, (BYTE *)name, length); } break; #if 0 case 'D': /* Duplicate is unused */ break; #endif case 'R': /* Rename */ status = vdrive_command_rename(vdrive, (BYTE *)name, length); break; case 'S': /* Scratch */ status = vdrive_command_scratch(vdrive, (BYTE *)name, length); break; case 'I': status = vdrive_command_initialize(vdrive); break; case 'N': /* Skip ":" at the start of the name. */ status = vdrive_command_format(vdrive, (name == NULL) ? NULL : name + 1); break; case 'V': status = vdrive_command_validate(vdrive); break; case 'B': /* Block, Buffer */ if (!name) /* B-x does not require a : */ name = (char *)(p + 2); if (minus) { status = vdrive_command_block(vdrive, minus[1], name + 1); } break; case 'M': /* Memory */ if (minus) { status = vdrive_command_memory(vdrive, minus + 1, length); } break; case 'P': /* Position */ status = vdrive_command_position(vdrive, p, length); break; case 'U': /* User */ if (!name) { /* Colons are optional */ name = (char *)(p + 1); } switch (p[1] & 0x0f) { case 1: /* UA */ if (name) status = vdrive_command_block(vdrive, (unsigned char)0xd2, name + 1); break; case 2: /* UB */ if (name) status = vdrive_command_block(vdrive, (unsigned char)0xd7, name + 1); break; case 3: /* Jumps */ case 4: case 5: case 6: case 7: case 8: status = CBMDOS_IPE_NOT_READY; break; case 9: /* UI */ if (p[2] == '-' || p[2] == '+') { status = CBMDOS_IPE_OK; /* Set IEC bus speed */ break; } /* Fall through. */ case 10: /* U:, UJ */ vdrive_close_all_channels(vdrive); /* Warm/Cold reset */ status = CBMDOS_IPE_DOS_VERSION; break; default: /* U0, UK..UO */ if (p[1] == '0') { status = CBMDOS_IPE_OK; break; } status = CBMDOS_IPE_NOT_READY; break; } /* Un */ break; default: break; } /* commands */ if (status == CBMDOS_IPE_INVAL) { log_error(vdrive_command_log, "Wrong command `%s'.", p); } vdrive_command_set_error(vdrive, status, 0, 0); lib_free((char *)p); return status; }
static int vdrive_command_block(vdrive_t *vdrive, unsigned char command, char *buffer) { int channel = 0, drive = 0, track = 0, sector = 0, position = 0; int l, rc; #ifdef DEBUG_DRIVE log_debug("vdrive_command_block command:%c.", command); #endif switch (command) { /* 1581 has u-R (shifted) and u-W (shifted) for block read/write without track/sector checking. */ /* Use this for U1,UA and U2,UB also */ case 0xd2: case 0xd7: l = vdrive_get_block_parameters(buffer, &channel, &drive, &track, §or); if (l < 0) { #ifdef DEBUG_DRIVE log_debug("b-R/W parsed OK. (l=%d) channel %d mode %d, " "drive=%d, track=%d sector=%d.", l, channel, vdrive->buffers[channel].mode, drive, track, sector); #endif if (vdrive->buffers[channel].mode != BUFFER_MEMORY_BUFFER) return CBMDOS_IPE_NO_CHANNEL; if (command == 0xd7) { /* For write */ if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) return CBMDOS_IPE_WRITE_PROTECT_ON; if (vdrive_write_sector(vdrive, vdrive->buffers[channel].buffer, track, sector) < 0) return CBMDOS_IPE_NOT_READY; } else { /* For read */ rc = vdrive_read_sector(vdrive, vdrive->buffers[channel].buffer, track, sector); if (rc > 0) return rc; if (rc < 0) return CBMDOS_IPE_NOT_READY; } vdrive->buffers[channel].bufptr = 0; } else { log_error(vdrive_command_log, "b-R/W invalid parameter " "C:%i D:%i T:%i S:%i.", channel, drive, track, sector); return l; } break; /* Old-style B-R and B-W */ case 'R': case 'W': l = vdrive_get_block_parameters(buffer, &channel, &drive, &track, §or); if (l < 0) { #ifdef DEBUG_DRIVE log_debug("b-r/w parsed OK. (l=%d) channel %d mode %d, " "drive=%d, track=%d sector=%d.", l, channel, vdrive->buffers[channel].mode, drive, track, sector); #endif if (vdrive->buffers[channel].mode != BUFFER_MEMORY_BUFFER) return CBMDOS_IPE_NO_CHANNEL; if (command == 'W') { /* For write */ if (vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) return CBMDOS_IPE_WRITE_PROTECT_ON; /* Update length of block based on the buffer pointer. */ l = vdrive->buffers[channel].bufptr - 1; vdrive->buffers[channel].buffer[0] = ( l < 1 ? 1 : l ); if (vdrive_write_sector(vdrive, vdrive->buffers[channel].buffer, track, sector) < 0) return CBMDOS_IPE_NOT_READY; /* after write, buffer pointer is 1. */ vdrive->buffers[channel].bufptr = 1; } else { /* For read */ rc = vdrive_read_sector(vdrive, vdrive->buffers[channel].buffer, track, sector); /* set buffer length base on first value */ vdrive->buffers[channel].length = vdrive->buffers[channel].buffer[0] + 1; /* buffer pointer is 1, not 0. */ vdrive->buffers[channel].bufptr = 1; if (rc > 0) return rc; if (rc < 0) return CBMDOS_IPE_NOT_READY; } } else { log_error(vdrive_command_log, "b-r/w invalid parameter " "C:%i D:%i T:%i S:%i.", channel, drive, track, sector); return l; } break; case 'A': case 'F': l = vdrive_get_block_parameters(buffer, &drive, &track, §or, &channel); if (l > 0) /* just 3 args used */ return l; if (command == 'A') { if (!vdrive_bam_allocate_sector(vdrive->image_format, vdrive->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 (vdrive_bam_alloc_next_free_sector(vdrive, vdrive->bam, (unsigned int*)&track, (unsigned int *)§or) >= 0) { /* Deallocate it and merely suggest it */ vdrive_bam_free_sector(vdrive->image_format, vdrive->bam, track, sector); } else { /* Found none */ track = 0; sector = 0; } vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_BLOCK, track, sector); return CBMDOS_IPE_NO_BLOCK; } } else { vdrive_bam_free_sector(vdrive->image_format, vdrive->bam, track, sector); } break; case 'P': l = vdrive_get_block_parameters(buffer, &channel, &position, &track, §or); if (l > 0) /* just 2 args used */ return l; if (vdrive->buffers[channel].mode != BUFFER_MEMORY_BUFFER) return CBMDOS_IPE_NO_CHANNEL; vdrive->buffers[channel].bufptr = position; break; case 'E': l = vdrive_get_block_parameters(buffer, &channel, &drive, &track, §or); log_warning(vdrive_command_log, "B-E: %d %d %d %d (needs TDE)", channel, drive, track, sector); break; default: return CBMDOS_IPE_INVAL; } return CBMDOS_IPE_OK; }
int vdrive_iec_read(vdrive_t *vdrive, BYTE *data, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); int status = SERIAL_OK; switch (p->mode) { case BUFFER_NOT_IN_USE: vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_OPEN, 0, 0); return SERIAL_ERROR; case BUFFER_MEMORY_BUFFER: *data = p->buffer[p->bufptr]; p->bufptr++; if (p->bufptr >= p->length) { /* Buffer pointer resets to 1, not 0. */ p->bufptr = 1; status = SERIAL_EOF; } break; case BUFFER_DIRECTORY_READ: case BUFFER_SEQUENTIAL: status = iec_read_sequential(vdrive, data, secondary); break; case BUFFER_COMMAND_CHANNEL: if (p->bufptr > p->length) { vdrive_command_set_error(vdrive, CBMDOS_IPE_OK, 0, 0); #if 0 #ifdef DEBUG_DRIVE log_debug("End of buffer in command channel."); #endif *data = 0xc7; #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) { log_debug("Disk read %d [%02d %02d] data %02x (%c).", p->mode, 0, 0, *data, (isprint(*data) ? *data : '.')); } #endif return SERIAL_EOF; #endif } *data = p->buffer[p->bufptr]; p->bufptr++; if (p->bufptr > p->length) { status = SERIAL_EOF; } break; case BUFFER_RELATIVE: status = vdrive_rel_read(vdrive, data, secondary); break; default: log_error(vdrive_iec_log, "Fatal: unknown buffermode on floppy-read."); } #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) { log_debug("Disk read %d [%02d %02d] data %02x (%c).", p->mode, 0, 0, *data, (isprint(*data) ? *data : '.')); } #endif return status; }
int vdrive_rel_write(vdrive_t *vdrive, BYTE data, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); if (vdrive->image->read_only) { vdrive_command_set_error(vdrive, CBMDOS_IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } /* Check if we need to grow the REL file. */ if (p->record >= p->record_max) { if (vdrive_rel_grow(vdrive, secondary, p->record) < 0) return SERIAL_OK; /* Couldn't grow it, error generated in function. */ } /* * Read next block if needed */ if (p->buffer[0]) { if (p->bufptr >= 256) { int status = 0; unsigned int track, sector; track = (unsigned int)p->buffer[0]; sector = (unsigned int)p->buffer[1]; /* Commit the buffers. */ vdrive_rel_commit(vdrive, p); /* Check to see if the next record is in the buffered sector */ if (p->track_next == track && p->sector_next == sector) { /* Swap the two buffers */ BYTE *tmp; tmp = p->buffer; p->buffer = p->buffer_next; p->buffer_next = tmp; p->track_next = p->track; p->sector_next = p->sector; p->track = track; p->sector = sector; status = 0; } else if (p->track!=track || p->sector != sector ) { /* load in the sector to memory */ status = disk_image_read_sector(vdrive->image, p->buffer, track, sector); } if (status == 0) { p->track = track; p->sector = sector; p->bufptr = p->bufptr - 254; p->length = p->length - 254; p->record_next = p->record_next - 254; /* keep buffered sector where ever it is */ /* we won't read in the next sector unless we have to */ } else { #ifdef CELL_DEBUG printf("ERROR: Cannot read track %i sector %i.\n", track, sector); #endif return SERIAL_EOF; } } } else { if (p->bufptr >= (unsigned int)( p->buffer[1] + 2 ) ) { #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) log_error(vdrive_rel_log, "Disk read %d [%02d %02d] data %02x (%c).", p->mode, 0, 0, data, (isprint(data) ? data : '.')); #endif if (vdrive_rel_grow(vdrive, secondary, p->record) < 0) { /* Couldn't grow it, error generated in function. */ return SERIAL_OK; } } } if (p->bufptr >= p->record_next) { /* We CANNOT write into the next record under any circumstances, unlike the reads. */ vdrive_command_set_error(vdrive, CBMDOS_IPE_OVERFLOW, 0, 0); /* But if we try, we still say it is okay, but with a DOS error. */ return SERIAL_OK; } /* write the data to the buffer */ p->buffer[p->bufptr] = data; /* increment the pointer */ p->bufptr++; /* flag this sector as dirty and written */ p->needsupdate |= ( DIRTY_SECTOR | WRITTEN_RECORD ); /* Mark this record as dirty only if we are still in it */ if (p->bufptr!=p->record_next) { p->needsupdate |= DIRTY_RECORD; } else { p->needsupdate &= (~DIRTY_RECORD); } /* Any pointer overflow gets handled at the beginning of this function. */ return SERIAL_OK; }
int vdrive_rel_read(vdrive_t *vdrive, BYTE *data, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); /* Check if we are past the end of the REL file. */ if (p->record >= p->record_max) { *data = 0x0d; vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_RECORD, 0, 0); return SERIAL_EOF; } /* * Read next block if needed */ if (p->buffer[0]) { if (p->bufptr >= 256) { int status = 0; unsigned int track, sector; track = (unsigned int)p->buffer[0]; sector = (unsigned int)p->buffer[1]; /* Commit the buffers. */ vdrive_rel_commit(vdrive, p); /* Check to see if the next record is in the buffered sector */ if (p->track_next == track && p->sector_next == sector) { /* Swap the two buffers */ BYTE *tmp; tmp = p->buffer; p->buffer = p->buffer_next; p->buffer_next = tmp; p->track_next = p->track; p->sector_next = p->sector; p->track = track; p->sector = sector; status = 0; } else if (p->track!=track || p->sector != sector ) { /* load in the sector to memory */ status = disk_image_read_sector(vdrive->image, p->buffer, track, sector); } if (status == 0) { p->track = track; p->sector = sector; p->bufptr = p->bufptr - 254; p->length = p->length - 254; p->record_next = p->record_next - 254; /* keep buffered sector where ever it is */ /* we won't read in the next sector unless we have to */ } else { #ifdef CELL_DEBUG printf("ERROR: Cannot read track %i sector %i.\n", track, sector); #endif *data = 0xc7; return SERIAL_EOF; } } } else { if (p->bufptr >= (unsigned int)( p->buffer[1] + 2 )) { /* check to see if we have the pointer overflow problem here. */ if (p->record_next > p->length) { *data = 0x0d; #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) log_error(vdrive_rel_log, "Disk read %d [%02d %02d] data %02x (%c).", p->mode, 0, 0, *data, (isprint(*data) ? *data : '.')); #endif vdrive_command_set_error(vdrive, CBMDOS_IPE_NO_RECORD, 0, 0); return SERIAL_EOF; } else { /* Yes, just rotate though to this buffer. */ if (p->bufptr>=256) { p->bufptr = p->bufptr - 254; p->length = p->length - 254; p->record_next = p->record_next - 254; } } } } *data = p->buffer[p->bufptr]; p->bufptr++; if (p->bufptr > p->length) { /* set up the buffer pointer to the next record */ p->bufptr = p->record_next; /* add the record length to the next record pointer */ p->record_next += p->side_sector[OFFSET_RECORD_LEN]; /* calculate the maximum length (for read, for write we should use record_next - 1 */ p->length = p->record_next - 1; /* increment the record reference */ p->record++; /* Exit if we have hit the end of the REL data. */ if (p->record >= p->record_max) { return SERIAL_EOF; } /* Find the length of the record starting from the end until no zeros are found */ if ( p->length < 256) { /* If the maximum length of the record is in this sector, just check for where the zeros end */ for (;p->length >= p->bufptr && p->buffer[p->length] == 0 ; p->length-- ); } else { int status=1; /* Get the next sector */ /* If it doesn't exist, we will use the max length calculated above */ if (p->buffer[0] != 0) { /* Read in the sector if it has not been buffered */ if (p->buffer[0] != p->track_next || p->buffer[1] != p->sector_next) { status = disk_image_read_sector(vdrive->image, p->buffer_next, p->buffer[0], p->buffer[1]); } else { status = 0; } } /* If all is good, calculate the length now */ if (!status) { /* update references, if the sector read in */ p->track_next = p->buffer[0]; p->sector_next = p->buffer[1]; /* Start looking in the buffered sector */ for (;p->length >= 256 && p->buffer_next[p->length-256+2] == 0 ; p->length-- ); /* If we crossed into the current sector, look there too */ if (p->length<256) { for (;p->length >= p->bufptr && p->buffer[p->length] == 0 ; p->length-- ); } } } //log_debug("Forced from read to position %d, 0 on channel %d.", p->record, secondary); return SERIAL_EOF; } return SERIAL_OK; }
int vdrive_iec_write(vdrive_t *vdrive, BYTE data, unsigned int secondary) { bufferinfo_t *p = &(vdrive->buffers[secondary]); if ((vdrive->image->read_only || VDRIVE_IMAGE_FORMAT_4000_TEST) && p->mode != BUFFER_COMMAND_CHANNEL) { vdrive_command_set_error(vdrive, CBMDOS_IPE_WRITE_PROTECT_ON, 0, 0); return SERIAL_ERROR; } #ifdef DEBUG_DRIVE if (p->mode == BUFFER_COMMAND_CHANNEL) { log_debug("Disk write %d [%02d %02d] data %02x (%c).", p->mode, 0, 0, data, (isprint(data) ? data : '.')); } #endif switch (p->mode) { case BUFFER_NOT_IN_USE: vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_OPEN, 0, 0); return SERIAL_ERROR; case BUFFER_DIRECTORY_READ: vdrive_command_set_error(vdrive, CBMDOS_IPE_NOT_WRITE, 0, 0); return SERIAL_ERROR; case BUFFER_MEMORY_BUFFER: p->buffer[p->bufptr] = data; p->bufptr++; if (p->bufptr >= p->length) { /* On writes, buffer pointer resets to 0. */ p->bufptr = 0; } return SERIAL_OK; case BUFFER_SEQUENTIAL: if (p->readmode == CBMDOS_FAM_READ) { return SERIAL_ERROR; } if (p->bufptr >= 256) { p->bufptr = 2; if (iec_write_sequential(vdrive, p, WRITE_BLOCK) < 0) { return SERIAL_ERROR; } } p->buffer[p->bufptr] = data; p->bufptr++; break; case BUFFER_COMMAND_CHANNEL: if (p->readmode == CBMDOS_FAM_READ) { p->bufptr = 0; p->readmode = CBMDOS_FAM_WRITE; } if (p->bufptr >= 256) { /* Limits checked later */ return SERIAL_ERROR; } p->buffer[p->bufptr] = data; p->bufptr++; break; case BUFFER_RELATIVE: return vdrive_rel_write(vdrive, data, secondary); break; default: log_error(vdrive_iec_log, "Fatal: Unknown write mode."); exit(-1); } 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; }