fox32+fox32rom: Remove the concept of "fast" and "shared" memory
Instead of having two banks of RAM, just have one bank. This requires the use of some cursed code, but it's fine :P Co-authored-by: Lua <lua@foxgirl.dev>
This commit is contained in:
parent
cb2712cbf4
commit
342c3e6061
14
src/bus.rs
14
src/bus.rs
|
@ -1,6 +1,6 @@
|
||||||
// bus.rs
|
// bus.rs
|
||||||
|
|
||||||
use crate::{DiskController, Memory, Mouse};
|
use crate::{DiskController, Memory, Mouse, Overlay};
|
||||||
|
|
||||||
use std::io::{stdout, Write};
|
use std::io::{stdout, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
@ -9,13 +9,14 @@ pub struct Bus {
|
||||||
pub disk_controller: DiskController,
|
pub disk_controller: DiskController,
|
||||||
pub memory: Memory,
|
pub memory: Memory,
|
||||||
pub mouse: Arc<Mutex<Mouse>>,
|
pub mouse: Arc<Mutex<Mouse>>,
|
||||||
|
pub overlays: Arc<Mutex<Vec<Overlay>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
pub fn read_io(&mut self, port: u32) -> u32 {
|
pub fn read_io(&mut self, port: u32) -> u32 {
|
||||||
match port {
|
match port {
|
||||||
0x80000000..=0x8000031F => { // overlay port
|
0x80000000..=0x8000031F => { // overlay port
|
||||||
let overlay_lock = self.memory.overlays.lock().unwrap();
|
let overlay_lock = self.overlays.lock().unwrap();
|
||||||
let overlay_number = (port & 0x000000FF) as usize;
|
let overlay_number = (port & 0x000000FF) as usize;
|
||||||
let setting = (port & 0x0000FF00) >> 8;
|
let setting = (port & 0x0000FF00) >> 8;
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ impl Bus {
|
||||||
}
|
}
|
||||||
0x02 => {
|
0x02 => {
|
||||||
// we're reading the framebuffer pointer of this overlay
|
// we're reading the framebuffer pointer of this overlay
|
||||||
overlay_lock[overlay_number].framebuffer_pointer + 0x80000000
|
overlay_lock[overlay_number].framebuffer_pointer
|
||||||
}
|
}
|
||||||
0x03 => {
|
0x03 => {
|
||||||
// we're reading the enable status of this overlay
|
// we're reading the enable status of this overlay
|
||||||
|
@ -106,7 +107,7 @@ impl Bus {
|
||||||
stdout().flush().expect("could not flush stdout");
|
stdout().flush().expect("could not flush stdout");
|
||||||
}
|
}
|
||||||
0x80000000..=0x8000031F => { // overlay port
|
0x80000000..=0x8000031F => { // overlay port
|
||||||
let mut overlay_lock = self.memory.overlays.lock().unwrap();
|
let mut overlay_lock = self.overlays.lock().unwrap();
|
||||||
let overlay_number = (port & 0x000000FF) as usize;
|
let overlay_number = (port & 0x000000FF) as usize;
|
||||||
let setting = (port & 0x0000FF00) >> 8;
|
let setting = (port & 0x0000FF00) >> 8;
|
||||||
|
|
||||||
|
@ -127,10 +128,7 @@ impl Bus {
|
||||||
}
|
}
|
||||||
0x02 => {
|
0x02 => {
|
||||||
// we're setting the framebuffer pointer of this overlay
|
// we're setting the framebuffer pointer of this overlay
|
||||||
if word < 0x80000000 {
|
overlay_lock[overlay_number].framebuffer_pointer = word;
|
||||||
panic!("overlay framebuffer must be within shared memory");
|
|
||||||
}
|
|
||||||
overlay_lock[overlay_number].framebuffer_pointer = word - 0x80000000;
|
|
||||||
}
|
}
|
||||||
0x03 => {
|
0x03 => {
|
||||||
// we're setting the enable status of this overlay
|
// we're setting the enable status of this overlay
|
||||||
|
|
|
@ -24,10 +24,7 @@ impl std::convert::From<u8> for Flag {
|
||||||
fn from(byte: u8) -> Self {
|
fn from(byte: u8) -> Self {
|
||||||
let carry = ((byte >> 1) & 1) != 0;
|
let carry = ((byte >> 1) & 1) != 0;
|
||||||
let zero = ((byte >> 0) & 1) != 0;
|
let zero = ((byte >> 0) & 1) != 0;
|
||||||
Flag {
|
Flag { carry, zero }
|
||||||
carry,
|
|
||||||
zero,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
53
src/main.rs
53
src/main.rs
|
@ -8,7 +8,7 @@ pub mod mouse;
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use cpu::{Cpu, Interrupt};
|
use cpu::{Cpu, Interrupt};
|
||||||
use disk::DiskController;
|
use disk::DiskController;
|
||||||
use memory::Memory;
|
use memory::{MEMORY_RAM_START, MEMORY_ROM_START, Memory, MemoryRam};
|
||||||
use mouse::Mouse;
|
use mouse::Mouse;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -74,37 +74,25 @@ fn main() {
|
||||||
let mut display = Display::new();
|
let mut display = Display::new();
|
||||||
let mouse = Arc::new(Mutex::new(Mouse::new()));
|
let mouse = Arc::new(Mutex::new(Mouse::new()));
|
||||||
|
|
||||||
// 32 MiB of shared memory
|
let memory = Memory::new(read_rom().as_slice());
|
||||||
let shared_memory = Arc::new(Mutex::new(vec![0u8; 0x02000000]));
|
let memory_cpu = memory.clone();
|
||||||
|
let memory_eventloop = memory.clone();
|
||||||
|
|
||||||
let mut cpu = {
|
let mut cpu = {
|
||||||
// 32 MiB of fast memory
|
let ram_size = memory_cpu.ram().len();
|
||||||
let cpu_fast_memory = vec![0; 0x02000000];
|
let ram_bottom_address = MEMORY_RAM_START;
|
||||||
let cpu_shared_memory = Arc::clone(&shared_memory);
|
let ram_top_address = ram_bottom_address + ram_size - 1;
|
||||||
let cpu_overlays = Arc::clone(&display.overlays);
|
println!("RAM: {:.2} MiB mapped at {:#010X}-{:#010X}", ram_size / 1048576, ram_bottom_address, ram_top_address);
|
||||||
let cpu_read_only_memory = read_rom();
|
|
||||||
|
|
||||||
let fast_size = cpu_fast_memory.len();
|
let rom_size = memory_cpu.rom().len();
|
||||||
let fast_bottom_address = 0x00000000;
|
let rom_bottom_address = MEMORY_ROM_START;
|
||||||
let fast_top_address = fast_bottom_address + fast_size - 1;
|
|
||||||
println!("Fast: {:.2} MiB mapped at {:#010X}-{:#010X}", fast_size / 1048576, fast_bottom_address, fast_top_address);
|
|
||||||
|
|
||||||
let shared_size = { cpu_shared_memory.lock().unwrap().len() };
|
|
||||||
let shared_bottom_address = 0x80000000;
|
|
||||||
let shared_top_address = shared_bottom_address + shared_size - 1;
|
|
||||||
println!("Shared: {:.2} MiB mapped at {:#010X}-{:#010X}", shared_size / 1048576, shared_bottom_address, shared_top_address);
|
|
||||||
|
|
||||||
let rom_size = cpu_read_only_memory.len();
|
|
||||||
let rom_bottom_address = 0xF0000000;
|
|
||||||
let rom_top_address = rom_bottom_address + rom_size - 1;
|
let rom_top_address = rom_bottom_address + rom_size - 1;
|
||||||
println!("ROM: {:.2} KiB mapped at {:#010X}-{:#010X}", rom_size / 1024, rom_bottom_address, rom_top_address);
|
println!("ROM: {:.2} KiB mapped at {:#010X}-{:#010X}", rom_size / 1024, rom_bottom_address, rom_top_address);
|
||||||
|
|
||||||
let memory = Memory::new(cpu_fast_memory, cpu_shared_memory, cpu_overlays, cpu_read_only_memory);
|
let bus_mouse = Arc::clone(&mouse);
|
||||||
|
let bus_overlays = Arc::clone(&display.overlays);
|
||||||
let cpu_mouse = Arc::clone(&mouse);
|
|
||||||
|
|
||||||
let disk_controller = DiskController::new();
|
let disk_controller = DiskController::new();
|
||||||
let bus = Bus { disk_controller, memory, mouse: cpu_mouse };
|
let bus = Bus { disk_controller, memory: memory_cpu, mouse: bus_mouse, overlays: bus_overlays };
|
||||||
Cpu::new(bus)
|
Cpu::new(bus)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,8 +155,6 @@ fn main() {
|
||||||
if let Event::MainEventsCleared = event {
|
if let Event::MainEventsCleared = event {
|
||||||
// update internal state and request a redraw
|
// update internal state and request a redraw
|
||||||
|
|
||||||
let mut shared_memory_lock = shared_memory.lock().expect("failed to lock the shared memory mutex");
|
|
||||||
//shared_memory_lock[0x01FFFFFF] = shared_memory_lock[0x01FFFFFF].overflowing_add(1).0; // increment vsync counter
|
|
||||||
match interrupt_sender.send(Interrupt::Request(0xFF)) { // vsync interrupt
|
match interrupt_sender.send(Interrupt::Request(0xFF)) { // vsync interrupt
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -176,8 +162,7 @@ fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
display.update(&mut *shared_memory_lock);
|
display.update(memory_eventloop.clone().ram());
|
||||||
drop(shared_memory_lock);
|
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
|
|
||||||
display.draw(pixels.get_frame());
|
display.draw(pixels.get_frame());
|
||||||
|
@ -229,16 +214,16 @@ impl Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, shared_memory: &mut [u8]) {
|
fn update(&mut self, ram: &MemoryRam) {
|
||||||
let overlay_lock = self.overlays.lock().unwrap();
|
let overlay_lock = self.overlays.lock().unwrap();
|
||||||
|
|
||||||
for i in 0..(HEIGHT*WIDTH*4) as usize {
|
for i in 0..(HEIGHT*WIDTH*4) as usize {
|
||||||
self.background[i] = shared_memory[i];
|
self.background[i] = ram[0x02000000 + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
for index in 0..=31 {
|
for index in 0..=31 {
|
||||||
if overlay_lock[index].enabled {
|
if overlay_lock[index].enabled {
|
||||||
blit_overlay(&mut self.background, &overlay_lock[index], shared_memory);
|
blit_overlay(&mut self.background, &overlay_lock[index], ram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,7 +242,7 @@ impl Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
// modified from https://github.com/parasyte/pixels/blob/main/examples/invaders/simple-invaders/src/sprites.rs
|
// modified from https://github.com/parasyte/pixels/blob/main/examples/invaders/simple-invaders/src/sprites.rs
|
||||||
fn blit_overlay(framebuffer: &mut [u8], overlay: &Overlay, shared_memory: &mut [u8]) {
|
fn blit_overlay(framebuffer: &mut [u8], overlay: &Overlay, ram: &[u8]) {
|
||||||
//assert!(overlay.x + overlay.width <= WIDTH);
|
//assert!(overlay.x + overlay.width <= WIDTH);
|
||||||
//assert!(overlay.y + overlay.height <= HEIGHT);
|
//assert!(overlay.y + overlay.height <= HEIGHT);
|
||||||
|
|
||||||
|
@ -277,7 +262,7 @@ fn blit_overlay(framebuffer: &mut [u8], overlay: &Overlay, shared_memory: &mut [
|
||||||
//println!("height: {}, difference: {}", height, difference);
|
//println!("height: {}, difference: {}", height, difference);
|
||||||
}
|
}
|
||||||
|
|
||||||
let overlay_framebuffer = &shared_memory[overlay.framebuffer_pointer as usize..(overlay.framebuffer_pointer+((width as u32)*(height as u32))) as usize];
|
let overlay_framebuffer = &ram[(overlay.framebuffer_pointer as usize)..((overlay.framebuffer_pointer+((width as u32)*(height as u32))) as usize)];
|
||||||
|
|
||||||
let mut overlay_framebuffer_index = 0;
|
let mut overlay_framebuffer_index = 0;
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
|
|
136
src/memory.rs
136
src/memory.rs
|
@ -1,97 +1,109 @@
|
||||||
// memory.rs
|
// memory.rs
|
||||||
|
|
||||||
use crate::{Overlay, warn};
|
use crate::warn;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::cell::UnsafeCell;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
pub struct Memory {
|
pub const MEMORY_RAM_SIZE: usize = 0x04000000; // 64 MiB
|
||||||
pub fast_memory: Vec<u8>,
|
pub const MEMORY_ROM_SIZE: usize = 0x00080000; // 512 KiB
|
||||||
fast_memory_size: usize,
|
|
||||||
pub shared_memory: Arc<Mutex<Vec<u8>>>,
|
|
||||||
shared_memory_size: usize,
|
|
||||||
|
|
||||||
pub overlays: Arc<Mutex<Vec<Overlay>>>,
|
pub const MEMORY_RAM_START: usize = 0x00000000;
|
||||||
|
pub const MEMORY_ROM_START: usize = 0xF0000000;
|
||||||
|
|
||||||
pub rom: Vec<u8>,
|
pub type MemoryRam = [u8; MEMORY_RAM_SIZE];
|
||||||
rom_size: usize,
|
pub type MemoryRom = [u8; MEMORY_ROM_SIZE];
|
||||||
|
|
||||||
|
struct MemoryInner {
|
||||||
|
ram: Box<MemoryRam>,
|
||||||
|
rom: Box<MemoryRom>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MemoryInner {
|
||||||
|
pub fn new(rom: &[u8]) -> Self {
|
||||||
|
let mut this = Self {
|
||||||
|
ram: Box::new([0u8; MEMORY_RAM_SIZE]),
|
||||||
|
rom: Box::new([0u8; MEMORY_ROM_SIZE]),
|
||||||
|
};
|
||||||
|
this.rom.as_mut_slice().write(rom).expect("failed to copy ROM to memory");
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Memory(Arc<UnsafeCell<MemoryInner>>);
|
||||||
|
|
||||||
|
// SAFETY: once MemoryInner is initialzed, there is no way to modify the Box
|
||||||
|
// pointers it contains and it does not matter if contents of the byte
|
||||||
|
// arrays are corrupted
|
||||||
|
unsafe impl Send for Memory {}
|
||||||
|
unsafe impl Sync for Memory {}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
pub fn new(fast_memory: Vec<u8>, shared_memory: Arc<Mutex<Vec<u8>>>, overlays: Arc<Mutex<Vec<Overlay>>>, rom: Vec<u8>) -> Self {
|
pub fn new(rom: &[u8]) -> Self {
|
||||||
// 3 extra bytes at the end to allow for reading the last byte of memory as an immediate pointer
|
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom))))
|
||||||
let shared_memory_size = { shared_memory.lock().unwrap().len() };
|
|
||||||
Memory {
|
|
||||||
fast_memory_size: fast_memory.len(),
|
|
||||||
fast_memory,
|
|
||||||
shared_memory_size,
|
|
||||||
shared_memory,
|
|
||||||
overlays,
|
|
||||||
rom_size: rom.len(),
|
|
||||||
rom,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn inner(&self) -> &mut MemoryInner {
|
||||||
|
unsafe { &mut *self.0.get() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ram(&self) -> &mut MemoryRam { &mut self.inner().ram }
|
||||||
|
pub fn rom(&self) -> &mut MemoryRom { &mut self.inner().rom }
|
||||||
|
|
||||||
pub fn read_8(&self, address: u32) -> u8 {
|
pub fn read_8(&self, address: u32) -> u8 {
|
||||||
let address = address as usize;
|
let address = address as usize;
|
||||||
|
|
||||||
let fast_bottom_address = 0x00000000;
|
let result = if address >= MEMORY_ROM_START && address < MEMORY_ROM_START + MEMORY_ROM_SIZE {
|
||||||
let fast_top_address = fast_bottom_address + self.fast_memory_size - 1;
|
self.rom().get(address - MEMORY_ROM_START)
|
||||||
|
|
||||||
let shared_bottom_address = 0x80000000;
|
|
||||||
let shared_top_address = shared_bottom_address + self.shared_memory_size - 1;
|
|
||||||
|
|
||||||
let rom_bottom_address = 0xF0000000;
|
|
||||||
let rom_top_address = rom_bottom_address + self.rom_size - 1;
|
|
||||||
|
|
||||||
if address >= fast_bottom_address && address <= fast_top_address {
|
|
||||||
self.fast_memory[address]
|
|
||||||
} else if address >= shared_bottom_address && address <= shared_top_address {
|
|
||||||
let shared_memory_lock = self.shared_memory.lock().unwrap();
|
|
||||||
shared_memory_lock[address - shared_bottom_address]
|
|
||||||
} else if address >= rom_bottom_address && address <= rom_top_address {
|
|
||||||
self.rom[address - rom_bottom_address]
|
|
||||||
} else {
|
} else {
|
||||||
warn(&format!("attempting to read unmapped memory address: {:#010X}", address));
|
self.ram().get(address - MEMORY_RAM_START)
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Some(value) => {
|
||||||
|
*value
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn(&format!("attempting to read from unmapped memory address: {:#010X}", address));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn read_16(&self, address: u32) -> u16 {
|
pub fn read_16(&self, address: u32) -> u16 {
|
||||||
(self.read_8(address + 1) as u16) << 8 |
|
(self.read_8(address) as u16) |
|
||||||
(self.read_8(address) as u16)
|
(self.read_8(address + 1) as u16) << 8
|
||||||
}
|
}
|
||||||
pub fn read_32(&self, address: u32) -> u32 {
|
pub fn read_32(&self, address: u32) -> u32 {
|
||||||
(self.read_8(address + 3) as u32) << 24 |
|
(self.read_8(address) as u32) |
|
||||||
(self.read_8(address + 2) as u32) << 16 |
|
|
||||||
(self.read_8(address + 1) as u32) << 8 |
|
(self.read_8(address + 1) as u32) << 8 |
|
||||||
(self.read_8(address) as u32)
|
(self.read_8(address + 2) as u32) << 16 |
|
||||||
|
(self.read_8(address + 3) as u32) << 24
|
||||||
}
|
}
|
||||||
pub fn write_8(&mut self, address: u32, byte: u8) {
|
|
||||||
|
pub fn write_8(&self, address: u32, byte: u8) {
|
||||||
let address = address as usize;
|
let address = address as usize;
|
||||||
|
|
||||||
let fast_bottom_address = 0x00000000;
|
if address >= MEMORY_ROM_START && address < MEMORY_ROM_START + MEMORY_ROM_SIZE {
|
||||||
let fast_top_address = fast_bottom_address + self.fast_memory_size - 1;
|
|
||||||
|
|
||||||
let shared_bottom_address = 0x80000000;
|
|
||||||
let shared_top_address = shared_bottom_address + self.shared_memory_size - 1;
|
|
||||||
|
|
||||||
let rom_bottom_address = 0xF0000000;
|
|
||||||
let rom_top_address = rom_bottom_address + self.rom_size - 1;
|
|
||||||
|
|
||||||
if address >= fast_bottom_address && address <= fast_top_address {
|
|
||||||
self.fast_memory[address] = byte;
|
|
||||||
} else if address >= shared_bottom_address && address <= shared_top_address {
|
|
||||||
let mut shared_memory_lock = self.shared_memory.lock().unwrap();
|
|
||||||
shared_memory_lock[address - shared_bottom_address] = byte;
|
|
||||||
} else if address >= rom_bottom_address && address <= rom_top_address {
|
|
||||||
warn(&format!("attempting to write to ROM address: {:#010X}", address));
|
warn(&format!("attempting to write to ROM address: {:#010X}", address));
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.ram().get_mut(address - MEMORY_RAM_START) {
|
||||||
|
Some(value) => {
|
||||||
|
*value = byte;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
warn(&format!("attempting to write to unmapped memory address: {:#010X}", address));
|
warn(&format!("attempting to write to unmapped memory address: {:#010X}", address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn write_16(&mut self, address: u32, half: u16) {
|
}
|
||||||
|
pub fn write_16(&self, address: u32, half: u16) {
|
||||||
self.write_8(address, (half & 0x00FF) as u8);
|
self.write_8(address, (half & 0x00FF) as u8);
|
||||||
self.write_8(address + 1, (half >> 8) as u8);
|
self.write_8(address + 1, (half >> 8) as u8);
|
||||||
}
|
}
|
||||||
pub fn write_32(&mut self, address: u32, word: u32) {
|
pub fn write_32(&self, address: u32, word: u32) {
|
||||||
self.write_8(address, (word & 0x000000FF) as u8);
|
self.write_8(address, (word & 0x000000FF) as u8);
|
||||||
self.write_8(address + 1, ((word & 0x0000FF00) >> 8) as u8);
|
self.write_8(address + 1, ((word & 0x0000FF00) >> 8) as u8);
|
||||||
self.write_8(address + 2, ((word & 0x00FF0000) >> 16) as u8);
|
self.write_8(address + 2, ((word & 0x00FF0000) >> 16) as u8);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user