Exemplo n.º 1
0
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;
}
Exemplo n.º 2
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;
}
Exemplo n.º 3
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,
                                        &sector);

        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,
                                        &sector);

        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, &sector,
                                        &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 *)&sector) >= 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,
                                        &sector);
        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, &sector);
        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;
}