1use super::Condition;
5use super::command::{Datum, Transfer};
6use super::run::{AbendExit, At, CicsHost, EIBCALEN, EIBFN, EIBRSRCE, ExitTarget, Flow, Handler, Handlers, R};
7use super::run::{bytes_cut, eib_bytes, eib_calen, eib_halfword, eib_text, int, ok, page, raise, text};
8use crate::abend::{Abend, AbendCode, Ending};
9use crate::lir::ParaId;
10use crate::unit::{LoadError, Loader, RunUnit};
11use crate::vocab::Pos;
12
13pub(super) fn cics_return<'w, P: Copy, O, S>(
16 x: &mut impl CicsHost<'w, P, O, S>,
17 at: &At<P, O, S>,
18 transid: Option<&Datum<P, O, S>>,
19 commarea: Option<&Datum<P, O, S>>,
20 length: Option<&Datum<P, O, S>>,
21) -> R<Flow> {
22 eib_bytes(x.unit(), EIBFN, &[0x0E, 0x08]);
23 let transid = text(x, transid, at.pos)?;
24 let commarea = bytes_cut(x, commarea, length, at.pos)?;
25 if !x.main() && (transid.is_some() || commarea.is_some()) {
26 return raise(x, at, Condition::INVREQ, 0);
27 }
28 if let Some(task) = x.unit().cics.as_mut() {
29 if let Some(t) = transid {
30 task.next_transid = Some(t.to_ascii_uppercase());
31 }
32 if commarea.is_some() {
33 task.returned_commarea = commarea;
34 }
35 }
36 Ok(Flow::End(Ending::Goback))
37}
38
39pub(super) fn link<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, t: &Transfer<P, O, S>, xctl: bool) -> R<Flow> {
43 eib_bytes(x.unit(), EIBFN, if xctl { &[0x0E, 0x04] } else { &[0x0E, 0x02] });
44 let Some(name) = text(x, t.program.as_ref(), at.pos)?.map(|n| n.to_ascii_uppercase()) else {
45 return Err(Abend::ironwork(format!("EXEC CICS {} needs PROGRAM", at.name), at.pos));
46 };
47 let page = page(x);
48 eib_text(x.unit(), page, EIBRSRCE, 8, &name);
49 let index = match x.unit().load(&name) {
50 Ok(i) => i,
51 Err(LoadError::NotFound) => return raise(x, at, Condition::PGMIDERR, 0),
52 Err(LoadError::Compile(m)) => return Err(Abend::ironwork(format!("EXEC CICS {} PROGRAM({name}): {m}", at.name), at.pos)),
53 };
54 let Some(program) = x.unit().programs[index].compiled.clone() else {
55 return Err(Abend::ironwork(format!("EXEC CICS {} PROGRAM({name}): the task's first program is already running", at.name), at.pos));
56 };
57 let mark = x.unit().mem.len();
58 let (area, item_len) = match &t.commarea {
59 Some(Datum::Place(p)) if !xctl => {
60 let loc = x.locate(*p, false)?;
61 (Some(loc.offset), loc.len)
62 }
63 Some(Datum::Place(_) | Datum::Value(_)) => {
64 let bytes = bytes_cut(x, t.commarea.as_ref(), t.length.as_ref(), at.pos)?.unwrap_or_default();
65 (Some(x.unit().push_temporary(&bytes)), bytes.len())
66 }
67 _ => (None, 0),
68 };
69 let length = int(x, t.length.as_ref(), at.pos)?.map_or(item_len, |n| n.max(0) as usize);
70 let saved = eib_calen(x.unit());
71 eib_halfword(x.unit(), EIBCALEN, length as i16);
72 if xctl {
73 x.handlers().abend = None;
74 }
75 let ending = enter(x, program, index, area, xctl, at.pos);
76 let unit = x.unit();
77 unit.release_temporaries(mark);
78 eib_halfword(unit, EIBCALEN, saved);
79 match ending? {
80 Ending::StopRun => Ok(Flow::End(Ending::StopRun)),
81 _ if xctl => Ok(Flow::End(Ending::Goback)),
82 _ => ok(x, at),
83 }
84}
85
86fn enter<'w, P: Copy, O, S, X: CicsHost<'w, P, O, S>>(x: &mut X, program: X::Program, index: usize, area: Option<usize>, xctl: bool, pos: Pos) -> R<Ending> {
89 x.unit().programs[index].initialized = false;
90 x.unit().enter(pos)?;
91 let ending = x.run_program(program, index, area, xctl);
92 let unit = x.unit();
93 unit.depth -= 1;
94 unit.programs[index].active = false;
95 ending
96}
97
98pub(super) fn abend<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, abcode: Option<&Datum<P, O, S>>, cancel: bool) -> R<Flow> {
100 let code = text(x, abcode, at.pos)?.unwrap_or_else(|| "????".into());
101 if cancel && let Some(task) = x.unit().cics.as_mut() {
102 task.cancelling = true;
103 }
104 Err(Abend { message: format!("EXEC CICS ABEND ABCODE({code})"), code: AbendCode::Cics(code), pos: at.pos, file: None })
105}
106
107fn interceptable(abend: &Abend) -> Option<String> {
110 match &abend.code {
111 AbendCode::Check(_) | AbendCode::Protection => Some("ASRA".into()),
112 AbendCode::Cics(code) if !code.starts_with("ASP") && code != "APSJ" => Some(code.clone()),
113 _ => None,
114 }
115}
116
117pub fn abend_exit<H, L: Loader<H>>(unit: &mut RunUnit<'_, H, L>, handlers: &mut Handlers, abend: &Abend) -> Option<ExitTarget> {
121 let code = interceptable(abend)?;
122 let task = unit.cics.as_mut().filter(|t| !t.cancelling)?;
123 let exit = handlers.abend.as_mut().filter(|e| e.active)?;
124 exit.active = false;
125 task.abcode = Some(code);
126 Some(exit.target.clone())
127}
128
129pub fn enter_exit_program<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, name: &str, pos: Pos) -> R<Ending> {
132 let apct = |message: String| Abend { code: AbendCode::Cics("APCT".into()), message, pos, file: None };
133 let index = match x.unit().load(name) {
134 Ok(i) => i,
135 Err(LoadError::NotFound) => return Err(apct(format!("HANDLE ABEND PROGRAM({name}): no program of the name"))),
136 Err(LoadError::Compile(m)) => return Err(Abend::ironwork(format!("HANDLE ABEND PROGRAM({name}): {m}"), pos)),
137 };
138 let Some(program) = x.unit().programs[index].compiled.clone() else {
139 return Err(apct(format!("HANDLE ABEND PROGRAM({name}): the task's first program is already running")));
140 };
141 let page = page(x);
142 eib_text(x.unit(), page, EIBRSRCE, 8, name);
143 let commarea = x.commarea();
144 enter(x, program, index, commarea, false, pos)
145}
146
147pub(super) fn handle_condition<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, labels: &[(Condition, Option<ParaId>)]) -> R<Flow> {
148 let conditions = &mut x.handlers().conditions;
149 for &(condition, label) in labels {
150 match label {
151 Some(p) => conditions.insert(condition, Handler::Label(p)),
152 None => conditions.remove(&condition),
153 };
154 }
155 ok(x, at)
156}
157
158pub(super) fn ignore_condition<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, ignored: &[Condition]) -> R<Flow> {
159 let conditions = &mut x.handlers().conditions;
160 for &condition in ignored {
161 conditions.insert(condition, Handler::Ignore);
162 }
163 ok(x, at)
164}
165
166pub(super) fn push_handle<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>) -> R<Flow> {
168 let handlers = x.handlers();
169 let saved = (std::mem::take(&mut handlers.conditions), handlers.abend.take());
170 handlers.stack.push(saved);
171 ok(x, at)
172}
173
174pub(super) fn pop_handle<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>) -> R<Flow> {
175 let handlers = x.handlers();
176 match handlers.stack.pop() {
177 Some((conditions, abend)) => {
178 handlers.conditions = conditions;
179 handlers.abend = abend;
180 ok(x, at)
181 }
182 None => raise(x, at, Condition::INVREQ, 0),
183 }
184}
185
186pub(super) fn handle_abend<'w, P: Copy, O, S>(x: &mut impl CicsHost<'w, P, O, S>, at: &At<P, O, S>, program: Option<&Datum<P, O, S>>, label: Option<ParaId>, reset: bool) -> R<Flow> {
190 let target = match (text(x, program, at.pos)?, label) {
191 (Some(name), _) => {
192 let name = name.to_ascii_uppercase();
193 match x.unit().load(&name) {
194 Ok(_) => Some(ExitTarget::Program(name)),
195 Err(LoadError::NotFound) => return raise(x, at, Condition::PGMIDERR, 1),
196 Err(LoadError::Compile(m)) => return Err(Abend::ironwork(format!("EXEC CICS HANDLE ABEND PROGRAM({name}): {m}"), at.pos)),
197 }
198 }
199 (None, Some(p)) => Some(ExitTarget::Label(p)),
200 (None, None) => None,
201 };
202 let handlers = x.handlers();
203 match (target, &mut handlers.abend) {
204 (Some(target), exit) => *exit = Some(AbendExit { target, active: true }),
205 (None, Some(exit)) => exit.active = reset,
206 (None, None) => {}
207 }
208 ok(x, at)
209}