Connect front panel to emulator
This commit is contained in:
parent
9e0073df41
commit
9706b052cb
@ -1,11 +1,12 @@
|
||||
use std::{path::Path, sync::mpsc::Sender};
|
||||
|
||||
use device_query::Keycode;
|
||||
use eframe::{
|
||||
egui::{self, Id, Painter, Response, Sense, TextureOptions, Ui, Widget},
|
||||
epaint::{pos2, vec2, Color32, Pos2, Rect, TextureHandle},
|
||||
};
|
||||
|
||||
use crate::audio::AudioMessage;
|
||||
use crate::{audio::AudioMessage, cpu::Status};
|
||||
|
||||
use self::switch::SwitchState;
|
||||
|
||||
@ -15,8 +16,27 @@ pub mod switch;
|
||||
const NULL_UV: Rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
|
||||
const NULL_TINT: Color32 = Color32::WHITE;
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct FrontpanelState {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ActionSwitch {
|
||||
RunStop,
|
||||
SingleStep,
|
||||
Examine,
|
||||
Deposit,
|
||||
Reset,
|
||||
Protect,
|
||||
Aux1,
|
||||
Aux2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum FrontpanelInteraction {
|
||||
ActionSwClicked(ActionSwitch, SwitchState),
|
||||
AdChanged(u16),
|
||||
PowerChanged(bool),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FrontpanelState {
|
||||
ad_sws: u16,
|
||||
power: bool,
|
||||
runstop: SwitchState,
|
||||
@ -27,27 +47,73 @@ struct FrontpanelState {
|
||||
prot: SwitchState,
|
||||
aux1: SwitchState,
|
||||
aux2: SwitchState,
|
||||
addr: u16,
|
||||
data: u8,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
impl FrontpanelState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ad_sws: 0,
|
||||
power: false,
|
||||
runstop: SwitchState::Neut,
|
||||
single_step: SwitchState::Neut,
|
||||
exam: SwitchState::Neut,
|
||||
dep: SwitchState::Neut,
|
||||
reset: SwitchState::Neut,
|
||||
prot: SwitchState::Neut,
|
||||
aux1: SwitchState::Neut,
|
||||
aux2: SwitchState::Neut,
|
||||
addr: 0,
|
||||
data: 0,
|
||||
status: Status::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ad_sws(&self) -> u16 {
|
||||
self.ad_sws
|
||||
}
|
||||
|
||||
pub fn power(&self) -> bool {
|
||||
self.power
|
||||
}
|
||||
|
||||
pub fn set_addr(&mut self, addr: u16) {
|
||||
self.addr = addr;
|
||||
}
|
||||
|
||||
pub fn set_data(&mut self, data: u8) {
|
||||
self.data = data;
|
||||
}
|
||||
|
||||
pub fn set_status(&mut self, status: Status) {
|
||||
self.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Frontpanel<'a> {
|
||||
id: Id,
|
||||
textures: &'a Textures,
|
||||
audio_tx: Sender<AudioMessage>,
|
||||
state: &'a mut FrontpanelState,
|
||||
interaction: Option<FrontpanelInteraction>,
|
||||
}
|
||||
|
||||
impl<'a> Frontpanel<'a> {
|
||||
pub fn new(id: Id, textures: &'a Textures, audio_tx: Sender<AudioMessage>) -> Self {
|
||||
pub fn new(textures: &'a Textures, state: &'a mut FrontpanelState) -> Self {
|
||||
Self {
|
||||
id,
|
||||
textures,
|
||||
audio_tx,
|
||||
state,
|
||||
interaction: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interaction(&self) -> Option<FrontpanelInteraction> {
|
||||
self.interaction
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Frontpanel<'_> {
|
||||
impl Widget for &mut Frontpanel<'_> {
|
||||
fn ui(self, ui: &mut Ui) -> egui::Response {
|
||||
let mut state: FrontpanelState = ui.data(|data| data.get_temp(self.id).unwrap_or_default());
|
||||
let sw_textures = switch::Textures::new(
|
||||
self.textures.sw_up.clone(),
|
||||
self.textures.sw_neut.clone(),
|
||||
@ -68,7 +134,14 @@ impl Widget for Frontpanel<'_> {
|
||||
ui.image(&self.textures.fp);
|
||||
for led in &LEDS {
|
||||
let pos = led.pos + fp_rect.left_top().to_vec2();
|
||||
let led_data = 0xAAAA;
|
||||
let led_data = match led.source {
|
||||
LedSource::Protect => 0x0,
|
||||
LedSource::Iff => 0x0,
|
||||
LedSource::Run => 0x0,
|
||||
LedSource::CpuStatus => u16::from(self.state.status.bits()),
|
||||
LedSource::Data => u16::from(self.state.data),
|
||||
LedSource::Address => self.state.addr,
|
||||
};
|
||||
let led_on = (led_data & led.mask) > 0;
|
||||
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(16.0, 16.0)), |ui| {
|
||||
ui.add(led::Led::new(led_on, &led_textures));
|
||||
@ -81,44 +154,42 @@ impl Widget for Frontpanel<'_> {
|
||||
let pos = switch.pos + fp_rect.left_top().to_vec2();
|
||||
if i == 0 {
|
||||
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
|
||||
let mut power_inv = !state.power;
|
||||
let mut power_inv = !self.state.power;
|
||||
if ui
|
||||
.add(switch::ToggleSwitch::new(&mut power_inv, &sw_textures))
|
||||
.drag_started()
|
||||
{
|
||||
if !power_inv {
|
||||
self.audio_tx.send(AudioMessage::FanOn).unwrap();
|
||||
} else {
|
||||
self.audio_tx.send(AudioMessage::FanOff).unwrap();
|
||||
}
|
||||
self.audio_tx.send(AudioMessage::PlaySwitchClick).unwrap();
|
||||
self.state.power = !power_inv;
|
||||
self.interaction =
|
||||
Some(FrontpanelInteraction::PowerChanged(self.state.power));
|
||||
};
|
||||
state.power = !power_inv;
|
||||
});
|
||||
}
|
||||
if (1..17).contains(&i) {
|
||||
let bit_mask = 1 << (16 - i);
|
||||
let mut sw_state = state.ad_sws & bit_mask > 0;
|
||||
let mut sw_state = self.state.ad_sws & bit_mask > 0;
|
||||
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
|
||||
if ui
|
||||
.add(switch::ToggleSwitch::new(&mut sw_state, &sw_textures))
|
||||
.drag_started()
|
||||
{
|
||||
self.audio_tx.send(AudioMessage::PlaySwitchClick).unwrap();
|
||||
self.state.ad_sws =
|
||||
(self.state.ad_sws & !(bit_mask)) | ((sw_state as u16) << (16 - i));
|
||||
self.interaction =
|
||||
Some(FrontpanelInteraction::AdChanged(self.state.ad_sws));
|
||||
};
|
||||
});
|
||||
state.ad_sws = (state.ad_sws & !(bit_mask)) | ((sw_state as u16) << (16 - i));
|
||||
}
|
||||
if (17..25).contains(&i) {
|
||||
let state = match i {
|
||||
17 => &mut state.runstop,
|
||||
18 => &mut state.single_step,
|
||||
19 => &mut state.exam,
|
||||
20 => &mut state.dep,
|
||||
21 => &mut state.reset,
|
||||
22 => &mut state.prot,
|
||||
23 => &mut state.aux1,
|
||||
24 => &mut state.aux2,
|
||||
17 => &mut self.state.runstop,
|
||||
18 => &mut self.state.single_step,
|
||||
19 => &mut self.state.exam,
|
||||
20 => &mut self.state.dep,
|
||||
21 => &mut self.state.reset,
|
||||
22 => &mut self.state.prot,
|
||||
23 => &mut self.state.aux1,
|
||||
24 => &mut self.state.aux2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
ui.allocate_ui_at_rect(Rect::from_center_size(pos, vec2(11.0, 25.0)), |ui| {
|
||||
@ -126,13 +197,24 @@ impl Widget for Frontpanel<'_> {
|
||||
.add(switch::ThreePosSwitch::new(state, &sw_textures))
|
||||
.drag_started()
|
||||
{
|
||||
self.audio_tx.send(AudioMessage::PlaySwitchClick).unwrap();
|
||||
let sw = match i {
|
||||
17 => ActionSwitch::RunStop,
|
||||
18 => ActionSwitch::SingleStep,
|
||||
19 => ActionSwitch::Examine,
|
||||
20 => ActionSwitch::Deposit,
|
||||
21 => ActionSwitch::Reset,
|
||||
22 => ActionSwitch::Protect,
|
||||
23 => ActionSwitch::Aux1,
|
||||
24 => ActionSwitch::Aux2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.interaction =
|
||||
Some(FrontpanelInteraction::ActionSwClicked(sw, *state));
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
ui.data_mut(|data| data.insert_temp(self.id, state));
|
||||
resp
|
||||
}
|
||||
}
|
||||
|
484
src/main.rs
484
src/main.rs
@ -10,7 +10,7 @@ use audio::{AudioMessage, AudioThread};
|
||||
use cpu::{MemCycle, Status, I8080};
|
||||
use device_query::DeviceState;
|
||||
use eframe::{
|
||||
egui::{self, menu, Button, Id, Label, Pos2, Rect, TextureHandle, TextureOptions, Ui},
|
||||
egui::{self, menu, Button, Label, TextureHandle, TextureOptions},
|
||||
NativeOptions,
|
||||
};
|
||||
use egui_modal::Modal;
|
||||
@ -18,7 +18,7 @@ use rand::RngCore;
|
||||
use rfd::FileDialog;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::frontpanel::{switch, Frontpanel};
|
||||
use crate::frontpanel::{switch::SwitchState, Frontpanel, FrontpanelInteraction, FrontpanelState};
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
env_logger::init();
|
||||
@ -37,23 +37,8 @@ struct Options {
|
||||
|
||||
struct AltairEmulator {
|
||||
textures: frontpanel::Textures,
|
||||
ad_sws: u16,
|
||||
power: bool,
|
||||
runstop: SwitchState,
|
||||
single_step: SwitchState,
|
||||
exam: SwitchState,
|
||||
dep: switch::SwitchState,
|
||||
reset: SwitchState,
|
||||
prot: SwitchState,
|
||||
aux1: SwitchState,
|
||||
aux2: SwitchState,
|
||||
fp_address: u16,
|
||||
fp_data: u8,
|
||||
fp_status: Status,
|
||||
mem: [u8; 65536],
|
||||
cpu: I8080,
|
||||
mouse_newdown: bool,
|
||||
mouse_olddown: bool,
|
||||
running: bool,
|
||||
audio_tx: Sender<AudioMessage>,
|
||||
options: Options,
|
||||
@ -61,6 +46,7 @@ struct AltairEmulator {
|
||||
device_state: DeviceState,
|
||||
kbd_newdown: bool,
|
||||
kbd_olddown: bool,
|
||||
fp_state: FrontpanelState,
|
||||
}
|
||||
|
||||
impl AltairEmulator {
|
||||
@ -103,23 +89,8 @@ impl AltairEmulator {
|
||||
// 21: 005
|
||||
Self {
|
||||
textures: frontpanel::Textures::new(&cc.egui_ctx),
|
||||
ad_sws: 0x0,
|
||||
power: false,
|
||||
runstop: SwitchState::Neut,
|
||||
single_step: SwitchState::Neut,
|
||||
exam: SwitchState::Neut,
|
||||
dep: switch::SwitchState::Neut,
|
||||
reset: SwitchState::Neut,
|
||||
prot: SwitchState::Neut,
|
||||
aux1: SwitchState::Neut,
|
||||
aux2: SwitchState::Neut,
|
||||
mem,
|
||||
fp_address: 0,
|
||||
fp_data: 0,
|
||||
fp_status: Status::empty(),
|
||||
cpu,
|
||||
mouse_newdown: false,
|
||||
mouse_olddown: false,
|
||||
running: false,
|
||||
audio_tx,
|
||||
options,
|
||||
@ -127,55 +98,62 @@ impl AltairEmulator {
|
||||
device_state: DeviceState::new(),
|
||||
kbd_newdown: false,
|
||||
kbd_olddown: false,
|
||||
fp_state: FrontpanelState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_fp(&mut self) {
|
||||
let cycle = self.cpu.get_mem_cycle();
|
||||
self.fp_status = cycle.get_status();
|
||||
self.fp_state.set_status(cycle.get_status());
|
||||
match cycle {
|
||||
MemCycle::Fetch(a) | MemCycle::Read(a) | MemCycle::StackRead(a) => {
|
||||
self.fp_address = a;
|
||||
self.fp_data = self.mem[a as usize];
|
||||
self.fp_state.set_addr(a);
|
||||
self.fp_state.set_data(self.mem[a as usize]);
|
||||
}
|
||||
MemCycle::Write(a, _) | MemCycle::StackWrite(a, _) | MemCycle::Out(a, _) => {
|
||||
self.fp_address = a;
|
||||
self.fp_data = 0xff;
|
||||
self.fp_state.set_addr(a);
|
||||
self.fp_state.set_data(0xff);
|
||||
}
|
||||
MemCycle::In(a) => {
|
||||
self.fp_address = a;
|
||||
self.fp_data = 0;
|
||||
self.fp_state.set_addr(a);
|
||||
self.fp_state.set_data(0);
|
||||
}
|
||||
MemCycle::Inta(_) => todo!(),
|
||||
MemCycle::Hlta(_) => {
|
||||
self.fp_data = 0xff;
|
||||
self.fp_address = 0xffff;
|
||||
self.fp_state.set_addr(0xffff);
|
||||
self.fp_state.set_data(0xff);
|
||||
}
|
||||
MemCycle::IntaHlt(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_cpu_cycle(&mut self) {
|
||||
let cycle = self.cpu.get_mem_cycle();
|
||||
let data = match cycle {
|
||||
MemCycle::Fetch(a) | MemCycle::Read(a) | MemCycle::StackRead(a) => self.mem[a as usize],
|
||||
MemCycle::Write(a, d) | MemCycle::StackWrite(a, d) => {
|
||||
self.mem[a as usize] = d;
|
||||
0
|
||||
}
|
||||
MemCycle::In(_) => 0,
|
||||
MemCycle::Out(_, _) => 0,
|
||||
MemCycle::Inta(_) => todo!(),
|
||||
MemCycle::Hlta(_) => {
|
||||
self.running = false;
|
||||
0
|
||||
}
|
||||
MemCycle::IntaHlt(_) => todo!(),
|
||||
};
|
||||
self.cpu.finish_m_cycle(data);
|
||||
self.update_fp();
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for AltairEmulator {
|
||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||
eframe::set_value(storage, "options", &self.options);
|
||||
}
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn image_topleft(pos: impl Into<Pos2>, texture: &TextureHandle, ui: &mut Ui) {
|
||||
ui.allocate_ui_at_rect(Rect::from_min_size(pos.into(), texture.size_vec2()), |ui| {
|
||||
ui.image(texture);
|
||||
});
|
||||
}
|
||||
|
||||
fn image_center(pos: impl Into<Pos2>, texture: &TextureHandle, ui: &mut Ui) {
|
||||
ui.allocate_ui_at_rect(
|
||||
Rect::from_center_size(pos.into(), texture.size_vec2()),
|
||||
|ui| {
|
||||
ui.image(texture);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
ctx.set_pixels_per_point(1.0);
|
||||
// frame.set_window_size((800.0, 333.0).into());
|
||||
let mut disable_fp_sws = self.option_window.is_some();
|
||||
@ -226,17 +204,84 @@ impl eframe::App for AltairEmulator {
|
||||
});
|
||||
});
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.style_mut().debug.debug_on_hover = true;
|
||||
ui.add(Frontpanel::new(
|
||||
Id::new("frontpanel"),
|
||||
&self.textures,
|
||||
self.audio_tx.clone(),
|
||||
));
|
||||
ui.add(Frontpanel::new(
|
||||
Id::new("frontpanel2"),
|
||||
&self.textures,
|
||||
self.audio_tx.clone(),
|
||||
));
|
||||
let mut fp = Frontpanel::new(&self.textures, &mut self.fp_state);
|
||||
ui.add(&mut fp);
|
||||
let interaction = fp.interaction();
|
||||
if let Some(interaction) = interaction {
|
||||
self.audio_tx.send(AudioMessage::PlaySwitchClick).unwrap();
|
||||
match interaction {
|
||||
FrontpanelInteraction::ActionSwClicked(sw, state) => {
|
||||
if self.fp_state.power() {
|
||||
match sw {
|
||||
frontpanel::ActionSwitch::RunStop => {
|
||||
if state == SwitchState::Up {
|
||||
self.running = false;
|
||||
} else {
|
||||
self.running = true;
|
||||
}
|
||||
}
|
||||
frontpanel::ActionSwitch::SingleStep => {
|
||||
self.run_cpu_cycle();
|
||||
}
|
||||
frontpanel::ActionSwitch::Examine => {
|
||||
if state == SwitchState::Up {
|
||||
// Assume M1
|
||||
self.cpu.finish_m_cycle(0xC3); // JMP
|
||||
self.cpu.finish_m_cycle(self.fp_state.ad_sws() as u8);
|
||||
self.cpu
|
||||
.finish_m_cycle((self.fp_state.ad_sws() >> 8) as u8);
|
||||
self.update_fp();
|
||||
} else {
|
||||
// Assume M1
|
||||
self.cpu.finish_m_cycle(0x0); // NOP
|
||||
self.update_fp();
|
||||
}
|
||||
}
|
||||
frontpanel::ActionSwitch::Deposit => {
|
||||
if state == SwitchState::Up {
|
||||
// Assume M1
|
||||
self.mem[self.cpu.get_mem_cycle().address() as usize] =
|
||||
self.fp_state.ad_sws() as u8;
|
||||
self.update_fp();
|
||||
} else {
|
||||
// Assume M1
|
||||
self.cpu.finish_m_cycle(0x0); // NOP
|
||||
self.mem[self.cpu.get_mem_cycle().address() as usize] =
|
||||
self.fp_state.ad_sws() as u8;
|
||||
self.update_fp();
|
||||
}
|
||||
}
|
||||
frontpanel::ActionSwitch::Reset => {
|
||||
if state == SwitchState::Up {
|
||||
self.cpu.reset();
|
||||
self.update_fp();
|
||||
}
|
||||
}
|
||||
frontpanel::ActionSwitch::Protect => (),
|
||||
frontpanel::ActionSwitch::Aux1 => (),
|
||||
frontpanel::ActionSwitch::Aux2 => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
FrontpanelInteraction::AdChanged(_) => (),
|
||||
FrontpanelInteraction::PowerChanged(pwr) => {
|
||||
if pwr {
|
||||
self.audio_tx.send(AudioMessage::FanOn).unwrap();
|
||||
self.cpu = I8080::new();
|
||||
self.update_fp();
|
||||
} else {
|
||||
self.audio_tx.send(AudioMessage::FanOff).unwrap();
|
||||
self.running = false;
|
||||
self.fp_state.set_status(Status::empty());
|
||||
self.fp_state.set_addr(0);
|
||||
self.fp_state.set_data(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.running {
|
||||
self.run_cpu_cycle();
|
||||
}
|
||||
// dbg!(ui.input(|input| input.pointer.latest_pos()));
|
||||
// dbg!(ui.input(|input| input.pointer.latest_pos()));
|
||||
// let (_, fp_rect) = ui.allocate_space((800.0, 333.0).into());
|
||||
@ -589,19 +634,21 @@ impl eframe::App for AltairEmulator {
|
||||
// self.cpu.finish_m_cycle(data);
|
||||
// self.update_fp();
|
||||
// }
|
||||
//
|
||||
ui.add(Label::new("Hello"));
|
||||
});
|
||||
|
||||
// let old_fan_enabled = self.options.fan_enabled;
|
||||
let old_fan_enabled = self.options.fan_enabled;
|
||||
if let Some(option_window) = self.option_window.as_mut() {
|
||||
if option_window.draw(ctx, &mut self.options) {
|
||||
self.option_window = None;
|
||||
}
|
||||
}
|
||||
// if (old_fan_enabled != self.options.fan_enabled) && self.power {
|
||||
// self.fan_audio_tx.send(self.options.fan_enabled).unwrap();
|
||||
// }
|
||||
if (old_fan_enabled != self.options.fan_enabled) && self.fp_state.power() {
|
||||
if self.options.fan_enabled {
|
||||
self.audio_tx.send(AudioMessage::FanOn).unwrap();
|
||||
} else {
|
||||
self.audio_tx.send(AudioMessage::FanOff).unwrap();
|
||||
}
|
||||
}
|
||||
if self.running {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
@ -721,292 +768,3 @@ impl Textures {
|
||||
Ok(ctx.load_texture(name, image, TextureOptions::LINEAR))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum SwitchState {
|
||||
Up,
|
||||
Neut,
|
||||
Down,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct SwitchInfo {
|
||||
pos: Pos2,
|
||||
}
|
||||
|
||||
static SWITCHES: [SwitchInfo; 25] = [
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(43.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(183.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(228.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(257.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(285.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(330.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(359.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(388.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(433.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(461.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(491.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(536.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(564.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(593.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(638.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(667.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(696.0, 175.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(190.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(248.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(305.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(363.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(420.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(478.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(535.0, 234.0),
|
||||
},
|
||||
SwitchInfo {
|
||||
pos: Pos2::new(593.0, 234.0),
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum LedSource {
|
||||
Protect,
|
||||
Iff,
|
||||
Run,
|
||||
CpuStatus,
|
||||
Data,
|
||||
Address,
|
||||
}
|
||||
|
||||
struct LedInfo {
|
||||
pos: Pos2,
|
||||
source: LedSource,
|
||||
mask: u16,
|
||||
}
|
||||
|
||||
static LEDS: [LedInfo; 36] = [
|
||||
LedInfo {
|
||||
pos: Pos2::new(118.0, 58.0),
|
||||
source: LedSource::Protect,
|
||||
mask: 0xFF,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(89.0, 58.0),
|
||||
source: LedSource::Iff,
|
||||
mask: 0xFF,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(89.0, 117.0),
|
||||
source: LedSource::Run,
|
||||
mask: 0x0,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(118.0, 117.0),
|
||||
source: LedSource::Iff,
|
||||
mask: 0x0,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(149.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x80,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(179.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x40,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(209.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x20,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(239.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x10,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(269.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x8,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(299.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x4,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(329.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x2,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(359.0, 58.0),
|
||||
source: LedSource::CpuStatus,
|
||||
mask: 0x1,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(461.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x80,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(491.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x40,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(536.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x20,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(564.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x10,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(593.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x8,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(638.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x4,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(667.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x2,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(696.0, 58.0),
|
||||
source: LedSource::Data,
|
||||
mask: 0x1,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(182.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x8000,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(227.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x4000,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(256.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x2000,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(285.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x1000,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(330.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x800,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(359.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x400,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(388.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x200,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(433.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x100,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(461.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x80,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(490.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x40,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(536.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x20,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(564.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x10,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(593.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x8,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(638.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x4,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(667.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x2,
|
||||
},
|
||||
LedInfo {
|
||||
pos: Pos2::new(696.0, 117.0),
|
||||
source: LedSource::Address,
|
||||
mask: 0x1,
|
||||
},
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user