From 0debae0678964b89993c27b3c51dffdcc210f3eb Mon Sep 17 00:00:00 2001
From: Simon Gellis <simongellis@gmail.com>
Date: Sat, 15 Feb 2025 16:14:03 -0500
Subject: [PATCH] Support hypothetical big-endian users

---
 src/memory.rs               | 85 ++++++++++++++++++++++++++++++++++---
 src/window/vram/bgmap.rs    | 24 +++++------
 src/window/vram/chardata.rs | 12 +++---
 src/window/vram/object.rs   |  8 ++--
 src/window/vram/utils.rs    |  4 +-
 5 files changed, 100 insertions(+), 33 deletions(-)

diff --git a/src/memory.rs b/src/memory.rs
index 9b54c8a..769326f 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -39,11 +39,12 @@ impl MemoryClient {
         MemoryView { region }
     }
 
-    pub fn write<T: bytemuck::NoUninit>(&self, sim: SimId, address: u32, data: &T) {
-        let data = bytemuck::bytes_of(data).to_vec();
+    pub fn write<T: MemoryValue>(&self, sim: SimId, address: u32, data: &T) {
+        let mut buffer = vec![];
+        data.to_bytes(&mut buffer);
         let (tx, _) = oneshot::channel();
         self.client
-            .send_command(EmulatorCommand::WriteMemory(sim, address, data, tx));
+            .send_command(EmulatorCommand::WriteMemory(sim, address, buffer, tx));
     }
 }
 
@@ -74,17 +75,87 @@ pub struct MemoryRef<'a> {
     inner: RwLockReadGuard<'a, BoxBytes>,
 }
 
+pub trait MemoryValue {
+    fn from_bytes(bytes: &[u8]) -> Self;
+    fn to_bytes(&self, buffer: &mut Vec<u8>);
+}
+
+macro_rules! primitive_memory_value_impl {
+    ($T:ty, $L: expr) => {
+        impl MemoryValue for $T {
+            #[inline]
+            fn from_bytes(bytes: &[u8]) -> Self {
+                let bytes: [u8; std::mem::size_of::<$T>()] = std::array::from_fn(|i| bytes[i]);
+                <$T>::from_le_bytes(bytes)
+            }
+            #[inline]
+            fn to_bytes(&self, buffer: &mut Vec<u8>) {
+                buffer.extend_from_slice(&self.to_le_bytes())
+            }
+        }
+    };
+}
+
+primitive_memory_value_impl!(u8, 1);
+primitive_memory_value_impl!(u16, 2);
+primitive_memory_value_impl!(u32, 4);
+
+impl<const N: usize, T: MemoryValue> MemoryValue for [T; N] {
+    #[inline]
+    fn from_bytes(bytes: &[u8]) -> Self {
+        std::array::from_fn(|i| {
+            T::from_bytes(&bytes[i * std::mem::size_of::<T>()..(i + 1) * std::mem::size_of::<T>()])
+        })
+    }
+    #[inline]
+    fn to_bytes(&self, buffer: &mut Vec<u8>) {
+        for item in self {
+            item.to_bytes(buffer);
+        }
+    }
+}
+
+pub struct MemoryIter<'a, T> {
+    bytes: &'a [u8],
+    index: usize,
+    _phantom: std::marker::PhantomData<T>,
+}
+
+impl<'a, T> MemoryIter<'a, T> {
+    fn new(bytes: &'a [u8]) -> Self {
+        Self {
+            bytes,
+            index: 0,
+            _phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+impl<T: MemoryValue> Iterator for MemoryIter<'_, T> {
+    type Item = T;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.index >= self.bytes.len() {
+            return None;
+        }
+        let bytes = &self.bytes[self.index..self.index + std::mem::size_of::<T>()];
+        self.index += std::mem::size_of::<T>();
+        Some(T::from_bytes(bytes))
+    }
+}
+
 impl MemoryRef<'_> {
-    pub fn read<T: bytemuck::AnyBitPattern>(&self, index: usize) -> T {
+    pub fn read<T: MemoryValue>(&self, index: usize) -> T {
         let from = index * size_of::<T>();
         let to = from + size_of::<T>();
-        *bytemuck::from_bytes(&self.inner[from..to])
+        T::from_bytes(&self.inner[from..to])
     }
 
-    pub fn range<T: bytemuck::AnyBitPattern>(&self, start: usize, count: usize) -> &[T] {
+    pub fn range<T: MemoryValue>(&self, start: usize, count: usize) -> MemoryIter<T> {
         let from = start * size_of::<T>();
         let to = from + (count * size_of::<T>());
-        bytemuck::cast_slice(&self.inner[from..to])
+        MemoryIter::new(&self.inner[from..to])
     }
 }
 
diff --git a/src/window/vram/bgmap.rs b/src/window/vram/bgmap.rs
index 93bca1a..8760347 100644
--- a/src/window/vram/bgmap.rs
+++ b/src/window/vram/bgmap.rs
@@ -239,30 +239,26 @@ impl BgMapRenderer {
         let brightness = self.brightness.borrow();
         let palettes = self.palettes.borrow();
 
-        let brts = brightness.range::<u8>(0, 8);
+        let brts = brightness.read::<[u8; 8]>(0);
         let colors = if generic_palette {
             [utils::generic_palette(Color32::RED); 4]
         } else {
-            [0, 2, 4, 6].map(|i| utils::parse_palette(palettes.read(i), brts, Color32::RED))
+            [0, 2, 4, 6].map(|i| utils::parse_palette(palettes.read(i), &brts, Color32::RED))
         };
 
-        for (i, cell) in bgmaps
-            .range::<u16>(bgmap_index * 4096, 4096)
-            .iter()
-            .enumerate()
-        {
+        for (i, cell) in bgmaps.range::<u16>(bgmap_index * 4096, 4096).enumerate() {
             let CellData {
                 char_index,
                 vflip,
                 hflip,
                 palette_index,
-            } = CellData::parse(*cell);
-            let char = chardata.range::<u16>(char_index * 8, 8);
+            } = CellData::parse(cell);
+            let char = chardata.read::<[u16; 8]>(char_index);
             let palette = &colors[palette_index];
 
             for row in 0..8 {
                 let y = row + (i / 64) * 8;
-                for (col, pixel) in utils::read_char_row(char, hflip, vflip, row).enumerate() {
+                for (col, pixel) in utils::read_char_row(&char, hflip, vflip, row).enumerate() {
                     let x = col + (i % 64) * 8;
                     image.write((x, y), palette[pixel as usize]);
                 }
@@ -276,7 +272,7 @@ impl BgMapRenderer {
         let brightness = self.brightness.borrow();
         let palettes = self.palettes.borrow();
 
-        let brts = brightness.range::<u8>(0, 8);
+        let brts = brightness.read::<[u8; 8]>(0);
 
         let cell = bgmaps.read::<u16>(index);
 
@@ -286,15 +282,15 @@ impl BgMapRenderer {
             hflip,
             palette_index,
         } = CellData::parse(cell);
-        let char = chardata.range::<u16>(char_index * 8, 8);
+        let char = chardata.read::<[u16; 8]>(char_index);
         let palette = if generic_palette {
             utils::generic_palette(Color32::RED)
         } else {
-            utils::parse_palette(palettes.read(palette_index * 2), brts, Color32::RED)
+            utils::parse_palette(palettes.read(palette_index * 2), &brts, Color32::RED)
         };
 
         for row in 0..8 {
-            for (col, pixel) in utils::read_char_row(char, hflip, vflip, row).enumerate() {
+            for (col, pixel) in utils::read_char_row(&char, hflip, vflip, row).enumerate() {
                 image.write((col, row), palette[pixel as usize]);
             }
         }
diff --git a/src/window/vram/chardata.rs b/src/window/vram/chardata.rs
index dc102d3..1cde8d4 100644
--- a/src/window/vram/chardata.rs
+++ b/src/window/vram/chardata.rs
@@ -221,8 +221,8 @@ impl CharacterDataWindow {
         };
         let palette = self.palettes.borrow().read(offset);
         let brightnesses = self.brightness.borrow();
-        let brts = brightnesses.range(0, 8);
-        utils::parse_palette(palette, brts, Color32::RED)
+        let brts = brightnesses.read(0);
+        utils::parse_palette(palette, &brts, Color32::RED)
     }
 
     fn show_chardata(&mut self, ui: &mut Ui) {
@@ -321,7 +321,7 @@ impl CharDataRenderer {
         let palette = self.load_palette(palette);
         let chardata = self.chardata.borrow();
         let character = chardata.range::<u16>(index * 8, 8);
-        for (row, pixels) in character.iter().enumerate() {
+        for (row, pixels) in character.enumerate() {
             for col in 0..8 {
                 let char = (pixels >> (col * 2)) & 0x03;
                 image.write((col, row), palette[char as usize]);
@@ -332,7 +332,7 @@ impl CharDataRenderer {
     fn render_character_data(&self, image: &mut VramImage, palette: VramPalette) {
         let palette = self.load_palette(palette);
         let chardata = self.chardata.borrow();
-        for (row, pixels) in chardata.range::<u16>(0, 8 * 2048).iter().enumerate() {
+        for (row, pixels) in chardata.range::<u16>(0, 8 * 2048).enumerate() {
             let char_index = row / 8;
             let row_index = row % 8;
             let x = (char_index % 16) * 8;
@@ -350,7 +350,7 @@ impl CharDataRenderer {
         };
         let palette = self.palettes.borrow().read(offset);
         let brightnesses = self.brightness.borrow();
-        let brts = brightnesses.range(0, 8);
-        utils::parse_palette(palette, brts, Color32::RED)
+        let brts = brightnesses.read(0);
+        utils::parse_palette(palette, &brts, Color32::RED)
     }
 }
diff --git a/src/window/vram/object.rs b/src/window/vram/object.rs
index 6313779..8c2aae3 100644
--- a/src/window/vram/object.rs
+++ b/src/window/vram/object.rs
@@ -282,7 +282,7 @@ impl ObjectRenderer {
             return;
         }
 
-        let brts = brightness.range::<u8>(0, 8);
+        let brts = brightness.read::<[u8; 8]>(0);
         let (x, y) = if use_pos {
             let x = match eye {
                 Eye::Left => obj.x - obj.parallax,
@@ -298,11 +298,11 @@ impl ObjectRenderer {
             Eye::Right => params.right_color,
         };
 
-        let char = chardata.range::<u16>(obj.data.char_index * 8, 8);
+        let char = chardata.read::<[u16; 8]>(obj.data.char_index);
         let palette = if params.generic_palette {
             utils::generic_palette(color)
         } else {
-            utils::parse_palette(palettes.read(8 + obj.data.palette_index * 2), brts, color)
+            utils::parse_palette(palettes.read(8 + obj.data.palette_index * 2), &brts, color)
         };
 
         for row in 0..8 {
@@ -311,7 +311,7 @@ impl ObjectRenderer {
                 continue;
             }
             for (col, pixel) in
-                utils::read_char_row(char, obj.data.hflip, obj.data.vflip, row).enumerate()
+                utils::read_char_row(&char, obj.data.hflip, obj.data.vflip, row).enumerate()
             {
                 let real_x = x + col as i16;
                 if !(0..384).contains(&real_x) {
diff --git a/src/window/vram/utils.rs b/src/window/vram/utils.rs
index 6b0b469..d95319c 100644
--- a/src/window/vram/utils.rs
+++ b/src/window/vram/utils.rs
@@ -10,7 +10,7 @@ pub fn generic_palette(color: Color32) -> [Color32; 4] {
     GENERIC_PALETTE.map(|brt| shade(brt, color))
 }
 
-pub fn parse_palette(palette: u8, brts: &[u8], color: Color32) -> [Color32; 4] {
+pub fn parse_palette(palette: u8, brts: &[u8; 8], color: Color32) -> [Color32; 4] {
     let shades = [
         Color32::BLACK,
         shade(brts[0], color),
@@ -117,7 +117,7 @@ impl CellData {
 }
 
 pub fn read_char_row(
-    char: &[u16],
+    char: &[u16; 8],
     hflip: bool,
     vflip: bool,
     row: usize,