chore(interpreter): move memory access functions to computer

This is mainly to ease the usage of memory access functions, meaning
leaner access of memory with general resulution of memory accesses via
the MemoryIndex struct.
This commit is contained in:
2025-06-18 20:29:54 +09:00
parent 79dc560689
commit 4f3d864179
6 changed files with 299 additions and 274 deletions

View File

@@ -1,7 +1,6 @@
use crate::operands::{Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, Word};
use crate::operands::{Byte, ImmediateOperand, Word};
use super::interpreter::InterpreterError;
use crate::interpreter::register::{Register, SegmentRegister};
/// 2*20 = 1MiB
const MEMORY_SIZE: usize = 1048576;
@@ -18,17 +17,24 @@ impl Memory {
}
}
/// Safely writes a [`Word`] into an index of memory.
/// Safely writes a [`ImmediateOperand`] into an index of memory.
/// Warning: Does access at `addr`, not `DS:addr`!
pub fn write_raw(&mut self, addr: Word, val: Word) -> Result<(), InterpreterError> {
pub fn write_raw(&mut self, addr: Word, val: ImmediateOperand) -> Result<(), InterpreterError> {
if (addr + 1) as usize > MEMORY_SIZE {
return Err(InterpreterError::MemoryOutOfBound(addr));
} else {
let [low, high] = val.to_le_bytes();
self.memory[addr as usize] = low;
self.memory[(addr + 1) as usize] = high;
Ok(())
match val {
ImmediateOperand::Byte(b) => {
self.memory[addr as usize] = b;
}
ImmediateOperand::Word(w) => {
let [low, high] = w.to_le_bytes();
self.memory[addr as usize] = low;
self.memory[(addr + 1) as usize] = high;
}
}
}
Ok(())
}
/// Safely reads a [`Word`] from an index of memory.
@@ -46,69 +52,4 @@ impl Memory {
.to_owned();
Ok(Word::from_be_bytes([b2, b1]))
}
/// Write an [`ImmediateOperand`] to a memory location indexed by a [`MemoryIndex`].
pub fn write(
&mut self,
regs: &Register,
sregs: &SegmentRegister,
idx: MemoryIndex,
val: ImmediateOperand,
) {
let idx = Memory::absolute_addr_from_idx(regs, sregs, idx);
match val {
ImmediateOperand::Byte(b) => {
log::debug!("Writing byte {b:#04x} to memory location {idx:#04x}");
self.memory[idx as usize] = b
}
ImmediateOperand::Word(value) => {
let byte1 = idx / 2;
let byte2 = idx / 2 + 1;
let [low, high]: [u8; 2] = value.to_le_bytes();
log::debug!(
"Writing bytes {low:#04x} and {high:#04x} to memory location {byte1:#04x} and {byte2:#04x}"
);
self.memory[byte1 as usize] = low;
self.memory[byte1 as usize] = high;
}
}
}
/// Read into memory with a [`MemoryIndex`] as index.
/// Always reads a whole word.
pub fn read(&self, regs: &Register, sregs: &SegmentRegister, idx: MemoryIndex) -> Word {
let idx = Self::absolute_addr_from_idx(regs, sregs, idx);
let byte1 = idx / 2;
let byte2 = idx / 2 + 1;
log::debug!("Reading bytes {byte1:#04x} and {byte2:#04x} from memory");
Word::from_le_bytes([self.memory[byte1 as usize], self.memory[byte2 as usize]])
}
/// Memory is always accessed with the data segment `ds` register as an offset.
fn addr_offset(addr: Word, sregs: &crate::interpreter::register::SegmentRegister) -> Word {
sregs.ds * 16 + addr
}
/// Calculate the absolute memory address from a [`MemoryIndex`] struct.
pub fn absolute_addr_from_idx(
regs: &Register,
sregs: &SegmentRegister,
idx: MemoryIndex,
) -> Word {
let mut base = ImmediateOperand::Word(0);
let mut index = ImmediateOperand::Word(0);
let mut disp = ImmediateOperandSigned::Word(0);
if let Some(base_reg) = idx.base {
base = regs.read(base_reg);
};
if let Some(index_reg) = idx.index {
index = regs.read(index_reg);
};
if let Some(displacement) = idx.displacement {
disp = ImmediateOperandSigned::from(displacement);
}
Self::addr_offset((base + index + disp).into(), sregs)
}
}