Skip to main content

ironwork_rt/
set.rs

1//! SET (lir.md ยง9.1): the `SetAddress` and `SetUpDown` ops, and the pointer rule of SET TO, which
2//! is otherwise a MOVE. SET TO TRUE and TO FALSE are MOVEs the executor resolves. SET TO ENTRY
3//! gives a function-pointer or procedure-pointer a value naming the entry a CALL through it enters.
4
5use crate::abend::Abend;
6use crate::host::{self, Host};
7use crate::loc;
8use crate::storage::{Kind, Loc, Val};
9use crate::store::{self, ProgramFacts};
10use crate::vocab::{Figurative, Pos};
11use numeric::precision::{Fixed, Places};
12
13type R<T> = Result<T, Abend>;
14
15/// SET ADDRESS OF: the offset in run-unit memory the records are given, None for NULL.
16pub fn address(val: Val, mem_len: usize, pos: Pos) -> R<Option<usize>> {
17    let address = match val {
18        Val::Address(a) => a,
19        Val::Fig(Figurative::Null) => 0,
20        _ => return Err(Abend::ironwork("SET ADDRESS OF takes a pointer, ADDRESS OF or NULL", pos)),
21    };
22    loc::offset_of(address, mem_len, pos)
23}
24
25/// SET TO: the value and sender MOVE takes into `dest`. A pointer takes only an address or NULL.
26pub fn to(dest: Loc, val: Val, src: Option<Loc>, pos: Pos) -> R<(Val, Option<Loc>)> {
27    match (dest.kind, val) {
28        (Kind::Pointer, v @ (Val::Address(_) | Val::Fig(Figurative::Null))) => Ok((v, None)),
29        (Kind::Pointer, _) => Err(Abend::ironwork("SET a pointer TO ADDRESS OF, NULL or another pointer", pos)),
30        (_, v) => Ok((v, src)),
31    }
32}
33
34/// A function-pointer or procedure-pointer set TO ENTRY holds this plus the entry's place in the
35/// run unit's list: above any storage address, below the JNI function table's values.
36const ENTRY_TAG: u32 = 0x7E00_0000;
37const ENTRY_SPAN: u32 = 0x0100_0000;
38
39/// An entry SET TO ENTRY named: its program-name, and whether a CALL through it is dynamic.
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct Entry {
42    pub name: String,
43    pub dynamic: bool,
44}
45
46/// SET TO ENTRY: the value the receivers take, the same each time the entry is named (C140).
47pub fn entry(entries: &mut Vec<Entry>, name: &str, dynamic: bool, pos: Pos) -> R<u32> {
48    let wanted = Entry { name: name.to_owned(), dynamic };
49    let slot = match entries.iter().position(|e| *e == wanted) {
50        Some(slot) => slot,
51        None => {
52            entries.push(wanted);
53            entries.len() - 1
54        }
55    };
56    u32::try_from(slot).ok().filter(|&s| s < ENTRY_SPAN).map(|s| ENTRY_TAG + s).ok_or_else(|| Abend::ironwork("SET TO ENTRY: more entries than a pointer value can tell apart", pos))
57}
58
59/// The entry a function-pointer's or procedure-pointer's value names, when SET TO ENTRY gave it.
60pub fn entry_of(entries: &[Entry], value: u32) -> Option<&Entry> {
61    value.checked_sub(ENTRY_TAG).filter(|&s| s < ENTRY_SPAN).and_then(|s| entries.get(s as usize))
62}
63
64/// SET UP BY or DOWN BY: each target in turn, by what reading it gives, moved by `by`.
65pub fn up_down<P: Copy>(x: &mut impl Host<P>, by: i64, down: bool, targets: &[P], pos: Pos) -> R<()> {
66    let step = if down { -by } else { by };
67    for &target in targets {
68        let dest = x.locate(target, false)?;
69        match host::read(x, dest, pos)? {
70            Val::Address(a) => {
71                let moved = u32::try_from(a as i64 + step).map_err(|_| Abend::ironwork("a pointer moved below zero", pos))?;
72                store::write(x.mem(), dest, &moved.to_be_bytes());
73            }
74            Val::Num(f) => {
75                let arith = x.facts().options().arith;
76                let next = f.add(Fixed::new(step as i128, Places::new(19, 0)), 0, arith).map_err(|_| Abend::ironwork("SET UP/DOWN", pos))?;
77                x.store_fixed(dest, &next, pos)?;
78            }
79            _ => return Err(Abend::ironwork("SET UP BY and DOWN BY take an index, integer or pointer", pos)),
80        }
81    }
82    Ok(())
83}