996
|
1 /*****************************************************************************
|
|
2
|
|
3 NAME
|
|
4 ick_ec.c -- external call support between C and C-INTERCAL
|
|
5
|
|
6 LICENSE TERMS
|
|
7 Copyright (C) 2008 Alex Smith
|
|
8
|
|
9 This program is free software; you can redistribute it and/or modify
|
|
10 it under the terms of the GNU General Public License as published by
|
|
11 the Free Software Foundation; either version 2 of the License, or
|
|
12 (at your option) any later version.
|
|
13
|
|
14 This program is distributed in the hope that it will be useful,
|
|
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 GNU General Public License for more details.
|
|
18
|
|
19 You should have received a copy of the GNU General Public License
|
|
20 along with this program; if not, write to the Free Software
|
|
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
22
|
|
23 ***************************************************************************/
|
|
24
|
|
25 /* Implement the INTERCAL flow control operators in C, except for ABSTAIN
|
|
26 and REINSTATE. Worryingly, this is freestanding-legal and needs no C
|
|
27 headers other than setjmp.h and stdint.h. (The printfs are just for
|
|
28 +printflow debug output and don't effect the code flow.) */
|
|
29
|
|
30 #define ICK_EC 1
|
|
31 #include "ick_lose.h"
|
|
32 #include "config.h"
|
|
33 #include "abcess.h" /* must come before ick_ec.h */
|
|
34 #include "ick_ec.h"
|
|
35 #include <setjmp.h>
|
|
36
|
|
37 /* Checkmode values, for reference:
|
|
38 * 0: not using checkmode
|
|
39 * 1: looking for a COME FROM or NEXT FROM, or COME FROM found
|
|
40 * 2: looking for a label to NEXT to or goto
|
|
41 * 3: NEXT FROM found
|
|
42 * 4: no suitable target found
|
|
43 * 5: do a resume(1) if this reaches ick_allecfuncs
|
|
44 * 6: starting up, run your init code
|
|
45 */
|
|
46 int ick_global_checkmode = 0;
|
|
47 /* ick_global_linelabel's marked as volatile to avoid problems with
|
|
48 optimisation of assignments to it near setjmps. */
|
|
49 unsigned long ick_global_linelabel;
|
|
50 static int ick_forgetamount = 0;
|
|
51 unsigned long ick_global_goto;
|
|
52 void* ick_global_createdata;
|
|
53
|
|
54 extern ick_overop* ick_oo_twospots;
|
|
55
|
|
56 /* Do a CREATEd operator check and call. */
|
|
57 /*@maynotreturn@*/ uint32_t ick_dounop(char* unopstr,
|
|
58 uint32_t arg1,
|
|
59 uint32_t arg2,
|
|
60 int emitlineno,
|
|
61 unsigned long vi1,
|
|
62 unsigned long vi2,
|
|
63 unsigned long vi3,
|
|
64 ick_type32 (*og1)(ick_type32),
|
|
65 ick_type32 (*og2)(ick_type32),
|
|
66 ick_type32 (*og3)(ick_type32),
|
|
67 void (*os1)(ick_type32, void(*)()),
|
|
68 void (*os2)(ick_type32, void(*)()),
|
|
69 void (*os3)(ick_type32, void(*)()),
|
|
70 /*@observer@*/ const char* errstr)
|
|
71 {
|
|
72 int st;
|
|
73 uint32_t rv;
|
|
74 st = ick_jicmatch(unopstr);
|
|
75 if(st)
|
|
76 {
|
|
77 ick_createdata icd[3];
|
|
78 icd[0].width=16; icd[1].width=16; icd[2].width=16;
|
|
79 icd[0].isarray=0; icd[1].isarray=0; icd[2].isarray=0;
|
|
80 icd[0].varnumber=1601; icd[1].varnumber=1602; icd[2].varnumber=1603;
|
|
81 icd[0].accessors.get=og1;icd[1].accessors.get=og2;icd[2].accessors.get=og3;
|
|
82 icd[0].accessors.set=os1;icd[1].accessors.set=os2;icd[2].accessors.set=os3;
|
|
83 icd[0].value=arg1; icd[1].value=arg2; icd[2].value=0;
|
|
84
|
|
85 ick_stash(ick_TWOSPOT, vi1, ick_twospots+vi1, ick_oo_twospots);
|
|
86 ick_stash(ick_TWOSPOT, vi2, ick_twospots+vi2, ick_oo_twospots);
|
|
87 ick_stash(ick_TWOSPOT, vi3, ick_twospots+vi3, ick_oo_twospots);
|
|
88 ick_oo_twospots[vi1]=icd[0].accessors;
|
|
89 ick_oo_twospots[vi2]=icd[1].accessors;
|
|
90 ick_oo_twospots[vi3]=icd[2].accessors;
|
|
91 ick_global_createdata=icd;
|
|
92 ick_dogoto(st, emitlineno, 1);
|
|
93 rv = og3(ick_twospots[vi3]);
|
|
94 ick_retrieve(ick_twospots+vi1, ick_TWOSPOT, vi1,
|
|
95 ick_twoforget[vi1], ick_oo_twospots);
|
|
96 ick_retrieve(ick_twospots+vi2, ick_TWOSPOT, vi2,
|
|
97 ick_twoforget[vi2], ick_oo_twospots);
|
|
98 ick_retrieve(ick_twospots+vi3, ick_TWOSPOT, vi3,
|
|
99 ick_twoforget[vi3], ick_oo_twospots);
|
|
100 }
|
|
101 else
|
|
102 ick_lose(IE000, emitlineno, errstr);
|
|
103 return rv;
|
|
104 }
|
|
105
|
|
106 /* Do a NEXT or goto. Gotos don't work with an empty NEXT stack, but that's
|
|
107 trivial to correct by doing a NEXT at the start of the program. */
|
|
108 /*@maynotreturn@*/ void ick_dogoto(unsigned long linelabel,
|
|
109 int emitlineno, int isnext)
|
|
110 {
|
|
111 /* OK, so auto is never necessary, but the point here is that this is being
|
|
112 stored on the stack deliberately so that it can be recalled if this
|
|
113 procedure is longjmped to. */
|
|
114 auto volatile int nextlevel = ick_nextindex;
|
|
115 if(ick_printflow&&linelabel<=65535&&isnext)
|
|
116 fprintf(stderr,"[next:%lu]",linelabel);
|
|
117 ick_global_checkmode = 2; /* look for linelabels */
|
|
118 ick_global_linelabel = linelabel;
|
|
119 /* If there was a FORGET earlier, implement it now, by removing the relevant
|
|
120 NEXT stack entries. Unfortunately, we can't remove them from the main C
|
|
121 stack if we care about the return address of this NEXT. If this is a GOTO
|
|
122 rather than a NEXT, it's safe to go back to the top NEXT on the NEXT stack
|
|
123 and redo it with a different target (because the return address will still
|
|
124 be correct), and all FORGETs work using an implied GOTO. (COME FROMs work
|
|
125 by telling the suckpoint to GOTO them.) */
|
|
126 if(!isnext)
|
|
127 {
|
|
128 if(ick_printflow&&ick_forgetamount)
|
|
129 fprintf(stderr,"[forget:%d]",ick_forgetamount);
|
|
130 else if(ick_printflow&&linelabel<=65535)
|
|
131 fprintf(stderr,"[goto:%lu]",linelabel);
|
|
132 nextlevel = ick_nextindex -= ick_forgetamount + 1;
|
|
133 ick_forgetamount = 0;
|
|
134 if(ick_nextindex < 0) ick_nextindex = 0;
|
|
135 longjmp(ick_next_jmpbufs[ick_nextindex],2);
|
|
136 }
|
|
137 else if(ick_nextindex==81)
|
|
138 ick_lose(IE123, emitlineno, (const char*)NULL);
|
|
139 /* longjmp return codes: 1 = resume, 2 = redo to a different target */
|
|
140 if(setjmp(ick_next_jmpbufs[ick_nextindex])==1)
|
|
141 {
|
|
142 /* Returning from the next. Clean up the next stack, just in
|
|
143 case the callee didn't. */
|
|
144 ick_nextindex = nextlevel;
|
|
145 ick_global_checkmode=0;
|
|
146 return;
|
|
147 }
|
|
148 ick_nextindex = nextlevel; /* in case we were longjmped to */
|
|
149 ++ick_nextindex;
|
|
150 ick_allecfuncs();
|
|
151 /* If the checkmode is 4, we didn't find a target. */
|
|
152 if(ick_global_checkmode == 4)
|
|
153 ick_lose(IE129, emitlineno, (const char*) NULL);
|
|
154 /* Otherwise, the function called return(). */
|
|
155 ick_doresume(1,emitlineno);
|
|
156 }
|
|
157
|
|
158 /* Schedule a FORGET. The actual FORGET is performed at the next GOTO,
|
|
159 which should happen immediately. */
|
|
160 void ick_scheduleforget(unsigned short amount)
|
|
161 {
|
|
162 ick_forgetamount += amount;
|
|
163 if(ick_forgetamount >= ick_nextindex)
|
|
164 ick_forgetamount = ick_nextindex-1;
|
|
165 }
|
|
166
|
|
167 /* Resume to a previous NEXT stack entry. */
|
|
168 /*@noreturn@*/ void ick_doresume(unsigned short amount, int emitlineno)
|
|
169 {
|
|
170 if(ick_printflow) fprintf(stderr,"[resume:%hu]",amount);
|
|
171 if(ick_forgetamount) ick_lose(IE778, emitlineno, (const char *)NULL);
|
|
172 if(!amount) ick_lose(IE621, emitlineno, (const char *)NULL);
|
|
173 ick_nextindex -= amount;
|
|
174 if(ick_nextindex < 1) /* the very first NEXT can't be RESUMEd to */
|
|
175 ick_lose(IE632, emitlineno, (const char *)NULL);
|
|
176 longjmp(ick_next_jmpbufs[ick_nextindex],1);
|
|
177 }
|
|
178
|
|
179 /* Run any ick_startup blocks. */
|
|
180 void ick_runstartups(void)
|
|
181 {
|
|
182 ick_global_checkmode = 6;
|
|
183 ick_allecfuncs();
|
|
184 ick_global_checkmode = 0;
|
|
185 }
|
|
186
|
|
187 /* Check to see if anything tries to steal control from a suckpoint.
|
|
188 * This allows line labels > 65535, but bear in mind that such high
|
|
189 * line labels cannot be COME FROM or NEXTed FROM or to by the user.
|
|
190 * ('High' line labels are used by the implementation to implement
|
|
191 * this function, but only as goto targets.)
|
|
192 */
|
|
193 /*@maynotreturn@*/ void ick_checksuckpoint(unsigned long linelabel)
|
|
194 {
|
|
195 /* Check to see if any suckpoints aim here; if not, return. */
|
|
196 ick_global_checkmode=1;
|
|
197 ick_global_goto=0;
|
|
198 ick_global_linelabel=linelabel;
|
|
199 ick_allecfuncs();
|
|
200 if(!ick_global_goto) {ick_global_checkmode=0; return;}
|
|
201 /* If this was a COME FROM, goto it. */
|
|
202 if(ick_global_checkmode == 1)
|
|
203 {
|
|
204 if(ick_printflow) fprintf(stderr,"[comefrom:%lu]",ick_global_linelabel);
|
|
205 ick_dogoto(ick_global_goto,-1,0); /* GOTO */
|
|
206 }
|
|
207 else if(ick_global_checkmode == 3)
|
|
208 {
|
|
209 if(ick_printflow) fprintf(stderr,"[nextfrom:%lu]",ick_global_linelabel);
|
|
210 ick_dogoto(ick_global_goto,-1,1); /* NEXT */
|
|
211 return; /* we were RESUMEd to */
|
|
212 }
|
|
213 /* This line should be unreachable. */
|
|
214 ick_lose(IE778, -1, (const char *)NULL);
|
|
215 }
|
|
216
|
|
217 uint16_t ick_getonespot(unsigned short extername)
|
|
218 {
|
|
219 int i=-1;
|
|
220 while(++i,1)
|
|
221 {
|
|
222 if(ick_ec_vars[i].ick_ec_vartype==ICK_EC_VARS_END) break;
|
|
223 if(ick_ec_vars[i].ick_ec_vartype==ick_ONESPOT)
|
|
224 if(ick_ec_vars[i].ick_ec_extername==extername)
|
|
225 return ick_onespots[ick_ec_vars[i].ick_ec_intername];
|
|
226 }
|
|
227 ick_lose(IE200,-1,(const char*)NULL);
|
|
228 }
|
|
229
|
|
230 void ick_setonespot(unsigned short extername, uint16_t value)
|
|
231 {
|
|
232 int i=-1;
|
|
233 while(++i,1)
|
|
234 {
|
|
235 if(ick_ec_vars[i].ick_ec_vartype==ICK_EC_VARS_END) break;
|
|
236 if(ick_ec_vars[i].ick_ec_vartype==ick_ONESPOT)
|
|
237 if(ick_ec_vars[i].ick_ec_extername==extername)
|
|
238 {
|
|
239 if(ick_oneforget[ick_ec_vars[i].ick_ec_intername]) return;
|
|
240 ick_onespots[ick_ec_vars[i].ick_ec_intername]=value;
|
|
241 return;
|
|
242 }
|
|
243 }
|
|
244 ick_lose(IE200,-1,(const char*)NULL);
|
|
245 }
|
|
246
|
|
247 uint32_t ick_gettwospot(unsigned short extername)
|
|
248 {
|
|
249 int i=-1;
|
|
250 while(++i,1)
|
|
251 {
|
|
252 if(ick_ec_vars[i].ick_ec_vartype==ICK_EC_VARS_END) break;
|
|
253 if(ick_ec_vars[i].ick_ec_vartype==ick_TWOSPOT)
|
|
254 if(ick_ec_vars[i].ick_ec_extername==extername)
|
|
255 return ick_twospots[ick_ec_vars[i].ick_ec_intername];
|
|
256 }
|
|
257 ick_lose(IE200,-1,(const char*)NULL);
|
|
258 }
|
|
259
|
|
260 void ick_settwospot(unsigned short extername, uint32_t value)
|
|
261 {
|
|
262 int i=-1;
|
|
263 while(++i,1)
|
|
264 {
|
|
265 if(ick_ec_vars[i].ick_ec_vartype==ICK_EC_VARS_END) break;
|
|
266 if(ick_ec_vars[i].ick_ec_vartype==ick_TWOSPOT)
|
|
267 if(ick_ec_vars[i].ick_ec_extername==extername)
|
|
268 {
|
|
269 if(ick_twoforget[ick_ec_vars[i].ick_ec_intername]) return;
|
|
270 ick_twospots[ick_ec_vars[i].ick_ec_intername]=value;
|
|
271 return;
|
|
272 }
|
|
273 }
|
|
274 ick_lose(IE200,-1,(const char*)NULL);
|
|
275 }
|
|
276
|
|
277 /* Register a CREATE target. This is just a wrapper for
|
|
278 ick_registercreation that gets around scoping problems. */
|
|
279 void ick_create(const char* sig, unsigned long target)
|
|
280 {
|
|
281 ick_registercreation(sig, target);
|
|
282 }
|
|
283
|
|
284 /* Accessor and mutator functions for CREATE data */
|
|
285 int ick_c_i_width(int argpos)
|
|
286 {
|
|
287 return ((ick_createdata*)ick_global_createdata)[argpos].width;
|
|
288 }
|
|
289 int ick_c_i_isarray(int argpos)
|
|
290 {
|
|
291 return ((ick_createdata*)ick_global_createdata)[argpos].isarray;
|
|
292 }
|
|
293 unsigned short ick_c_i_varnumber(int argpos)
|
|
294 {
|
|
295 return ((ick_createdata*)ick_global_createdata)[argpos].varnumber;
|
|
296 }
|
|
297 uint32_t ick_c_i_value(int argpos)
|
|
298 {
|
|
299 return (uint32_t)(((ick_createdata*)ick_global_createdata)[argpos].value);
|
|
300 }
|
|
301 uint32_t ick_c_i_getvalue(int argpos)
|
|
302 {
|
|
303 if(!(((ick_createdata*)ick_global_createdata)[argpos].accessors.get))
|
|
304 return ick_c_i_value(argpos);
|
|
305 return ((ick_createdata*)ick_global_createdata)[argpos].accessors.
|
|
306 get(ick_c_i_value(argpos));
|
|
307 }
|
|
308 void ick_c_i_setvalue(int argpos, uint32_t newval)
|
|
309 {
|
|
310 if(((ick_createdata*)ick_global_createdata)[argpos].accessors.set)
|
|
311 ((ick_createdata*)ick_global_createdata)[argpos].accessors.
|
|
312 set(newval,NULL);
|
|
313 }
|