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; }
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; }
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; }