Merge branch 'master' of pterpstra.com:pjht/altair_emu
This commit is contained in:
commit
dcbce1a1a5
@ -7,6 +7,7 @@ mod option_window;
|
|||||||
mod ram;
|
mod ram;
|
||||||
mod state;
|
mod state;
|
||||||
mod window;
|
mod window;
|
||||||
|
mod twosio;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ pub struct OptionWindow {
|
|||||||
category: OptionsCategory,
|
category: OptionsCategory,
|
||||||
card_options: Vec<(&'static Type, Box<dyn SettingsUi>)>,
|
card_options: Vec<(&'static Type, Box<dyn SettingsUi>)>,
|
||||||
select_idx: Option<usize>,
|
select_idx: Option<usize>,
|
||||||
|
allow_changing_cards: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
@ -41,15 +42,18 @@ impl OptionWindow {
|
|||||||
category: OptionsCategory::General,
|
category: OptionsCategory::General,
|
||||||
card_options,
|
card_options,
|
||||||
select_idx,
|
select_idx,
|
||||||
|
allow_changing_cards: !state.power(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_settings(&self, state: &mut EmuState) {
|
fn sync_settings(&self, state: &mut EmuState) {
|
||||||
state.update_options(self.options);
|
state.update_options(self.options);
|
||||||
|
if self.allow_changing_cards {
|
||||||
let cards = self.card_options.iter().map(|(typ, settings_ui)| (*typ, typ.new_card(ron::from_str(&settings_ui.serialize_settings()).unwrap()).unwrap())).collect();
|
let cards = self.card_options.iter().map(|(typ, settings_ui)| (*typ, typ.new_card(ron::from_str(&settings_ui.serialize_settings()).unwrap()).unwrap())).collect();
|
||||||
state.set_cards(cards);
|
state.set_cards(cards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Window for OptionWindow {
|
impl Window for OptionWindow {
|
||||||
fn draw(&mut self, ctx: &egui::Context, state: &mut EmuState) -> bool {
|
fn draw(&mut self, ctx: &egui::Context, state: &mut EmuState) -> bool {
|
||||||
@ -69,6 +73,7 @@ impl Window for OptionWindow {
|
|||||||
}
|
}
|
||||||
OptionsCategory::Cards => {
|
OptionsCategory::Cards => {
|
||||||
TopBottomPanel::top("card_opts").show_inside(ui, |ui| {
|
TopBottomPanel::top("card_opts").show_inside(ui, |ui| {
|
||||||
|
ui.set_enabled(self.allow_changing_cards);
|
||||||
SidePanel::left("card_opts_left")
|
SidePanel::left("card_opts_left")
|
||||||
.show_separator_line(false)
|
.show_separator_line(false)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
|
38
src/state.rs
38
src/state.rs
@ -15,6 +15,7 @@ pub struct EmuState {
|
|||||||
options: Options,
|
options: Options,
|
||||||
fp_state: FrontpanelState,
|
fp_state: FrontpanelState,
|
||||||
cards: Vec<(&'static Type, Box<dyn Card>)>,
|
cards: Vec<(&'static Type, Box<dyn Card>)>,
|
||||||
|
io_cache: Option<(u8, u8)>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmuState {
|
impl EmuState {
|
||||||
@ -34,6 +35,7 @@ impl EmuState {
|
|||||||
options,
|
options,
|
||||||
fp_state: FrontpanelState::default(),
|
fp_state: FrontpanelState::default(),
|
||||||
cards,
|
cards,
|
||||||
|
io_cache: None,
|
||||||
};
|
};
|
||||||
slf.apply_options();
|
slf.apply_options();
|
||||||
slf
|
slf
|
||||||
@ -125,8 +127,10 @@ impl EmuState {
|
|||||||
| MemCycle::Hlta => {
|
| MemCycle::Hlta => {
|
||||||
self.fp_state.set_data(0xff);
|
self.fp_state.set_data(0xff);
|
||||||
}
|
}
|
||||||
MemCycle::In(_) => {
|
MemCycle::In(a) => {
|
||||||
self.fp_state.set_data(0);
|
let data = self.read_io(a as u8);
|
||||||
|
self.io_cache = Some((a as u8, data));
|
||||||
|
self.fp_state.set_data(data);
|
||||||
}
|
}
|
||||||
MemCycle::Inta(_) => todo!(),
|
MemCycle::Inta(_) => todo!(),
|
||||||
MemCycle::IntaHlt(_) => todo!(),
|
MemCycle::IntaHlt(_) => todo!(),
|
||||||
@ -141,8 +145,22 @@ impl EmuState {
|
|||||||
self.write_mem(a, d);
|
self.write_mem(a, d);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
MemCycle::In(_) => 0,
|
MemCycle::In(a) => {
|
||||||
MemCycle::Out(_, _) => 0,
|
if let Some((cached_addr, cached_data)) = self.io_cache {
|
||||||
|
if cached_addr == a as u8 {
|
||||||
|
self.io_cache = None;
|
||||||
|
cached_data
|
||||||
|
} else {
|
||||||
|
self.read_io(a as u8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.read_io(a as u8)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MemCycle::Out(a, d) => {
|
||||||
|
self.write_io(a as u8, d);
|
||||||
|
0
|
||||||
|
},
|
||||||
MemCycle::Inta(_) => todo!(),
|
MemCycle::Inta(_) => todo!(),
|
||||||
MemCycle::Hlta => {
|
MemCycle::Hlta => {
|
||||||
self.running = false;
|
self.running = false;
|
||||||
@ -211,10 +229,22 @@ impl EmuState {
|
|||||||
self.cards.iter().map(|(typ, card)| (typ.name().to_string(), card.get_settings())).collect()
|
self.cards.iter().map(|(typ, card)| (typ.name().to_string(), card.get_settings())).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn power(&self) -> bool {
|
||||||
|
self.fp_state.power()
|
||||||
|
}
|
||||||
|
|
||||||
fn read_mem(&mut self, address: u16) -> u8 {
|
fn read_mem(&mut self, address: u16) -> u8 {
|
||||||
self.cards.iter_mut().find_map(|(_, card)| card.read_mem(address)).unwrap_or(0xFF)
|
self.cards.iter_mut().find_map(|(_, card)| card.read_mem(address)).unwrap_or(0xFF)
|
||||||
}
|
}
|
||||||
fn write_mem(&mut self, address: u16, data: u8) {
|
fn write_mem(&mut self, address: u16, data: u8) {
|
||||||
self.cards.iter_mut().find_map(|(_, card)| card.write_mem(address, data));
|
self.cards.iter_mut().find_map(|(_, card)| card.write_mem(address, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_io(&mut self, address: u8) -> u8 {
|
||||||
|
self.cards.iter_mut().find_map(|(_, card)| card.read_io(address)).unwrap_or(0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_io(&mut self, address: u8, data: u8) {
|
||||||
|
self.cards.iter_mut().find_map(|(_, card)| card.write_io(address, data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
108
src/twosio.rs
Normal file
108
src/twosio.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use bitflags::bitflags;
|
||||||
|
use eframe::egui::{Ui, ComboBox};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
card::{Card, SettingsUi},
|
||||||
|
register,
|
||||||
|
};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
struct Status: u8 {
|
||||||
|
const RDRF = 0b0000_0001;
|
||||||
|
const TDRE = 0b0000_0010;
|
||||||
|
const DCD = 0b0000_0100;
|
||||||
|
const CTS = 0b0000_1000;
|
||||||
|
const FE = 0b0001_0000;
|
||||||
|
const OVRN = 0b0010_0000;
|
||||||
|
const PE = 0b0100_0000;
|
||||||
|
const IRQ = 0b1000_0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TwoSioSettings {
|
||||||
|
start_addr: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TwoSio {
|
||||||
|
start_addr: u8,
|
||||||
|
port1_status: Status,
|
||||||
|
port1_recv_data: u8,
|
||||||
|
port2_status: Status,
|
||||||
|
port2_recv_data: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Card for TwoSio {
|
||||||
|
fn new(settings: ron::Value) -> Result<TwoSio, anyhow::Error> {
|
||||||
|
let settings: TwoSioSettings = settings.into_rust().unwrap();
|
||||||
|
Ok(Self {
|
||||||
|
start_addr: settings.start_addr,
|
||||||
|
port1_status: Status::TDRE,
|
||||||
|
port1_recv_data: 0,
|
||||||
|
port2_status: Status::TDRE,
|
||||||
|
port2_recv_data: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_settings() -> String {
|
||||||
|
ron::to_string(&TwoSioSettings {
|
||||||
|
start_addr: 0,
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_settings(&self) -> String {
|
||||||
|
ron::to_string(&TwoSioSettings {
|
||||||
|
start_addr: self.start_addr,
|
||||||
|
}).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_ui(settings: ron::Value) -> anyhow::Result<impl SettingsUi>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Ok(TwoSioSettingsUi {
|
||||||
|
settings: settings.into_rust().unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_io(&mut self, address: u8) -> Option<u8> {
|
||||||
|
if address & 0xF8 == self.start_addr {
|
||||||
|
match address & 0x2 {
|
||||||
|
0 => Some(self.port1_status.bits()),
|
||||||
|
1 => Some(self.port1_recv_data),
|
||||||
|
2 => Some(self.port2_status.bits()),
|
||||||
|
3 => Some(self.port2_recv_data),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TwoSioSettingsUi {
|
||||||
|
settings: TwoSioSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsUi for TwoSioSettingsUi {
|
||||||
|
fn draw_ui(&mut self, ui: &mut Ui) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Base address");
|
||||||
|
ComboBox::from_id_source("2sio_base").selected_text(format!("{:#x}", self.settings.start_addr)).show_ui(ui, |ui| {
|
||||||
|
for i in 0..64 {
|
||||||
|
let start_addr = (i as u8) * 4;
|
||||||
|
ui.selectable_value(&mut self.settings.start_addr, start_addr, format!("{:#x}", start_addr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_settings(&self) -> String {
|
||||||
|
ron::to_string(&self.settings).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(TwoSio, "88-2SIO");
|
Loading…
Reference in New Issue
Block a user