Skip to content

Commit c19ecf8

Browse files
committed
Show effective speed in the UI when running in fast-forward mode
Makes it easier to see how fast things are actually running.
1 parent 0a460df commit c19ecf8

7 files changed

Lines changed: 83 additions & 9 deletions

File tree

core/src/emulator/comm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub struct EmulatorStatus {
114114
pub fdd: [FddStatus; 3],
115115
pub model: MacModel,
116116
pub speed: EmulatorSpeed,
117+
pub effective_speed: f64,
117118
pub scsi: [Option<ScsiTargetStatus>; 7],
118119
}
119120

core/src/emulator/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ dispatch! {
187187
fn cpu_breakpoints(&self) -> &[Breakpoint] { breakpoints() }
188188
fn cpu_get_step_over(&self) -> Option<Address> { get_step_over() }
189189
fn speed(&self) -> EmulatorSpeed { bus.speed }
190+
fn effective_speed(&self) -> f64 { bus.get_effective_speed() }
190191
fn debug_properties(&self) -> DebuggableProperties { bus.get_debug_properties() }
191192
fn get_audio_channel(&self) -> AudioReceiver { bus.get_audio_channel() }
192193
}
@@ -589,6 +590,7 @@ impl Emulator {
589590
})
590591
}),
591592
speed: self.config.speed(),
593+
effective_speed: self.config.effective_speed(),
592594
})))?;
593595

594596
// Next code stream for disassembly listing

core/src/mac/compact/bus.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ where
228228
self.model
229229
}
230230

231+
pub fn get_effective_speed(&self) -> f64 {
232+
self.via.rtc.effective_speed()
233+
}
234+
231235
pub(crate) fn get_audio_channel(&self) -> AudioReceiver {
232236
self.audio.receiver.as_ref().unwrap().clone()
233237
}

core/src/mac/macii/bus.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ where
270270
self.model
271271
}
272272

273+
pub fn get_effective_speed(&self) -> f64 {
274+
self.via1.rtc.effective_speed()
275+
}
276+
273277
pub(crate) fn get_audio_channel(&self) -> AudioReceiver {
274278
self.asc.receiver.as_ref().unwrap().clone()
275279
}

core/src/mac/rtc.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ pub struct Rtc {
6060
data_out: Option<u8>,
6161

6262
data: RtcData,
63+
64+
// Computed on the fly, no need to save the state.
65+
#[serde(skip, default = "default_effective_speed")]
66+
effective_speed: f64,
67+
#[serde(skip)]
68+
last_second_real_time_ms: Option<i64>,
69+
}
70+
71+
fn default_effective_speed() -> f64 {
72+
1.0
6373
}
6474

6575
#[derive(Serialize, Deserialize)]
@@ -175,6 +185,8 @@ impl Default for Rtc {
175185
seconds,
176186
pram: RtcData::empty_pram(),
177187
},
188+
effective_speed: 1.0,
189+
last_second_real_time_ms: Some(Local::now().timestamp_millis()),
178190
}
179191
}
180192
}
@@ -202,12 +214,27 @@ impl Rtc {
202214
.and_hms_opt(0, 0, 0)
203215
.unwrap();
204216
self.data.seconds = dt.signed_duration_since(mac_epoch).num_seconds() as u32;
217+
self.last_second_real_time_ms = Some(Local::now().timestamp_millis());
205218
}
206219

207220
/// Pokes the RTC that one second has passed
208221
/// In the emulator, one second interrupt is driven by the VIA for ease.
209222
pub fn second(&mut self) {
210223
self.data.seconds = self.data.seconds.wrapping_add(1);
224+
225+
let now_ms = Local::now().timestamp_millis();
226+
if let Some(last_ms) = self.last_second_real_time_ms {
227+
let real_elapsed_secs = (now_ms - last_ms) as f64 / 1000.0;
228+
if real_elapsed_secs > 0.0 {
229+
let instantaneous = 1.0 / real_elapsed_secs;
230+
self.effective_speed = self.effective_speed.mul_add(0.8, instantaneous * 0.2);
231+
}
232+
}
233+
self.last_second_real_time_ms = Some(now_ms);
234+
}
235+
236+
pub fn effective_speed(&self) -> f64 {
237+
self.effective_speed
211238
}
212239

213240
/// Updates RTC I/O lines from the VIA.

frontend_egui/src/app.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,40 @@ impl SnowGui {
15581558
}
15591559
}
15601560

1561+
fn draw_fast_forward_button(&self, ui: &mut egui::Ui) -> egui::Response {
1562+
let is_active = self.emu.is_fastforward();
1563+
let selection_color = ui.visuals().selection.stroke.color;
1564+
let icon = egui_material_icons::icons::ICON_FAST_FORWARD;
1565+
let mut text = egui::RichText::new(icon);
1566+
if is_active {
1567+
text = text.color(selection_color);
1568+
}
1569+
1570+
let response = ui
1571+
.add_enabled(self.emu.is_running(), egui::Button::new(text))
1572+
.on_hover_text("Fast-forward execution");
1573+
1574+
if is_active {
1575+
let font_id = egui::FontId::proportional(9.0);
1576+
let badge_text = egui::WidgetText::from(
1577+
egui::RichText::new(self.emu.effective_speed_label()).font(font_id),
1578+
);
1579+
let galley = badge_text.into_galley(
1580+
ui,
1581+
Some(egui::TextWrapMode::Extend),
1582+
f32::MAX,
1583+
egui::FontSelection::Default,
1584+
);
1585+
let badge_pos = egui::pos2(
1586+
response.rect.right() - galley.size().x - 1.0,
1587+
response.rect.top() + 1.0,
1588+
);
1589+
ui.painter().galley(badge_pos, galley, selection_color);
1590+
}
1591+
1592+
response
1593+
}
1594+
15611595
fn draw_toolbar(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
15621596
ui.horizontal(|ui| {
15631597
ui.style_mut().text_styles.insert(
@@ -1606,15 +1640,7 @@ impl SnowGui {
16061640
self.emu.run();
16071641
}
16081642

1609-
if ui
1610-
.add_enabled(
1611-
self.emu.is_running(),
1612-
egui::Button::new(egui_material_icons::icons::ICON_FAST_FORWARD)
1613-
.selected(self.emu.is_fastforward()),
1614-
)
1615-
.on_hover_text("Fast-forward execution")
1616-
.clicked()
1617-
{
1643+
if self.draw_fast_forward_button(ui).clicked() {
16181644
self.emu.toggle_fastforward();
16191645
}
16201646

frontend_egui/src/emulator.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,16 @@ impl EmulatorState {
854854
}
855855
}
856856

857+
pub fn effective_speed_label(&self) -> String {
858+
let Some(ref status) = self.status else {
859+
return String::new();
860+
};
861+
if status.speed == EmulatorSpeed::Accurate {
862+
return String::new();
863+
}
864+
format!("{:.1}x", status.effective_speed)
865+
}
866+
857867
/// Returns the currently emulated Macintosh model
858868
pub fn get_model(&self) -> Option<MacModel> {
859869
let status = self.status.as_ref()?;

0 commit comments

Comments
 (0)