Finish binary file loading
This commit is contained in:
parent
9a2e02e1ee
commit
189fd790c1
126
src/load_bin_window.rs
Normal file
126
src/load_bin_window.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::{fmt::Display, path::PathBuf};
|
||||||
|
|
||||||
|
use eframe::egui::{self, DragValue};
|
||||||
|
use egui_modal::Modal;
|
||||||
|
use rfd::FileDialog;
|
||||||
|
|
||||||
|
use crate::{state::EmuState, window::Window};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum BinaryType {
|
||||||
|
Raw,
|
||||||
|
IntelHex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BinaryType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Raw => f.write_str("Raw"),
|
||||||
|
Self::IntelHex => f.write_str("Intel HEX"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoadBinWindow {
|
||||||
|
path: Option<PathBuf>,
|
||||||
|
ftype: BinaryType,
|
||||||
|
start_addr: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadBinWindow {
|
||||||
|
pub fn new(ctx: &egui::Context) -> Self {
|
||||||
|
Modal::new(ctx, "load_bin_modal").open();
|
||||||
|
Self {
|
||||||
|
path: None,
|
||||||
|
ftype: BinaryType::Raw,
|
||||||
|
start_addr: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_file(&mut self) {
|
||||||
|
let ihex_exts = ["hex", "mcs", "int", "ihex", "ihe", "ihx"];
|
||||||
|
let file = FileDialog::new()
|
||||||
|
.add_filter(
|
||||||
|
"Binary files",
|
||||||
|
&["bin", "img", "hex", "mcs", "int", "ihex", "ihe", "ihx"],
|
||||||
|
)
|
||||||
|
.add_filter("All files", &["*"])
|
||||||
|
.pick_file();
|
||||||
|
let Some(file) = file else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if file
|
||||||
|
.extension()
|
||||||
|
.map_or(false, |ext| ihex_exts.contains(&ext.to_str().unwrap_or("")))
|
||||||
|
{
|
||||||
|
self.ftype = BinaryType::IntelHex;
|
||||||
|
} else {
|
||||||
|
self.ftype = BinaryType::Raw;
|
||||||
|
}
|
||||||
|
self.path = Some(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_file(&self, state: &mut EmuState) {
|
||||||
|
let path = self.path.as_ref().unwrap();
|
||||||
|
match self.ftype {
|
||||||
|
BinaryType::Raw => {
|
||||||
|
let bin = std::fs::read(path).unwrap();
|
||||||
|
state.write_binary(self.start_addr as usize, bin);
|
||||||
|
}
|
||||||
|
BinaryType::IntelHex => {
|
||||||
|
let data = std::fs::read_to_string(path).unwrap();
|
||||||
|
for record in ihex::Reader::new(&data) {
|
||||||
|
let record = record.unwrap();
|
||||||
|
match record {
|
||||||
|
ihex::Record::Data { offset, value } => {
|
||||||
|
state.write_binary(offset as usize, value);
|
||||||
|
}
|
||||||
|
ihex::Record::StartLinearAddress(_) => todo!(),
|
||||||
|
ihex::Record::EndOfFile => (),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window for LoadBinWindow {
|
||||||
|
fn draw(&mut self, ctx: &egui::Context, state: &mut EmuState) -> bool {
|
||||||
|
let modal = Modal::new(ctx, "load_bin_modal");
|
||||||
|
modal.show(|ui| {
|
||||||
|
modal.title(ui, "Load binary");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Choose file").clicked() {
|
||||||
|
self.choose_file();
|
||||||
|
}
|
||||||
|
ui.label(format!(
|
||||||
|
"{}",
|
||||||
|
self.path.as_ref().map_or("".into(), |x| x
|
||||||
|
.file_name()
|
||||||
|
.map_or("".into(), |x| x.to_string_lossy().to_string()))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
egui::ComboBox::from_label("File type")
|
||||||
|
.selected_text(format!("{}", self.ftype))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(&mut self.ftype, BinaryType::Raw, "Raw");
|
||||||
|
ui.selectable_value(&mut self.ftype, BinaryType::IntelHex, "Intel HEX");
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.set_enabled(self.ftype != BinaryType::IntelHex);
|
||||||
|
ui.add(DragValue::new(&mut self.start_addr).clamp_range(0..=65535));
|
||||||
|
ui.label("Start address");
|
||||||
|
});
|
||||||
|
modal.buttons(ui, |ui| {
|
||||||
|
modal.caution_button(ui, "Cancel");
|
||||||
|
ui.add_enabled_ui(self.path.is_some(), |ui| {
|
||||||
|
if modal.suggested_button(ui, "Load").clicked() {
|
||||||
|
self.load_file(state);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
!modal.is_open()
|
||||||
|
}
|
||||||
|
}
|
44
src/main.rs
44
src/main.rs
@ -2,10 +2,11 @@ mod audio;
|
|||||||
mod card;
|
mod card;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod frontpanel;
|
mod frontpanel;
|
||||||
mod ram;
|
mod load_bin_window;
|
||||||
mod window;
|
|
||||||
mod state;
|
|
||||||
mod option_window;
|
mod option_window;
|
||||||
|
mod ram;
|
||||||
|
mod state;
|
||||||
|
mod window;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ use eframe::{
|
|||||||
NativeOptions,
|
NativeOptions,
|
||||||
};
|
};
|
||||||
use frontpanel::Textures;
|
use frontpanel::Textures;
|
||||||
|
use load_bin_window::LoadBinWindow;
|
||||||
use option_window::OptionWindow;
|
use option_window::OptionWindow;
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -56,12 +58,11 @@ struct AltairEmulator {
|
|||||||
windows: HashMap<&'static str, Box<dyn Window>>,
|
windows: HashMap<&'static str, Box<dyn Window>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl AltairEmulator {
|
impl AltairEmulator {
|
||||||
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
let audio_tx = AudioThread::init();
|
let audio_tx = AudioThread::init();
|
||||||
let options = if cc.storage.unwrap().get_string("options").is_none() {
|
let options = if cc.storage.unwrap().get_string("options").is_none() {
|
||||||
Options::default()
|
Options::default()
|
||||||
} else {
|
} else {
|
||||||
eframe::get_value(cc.storage.unwrap(), "options").unwrap()
|
eframe::get_value(cc.storage.unwrap(), "options").unwrap()
|
||||||
};
|
};
|
||||||
@ -89,37 +90,8 @@ impl AltairEmulator {
|
|||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button("Load binary file").clicked() {
|
if ui.button("Load binary file").clicked() {
|
||||||
let ihex_exts = ["hex", "mcs", "int", "ihex", "ihe", "ihx"];
|
self.windows
|
||||||
let file = FileDialog::new()
|
.insert("load_bin_window", Box::new(LoadBinWindow::new(ui.ctx())));
|
||||||
.add_filter(
|
|
||||||
"Binary files",
|
|
||||||
&["bin", "img", "hex", "mcs", "int", "ihex", "ihe", "ihx"],
|
|
||||||
)
|
|
||||||
.add_filter("All files", &["*"])
|
|
||||||
.pick_file();
|
|
||||||
if let Some(file) = file {
|
|
||||||
if file
|
|
||||||
.extension()
|
|
||||||
.map_or(false, |ext| ihex_exts.contains(&ext.to_str().unwrap_or("")))
|
|
||||||
{
|
|
||||||
let data = std::fs::read_to_string(file).unwrap();
|
|
||||||
for record in ihex::Reader::new(&data) {
|
|
||||||
let record = record.unwrap();
|
|
||||||
match record {
|
|
||||||
ihex::Record::Data { offset, value } => {
|
|
||||||
self.state.write_binary(offset as usize, value);
|
|
||||||
}
|
|
||||||
ihex::Record::StartLinearAddress(_) => todo!(),
|
|
||||||
ihex::Record::EndOfFile => (),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Raw binary
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
self.state.update_fp();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use eframe::egui::{self, Slider};
|
use eframe::egui::{self, Slider};
|
||||||
use egui_modal::Modal;
|
use egui_modal::Modal;
|
||||||
|
|
||||||
use crate::{Options, state::EmuState, window::Window};
|
use crate::{state::EmuState, window::Window, Options};
|
||||||
|
|
||||||
pub struct OptionWindow {
|
pub struct OptionWindow {
|
||||||
options: Options,
|
options: Options,
|
||||||
|
@ -2,7 +2,12 @@ use std::sync::mpsc::Sender;
|
|||||||
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
|
||||||
use crate::{cpu::{I8080, Status, MemCycle}, audio::AudioMessage, Options, frontpanel::{FrontpanelState, FrontpanelInteraction, ActionSwitch, switch::SwitchState}};
|
use crate::{
|
||||||
|
audio::AudioMessage,
|
||||||
|
cpu::{MemCycle, Status, I8080},
|
||||||
|
frontpanel::{switch::SwitchState, ActionSwitch, FrontpanelInteraction, FrontpanelState},
|
||||||
|
Options,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct EmuState {
|
pub struct EmuState {
|
||||||
mem: [u8; 65536],
|
mem: [u8; 65536],
|
||||||
@ -23,7 +28,7 @@ impl EmuState {
|
|||||||
running: false,
|
running: false,
|
||||||
audio_tx,
|
audio_tx,
|
||||||
options,
|
options,
|
||||||
fp_state: FrontpanelState::new()
|
fp_state: FrontpanelState::new(),
|
||||||
};
|
};
|
||||||
slf.apply_options();
|
slf.apply_options();
|
||||||
slf
|
slf
|
||||||
|
Loading…
Reference in New Issue
Block a user