summaryrefslogblamecommitdiff
path: root/src/devices/memory_device.rs
blob: 8efb12a724b6cc0c4952933a97658ce78b73f2ca (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
             
 
                  
 
 
                      




                                                                     


                            
 
 















































                                                                          
 
 

                          
                            

                              








                                                              
 


                                                              
 


                                                             

         


















                                                                       
                                 
                                                                    



                                                       
                                               

                                                    
                                                               















                                                          
 









                          
     



                                                                   
     
use crate::*;

use std::cmp::min;


type Page = [u8; 256];

pub struct MemoryDevice {
    pub limit: u16,          // maximum provisionable number of pages
    pub requested: u16,      // number of pages requested by program
    pub provisioned: usize,  // number of pages provisioned for use
    pub pages: Vec<Page>,    // all allocated pages
    pub head_1: HeadAddress,
    pub head_2: HeadAddress,
    pub copy_length: u16,
}


impl Device for MemoryDevice {
    fn read(&mut self, port: u8) -> u8 {
        match port {
            0x0 => self.read_head_1(),
            0x1 => self.read_head_1(),
            0x2 => read_h!(self.head_1.offset),
            0x3 => read_l!(self.head_1.offset),
            0x4 => read_h!(self.head_1.address),
            0x5 => read_l!(self.head_1.address),
            0x6 => read_h!(self.provisioned),
            0x7 => read_l!(self.provisioned),
            0x8 => self.read_head_2(),
            0x9 => self.read_head_2(),
            0xa => read_h!(self.head_2.offset),
            0xb => read_l!(self.head_2.offset),
            0xc => read_h!(self.head_2.address),
            0xd => read_l!(self.head_2.address),
            0xe => 0x00,
            0xf => 0x00,
            _ => unreachable!(),
        }
    }

    fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
        match port {
            0x0 => self.write_head_1(value),
            0x1 => self.write_head_1(value),
            0x2 => write_h!(self.head_1.offset, value),
            0x3 => write_l!(self.head_1.offset, value),
            0x4 => write_h!(self.head_1.address, value),
            0x5 => write_l!(self.head_1.address, value),
            0x6 => write_h!(self.requested, value),
            0x7 => { write_l!(self.requested, value); self.provision(); },
            0x8 => self.write_head_2(value),
            0x9 => self.write_head_2(value),
            0xa => write_h!(self.head_2.offset, value),
            0xb => write_l!(self.head_2.offset, value),
            0xc => write_h!(self.head_2.address, value),
            0xd => write_l!(self.head_2.address, value),
            0xe => write_h!(self.copy_length, value),
            0xf => { write_l!(self.copy_length, value); self.copy(); },
            _ => unreachable!(),
        };
        return None;
    }

    fn wake(&mut self) -> bool {
        false
    }
}


impl MemoryDevice {
    pub fn new() -> Self {
        Self {
            limit: u16::MAX,
            requested: 0,
            provisioned: 0,
            pages: Vec::new(),
            head_1: HeadAddress::new(),
            head_2: HeadAddress::new(),
            copy_length: 0,
        }
    }

    pub fn read_head_1(&mut self) -> u8 {
        let (page_i, byte_i) = self.head_1.get_page_address();
        self.read_byte(page_i, byte_i)
    }

    pub fn read_head_2(&mut self) -> u8 {
        let (page_i, byte_i) = self.head_2.get_page_address();
        self.read_byte(page_i, byte_i)
    }

    fn read_byte(&self, page_i: usize, byte_i: usize) -> u8 {
        match self.pages.get(page_i) {
            Some(page) => page[byte_i],
            None => 0,
        }
    }

    pub fn write_head_1(&mut self, value: u8) {
        let (page_i, byte_i) = self.head_1.get_page_address();
        self.write_byte(page_i, byte_i, value);
    }

    pub fn write_head_2(&mut self, value: u8) {
        let (page_i, byte_i) = self.head_2.get_page_address();
        self.write_byte(page_i, byte_i, value);
    }

    // Write a byte to a page of memory.
    fn write_byte(&mut self, page_i: usize, byte_i: usize, value: u8) {
        match self.pages.get_mut(page_i) {
            Some(page) => page[byte_i] = value,
            None => if page_i < self.provisioned {
                self.pages.resize(page_i + 1, [0; 256]);
                self.pages[page_i][byte_i] = value;
            }
        }
    }

    pub fn provision(&mut self) {
        self.provisioned = min(self.requested, self.limit) as usize;
        // Defer allocation of new pages.
        self.pages.truncate(self.provisioned as usize);
    }

    pub fn copy(&mut self) {
        let src  = self.head_2.offset as usize;
        let dest = self.head_1.offset as usize;
        let count = self.copy_length as usize;

        // Pre-allocate destination pages as needed.
        let pages_needed = min(dest + count, self.provisioned);
        if pages_needed > self.pages.len() {
            self.pages.resize(pages_needed, [0; 256]);
        }

        for i in 0..count {
            let src_page = match self.pages.get(src + i) {
                Some(src_page) => src_page.to_owned(),
                None => [0; 256],
            };
            match self.pages.get_mut(dest + i) {
                Some(dest) => *dest = src_page,
                None => break,
            };
        }
    }
}


pub struct HeadAddress {
    pub offset: u16,
    pub address: u16,
}

impl HeadAddress {
    pub fn new() -> Self {
        Self {
            offset: 0,
            address: 0,
        }
    }

    fn get_page_address(&mut self) -> (usize, usize) {
        let page_i = (self.offset + (self.address / 256)) as usize;
        let byte_i = (self.address % 256) as usize;
        self.address = self.address.wrapping_add(1);
        (page_i, byte_i)
    }
}