looklook 0.7.0

Descriptive signal synthesiser.
// Copyright 2026 Gabriel Bjørnager Jensen.
//
// This file is part of LOOKLOOK.
//
// LOOKLOOK is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later ver-
// sion.
//
// LOOKLOOK is distributed in the hope that it will be useful, but WITHOUT ANY WAR-
// RANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License along
// with LOOKLOOK. If not, see <https://www.gnu.org/licenses/>.

//! The [`Mem`] type.

use crate::vm::Int;

use std::mem::take;
use std::slice;

/// A memory management unit
#[derive(Debug, Default)]
pub struct Mem {
	/// The base address in the host's memory.
	base: Box<[Int<4>]>,
}

 impl Mem {
	/// The default memory size.
	pub const DEFAULT_SIZE: usize = 256;

	/// The maximum capacity supported by both the
	/// MC68EC020 and the host.
	pub const MAX_CAPACITY: usize = 2_usize.checked_pow(24).unwrap_or(usize::MAX);

	/// Constructs an empty memory.
	#[inline]
	#[must_use]
	pub fn new() -> Self {
		Default::default()
	}

	/// Constructs a memory with a predefined size.
	#[must_use]
	pub fn with_capacity(mut size: usize) -> Self {
		size = size.div_ceil(size_of::<u32>());
		size = size.min(Self::MAX_CAPACITY);

		let base = vec![Default::default(); size].into();
		Self { base }
	}

	/// Reallocates memory.
	///
	/// The provided size is measured in count of bytes.
	pub fn allocate(&mut self, mut size: usize) {
		size = size.div_ceil(size_of::<Int<4>>());
		size = size.min(Self::MAX_CAPACITY);

		let mut base = take(&mut self.base).into_vec();

		base.clear();
		base.resize(size, Default::default());

		self.base = base.into();
	}

	/// Fills the entire (allocated) memory space with a
	/// specific byte value.
	#[inline]
	pub fn fill(&mut self, mut value: Int<4>) {
		value = value.to_be();
		self.base.fill(value);
	}

	/// Loads a byte from memory.
	///
	/// If the address is unimplemented or out of bounds, zero is read instead.
	#[inline]
	pub fn load_byte(&mut self, addr: Int<4>) -> Int<1> {
		if let Some(&value) = self.as_bytes().get(addr.as_usize()) {
			Int::from_u8(value)
		} else {
			Default::default()
		}
	}

	/// Loads a word from memory.
	///
	/// If any byte is unimplemented or out of bounds it is read as zero instead.
	#[inline]
	pub fn load_word(&mut self, addr: Int<4>) -> Int<2> {
		let bytes = match self.as_bytes().get(addr.as_usize()..) {
			Some(&[byte0, byte1, ..]) => {
				[byte0, byte1]
			}

			Some(&[byte0]) => {
				[byte0, 0]
			}

			_ => {
				[0, 0]
			}
		};

		Int::from_be_bytes(bytes)
	}

	/// Loads a long-word from memory.
	///
	/// If any byte is unimplemented or out of bounds it is read as zero instead.
	#[inline]
	pub fn load_long(&mut self, addr: Int<4>) -> Int<4> {
		let bytes = match self.as_bytes().get(addr.as_usize()..) {
			Some(&[byte0, byte1, byte2, byte3, ..]) => {
				[byte0, byte1, byte2, byte3]
			}

			Some(&[byte0, byte1, byte2]) => {
				[byte0, byte1, byte2, 0]
			}

			Some(&[byte0, byte1]) => {
				[byte0, byte1, 0, 0]
			}

			Some(&[byte0]) => {
				[byte0, 0, 0, 0]
			}

			_ => {
				[0, 0, 0, 0]
			}
		};

		Int::from_be_bytes(bytes)
	}

	/// Stores a [`u8`] value into memory.
	///
	/// If the address is unimplemented or out of bounds, the value is discarded.
	#[inline]
	pub fn store_u8(&mut self, addr: Int<4>, value: u8) {
		if let Some(slot) = self.as_bytes_mut().get_mut(addr.as_usize()) {
			*slot = value;
		}
	}

	/// Retrieves the allocated capacity, measured in
	/// count of bytes.
	#[inline]
	#[must_use]
	pub fn capacity(&self) -> usize {
		self.base.len()
	}

 	/// Borrows the memory as (`8`-bit) bytes.
	#[inline]
	#[must_use]
	pub fn as_bytes(&self) -> &[u8] {
		let len  = self.capacity();
		let data = self.as_ptr();

		unsafe { slice::from_raw_parts(data, len) }
	}

	/// Mutably borrows the memory as (`8`-bit) bytes.
	#[inline]
	#[must_use]
	pub fn as_bytes_mut(&mut self) -> &mut [u8] {
		let len  = self.capacity();
		let data = self.as_mut_ptr();

		unsafe { slice::from_raw_parts_mut(data, len) }
	}

	/// Retrieves a pointer to the base.
	#[inline]
	#[must_use]
	pub fn as_ptr(&self) -> *const u8 {
		self.base.as_ptr().cast::<u8>()
	}

	/// Retrieves a mutable pointer to the base.
	#[inline]
	#[must_use]
	pub fn as_mut_ptr(&mut self) -> *mut u8 {
		self.base.as_mut_ptr().cast::<u8>()
	}
}

impl Drop for Mem {
	#[inline(always)]
	fn drop(&mut self) {}
}