mirror of
https://github.com/transistorfet/moa.git
synced 2025-01-22 02:29:58 +00:00
Added minifb frontend with very WIP genesis peripherals
This commit is contained in:
parent
892f93f053
commit
109ae4db55
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[workspace]
|
||||
members = [".", "frontends/moa-console"]
|
||||
members = [".", "frontends/moa-console", "frontends/moa-minifb"]
|
||||
default-members = ["frontends/moa-console"]
|
||||
|
||||
[features]
|
||||
|
BIN
binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin
Executable file
BIN
binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin
Executable file
Binary file not shown.
12
frontends/moa-minifb/Cargo.toml
Normal file
12
frontends/moa-minifb/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "moa-minifb"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
default-run = "moa-genesis"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
moa = { path = "../../" }
|
||||
minifb = "0.19"
|
||||
|
28
frontends/moa-minifb/src/bin/moa-genesis.rs
Normal file
28
frontends/moa-minifb/src/bin/moa-genesis.rs
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
use std::thread;
|
||||
use std::sync::Arc;
|
||||
|
||||
use moa::machines::genesis::build_genesis;
|
||||
use moa_minifb::MiniFrontend;
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
let mut frontend = Arc::new(MiniFrontend::init_frontend());
|
||||
|
||||
{
|
||||
let frontend = frontend.clone();
|
||||
thread::spawn(move || {
|
||||
let mut system = build_genesis(&*frontend).unwrap();
|
||||
system.run_loop();
|
||||
});
|
||||
}
|
||||
|
||||
frontend.start();
|
||||
*/
|
||||
|
||||
let mut frontend = MiniFrontend::init_frontend();
|
||||
let mut system = build_genesis(&frontend).unwrap();
|
||||
|
||||
frontend.start(system);
|
||||
}
|
||||
|
68
frontends/moa-minifb/src/lib.rs
Normal file
68
frontends/moa-minifb/src/lib.rs
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use minifb::{self, Key};
|
||||
|
||||
use moa::error::Error;
|
||||
use moa::system::System;
|
||||
use moa::host::traits::{Host, WindowUpdater};
|
||||
|
||||
|
||||
const WIDTH: usize = 640;
|
||||
const HEIGHT: usize = 360;
|
||||
|
||||
pub struct MiniFrontend {
|
||||
pub buffer: Mutex<Vec<u32>>,
|
||||
pub updater: Mutex<Option<Box<dyn WindowUpdater>>>,
|
||||
}
|
||||
|
||||
impl Host for MiniFrontend {
|
||||
fn add_window(&self, updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
|
||||
let mut unlocked = self.updater.lock().unwrap();
|
||||
if unlocked.is_some() {
|
||||
return Err(Error::new("A window updater has already been registered with the frontend"));
|
||||
}
|
||||
*unlocked = Some(updater);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MiniFrontend {
|
||||
pub fn init_frontend() -> MiniFrontend {
|
||||
MiniFrontend {
|
||||
buffer: Mutex::new(vec![0; WIDTH * HEIGHT]),
|
||||
updater: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn start(&self) {
|
||||
pub fn start(&self, mut system: System) {
|
||||
let mut window = minifb::Window::new(
|
||||
"Test - ESC to exit",
|
||||
WIDTH,
|
||||
HEIGHT,
|
||||
minifb::WindowOptions::default(),
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("{}", e);
|
||||
});
|
||||
|
||||
// Limit to max ~60 fps update rate
|
||||
window.limit_update_rate(Some(Duration::from_micros(16600)));
|
||||
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
system.run_for(16_600_000);
|
||||
|
||||
match &mut *self.updater.lock().unwrap() {
|
||||
Some(updater) => {
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
updater.update_frame(WIDTH as u32, HEIGHT as u32, &mut buffer);
|
||||
window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap();
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ pub enum LogLevel {
|
||||
Debug,
|
||||
}
|
||||
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Debug;
|
||||
static mut LOG_LEVEL: LogLevel = LogLevel::Warning;
|
||||
|
||||
pub fn log_level() -> LogLevel {
|
||||
unsafe { LOG_LEVEL }
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::host::traits::WindowUpdater;
|
||||
use crate::host::traits::{WindowUpdater, BlitableSurface};
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -11,6 +11,27 @@ pub struct Frame {
|
||||
pub bitmap: Vec<u32>,
|
||||
}
|
||||
|
||||
impl BlitableSurface for Frame {
|
||||
fn set_size(&mut self, width: u32, height: u32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
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) {
|
||||
for y in pos_y..(pos_y + height) {
|
||||
for x in pos_x..(pos_x + width) {
|
||||
match bitmap.next().unwrap() {
|
||||
0 => { },
|
||||
value if x < self.width && y < self.height => { self.bitmap[(x + (y * self.width)) as usize] = value; },
|
||||
_ => { },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct FrameSwapper {
|
||||
pub current: Frame,
|
||||
pub previous: Frame,
|
||||
@ -36,16 +57,11 @@ impl FrameSwapper {
|
||||
impl WindowUpdater for FrameSwapper {
|
||||
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
|
||||
std::mem::swap(&mut self.current, &mut self.previous);
|
||||
if self.current.width != width || self.current.height != height {
|
||||
self.current.width = width;
|
||||
self.current.height = height;
|
||||
self.current.bitmap.resize((width * height) as usize, 0);
|
||||
self.previous = self.current.clone();
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..(width as usize * height as usize) {
|
||||
bitmap[i] = self.current.bitmap[i];
|
||||
for y in 0..self.current.height {
|
||||
for x in 0..self.current.width {
|
||||
bitmap[(x + (y * width)) as usize] = self.current.bitmap[(x + (y * self.current.width)) as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,3 +19,8 @@ pub trait WindowUpdater: Send {
|
||||
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
|
||||
}
|
||||
|
||||
pub trait BlitableSurface {
|
||||
fn set_size(&mut self, width: u32, height: u32);
|
||||
fn blit<B: Iterator<Item=u32>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
|
||||
}
|
||||
|
||||
|
52
src/machines/genesis.rs
Normal file
52
src/machines/genesis.rs
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::system::System;
|
||||
use crate::memory::{MemoryBlock, BusPort};
|
||||
use crate::devices::{wrap_transmutable, Debuggable};
|
||||
|
||||
use crate::cpus::m68k::{M68k, M68kType};
|
||||
use crate::peripherals::genesis;
|
||||
|
||||
use crate::host::traits::{Host, WindowUpdater};
|
||||
|
||||
|
||||
use std::sync::Arc;
|
||||
pub fn build_genesis<H: Host>(host: &H) -> Result<System, Error> {
|
||||
let mut system = System::new();
|
||||
|
||||
let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 01) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 00) [!].bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Home Alone (beta).bin").unwrap();
|
||||
//let rom = MemoryBlock::load("binaries/genesis/Teenage Mutant Ninja Turtles - The Hyperstone Heist (U) [!].bin").unwrap();
|
||||
system.add_addressable_device(0x00000000, wrap_transmutable(rom)).unwrap();
|
||||
|
||||
let ram = MemoryBlock::new(vec![0; 0x00010000]);
|
||||
system.add_addressable_device(0x00FF0000, wrap_transmutable(ram)).unwrap();
|
||||
|
||||
|
||||
|
||||
let coproc_shared_mem = MemoryBlock::new(vec![0; 0x00010000]);
|
||||
system.add_addressable_device(0x00A00000, wrap_transmutable(coproc_shared_mem)).unwrap();
|
||||
|
||||
|
||||
let controllers = genesis::controllers::GenesisController::new();
|
||||
system.add_addressable_device(0x00a10000, wrap_transmutable(controllers)).unwrap();
|
||||
|
||||
let coproc = genesis::coproc_memory::CoprocessorMemory::new();
|
||||
system.add_addressable_device(0x00a11000, wrap_transmutable(coproc)).unwrap();
|
||||
|
||||
let vdp = genesis::ym7101::Ym7101::new(host);
|
||||
system.add_addressable_device(0x00c00000, wrap_transmutable(vdp)).unwrap();
|
||||
|
||||
|
||||
let mut cpu = M68k::new(M68kType::MC68000, 7_670_454, BusPort::new(0, 24, 16, system.bus.clone()));
|
||||
|
||||
//cpu.enable_tracing();
|
||||
//cpu.add_breakpoint(0x1dd0); // Sonic: some kind of palette fading function
|
||||
//cpu.decoder.dump_disassembly(&mut system, 0x206, 0x2000);
|
||||
|
||||
system.add_interruptable_device(wrap_transmutable(cpu)).unwrap();
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
pub mod computie;
|
||||
pub mod genesis;
|
||||
|
||||
|
146
src/peripherals/genesis/controllers.rs
Normal file
146
src/peripherals/genesis/controllers.rs
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::devices::{Address, Addressable, Transmutable, MAX_READ};
|
||||
|
||||
|
||||
const REG_VERSION: Address = 0x01;
|
||||
const REG_DATA1: Address = 0x03;
|
||||
const REG_DATA2: Address = 0x05;
|
||||
const REG_DATA3: Address = 0x07;
|
||||
const REG_CTRL1: Address = 0x09;
|
||||
const REG_CTRL2: Address = 0x0B;
|
||||
const REG_CTRL3: Address = 0x0D;
|
||||
const REG_S_CTRL1: Address = 0x13;
|
||||
const REG_S_CTRL2: Address = 0x19;
|
||||
const REG_S_CTRL3: Address = 0x1F;
|
||||
|
||||
|
||||
const DEV_NAME: &'static str = "genesis_controller";
|
||||
|
||||
pub struct GenesisControllerPort {
|
||||
/// Data contains bits:
|
||||
/// 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
/// X | Y | Z | MODE | START | A | B | C | RIGHT | LEFT | DOWN | UP
|
||||
pub data: u16,
|
||||
|
||||
pub ctrl: u8,
|
||||
pub th_count: u8,
|
||||
pub next_read: u8,
|
||||
|
||||
pub s_ctrl: u8,
|
||||
}
|
||||
|
||||
impl GenesisControllerPort {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: 0,
|
||||
ctrl: 0,
|
||||
th_count: 0,
|
||||
next_read: 0,
|
||||
s_ctrl: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_data(&mut self, data: u8) {
|
||||
let prev_th = self.next_read & 0x40;
|
||||
self.next_read = data & self.ctrl;
|
||||
|
||||
if (self.next_read ^ prev_th) != 0 {
|
||||
// TH bit was toggled
|
||||
self.th_count += 1;
|
||||
self.next_read = match self.th_count {
|
||||
0 => self.next_read | ((self.data & 0x003F) as u8),
|
||||
1 => self.next_read | (((self.data & 0x00C0) >> 2) as u8) | ((self.data & 0x0003) as u8),
|
||||
2 => self.next_read | ((self.data & 0x003F) as u8),
|
||||
3 => self.next_read | (((self.data & 0x00C0) >> 2) as u8),
|
||||
4 => self.next_read | ((self.data & 0x0030) as u8) | (((self.data & 0x0F00) >> 8) as u8),
|
||||
5 => self.next_read | (((self.data & 0x00C0) >> 2) as u8) | 0x0F,
|
||||
6 => self.next_read | ((self.data & 0x003F) as u8),
|
||||
7 => {
|
||||
self.th_count = 0;
|
||||
self.next_read | (((self.data & 0x00C0) >> 2) as u8) | ((self.data & 0x0003) as u8)
|
||||
},
|
||||
_ => {
|
||||
self.th_count = 0;
|
||||
0
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub struct GenesisController {
|
||||
pub port_1: GenesisControllerPort,
|
||||
pub port_2: GenesisControllerPort,
|
||||
pub expansion: GenesisControllerPort,
|
||||
}
|
||||
|
||||
impl GenesisController {
|
||||
pub fn new() -> Self {
|
||||
GenesisController {
|
||||
port_1: GenesisControllerPort::new(),
|
||||
port_2: GenesisControllerPort::new(),
|
||||
expansion: GenesisControllerPort::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressable for GenesisController {
|
||||
fn len(&self) -> usize {
|
||||
0x30
|
||||
}
|
||||
|
||||
fn read(&mut self, mut addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||
// If the address is even, only the second byte (odd byte) will be meaningful
|
||||
let mut i = 0;
|
||||
if (addr % 2) == 0 {
|
||||
addr += 1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match addr {
|
||||
REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion
|
||||
REG_DATA1 => { data[i] = self.port_1.next_read; },
|
||||
REG_DATA2 => { data[i] = self.port_2.next_read; },
|
||||
REG_DATA3 => { data[i] = self.expansion.next_read; },
|
||||
REG_CTRL1 => { data[i] = self.port_1.ctrl; },
|
||||
REG_CTRL2 => { data[i] = self.port_2.ctrl; },
|
||||
REG_CTRL3 => { data[i] = self.expansion.ctrl; },
|
||||
REG_S_CTRL1 => { data[i] = self.port_1.s_ctrl | 0x02; },
|
||||
REG_S_CTRL2 => { data[i] = self.port_2.s_ctrl | 0x02; },
|
||||
REG_S_CTRL3 => { data[i] = self.expansion.s_ctrl | 0x02; },
|
||||
_ => { warning!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); },
|
||||
}
|
||||
info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||
match addr {
|
||||
REG_DATA1 => { self.port_1.set_data(data[0]); }
|
||||
REG_DATA2 => { self.port_2.set_data(data[0]); },
|
||||
REG_DATA3 => { self.expansion.set_data(data[0]); },
|
||||
REG_CTRL1 => { self.port_1.ctrl = data[0]; },
|
||||
REG_CTRL2 => { self.port_2.ctrl = data[0]; },
|
||||
REG_CTRL3 => { self.expansion.ctrl = data[0]; },
|
||||
REG_S_CTRL1 => { self.port_1.s_ctrl = data[0] & 0xF8; },
|
||||
REG_S_CTRL2 => { self.port_2.s_ctrl = data[0] & 0xF8; },
|
||||
REG_S_CTRL3 => { self.expansion.s_ctrl = data[0] & 0xF8; },
|
||||
_ => { warning!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); },
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO make a step function to reset the TH count after 1.5ms
|
||||
|
||||
impl Transmutable for GenesisController {
|
||||
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
69
src/peripherals/genesis/coproc_memory.rs
Normal file
69
src/peripherals/genesis/coproc_memory.rs
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::devices::{Address, Addressable, Transmutable, MAX_READ};
|
||||
|
||||
|
||||
const DEV_NAME: &'static str = "coprocessor";
|
||||
|
||||
pub struct CoprocessorMemory {
|
||||
pub bus_request: bool,
|
||||
pub reset: bool,
|
||||
}
|
||||
|
||||
|
||||
impl CoprocessorMemory {
|
||||
pub fn new() -> Self {
|
||||
CoprocessorMemory {
|
||||
bus_request: false,
|
||||
reset: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Addressable for CoprocessorMemory {
|
||||
fn len(&self) -> usize {
|
||||
0x4000
|
||||
}
|
||||
|
||||
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||
match addr {
|
||||
0x100 => {
|
||||
data[0] = if self.bus_request && self.reset { 0x01 } else { 0x00 };
|
||||
},
|
||||
_ => { warning!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); },
|
||||
}
|
||||
info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||
info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||
match addr {
|
||||
0x000 => { /* ROM vs DRAM mode */ },
|
||||
0x100 => {
|
||||
if data[0] != 0 {
|
||||
self.bus_request = true;
|
||||
} else {
|
||||
self.bus_request = false;
|
||||
}
|
||||
},
|
||||
0x200 => {
|
||||
if data[0] != 0 {
|
||||
self.reset = false;
|
||||
} else {
|
||||
self.reset = true;
|
||||
}
|
||||
},
|
||||
_ => { warning!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); },
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transmutable for CoprocessorMemory {
|
||||
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
5
src/peripherals/genesis/mod.rs
Normal file
5
src/peripherals/genesis/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
pub mod ym7101;
|
||||
pub mod controllers;
|
||||
pub mod coproc_memory;
|
||||
|
@ -122,7 +122,7 @@ impl Ym7101State {
|
||||
fn set_register(&mut self, data: u16) {
|
||||
let reg = (data & 0x1F00) >> 8;
|
||||
self.regs[reg as usize] = (data & 0x00FF) as u8;
|
||||
debug!("{}: register {:x} set to {:x}", DEV_NAME, reg, self.regs[reg as usize]);
|
||||
info!("{}: register {:x} set to {:x}", DEV_NAME, reg, self.regs[reg as usize]);
|
||||
}
|
||||
|
||||
pub fn set_dma_mode(&mut self, mode: DmaType) {
|
||||
@ -154,7 +154,7 @@ impl Ym7101State {
|
||||
(((self.regs[REG_DMA_COUNTER_HIGH] as u32) << 8) | (self.regs[REG_DMA_COUNTER_LOW] as u32)) as i32
|
||||
}
|
||||
|
||||
pub fn setup_dma_transfer(&mut self, upper: u16, lower: u16) {
|
||||
pub fn setup_transfer(&mut self, upper: u16, lower: u16) {
|
||||
self.transfer_upper = None;
|
||||
self.transfer_type = ((((upper & 0xC000) >> 14) | ((lower & 0x00F0) >> 2))) as u8;
|
||||
self.transfer_addr = ((upper & 0x3FFF) | ((lower & 0x0003) << 14)) as u32;
|
||||
@ -163,7 +163,7 @@ impl Ym7101State {
|
||||
4 => TargetType::Vsram,
|
||||
_ => TargetType::Cram,
|
||||
};
|
||||
debug!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_addr);
|
||||
info!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_addr);
|
||||
if (self.transfer_type & 0x20) != 0 {
|
||||
if (self.transfer_type & 0x10) != 0 {
|
||||
self.set_dma_mode(DmaType::Copy);
|
||||
@ -204,14 +204,19 @@ impl Ym7101State {
|
||||
}
|
||||
|
||||
pub fn get_palette_colour(&self, palette: u8, colour: u8) -> u32 {
|
||||
let rgb = read_beu16(&self.cram[((palette * 16) + colour) as usize..]);
|
||||
if colour == 0 {
|
||||
return 0;
|
||||
}
|
||||
let rgb = read_beu16(&self.cram[(((palette * 16) + colour) * 2) as usize..]);
|
||||
(((rgb & 0xF00) as u32) >> 4) | (((rgb & 0x0F0) as u32) << 8) | (((rgb & 0x00F) as u32) << 20)
|
||||
}
|
||||
|
||||
pub fn get_pattern_iter<'a>(&'a self, pattern_name: u16) -> PatternIterator<'a> {
|
||||
let pattern_addr = (pattern_name & 0x07FF) << 5;
|
||||
let pattern_palette = ((pattern_name & 0x6000) >> 13) as u8;
|
||||
PatternIterator::new(&self, pattern_addr as u32, pattern_palette)
|
||||
let h_rev = (pattern_name & 0x0800) != 0;
|
||||
let v_rev = (pattern_name & 0x1000) != 0;
|
||||
PatternIterator::new(&self, pattern_addr as u32, pattern_palette, h_rev, v_rev)
|
||||
}
|
||||
|
||||
pub fn get_scroll_size(&self) -> (u16, u16) {
|
||||
@ -243,10 +248,10 @@ impl Ym7101State {
|
||||
frame.bitmap[i] = bg_colour;
|
||||
}
|
||||
|
||||
//self.draw_cell_table(frame, self.get_vram_scroll_b_addr(), coords, scroll_size);
|
||||
self.draw_cell_table(frame, self.get_vram_scroll_b_addr(), coords, scroll_size);
|
||||
self.draw_cell_table(frame, self.get_vram_scroll_a_addr(), coords, scroll_size);
|
||||
//self.draw_cell_table(frame, self.get_vram_window_addr(), coords, scroll_size);
|
||||
//self.draw_sprites(frame, (self.regs[REG_SPRITES_ADDR] as u32) << 9, coords, scroll_size);
|
||||
self.draw_cell_table(frame, self.get_vram_window_addr(), coords, scroll_size);
|
||||
self.draw_sprites(frame, (self.regs[REG_SPRITES_ADDR] as u32) << 9, coords, scroll_size);
|
||||
}
|
||||
|
||||
pub fn draw_cell_table(&mut self, frame: &mut Frame, cell_table: u32, coords: ((u16, u16), (u16, u16)), scroll_size: (u16, u16)) {
|
||||
@ -289,16 +294,24 @@ fn scroll_size(size: u8) -> u16 {
|
||||
pub struct PatternIterator<'a> {
|
||||
state: &'a Ym7101State,
|
||||
palette: u8,
|
||||
next: usize,
|
||||
base: usize,
|
||||
h_rev: bool,
|
||||
v_rev: bool,
|
||||
line: i8,
|
||||
col: i8,
|
||||
second: bool,
|
||||
}
|
||||
|
||||
impl<'a> PatternIterator<'a> {
|
||||
pub fn new(state: &'a Ym7101State, start: u32, palette: u8) -> Self {
|
||||
pub fn new(state: &'a Ym7101State, start: u32, palette: u8, h_rev: bool, v_rev: bool) -> Self {
|
||||
Self {
|
||||
state,
|
||||
palette,
|
||||
next: start as usize,
|
||||
base: start as usize,
|
||||
h_rev,
|
||||
v_rev,
|
||||
line: 0,
|
||||
col: 0,
|
||||
second: false,
|
||||
}
|
||||
}
|
||||
@ -308,19 +321,25 @@ impl<'a> Iterator for PatternIterator<'a> {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
//if self.next >= 32 {
|
||||
// None
|
||||
//}
|
||||
if !self.second {
|
||||
let value = self.state.get_palette_colour(self.palette, self.state.vram[self.next] >> 4);
|
||||
self.second = true;
|
||||
Some(value)
|
||||
let offset = self.base + (if !self.v_rev { self.line } else { 7 - self.line }) as usize * 4 + (if !self.h_rev { self.col } else { 3 - self.col }) as usize;
|
||||
let value = if (!self.h_rev && !self.second) || (self.h_rev && self.second) {
|
||||
self.state.get_palette_colour(self.palette, self.state.vram[offset] >> 4)
|
||||
} else {
|
||||
self.state.get_palette_colour(self.palette, self.state.vram[offset] & 0x0f)
|
||||
};
|
||||
|
||||
if !self.second {
|
||||
self.second = true;
|
||||
} else {
|
||||
let value = self.state.get_palette_colour(self.palette, self.state.vram[self.next] & 0x0f);
|
||||
self.next += 1;
|
||||
self.second = false;
|
||||
Some(value)
|
||||
self.col += 1;
|
||||
if self.col >= 4 {
|
||||
self.col = 0;
|
||||
self.line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,18 +388,26 @@ impl Steppable for Ym7101 {
|
||||
}
|
||||
|
||||
self.state.h_clock += diff;
|
||||
if (self.state.status & STATUS_IN_HBLANK) == 0 && self.state.v_clock > 58_820 {
|
||||
self.state.status |= STATUS_IN_HBLANK;
|
||||
}
|
||||
if self.state.h_clock > 63_500 {
|
||||
self.state.status &= !STATUS_IN_HBLANK;
|
||||
self.state.h_clock = 0;
|
||||
self.state.h_scanlines = self.state.h_scanlines.wrapping_add(1);
|
||||
if self.state.hsync_int_enabled() && self.state.h_scanlines >= self.state.regs[REG_H_INTERRUPT] {
|
||||
self.state.h_scanlines = 0;
|
||||
self.state.h_scanlines = self.state.h_scanlines.wrapping_sub(1);
|
||||
if self.state.hsync_int_enabled() && self.state.h_scanlines == 0 {
|
||||
self.state.h_scanlines = self.state.regs[REG_H_INTERRUPT];
|
||||
system.get_interrupt_controller().set(true, 4, 28)?;
|
||||
self.state.reset_int = true;
|
||||
}
|
||||
}
|
||||
|
||||
self.state.v_clock += diff;
|
||||
if (self.state.status & STATUS_IN_VBLANK) == 0 && self.state.v_clock > 14_218_000 {
|
||||
self.state.status |= STATUS_IN_VBLANK;
|
||||
}
|
||||
if self.state.v_clock > 16_630_000 {
|
||||
self.state.status &= !STATUS_IN_VBLANK;
|
||||
self.state.v_clock = 0;
|
||||
if self.state.vsync_int_enabled() {
|
||||
system.get_interrupt_controller().set(true, 6, 30)?;
|
||||
@ -390,6 +417,10 @@ impl Steppable for Ym7101 {
|
||||
let mut swapper = self.swapper.lock().unwrap();
|
||||
self.state.draw_frame(&mut swapper.current);
|
||||
|
||||
//let mut swapper = self.swapper.lock().unwrap();
|
||||
//let iter = PatternIterator::new(&self.state, 0x260, 0, true, true);
|
||||
//swapper.current.blit(0, 0, iter, 8, 8);
|
||||
|
||||
/*
|
||||
// Print Palette
|
||||
for i in 0..16 {
|
||||
@ -420,10 +451,18 @@ impl Steppable for Ym7101 {
|
||||
let mut src_addr = self.state.get_dma_src_addr();
|
||||
let mut count = self.state.get_dma_count();
|
||||
|
||||
debug!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.state.transfer_type, src_addr, self.state.transfer_target, self.state.transfer_addr, count);
|
||||
info!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.state.transfer_type, src_addr, self.state.transfer_target, self.state.transfer_addr, count);
|
||||
let mut bus = system.get_bus();
|
||||
|
||||
// TODO temporary for debugging, will break at the first cram transfer after the display is on
|
||||
//if (self.state.regs[REG_MODE_SET_2] & 0x40) != 0 && self.state.transfer_target == TargetType::Cram {
|
||||
// system.get_interrupt_controller().target.as_ref().map(|cpu| cpu.borrow_mut().as_debuggable().unwrap().enable_debugging());
|
||||
//}
|
||||
|
||||
//bus.dump_memory(src_addr as Address, count as Address);
|
||||
while count > 0 {
|
||||
let data = bus.read(src_addr as Address, 2)?;
|
||||
let mut data = [0; 2];
|
||||
bus.read(src_addr as Address, &mut data)?;
|
||||
|
||||
{
|
||||
let addr = self.state.transfer_addr;
|
||||
@ -441,7 +480,7 @@ impl Steppable for Ym7101 {
|
||||
let mut src_addr = self.state.get_dma_src_addr();
|
||||
let mut count = self.state.get_dma_count();
|
||||
|
||||
debug!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, src_addr, self.state.transfer_addr, count);
|
||||
info!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, src_addr, self.state.transfer_addr, count);
|
||||
while count > 0 {
|
||||
self.state.vram[self.state.transfer_addr as usize] = self.state.vram[src_addr as usize];
|
||||
self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32;
|
||||
@ -452,7 +491,7 @@ impl Steppable for Ym7101 {
|
||||
DmaType::Fill => {
|
||||
let mut count = self.state.get_dma_count();
|
||||
|
||||
debug!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.state.transfer_addr, count, self.state.transfer_fill);
|
||||
info!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.state.transfer_addr, count, self.state.transfer_fill);
|
||||
while count > 0 {
|
||||
self.state.vram[self.state.transfer_addr as usize] = self.state.transfer_fill as u8;
|
||||
self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32;
|
||||
@ -474,21 +513,19 @@ impl Addressable for Ym7101 {
|
||||
0x20
|
||||
}
|
||||
|
||||
fn read(&mut self, addr: Address, count: usize) -> Result<[u8; MAX_READ], Error> {
|
||||
let mut data = [0; MAX_READ];
|
||||
|
||||
fn read(&mut self, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||
match addr {
|
||||
// Read from Data Port
|
||||
0x00 => {
|
||||
0x00 | 0x02 => {
|
||||
{
|
||||
let addr = self.state.transfer_addr;
|
||||
let target = self.state.get_transfer_target_mut();
|
||||
for i in 0..count {
|
||||
for i in 0..data.len() {
|
||||
data[i] = target[addr as usize + i];
|
||||
}
|
||||
}
|
||||
self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32;
|
||||
debug!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, count, self.state.transfer_target, addr, data[0], data[1]);
|
||||
info!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, data.len(), self.state.transfer_target, addr, data[0], data[1]);
|
||||
},
|
||||
|
||||
// Read from Control Port
|
||||
@ -500,20 +537,20 @@ impl Addressable for Ym7101 {
|
||||
|
||||
_ => { println!("{}: !!! unhandled read from {:x}", DEV_NAME, addr); },
|
||||
}
|
||||
Ok(data)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||
match addr {
|
||||
// Write to Data Port
|
||||
0x00 => {
|
||||
0x00 | 0x02 => {
|
||||
if (self.state.transfer_type & 0x30) == 0x20 {
|
||||
self.state.transfer_upper = None;
|
||||
// TODO this caused problems, might not correct
|
||||
//self.state.transfer_fill = read_beu16(data);
|
||||
self.state.transfer_fill = (data[0] as u16) << 8 | (data[1] as u16);
|
||||
self.state.transfer_fill = read_beu16(data);
|
||||
self.state.set_dma_mode(DmaType::Fill);
|
||||
} else {
|
||||
info!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.state.transfer_target, self.state.transfer_addr, data);
|
||||
|
||||
{
|
||||
let addr = self.state.transfer_addr as usize;
|
||||
let target = self.state.get_transfer_target_mut();
|
||||
@ -522,13 +559,13 @@ impl Addressable for Ym7101 {
|
||||
}
|
||||
}
|
||||
self.state.transfer_addr += self.state.regs[REG_AUTO_INCREMENT] as u32;
|
||||
debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.state.transfer_target, addr, data);
|
||||
}
|
||||
},
|
||||
|
||||
// Write to Control Port
|
||||
0x04 | 0x06 => {
|
||||
//debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data);
|
||||
debug!("{}: write {} bytes to port {:x} with data {:?}", DEV_NAME, data.len(), addr, data);
|
||||
|
||||
let value = read_beu16(data);
|
||||
if (value & 0xC000) == 0x8000 {
|
||||
self.state.set_register(value);
|
||||
@ -542,8 +579,8 @@ impl Addressable for Ym7101 {
|
||||
} else {
|
||||
match (data.len(), self.state.transfer_upper) {
|
||||
(2, None) => { self.state.transfer_upper = Some(value) },
|
||||
(2, Some(upper)) => self.state.setup_dma_transfer(upper, read_beu16(data)),
|
||||
(4, None) => self.state.setup_dma_transfer(value, read_beu16(&data[2..])),
|
||||
(2, Some(upper)) => self.state.setup_transfer(upper, read_beu16(data)),
|
||||
(4, None) => self.state.setup_transfer(value, read_beu16(&data[2..])),
|
||||
_ => { error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data); },
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
pub mod ata;
|
||||
pub mod mc68681;
|
||||
pub mod genesis;
|
||||
|
||||
|
4
todo.txt
4
todo.txt
@ -1,4 +1,7 @@
|
||||
|
||||
* modify the interrupt handling to make the interrupted device acknowledge the interrupt, probably via the interrupt controller somehow. It might need to be async
|
||||
such that the cpu tells the int_controller it's acknowledged, and then the interrupting device can check the interrupt controller if it needs to see the ack
|
||||
|
||||
* each device that can make a bus request should have a BusPort which is used to access the bus. Not sure how it'll be created or passed to the device, since
|
||||
the offset should be set by the builder or system, and the mask and data size should be sent by the CPU (although I suppose some systems could hook it up differently)
|
||||
* what about even vs odd accesses? If you access a byte, should the bus port possible turn it into a word access, and return only the byte portion?
|
||||
@ -9,6 +12,7 @@
|
||||
|
||||
* make devices nameable, using a hashmap to store them
|
||||
* can you eventually make the system connections all configurable via a config file?
|
||||
* you could modify read/write to return the number of bytes read or written for dynamic bus sizing used by the MC68020+
|
||||
|
||||
|
||||
* make tests for each instruction
|
||||
|
Loading…
x
Reference in New Issue
Block a user