Added minifb frontend with very WIP genesis peripherals

This commit is contained in:
transistor 2021-10-27 21:01:18 -07:00
parent 892f93f053
commit 109ae4db55
16 changed files with 499 additions and 55 deletions

View File

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

Binary file not shown.

View 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"

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

View 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 => { }
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
pub mod computie;
pub mod genesis;

View 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)
}
}

View 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)
}
}

View File

@ -0,0 +1,5 @@
pub mod ym7101;
pub mod controllers;
pub mod coproc_memory;

View File

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

View File

@ -1,4 +1,5 @@
pub mod ata;
pub mod mc68681;
pub mod genesis;

View File

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