/* Append to the current tape file in memory; returns 0 if a block was saved or non-zero if there was an error at the emulator level, or tape traps are not active */ int tape_save_trap( void ) { libspectrum_tape_block *block; libspectrum_byte parity, *data; size_t length; int i; /* Do nothing if tape traps aren't active */ if( !settings_current.tape_traps || tape_recording ) return 2; /* Check we're in the right ROM */ if( ! trap_check_rom() ) return 3; block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_ROM ); /* The +2 here is for the flag and parity bytes */ length = DE + 2; libspectrum_tape_block_set_data_length( block, length ); data = malloc( length * sizeof(libspectrum_byte) ); if( !data ) { free( block ); return 1; } libspectrum_tape_block_set_data( block, data ); /* First, store the flag byte (and initialise the parity counter) */ data[0] = parity = A; /* then the main body of the data, counting parity along the way */ for( i=0; i<DE; i++) { libspectrum_byte b = readbyte_internal( IX+i ); parity ^= b; data[i+1] = b; } /* And finally the parity byte */ data[ DE+1 ] = parity; /* Give a 1 second pause after this block */ libspectrum_tape_block_set_pause( block, 1000 ); libspectrum_tape_append_block( tape, block ); tape_modified = 1; ui_tape_browser_update( UI_TAPE_BROWSER_NEW_BLOCK, block ); /* And then return via the RET at #053E, except on Timex 2068 at #00E4 */ if ( machine_current->machine == LIBSPECTRUM_MACHINE_TC2068 || machine_current->machine == LIBSPECTRUM_MACHINE_TS2068 ) { PC = 0x00e4; } else { PC = 0x053e; } 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( 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; }
/* Append to the current tape file in memory; returns 0 if a block was saved or non-zero if there was an error at the emulator level, or tape traps are not active */ int tape_save_trap( void ) { libspectrum_tape_block *block; libspectrum_tape_rom_block *rom_block; libspectrum_byte parity; int i; /* Do nothing if tape traps aren't active */ if( ! settings_current.tape_traps ) return 2; /* Check we're in the right ROM */ if( ! trap_check_rom() ) return 3; /* Get a new block to store this data in */ block = (libspectrum_tape_block*)malloc( sizeof( libspectrum_tape_block )); if( block == NULL ) return 1; /* This is a ROM block */ block->type = LIBSPECTRUM_TAPE_BLOCK_ROM; rom_block = &(block->types.rom); /* The +2 here is for the flag and parity bytes */ rom_block->length = DE + 2; rom_block->data = (libspectrum_byte*)malloc( rom_block->length * sizeof(libspectrum_byte) ); if( rom_block->data == NULL ) { free( block ); return 1; } /* First, store the flag byte (and initialise the parity counter) */ rom_block->data[0] = parity = A; /* then the main body of the data, counting parity along the way */ for( i=0; i<DE; i++) { libspectrum_byte b = readbyte( IX+i ); parity ^= b; rom_block->data[i+1] = b; } /* And finally the parity byte */ rom_block->data[ DE+1 ] = parity; /* Give a 1 second pause after this block */ rom_block->pause = 1000; /* Add the block to the current tape file */ tape.blocks = g_slist_append( tape.blocks, (gpointer)block ); /* If we previously didn't have a tape loaded ( implied by tape.current_block == NULL ), set up so that we point to the start of the tape */ if( tape.current_block == NULL ) { tape.current_block = tape.blocks; libspectrum_tape_init_block((libspectrum_tape_block*)tape.blocks->data); } /* And then return via the RET at #053E */ PC = 0x053e; 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( void ) { libspectrum_tape_block *current_block; libspectrum_tape_rom_block *rom_block; GSList *block_ptr; libspectrum_tape_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() ) return 3; /* Return with error if no tape file loaded */ if( tape.blocks == NULL ) return 1; current_block = (libspectrum_tape_block*)(tape.current_block->data); block_ptr = tape.current_block->next; /* Loop if we hit the end of the tape */ if( block_ptr == NULL ) { block_ptr = tape.blocks; } next_block = (libspectrum_tape_block*)(block_ptr->data); libspectrum_tape_init_block( next_block ); /* If this block isn't a ROM loader, start it playing Then return with `error' so that we actually do whichever instruction it was that caused the trap to hit */ if( current_block->type != LIBSPECTRUM_TAPE_BLOCK_ROM ) { error = tape_play(); if( error ) return 3; return -1; } rom_block = &(current_block->types.rom); /* All returns made via the RET at #05E2 */ PC = 0x05e2; error = trap_load_block( rom_block ); if( error ) return error; /* Peek at the next block. If it's a ROM block, just move along and return */ if( next_block->type == LIBSPECTRUM_TAPE_BLOCK_ROM ) { tape.current_block = block_ptr; 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 */ current_block->types.rom.state = LIBSPECTRUM_TAPE_STATE_PAUSE; /* And start the tape playing */ error = tape_play(); /* On error, still return without error as we did sucessfully do the tape trap, and so don't want to do the trigger instruction */ if( error ) return 0; return 0; }