void laserdisc_device::update_slider_pos() { attotime curtime = machine().time(); // if not moving, update to now if (m_attospertrack == 0) m_sliderupdate = curtime; // otherwise, compute the number of tracks covered else { attoseconds_t delta = (curtime - m_sliderupdate).as_attoseconds(); // determine how many tracks we covered and advance if (m_attospertrack >= 0) { INT32 tracks_covered = delta / m_attospertrack; add_and_clamp_track(tracks_covered); if (tracks_covered != 0) m_sliderupdate += attotime(0, tracks_covered * m_attospertrack); } else { INT32 tracks_covered = delta / -m_attospertrack; add_and_clamp_track(-tracks_covered); if (tracks_covered != 0) m_sliderupdate += attotime(0, tracks_covered * -m_attospertrack); } } }
void speaker_level_w(device_t *device, int new_level) { speaker_state *sp = get_safe_token(device); int volume; attotime time; if (new_level == sp->level) return; if (new_level < 0) new_level = 0; else if (new_level >= sp->num_levels) new_level = sp->num_levels - 1; volume = sp->levels[sp->level]; time = device->machine().time(); if (time < sp->channel_next_sample_time) { /* Stream sample is yet unfinished, but we may have one or more interm. samples */ update_interm_samples(sp, time, volume); /* Do not forget to update speaker state before returning! */ sp->level = new_level; return; } /* Reaching here means such time has passed since last stream update * that we can add at least one complete sample to the stream. * The details have to be handled by speaker_sound_update() */ /* Force streams.c to update sound until this point in time now */ sp->channel->update(); /* This is redundant because time update has to be done within speaker_sound_update() anyway, * however this ensures synchronization between the speaker and stream timing: */ sp->channel_last_sample_time = sp->channel->sample_time(); sp->channel_next_sample_time = sp->channel_last_sample_time + attotime(0, sp->channel_sample_period); sp->next_interm_sample_time = sp->channel_last_sample_time + attotime(0, sp->interm_sample_period); sp->last_update_time = sp->channel_last_sample_time; /* Assertion: time - last_update_time < channel_sample_period, i.e. time < channel_next_sample_time */ /* The overshooting fraction of time will make zero, one or more interm. samples: */ update_interm_samples(sp, time, volume); /* Finally update speaker state before returning */ sp->level = new_level; } /* speaker_level_w */
DRIVER_INIT_MEMBER(europc_pc_state,europc) { UINT8 *rom = &memregion("maincpu")->base()[0]; int i; /* fix century rom bios bug ! if year <79 month (and not CENTURY) is loaded with 0x20 */ if (rom[0xff93e]==0xb6){ // mov dh, UINT8 a; rom[0xff93e]=0xb5; // mov ch, for (i=0xf8000, a=0; i<0xfffff; i++ ) a+=rom[i]; rom[0xfffff]=256-a; } memset(&m_rtc_data,0,sizeof(m_rtc_data)); m_rtc_reg = 0; m_rtc_state = 0; m_rtc_data[0xf]=1; m_rtc_timer = timer_alloc(); m_rtc_timer->adjust(attotime::zero, 0, attotime(1,0)); // europc_rtc_set_time(); machine().device<nvram_device>("nvram")->set_base(m_rtc_data, sizeof(m_rtc_data)); m_aga = machine().device<isa8_aga_device>("aga:aga"); }
ROM_END /*------------------------------------------------- device start callback -------------------------------------------------*/ static DEVICE_START( namco_52xx ) { namco_52xx_interface *intf = (namco_52xx_interface *)device->static_config(); namco_52xx_state *state = get_safe_token(device); astring tempstring; /* find our CPU */ state->m_cpu = device->subdevice("mcu"); assert(state->m_cpu != NULL); /* find the attached discrete sound device */ assert(intf->discrete != NULL); state->m_discrete = device->machine().device(intf->discrete); assert(state->m_discrete != NULL); state->m_basenode = intf->firstnode; /* resolve our read/write callbacks */ state->m_romread.resolve(intf->romread, *device); state->m_si.resolve(intf->si, *device); /* start the external clock */ if (intf->extclock != 0) device->machine().scheduler().timer_pulse(attotime(0, intf->extclock), FUNC(external_clock_pulse), 0, device); }
void ttl74123_device::start_pulse() { attotime duration = compute_duration(); if(timer_running()) { /* retriggering, but not if we are called to quickly */ attotime delay_time = attotime(0, ATTOSECONDS_PER_SECOND * m_cap * 220); if(m_timer->elapsed() >= delay_time) { m_timer->adjust(duration); if (LOG) logerror("74123 %s: Retriggering pulse. Duration: %f\n", tag(), duration.as_double()); } else { if (LOG) logerror("74123 %s: Retriggering failed.\n", tag()); } } else { /* starting */ m_timer->adjust(duration); set_output(); if (LOG) logerror("74123 %s: Starting pulse. Duration: %f\n", tag(), duration.as_double()); } }
void video_manager::recompute_speed(const attotime &emutime) { // if we don't have a starting time yet, or if we're paused, reset our starting point if (m_speed_last_realtime == 0 || machine().paused()) { m_speed_last_realtime = osd_ticks(); m_speed_last_emutime = emutime; } // if it has been more than the update interval, update the time attotime delta_emutime = emutime - m_speed_last_emutime; if (delta_emutime > attotime(0, ATTOSECONDS_PER_SPEED_UPDATE)) { // convert from ticks to attoseconds osd_ticks_t realtime = osd_ticks(); osd_ticks_t delta_realtime = realtime - m_speed_last_realtime; osd_ticks_t tps = osd_ticks_per_second(); m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime; // remember the last times m_speed_last_realtime = realtime; m_speed_last_emutime = emutime; // if we're throttled, this time period counts for overall speed; otherwise, we reset the counter if (!m_fastforward) m_overall_valid_counter++; else m_overall_valid_counter = 0; // if we've had at least 4 consecutive valid periods, accumulate stats if (m_overall_valid_counter >= 4) { m_overall_real_ticks += delta_realtime; while (m_overall_real_ticks >= tps) { m_overall_real_ticks -= tps; m_overall_real_seconds++; } m_overall_emutime += delta_emutime; } } // if we're past the "time-to-execute" requested, signal an exit if (m_seconds_to_run != 0 && emutime.seconds() >= m_seconds_to_run) { screen_device *screen = machine().first_screen(); if (screen != nullptr) { // create a final screenshot emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); osd_file::error filerr = file.open(machine().basename(), PATH_SEPARATOR "final.png"); if (filerr == osd_file::error::NONE) save_snapshot(screen, file); } //printf("Scheduled exit at %f\n", emutime.as_double()); // schedule our demise machine().schedule_exit(); } }
void europc_rtc_init(running_machine &machine) { memset(&europc_rtc,0,sizeof(europc_rtc)); europc_rtc.data[0xf]=1; europc_rtc.timer = machine.scheduler().timer_alloc(FUNC(europc_rtc_timer)); europc_rtc.timer->adjust(attotime::zero, 0, attotime(1,0)); }
/* Called via stream->update(). * This can be triggered by the core (based on emulated time) or via speaker_level_w(). */ static STREAM_UPDATE( speaker_sound_update ) { speaker_state *sp = (speaker_state *) param; stream_sample_t *buffer = outputs[0]; int volume = sp->levels[sp->level]; double filtered_volume; attotime sampled_time = attotime::zero; if (samples > 0) { /* Prepare to update time state */ sampled_time = attotime(0, sp->channel_sample_period); if (samples > 1) sampled_time *= samples; /* Note: since the stream is in the process of being updated, * stream->sample_time() will return the time before the update! (MAME 0.130) * Avoid using it here in order to avoid a subtle dependence on the stream implementation. */ } if (samples-- > 0) { /* Note that first interm. sample may be composed... */ filtered_volume = update_interm_samples_get_filtered_volume(sp, volume); /* Composite volume is now quantized to the stream resolution */ *buffer++ = (stream_sample_t)filtered_volume; /* Any additional samples will be homogeneous, however may need filtering across samples: */ while (samples-- > 0) { filtered_volume = update_interm_samples_get_filtered_volume(sp, volume); *buffer++ = (stream_sample_t)filtered_volume; } /* Update the time state */ sp->channel_last_sample_time += sampled_time; sp->channel_next_sample_time = sp->channel_last_sample_time + attotime(0, sp->channel_sample_period); sp->next_interm_sample_time = sp->channel_last_sample_time + attotime(0, sp->interm_sample_period); sp->last_update_time = sp->channel_last_sample_time; } } /* speaker_sound_update */
void europc_pc_state::europc_rtc_init() { memset(&m_rtc_data,0,sizeof(m_rtc_data)); m_rtc_reg = 0; m_rtc_state = 0; m_rtc_data[0xf]=1; m_rtc_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(europc_pc_state::europc_rtc_timer),this)); m_rtc_timer->adjust(attotime::zero, 0, attotime(1,0)); }
void namco_52xx_device::device_start() { /* resolve our read/write callbacks */ m_romread.resolve_safe(0); m_si.resolve_safe(0); /* start the external clock */ if (m_extclock != 0) machine().scheduler().timer_pulse(attotime(0, m_extclock), timer_expired_delegate(FUNC(namco_52xx_device::external_clock_pulse),this), 0); }
void speaker_sound_device::device_reset() { int i; m_level = 0; for (i = 0; i < FILTER_LENGTH; i++) m_composed_volume[i] = 0; m_composed_sample_index = 0; m_last_update_time = machine().time(); m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate()); m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period); m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER; m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period); m_channel_last_sample_time = m_channel->sample_time(); m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period); m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period); m_interm_sample_index = 0; m_prevx = m_prevy = 0.0; }
void speaker_sound_device::finalize_interm_sample(int volume) { double fraction; /* Fill the composed sample up if it was incomplete */ fraction = make_fraction(m_next_interm_sample_time, m_last_update_time, m_interm_sample_period_secfrac); m_composed_volume[m_composed_sample_index] += volume * fraction; /* Update time state */ m_last_update_time = m_next_interm_sample_time; m_next_interm_sample_time += attotime(0, m_interm_sample_period); /* For compatibility with filtering, do not incr. index and initialise next sample yet. */ }
static void finalize_interm_sample(speaker_state *sp, int volume) { double fraction; /* Fill the composed sample up if it was incomplete */ fraction = make_fraction(sp->next_interm_sample_time, sp->last_update_time, sp->interm_sample_period_secfrac); sp->composed_volume[sp->composed_sample_index] += volume * fraction; /* Update time state */ sp->last_update_time = sp->next_interm_sample_time; sp->next_interm_sample_time += attotime(0, sp->interm_sample_period); /* For compatibility with filtering, do not incr. index and initialise next sample yet. */ }
INPUT_PORTS_END void cms_state::machine_start() { m_bank1->configure_entries(0, 16, m_rom->base(), 0x4000); memset(&m_rtc_data, 0, sizeof(m_rtc_data)); m_rtc_reg = 0; m_rtc_state = 0; m_rtc_data[0xf] = 1; m_rtc_timer = timer_alloc(); m_rtc_timer->adjust(attotime::zero, 0, attotime(1, 0)); }
static WRITE8_DEVICE_HANDLER( output_port_0_w ) { /* Note: We compute the timeout time on a write here. Unfortunately, the situation is kind of weird, because the discrete sound system is also affected by this timeout. In fact, it is very important that our timing calculation timeout AFTER the sound system's equivalent computation, or else we will hang notes. */ hitme_state *state = device->machine->driver_data<hitme_state>(); UINT8 raw_game_speed = input_port_read(device->machine, "R3"); double resistance = raw_game_speed * 25000 / 100; attotime duration = attotime(0, ATTOSECONDS_PER_SECOND * 0.45 * 6.8e-6 * resistance * (data + 1)); state->timeout_time = device->machine->time() + duration; discrete_sound_w(device, HITME_DOWNCOUNT_VAL, data); discrete_sound_w(device, HITME_OUT0, 1); }
void running_machine::soft_reset(void *ptr, INT32 param) { logerror("Soft reset\n"); // temporarily in the reset phase m_current_phase = MACHINE_PHASE_RESET; // set up the watchdog timer; only start off enabled if explicitly configured m_watchdog_enabled = (config().m_watchdog_vblank_count != 0 || config().m_watchdog_time != attotime::zero); watchdog_reset(); m_watchdog_enabled = true; // call all registered reset callbacks call_notifiers(MACHINE_NOTIFY_RESET); // setup autoboot if needed m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0); // now we're running m_current_phase = MACHINE_PHASE_RUNNING; }
static TIMER_CALLBACK( schaser_effect_555_cb ) { _8080bw_state *state = machine.driver_data<_8080bw_state>(); int effect = param; attotime new_time; /* Toggle 555 output */ state->m_schaser_effect_555_is_low = !state->m_schaser_effect_555_is_low; state->m_schaser_effect_555_time_remain = attotime::zero; state->m_schaser_effect_555_time_remain_savable = state->m_schaser_effect_555_time_remain.as_double(); if (state->m_schaser_effect_555_is_low) new_time = PERIOD_OF_555_ASTABLE(0, RES_K(20), CAP_U(1)) / 2; else { if (effect) new_time = attotime(0, ATTOSECONDS_PER_SECOND * .8873 * schaser_effect_rc[effect]); else new_time = attotime::never; } state->m_schaser_effect_555_timer->adjust(new_time, effect); sn76477_enable_w(state->m_sn, !(state->m_schaser_effect_555_is_low || state->m_schaser_explosion)); sn76477_one_shot_cap_voltage_w(state->m_sn, !(state->m_schaser_effect_555_is_low || state->m_schaser_explosion) ? 0 : SN76477_EXTERNAL_VOLTAGE_DISCONNECT); }
void video_manager::exit() { // stop recording any movie for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++) { if (index < m_avis.size()) end_recording_avi(index); if (index < m_mngs.size()) end_recording_mng(index); if (!m_snap_native) break; } // free the snapshot target machine().render().target_free(m_snap_target); m_snap_bitmap.reset(); // print a final result if we have at least 2 seconds' worth of data if (!emulator_info::standalone() && m_overall_emutime.seconds() >= 1) { osd_ticks_t tps = osd_ticks_per_second(); double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps; double final_emu_time = m_overall_emutime.as_double(); osd_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds()); } }
void video_manager::update_throttle(attotime emutime) { /* Throttling theory: This routine is called periodically with an up-to-date emulated time. The idea is to synchronize real time with emulated time. We do this by "throttling", or waiting for real time to catch up with emulated time. In an ideal world, it will take less real time to emulate and render each frame than the emulated time, so we need to slow things down to get both times in sync. There are many complications to this model: * some games run too slow, so each frame we get further and further behind real time; our only choice here is to not throttle * some games have very uneven frame rates; one frame will take a long time to emulate, and the next frame may be very fast * we run on top of multitasking OSes; sometimes execution time is taken away from us, and this means we may not get enough time to emulate one frame * we may be paused, and emulated time may not be marching forward * emulated time could jump due to resetting the machine or restoring from a saved state */ static const UINT8 popcount[256] = { 0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8 }; // outer scope so we can break out in case of a resync while (1) { // apply speed factor to emu time if (m_speed != 0 && m_speed != 1000) { // multiply emutime by 1000, then divide by the global speed factor emutime = (emutime * 1000) / m_speed; } // compute conversion factors up front osd_ticks_t ticks_per_second = osd_ticks_per_second(); attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second * m_throttle_rate; // if we're paused, emutime will not advance; instead, we subtract a fixed // amount of time (1/60th of a second) from the emulated time that was passed in, // and explicitly reset our tracked real and emulated timers to that value ... // this means we pretend that the last update was exactly 1/60th of a second // ago, and was in sync in both real and emulated time if (machine().paused()) { m_throttle_emutime = emutime - attotime(0, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE); m_throttle_realtime = m_throttle_emutime; } // attempt to detect anomalies in the emulated time by subtracting the previously // reported value from our current value; this should be a small value somewhere // between 0 and 1/10th of a second ... anything outside of this range is obviously // wrong and requires a resync attoseconds_t emu_delta_attoseconds = (emutime - m_throttle_emutime).as_attoseconds(); if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10) { if (LOG_THROTTLE) machine().logerror("Resync due to weird emutime delta: %s\n", attotime(0, emu_delta_attoseconds).as_string(18)); break; } // now determine the current real time in OSD-specified ticks; we have to be careful // here because counters can wrap, so we only use the difference between the last // read value and the current value in our computations osd_ticks_t diff_ticks = osd_ticks() - m_throttle_last_ticks; m_throttle_last_ticks += diff_ticks; // if it has been more than a full second of real time since the last call to this // function, we just need to resynchronize if (diff_ticks >= ticks_per_second) { if (LOG_THROTTLE) machine().logerror("Resync due to real time advancing by more than 1 second\n"); break; } // convert this value into attoseconds for easier comparison attoseconds_t real_delta_attoseconds = diff_ticks * attoseconds_per_tick; // now update our real and emulated timers with the current values m_throttle_emutime = emutime; m_throttle_realtime += attotime(0, real_delta_attoseconds); // keep a history of whether or not emulated time beat real time over the last few // updates; this can be used for future heuristics m_throttle_history = (m_throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds); // determine how far ahead real time is versus emulated time; note that we use the // accumulated times for this instead of the deltas for the current update because // we want to track time over a longer duration than a single update attoseconds_t real_is_ahead_attoseconds = (m_throttle_emutime - m_throttle_realtime).as_attoseconds(); // if we're more than 1/10th of a second out, or if we are behind at all and emulation // is taking longer than the real frame, we just need to resync if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 || (real_is_ahead_attoseconds < 0 && popcount[m_throttle_history & 0xff] < 6)) { if (LOG_THROTTLE) machine().logerror("Resync due to being behind: %s (history=%08X)\n", attotime(0, -real_is_ahead_attoseconds).as_string(18), m_throttle_history); break; } // if we're behind, it's time to just get out if (real_is_ahead_attoseconds < 0) return; // compute the target real time, in ticks, where we want to be osd_ticks_t target_ticks = m_throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick; // throttle until we read the target, and update real time to match the final time diff_ticks = throttle_until_ticks(target_ticks) - m_throttle_last_ticks; m_throttle_last_ticks += diff_ticks; m_throttle_realtime += attotime(0, diff_ticks * attoseconds_per_tick); return; } // reset realtime and emutime to the same value m_throttle_realtime = m_throttle_emutime = emutime; }
void video_manager::exit() { // stop recording any movie end_recording(MF_AVI); end_recording(MF_MNG); // free the snapshot target machine().render().target_free(m_snap_target); m_snap_bitmap.reset(); // print a final result if we have at least 2 seconds' worth of data if (m_overall_emutime.seconds() >= 1) { osd_ticks_t tps = osd_ticks_per_second(); double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps; double final_emu_time = m_overall_emutime.as_double(); osd_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds()); } }
void mame_machine_manager::reset() { // setup autoboot if needed m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0); }
void video_manager::exit() { // stop recording any movie end_recording(); // free all the graphics elements for (int i = 0; i < MAX_GFX_ELEMENTS; i++) gfx_element_free(machine().gfx[i]); // free the snapshot target machine().render().target_free(m_snap_target); m_snap_bitmap.reset(); // print a final result if we have at least 2 seconds' worth of data if (m_overall_emutime.seconds >= 1) { osd_ticks_t tps = osd_ticks_per_second(); double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps; double final_emu_time = m_overall_emutime.as_double(); mame_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds); } }
void device_scheduler::timeslice() { bool call_debugger = ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0); // build the execution list if we don't have one yet if (m_execute_list == NULL) rebuild_execute_list(); // execute timers execute_timers(); // loop until we hit the next timer while (m_basetime < m_timer_list->m_expire) { // by default, assume our target is the end of the next quantum attotime target = m_basetime + attotime(0, m_quantum_list.first()->m_actual); // however, if the next timer is going to fire before then, override if (m_timer_list->m_expire < target) target = m_timer_list->m_expire; LOG(("------------------\n")); LOG(("cpu_timeslice: target = %s\n", target.as_string())); // apply pending suspension changes UINT32 suspendchanged = 0; for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec) { suspendchanged |= exec->m_suspend ^ exec->m_nextsuspend; exec->m_suspend = exec->m_nextsuspend; exec->m_nextsuspend &= ~SUSPEND_REASON_TIMESLICE; exec->m_eatcycles = exec->m_nexteatcycles; } // recompute the execute list if any CPUs changed their suspension state if (suspendchanged != 0) rebuild_execute_list(); // loop over non-suspended CPUs for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec) { // only process if our target is later than the CPU's current time (coarse check) if (target.seconds >= exec->m_localtime.seconds) { // compute how many attoseconds to execute this CPU attoseconds_t delta = target.attoseconds - exec->m_localtime.attoseconds; if (delta < 0 && target.seconds > exec->m_localtime.seconds) delta += ATTOSECONDS_PER_SECOND; assert(delta == (target - exec->m_localtime).as_attoseconds()); // if we have enough for at least 1 cycle, do the math if (delta >= exec->m_attoseconds_per_cycle) { // compute how many cycles we want to execute int ran = exec->m_cycles_running = divu_64x32((UINT64)delta >> exec->m_divshift, exec->m_divisor); LOG((" cpu '%s': %d cycles\n", exec->device().tag(), exec->m_cycles_running)); // if we're not suspended, actually execute if (exec->m_suspend == 0) { g_profiler.start(exec->m_profiler); // note that this global variable cycles_stolen can be modified // via the call to cpu_execute exec->m_cycles_stolen = 0; m_executing_device = exec; *exec->m_icountptr = exec->m_cycles_running; if (!call_debugger) exec->run(); else { debugger_start_cpu_hook(&exec->device(), target); exec->run(); debugger_stop_cpu_hook(&exec->device()); } // adjust for any cycles we took back assert(ran >= *exec->m_icountptr); ran -= *exec->m_icountptr; assert(ran >= exec->m_cycles_stolen); ran -= exec->m_cycles_stolen; g_profiler.stop(); } // account for these cycles exec->m_totalcycles += ran; // update the local time for this CPU attotime delta = attotime(0, exec->m_attoseconds_per_cycle * ran); assert(delta >= attotime::zero); exec->m_localtime += delta; LOG((" %d ran, %d total, time = %s\n", ran, (INT32)exec->m_totalcycles, exec->m_localtime.as_string())); // if the new local CPU time is less than our target, move the target up, but not before the base if (exec->m_localtime < target) { assert(exec->m_localtime < target); target = max(exec->m_localtime, m_basetime); LOG((" (new target)\n")); } } } }
void js_main_loop() { attotime stoptime = scheduler->time() + attotime(0,HZ_TO_ATTOSECONDS(60)); while (scheduler->time() < stoptime) { scheduler->timeslice(); } }
void speaker_sound_device::speaker_postload() { m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period); m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period); }
void speaker_sound_device::device_start() { int i; double x; m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate(), this); m_level = 0; for (i = 0; i < FILTER_LENGTH; i++) m_composed_volume[i] = 0; m_composed_sample_index = 0; m_last_update_time = machine().time(); m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate()); m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period); m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER; m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period); m_channel_last_sample_time = m_channel->sample_time(); m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period); m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period); m_interm_sample_index = 0; m_prevx = m_prevy = 0.0; /* Note: To avoid time drift due to floating point inaccuracies, * it is good if the speaker time synchronizes itself with the stream timing regularly. */ /* Compute filter kernel; */ /* (Done for each device though the data is shared... * No problem really, but should be done as part of system init if I knew how) */ #if 1 /* This is an approximated sinc (a perfect sinc makes an ideal low-pass filter). * FILTER_STEP determines the cutoff frequency, * which should be below the Nyquist freq, i.e. half the sample rate. * Smaller step => kernel extends in time domain => lower cutoff freq * In this case, with sinc, filter step PI corresponds to the Nyq. freq. * Since we do not get a perfect filter => must lower the cutoff freq some more. * For example, step PI/(2*RATE_MULTIPLIER) corresponds to cutoff freq = sample rate / 4; * With -samplerate 48000, cutoff freq is ca 12kHz while the Nyq. freq is 24kHz. * With -samplerate 96000, cutoff freq is ca 24kHz while the Nyq. freq is 48kHz. * For a steeper, more efficient filter, increase FILTER_LENGTH at the expense of CPU usage. */ #define FILTER_STEP (M_PI / 2 / RATE_MULTIPLIER) /* Distribute symmetrically on x axis; center has x=0 if length is odd */ for (i = 0, x = (0.5 - FILTER_LENGTH / 2.) * FILTER_STEP; i < FILTER_LENGTH; i++, x += FILTER_STEP) { if (x == 0) m_ampl[i] = 1; else m_ampl[i] = sin(x) / x; } #else /* Trivial average filter with poor frequency cutoff properties; * First zero (frequency where amplification=0) = sample rate / filter length * Cutoff frequency approx <= first zero / 2 */ for (i = 0, i < FILTER_LENGTH; i++) m_ampl[i] = 1; #endif save_item(NAME(m_level)); save_item(NAME(m_composed_volume)); save_item(NAME(m_composed_sample_index)); save_item(NAME(m_channel_last_sample_time)); save_item(NAME(m_interm_sample_index)); save_item(NAME(m_last_update_time)); save_item(NAME(m_prevx)); save_item(NAME(m_prevy)); machine().save().register_postload(save_prepost_delegate(FUNC(speaker_sound_device::speaker_postload), this)); }
static DEVICE_START( speaker ) { speaker_state *sp = get_safe_token(device); const speaker_interface *intf = (const speaker_interface *) device->static_config(); int i; double x; sp->channel = device->machine().sound().stream_alloc(*device, 0, 1, device->machine().sample_rate(), sp, speaker_sound_update); if (intf != NULL) { assert(intf->num_level > 1); assert(intf->levels != NULL); sp->num_levels = intf->num_level; sp->levels = intf->levels; } else { sp->num_levels = 2; sp->levels = default_levels; } sp->level = 0; for (i = 0; i < FILTER_LENGTH; i++) sp->composed_volume[i] = 0; sp->composed_sample_index = 0; sp->last_update_time = device->machine().time(); sp->channel_sample_period = HZ_TO_ATTOSECONDS(device->machine().sample_rate()); sp->channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(sp->channel_sample_period); sp->interm_sample_period = sp->channel_sample_period / RATE_MULTIPLIER; sp->interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(sp->interm_sample_period); sp->channel_last_sample_time = sp->channel->sample_time(); sp->channel_next_sample_time = sp->channel_last_sample_time + attotime(0, sp->channel_sample_period); sp->next_interm_sample_time = sp->channel_last_sample_time + attotime(0, sp->interm_sample_period); sp->interm_sample_index = 0; /* Note: To avoid time drift due to floating point inaccuracies, * it is good if the speaker time synchronizes itself with the stream timing regularly. */ /* Compute filter kernel; */ /* (Done for each device though the data is shared... * No problem really, but should be done as part of system init if I knew how) */ #if 1 /* This is an approximated sinc (a perfect sinc makes an ideal low-pass filter). * FILTER_STEP determines the cutoff frequency, * which should be below the Nyquist freq, i.e. half the sample rate. * Smaller step => kernel extends in time domain => lower cutoff freq * In this case, with sinc, filter step PI corresponds to the Nyq. freq. * Since we do not get a perfect filter => must lower the cutoff freq some more. * For example, step PI/(2*RATE_MULTIPLIER) corresponds to cutoff freq = sample rate / 4; * With -samplerate 48000, cutoff freq is ca 12kHz while the Nyq. freq is 24kHz. * With -samplerate 96000, cutoff freq is ca 24kHz while the Nyq. freq is 48kHz. * For a steeper, more efficient filter, increase FILTER_LENGTH at the expense of CPU usage. */ #define FILTER_STEP (M_PI / 2 / RATE_MULTIPLIER) /* Distribute symmetrically on x axis; center has x=0 if length is odd */ for (i = 0, x = (0.5 - FILTER_LENGTH / 2.) * FILTER_STEP; i < FILTER_LENGTH; i++, x += FILTER_STEP) { if (x == 0) ampl[i] = 1; else ampl[i] = sin(x) / x; } #else /* Trivial average filter with poor frequency cutoff properties; * First zero (frequency where amplification=0) = sample rate / filter length * Cutoff frequency approx <= first zero / 2 */ for (i = 0, i < FILTER_LENGTH; i++) ampl[i] = 1; #endif }
void device_scheduler::timeslice() { bool call_debugger = ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0); // build the execution list if we don't have one yet if (UNEXPECTED(m_execute_list == NULL)) rebuild_execute_list(); // if the current quantum has expired, find a new one while (m_basetime >= m_quantum_list.first()->m_expire) m_quantum_allocator.reclaim(m_quantum_list.detach_head()); // loop until we hit the next timer while (m_basetime < m_timer_list->m_expire) { // by default, assume our target is the end of the next quantum attotime target = m_basetime + attotime(0, m_quantum_list.first()->m_actual); // however, if the next timer is going to fire before then, override if (m_timer_list->m_expire < target) target = m_timer_list->m_expire; // do we have pending suspension changes? if (m_suspend_changes_pending) apply_suspend_changes(); // loop over all CPUs for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec) { // only process if this CPU is executing or truly halted (not yielding) // and if our target is later than the CPU's current time (coarse check) if (EXPECTED((exec->m_suspend == 0 || exec->m_eatcycles) && target.seconds >= exec->m_localtime.seconds)) { // compute how many attoseconds to execute this CPU attoseconds_t delta = target.attoseconds - exec->m_localtime.attoseconds; if (delta < 0 && target.seconds > exec->m_localtime.seconds) delta += ATTOSECONDS_PER_SECOND; assert(delta == (target - exec->m_localtime).as_attoseconds()); // if we have enough for at least 1 cycle, do the math if (delta >= exec->m_attoseconds_per_cycle) { // compute how many cycles we want to execute int ran = exec->m_cycles_running = divu_64x32((UINT64)delta >> exec->m_divshift, exec->m_divisor); // if we're not suspended, actually execute if (exec->m_suspend == 0) { // note that this global variable cycles_stolen can be modified // via the call to cpu_execute exec->m_cycles_stolen = 0; m_executing_device = exec; *exec->m_icountptr = exec->m_cycles_running; if (!call_debugger) exec->run(); else { debugger_start_cpu_hook(&exec->device(), target); exec->run(); debugger_stop_cpu_hook(&exec->device()); } // adjust for any cycles we took back assert(ran >= *exec->m_icountptr); ran -= *exec->m_icountptr; assert(ran >= exec->m_cycles_stolen); ran -= exec->m_cycles_stolen; } // account for these cycles exec->m_totalcycles += ran; // update the local time for this CPU attotime delta(0, exec->m_attoseconds_per_cycle * ran); assert(delta >= attotime::zero); exec->m_localtime += delta; // if the new local CPU time is less than our target, move the target up, but not before the base if (exec->m_localtime < target) { target = max(exec->m_localtime, m_basetime); } } } }
/*------------------------------------------------- call_load -------------------------------------------------*/ bool snapshot_image_device::call_load() { /* adjust the timer */ m_timer->adjust(attotime(m_delay_seconds, m_delay_attoseconds),0); return IMAGE_INIT_PASS; }