Added command line options for minifb and TRS-80

This commit is contained in:
transistor 2021-11-11 09:52:18 -08:00
parent 6cc9e98e35
commit 9d799e308d
8 changed files with 153 additions and 101 deletions

View File

@ -9,4 +9,5 @@ default-run = "moa-genesis"
[dependencies]
moa = { path = "../../" }
minifb = "0.19"
clap = "3.0.0-beta.5"

View File

@ -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)
});
}

View File

@ -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)
});
}

View File

@ -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));
}
}

View File

@ -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));
}

View File

@ -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]);
}

View File

@ -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))?;

View File

@ -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?