attotime &attotime::operator*=(UINT32 factor) { // if one of the items is attotime::never, return attotime::never if (seconds >= ATTOTIME_MAX_SECONDS) return *this = never; // 0 times anything is zero if (factor == 0) return *this = zero; // split attoseconds into upper and lower halves which fit into 32 bits UINT32 attolo; UINT32 attohi = divu_64x32_rem(attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); // scale the lower half, then split into high/low parts UINT64 temp = mulu_32x32(attolo, factor); UINT32 reslo; temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reslo); // scale the upper half, then split into high/low parts temp += mulu_32x32(attohi, factor); UINT32 reshi; temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reshi); // scale the seconds temp += mulu_32x32(seconds, factor); if (temp >= ATTOTIME_MAX_SECONDS) return *this = never; // build the result seconds = temp; attoseconds = (attoseconds_t)reslo + mul_32x32(reshi, ATTOSECONDS_PER_SECOND_SQRT); return *this; }
attotime attotime_mul(attotime _time1, UINT32 factor) { UINT32 attolo, attohi, reslo, reshi; UINT64 temp; /* if one of the items is attotime_never, return attotime_never */ if (_time1.seconds >= ATTOTIME_MAX_SECONDS) return attotime_never; /* 0 times anything is zero */ if (factor == 0) return attotime_zero; /* split attoseconds into upper and lower halves which fit into 32 bits */ attohi = divu_64x32_rem(_time1.attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); /* scale the lower half, then split into high/low parts */ temp = mulu_32x32(attolo, factor); temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reslo); /* scale the upper half, then split into high/low parts */ temp += mulu_32x32(attohi, factor); temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reshi); /* scale the seconds */ temp += mulu_32x32(_time1.seconds, factor); if (temp >= ATTOTIME_MAX_SECONDS) return attotime_never; /* build the result */ return attotime_make(temp, (attoseconds_t)reslo + mul_32x32(reshi, ATTOSECONDS_PER_SECOND_SQRT)); }
attotime attotime_div(attotime _time1, UINT32 factor) { UINT32 attolo, attohi, reshi, reslo, remainder; attotime result; UINT64 temp; /* if one of the items is attotime_never, return attotime_never */ if (_time1.seconds >= ATTOTIME_MAX_SECONDS) return attotime_never; /* ignore divide by zero */ if (factor == 0) return _time1; /* split attoseconds into upper and lower halves which fit into 32 bits */ attohi = divu_64x32_rem(_time1.attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); /* divide the seconds and get the remainder */ result.seconds = divu_64x32_rem(_time1.seconds, factor, &remainder); /* combine the upper half of attoseconds with the remainder and divide that */ temp = (INT64)attohi + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); reshi = divu_64x32_rem(temp, factor, &remainder); /* combine the lower half of attoseconds with the remainder and divide that */ temp = attolo + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); reslo = divu_64x32_rem(temp, factor, &remainder); /* round based on the remainder */ result.attoseconds = (attoseconds_t)reslo + mulu_32x32(reshi, ATTOSECONDS_PER_SECOND_SQRT); if (remainder >= factor / 2) if (++result.attoseconds >= ATTOSECONDS_PER_SECOND) { result.attoseconds = 0; result.seconds++; } return result; }
attotime &attotime::operator/=(UINT32 factor) { // if one of the items is attotime::never, return attotime::never if (seconds >= ATTOTIME_MAX_SECONDS) return *this = never; // ignore divide by zero if (factor == 0) return *this; // split attoseconds into upper and lower halves which fit into 32 bits UINT32 attolo; UINT32 attohi = divu_64x32_rem(attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); // divide the seconds and get the remainder UINT32 remainder; seconds = divu_64x32_rem(seconds, factor, &remainder); // combine the upper half of attoseconds with the remainder and divide that UINT64 temp = (INT64)attohi + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); UINT32 reshi = divu_64x32_rem(temp, factor, &remainder); // combine the lower half of attoseconds with the remainder and divide that temp = attolo + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); UINT32 reslo = divu_64x32_rem(temp, factor, &remainder); // round based on the remainder attoseconds = (attoseconds_t)reslo + mulu_32x32(reshi, ATTOSECONDS_PER_SECOND_SQRT); if (remainder >= factor / 2) if (++attoseconds >= ATTOSECONDS_PER_SECOND) { attoseconds = 0; seconds++; } return *this; }
const char *attotime::as_string(int precision) const { static char buffers[8][30]; static int nextbuf; char *buffer = &buffers[nextbuf++ % 8][0]; // special case: never if (*this == never) sprintf(buffer, "%-*s", precision, "(never)"); // case 1: we want no precision; seconds only else if (precision == 0) sprintf(buffer, "%d", seconds); // case 2: we want 9 or fewer digits of precision else if (precision <= 9) { UINT32 upper = attoseconds / ATTOSECONDS_PER_SECOND_SQRT; int temp = precision; while (temp < 9) { upper /= 10; temp++; } sprintf(buffer, "%d.%0*d", seconds, precision, upper); } // case 3: more than 9 digits of precision else { UINT32 lower; UINT32 upper = divu_64x32_rem(attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &lower); int temp = precision; while (temp < 18) { lower /= 10; temp++; } sprintf(buffer, "%d.%09d%0*d", seconds, upper, precision - 9, lower); } return buffer; }
const char *attotime_string(attotime _time, int precision) { static char buffers[8][30]; static int nextbuf; char *buffer = &buffers[nextbuf++ % 8][0]; /* case 1: we want no precision; seconds only */ if (precision == 0) sprintf(buffer, "%d", _time.seconds); /* case 2: we want 9 or fewer digits of precision */ else if (precision <= 9) { UINT32 upper = _time.attoseconds / ATTOSECONDS_PER_SECOND_SQRT; int temp = precision; while (temp < 9) { upper /= 10; temp++; } sprintf(buffer, "%d.%0*d", _time.seconds, precision, upper); } /* case 3: more than 9 digits of precision */ else { UINT32 lower; UINT32 upper = divu_64x32_rem(_time.attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &lower); int temp = precision; while (temp < 18) { lower /= 10; temp++; } sprintf(buffer, "%d.%09d%0*d", _time.seconds, upper, precision - 9, lower); } return buffer; }
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 == nullptr)) 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; LOG(("------------------\n")); LOG(("cpu_timeslice: target = %s\n", target.as_string(PRECISION))); // 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 != nullptr; 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(u64(delta) >> exec->m_divshift, exec->m_divisor); LOG((" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, 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 deltatime; if (ran < exec->m_cycles_per_second) deltatime = attotime(0, exec->m_attoseconds_per_cycle * ran); else { u32 remainder; s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, &remainder); deltatime = attotime(secs, u64(remainder) * exec->m_attoseconds_per_cycle); } assert(deltatime >= attotime::zero); exec->m_localtime += deltatime; LOG((" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION))); // 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 = std::max(exec->m_localtime, m_basetime); LOG((" (new target)\n")); } } } }