Modified frame/frameswapper a bit

This commit is contained in:
transistor 2021-12-08 13:52:11 -08:00
parent 6dbae9620d
commit 8db32ab9b3
5 changed files with 139 additions and 94 deletions

View File

@ -11,6 +11,21 @@ pub struct Frame {
pub bitmap: Vec<u32>, pub bitmap: Vec<u32>,
} }
impl Frame {
pub fn new(width: u32, height: u32) -> Self {
Self { width, height, bitmap: vec![0; (width * height) as usize] }
}
pub fn new_shared(width: u32, height: u32) -> Arc<Mutex<Frame>> {
Arc::new(Mutex::new(Frame::new(width, height)))
}
pub fn new_updater(frame: Arc<Mutex<Frame>>) -> Box<dyn WindowUpdater> {
Box::new(FrameUpdateWrapper(frame))
}
}
impl BlitableSurface for Frame { impl BlitableSurface for Frame {
fn set_size(&mut self, width: u32, height: u32) { fn set_size(&mut self, width: u32, height: u32) {
self.width = width; self.width = width;
@ -38,56 +53,66 @@ impl BlitableSurface for Frame {
} }
} }
pub struct FrameUpdateWrapper(Arc<Mutex<Frame>>);
impl WindowUpdater for FrameUpdateWrapper {
fn get_size(&mut self) -> (u32, u32) {
match self.0.lock() {
Ok(frame) => (frame.width, frame.height),
_ => (0, 0),
}
}
fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) {
if let Ok(frame) = self.0.lock() {
for y in 0..frame.height {
for x in 0..frame.width {
bitmap[(x + (y * width)) as usize] = frame.bitmap[(x + (y * frame.width)) as usize];
}
}
}
}
}
#[derive(Clone)]
pub struct FrameSwapper { pub struct FrameSwapper {
pub current: Frame, pub current: Arc<Mutex<Frame>>,
//pub previous: Frame, pub previous: Arc<Mutex<Frame>>,
} }
impl FrameSwapper { impl FrameSwapper {
pub fn new(width: u32, height: u32) -> FrameSwapper { pub fn new(width: u32, height: u32) -> FrameSwapper {
FrameSwapper { FrameSwapper {
current: Frame { width, height, bitmap: vec![0; (width * height) as usize] }, current: Arc::new(Mutex::new(Frame::new(width, height))),
//previous: Frame { width, height, bitmap: vec![0; (width * height) as usize] }, previous: Arc::new(Mutex::new(Frame::new(width, height))),
} }
} }
pub fn new_shared(width: u32, height: u32) -> Arc<Mutex<FrameSwapper>> { pub fn to_boxed(swapper: FrameSwapper) -> Box<dyn WindowUpdater> {
Arc::new(Mutex::new(FrameSwapper::new(width, height))) Box::new(swapper)
}
pub fn to_boxed(swapper: Arc<Mutex<FrameSwapper>>) -> Box<dyn WindowUpdater> {
Box::new(FrameSwapperWrapper(swapper))
} }
} }
impl WindowUpdater for FrameSwapper { impl WindowUpdater for FrameSwapper {
fn get_size(&mut self) -> (u32, u32) { fn get_size(&mut self) -> (u32, u32) {
(self.current.width, self.current.height) if let Ok(frame) = self.current.lock() {
(frame.width, frame.height)
} else {
(0, 0)
}
} }
fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) { fn update_frame(&mut self, width: u32, _height: u32, bitmap: &mut [u32]) {
//std::mem::swap(&mut self.current, &mut self.previous); std::mem::swap(&mut self.current.lock().unwrap().bitmap, &mut self.previous.lock().unwrap().bitmap);
for y in 0..self.current.height { if let Ok(frame) = self.previous.lock() {
for x in 0..self.current.width { for y in 0..frame.height {
bitmap[(x + (y * width)) as usize] = self.current.bitmap[(x + (y * self.current.width)) as usize]; for x in 0..frame.width {
bitmap[(x + (y * width)) as usize] = frame.bitmap[(x + (y * frame.width)) as usize];
}
} }
} }
} }
} }
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]) {
if let Ok(mut swapper) = self.0.lock() {
swapper.update_frame(width, height, bitmap);
}
}
}

View File

@ -539,14 +539,14 @@ impl<'a> Iterator for PatternIterator<'a> {
pub struct Ym7101 { pub struct Ym7101 {
pub swapper: Arc<Mutex<FrameSwapper>>, pub swapper: FrameSwapper,
pub state: Ym7101State, pub state: Ym7101State,
pub external_interrupt: HostData<bool>, pub external_interrupt: HostData<bool>,
} }
impl Ym7101 { impl Ym7101 {
pub fn new<H: Host>(host: &mut H, external_interrupt: HostData<bool>) -> Ym7101 { pub fn new<H: Host>(host: &mut H, external_interrupt: HostData<bool>) -> Ym7101 {
let swapper = FrameSwapper::new_shared(320, 224); let swapper = FrameSwapper::new(320, 224);
host.add_window(FrameSwapper::to_boxed(swapper.clone())).unwrap(); host.add_window(FrameSwapper::to_boxed(swapper.clone())).unwrap();
@ -607,12 +607,12 @@ impl Steppable for Ym7101 {
system.get_interrupt_controller().set(true, 6, 30)?; system.get_interrupt_controller().set(true, 6, 30)?;
} }
let mut swapper = self.swapper.lock().unwrap(); let mut frame = self.swapper.current.lock().unwrap();
self.state.draw_frame(&mut swapper.current); self.state.draw_frame(&mut frame);
//let mut swapper = self.swapper.lock().unwrap(); //let mut frame = self.swapper.current.lock().unwrap();
//let iter = PatternIterator::new(&self.state, 0x260, 0, true, true); //let iter = PatternIterator::new(&self.state, 0x260, 0, true, true);
//swapper.current.blit(0, 0, iter, 8, 8); //frame.blit(0, 0, iter, 8, 8);
/* /*
// Print Palette // Print Palette
@ -623,13 +623,13 @@ impl Steppable for Ym7101 {
/* /*
// Print Pattern Table // Print Pattern Table
let mut swapper = self.swapper.lock().unwrap(); let mut frame = self.swapper.current.lock().unwrap();
let (cells_h, cells_v) = self.state.get_screen_size(); let (cells_h, cells_v) = self.state.get_screen_size();
for cell_y in 0..cells_v { for cell_y in 0..cells_v {
for cell_x in 0..cells_h { for cell_x in 0..cells_h {
let pattern_addr = (cell_x + (cell_y * cells_h)) * 32; let pattern_addr = (cell_x + (cell_y * cells_h)) * 32;
let iter = PatternIterator::new(&self.state, pattern_addr as u32, 0, false, false); let iter = PatternIterator::new(&self.state, pattern_addr as u32, 0, false, false);
swapper.current.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8); frame.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8);
} }
} }
*/ */
@ -637,8 +637,8 @@ impl Steppable for Ym7101 {
/* /*
// Print Sprite // Print Sprite
let mut swapper = self.swapper.lock().unwrap(); let mut frame = self.swapper.current.lock().unwrap();
self.state.draw_background(&mut swapper.current); self.state.draw_background(&mut frame);
let sprite_table = self.get_vram_sprites_addr(); let sprite_table = self.get_vram_sprites_addr();
let (cells_h, cells_v) = self.state.get_screen_size(); let (cells_h, cells_v) = self.state.get_screen_size();
let sprite = 0; let sprite = 0;
@ -654,18 +654,18 @@ impl Steppable for Ym7101 {
let pattern_addr = (pattern_gen + (cell_y * size_h) + cell_x) as u32; let pattern_addr = (pattern_gen + (cell_y * size_h) + cell_x) as u32;
println!("pattern: ({}, {}) {:x}", cell_x, cell_y, pattern_addr); println!("pattern: ({}, {}) {:x}", cell_x, cell_y, pattern_addr);
let iter = PatternIterator::new(&self.state, pattern_addr * 32, 3, true, true); let iter = PatternIterator::new(&self.state, pattern_addr * 32, 3, true, true);
swapper.current.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8); frame.blit((cell_x << 3) as u32, (cell_y << 3) as u32, iter, 8, 8);
} }
} }
*/ */
//let mut swapper = self.swapper.lock().unwrap(); //let mut frame = self.swapper.current.lock().unwrap();
//swapper.current.blit(0, 0, PatternIterator::new(&self.state, 0x408 * 32, 3, false, false), 8, 8); //frame.blit(0, 0, PatternIterator::new(&self.state, 0x408 * 32, 3, false, false), 8, 8);
//swapper.current.blit(0, 8, PatternIterator::new(&self.state, 0x409 * 32, 3, false, false), 8, 8); //frame.blit(0, 8, PatternIterator::new(&self.state, 0x409 * 32, 3, false, false), 8, 8);
//swapper.current.blit(8, 0, PatternIterator::new(&self.state, 0x402 * 32, 3, false, false), 8, 8); //frame.blit(8, 0, PatternIterator::new(&self.state, 0x402 * 32, 3, false, false), 8, 8);
//swapper.current.blit(8, 8, PatternIterator::new(&self.state, 0x403 * 32, 3, false, false), 8, 8); //frame.blit(8, 8, PatternIterator::new(&self.state, 0x403 * 32, 3, false, false), 8, 8);
//swapper.current.blit(16, 0, PatternIterator::new(&self.state, 0x404 * 32, 3, false, false), 8, 8); //frame.blit(16, 0, PatternIterator::new(&self.state, 0x404 * 32, 3, false, false), 8, 8);
//swapper.current.blit(16, 8, PatternIterator::new(&self.state, 0x405 * 32, 3, false, false), 8, 8); //frame.blit(16, 8, PatternIterator::new(&self.state, 0x405 * 32, 3, false, false), 8, 8);
} }
if self.state.transfer_run != DmaType::None && (self.state.mode_2 & MODE2_BF_DMA_ENABLED) != 0 { if self.state.transfer_run != DmaType::None && (self.state.mode_2 & MODE2_BF_DMA_ENABLED) != 0 {

View File

@ -5,24 +5,24 @@ use crate::error::Error;
use crate::system::System; use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable}; use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
use crate::host::gfx::FrameSwapper; use crate::host::gfx::Frame;
use crate::host::traits::{Host, BlitableSurface}; use crate::host::traits::{Host, BlitableSurface};
const SCRN_BASE: u32 = 0x07A700; const SCRN_BASE: u32 = 0x07A700;
pub struct MacVideo { pub struct MacVideo {
pub swapper: Arc<Mutex<FrameSwapper>>, pub frame: Arc<Mutex<Frame>>,
} }
impl MacVideo { impl MacVideo {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> { pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let swapper = FrameSwapper::new_shared(512, 342); let frame = Frame::new_shared(512, 342);
host.add_window(FrameSwapper::to_boxed(swapper.clone()))?; host.add_window(Frame::new_updater(frame.clone()))?;
Ok(Self { Ok(Self {
swapper, frame,
}) })
} }
} }
@ -63,11 +63,11 @@ impl Iterator for BitIter {
impl Steppable for MacVideo { impl Steppable for MacVideo {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> { fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
let mut memory = system.get_bus(); let mut memory = system.get_bus();
let mut swapper = self.swapper.lock().unwrap(); let mut frame = self.frame.lock().unwrap();
for y in 0..342 { for y in 0..342 {
for x in 0..(512 / 16) { for x in 0..(512 / 16) {
let word = memory.read_beu16((SCRN_BASE + (x * 2) + (y * (512 / 8))) as Address)?; let word = memory.read_beu16((SCRN_BASE + (x * 2) + (y * (512 / 8))) as Address)?;
swapper.current.blit(x * 16, y, BitIter::new(word), 16, 1); frame.blit(x * 16, y, BitIter::new(word), 16, 1);
} }
} }
Ok(16_600_000) Ok(16_600_000)

View File

@ -6,7 +6,7 @@ use crate::system::System;
use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable}; use crate::devices::{ClockElapsed, Address, Addressable, Steppable, Transmutable};
use crate::host::keys::Key; use crate::host::keys::Key;
use crate::host::gfx::{FrameSwapper}; use crate::host::gfx::{Frame};
use crate::host::traits::{Host, BlitableSurface, KeyboardUpdater}; use crate::host::traits::{Host, BlitableSurface, KeyboardUpdater};
use super::keymap; use super::keymap;
@ -16,21 +16,21 @@ use super::charset::CharacterGenerator;
const DEV_NAME: &'static str = "model1"; const DEV_NAME: &'static str = "model1";
pub struct Model1Peripherals { pub struct Model1Peripherals {
pub swapper: Arc<Mutex<FrameSwapper>>, pub frame: Arc<Mutex<Frame>>,
pub keyboard_mem: Arc<Mutex<[u8; 8]>>, pub keyboard_mem: Arc<Mutex<[u8; 8]>>,
pub video_mem: [u8; 1024], pub video_mem: [u8; 1024],
} }
impl Model1Peripherals { impl Model1Peripherals {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> { pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let swapper = FrameSwapper::new_shared(384, 128); let frame = Frame::new_shared(384, 128);
let keyboard_mem = Arc::new(Mutex::new([0; 8])); let keyboard_mem = Arc::new(Mutex::new([0; 8]));
host.add_window(FrameSwapper::to_boxed(swapper.clone()))?; host.add_window(Frame::new_updater(frame.clone()))?;
host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?; host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?;
Ok(Self { Ok(Self {
swapper, frame,
keyboard_mem, keyboard_mem,
video_mem: [0; 1024], video_mem: [0; 1024],
}) })
@ -48,13 +48,13 @@ impl KeyboardUpdater for Model1KeyboardUpdater {
impl Steppable for Model1Peripherals { impl Steppable for Model1Peripherals {
fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> { fn step(&mut self, _system: &System) -> Result<ClockElapsed, Error> {
let mut swapper = self.swapper.lock().unwrap(); let mut frame = self.frame.lock().unwrap();
swapper.current.clear(0); frame.clear(0);
for y in 0..16 { for y in 0..16 {
for x in 0..64 { for x in 0..64 {
let ch = self.video_mem[x + (y * 64)]; let ch = self.video_mem[x + (y * 64)];
let iter = CharacterGenerator::new((ch - 0x20) % 64); let iter = CharacterGenerator::new((ch - 0x20) % 64);
swapper.current.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8); frame.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8);
} }
} }

View File

@ -1,48 +1,66 @@
* I'm trying to explore the alternative of having the frontend call a function and pass a closure that takes a frame (or buffer) and then draws
calls the update function from there...
* you can't put a closure into the WindowUpdate trait because you pass it in as Box<dyn WindowUpdater> which is a trait object, and you
can't mix generics and trait objects...
* you could maybe pass a closure in if you pass the updater as a generic WindowUpdater, although then you can only have one per application
* you could have a shared buffer that you submit to the frontend, and you both update it whenever (less synchronized which might not be good)
* you could make the sound device be an object that is passed back to the simulation section like SimplePty. You need to either register
a callback with the frontend sound system that is called when it needs data, or you write to a shared buffer which is passed back to the
frontend when it needs it, or it has a copy it can use directly
* can you make some kind of signal that goes high when a frame has been drawn, and also all the system to pause execution at that point
* for Signal/Register, you could possibly unify them, or you could distinguish them even more
* should you rename Register to AsyncSignal or something
* copy the callback over to Signal, or even make a trait that implements it for both? Or should you make a special object that is observable
which should always use the callback, and Signal would always be used when the callback wasn't used
* think more about what kinds of signals are used:
- one setter with multiple passive listeners
- one one-shot setter (no reset) with one active listener that resets the signal
-
* add sound
* should you rename devices.rs traits.rs? * should you rename devices.rs traits.rs?
* should SharedData be HostData, or something else? I don't think the name is very informative
* rewrite the frame swapper thing to either not use the swapper or somethnig... it's just very sloppy and needs improving * rewrite the frame swapper thing to either not use the swapper or somethnig... it's just very sloppy and needs improving
* modify the frame swapper and frontend to avoid the extra buffer copy
* add command line arguments to speed up or slow down either the frame rate limiter or the simulated time per frame
* can you make the connections between things (like memory adapters), be expressed in a way that's more similar to the electrical design? * can you make the connections between things (like memory adapters), be expressed in a way that's more similar to the electrical design?
like specifying that address pins 10-7 should be ignored/unconnected, pin 11 will connect to "chip select", etc like specifying that address pins 10-7 should be ignored/unconnected, pin 11 will connect to "chip select", etc
* can you make the address bus/repeating thing in the mac with the rom and ram, can you make it work for both the 128 and 512
* add sound
* should you simulate bus arbitration? * should you simulate bus arbitration?
* interrupts could be done in a better way * interrupts could be done in a better way
* need a better way of handling disparate reads/writes to I/O spaces, rather than having multiple devices or having a massive chunk of address space allocated, continuously
* modify the frame swapper and frontend to avoid the extra buffer copy
* add command line arguments to speed up or slow down either the frame rate limiter or the simulated time per frame
* need to implement the 1.5ms reset in the genesis controllers
* 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
* should you modify Addressable to also take the absolute address as input? I'm thinking of how the same device could be mapped to multiple addresses in memory instead * should you modify Addressable to also take the absolute address as input? I'm thinking of how the same device could be mapped to multiple addresses in memory instead
of taking up a whole range of addresses of taking up a whole range of addresses
* 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)
* could you use a generic sharable signal thing for sharing data, such as the VIA in mac128 where a single output bit determines the video mode (which would be a separate device)
So both could share the same Signal, one setting it and the other reading it, but how would you actually configure/build that?
* add more m68k tests and try to test against a working impl
* you could modify read()/write() in Addressable to return the number of bytes read or written for dynamic bus sizing used by the MC68020+ * you could modify read()/write() in Addressable to return the number of bytes read or written for dynamic bus sizing used by the MC68020+
Debugger:
* how can you improve the debugger?
* the command line definitely needs to be fixed so it prints the prompt correctly
* debugger could maybe even allows arrows left/right for editing, and up/down for history
Genesis/Mega Drive: Genesis/Mega Drive:
* need to implement the 1.5ms reset in the genesis controllers
* 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') * 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 * make the ym7101 set/reset the v_int occurred flag based on the interrupt controller
Macintosh:
* issues when booting the rom, attempt to write to rom during the driver init/open phase
* for the address bus/repeating thing in the mac with the rom and ram, can you make it work for both the 128 and 512
68000: 68000:
* add instruction timing to M68k * add instruction timing to M68k
* make tests for each instruction
* check all instructions in the docs * check all instructions in the docs
* unimplemented: BFFFO, BFINS, CHK, ILLEGAL, NBCD, NEGX, RTR, RTD * unimplemented: BFFFO, BFINS, CHK, ILLEGAL, NBCD, NEGX, RTR, RTD
@ -52,13 +70,15 @@ Genesis/Mega Drive:
* add support for FPU * add support for FPU
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc * Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc
* add more m68k tests and try to test against a working impl (m68k-test-suite project)
Z80: Z80:
* add instruction timings to Z80 * add instruction timings to Z80
* unimplemented: CPD, CPDR, CPI, CPIR, DAA, IND, INDR, INI, INIR, INic, INx, OTDR, OTIR, OUTD, OUTI, OUTic, OUTx, RETI, RETN, RLD, RRD
* work on mac128/512 * work on mac128/512
* work on sega genesis * work on sega genesis
* how can you have multiple CPUs
* can you eventually make the system connections all configurable via a config file? * can you eventually make the system connections all configurable via a config file?