Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b79f35ac61 | ||
|
|
89128b8207 | ||
|
|
b0404a5da2 | ||
|
|
16cf54e5cf | ||
|
|
423743ea5c | ||
|
|
005ed509d5 | ||
|
|
0585bc2740 | ||
|
|
62de97384d | ||
|
|
1a54c2e6fc | ||
|
|
d109c71c15 | ||
|
|
1628a45f7d | ||
|
|
cebdcfe220 | ||
|
|
cb05a1e5e6 | ||
|
|
8113b003f7 | ||
|
|
2e8b39a74d | ||
|
|
1c730b3d77 |
Generated
+1
-1
@@ -1709,7 +1709,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemur"
|
name = "lemur"
|
||||||
version = "0.2.2"
|
version = "0.2.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ description = "An emulator for the Virtual Boy."
|
|||||||
repository = "https://git.virtual-boy.com/PVB/lemur"
|
repository = "https://git.virtual-boy.com/PVB/lemur"
|
||||||
publish = false
|
publish = false
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
version = "0.2.2"
|
version = "0.2.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
+9
-2
@@ -1,3 +1,4 @@
|
|||||||
|
set -e
|
||||||
version=$(cat Cargo.toml | sed -n -e '/version/ {s/.* = *//p;q}' | tr -d '"')
|
version=$(cat Cargo.toml | sed -n -e '/version/ {s/.* = *//p;q}' | tr -d '"')
|
||||||
|
|
||||||
read -p "You wanted to release $version, right? [Y/n] " -n 1 -r
|
read -p "You wanted to release $version, right? [Y/n] " -n 1 -r
|
||||||
@@ -18,11 +19,15 @@ if ! command -v jq 2>&1 >/dev/null; then
|
|||||||
echo "Please install jq."
|
echo "Please install jq."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if ! command -v docker 2>&1 >/dev/null; then
|
||||||
|
echo "Please install docker."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
docker build -f build.Dockerfile -t lemur-build .
|
docker build -f build.Dockerfile -t lemur-build .
|
||||||
MSYS_NO_PATHCONV=1 docker run -it --rm -v .:/app -w /app --entrypoint bash lemur-build /app/scripts/do-bundle.sh
|
MSYS_NO_PATHCONV=1 docker run -it --rm -v .:/app -w /app --entrypoint bash lemur-build /app/scripts/do-bundle.sh
|
||||||
|
|
||||||
read -r -d EOF 'body' <<EOF
|
body=$(cat <<EOF
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
The emulator can be found in the "Downloads" section of this release.
|
The emulator can be found in the "Downloads" section of this release.
|
||||||
@@ -43,8 +48,9 @@ If you're not sure which to choose, use [this guide](https://support.apple.com/e
|
|||||||
|
|
||||||
You can either download and run \`lemur-linux\`, or download and install the attached .deb file.
|
You can either download and run \`lemur-linux\`, or download and install the attached .deb file.
|
||||||
EOF
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
read -r -d EOF 'payload' <<EOF
|
payload=$(cat <<EOF
|
||||||
{
|
{
|
||||||
"body": $(echo "$body" | jq -Rsa .),
|
"body": $(echo "$body" | jq -Rsa .),
|
||||||
"draft": false,
|
"draft": false,
|
||||||
@@ -53,6 +59,7 @@ read -r -d EOF 'payload' <<EOF
|
|||||||
"tag_name": "v${version}"
|
"tag_name": "v${version}"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
echo "Creating release..."
|
echo "Creating release..."
|
||||||
response=$(curl -s --json "$payload" "https://git.virtual-boy.com/api/v1/repos/PVB/lemur/releases?token=$RELEASE_TOKEN")
|
response=$(curl -s --json "$payload" "https://git.virtual-boy.com/api/v1/repos/PVB/lemur/releases?token=$RELEASE_TOKEN")
|
||||||
|
|||||||
+1
-1
Submodule shrooms-vb-core updated: b4feff41f0...155a3aa678
+17
-11
@@ -251,17 +251,23 @@ impl Viewport {
|
|||||||
});
|
});
|
||||||
egui_extras::install_image_loaders(&ctx);
|
egui_extras::install_image_loaders(&ctx);
|
||||||
|
|
||||||
let mut painter = egui_wgpu::winit::Painter::new(
|
#[allow(unused_mut)]
|
||||||
ctx.clone(),
|
let mut wgpu_config = egui_wgpu::WgpuConfiguration {
|
||||||
egui_wgpu::WgpuConfiguration {
|
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
..egui_wgpu::WgpuConfiguration::default()
|
||||||
..egui_wgpu::WgpuConfiguration::default()
|
};
|
||||||
},
|
#[cfg(windows)]
|
||||||
1,
|
{
|
||||||
None,
|
if let egui_wgpu::WgpuSetup::CreateNew {
|
||||||
false,
|
supported_backends, ..
|
||||||
true,
|
} = &mut wgpu_config.wgpu_setup
|
||||||
);
|
{
|
||||||
|
*supported_backends -= wgpu::Backends::VULKAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut painter =
|
||||||
|
egui_wgpu::winit::Painter::new(ctx.clone(), wgpu_config, 1, None, false, true);
|
||||||
|
|
||||||
let mut info = ViewportInfo::default();
|
let mut info = ViewportInfo::default();
|
||||||
let mut builder = app.initial_viewport();
|
let mut builder = app.initial_viewport();
|
||||||
|
|||||||
+57
-49
@@ -141,6 +141,8 @@ pub struct Emulator {
|
|||||||
linked: Arc<AtomicBool>,
|
linked: Arc<AtomicBool>,
|
||||||
renderers: HashMap<SimId, TextureSink>,
|
renderers: HashMap<SimId, TextureSink>,
|
||||||
messages: HashMap<SimId, mpsc::Sender<Toast>>,
|
messages: HashMap<SimId, mpsc::Sender<Toast>>,
|
||||||
|
eye_contents: Vec<u8>,
|
||||||
|
audio_samples: Vec<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
@@ -164,6 +166,8 @@ impl Emulator {
|
|||||||
linked,
|
linked,
|
||||||
renderers: HashMap::new(),
|
renderers: HashMap::new(),
|
||||||
messages: HashMap::new(),
|
messages: HashMap::new(),
|
||||||
|
eye_contents: vec![0u8; 384 * 224 * 2],
|
||||||
|
audio_samples: vec![0.0; EXPECTED_FRAME_SIZE],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,56 +261,8 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let mut eye_contents = vec![0u8; 384 * 224 * 2];
|
|
||||||
let mut audio_samples = vec![];
|
|
||||||
loop {
|
loop {
|
||||||
let p1_running = self.running[SimId::Player1.to_index()].load(Ordering::Acquire);
|
let idle = self.tick();
|
||||||
let p2_running = self.running[SimId::Player2.to_index()].load(Ordering::Acquire);
|
|
||||||
let mut idle = p1_running || p2_running;
|
|
||||||
if p1_running && p2_running {
|
|
||||||
Sim::emulate_many(&mut self.sims);
|
|
||||||
} else if p1_running {
|
|
||||||
self.sims[SimId::Player1.to_index()].emulate();
|
|
||||||
} else if p2_running {
|
|
||||||
self.sims[SimId::Player2.to_index()].emulate();
|
|
||||||
}
|
|
||||||
|
|
||||||
for sim_id in SimId::values() {
|
|
||||||
let Some(renderer) = self.renderers.get_mut(&sim_id) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if sim.read_pixels(&mut eye_contents) {
|
|
||||||
idle = false;
|
|
||||||
if renderer.queue_render(&eye_contents).is_err() {
|
|
||||||
self.renderers.remove(&sim_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let p1_audio =
|
|
||||||
p1_running && self.audio_on[SimId::Player1.to_index()].load(Ordering::Acquire);
|
|
||||||
let p2_audio =
|
|
||||||
p2_running && self.audio_on[SimId::Player2.to_index()].load(Ordering::Acquire);
|
|
||||||
let weight = if p1_audio && p2_audio { 0.5 } else { 1.0 };
|
|
||||||
if p1_audio {
|
|
||||||
if let Some(sim) = self.sims.get_mut(SimId::Player1.to_index()) {
|
|
||||||
sim.read_samples(&mut audio_samples, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p2_audio {
|
|
||||||
if let Some(sim) = self.sims.get_mut(SimId::Player2.to_index()) {
|
|
||||||
sim.read_samples(&mut audio_samples, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if audio_samples.is_empty() {
|
|
||||||
audio_samples.resize(EXPECTED_FRAME_SIZE, 0.0);
|
|
||||||
} else {
|
|
||||||
idle = false;
|
|
||||||
}
|
|
||||||
self.audio.update(&audio_samples);
|
|
||||||
audio_samples.clear();
|
|
||||||
if idle {
|
if idle {
|
||||||
// The game is paused, and we have output all the video/audio we have.
|
// The game is paused, and we have output all the video/audio we have.
|
||||||
// Block the thread until a new command comes in.
|
// Block the thread until a new command comes in.
|
||||||
@@ -331,6 +287,58 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns true if the emulator is "idle" (i.e. this didn't output anything)
|
||||||
|
pub fn tick(&mut self) -> bool {
|
||||||
|
let p1_running = self.running[SimId::Player1.to_index()].load(Ordering::Acquire);
|
||||||
|
let p2_running = self.running[SimId::Player2.to_index()].load(Ordering::Acquire);
|
||||||
|
let mut idle = p1_running || p2_running;
|
||||||
|
if p1_running && p2_running {
|
||||||
|
Sim::emulate_many(&mut self.sims);
|
||||||
|
} else if p1_running {
|
||||||
|
self.sims[SimId::Player1.to_index()].emulate();
|
||||||
|
} else if p2_running {
|
||||||
|
self.sims[SimId::Player2.to_index()].emulate();
|
||||||
|
}
|
||||||
|
|
||||||
|
for sim_id in SimId::values() {
|
||||||
|
let Some(renderer) = self.renderers.get_mut(&sim_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if sim.read_pixels(&mut self.eye_contents) {
|
||||||
|
idle = false;
|
||||||
|
if renderer.queue_render(&self.eye_contents).is_err() {
|
||||||
|
self.renderers.remove(&sim_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let p1_audio =
|
||||||
|
p1_running && self.audio_on[SimId::Player1.to_index()].load(Ordering::Acquire);
|
||||||
|
let p2_audio =
|
||||||
|
p2_running && self.audio_on[SimId::Player2.to_index()].load(Ordering::Acquire);
|
||||||
|
let weight = if p1_audio && p2_audio { 0.5 } else { 1.0 };
|
||||||
|
if p1_audio {
|
||||||
|
if let Some(sim) = self.sims.get_mut(SimId::Player1.to_index()) {
|
||||||
|
sim.read_samples(&mut self.audio_samples, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p2_audio {
|
||||||
|
if let Some(sim) = self.sims.get_mut(SimId::Player2.to_index()) {
|
||||||
|
sim.read_samples(&mut self.audio_samples, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.audio_samples.is_empty() {
|
||||||
|
self.audio_samples.resize(EXPECTED_FRAME_SIZE, 0.0);
|
||||||
|
} else {
|
||||||
|
idle = false;
|
||||||
|
}
|
||||||
|
self.audio.update(&self.audio_samples);
|
||||||
|
self.audio_samples.clear();
|
||||||
|
idle
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_command(&mut self, command: EmulatorCommand) {
|
fn handle_command(&mut self, command: EmulatorCommand) {
|
||||||
match command {
|
match command {
|
||||||
EmulatorCommand::ConnectToSim(sim_id, renderer, messages) => {
|
EmulatorCommand::ConnectToSim(sim_id, renderer, messages) => {
|
||||||
|
|||||||
@@ -147,7 +147,8 @@ impl Sim {
|
|||||||
let memory = vec![0u64; size.div_ceil(4)];
|
let memory = vec![0u64; size.div_ceil(4)];
|
||||||
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
|
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
|
||||||
unsafe { vb_init(sim) };
|
unsafe { vb_init(sim) };
|
||||||
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) };
|
// pseudohalt is disabled due to breaking red alarm
|
||||||
|
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 0) };
|
||||||
unsafe { vb_reset(sim) };
|
unsafe { vb_reset(sim) };
|
||||||
|
|
||||||
// set up userdata
|
// set up userdata
|
||||||
|
|||||||
Reference in New Issue
Block a user