mirror of
https://github.com/transistorfet/moa.git
synced 2025-01-23 08:32:36 +00:00
Added command line options for minifb and TRS-80
This commit is contained in:
parent
6cc9e98e35
commit
9d799e308d
@ -9,4 +9,5 @@ default-run = "moa-genesis"
|
||||
[dependencies]
|
||||
moa = { path = "../../" }
|
||||
minifb = "0.19"
|
||||
clap = "3.0.0-beta.5"
|
||||
|
||||
|
@ -1,9 +1,14 @@
|
||||
|
||||
|
||||
use moa_minifb;
|
||||
use moa::machines::genesis::build_genesis;
|
||||
use moa_minifb::{run_inline, run_threaded};
|
||||
|
||||
fn main() {
|
||||
//run_inline(build_genesis);
|
||||
run_threaded(build_genesis);
|
||||
let matches = moa_minifb::new("Sega Genesis/Mega Drive Emulator")
|
||||
.get_matches();
|
||||
|
||||
moa_minifb::run(matches, |frontends| {
|
||||
build_genesis(frontend)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,19 @@
|
||||
|
||||
use moa::machines::trs80::build_trs80;
|
||||
use moa_minifb::{run_inline, run_threaded};
|
||||
use moa_minifb;
|
||||
use moa::machines::trs80::{build_trs80, Trs80Options};
|
||||
|
||||
fn main() {
|
||||
//run_inline(build_trs80);
|
||||
run_threaded(build_trs80);
|
||||
let matches = moa_minifb::new("TRS-80 Emulator")
|
||||
.arg("-r, --rom=[FILE] 'ROM file to load at the start of memory'")
|
||||
.get_matches();
|
||||
|
||||
let mut options = Trs80Options::new();
|
||||
if let Some(filename) = matches.value_of("rom") {
|
||||
options.rom = filename.to_string();
|
||||
}
|
||||
|
||||
moa_minifb::run(matches, |frontend| {
|
||||
build_trs80(frontend, options)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,72 @@
|
||||
|
||||
mod keys;
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use minifb::{self, Key};
|
||||
use clap::{App, ArgMatches};
|
||||
|
||||
use moa::error::Error;
|
||||
use moa::system::System;
|
||||
use moa::host::traits::{Host, JoystickDevice, JoystickUpdater, KeyboardUpdater, WindowUpdater};
|
||||
|
||||
mod keys;
|
||||
use crate::keys::map_key;
|
||||
|
||||
|
||||
//const WIDTH: usize = 320;
|
||||
//const HEIGHT: usize = 224;
|
||||
const WIDTH: u32 = 320;
|
||||
const HEIGHT: u32 = 224;
|
||||
|
||||
|
||||
pub fn new(name: &str) -> App {
|
||||
App::new(name)
|
||||
.arg("-s, --scale=[1,2,4] 'Scale the screen'")
|
||||
.arg("-t, --threaded 'Run the simulation in a separate thread'")
|
||||
}
|
||||
|
||||
pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
||||
if matches.value_of("threaded").is_some() {
|
||||
run_inline(matches, init);
|
||||
} else {
|
||||
run_threaded(matches, init);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_inline<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> {
|
||||
let mut frontend = MiniFrontendBuilder::new();
|
||||
let system = init(&mut frontend).unwrap();
|
||||
|
||||
frontend
|
||||
.build()
|
||||
.start(matches, Some(system));
|
||||
}
|
||||
|
||||
pub fn run_threaded<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
||||
let frontend = Arc::new(Mutex::new(MiniFrontendBuilder::new()));
|
||||
|
||||
{
|
||||
let frontend = frontend.clone();
|
||||
thread::spawn(move || {
|
||||
let mut system = init(&mut *(frontend.lock().unwrap())).unwrap();
|
||||
frontend.lock().unwrap().finalize();
|
||||
system.run_loop();
|
||||
});
|
||||
}
|
||||
|
||||
wait_until_initialized(frontend.clone());
|
||||
|
||||
frontend
|
||||
.lock().unwrap()
|
||||
.build()
|
||||
.start(matches, None);
|
||||
}
|
||||
|
||||
fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
|
||||
while frontend.lock().unwrap().finalized == false {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
|
||||
const WIDTH: usize = 384;
|
||||
const HEIGHT: usize = 128;
|
||||
|
||||
pub struct MiniFrontendBuilder {
|
||||
pub window: Option<Box<dyn WindowUpdater>>,
|
||||
@ -90,21 +138,32 @@ pub struct MiniFrontend {
|
||||
impl MiniFrontend {
|
||||
pub fn new(window: Option<Box<dyn WindowUpdater>>, joystick: Option<Box<dyn JoystickUpdater>>, keyboard: Option<Box<dyn KeyboardUpdater>>) -> Self {
|
||||
Self {
|
||||
buffer: vec![0; WIDTH * HEIGHT],
|
||||
buffer: vec![0; (WIDTH * HEIGHT) as usize],
|
||||
window,
|
||||
joystick,
|
||||
keyboard,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, mut system: Option<System>) {
|
||||
pub fn start(&mut self, matches: ArgMatches, mut system: Option<System>) {
|
||||
let mut options = minifb::WindowOptions::default();
|
||||
options.scale = minifb::Scale::X2;
|
||||
options.scale = match matches.value_of("scale").map(|s| u8::from_str_radix(s, 10).unwrap()) {
|
||||
Some(1) => minifb::Scale::X1,
|
||||
Some(2) => minifb::Scale::X2,
|
||||
Some(4) => minifb::Scale::X4,
|
||||
_ => minifb::Scale::X2,
|
||||
};
|
||||
|
||||
let mut size = (WIDTH, HEIGHT);
|
||||
if let Some(updater) = self.window.as_mut() {
|
||||
size = updater.get_size();
|
||||
self.buffer = vec![0; (size.0 * size.1) as usize];
|
||||
}
|
||||
|
||||
let mut window = minifb::Window::new(
|
||||
"Test - ESC to exit",
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
size.0 as usize,
|
||||
size.1 as usize,
|
||||
options,
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
@ -144,47 +203,10 @@ impl MiniFrontend {
|
||||
}
|
||||
|
||||
if let Some(updater) = self.window.as_mut() {
|
||||
updater.update_frame(WIDTH as u32, HEIGHT as u32, &mut self.buffer);
|
||||
window.update_with_buffer(&self.buffer, WIDTH, HEIGHT).unwrap();
|
||||
updater.update_frame(size.0, size.1, &mut self.buffer);
|
||||
window.update_with_buffer(&self.buffer, size.0 as usize, size.1 as usize).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn run_inline<I>(init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> {
|
||||
let mut frontend = MiniFrontendBuilder::new();
|
||||
let system = init(&mut frontend).unwrap();
|
||||
|
||||
frontend
|
||||
.build()
|
||||
.start(Some(system));
|
||||
}
|
||||
|
||||
pub fn run_threaded<I>(init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
||||
let frontend = Arc::new(Mutex::new(MiniFrontendBuilder::new()));
|
||||
|
||||
{
|
||||
let frontend = frontend.clone();
|
||||
thread::spawn(move || {
|
||||
let mut system = init(&mut *(frontend.lock().unwrap())).unwrap();
|
||||
frontend.lock().unwrap().finalize();
|
||||
system.run_loop();
|
||||
});
|
||||
}
|
||||
|
||||
wait_until_initialized(frontend.clone());
|
||||
|
||||
frontend
|
||||
.lock().unwrap()
|
||||
.build()
|
||||
.start(None);
|
||||
}
|
||||
|
||||
fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
|
||||
while frontend.lock().unwrap().finalized == false {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ impl BlitableSurface for Frame {
|
||||
self.bitmap.resize((width * height) as usize, 0);
|
||||
}
|
||||
|
||||
fn blit<B: Iterator<Item=u32>>(&mut self, pos_x: u32, mut pos_y: u32, mut bitmap: B, width: u32, height: u32) {
|
||||
fn blit<B: Iterator<Item=u32>>(&mut self, pos_x: u32, pos_y: u32, mut bitmap: B, width: u32, height: u32) {
|
||||
for y in pos_y..(pos_y + height) {
|
||||
for x in pos_x..(pos_x + width) {
|
||||
match bitmap.next().unwrap() {
|
||||
@ -61,6 +61,10 @@ impl FrameSwapper {
|
||||
}
|
||||
|
||||
impl WindowUpdater for FrameSwapper {
|
||||
fn get_size(&mut self) -> (u32, u32) {
|
||||
(self.current.width, self.current.height)
|
||||
}
|
||||
|
||||
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
|
||||
std::mem::swap(&mut self.current, &mut self.previous);
|
||||
|
||||
@ -75,6 +79,10 @@ impl WindowUpdater for FrameSwapper {
|
||||
pub struct FrameSwapperWrapper(Arc<Mutex<FrameSwapper>>);
|
||||
|
||||
impl WindowUpdater for FrameSwapperWrapper {
|
||||
fn get_size(&mut self) -> (u32, u32) {
|
||||
self.0.lock().map(|mut swapper| swapper.get_size()).unwrap_or((0, 0))
|
||||
}
|
||||
|
||||
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
|
||||
self.0.lock().map(|mut swapper| swapper.update_frame(width, height, bitmap));
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ pub trait Tty {
|
||||
}
|
||||
|
||||
pub trait WindowUpdater: Send {
|
||||
fn get_size(&mut self) -> (u32, u32);
|
||||
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
|
||||
}
|
||||
|
||||
|
@ -9,23 +9,40 @@ use crate::peripherals::trs80;
|
||||
|
||||
use crate::host::traits::Host;
|
||||
|
||||
pub struct Trs80Options {
|
||||
pub rom: String,
|
||||
pub memory: u16,
|
||||
pub frequency: u32,
|
||||
}
|
||||
|
||||
pub fn build_trs80<H: Host>(host: &mut H) -> Result<System, Error> {
|
||||
impl Trs80Options {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
rom: "binaries/trs80/level2.rom".to_string(),
|
||||
memory: 0xC000,
|
||||
frequency: 1_774_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn build_trs80<H: Host>(host: &mut H, options: Trs80Options) -> Result<System, Error> {
|
||||
let mut system = System::new();
|
||||
|
||||
let mut rom = MemoryBlock::new(vec![0; 0x4000]);
|
||||
rom.load_at(0x0000, "binaries/trs80/level1.rom")?;
|
||||
//rom.load_at(0x0000, "binaries/trs80/level1.rom")?;
|
||||
//rom.load_at(0x0000, "binaries/trs80/level2.rom")?;
|
||||
rom.load_at(0x0000, &options.rom)?;
|
||||
rom.read_only();
|
||||
system.add_addressable_device(0x0000, wrap_transmutable(rom))?;
|
||||
|
||||
let ram = MemoryBlock::new(vec![0; 0xC000]);
|
||||
let ram = MemoryBlock::new(vec![0; options.memory as usize]);
|
||||
system.add_addressable_device(0x4000, wrap_transmutable(ram))?;
|
||||
|
||||
let model1 = trs80::model1::Model1Peripherals::create(host)?;
|
||||
system.add_addressable_device(0x37E0, wrap_transmutable(model1)).unwrap();
|
||||
|
||||
let mut cpu = Z80::new(Z80Type::Z80, 4_000_000, BusPort::new(0, 16, 8, system.bus.clone()));
|
||||
let mut cpu = Z80::new(Z80Type::Z80, options.frequency, BusPort::new(0, 16, 8, system.bus.clone()));
|
||||
//cpu.add_breakpoint(0x0);
|
||||
//cpu.add_breakpoint(0xb55);
|
||||
//cpu.add_breakpoint(0xb76);
|
||||
@ -46,9 +63,9 @@ pub fn build_trs80<H: Host>(host: &mut H) -> Result<System, Error> {
|
||||
//cpu.add_breakpoint(0xc77);
|
||||
//cpu.add_breakpoint(0xc83);
|
||||
//cpu.add_breakpoint(0x96d);
|
||||
cpu.add_breakpoint(0x970);
|
||||
cpu.add_breakpoint(0x9e2);
|
||||
cpu.add_breakpoint(0x9f9);
|
||||
//cpu.add_breakpoint(0x970);
|
||||
//cpu.add_breakpoint(0x9e2);
|
||||
//cpu.add_breakpoint(0x9f9);
|
||||
|
||||
system.add_interruptable_device("cpu", wrap_transmutable(cpu))?;
|
||||
|
||||
|
60
todo.txt
60
todo.txt
@ -1,28 +1,8 @@
|
||||
|
||||
* there is a problem where something writes to the rom area which causes a crash
|
||||
At 0x16cde, a move writes an invalid value to 0 via an indirect %a2 reg. The value of the reg might have changed during an interrupt, but it definitely breaks when the next interrupt occurs
|
||||
Before the loop is 0x16a0e which then calculates the count and such
|
||||
0x16584 is where the memory address 0xffd11a is updated, which is then used for the bad 0x0000 address which causes the improper write. 0x16570 is a better start
|
||||
On broken cycle: %a1 = 1df40, moves that location + 1 to %d0
|
||||
|
||||
|
||||
* 0x1650e is where 0xffac08 is changed to 0xd100
|
||||
* 0x16572 is where 0xffd100 is changed to 0
|
||||
* what if it's a problem when turning the 0 into a full address (which should be ff0000 or ffff00 but instead ends up being 000000)
|
||||
|
||||
|
||||
|
||||
|
||||
* the overflow bit is only correct for addition but not subtraction... in subtraction, two positives can result in a negative and vice versa
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* fix ym7101 to better handle V/H interrupts (right now it sets and then the next step will clear, but it'd be nice if it could 'edge trigger')
|
||||
|
||||
* could have a remapper device, which takes a big swath of addresses in and maps them to another set of addresses (for Mac VIA generic to bus-hookup-in-mac adapter)
|
||||
* how can you do devices that change their address map during operation, like mac which puts rom at 0 and ram at 600000 temporarily
|
||||
* i need a better way of handling disperate reads/writes to I/O spaces, rather than having multiple devices or having a massive chunk of address space allocated, continuously
|
||||
@ -32,36 +12,44 @@ On broken cycle: %a1 = 1df40, moves that location + 1 to %d0
|
||||
So both could share the same Signal, one setting it and the other reading it, but how would you actually configure/build that?
|
||||
|
||||
|
||||
* make it possible to specify the rom on the command line. Would this be machine specific?
|
||||
* make the frontend resize its window based on the frame swapper
|
||||
* make it possible to set the frame sizes when creating the frame swapper
|
||||
* should you rename devices.rs traits.rs?
|
||||
|
||||
|
||||
|
||||
* implement a Z80
|
||||
* maybe see about a Mac 128k or something
|
||||
|
||||
* add instruction timing to M68k
|
||||
* YM7101 timing is causing it to be very slow... speeding this up increasing rendering speed a lot, even though the frame shouldn't be drawn that often... not sure what's wrong with the timing
|
||||
* make the ym7101 set/reset the v_int occurred flag based on the interrupt controller
|
||||
|
||||
|
||||
* you could modify read()/write() in Addressable to return the number of bytes read or written for dynamic bus sizing used by the MC68020+
|
||||
* should you simulate bus arbitration?
|
||||
|
||||
|
||||
|
||||
* make tests for each instruction
|
||||
* check all instructions in the docs
|
||||
|
||||
* unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX
|
||||
* >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, RTM, PACK, TRAPcc, UNPK
|
||||
|
||||
* add support for MMU
|
||||
* add support for FPU
|
||||
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc
|
||||
Genesis/Mega Drive:
|
||||
* fix ym7101 to better handle V/H interrupts (right now it sets and then the next step will clear, but it'd be nice if it could 'edge trigger')
|
||||
* YM7101 timing is causing it to be very slow... speeding this up increasing rendering speed a lot, even though the frame shouldn't be drawn that often... not sure what's wrong with the timing
|
||||
* make the ym7101 set/reset the v_int occurred flag based on the interrupt controller
|
||||
|
||||
|
||||
* how can you have multple CPUs
|
||||
68000:
|
||||
* add instruction timing to M68k
|
||||
* make tests for each instruction
|
||||
* check all instructions in the docs
|
||||
|
||||
* unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX
|
||||
* >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, RTM, PACK, TRAPcc, UNPK
|
||||
|
||||
* add support for MMU
|
||||
* add support for FPU
|
||||
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc
|
||||
|
||||
Z80:
|
||||
* add instruction timings to Z80
|
||||
|
||||
|
||||
|
||||
* how can you have multiple CPUs
|
||||
* each device that can make a bus request should have a BusPort which is used to access the bus
|
||||
* can you eventually make the system connections all configurable via a config file?
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user