/* What happens when we write to the ULA? */ static void ula_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b ) { last_byte = b; display_set_lores_border( b & 0x07 ); sound_beeper( (!!(b & 0x10) << 1) + ( (!(b & 0x8)) | tape_microphone ) ); /* FIXME: shouldn't really be using the memory capabilities here */ if( machine_current->timex ) { ula_default_value = 0x5f; } else if( machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY ) { ula_default_value = 0xbf; } else if( machine_current->capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY || !settings_current.issue2 ) { /* 128K always acts like an Issue 3 */ ula_default_value = b & 0x10 ? 0xff : 0xbf; } else { /* Issue 2 */ ula_default_value = b & 0x18 ? 0xff : 0xbf; } }
int tape_play( void ) { libspectrum_tape_block* block; int error; if( tape.blocks == NULL ) return 1; block = (libspectrum_tape_block*)(tape.current_block->data); /* If tape traps are active and the current block is a ROM block, do nothing, _unless_ the ROM block has already reached the pause at its end which (hopefully) means we're in the magic state involving starting slow loading whilst tape traps are active */ if( settings_current.tape_traps && block->type == LIBSPECTRUM_TAPE_BLOCK_ROM && block->types.rom.state != LIBSPECTRUM_TAPE_STATE_PAUSE ) return 0; /* Otherwise, start the tape going */ tape_playing = 1; tape_microphone = 0; sound_beeper( 1, tape_microphone ); error = tape_next_edge(); if( error ) return error; return 0; }
int tape_init( void ) { tape.blocks = NULL; tape_playing = 0; tape_microphone = 0; sound_beeper( 1, tape_microphone ); return 0; }
int tape_next_edge( void ) { int error; libspectrum_error libspec_error; libspectrum_dword edge_tstates; int flags; /* If the tape's not playing, just return */ if( ! tape_playing ) return 0; /* Get the time until the next edge */ libspec_error = libspectrum_tape_get_next_edge( &tape, &edge_tstates, &flags ); if( libspec_error != LIBSPECTRUM_ERROR_NONE ) return libspec_error; /* Invert the microphone state */ if( edge_tstates || ( flags & LIBSPECTRUM_TAPE_FLAGS_STOP ) ) { tape_microphone = !tape_microphone; sound_beeper( 1, 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 ) && machine_current->machine == SPECTRUM_MACHINE_48 ) ) { error = tape_stop(); if( error ) return error; return 0; } /* If that was the end of a block, tape traps are active _and_ the new block is a ROM loader, return without putting another event into the queue */ if( ( flags & LIBSPECTRUM_TAPE_FLAGS_BLOCK ) && settings_current.tape_traps && ((libspectrum_tape_block*)(tape.current_block->data))->type == LIBSPECTRUM_TAPE_BLOCK_ROM ) { tape_playing = 0; return 0; } /* Otherwise, put this into the event queue which will cause the next block to become active when it occurs */ error = event_add( tstates + edge_tstates, EVENT_TYPE_EDGE ); if( error ) return error; 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 ); }
unsigned int out(int h,int l,int a) { static int cpc_f4=0; /* unlike a real speccy, it seems we should only emulate exact port * number matches, rather than using bitmasks. */ if(do_cpc<1) switch(l) { case 0xfd: switch(h) { case 0xff: do_cpc=-1; write_reg: ay_current_reg=(a&15); break; case 0xbf: do_cpc=-1; write_dat: sound_ay_write(ay_current_reg,a,tstates); break; default: /* ok, since we do at least have low byte=FDh, * do bitmask for top byte to allow for * crappy .ay conversions. But don't disable * CPC autodetect, just in case. */ if((h&0xc0)==0xc0) goto write_reg; if((h&0xc0)==0x80) goto write_dat; } break; case 0xfe: do_cpc=-1; sound_beeper(a&0x10); break; } if(do_cpc>-1) switch(h) { case 0xf6: switch(a&0xc0) { case 0x80: /* write */ sound_ay_write(ay_current_reg,cpc_f4,tstates); break; case 0xc0: /* select */ ay_current_reg=(cpc_f4&15); break; } break; case 0xf4: cpc_f4=a; if(!do_cpc) { /* restart as a more CPC-ish emulation */ do_cpc=1; sound_ay_reset_cpc(); tsmax=FRAME_STATES_CPC; if(tstates>tsmax) tstates-=tsmax; } break; } return(0); /* additional t-states */ }