idk why these weren't alr committed

This commit is contained in:
2026-03-04 04:38:31 +00:00
parent fb2e734a2f
commit 29077adb38
10 changed files with 865 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
## Architecture
32 bit instruction width/word size
## Instruction Types:
Type Sizes:
- u16: [16 bits]
- u6: [6 bits]
- Reg: [5 bits]
- Opcode: [6 bits]
Instruction Types:
- R: [Opcode] [Reg] [Reg] [Reg] [Reg] [u6]
- I: [Opcode] [Reg] [Reg] [u16 ]
## Instruction Set
// move
mov <src: Reg> <dst: Reg> [ unused ]
cmov <src: Reg> <dst: Reg> <cmp: Reg>
// load
ldb <src: Reg> <dst: Reg> <offset: u16>
ldbs <src: Reg> <dst: Reg> <offset: u16>
ldh <src: Reg> <dst: Reg> <offset: u16>
ldhs <src: Reg> <dst: Reg> <offset: u16>
ldw <src: Reg> <dst: Reg> <offset: u16>
// store
stb <src: Reg> <dst: Reg> <offset: u16>
sth <src: Reg> <dst: Reg> <offset: u16>
stw <src: Reg> <dst: Reg> <offset: u16>
// immediate
lli <dst: Reg> [unused] <imm: u16>
lui <dst: Reg> [unused] <imm: u16>
// comparison
ieq <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
ine <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
igt <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
ige <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
ilt <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
ile <sr1: Reg> <sr2: Reg> <dst: Reg> [unused]
// Jump
jmp [ unused ] <addr: Reg> <offset: u16> // unconditional
jez <cmp: Reg> <addr: Reg> <offset: u16> // conditional on cmp == 0
jnz <cmp: Reg> <addr: Reg> <offset: u16>
jic [ unused ] <addr: Reg> <offset: u16> // conditional on carry
jnc [ unused ] <addr: Reg> <offset: u16>
// Bitwise
and <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // and
nnd <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // nand
or <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // or
nor <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // nor
xor <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // xor
xnr <sr1: Reg> <sr2: Reg> <dst: Reg> [unused] // xnor
not <src: Reg> [ unused ] <dst: Reg> [unused] // not
// Arithmetic
add <sr1: Reg> <sr2: Reg> <dst: Reg>
sub <sr1: Reg> <sr2: Reg> <dst: Reg>
shl <src: Reg> <rshamt: Reg> <dst: Reg> <ishamt: u6> // left shift of rshamt+ishamt
shr <src: Reg> <rshamt: Reg> <dst: Reg> <ishamt: u6> // right shift
// Immediate Arithmetic
addi <sr1: Reg> <dst: Reg> <imm: u16>
subi <sr1: Reg> <dst: Reg> <imm: u16>
// Misc
nop (zero/do nothing)
int <imm: u6>
hlt
// Atomic
// Atomic Compare and Swap - compares src and dst, if src
acs <src: Reg> <cmp: Reg> <dst: Reg> <res: Reg>
## Assembler Pseudoinstructions
Definitions:
Label - named address, a function or global
```
lwi <src: Label|Imm> <dest: Reg> // loads the literal value of Label|Imm into dest
// if src is a Label, take literal value of Label
=> lli $src, $dest
=> lui $src, $dest
lli <src: Label> <dest: Reg> // loads the lower 16 bits of the literal value of Label into dest
... -> similarly defined for lui
ldw <src: Label> <dest: Reg> // loads the word at the address of Label into dest
... => similarly defined for ldb, ldbs, ldh, ldhs
// load the label value of src into dest with lui/lli
=> lli $src, $dest
=> lui $src, $dest
=> ldw $dest, $dest
stw <src: Reg> <dest: Label> // stores the word in src using the address/label that is dest.
// load the literal value of dest into the accumulator with lui/lli
=> lli $dest, acc
=> lui $dest, acc
=> stw $src, acc
jmp <dest: Label>
CASE dest > u16::MAX:
=> lli $dest, acc
=> lui $dest, acc
=> jmp $dest, 0
CASE dest < u16::MAX:
// assembler inserts the literal value of dest
=> jmp $dest, 0
jmp <lbl: Label> <dest: Reg>
CASE lbl > u16::MAX:
AssemblerError!
CASE lbl < u16::MAX:
=> jmp $dest, lbl
call <addr: Label>
=> subi spr, 4, spr
=> stw pcx, spr, 0
=> jmp $addr
func
// push bpr
=> subi spr, 4, spr
=> stw bpr, spr, 0
// replace bpr with spr
=> mov spr, bpr
return
=> mov bpr, spr
// pop bpr
=> addi spr, 4, spr
=> ldw spr, bpr, 0
// load return addr
=> ldw spr, ret, 0
=> addi, spr, 4, spr
=> jmp ret, 4
```
+42
View File
@@ -0,0 +1,42 @@
use std::{fs, path::PathBuf};
use clap::{Parser, ValueEnum};
use crate::{MemoryMap, RandomAccessMemory};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct DsaArgs {
/// Memory map to use on boot.
/// Overrides default value.
#[arg(long = "mmap")]
memory_map: Option<PathBuf>,
#[arg(value_enum, long = "mem", default_value = "bulk-alloc")]
pub memory_bank: MemoryBank,
}
impl DsaArgs {
pub fn get_memory_map(&self) -> Option<MemoryMap> {
self.memory_map.as_ref().and_then(|m| {
fs::read_to_string(m)
.ok()
.and_then(|map| ron::from_str(&map).ok())
})
}
}
#[derive(Debug, Clone, ValueEnum, Default)]
pub enum MemoryBank {
#[cfg(feature = "mainstore-bulkalloc")]
#[default]
BulkAlloc,
#[cfg(feature = "mainstore-arraymap")]
ArrayMap,
#[cfg(feature = "mainstore-hashmap")]
HashMap,
#[cfg(feature = "mainstore-prealloc")]
PreAlloc,
#[cfg(feature = "mainstore-stackarray")]
StackArray,
}
+177
View File
@@ -0,0 +1,177 @@
#[derive(Clone, Copy)]
pub struct InstructionWord(pub u32);
impl InstructionWord {
#[inline]
pub fn opcode(self) -> u8 {
((self.0 >> 26) & 0x3F) as u8
}
// Src shared between R type and I type
#[inline]
pub fn src1(self) -> Reg {
Reg::from_u8_unchecked(((self.0 >> 21) & 0x1F) as u8)
}
// Dest shared between R type and I type
#[inline]
pub fn dest(self) -> Reg {
Reg::from_u8_unchecked(((self.0 >> 16) & 0x1F) as u8)
}
// Secondary Src for R type only
#[inline]
pub fn src2(self) -> Reg {
Reg::from_u8_unchecked(((self.0 >> 11) & 0x1F) as u8)
}
// Misc/Conditional reg for R type only
#[inline]
pub fn misc(self) -> Reg {
Reg::from_u8_unchecked(((self.0 >> 6) & 0x1F) as u8)
}
// Shift amount / misc data for R type only
#[inline]
pub fn shamt(self) -> u8 {
(self.0 & 0x3F) as u8
}
// 16 Bit immediate for I type only
#[inline]
pub fn imm16(self) -> u16 {
self.0 as u16
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Opcode {
Nop = 0,
// move
Mov = 1,
CMov = 2,
// load
Ldb = 3,
Ldbs = 4,
Ldh = 5,
Ldhs = 6,
Ldw = 7,
// store
Stb = 8,
Sth = 9,
Stw = 10,
// load immediate
Lli = 11,
Lui = 12,
// comparison
Ieq = 13,
Ine = 14,
Ilt = 15,
Ile = 16,
Igt = 17,
Ige = 18,
// jump
Jmp = 19,
Jez = 20,
Jnz = 21,
Jic = 22,
Jnc = 23,
// bitwise
And = 24,
Nand = 25,
Or = 26,
Nor = 27,
Xor = 28,
Xnor = 29,
Not = 30,
// arithmetic
Add = 31,
Sub = 32,
Shl = 33,
Shr = 34,
Addi = 35,
Subi = 36,
// system
Int = 37,
Hlt = 38,
}
impl Opcode {
#[inline]
pub fn from_u8(val: u8) -> Option<Self> {
if val <= Self::Hlt as u8 {
Some(unsafe { std::mem::transmute(val) })
} else {
None
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
#[non_exhaustive]
pub enum Reg {
// general purpose
Rg0 = 0,
Rg1 = 1,
Rg2 = 2,
Rg3 = 3,
Rg4 = 4,
Rg5 = 5,
Rg6 = 6,
Rg7 = 7,
Rg8 = 8,
Rg9 = 9,
Rga = 10,
Rgb = 11,
Rgc = 12,
Rgd = 13,
Rge = 14,
Rgf = 15,
// special purpose
Zero = 16,
Acc = 17,
Spr = 18,
Bpr = 19,
Ret = 20,
Idr = 21,
Mmr = 22,
// system - read only
Mar = 23,
Mdr = 24,
Sts = 25,
Cir = 26,
Pcx = 27,
#[default]
Null = 28,
}
impl Reg {
#[must_use]
#[inline]
pub fn from_u8_unchecked(idx: u8) -> Self {
debug_assert!(idx <= 28);
unsafe { std::mem::transmute(idx) }
}
#[inline]
pub fn from_u8(idx: u8) -> Result<Self, ()> {
if idx > 28 {
return Err(());
}
Ok(Self::from_u8_unchecked(idx))
}
}
+1
View File
@@ -0,0 +1 @@
pub mod instructions;
+105
View File
@@ -0,0 +1,105 @@
use std::{
alloc::{Layout, alloc_zeroed},
hint::{likely, unlikely},
};
use crate::memory::{
PhysAddr, RandomAccessMemory,
ram::{NUM_PAGES, Page, idx},
};
pub struct ArrayStore {
pages: Box<[*mut Page; NUM_PAGES]>,
}
unsafe impl Send for ArrayStore {}
impl ArrayStore {
pub fn new() -> Self {
let pages = vec![0 as *mut Page; 2 << 20]
.into_boxed_slice()
.try_into()
.unwrap();
Self { pages }
}
fn alloc(&mut self) -> *mut Page {
let layout = Layout::from_size_align(4096, 4096).unwrap();
unsafe { alloc_zeroed(layout) as *mut Page }
}
}
impl RandomAccessMemory for ArrayStore {
#[inline(always)]
fn read_word(&self, addr: PhysAddr) -> u32 {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
}
// Slow path: MMIO, fault, etc — never inlined
// Since we're only reading, we can safely return 0
return 0;
}
#[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
}
return 0;
}
#[inline(always)]
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
}
return &[0; 4096];
}
#[inline(always)]
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe {
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
}
}
#[inline(always)]
fn write_word(&mut self, addr: PhysAddr, value: u32) {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
}
#[inline(always)]
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
}
}
+132
View File
@@ -0,0 +1,132 @@
use std::{
alloc::{Layout, alloc_zeroed},
hint::{likely, unlikely},
};
use crate::memory::{
PhysAddr, RandomAccessMemory,
ram::{NUM_PAGES, Page, idx},
};
pub struct BulkAllocStore {
pages: Box<[*mut Page; NUM_PAGES]>,
pre_allocated: [*mut Page; Self::PREALLOC],
idx: u8,
}
unsafe impl Send for BulkAllocStore {}
impl BulkAllocStore {
const PREALLOC: usize = u8::MAX as usize;
pub fn new() -> Self {
let pages = vec![0 as *mut Page; 2 << 20]
.into_boxed_slice()
.try_into()
.unwrap();
Self {
pages,
pre_allocated: Self::alloc_set(),
idx: 0,
}
}
fn alloc_set() -> [*mut Page; Self::PREALLOC] {
let layout = Layout::from_size_align(4096 * Self::PREALLOC, 4096).unwrap();
let mut region = unsafe { alloc_zeroed(layout) as *mut Page };
let mut set = [0 as *mut Page; Self::PREALLOC];
for i in 0..Self::PREALLOC {
set[i] = region;
region = unsafe { region.byte_add(4096) };
}
set
}
fn alloc(&mut self) -> *mut Page {
if self.idx < Self::PREALLOC as u8 {
let page = self.pre_allocated[self.idx as usize];
self.idx += 1;
page
} else {
self.idx = 0;
self.pre_allocated = Self::alloc_set();
self.pre_allocated[0]
}
}
}
impl RandomAccessMemory for BulkAllocStore {
#[inline(always)]
fn read_word(&self, addr: PhysAddr) -> u32 {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
}
// Slow path: MMIO, fault, etc — never inlined
// Since we're only reading, we can safely return 0
return 0;
}
#[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
}
return 0;
}
#[inline(always)]
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
}
return &[0; 4096];
}
#[inline(always)]
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe {
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
}
}
#[inline(always)]
fn write_word(&mut self, addr: PhysAddr, value: u32) {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
}
#[inline(always)]
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
}
}
+63
View File
@@ -0,0 +1,63 @@
use fxhash::FxHashMap;
use crate::memory::{PhysAddr, RandomAccessMemory, ram::Page};
pub struct HashStore {
pages: FxHashMap<PhysAddr, Page>,
}
impl HashStore {
pub fn new() -> Self {
Self {
pages: FxHashMap::default(),
}
}
#[inline(always)]
const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) {
(addr & !(0xFFF), (addr & 0xFFF) as usize)
}
}
impl RandomAccessMemory for HashStore {
#[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
let (page, offset) = Self::segment_addr(addr);
self.pages.get(&page).map(|p| p[offset]).unwrap_or(0)
}
#[inline(always)]
fn read_word(&self, addr: PhysAddr) -> u32 {
let (page, offset) = Self::segment_addr(addr);
debug_assert_eq!(offset % 4, 0);
let page = self.pages.get(&page).unwrap_or(&[0; 4096]);
u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap())
}
#[inline(always)]
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 0x1000, 0);
self.pages.get(&addr).unwrap_or(&[0; 4096])
}
#[inline(always)]
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
let (page, offset) = Self::segment_addr(addr);
self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value;
}
#[inline(always)]
fn write_word(&mut self, addr: PhysAddr, value: u32) {
let (page, offset) = Self::segment_addr(addr);
debug_assert_eq!(offset % 4, 0);
let page = self.pages.entry(page).or_insert_with(|| [0; 4096]);
page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes());
}
#[inline(always)]
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
debug_assert_eq!(addr % 0x1000, 0);
let page = self.pages.entry(addr).or_insert_with(|| [0; 4096]);
page.copy_from_slice(value);
}
}
+49
View File
@@ -0,0 +1,49 @@
use crate::memory::PhysAddr;
#[cfg(feature = "mainstore-arraymap")]
pub mod arraymap;
#[cfg(feature = "mainstore-arraymap")]
pub use arraymap::ArrayStore;
#[cfg(feature = "mainstore-hashmap")]
pub mod hashmap;
#[cfg(feature = "mainstore-hashmap")]
pub use hashmap::HashStore;
#[cfg(feature = "mainstore-stackarray")]
pub mod stackarray;
#[cfg(feature = "mainstore-stackarray")]
pub use stackarray::StackArrayStore;
#[cfg(feature = "mainstore-bulkalloc")]
pub mod bulkalloc;
#[cfg(feature = "mainstore-bulkalloc")]
pub use bulkalloc::BulkAllocStore;
#[cfg(feature = "mainstore-prealloc")]
pub mod prealloc;
#[cfg(feature = "mainstore-prealloc")]
pub use prealloc::PreAllocStore;
const NUM_PAGES: usize = 2 << 20;
type Page = [u8; 4096];
#[inline(always)]
const fn idx(addr: PhysAddr) -> (usize, usize) {
((addr >> 12) as usize, (addr & 0xFFF) as usize)
}
pub trait RandomAccessMemory {
fn read_byte(&self, addr: PhysAddr) -> u8;
fn write_byte(&mut self, addr: PhysAddr, value: u8);
fn read_word(&self, addr: PhysAddr) -> u32;
fn write_word(&mut self, addr: PhysAddr, value: u32);
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096];
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]);
}
+47
View File
@@ -0,0 +1,47 @@
use crate::memory::{PhysAddr, RandomAccessMemory, ram::NUM_PAGES};
pub struct PreAllocStore {
heap: Box<[u8; NUM_PAGES * 4096]>,
}
unsafe impl Send for PreAllocStore {}
impl PreAllocStore {
pub fn new() -> Self {
Self {
heap: unsafe { Box::new_zeroed().assume_init() },
}
}
}
impl RandomAccessMemory for PreAllocStore {
#[inline(always)]
fn read_word(&self, addr: PhysAddr) -> u32 {
unsafe { (self.heap.as_ptr().add(addr as usize) as *const u32).read() }
}
#[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
self.heap[addr as usize]
}
#[inline(always)]
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 4096, 0);
unsafe { (self.heap.as_ptr().add(addr as usize) as *const [u8; 4096]).as_ref_unchecked() }
}
#[inline(always)]
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
self.heap[addr as usize] = value;
}
#[inline(always)]
fn write_word(&mut self, addr: PhysAddr, value: u32) {
unsafe { (self.heap.as_ptr().add(addr as usize) as *mut u32).write(value) };
}
#[inline(always)]
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
self.heap[addr as usize..addr as usize + 4096].copy_from_slice(value);
}
}
+103
View File
@@ -0,0 +1,103 @@
use std::{
alloc::{Layout, alloc_zeroed},
hint::{likely, unlikely},
};
use crate::memory::{
PhysAddr, RandomAccessMemory,
ram::{NUM_PAGES, Page, idx},
};
pub struct StackArrayStore {
pages: [*mut Page; NUM_PAGES],
}
unsafe impl Send for StackArrayStore {}
impl StackArrayStore {
pub fn new() -> Self {
Self {
pages: [0 as *mut Page; 2 << 20],
}
}
fn alloc(&mut self) -> *mut Page {
let layout = Layout::from_size_align(4096, 4096).unwrap();
unsafe { alloc_zeroed(layout) as *mut Page }
}
}
impl RandomAccessMemory for StackArrayStore {
#[inline(always)]
fn read_word(&self, addr: PhysAddr) -> u32 {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u32).byte_add(offset).read() };
}
// Slow path: MMIO, fault, etc — never inlined
// Since we're only reading, we can safely return 0
return 0;
}
#[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
let (idx, offset) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { (self.pages[idx] as *const u8).byte_add(offset).read() };
}
return 0;
}
#[inline(always)]
fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if likely(!self.pages[idx].is_null()) {
return unsafe { &*(self.pages[idx] as *const [u8; 4096]) };
}
return &[0; 4096];
}
#[inline(always)]
fn write_byte(&mut self, addr: PhysAddr, value: u8) {
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe {
(self.pages[idx] as *mut u8).byte_add(offset).write(value);
}
}
#[inline(always)]
fn write_word(&mut self, addr: PhysAddr, value: u32) {
debug_assert_eq!(addr % 4, 0);
let (idx, offset) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut u32).byte_add(offset).write(value) }
}
#[inline(always)]
fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
debug_assert_eq!(addr % 4096, 0);
let (idx, _) = idx(addr);
if unlikely(self.pages[idx].is_null()) {
self.pages[idx] = self.alloc();
}
unsafe { (self.pages[idx] as *mut [u8; 4096]).write(*value) }
}
}