osiris_set_std/memory.rs
1//! 0x03 Memory operations (store/load, stack, allocation)
2//! * `0x0301` [store_words]:`target` `[start:end]`
3//! * `0x0302` [load_words]:`target` `[start:end]`
4//! * `0x0303` [get_memory_size]:`target`
5//! * `0x0304` [store_floats]:`target` `[start:end]`
6//! * `0x0305` [load_floats]:`target` `[start:end]`
7/// TODO allocation, etc
8
9use std::collections::HashMap;
10
11use osiris_data::data::atomic::Word;
12use osiris_data::data::composite::Array;
13use osiris_data::data::identification::{Address, Area};
14use osiris_data::memory::MemoryError;
15use osiris_process::operation::error::{OperationError, OperationResult, promote};
16use osiris_process::operation::{Operation, OperationSet};
17use osiris_process::operation::scheme::{ArgumentType, InstructionScheme, OperationId};
18use osiris_process::processor::Cpu;
19use osiris_process::register::floating_point::Number;
20use osiris_process::register::RegisterBank;
21
22pub const SET_MASK: u16 = 0x0300;
23pub const STORE_WORDS: OperationId = OperationId::new(SET_MASK | 0x01);
24pub const LOAD_WORDS: OperationId = OperationId::new(SET_MASK | 0x02);
25pub const GET_MEMORY_SIZE: OperationId = OperationId::new(SET_MASK | 0x03);
26pub const STORE_FLOATS: OperationId = OperationId::new(SET_MASK | 0x04);
27pub const LOAD_FLOATS: OperationId = OperationId::new(SET_MASK | 0x05);
28
29/// # `0x0301` [store_words]:`target` `[start:end]`
30///
31/// ## Target
32/// - The register containing the address from whom to write to.
33///
34/// ## Arguments
35/// - Range($start, $end)
36///
37/// ## Operation
38/// - `MEM[REG[$target]; ($end-$start)]` = `REG[$start..=$end]`
39///
40/// To store only one register into memory, just make `$start == $end`.
41///
42/// ## Errors
43/// - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
44/// - [OperationError::MemoryError] if a memory error occurs.
45pub fn store_words(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
46 let range = scheme.argument.get_range()?;
47 let start = cpu.bank_get(scheme.target);
48 let chunk = cpu.state.bank.slice(range);
49 promote(cpu.ram_mut().copy(Address::from_word(start), Array::from(chunk)))
50}
51
52/// # `0x0302` [load_words]:`target` `[start:end]`
53///
54/// This page describes the instruction scheme.
55///
56/// ## Target
57/// - The register containing the address to read from.
58///
59/// ## Arguments
60/// - Range($start, $end)
61///
62/// ## Operation
63/// - `REG[$start..=$end]` = `MEM[REG[$target]; ($end-$start)]`
64///
65/// To load only one register from memory, just make `$start == $end`.
66///
67/// ## Errors
68/// - [OperationError::InvalidArgumentType] if the provided argument is not a [ArgumentType::Range],
69/// - [OperationError::MemoryError]:[MemoryError::OutOfBounds] if a memory error occurs.
70pub fn load_words(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
71 let range = scheme.argument.get_range()?;
72 let memory = cpu.memory();
73 let ram = memory.borrow();
74 let start = cpu.bank_get(scheme.target).to_u64();
75 let end = start + range.count() as u64 - 1;
76 let area = Area::region(Address::new(start), (end - start) as usize);
77 let slice = promote(ram.slice(area))?;
78 cpu.state.bank.copy(range.start, slice);
79 Ok(())
80}
81
82/// # `0x0303` [get_memory_size]:`target`
83///
84/// This page describes the instruction scheme.
85///
86/// ## Target
87/// - The register to write the memory size.
88///
89/// ## Arguments
90/// - None.
91///
92/// ## Operation
93/// - `REG[$target] = MEM.len()`
94///
95/// ## Errors
96/// - [OperationError::InvalidArgumentType] if the provided argument is not a [ArgumentType::NoArgument],
97pub fn get_memory_size(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
98 scheme.argument.get_no_argument()?;
99 cpu.bank_set(scheme.target, Word::from_usize(cpu.memory_size()));
100 Ok(())
101}
102
103
104/// # `0x0304` [store_floats]:`target` `[start:end]`
105///
106/// ## Target
107/// - The register containing the address from whom to write to.
108///
109/// ## Arguments
110/// - Range($start, $end)
111///
112/// ## Operation
113/// - copies `MEM[REG[$target]; ($end-$start)]` into `REG[$start..=$end]`
114///
115/// To store only one register into memory, just make `$start == $end`.
116///
117/// ## Errors
118/// - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
119/// - [OperationError::MemoryError] if a memory error occurs.
120pub fn store_floats(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
121 let range = scheme.argument.get_range()?;
122 let start = cpu.bank_get(scheme.target);
123 let chunk = cpu.state.vector.slice(range);
124 promote(cpu.ram_mut().copy(Address::from_word(start), Number::slice_to_array(chunk)))
125}
126
127/// # `0x0305` [load_floats]:`target` `[start:end]`
128///
129/// This page describes the instruction scheme.
130///
131/// ## Target
132/// - The register containing the address to read from.
133///
134/// ## Arguments
135/// - Range($start, $end)
136///
137/// ## Operation
138/// - copies `FLOAT[$start..=$end]` into `MEM[REG[$target]; ($end-$start)]`
139///
140/// To load only one register from memory, just make `$start == $end`.
141///
142/// ## Errors
143/// - [OperationError::InvalidArgumentType] if the provided argument is not a [ArgumentType::Range],
144/// - [OperationError::MemoryError]:[MemoryError::OutOfBounds] if a memory error occurs.
145pub fn load_floats(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
146 let range = scheme.argument.get_range()?;
147 let memory = cpu.memory();
148 let ram = memory.borrow();
149 let start = cpu.bank_get(scheme.target).to_u64();
150 let end = start + range.count() as u64 - 1;
151 let area = Area::region(Address::new(start), (end - start) as usize);
152 let slice = promote(ram.slice(area))?;
153 cpu.state.vector.copy(range.start, &Number::from_words_slice(slice));
154 Ok(())
155}
156
157/// Returns memory operations.
158///
159/// ## Operations
160///
161/// * `0x0301` [store_words]:`target` `[start:end]`
162/// * `0x0302` [load_words]:`target` `[start:end]`
163/// * `0x0303` [get_memory_size]:`target`
164/// * `0x0304` [store_floats]:`target` `[start:end]`
165/// * `0x0305` [load_floats]:`target` `[start:end]`
166pub fn operation_set() -> OperationSet {
167 let mut set: OperationSet = HashMap::new();
168 set.insert(
169 STORE_WORDS,
170 Operation::new(STORE_WORDS, "store-words".to_string(), true, ArgumentType::Range, store_words),
171 );
172 set.insert(
173 LOAD_WORDS,
174 Operation::new(LOAD_WORDS, "load-words".to_string(), true, ArgumentType::Range, load_words),
175 );
176 set.insert(
177 GET_MEMORY_SIZE,
178 Operation::new(GET_MEMORY_SIZE, "get-memory-size".to_string(), true, ArgumentType::NoArgument, get_memory_size),
179 );
180 set.insert(
181 STORE_FLOATS,
182 Operation::new(STORE_FLOATS, "store-float".to_string(), true, ArgumentType::Range, store_floats),
183 );
184 set.insert(
185 LOAD_FLOATS,
186 Operation::new(LOAD_FLOATS, "load-floats".to_string(), true, ArgumentType::Range, load_floats),
187 );
188 set
189}