diff --git a/sim_defs.h b/sim_defs.h index 80e8946a..a95989a5 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -267,10 +267,13 @@ typedef uint32 t_addr; #if defined(_MSC_VER) #define SIM_INLINE _inline +#define SIM_NOINLINE _declspec (noinline) #elif defined(__GNUC__) #define SIM_INLINE inline +#define SIM_NOINLINE __attribute__ ((noinline)) #else #define SIM_INLINE +#define SIM_NOINLINE #endif /* Storage class modifier for weak link definition for sim_vm_init() */ diff --git a/sim_timer.c b/sim_timer.c index f59e1d03..e6502e8c 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -72,6 +72,11 @@ event sim_timespec_diff subtract two timespec values sim_timer_activate_after schedule unit for specific time + sim_timer_activate_time determine activation time + sim_timer_activate_time_usecs determine activation time in usecs + sim_rom_read_with_delay delay for default or specified delay + sim_get_rom_delay_factor get current or initialize 1usec delay factor + sim_set_rom_delay_factor set specific delay factor The calibration, idle, and throttle routines are OS-independent; the _os_ @@ -156,6 +161,7 @@ static uint32 sim_os_clock_resoluton_ms = 0; static uint32 sim_os_tick_hz = 0; static uint32 sim_idle_stable = SIM_IDLE_STDFLT; static uint32 sim_idle_calib_pct = 0; +static uint32 sim_rom_delay = 0; static uint32 sim_throt_ms_start = 0; static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; @@ -1023,6 +1029,7 @@ sim_throttle_unit.action = &sim_throt_svc; sim_register_clock_unit_tmr (&SIM_INTERNAL_UNIT, SIM_INTERNAL_CLK); sim_idle_enab = FALSE; /* init idle off */ sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ +sim_set_rom_delay_factor (sim_get_rom_delay_factor ()); /* initialize ROM delay factor */ clock_last = clock_start = sim_os_msec (); sim_os_clock_resoluton_ms = 1000; @@ -1235,6 +1242,7 @@ return SCPE_OK; REG sim_timer_reg[] = { { DRDATAD (IDLE_CYC_MS, sim_idle_cyc_ms, 32, "Cycles Per Millisecond"), PV_RSPC|REG_RO}, + { DRDATAD (ROM_DELAY, sim_rom_delay, 32, "ROM memory reference delay"), PV_RSPC|REG_RO}, { NULL } }; @@ -2779,3 +2787,76 @@ for (tmr=0; tmr> 8) & 0xff00) | ((val >> 24) & 0xff); +} + +static volatile int32 rom_loopval = 0; + +SIM_NOINLINE int32 sim_rom_read_with_delay (int32 val) +{ +uint32 i, l = sim_rom_delay; + +for (i = 0; i < l; i++) + rom_loopval |= (rom_loopval + val) ^ _rom_swapb (_rom_swapb (rom_loopval + val)); +return val + rom_loopval; +} + +SIM_NOINLINE uint32 sim_get_rom_delay_factor (void) +{ +/* Calibrate the loop delay factor at startup. + Do this 4 times and use the largest value computed. + The goal here is to come up with a delay factor which will throttle + a 6 byte delay loop running from ROM address space to execute + 1 instruction per usec */ + +if (sim_rom_delay == 0) { + uint32 i, ts, te, c = 10000, samples = 0; + while (1) { + c = c * 2; + te = sim_os_msec(); + while (te == (ts = sim_os_msec ())); /* align on ms tick */ + +/* This is merely a busy wait with some "work" that won't get optimized + away by a good compiler. loopval always is zero. To avoid smart compilers, + the loopval variable is referenced in the function arguments so that the + function expression is not loop invariant. It also must be referenced + by subsequent code to avoid the whole computation being eliminated. */ + + for (i = 0; i < c; i++) + rom_loopval |= (rom_loopval + ts) ^ _rom_swapb (_rom_swapb (rom_loopval + ts)); + te = sim_os_msec (); + if ((te - ts) < 50) /* sample big enough? */ + continue; + if (sim_rom_delay < (rom_loopval + (c / (te - ts) / 1000) + 1)) + sim_rom_delay = rom_loopval + (c / (te - ts) / 1000) + 1; + if (++samples >= 4) + break; + c = c / 2; + } + if (sim_rom_delay < 5) + sim_rom_delay = 5; + } +return sim_rom_delay; +} + +void sim_set_rom_delay_factor (uint32 delay) +{ +sim_rom_delay = delay; +} diff --git a/sim_timer.h b/sim_timer.h index 637a7592..aeb92cff 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -151,6 +151,9 @@ t_bool sim_timer_idle_capable (uint32 *host_ms_sleep_1, uint32 *host_tick_ms); #define PRIORITY_NORMAL 0 #define PRIORITY_ABOVE_NORMAL 1 t_stat sim_os_set_thread_priority (int below_normal_above); +uint32 sim_get_rom_delay_factor (void); +void sim_set_rom_delay_factor (uint32 delay); +int32 sim_rom_read_with_delay (int32 val); extern t_bool sim_idle_enab; /* idle enabled flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */