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; }
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; }
// 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; }
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; }
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; }
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 ); }
/* 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; }
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; }
/* 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; }
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; }
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); }
/* 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; }
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; }
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; }