Skip to main content

ironwork_rt/cics/
run.rs

1//! Running a command against the task: what it asks of the executor (`CicsHost`), reading and
2//! storing its options, the EXEC interface block, raising conditions, and dispatch to the services.
3
4use super::command::{Cics, CicsCommand, Datum, Record, Resp};
5use super::{Condition, Task, file_control, maps, program, services};
6use crate::abend::{Abend, AbendCode, Ending};
7use crate::bms::Mapset;
8use crate::host::Host;
9use crate::lir::{ParaId, Step};
10use crate::storage::{Loc, Val};
11use crate::store::{self, ProgramFacts};
12use crate::unit::{ADDRESS_BASE, Loader, RunUnit, UnitHost};
13use crate::vocab::Pos;
14use numeric::precision::{Fixed, Places};
15use std::collections::HashMap;
16use zarch::decimal::{self, Decimal};
17use zarch::ebcdic::{self, CodePage};
18
19pub(super) type R<T> = Result<T, Abend>;
20
21pub const EIBTIME: usize = 0x00;
22pub const EIBDATE: usize = 0x04;
23pub const EIBTRNID: usize = 0x08;
24pub const EIBTASKN: usize = 0x0C;
25pub const EIBTRMID: usize = 0x10;
26pub const EIBCPOSN: usize = 0x16;
27pub const EIBCALEN: usize = 0x18;
28pub const EIBAID: usize = 0x1A;
29pub const EIBFN: usize = 0x1B;
30pub const EIBRSRCE: usize = 0x33;
31pub const EIBRESP: usize = 0x4C;
32pub const EIBRESP2: usize = 0x50;
33
34/// What a command asks of the executor running it beyond `Host` and the run unit: the logical
35/// level's handlers, operands that are not data items, names only the executor can resolve, and
36/// running a program for LINK and XCTL.
37pub trait CicsHost<'w, P: Copy, O, S>: Host<P> + UnitHost<'w> {
38    fn handlers(&mut self) -> &mut Handlers;
39    /// An operand's bytes as CALL BY CONTENT passes them.
40    fn content(&mut self, operand: &O, pos: Pos) -> R<Vec<u8>>;
41    /// An operand's value as a subscript takes it.
42    fn integer_of(&mut self, operand: &O, pos: Pos) -> R<i64>;
43    fn text(&self, text: &S) -> String;
44    /// This activation of the program, which a HANDLE ABEND LABEL it sets belongs to.
45    fn activation(&self) -> u64;
46    fn program_id(&self) -> String;
47    /// Where DFHCOMMAREA is, when the program has one with an address.
48    fn commarea(&self) -> Option<usize>;
49    /// A mapset from the copy libraries; None when no library holds it.
50    fn mapset(&mut self, name: &str) -> Option<Result<Mapset, String>>;
51    /// The data item a name alone refers to, when it names one.
52    fn item_named(&mut self, name: &str, pos: Pos) -> R<Option<Loc>>;
53    /// The data item a name alone refers to, or the abend a reference to it gives.
54    fn locate_named(&mut self, name: &str, pos: Pos) -> R<Loc>;
55    /// The length of the data item `place` when it is in a LINKAGE record with no address, where
56    /// ADDRESS OF gives NULL; None when it has storage to locate.
57    fn unaddressed(&mut self, place: P) -> Option<usize>;
58    /// Runs program `index` from its start with DFHEIBLK and `commarea` as its USING items; for
59    /// XCTL, in this program's place at its logical level, with the handlers `Handlers::xctl`
60    /// leaves it (C239, C146). `program` is None for the task's first program, whose handle the
61    /// run unit does not hold (C148).
62    fn run_program(&mut self, program: Option<Self::Program>, index: usize, commarea: Option<usize>, xctl: bool) -> R<Ending>;
63}
64
65/// HANDLE CONDITION, IGNORE CONDITION and HANDLE ABEND, which belong to the logical level: every
66/// program CALLed at it shares them (C234).
67#[derive(Clone, Debug, Default)]
68pub struct Handlers {
69    pub conditions: HashMap<Condition, Handler>,
70    /// What each PUSH HANDLE at this logical level suspended.
71    pub stack: Vec<(HashMap<Condition, Handler>, Option<AbendExit>)>,
72    pub abend: Option<AbendExit>,
73}
74
75impl Handlers {
76    /// PUSH HANDLE: suspends HANDLE CONDITION, IGNORE CONDITION and HANDLE ABEND until `pop`.
77    pub fn push(&mut self) {
78        let suspended = (std::mem::take(&mut self.conditions), self.abend.take());
79        self.stack.push(suspended);
80    }
81
82    /// POP HANDLE: false when no PUSH HANDLE at this logical level is left to undo.
83    pub fn pop(&mut self) -> bool {
84        let Some((conditions, abend)) = self.stack.pop() else { return false };
85        self.conditions = conditions;
86        self.abend = abend;
87        true
88    }
89
90    /// The handlers a CALLed program starts with: the level's, which a dynamic CALL first
91    /// suspends with the PUSH HANDLE of CBLPSHPOP(ON) (C234).
92    pub fn lend(&mut self, pushes: bool) -> Handlers {
93        let mut level = std::mem::take(self);
94        if pushes {
95            level.push();
96        }
97        level
98    }
99
100    /// The handlers the program XCTL starts has: the level's HANDLE ABEND exit and PUSH HANDLE
101    /// stack, each exit the stack holds kept, and no HANDLE CONDITION or IGNORE CONDITION, which
102    /// belong to the program XCTL releases, pushed or not (C146).
103    pub fn xctl(&mut self) -> Handlers {
104        let stack = std::mem::take(&mut self.stack).into_iter().map(|(_, abend)| (HashMap::new(), abend)).collect();
105        Handlers { conditions: HashMap::new(), stack, abend: self.abend.take() }
106    }
107
108    /// Takes the level's handlers back from a CALLed program that has ended, as it left them; with
109    /// `pops`, for a dynamic CALL it returned from, the POP HANDLE that undoes the CALL's PUSH, which
110    /// changes nothing when no PUSH is left (C234).
111    pub fn take_back(&mut self, callee: &mut Handlers, pops: bool) {
112        *self = std::mem::take(callee);
113        if pops {
114            self.pop();
115        }
116    }
117}
118
119/// The program level's HANDLE ABEND exit, which CANCEL and entering it deactivate and RESET
120/// reactivates.
121#[derive(Clone, Debug, PartialEq, Eq)]
122pub struct AbendExit {
123    pub target: ExitTarget,
124    pub active: bool,
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
128pub enum ExitTarget {
129    /// A paragraph of the program activation `owner` that set the exit, entered as by a GO TO at
130    /// the HANDLE ABEND command `at`.
131    Label { paragraph: ParaId, owner: u64, at: Pos },
132    /// A program, entered as by LINK with the COMMAREA and EIBCALEN of the program that set the
133    /// exit, when it had a COMMAREA.
134    Program { name: String, commarea: Option<(usize, i16)> },
135}
136
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138pub enum Handler {
139    /// A paragraph of the program activation `owner` that issued the HANDLE CONDITION (C235).
140    Label { paragraph: ParaId, owner: u64 },
141    Ignore,
142}
143
144/// Where control goes after a command.
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub enum Flow {
147    Next,
148    /// A handled condition.
149    GoTo(ParaId),
150    /// RETURN, XCTL, or STOP RUN in the program XCTL started, which ends the level as RETURN does
151    /// (C144).
152    End(Ending),
153}
154
155impl From<Flow> for Step {
156    fn from(flow: Flow) -> Self {
157        match flow {
158            Flow::Next => Step::Next,
159            Flow::GoTo(p) => Step::GoTo(p),
160            Flow::End(e) => Step::End(e),
161        }
162    }
163}
164
165/// A command's name as written, which messages give, its RESP, RESP2 and NOHANDLE, and where it is.
166pub struct At<'c, P, O, S> {
167    pub name: &'c str,
168    pub resp: &'c Resp<P, O, S>,
169    pub pos: Pos,
170}
171
172/// EXEC CICS is refused outside a CICS task.
173pub fn in_task<H, L: Loader<H>>(unit: &RunUnit<'_, H, L>, name: &str, pos: Pos) -> R<()> {
174    match unit.cics {
175        Some(_) => Ok(()),
176        None => Err(Abend::ironwork(format!("EXEC CICS {name} was reached outside a CICS task: run the program with `ironwork cics`"), pos)),
177    }
178}
179
180/// Every command starts with EIBRESP and EIBRESP2 zero.
181pub fn begin_command<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>) {
182    eib_fullword(unit, EIBRESP, 0);
183    eib_fullword(unit, EIBRESP2, 0);
184}
185
186/// Runs a command.
187pub fn run<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, command: &CicsCommand<P, O, S>, pos: Pos) -> R<Flow> {
188    if let Cics::Refused(why) = &command.command {
189        return Err(Abend::ironwork(x.text(why), pos));
190    }
191    let name = x.text(&command.name);
192    in_task(x.unit(), &name, pos)?;
193    begin_command(x.unit());
194    let at = At { name: &name, resp: &command.resp, pos };
195    match &command.command {
196        Cics::File { verb, file, options } => file_control::run(x, &at, *verb, file.as_ref(), options),
197        Cics::Return { transid, commarea, length, channel, immediate } => program::cics_return(x, &at, transid.as_ref(), commarea.as_ref(), length.as_ref(), channel.is_some() || *immediate),
198        Cics::Link(t) => program::link(x, &at, t, false),
199        Cics::Xctl(t) => program::link(x, &at, t, true),
200        Cics::Abend { abcode, cancel } => program::abend(x, &at, abcode.as_ref(), *cancel),
201        Cics::HandleCondition(labels) => program::handle_condition(x, &at, labels),
202        Cics::IgnoreCondition(conditions) => program::ignore_condition(x, &at, conditions),
203        Cics::PushHandle => program::push_handle(x, &at),
204        Cics::PopHandle => program::pop_handle(x, &at),
205        Cics::HandleAbend { program, label, reset } => program::handle_abend(x, &at, program.as_ref(), *label, *reset),
206        Cics::HandleAid | Cics::Freemain | Cics::Enq | Cics::Deq | Cics::Delay => ok(x, &at),
207        Cics::SendMap { map, mapset, from, maponly, dataonly, cursor, control } => {
208            maps::send_map(x, &at, maps::MapNames { map: map.as_ref(), mapset: mapset.as_ref() }, from.as_ref(), (*maponly, *dataonly), cursor.as_ref(), *control)
209        }
210        Cics::ReceiveMap { map, mapset, into, set } => maps::receive_map(x, &at, maps::MapNames { map: map.as_ref(), mapset: mapset.as_ref() }, into.as_ref(), set.as_ref()),
211        Cics::SendControl { cursor, control } => maps::send_control(x, &at, cursor.as_ref(), *control),
212        Cics::Receive(record) => maps::receive_raw(x, &at, record),
213        Cics::Asktime { abstime } => services::asktime(x, &at, abstime.as_ref()),
214        Cics::Formattime { abstime, datesep, timesep, outputs } => services::formattime(x, &at, abstime.as_ref(), datesep.as_ref(), timesep.as_ref(), outputs),
215        Cics::Assign(assign) => services::assign(x, &at, assign),
216        Cics::Getmain { flength, length, initimg, set } => services::getmain(x, &at, flength.as_ref(), length.as_ref(), initimg.as_ref(), set.as_ref()),
217        Cics::Syncpoint { rollback } => services::syncpoint(x, &at, *rollback),
218        Cics::Address { eib, commarea, cwa, twa } => services::address(x, &at, eib.as_ref(), commarea.as_ref(), cwa.as_ref(), twa.as_ref()),
219        Cics::SendText { from, length } => services::send_text(x, &at, from.as_ref(), length.as_ref()),
220        Cics::WriteOperator { text, textlength } => services::write_operator(x, &at, text.as_ref(), textlength.as_ref()),
221        Cics::WriteqTs { queue, from, length, rewrite, item, numitems } => {
222            services::writeq_ts(x, &at, queue.as_ref(), (from.as_ref(), length.as_ref()), *rewrite, item.as_ref(), numitems.as_ref())
223        }
224        Cics::ReadqTs { queue, next, item, numitems, record } => services::readq_ts(x, &at, queue.as_ref(), *next, item.as_ref(), numitems.as_ref(), record),
225        Cics::DeleteqTs { queue } => services::deleteq_ts(x, &at, queue.as_ref()),
226        Cics::WriteqTd { queue, from, length } => services::writeq_td(x, &at, queue.as_ref(), from.as_ref(), length.as_ref()),
227        Cics::ReadqTd { queue, record } => services::readq_td(x, &at, queue.as_ref(), record),
228        Cics::DeleteqTd { queue } => services::deleteq_td(x, &at, queue.as_ref()),
229        Cics::Unsupported => Err(Abend::ironwork(unsupported(&name), pos)),
230        Cics::Refused(_) => unreachable!("a refused command abends before it runs"),
231    }
232}
233
234/// The abend message for a command ironwork does not carry out.
235pub fn unsupported(name: &str) -> String {
236    format!("EXEC CICS {name} is not supported yet")
237}
238
239/// The task; `run` has already refused to run a command outside one.
240pub(super) fn task<'a, 'w: 'a, P: Copy + 'a, O: 'a, S: 'a>(x: &'a mut impl CicsHost<'w, P, O, S>) -> &'a mut Task {
241    x.unit().cics.as_mut().expect("EXEC CICS runs only in a task")
242}
243
244pub(super) fn page<'w, P: Copy, O, S>(x: &impl CicsHost<'w, P, O, S>) -> &'static CodePage {
245    x.facts().page()
246}
247
248/// Text in the program's code page, a character it cannot encode as a space.
249pub(super) fn encoded(page: &CodePage, text: &str) -> Vec<u8> {
250    text.chars().map(|c| page.encode_char(c).unwrap_or(ebcdic::SPACE)).collect()
251}
252
253/// An option's bytes: a data item's storage, or another operand's as CALL BY CONTENT passes it.
254pub fn bytes<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, d: Option<&Datum<P, O, S>>, pos: Pos) -> R<Option<Vec<u8>>> {
255    Ok(match d {
256        Some(Datum::Place(p)) => {
257            let loc = x.locate(*p, false)?;
258            Some(store::bytes(x.mem(), loc).to_vec())
259        }
260        Some(Datum::Value(o)) => Some(x.content(o, pos)?),
261        _ => None,
262    })
263}
264
265/// An option's bytes, cut to a length option when that is shorter.
266pub(super) fn bytes_cut<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, data: Option<&Datum<P, O, S>>, length: Option<&Datum<P, O, S>>, pos: Pos) -> R<Option<Vec<u8>>> {
267    let Some(mut bytes) = bytes(x, data, pos)? else { return Ok(None) };
268    if let Some(n) = int(x, length, pos)? {
269        bytes.truncate(n.max(0) as usize);
270    }
271    Ok(Some(bytes))
272}
273
274/// FROM's bytes cut to LENGTH (or TEXT and TEXTLENGTH for WRITE OPERATOR); `from` names it.
275pub(super) fn sent<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, data: Option<&Datum<P, O, S>>, length: Option<&Datum<P, O, S>>, from: &str) -> R<Vec<u8>> {
276    bytes_cut(x, data, length, at.pos)?.ok_or_else(|| Abend::ironwork(format!("EXEC CICS {} needs {from}", at.name), at.pos))
277}
278
279pub(super) fn int<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, d: Option<&Datum<P, O, S>>, pos: Pos) -> R<Option<i64>> {
280    match d {
281        Some(Datum::Place(p)) => x.integer(*p, pos).map(Some),
282        Some(Datum::Value(o)) => x.integer_of(o, pos).map(Some),
283        _ => Ok(None),
284    }
285}
286
287/// A resource name (PROGRAM, TRANSID, QUEUE, FILE, ABCODE) with trailing spaces removed; text as
288/// written loses its quotes.
289pub(super) fn text<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, d: Option<&Datum<P, O, S>>, pos: Pos) -> R<Option<String>> {
290    if let Some(Datum::Text(t)) = d {
291        return Ok(Some(x.text(t).trim().trim_matches(|c| c == '\'' || c == '"').to_owned()));
292    }
293    let page = page(x);
294    Ok(bytes(x, d, pos)?.map(|b| page.decode(&b).trim_end().to_owned()))
295}
296
297/// Locates a receiver the command only writes: its old bytes reach nothing, so they are not read.
298fn receiver<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, p: P) -> R<Loc> {
299    let was = x.unit().writing(true);
300    let loc = x.locate(p, false);
301    x.unit().writing(was);
302    loc
303}
304
305/// MOVEs bytes into the data item an option names, as INTO and the like receive them; `name` is
306/// the option's.
307pub(super) fn store_bytes<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, d: Option<&Datum<P, O, S>>, name: &str, bytes: &[u8]) -> R<()> {
308    match d {
309        Some(Datum::Place(p)) => {
310            let loc = receiver(x, *p)?;
311            x.assign(loc, Val::Bytes(bytes.to_vec()), None, at.pos)
312        }
313        Some(Datum::Value(_)) => Err(Abend::ironwork(format!("EXEC CICS {}: {name} must name a data item", at.name), at.pos)),
314        _ => Ok(()),
315    }
316}
317
318pub(super) fn store_int<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, d: Option<&Datum<P, O, S>>, value: i64, pos: Pos) -> R<()> {
319    match d {
320        Some(Datum::Place(p)) => {
321            let dest = receiver(x, *p)?;
322            x.store_fixed(dest, &Fixed::new(i128::from(value), Places::new(19, 0)), pos)
323        }
324        _ => Ok(()),
325    }
326}
327
328/// Stores an address, or NULL for None, into the POINTER an option names.
329pub(super) fn store_pointer<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, d: Option<&Datum<P, O, S>>, offset: Option<usize>, pos: Pos) -> R<()> {
330    if let Some(Datum::Place(p)) = d {
331        let loc = receiver(x, *p)?;
332        let address = offset.map_or(0, |o| ADDRESS_BASE + o as u32);
333        x.assign(loc, Val::Address(address), None, pos)?;
334    }
335    Ok(())
336}
337
338pub(super) fn eib_bytes<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, offset: usize, bytes: &[u8]) {
339    let at = unit.eib + offset;
340    unit.write(at, bytes);
341}
342
343pub(super) fn eib_halfword<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, offset: usize, value: i16) {
344    eib_bytes(unit, offset, &value.to_be_bytes());
345}
346
347pub(super) fn eib_fullword<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, offset: usize, value: i32) {
348    eib_bytes(unit, offset, &value.to_be_bytes());
349}
350
351/// A 4-byte packed EIB field: seven digits and a sign.
352pub(super) fn eib_packed<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, offset: usize, value: i64) {
353    let mut field = [0u8; 4];
354    let _ = decimal::encode(&mut field, Decimal { negative: value < 0, magnitude: value.unsigned_abs() as u128 });
355    eib_bytes(unit, offset, &field);
356}
357
358/// An EIB text field, in EBCDIC, padded with spaces or cut to `len`.
359pub(super) fn eib_text<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, page: &CodePage, offset: usize, len: usize, text: &str) {
360    let mut bytes = encoded(page, text);
361    bytes.resize(len, ebcdic::SPACE);
362    eib_bytes(unit, offset, &bytes);
363}
364
365pub(super) fn eib_calen<H, L: Loader<H>>(unit: &RunUnit<'_, H, L>) -> i16 {
366    let at = unit.eib + EIBCALEN;
367    i16::from_be_bytes([unit.mem[at], unit.mem[at + 1]])
368}
369
370/// EIBCALEN and whether it may hold input, to put back as they were when a program run inside
371/// a command returns.
372pub(super) fn kept_calen<H, L: Loader<H>>(unit: &RunUnit<'_, H, L>) -> (i16, bool) {
373    (eib_calen(unit), unit.holds_input(unit.eib + EIBCALEN, 2))
374}
375
376pub(super) fn restore_calen<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, (value, input): (i16, bool)) {
377    eib_halfword(unit, EIBCALEN, value);
378    unit.mark_input(unit.eib + EIBCALEN, 2, input);
379}
380
381/// Fills the EXEC interface block for the task's first program: time, date, transaction, task
382/// number, terminal, COMMAREA length and the AID that started it.
383pub fn begin_task<H: Clone, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, page: &CodePage, length: usize) {
384    let (seconds, hundredths) = unit.now();
385    let abstime = super::abstime(seconds, hundredths);
386    let (transid, termid, number) = unit.cics.as_ref().map(|t| (t.transid.clone(), t.termid.clone(), t.number)).unwrap_or_default();
387    eib_packed(unit, EIBTIME, super::eib_time(abstime));
388    eib_packed(unit, EIBDATE, super::eib_date(abstime));
389    eib_text(unit, page, EIBTRNID, 4, &transid);
390    eib_packed(unit, EIBTASKN, i64::from(number));
391    eib_text(unit, page, EIBTRMID, 4, &termid);
392    eib_halfword(unit, EIBCALEN, length as i16);
393    // The caller chose the COMMAREA, and the operator the key that started the task: both input.
394    unit.mark_input(unit.eib + EIBCALEN, 2, length > 0);
395    if let Some(aid) = unit.cics.as_ref().and_then(|t| t.initial_aid) {
396        eib_bytes(unit, EIBAID, &[aid]);
397        unit.mark_input(unit.eib + EIBAID, 1, true);
398    }
399}
400
401/// Gives a record to INTO or SET and its length to LENGTH. A record longer than LENGTH (or the
402/// INTO item) arrives cut short, with LENGERR.
403pub(super) fn deliver<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, record: &Record<P, O, S>, data: &[u8]) -> R<Flow> {
404    let limit = match int(x, record.length.as_ref(), at.pos)? {
405        Some(n) => n.max(0) as usize,
406        None => match &record.into {
407            Some(Datum::Place(p)) => x.locate(*p, false)?.len,
408            _ => data.len(),
409        },
410    };
411    if record.set.is_some() {
412        let area = x.unit().push_temporary(data);
413        store_pointer(x, record.set.as_ref(), Some(area), at.pos)?;
414    } else {
415        store_bytes(x, at, record.into.as_ref(), "INTO", &data[..data.len().min(limit)])?;
416    }
417    store_int(x, record.length.as_ref(), data.len() as i64, at.pos)?;
418    if data.len() > limit && record.set.is_none() {
419        return raise(x, at, Condition::LENGERR, 0);
420    }
421    ok(x, at)
422}
423
424/// A command that succeeded: RESP and RESP2, when asked for, are zero.
425pub fn ok<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>) -> R<Flow> {
426    store_int(x, at.resp.resp.as_ref(), 0, at.pos)?;
427    store_int(x, at.resp.resp2.as_ref(), 0, at.pos)?;
428    Ok(Flow::Next)
429}
430
431/// A command that succeeded after running other commands, as LINK does: EIBRESP and RESP are zero
432/// again, and EIBRESP2 and RESP2 are `resp2`.
433pub(super) fn normal<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, resp2: i32) -> R<Flow> {
434    eib_fullword(x.unit(), EIBRESP, 0);
435    eib_fullword(x.unit(), EIBRESP2, resp2);
436    store_int(x, at.resp.resp.as_ref(), 0, at.pos)?;
437    store_int(x, at.resp.resp2.as_ref(), i64::from(resp2), at.pos)?;
438    Ok(Flow::Next)
439}
440
441/// Raises a condition. RESP or NOHANDLE take it; otherwise HANDLE CONDITION (the condition's own
442/// entry, else ERROR) or IGNORE CONDITION decides, a label set by another activation abending
443/// APC2 (C235); otherwise the task abends with the condition's AEIx code. A HANDLE ABEND exit can
444/// intercept either abend.
445pub fn raise<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, condition: Condition, resp2: i32) -> R<Flow> {
446    let resp = condition.resp();
447    eib_fullword(x.unit(), EIBRESP, resp);
448    eib_fullword(x.unit(), EIBRESP2, resp2);
449    if at.resp.resp.is_some() {
450        store_int(x, at.resp.resp.as_ref(), i64::from(resp), at.pos)?;
451        store_int(x, at.resp.resp2.as_ref(), i64::from(resp2), at.pos)?;
452        return Ok(Flow::Next);
453    }
454    if at.resp.nohandle {
455        return Ok(Flow::Next);
456    }
457    let me = x.activation();
458    let handlers = x.handlers();
459    match handlers.conditions.get(&condition).or_else(|| handlers.conditions.get(&Condition::ERROR)).copied() {
460        Some(Handler::Ignore) => Ok(Flow::Next),
461        Some(Handler::Label { paragraph, owner }) if owner == me => Ok(Flow::GoTo(paragraph)),
462        Some(Handler::Label { .. }) => Err(Abend {
463            code: AbendCode::Cics("APC2".into()),
464            message: format!("EXEC CICS {}: {} was raised; its HANDLE CONDITION label is in a program that is not running there, which CICS cannot branch to", at.name, condition.name()),
465            pos: at.pos,
466            file: None,
467        }),
468        None => Err(Abend {
469            code: AbendCode::Cics(condition.default_abend().into()),
470            message: format!("EXEC CICS {}: {} was raised with no RESP, HANDLE CONDITION or IGNORE CONDITION", at.name, condition.name()),
471            pos: at.pos,
472            file: None,
473        }),
474    }
475}