1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pc_system.cc 14092 2021-01-30 18:05:55Z sshwarts $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2001-2017  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 //
21 /////////////////////////////////////////////////////////////////////////
22 
23 #include "bochs.h"
24 #include "cpu/cpu.h"
25 #include "iodev/iodev.h"
26 #define LOG_THIS bx_pc_system.
27 
28 #if defined(PROVIDE_M_IPS)
29 double     m_ips; // Millions of Instructions Per Second
30 #endif
31 
32 // Option for turning off BX_TIMER_DEBUG?
33 // Check out m_ips and ips
34 
35 #define SpewPeriodicTimerInfo 0
36 #define MinAllowableTimerPeriod 1
37 
38 const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff;
39 
40   // constructor
bx_pc_system_c()41 bx_pc_system_c::bx_pc_system_c()
42 {
43   this->put("pc_system", "SYS");
44 
45   BX_ASSERT(numTimers == 0);
46 
47   // Timer[0] is the null timer.  It is initialized as a special
48   // case here.  It should never be turned off or modified, and its
49   // duration should always remain the same.
50   ticksTotal = 0; // Reset ticks since emulator started.
51   timer[0].inUse      = 1;
52   timer[0].period     = NullTimerInterval;
53   timer[0].active     = 1;
54   timer[0].continuous = 1;
55   timer[0].funct      = nullTimer;
56   timer[0].this_ptr   = this;
57   numTimers = 1; // So far, only the nullTimer.
58 }
59 
initialize(Bit32u ips)60 void bx_pc_system_c::initialize(Bit32u ips)
61 {
62   ticksTotal = 0;
63   timer[0].timeToFire = NullTimerInterval;
64   currCountdown       = NullTimerInterval;
65   currCountdownPeriod = NullTimerInterval;
66   lastTimeUsec = 0;
67   usecSinceLast = 0;
68   triggeredTimer = 0;
69   HRQ = 0;
70   kill_bochs_request = 0;
71 
72   // parameter 'ips' is the processor speed in Instructions-Per-Second
73   m_ips = double(ips) / 1000000.0L;
74 
75   BX_DEBUG(("ips = %u", (unsigned) ips));
76 }
77 
set_HRQ(bool val)78 void bx_pc_system_c::set_HRQ(bool val)
79 {
80   HRQ = val;
81   if (val)
82     BX_CPU(0)->async_event = 1;
83 }
84 
raise_INTR(void)85 void bx_pc_system_c::raise_INTR(void)
86 {
87   if (bx_dbg.interrupts)
88     BX_INFO(("pc_system: Setting INTR=1 on bootstrap processor %d", BX_BOOTSTRAP_PROCESSOR));
89 
90   BX_CPU(BX_BOOTSTRAP_PROCESSOR)->raise_INTR();
91 }
92 
clear_INTR(void)93 void bx_pc_system_c::clear_INTR(void)
94 {
95   if (bx_dbg.interrupts)
96     BX_INFO(("pc_system: Setting INTR=0 on bootstrap processor %d", BX_BOOTSTRAP_PROCESSOR));
97 
98   BX_CPU(BX_BOOTSTRAP_PROCESSOR)->clear_INTR();
99 }
100 
101 //
102 // Read from the IO memory address space
103 //
104 
105   Bit32u BX_CPP_AttrRegparmN(2)
inp(Bit16u addr,unsigned io_len)106 bx_pc_system_c::inp(Bit16u addr, unsigned io_len)
107 {
108   Bit32u ret = bx_devices.inp(addr, io_len);
109   return ret;
110 }
111 
112 //
113 // Write to the IO memory address space.
114 //
115 
116   void BX_CPP_AttrRegparmN(3)
outp(Bit16u addr,Bit32u value,unsigned io_len)117 bx_pc_system_c::outp(Bit16u addr, Bit32u value, unsigned io_len)
118 {
119   bx_devices.outp(addr, value, io_len);
120 }
121 
set_enable_a20(bool value)122 void bx_pc_system_c::set_enable_a20(bool value)
123 {
124 #if BX_SUPPORT_A20
125   bool old_enable_a20 = enable_a20;
126 
127   if (value) {
128     enable_a20 = 1;
129 #if BX_CPU_LEVEL < 2
130     a20_mask =    0xfffff;
131 #elif BX_CPU_LEVEL == 2
132     a20_mask =   0xffffff;
133 #elif BX_PHY_ADDRESS_LONG
134     a20_mask = BX_CONST64(0xffffffffffffffff);
135 #else  /* 386+ */
136     a20_mask = 0xffffffff;
137 #endif
138   }
139   else {
140     enable_a20 = 0;
141     /* mask off A20 address line */
142 #if BX_PHY_ADDRESS_LONG
143     a20_mask = BX_CONST64(0xffffffffffefffff);
144 #else
145     a20_mask = 0xffefffff;
146 #endif
147   }
148 
149   BX_DBG_A20_REPORT(enable_a20);
150 
151   BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20));
152 
153   // If there has been a transition, we need to notify the CPUs so
154   // they can potentially invalidate certain cache info based on
155   // A20-line-applied physical addresses.
156   if (old_enable_a20 != enable_a20) MemoryMappingChanged();
157 #else
158   BX_DEBUG(("set_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
159 #endif
160 }
161 
get_enable_a20(void)162 bool bx_pc_system_c::get_enable_a20(void)
163 {
164 #if BX_SUPPORT_A20
165   BX_DEBUG(("A20: get() = %u", (unsigned) enable_a20));
166 
167   return enable_a20;
168 #else
169   BX_DEBUG(("get_enable_a20: ignoring: BX_SUPPORT_A20 = 0"));
170   return 1;
171 #endif
172 }
173 
MemoryMappingChanged(void)174 void bx_pc_system_c::MemoryMappingChanged(void)
175 {
176   for (unsigned i=0; i<BX_SMP_PROCESSORS; i++)
177     BX_CPU(i)->TLB_flush();
178 }
179 
invlpg(bx_address addr)180 void bx_pc_system_c::invlpg(bx_address addr)
181 {
182   for (unsigned i=0; i<BX_SMP_PROCESSORS; i++)
183     BX_CPU(i)->TLB_invlpg(addr);
184 }
185 
Reset(unsigned type)186 int bx_pc_system_c::Reset(unsigned type)
187 {
188   // type is BX_RESET_HARDWARE or BX_RESET_SOFTWARE
189   BX_INFO(("bx_pc_system_c::Reset(%s) called",type==BX_RESET_HARDWARE?"HARDWARE":"SOFTWARE"));
190 
191   set_enable_a20(1);
192 
193   // Always reset cpu
194   for (int i=0; i<BX_SMP_PROCESSORS; i++) {
195     BX_CPU(i)->reset(type);
196   }
197 
198   // Reset devices only on Hardware resets
199   if (type==BX_RESET_HARDWARE) {
200     DEV_reset_devices(type);
201   }
202 
203   return(0);
204 }
205 
IAC(void)206 Bit8u bx_pc_system_c::IAC(void)
207 {
208   return DEV_pic_iac();
209 }
210 
exit(void)211 void bx_pc_system_c::exit(void)
212 {
213   // delete all registered timers (exception: null timer and APIC timer)
214   numTimers = 1 + BX_SUPPORT_APIC;
215   bx_devices.exit();
216   if (bx_gui) {
217     bx_gui->cleanup();
218     bx_gui->exit();
219   }
220 }
221 
register_state(void)222 void bx_pc_system_c::register_state(void)
223 {
224   bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "pc_system", "PC System State");
225   BXRS_PARAM_BOOL(list, enable_a20, enable_a20);
226   BXRS_HEX_PARAM_SIMPLE(list, a20_mask);
227   BXRS_DEC_PARAM_SIMPLE(list, currCountdown);
228   BXRS_DEC_PARAM_SIMPLE(list, currCountdownPeriod);
229   BXRS_DEC_PARAM_SIMPLE(list, ticksTotal);
230   BXRS_DEC_PARAM_SIMPLE(list, lastTimeUsec);
231   BXRS_DEC_PARAM_SIMPLE(list, usecSinceLast);
232   BXRS_PARAM_BOOL(list, HRQ, HRQ);
233 
234   bx_list_c *timers = new bx_list_c(list, "timer");
235   for (unsigned i = 0; i < numTimers; i++) {
236     char name[4];
237     sprintf(name, "%u", i);
238     bx_list_c *bxtimer = new bx_list_c(timers, name);
239     BXRS_PARAM_BOOL(bxtimer, inUse, timer[i].inUse);
240     BXRS_DEC_PARAM_FIELD(bxtimer, period, timer[i].period);
241     BXRS_DEC_PARAM_FIELD(bxtimer, timeToFire, timer[i].timeToFire);
242     BXRS_PARAM_BOOL(bxtimer, active, timer[i].active);
243     BXRS_PARAM_BOOL(bxtimer, continuous, timer[i].continuous);
244     BXRS_DEC_PARAM_FIELD(bxtimer, param, timer[i].param);
245   }
246 }
247 
248 // ================================================
249 // Bochs internal timer delivery framework features
250 // ================================================
251 
register_timer(void * this_ptr,void (* funct)(void *),Bit32u useconds,bool continuous,bool active,const char * id)252 int bx_pc_system_c::register_timer(void *this_ptr, void (*funct)(void *),
253   Bit32u useconds, bool continuous, bool active, const char *id)
254 {
255   // Convert useconds to number of ticks.
256   Bit64u ticks = (Bit64u) (double(useconds) * m_ips);
257 
258   return register_timer_ticks(this_ptr, funct, ticks, continuous, active, id);
259 }
260 
register_timer_ticks(void * this_ptr,bx_timer_handler_t funct,Bit64u ticks,bool continuous,bool active,const char * id)261 int bx_pc_system_c::register_timer_ticks(void* this_ptr, bx_timer_handler_t funct,
262     Bit64u ticks, bool continuous, bool active, const char *id)
263 {
264   unsigned i;
265 
266   // If the timer frequency is rediculously low, make it more sane.
267   // This happens when 'ips' is too low.
268   if (ticks < MinAllowableTimerPeriod) {
269     //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
270     //          ticks, MinAllowableTimerPeriod));
271     ticks = MinAllowableTimerPeriod;
272   }
273 
274   // search for new timer (i = 0 is reserved for NullTimer)
275   for (i = 1; i < numTimers; i++) {
276     if (timer[i].inUse == 0)
277       break;
278   }
279 
280   if (numTimers >= BX_MAX_TIMERS) {
281     BX_PANIC(("register_timer: too many registered timers"));
282     return -1;
283   }
284 #if BX_TIMER_DEBUG
285   if (this_ptr == NULL)
286     BX_PANIC(("register_timer_ticks: this_ptr is NULL!"));
287   if (funct == NULL)
288     BX_PANIC(("register_timer_ticks: funct is NULL!"));
289 #endif
290 
291   timer[i].inUse      = 1;
292   timer[i].period     = ticks;
293   timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + ticks;
294   timer[i].active     = active;
295   timer[i].continuous = continuous;
296   timer[i].funct      = funct;
297   timer[i].this_ptr   = this_ptr;
298   strncpy(timer[i].id, id, BxMaxTimerIDLen);
299   timer[i].id[BxMaxTimerIDLen-1] = 0; // Null terminate if not already.
300   timer[i].param      = 0;
301 
302   if (active) {
303     if (ticks < Bit64u(currCountdown)) {
304       // This new timer needs to fire before the current countdown.
305       // Skew the current countdown and countdown period to be smaller
306       // by the delta.
307       currCountdownPeriod -= (currCountdown - Bit32u(ticks));
308       currCountdown = Bit32u(ticks);
309     }
310   }
311 
312   BX_DEBUG(("timer id %d registered for '%s'", i, id));
313   // If we didn't find a free slot, increment the bound, numTimers.
314   if (i==numTimers)
315     numTimers++; // One new timer installed.
316 
317   // Return timer id.
318   return i;
319 }
320 
countdownEvent(void)321 void bx_pc_system_c::countdownEvent(void)
322 {
323   unsigned i, first = numTimers, last = 0;
324   Bit64u   minTimeToFire;
325   bool  triggered[BX_MAX_TIMERS];
326 
327   // The countdown decremented to 0.  We need to service all the active
328   // timers, and invoke callbacks from those timers which have fired.
329 #if BX_TIMER_DEBUG
330   if (currCountdown != 0)
331     BX_PANIC(("countdownEvent: ticks!=0"));
332 #endif
333 
334   // Increment global ticks counter by number of ticks which have
335   // elapsed since the last update.
336   ticksTotal += Bit64u(currCountdownPeriod);
337   minTimeToFire = (Bit64u) -1;
338 
339   for (i = 0; i < numTimers; i++) {
340     triggered[i] = 0; // Reset triggered flag.
341     if (timer[i].active) {
342 #if BX_TIMER_DEBUG
343       if (ticksTotal > timer[i].timeToFire)
344         BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i,
345                   timer[i].timeToFire-ticksTotal));
346 #endif
347       if (ticksTotal == timer[i].timeToFire) {
348         // This timer is ready to fire.
349         triggered[i] = 1;
350 
351         if (timer[i].continuous==0) {
352           // If triggered timer is one-shot, deactive.
353           timer[i].active = 0;
354         } else {
355           // Continuous timer, increment time-to-fire by period.
356           timer[i].timeToFire += timer[i].period;
357           if (timer[i].timeToFire < minTimeToFire)
358             minTimeToFire = timer[i].timeToFire;
359         }
360         if (i < first) first = i;
361         last = i;
362       } else {
363         // This timer is not ready to fire yet.
364         if (timer[i].timeToFire < minTimeToFire)
365           minTimeToFire = timer[i].timeToFire;
366       }
367     }
368   }
369 
370   // Calculate next countdown period.  We need to do this before calling
371   // any of the callbacks, as they may call timer features, which need
372   // to be advanced to the next countdown cycle.
373   currCountdown = currCountdownPeriod =
374       Bit32u(minTimeToFire - ticksTotal);
375 
376   for (i = first; i <= last; i++) {
377     // Call requested timer function.  It may request a different
378     // timer period or deactivate etc.
379     if (triggered[i] && (timer[i].funct != NULL)) {
380       triggeredTimer = i;
381       timer[i].funct(timer[i].this_ptr);
382       triggeredTimer = 0;
383     }
384   }
385 }
386 
nullTimer(void * this_ptr)387 void bx_pc_system_c::nullTimer(void* this_ptr)
388 {
389   // This function is always inserted in timer[0].  It is sort of
390   // a heartbeat timer.  It ensures that at least one timer is
391   // always active to make the timer logic more simple, and has
392   // a duration of less than the maximum 32-bit integer, so that
393   // a 32-bit size can be used for the hot countdown timer.  The
394   // rest of the timer info can be 64-bits.  This is also a good
395   // place for some logic to report actual emulated
396   // instructions-per-second (IPS) data when measured relative to
397   // the host computer's wall clock.
398 
399   UNUSED(this_ptr);
400 
401 #if SpewPeriodicTimerInfo
402   BX_INFO(("==================================="));
403   for (unsigned i=0; i < bx_pc_system.numTimers; i++) {
404     if (bx_pc_system.timer[i].active) {
405       BX_INFO(("BxTimer(%s): period=" FMT_LL "u, continuous=%u",
406                bx_pc_system.timer[i].id, bx_pc_system.timer[i].period,
407                bx_pc_system.timer[i].continuous));
408     }
409   }
410 #endif
411 }
412 
benchmarkTimer(void * this_ptr)413 void bx_pc_system_c::benchmarkTimer(void* this_ptr)
414 {
415   bx_pc_system_c *class_ptr = (bx_pc_system_c *) this_ptr;
416   class_ptr->kill_bochs_request = 1;
417   bx_user_quit = 1;
418 }
419 
420 #if BX_ENABLE_STATISTICS
dumpStatsTimer(void * this_ptr)421 void bx_pc_system_c::dumpStatsTimer(void* this_ptr)
422 {
423   printf("=== statistics dump " FMT_LL "u ===\n", bx_pc_system.time_ticks());
424   print_statistics_tree(SIM->get_statistics_root());
425   fflush(stdout);
426 }
427 #endif
428 
429 #if BX_DEBUGGER
timebp_handler(void * this_ptr)430 void bx_pc_system_c::timebp_handler(void* this_ptr)
431 {
432    BX_CPU(0)->break_point = BREAK_POINT_TIME;
433    BX_DEBUG(("Time breakpoint triggered"));
434 
435    if (timebp_queue_size > 1) {
436      Bit64s new_diff = timebp_queue[1] - bx_pc_system.time_ticks();
437      bx_pc_system.activate_timer_ticks(timebp_timer, new_diff, 0);
438    }
439    timebp_queue_size--;
440    for (int i = 0; i < timebp_queue_size; i++)
441      timebp_queue[i] = timebp_queue[i+1];
442 }
443 #endif // BX_DEBUGGER
444 
time_usec_sequential()445 Bit64u bx_pc_system_c::time_usec_sequential()
446 {
447    Bit64u this_time_usec = time_usec();
448    if(this_time_usec != lastTimeUsec) {
449       Bit64u diff_usec = this_time_usec-lastTimeUsec;
450       lastTimeUsec = this_time_usec;
451       if(diff_usec >= usecSinceLast) {
452         usecSinceLast = 0;
453       } else {
454         usecSinceLast -= diff_usec;
455       }
456    }
457    usecSinceLast++;
458    return (this_time_usec+usecSinceLast);
459 }
460 
time_usec()461 Bit64u bx_pc_system_c::time_usec()
462 {
463   return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips);
464 }
465 
time_nsec()466 Bit64u bx_pc_system_c::time_nsec()
467 {
468   return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips * 1000.0);
469 }
470 
start_timers(void)471 void bx_pc_system_c::start_timers(void) { }
472 
activate_timer_ticks(unsigned i,Bit64u ticks,bool continuous)473 void bx_pc_system_c::activate_timer_ticks(unsigned i, Bit64u ticks, bool continuous)
474 {
475 #if BX_TIMER_DEBUG
476   if (i >= numTimers)
477     BX_PANIC(("activate_timer_ticks: timer %u OOB", i));
478   if (i == 0)
479     BX_PANIC(("activate_timer_ticks: timer 0 is the NullTimer!"));
480   if (timer[i].period < MinAllowableTimerPeriod)
481     BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL "u < min of %u",
482               i, timer[i].period, MinAllowableTimerPeriod));
483 #endif
484 
485   // If the timer frequency is rediculously low, make it more sane.
486   // This happens when 'ips' is too low.
487   if (ticks < MinAllowableTimerPeriod) {
488     //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
489     //          ticks, MinAllowableTimerPeriod));
490     ticks = MinAllowableTimerPeriod;
491   }
492 
493   timer[i].period = ticks;
494   timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + ticks;
495   timer[i].active     = 1;
496   timer[i].continuous = continuous;
497 
498   if (ticks < Bit64u(currCountdown)) {
499     // This new timer needs to fire before the current countdown.
500     // Skew the current countdown and countdown period to be smaller
501     // by the delta.
502     currCountdownPeriod -= (currCountdown - Bit32u(ticks));
503     currCountdown = Bit32u(ticks);
504   }
505 }
506 
activate_timer(unsigned i,Bit32u useconds,bool continuous)507 void bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bool continuous)
508 {
509   Bit64u ticks;
510 
511 #if BX_TIMER_DEBUG
512   if (i >= numTimers)
513     BX_PANIC(("activate_timer: timer %u OOB", i));
514   if (i == 0)
515     BX_PANIC(("activate_timer: timer 0 is the nullTimer!"));
516 #endif
517 
518   // if useconds = 0, use default stored in period field
519   // else set new period from useconds
520   if (useconds==0) {
521     ticks = timer[i].period;
522   } else {
523     // convert useconds to number of ticks
524     ticks = (Bit64u) (double(useconds) * m_ips);
525 
526     // If the timer frequency is rediculously low, make it more sane.
527     // This happens when 'ips' is too low.
528     if (ticks < MinAllowableTimerPeriod) {
529       ticks = MinAllowableTimerPeriod;
530     }
531 
532     timer[i].period = ticks;
533   }
534 
535   activate_timer_ticks(i, ticks, continuous);
536 }
537 
activate_timer_nsec(unsigned i,Bit64u nseconds,bool continuous)538 void bx_pc_system_c::activate_timer_nsec(unsigned i, Bit64u nseconds, bool continuous)
539 {
540   Bit64u ticks;
541 
542   // if nseconds = 0, use default stored in period field
543   // else set new period from useconds
544   if (nseconds==0) {
545     ticks = timer[i].period;
546   } else {
547     // convert nseconds to number of ticks
548     ticks = (Bit64u) (double(nseconds) * m_ips / 1000.0);
549 
550     // If the timer frequency is rediculously low, make it more sane.
551     // This happens when 'ips' is too low.
552     if (ticks < MinAllowableTimerPeriod) {
553       ticks = MinAllowableTimerPeriod;
554     }
555 
556     timer[i].period = ticks;
557   }
558 
559   activate_timer_ticks(i, ticks, continuous);
560 }
561 
deactivate_timer(unsigned i)562 void bx_pc_system_c::deactivate_timer(unsigned i)
563 {
564 #if BX_TIMER_DEBUG
565   if (i >= numTimers)
566     BX_PANIC(("deactivate_timer: timer %u OOB", i));
567   if (i == 0)
568     BX_PANIC(("deactivate_timer: timer 0 is the nullTimer!"));
569 #endif
570 
571   timer[i].active = 0;
572 }
573 
unregisterTimer(unsigned timerIndex)574 bool bx_pc_system_c::unregisterTimer(unsigned timerIndex)
575 {
576 #if BX_TIMER_DEBUG
577   if (timerIndex >= numTimers)
578     BX_PANIC(("unregisterTimer: timer %u OOB", timerIndex));
579   if (timerIndex == 0)
580     BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
581   if (timer[timerIndex].inUse == 0)
582     BX_PANIC(("unregisterTimer: timer %u is not in-use!", timerIndex));
583 #endif
584 
585   if (timer[timerIndex].active) {
586     BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[timerIndex].id));
587     return 0; // Fail.
588   }
589 
590   // Reset timer fields for good measure.
591   timer[timerIndex].inUse      = 0; // No longer registered.
592   timer[timerIndex].period     = BX_MAX_BIT64S; // Max value (invalid)
593   timer[timerIndex].timeToFire = BX_MAX_BIT64S; // Max value (invalid)
594   timer[timerIndex].continuous = 0;
595   timer[timerIndex].funct      = NULL;
596   timer[timerIndex].this_ptr   = NULL;
597   memset(timer[timerIndex].id, 0, BxMaxTimerIDLen);
598 
599   if (timerIndex == (numTimers - 1)) numTimers--;
600 
601   return 1; // OK
602 }
603 
setTimerParam(unsigned timerIndex,Bit32u param)604 void bx_pc_system_c::setTimerParam(unsigned timerIndex, Bit32u param)
605 {
606 #if BX_TIMER_DEBUG
607   if (timerIndex >= numTimers)
608     BX_PANIC(("setTimerParam: timer %u OOB", timerIndex));
609 #endif
610   timer[timerIndex].param = param;
611 }
612 
isa_bus_delay(void)613 void bx_pc_system_c::isa_bus_delay(void)
614 {
615   // Emulate 8 MHz ISA bus speed
616   if (m_ips > 4.0) {
617     tickn((Bit32u)(m_ips * 2.0));
618   }
619 }
620