예제 #1
0
static int
update_archive_file( char *archive_file, libspectrum_tape *tape )
{
  libspectrum_tape_block* info_block;
  libspectrum_error error;
  libspectrum_tape *tzx;
  libspectrum_tape_iterator iterator;

  if( read_tape( archive_file, &tzx ) ) return 1;

  /* Get the new archive block */
  info_block = libspectrum_tape_iterator_init( &iterator, tzx );
  while( info_block &&
         libspectrum_tape_block_type( info_block ) !=
           LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO ) {
    info_block = libspectrum_tape_iterator_next( &iterator );
  }

  if( !info_block ) {
    libspectrum_tape_free( tzx );
    return 1;
  }

  /* Remove any existing archive block */
  error = remove_block_type( tape, LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO );
  if( error ) { libspectrum_tape_free( tzx ); return error; }

  /* Finally, put the new info block at the beginning of the block list */
  error = libspectrum_tape_insert_block( tape, info_block, 0 );
  if( error ) { libspectrum_tape_free( tzx ); return error; }

  return 0;
}
예제 #2
0
static int
remove_block_type( libspectrum_tape *tape, libspectrum_tape_type id )
{
  libspectrum_tape_block* block;
  libspectrum_tape_iterator iterator;

  for( block = libspectrum_tape_iterator_init( &iterator, tape );
       block;
       block = libspectrum_tape_iterator_next( &iterator ) ) {
    if( libspectrum_tape_block_type( block ) == id ) {
      libspectrum_tape_remove_block( tape, iterator );

      /* Iterator is invalidated by delete, so start again */
      block = libspectrum_tape_iterator_init( &iterator, tape );
    }
  }

  return 0;
}
예제 #3
0
// Return a string describing a give tape block
const char *Tape::GetBlockDetails (libspectrum_tape_block *block)
{
    static char sz[128];
    sz[0] = '\0';

    char szExtra[64] = "";
    char szName[11] = "";
    const char *psz = NULL;

    libspectrum_byte *data = libspectrum_tape_block_data(block);
    long length = static_cast<long>(libspectrum_tape_block_data_length(block));


    // Is there enough data to include a possible filename?
    if (length >= 12)
    {
        for (int i = 0 ; i < 10 ; i++)
        {
            char ch = data[i+2];
            szName[i] = (ch >= ' ' && ch <= 0x7f) ? ch : '?';
        }
        szName[10] = '\0';
    }

    // Spectrum header length and type byte?
    if (length == 17+2 && data[0] == 0x00)
    {
        // Examine Spectrum file type
        switch (data[1])
        {
            case 0:
            {
                psz = "ZX BASIC";

                UINT uLine = (data[15] << 8) | data[14];
                if (uLine != 0xffff)
                    sprintf(szExtra, " LINE %u", uLine);

                break;
            }

            case 1: psz = "ZX DATA()"; break;
            case 2: psz = "ZX DATA$()"; break;

            case 3:
            {
                psz = "ZX CODE";

                UINT uAddr = (data[15] << 8) | data[14];
                UINT uLen = (data[13] << 8) | data[12];
                sprintf(szExtra, " %u,%u", uAddr, uLen);

                break;
            }
        }
    }
    // SAM header length and type byte?
    // Real length is 82, but TZX spec suggests there could be up to 7-8 trailing bits, so accept 83
    else if ((length == 80+2 || length == 80+1+2) && data[0] == 0x01)
    {
        // Examine SAM file type
        switch (data[1])
        {
            case 16:
            {
                psz = "BASIC";

                UINT uLine = (data[40] << 8) | data[39];
                if (data[38] == 0)
                    sprintf(szExtra, " LINE %u", uLine);

                break;
            }

            case 17: psz = "DATA()"; break;
            case 18: psz = "DATA$"; break;
            case 19:
            {
                psz = "CODE";

                UINT uAddr = TPeek(data+32) + 16384;
                UINT uLen = TPeek(data+35);

                sprintf(szExtra, " %u,%u", uAddr, uLen);
                if (data[38] == 0)
                    sprintf(szExtra+strlen(szExtra), ",%u", TPeek(data+38));

                break;
            }

            case 20:
            {
                psz = "SCREEN$";
                UINT uMode = data[17]+1;
                sprintf(szExtra, " MODE %u", uMode);
                break;
            }
        }
    }

    // Do we have a type string?
    if (psz)
    {
        // Start with type and append filename
        strcpy(sz, psz);
        strcat(sz, ": '");
        strcat(sz, szName);
        strcat(sz, "'");

        // Append any additional type-specific details
        if (szExtra[0])
        {
            strcat(sz, " ");
            strcat(sz, szExtra);
        }
    }

    // No details yet?
    if (!sz[0])
    {
        libspectrum_tape_type type = libspectrum_tape_block_type(block);

        switch (type)
        {
            case LIBSPECTRUM_TAPE_BLOCK_ROM:
            case LIBSPECTRUM_TAPE_BLOCK_TURBO:
            {
                // Raw tape block data length
                size_t length = libspectrum_tape_block_data_length(block);

                // If possible, exclude the type, sync, and checksum bytes from the length
                if (length >= 3)
                    length -= 3;

                snprintf(sz, sizeof(sz), "%u bytes", length);
                break;
            }

            case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
            case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
                snprintf(sz, sizeof(sz), "%u bytes", libspectrum_tape_block_data_length(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
                snprintf(sz, sizeof(sz), "%u tstates", libspectrum_tape_block_pulse_length(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_PULSES:
                snprintf(sz, sizeof(sz), "%u pulses", libspectrum_tape_block_count(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
                snprintf(sz, sizeof(sz), "%ums", libspectrum_tape_block_pause(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
            case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
            case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
            case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
                snprintf(sz, sizeof(sz), "%s", libspectrum_tape_block_text(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_JUMP:
            {
                int offset = libspectrum_tape_block_offset(block);
                if (offset >= 0)
                    snprintf(sz, sizeof(sz), "Forward %d blocks", offset);
                else
                    snprintf(sz, sizeof(sz), "Backward %d blocks", -offset);
                break;
            }

            case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
                snprintf(sz, sizeof(sz), "%u iterations", libspectrum_tape_block_count(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_SELECT:
                snprintf(sz, sizeof(sz), "%u options", libspectrum_tape_block_count(block));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
                snprintf(sz, sizeof(sz), "%u data symbols",
                    libspectrum_tape_generalised_data_symbol_table_symbols_in_block(libspectrum_tape_block_data_table(block)));
                break;

            case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
            {
                size_t count = libspectrum_tape_block_count(block);

                for (size_t i = 0 ; i < count ; i++)
                {
                    int id = libspectrum_tape_block_ids(block, i);
                    const char *value = libspectrum_tape_block_texts(block, i);

                    // Full title TZX id?
                    if (id == 0x00)
                        strncpy(sz, value, sizeof(sz)-1);
                }
                break;
            }

            case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
            {
                size_t count = libspectrum_tape_block_count(block);

                for (size_t i = 0 ; i < count ; i++)
                {
                    int type = libspectrum_tape_block_types(block, i);
                    int id = libspectrum_tape_block_ids(block, i);

                    // Skip anything but the TZX "Computers" type
                    if (type != 0)
                        continue;

                    // Check for relevant computer ids
                    if (id == 9)
                        strcpy(sz, "SAM Coupe");
                    else if ((id >= 0x00 && id <= 0x05) || id == 0x0e)
                        strcpy(sz, "ZX Spectrum");
                    else if (id == 0x08)
                        strcpy(sz, "Pentagon");
                    else if (id == 0x06 || id == 0x07)
                        strcpy(sz, "Timex Sinclair");
                    else
                        snprintf(sz, sizeof(sz), "Unknown (%02X)", id);
                }

                break;
            }

            default:
                break;
        }
    }

    return sz;
}
예제 #4
0
bool Tape::LoadTrap ()
{
    if (!IsInserted())
        return false;

    // If traps are disabled, try normal loading
    if (!GetOption(tapetraps))
    {
        Play();
        return false;
    }

    // Skip over any metadata blocks
    libspectrum_tape_block *block = libspectrum_tape_current_block(pTape);
    while (block && libspectrum_tape_block_metadata(block))
        block = libspectrum_tape_select_next_block(pTape);

    // Nothing else to process?
    if (!block)
        return false;

    libspectrum_tape_type type = libspectrum_tape_block_type(block);
    libspectrum_tape_state_type state = libspectrum_tape_state(pTape);

    // Consider both ROM blocks (normal speed) and turbo blocks, as used by custom SAM tape speeds (DEVICE tX)
    if ((type != LIBSPECTRUM_TAPE_BLOCK_ROM && type != LIBSPECTRUM_TAPE_BLOCK_TURBO) || state != LIBSPECTRUM_TAPE_STATE_PILOT)
    {
        // Fall back on non-trap loading for anything else
        Play();
        return false;
    }


    libspectrum_byte *pbData = libspectrum_tape_block_data(block);
    size_t nData = libspectrum_tape_block_data_length(block);

    // Base load address and load request size
    WORD wDest = HL;
    int nWanted = (read_byte(0x5ac8) << 16) | DE;

    // Fetch block type
    H = *pbData++;
    nData--;

    // Spectrum header?
    if (H == 0)
    {
        // Override request length 
        nWanted = (nWanted & ~0xff) | 17;
    }
    // Otherwise the type byte must match the request
    else if (H != A_)
    {
        // Advance to next block
        libspectrum_tape_select_next_block(pTape);

        // Failed, exit via: RET NZ
        F &= ~(FLAG_C|FLAG_Z);
        PC = 0xe6f6;

        return true;
    }

    // Parity byte initialised to type byte
    L = H;

    // More still to load?
    while (nWanted >= 0)
    {
        // Are we out of source data?  (ToDo: support continuation blocks?)
        if (!nData)
        {
            // Advance to next block
            libspectrum_tape_select_next_block(pTape);

            // Failed, exit via: RET NZ
            F &= ~(FLAG_C|FLAG_Z);
            PC = 0xe6f6;

            return true;
        }

        // Read next byte and update parity
        L ^= (H = *pbData++);
        nData--;

        // Request complete?
        if (!nWanted)
            break;

        // Write new byte
        write_byte(wDest, H);
        wDest++;
        nWanted--;

        // Destination now in the top 16K?
        if (wDest >= 0xc000)
        {
            // Slide paging up and move pointer back
            IO::OutHmpr(hmpr+1);
            wDest -= 0x4000;
        }
    }

    // Advance to next block
    libspectrum_tape_select_next_block(pTape);

    // Exit via: LD A,L ; CP 1 ; RET
    PC = 0xe739;

    return true;
}
예제 #5
0
int
tape_block_details( char *buffer, size_t length,
		    libspectrum_tape_block *block )
{
  libspectrum_byte *data;
  const char *type; unsigned char name[11];
  int offset;
  size_t i;
  unsigned long total_pulses;

  buffer[0] = '\0';

  switch( libspectrum_tape_block_type( block ) ) {

  case LIBSPECTRUM_TAPE_BLOCK_ROM:
  case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK:
    /* See if this looks like a standard Spectrum header and if so
       display some extra data */
    if( libspectrum_tape_block_data_length( block ) != 19 ) goto normal;

    data = libspectrum_tape_block_data( block );

    /* Flag byte is 0x00 for headers */
    if( data[0] != 0x00 ) goto normal;

    switch( data[1] ) {
    case 0x00: type = "Program"; break;
    case 0x01: type = "Number array"; break;
    case 0x02: type = "Character array"; break;
    case 0x03: type = "Bytes"; break;
    default: goto normal;
    }
    
    make_name( name, &data[2] );

    snprintf( buffer, length, "%s: \"%s\"", type, name );

    break;

  normal:
    snprintf( buffer, length, "%lu bytes",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_TURBO:
  case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
  case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
    snprintf( buffer, length, "%lu bytes",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
    snprintf( buffer, length, "%lu tstates",
	      (unsigned long)libspectrum_tape_block_pulse_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PULSES:
    snprintf( buffer, length, "%lu pulses",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE:
    total_pulses = 0;
    for( i=0; i < libspectrum_tape_block_count( block ); i++ )
      total_pulses += libspectrum_tape_block_pulse_repeats( block, i );
    snprintf( buffer, length, "%lu pulses", total_pulses );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
    snprintf( buffer, length, "%lu ms",
	      (unsigned long)libspectrum_tape_block_pause( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
  case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
  case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
  case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
    snprintf( buffer, length, "%s", libspectrum_tape_block_text( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_JUMP:
    offset = libspectrum_tape_block_offset( block );
    if( offset > 0 ) {
      snprintf( buffer, length, "Forward %d blocks", offset );
    } else {
      snprintf( buffer, length, "Backward %d blocks", -offset );
    }
    break;

  case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
    snprintf( buffer, length, "%lu iterations",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_SELECT:
    snprintf( buffer, length, "%lu options",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
    snprintf( buffer, length, "%lu data symbols",
	      (unsigned long)libspectrum_tape_generalised_data_symbol_table_symbols_in_block( libspectrum_tape_block_data_table( block ) ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
    /* Could do something better with this one */
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
  case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
  case LIBSPECTRUM_TAPE_BLOCK_STOP48:
  case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL:
  case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
  case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
  case LIBSPECTRUM_TAPE_BLOCK_CONCAT:
    break;

  }

  return 0;
}
예제 #6
0
void
tape_next_edge( libspectrum_dword last_tstates, int type, void *user_data )
{
  libspectrum_error libspec_error;
  libspectrum_tape_block *block;

  libspectrum_dword edge_tstates;
  int flags;

  /* If the tape's not playing, just return */
  if( ! tape_playing ) return;

  /* Get the time until the next edge */
  libspec_error = libspectrum_tape_get_next_edge( &edge_tstates, &flags,
						  tape );
  if( libspec_error != LIBSPECTRUM_ERROR_NONE ) return;

  /* Invert the microphone state */
  if( edge_tstates ||
      !( flags & LIBSPECTRUM_TAPE_FLAGS_NO_EDGE ) ||
      ( flags & ( LIBSPECTRUM_TAPE_FLAGS_STOP |
                  LIBSPECTRUM_TAPE_FLAGS_LEVEL_LOW |
                  LIBSPECTRUM_TAPE_FLAGS_LEVEL_HIGH ) ) ) {

    if( flags & LIBSPECTRUM_TAPE_FLAGS_NO_EDGE ) {
      /* Do nothing */
    } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_LOW ) {
      tape_microphone = 0;
    } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_HIGH ) {
      tape_microphone = 1;
    } else {
      tape_microphone = !tape_microphone;
    }
  }

  sound_beeper( last_tstates, tape_microphone );

  /* If we've been requested to stop the tape, do so and then
     return without stacking another edge */
  if( ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP ) ||
      ( ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP48 ) && 
	( !( libspectrum_machine_capabilities( machine_current->machine ) &
	     LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY
	   )
	)
      )
    )
  {
    tape_stop();
    return;
  }

  /* If that was the end of a block, update the browser */
  if( flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK ) {

    ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );

    /* If the tape was started automatically, tape traps are active
       and the new block is a ROM loader, stop the tape and return
       without putting another event into the queue */
    block = libspectrum_tape_current_block( tape );
    if( tape_autoplay && settings_current.tape_traps &&
	libspectrum_tape_block_type( block ) == LIBSPECTRUM_TAPE_BLOCK_ROM
      ) {
      tape_stop();
      return;
    }
  }

  /* Otherwise, put this into the event queue; remember that this edge
     should occur 'edge_tstates' after the last edge, not after the
     current time (these will be slightly different as we only process
     events between instructions). */
  event_add( last_tstates + edge_tstates, tape_edge_event );

  /* Store length flags for acceleration purposes */
  loader_set_acceleration_flags( flags );
}
예제 #7
0
/* Load the next tape block into memory; returns 0 if a block was
   loaded (even if it had an tape loading error or equivalent) or
   non-zero if there was an error at the emulator level, or tape traps
   are not active */
int tape_load_trap( void )
{
  libspectrum_tape_block *block, *next_block;
  int error;

  /* Do nothing if tape traps aren't active, or the tape is already playing */
  if( !settings_current.tape_traps || tape_playing ) return 2;

  /* Do nothing if we're not in the correct ROM */
  if( !trap_check_rom( CHECK_TAPE_ROM ) ) return 3;

  /* Return with error if no tape file loaded */
  if( !libspectrum_tape_present( tape ) ) return 1;

  block = libspectrum_tape_current_block( tape );

  /* Skip over any meta-data blocks */
  while( libspectrum_tape_block_metadata( block ) ) {
    block = libspectrum_tape_select_next_block( tape );
    if( !block ) return 1;
  }
  
  /* If this block isn't a ROM loader, start the block playing. After
     that, return with `error' so that we actually do whichever
     instruction it was that caused the trap to hit */
  if( libspectrum_tape_block_type( block ) != LIBSPECTRUM_TAPE_BLOCK_ROM ||
      libspectrum_tape_state( tape ) != LIBSPECTRUM_TAPE_STATE_PILOT ) {
    tape_play( 1 );
    return -1;
  }

  /* We don't properly handle the case of partial loading, so don't run
     the traps in that situation */
  if( libspectrum_tape_block_data_length( block ) != DE + 2 ) {
    tape_play( 1 );
    return -1;
  }

  /* All returns made via the RET at #05E2, except on Timex 2068 at #0136 */
  if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 ||
       machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) {
    PC = 0x0136;
  } else {
    PC = 0x05e2;
  }

  error = trap_load_block( block );
  if( error ) return error;

  /* Peek at the next block. If it's a ROM block, move along, initialise
     the block, and return */
  next_block = libspectrum_tape_peek_next_block( tape );

  if( libspectrum_tape_block_type(next_block) == LIBSPECTRUM_TAPE_BLOCK_ROM ) {

    next_block = libspectrum_tape_select_next_block( tape );
    if( !next_block ) return 1;

    ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );

    return 0;
  }

  /* If the next block isn't a ROM block, set ourselves up such that the
     next thing to occur is the pause at the end of the current block */
  libspectrum_tape_set_state( tape, LIBSPECTRUM_TAPE_STATE_PAUSE );

  return 0;
}
예제 #8
0
int
tape_block_details( char *buffer, size_t length,
		    libspectrum_tape_block *block )
{
  libspectrum_byte *data;
  const char *type; unsigned char name[11];
  int offset;

  buffer[0] = '\0';

  switch( libspectrum_tape_block_type( block ) ) {

  case LIBSPECTRUM_TAPE_BLOCK_ROM:
    /* See if this looks like a standard Spectrum header and if so
       display some extra data */
    if( libspectrum_tape_block_data_length( block ) != 19 ) goto normal;

    data = libspectrum_tape_block_data( block );

    /* Flag byte is 0x00 for headers */
    if( data[0] != 0x00 ) goto normal;

    switch( data[1] ) {
    case 0x00: type = "Program"; break;
    case 0x01: type = "Number array"; break;
    case 0x02: type = "Character array"; break;
    case 0x03: type = "Bytes"; break;
    default: goto normal;
    }

    make_name( name, &data[2] );

    snprintf( buffer, length, "%s: \"%s\"", type, name );

    break;

  normal:
    snprintf( buffer, length, "Data (%lu Bytes)",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_TURBO:
    snprintf( buffer, length, "Turbo Data (%lu Bytes)",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;  
  case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
    snprintf( buffer, length, "Pure Data (%lu Bytes)",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;  
  case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
    snprintf( buffer, length, "Raw Data (%lu Bytes)",
	      (unsigned long)libspectrum_tape_block_data_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
    snprintf( buffer, length, "Pure Tone (%lu tstates)",
	      (unsigned long)libspectrum_tape_block_pulse_length( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PULSES:
    snprintf( buffer, length, "Sequence Of %lu Pulses",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
    if((unsigned long)libspectrum_tape_block_pause( block )==0)
      snprintf( buffer, length, "[Stop The Tape]");
    else 
      snprintf( buffer, length, "[Pause - %lu ms]",(unsigned long)libspectrum_tape_block_pause( block ));
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
  case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
  case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
  case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
    snprintf( buffer, length, "[%s]", libspectrum_tape_block_text( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_JUMP:
    offset = libspectrum_tape_block_offset( block );
    if( offset > 0 ) {
      snprintf( buffer, length, "[Forward %d Blocks]", offset );
    } else {
      snprintf( buffer, length, "[Backward %d Blocks]", -offset );
    }
    break;

  case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
    snprintf( buffer, length, "[Loop %lu Times]",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_SELECT:
    snprintf( buffer, length, "[Select %lu Options]",
	      (unsigned long)libspectrum_tape_block_count( block ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
    snprintf( buffer, length, "[Generalised Data - %lu Data Symbols]",
	      (unsigned long)libspectrum_tape_generalised_data_symbol_table_symbols_in_block( (void*)libspectrum_tape_block_data_table( block ) ) );
    break;

  case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
    /* Could do something better with this one */
    break;

  case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
    snprintf( buffer, length, "[Group End]");
    break;
  case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
    snprintf( buffer, length, "[Loop End]");
    break;  
  case LIBSPECTRUM_TAPE_BLOCK_STOP48:
    snprintf( buffer, length, "[Stop The Tape If 48]");
    break;    
  case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
    snprintf( buffer, length, "[Archive Info]");
    break;  
  case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
    snprintf( buffer, length, "[Hardware]");
    break;    
  case LIBSPECTRUM_TAPE_BLOCK_CONCAT:
    snprintf( buffer, length, "[Concat]");  
    break;

  }

  return 0;
}
예제 #9
0
/* Load the next tape block into memory; returns 0 if a block was
   loaded (even if it had an tape loading error or equivalent) or
   non-zero if there was an error at the emulator level, or tape traps
   are not active */
int tape_load_trap(Z80Regs * regs)
{
  libspectrum_tape_block *block, *next_block;
  int error;

  /* Do nothing if tape traps aren't active, or the tape is already playing */
  if( !mconfig.flash_loading || tape_playing ) return 1;

  /* Do nothing if we're not in the correct ROM */
  //if( ! trap_check_rom() ) return 3;

  /* Return with error if no tape file loaded */
  if( !libspectrum_tape_present( tape ) ) return 1;

  block = libspectrum_tape_current_block( tape );

  /* Skip over any meta-data blocks */
  while( libspectrum_tape_block_metadata( block ) ) {
    block = libspectrum_tape_select_next_block( tape );
    if( !block ) return 1;
  }

  /* If this block isn't a ROM loader, start the block playing. After
     that, return with `error' so that we actually do whichever
     instruction it was that caused the trap to hit */
  if( libspectrum_tape_block_type( block ) != LIBSPECTRUM_TAPE_BLOCK_ROM 
   || libspectrum_tape_state( tape ) != LIBSPECTRUM_TAPE_STATE_PILOT  
  ) {
    tape_play( 1 );
    return /*-1*/1;
  }

  /* We don't properly handle the case of partial loading, so don't run
     the traps in that situation *///Esto hace cascar algunos games
   
  /* 
  if( libspectrum_tape_block_data_length( block ) != regs->DE.W + 2 ) {
    tape_play( 1 );
    return 1;
  }
  */
  
  error = trap_load_block( block , regs);//carga el bloque
  if( error ) return -1;

  /* Peek at the next block. If it's a ROM block, move along, initialise
     the block, and return */
  next_block = libspectrum_tape_peek_next_block( tape );

  if( libspectrum_tape_block_type(next_block) == LIBSPECTRUM_TAPE_BLOCK_ROM ) {

    next_block = libspectrum_tape_select_next_block( tape );
    if( !next_block ) return 1;

    //ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );    

    return 0;//FLASHLOADED!
  }

  /* If the next block isn't a ROM block, set ourselves up such that the
     next thing to occur is the pause at the end of the current block */
     
  libspectrum_tape_set_state( tape, LIBSPECTRUM_TAPE_STATE_PAUSE );//TODO ? autodetect, sino no tira?
  //libspectrum_tape_block_set_state( block, LIBSPECTRUM_TAPE_STATE_PAUSE );
  
  /*
  error = tape_play(1);
  if( error ) return -1;
  */
  return 0;
  
}
예제 #10
0
int
tape_next_edge(Z80Regs *regs, int *edge_tstates, int *bit)
{
  int error; libspectrum_error libspec_error;
  libspectrum_tape_block *block;
  
  //libspectrum_dword edge_tstates;
  int flags;
  *edge_tstates = 0;
  *bit=-1;
  int one_value=0;
  /* If the tape's not playing, just return */
  if( ! tape_playing ) return 0;

  block = libspectrum_tape_current_block( tape );
  int type = libspectrum_tape_block_type( block );
  if(type==LIBSPECTRUM_TAPE_BLOCK_ROM || 
     type==LIBSPECTRUM_TAPE_BLOCK_TURBO || 
     type==LIBSPECTRUM_TAPE_BLOCK_PURE_DATA /*||
     type==LIBSPECTRUM_TAPE_BLOCK_RAW_DATA*/)
  { 
     int state = libspectrum_tape_state( tape );

     if(state==LIBSPECTRUM_TAPE_STATE_DATA1 || state==LIBSPECTRUM_TAPE_STATE_DATA2)
     {
        if(block->type==LIBSPECTRUM_TAPE_BLOCK_TURBO)
        {
           one_value = block->types.turbo.bit1_length;
        }
        else if(block->type==LIBSPECTRUM_TAPE_BLOCK_PURE_DATA)
        {
           one_value = block->types.pure_data.bit1_length;
        }
        else
        {
           one_value = 1710;
        }        
     }             
  }   

  /* Get the time until the next edge */
  libspectrum_dword tmp = 0;
  libspec_error = libspectrum_tape_get_next_edge(&tmp, &flags, tape);
  *edge_tstates = tmp;
  
  if(one_value!=0)
     *bit = *edge_tstates == one_value ? 1 : 0;
 
  //edge_tstates_target = edge_tstates_target+*edge_tstates; 
    
  if( libspec_error != LIBSPECTRUM_ERROR_NONE ) return libspec_error;

  /* Invert the microphone state */
  if( *edge_tstates || ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP ) ) {

    if( flags & LIBSPECTRUM_TAPE_FLAGS_NO_EDGE ) {
      // Do nothing 
    } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_LOW ) {
      tape_microphone = 0;
    } else if( flags & LIBSPECTRUM_TAPE_FLAGS_LEVEL_HIGH ) {
      tape_microphone = 1;
    } else {
      tape_microphone = !tape_microphone;
    }

    if(regs!=NULL)
       sound_beeper_1(tape_microphone,regs->IPeriod-regs->ICount);//TODO: poner pref
  }

  /* If we've been requested to stop the tape, do so and then
     return without stacking another edge */
     
  int zx48=0;
  if(regs!=NULL)
    zx48=regs->IPeriod==69888;
         
  if((flags & LIBSPECTRUM_TAPE_FLAGS_STOP) || ( zx48 && (flags & LIBSPECTRUM_TAPE_FLAGS_STOP48) ) )
  {
    error = tape_stop(); if( error ) return error;
    return 0;
  }

  /* If that was the end of a block, update the browser */
  if( flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK ) {

    //ui_tape_browser_update( UI_TAPE_BROWSER_SELECT_BLOCK, NULL );

    /* If the tape was started automatically, tape traps are active
       and the new block is a ROM loader, stop the tape and return
       without putting another event into the queue */
    block = libspectrum_tape_current_block( tape );    
    if( /*tape_autoplay &&*/ mconfig.flash_loading &&
	    libspectrum_tape_block_type( block ) == LIBSPECTRUM_TAPE_BLOCK_ROM
      ) {
      error = tape_stop(); if( error ) return error;
      return 0;//TODO: devolver valor que nos diga que nos diga que no hay que devolver otro edge?
    }
    
  }

  /* Otherwise, put this into the event queue; remember that this edge
     should occur 'edge_tstates' after the last edge, not after the
     current time (these will be slightly different as we only process
     events between instructions). */

  return 0;
}
예제 #11
0
int
main(int argc, char *argv[])
{
	int opt, i, quiet = 0;
	char *output = "output.tzx", *p;
	int turbo_params[6] = { 2168, 3323, 667, 735, 855, 1710 };

	FILE *fd;
	size_t data_len;

	libspectrum_tape *tape;
	libspectrum_byte *data;
	libspectrum_tape_block *block;
	libspectrum_dword pause, pause_tstates;

	while ((opt = getopt(argc, argv, "vhqt:o:")) != -1)
	{
		switch(opt)
		{
			case 'q':
				quiet = 1;
				break;
			case 't':
				if (sscanf(optarg, "%i:%i:%i:%i:%i:%i", &turbo_params[0],
					&turbo_params[1], &turbo_params[2], &turbo_params[3], 
					&turbo_params[4], &turbo_params[5]) != 6)
					fatal(argv[0], "Failed to parse turbo params");
				break;
			case 'o':
				output = strdup(optarg);
				break;
			case 'h':
				help(argv[0]);
				exit(0);
			case 'v':
				fprintf(stderr,  VERSION "\n");
				exit(0);
			default:
				fprintf(stderr, "\n");
				help(argv[0]);
				exit(1);
		}
	}

	if (optind >= argc)
		fatal(argv[0], "Expected input filename to process");

	if (libspectrum_init())
		fatal(argv[0], "Failed to init libspectrum");

	fd = fopen(argv[optind], "rb");
	if (!fd)
		fatal(argv[0], "Failed to open input file");

	fseek(fd, 0, SEEK_END);
	data_len = ftell(fd);
	fseek(fd, 0, SEEK_SET);

	data = (libspectrum_byte *) malloc(data_len);
	if (!data)
	{
		fclose(fd);
		fatal(argv[0], "Failed to allocate memory");
	}

	if (fread(data, sizeof(libspectrum_byte), data_len, fd) != data_len)
	{
		fclose(fd);
		free(data);
		fatal(argv[0], "Failed to read input file");
	}
	fclose(fd);

	tape = libspectrum_tape_alloc();
	if (!tape)
	{
		fclose(fd);
		free(data);
		fatal(argv[0], "Failed to allocate tape memory");
	}

	if (libspectrum_tape_read(tape, data, data_len, LIBSPECTRUM_ID_UNKNOWN, argv[optind]))
	{
		fclose(fd);
		free(data);
		free(tape);
		fatal(argv[0], "Failed to read tape");
	}
	free(data);

	for (i = optind + 1; i < argc; i++)
	{
		for (p = argv[i]; *p && isdigit(*p); p++);

		if (*p)
		{
			fprintf(stderr, "WARNING: Parameter is not a valid block number, skipping\n");
			continue;
		}

		if (libspectrum_tape_nth_block(tape, atoi(argv[i])))
			continue;

		block = libspectrum_tape_current_block(tape);
		if (!block)
			continue;

		if (!quiet)
			printf("%s: Setting block #%i (type 0x%02x): ", argv[0], atoi(argv[i]),
					libspectrum_tape_block_type(block));

		data = libspectrum_tape_block_data(block);
		data_len = libspectrum_tape_block_data_length(block);
		pause = libspectrum_tape_block_pause(block);
		pause_tstates = libspectrum_tape_block_pause_tstates(block);

		if (libspectrum_tape_block_set_type(block, LIBSPECTRUM_TAPE_BLOCK_TURBO)
			|| libspectrum_tape_block_set_pilot_pulses(block, turbo_params[1])
			|| libspectrum_tape_block_set_pilot_length(block, turbo_params[0])
			|| libspectrum_tape_block_set_sync1_length(block, turbo_params[2])
			|| libspectrum_tape_block_set_sync2_length(block, turbo_params[3])
			|| libspectrum_tape_block_set_bit0_length(block, turbo_params[4])
			|| libspectrum_tape_block_set_bit1_length(block, turbo_params[5])
			|| libspectrum_tape_block_set_data(block, data)
		 	|| libspectrum_tape_block_set_data_length(block, data_len)
			|| libspectrum_tape_block_set_pause(block, pause)
			|| libspectrum_tape_block_set_pause_tstates(block, pause_tstates)
			|| libspectrum_tape_block_set_bits_in_last_byte(block, 8)
			)
		{
			if (!quiet)
				printf("failed!\n");
			fprintf(stderr, "WARNING: Failed to set block %i\n", atoi(argv[i]));
			continue;
		}

		if (!quiet)
			printf("OK\n");
	}

	data = NULL;
	data_len = 0;
	if (libspectrum_tape_write(&data, &data_len, tape, LIBSPECTRUM_ID_TAPE_TZX))
	{
		free(tape);
		fatal(argv[0], "Failed to write the output file");
	}

	fd = fopen(output, "wb");
	if (!fd)
	{
		free(data);
		free(tape);
		fatal(argv[0], "Failed to open output file");
	}

	if (fwrite(data, sizeof(libspectrum_byte), data_len, fd) != data_len)
	{
		fclose(fd);
		free(data);
		free(tape);
		fatal(argv[0], "Failed to write output file");
	}
	fclose(fd);
	free(data);
	free(tape);

	exit(0);
}
예제 #12
0
/* Called when a new block is started to initialise its internal state */
libspectrum_error
libspectrum_tape_block_init( libspectrum_tape_block *block,
                             libspectrum_tape_block_state *state )
{
  if( !block ) return LIBSPECTRUM_ERROR_NONE;

  switch( libspectrum_tape_block_type( block ) ) {

  case LIBSPECTRUM_TAPE_BLOCK_ROM:
    return rom_init( &(block->types.rom), &(state->block_state.rom) );
  case LIBSPECTRUM_TAPE_BLOCK_TURBO:
    return turbo_init( &(block->types.turbo), &(state->block_state.turbo) );
  case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
    state->block_state.pure_tone.edge_count = block->types.pure_tone.pulses;
    break;
  case LIBSPECTRUM_TAPE_BLOCK_PULSES:
    state->block_state.pulses.edge_count = 0;
    break;
  case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
    return pure_data_init( &(block->types.pure_data),
                           &(state->block_state.pure_data) );
  case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
    raw_data_init( &(block->types.raw_data), &(state->block_state.raw_data) );
    return LIBSPECTRUM_ERROR_NONE;
  case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
    return generalised_data_init( &(block->types.generalised_data),
                                  &(state->block_state.generalised_data) );
  case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
    state->block_state.rle_pulse.index = 0;
    return LIBSPECTRUM_ERROR_NONE;
  case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE:
    state->block_state.pulse_sequence.index = 0;
    state->block_state.pulse_sequence.pulse_count = 0;
    state->block_state.pulse_sequence.level = -1;
    return LIBSPECTRUM_ERROR_NONE;
  case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK:
    return data_block_init( &(block->types.data_block),
                            &(state->block_state.data_block) );

  /* These blocks need no initialisation */
  case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
  case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
  case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
  case LIBSPECTRUM_TAPE_BLOCK_JUMP:
  case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
  case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
  case LIBSPECTRUM_TAPE_BLOCK_SELECT:
  case LIBSPECTRUM_TAPE_BLOCK_STOP48:
  case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL:
  case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
  case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
  case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
  case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
  case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
    return LIBSPECTRUM_ERROR_NONE;

  default:
    libspectrum_print_error(
      LIBSPECTRUM_ERROR_LOGIC,
      "libspectrum_tape_init_block: unknown block type 0x%02x",
      block->type
    );
    return LIBSPECTRUM_ERROR_LOGIC;
  }

  return LIBSPECTRUM_ERROR_NONE;
}
예제 #13
0
static libspectrum_dword
find_sample_rate( libspectrum_tape *tape )
{
    libspectrum_tape_iterator iterator;
    libspectrum_tape_block *block;
    libspectrum_dword sample_rate = 44100;
    int found = 0;

    /* FIXME: If tape has only one block that is a sampled type, just use it's rate
       and if it RLE, we should just zlib and write */
    for( block = libspectrum_tape_iterator_init( &iterator, tape );
            block;
            block = libspectrum_tape_iterator_next( &iterator ) )
    {
        switch( libspectrum_tape_block_type( block ) ) {

        case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
        case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
        {
            libspectrum_dword block_rate =
                3500000 / libspectrum_tape_block_bit_length( block );

            if( found ) {
                if( block_rate != sample_rate ) {
                    libspectrum_print_error(
                        LIBSPECTRUM_ERROR_WARNING,
                        "find_sample_rate: converting tape with mixed sample rates; conversion may well not work"
                    );
                }
            }
            sample_rate = block_rate;
            found = 1;
        }
        break;

        case LIBSPECTRUM_TAPE_BLOCK_ROM:
        case LIBSPECTRUM_TAPE_BLOCK_TURBO:
        case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
        case LIBSPECTRUM_TAPE_BLOCK_PULSES:
        case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
        case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
        case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
        case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
        case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
        case LIBSPECTRUM_TAPE_BLOCK_JUMP:
        case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
        case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
        case LIBSPECTRUM_TAPE_BLOCK_SELECT:
        case LIBSPECTRUM_TAPE_BLOCK_STOP48:
        case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
        case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
        case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
        case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
        case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
        case LIBSPECTRUM_TAPE_BLOCK_CONCAT:
            break;

        default:
            libspectrum_print_error(
                LIBSPECTRUM_ERROR_LOGIC,
                "libspectrum_csw_write: unknown block type 0x%02x",
                libspectrum_tape_block_type( block )
            );

        }
    }

    return sample_rate;
}
예제 #14
0
파일: tzx_write.c 프로젝트: CiaranG/ZXdroid
libspectrum_error
internal_tzx_write( libspectrum_byte **buffer, size_t *length,
		    libspectrum_tape *tape )
{
  libspectrum_error error;
  libspectrum_tape_iterator iterator;
  libspectrum_tape_block *block;
  libspectrum_byte *ptr = *buffer;

  size_t signature_length = strlen( libspectrum_tzx_signature );

  /* First, write the .tzx signature and the version numbers */
  libspectrum_make_room( buffer, signature_length + 2, &ptr, length );

  memcpy( ptr, libspectrum_tzx_signature, signature_length );
  ptr += signature_length;

  *ptr++ = 1;		/* Major version number */
  *ptr++ = 20;		/* Minor version number */

  for( block = libspectrum_tape_iterator_init( &iterator, tape );
       block;
       block = libspectrum_tape_iterator_next( &iterator )       )
  {
    switch( libspectrum_tape_block_type( block ) ) {

    case LIBSPECTRUM_TAPE_BLOCK_ROM:
      tzx_write_rom( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_TURBO:
      tzx_write_turbo( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE:
      tzx_write_pure_tone( block, buffer, &ptr, length );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_PULSES:
      tzx_write_pulses( block, buffer, &ptr, length );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA:
      tzx_write_data( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA:
      tzx_write_raw_data( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA:
      error = tzx_write_generalised_data( block, buffer, &ptr, length );
      if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( *buffer ); return error; }
      break;

    case LIBSPECTRUM_TAPE_BLOCK_PAUSE:
      tzx_write_pause( block, buffer, &ptr, length );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_GROUP_START:
      tzx_write_group_start( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_GROUP_END:
      tzx_write_empty_block( buffer, &ptr, length, libspectrum_tape_block_type( block ) );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_JUMP:
      tzx_write_jump( block, buffer, &ptr, length );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_LOOP_START:
      tzx_write_loop_start( block, buffer, &ptr, length );
      break;
    case LIBSPECTRUM_TAPE_BLOCK_LOOP_END:
      tzx_write_empty_block( buffer, &ptr, length, libspectrum_tape_block_type( block ) );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_SELECT:
      tzx_write_select( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_STOP48:
      tzx_write_stop( buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_COMMENT:
      tzx_write_comment( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_MESSAGE:
      tzx_write_message( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO:
      tzx_write_archive_info( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_HARDWARE:
      tzx_write_hardware( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_CUSTOM:
      tzx_write_custom( block, buffer, &ptr, length );
      break;

    case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE:
      error = tzx_write_rle( block, buffer, &ptr, length, tape, iterator );
      if( error != LIBSPECTRUM_ERROR_NONE ) { libspectrum_free( *buffer ); return error; }
      break;

    default:
      libspectrum_free( *buffer );
      libspectrum_print_error(
        LIBSPECTRUM_ERROR_LOGIC,
	"libspectrum_tzx_write: unknown block type 0x%02x",
	libspectrum_tape_block_type( block )
      );
      return LIBSPECTRUM_ERROR_LOGIC;
    }
  }

  (*length) = ptr - *buffer;

  return LIBSPECTRUM_ERROR_NONE;
}