996
|
1 /* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
|
|
2 *
|
|
3 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
|
|
4 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
|
|
5 *
|
|
6 * This program is free software: you can redistribute it and/or modify
|
|
7 * it under the terms of the GNU General Public License as published by
|
|
8 * the Free Software Foundation, either version 3 of the License, or
|
|
9 * (at the proxy's option) any later version. Arvid Norlander is a
|
|
10 * proxy who can decide which future versions of the GNU General Public
|
|
11 * License can be used.
|
|
12 *
|
|
13 * This program is distributed in the hope that it will be useful,
|
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 * GNU General Public License for more details.
|
|
17 *
|
|
18 * You should have received a copy of the GNU General Public License
|
|
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20 */
|
|
21
|
|
22 #include "global.h"
|
|
23 #include "stack.h"
|
|
24 #include "vector.h"
|
|
25 #include "ip.h"
|
|
26 #include "settings.h"
|
|
27 #include "diagnostic.h"
|
|
28
|
|
29 #include <assert.h>
|
|
30 #include <string.h> /* memcpy, memset */
|
|
31
|
|
32 /// How many new items to allocate in one go?
|
|
33 #define ALLOCCHUNKSIZE 4096
|
|
34
|
|
35 /******************************
|
|
36 * Constructor and destructor *
|
|
37 ******************************/
|
|
38
|
|
39 FUNGE_ATTR_FAST funge_stack * stack_create(void)
|
|
40 {
|
|
41 funge_stack * tmp = (funge_stack*)cf_malloc(sizeof(funge_stack));
|
|
42 if (FUNGE_UNLIKELY(!tmp))
|
|
43 return NULL;
|
|
44 tmp->entries = (funge_cell*)cf_malloc_noptr(ALLOCCHUNKSIZE * sizeof(funge_cell));
|
|
45 if (FUNGE_UNLIKELY(!tmp->entries)) {
|
|
46 cf_free(tmp);
|
|
47 return NULL;
|
|
48 }
|
|
49 tmp->size = ALLOCCHUNKSIZE;
|
|
50 tmp->top = 0;
|
|
51 return tmp;
|
|
52 }
|
|
53
|
|
54 FUNGE_ATTR_FAST void stack_free(funge_stack * stack)
|
|
55 {
|
|
56 if (FUNGE_UNLIKELY(!stack))
|
|
57 return;
|
|
58 if (FUNGE_LIKELY(stack->entries != NULL)) {
|
|
59 cf_free(stack->entries);
|
|
60 stack->entries = NULL;
|
|
61 }
|
|
62 cf_free(stack);
|
|
63 }
|
|
64
|
|
65 #ifdef CONCURRENT_FUNGE
|
|
66 // Used for concurrency
|
|
67 FUNGE_ATTR_FAST FUNGE_ATTR_MALLOC FUNGE_ATTR_NONNULL FUNGE_ATTR_WARN_UNUSED
|
|
68 static inline funge_stack * stack_duplicate(const funge_stack * old)
|
|
69 {
|
|
70 funge_stack * tmp = (funge_stack*)cf_malloc(sizeof(funge_stack));
|
|
71 if (FUNGE_UNLIKELY(!tmp))
|
|
72 return NULL;
|
|
73 tmp->entries = (funge_cell*)cf_malloc_noptr((old->top + 1) * sizeof(funge_cell));
|
|
74 if (FUNGE_UNLIKELY(!tmp->entries)) {
|
|
75 cf_free(tmp);
|
|
76 return NULL;
|
|
77 }
|
|
78 tmp->size = old->top + 1;
|
|
79 tmp->top = old->top;
|
|
80 // Not sure if memcpy() on 0 is well defined, so lets be careful.
|
|
81 if (tmp->top != 0)
|
|
82 memcpy(tmp->entries, old->entries, sizeof(funge_cell) * tmp->top);
|
|
83 return tmp;
|
|
84 }
|
|
85 #endif
|
|
86
|
|
87 FUNGE_ATTR_FAST FUNGE_ATTR_COLD FUNGE_ATTR_NORET
|
|
88 static void stack_oom(void)
|
|
89 {
|
|
90 DIAG_OOM("Failed to allocate enough memory for new stack items");
|
|
91 }
|
|
92
|
|
93 /*************************************
|
|
94 * Basic push/pop/peeks and prealloc *
|
|
95 *************************************/
|
|
96
|
|
97 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
98 static inline void stack_prealloc_space(funge_stack * restrict stack, size_t minfree)
|
|
99 {
|
|
100 if ((stack->top + minfree) >= stack->size) {
|
|
101 size_t newsize = stack->size + minfree;
|
|
102 // Round upwards to whole ALLOCCHUNKSIZEed blocks.
|
|
103 newsize += ALLOCCHUNKSIZE - (newsize % ALLOCCHUNKSIZE);
|
|
104 stack->entries = (funge_cell*)cf_realloc(stack->entries, newsize * sizeof(funge_cell));
|
|
105 if (FUNGE_UNLIKELY(!stack->entries)) {
|
|
106 stack_oom();
|
|
107 }
|
|
108 stack->size = newsize;
|
|
109 }
|
|
110 }
|
|
111
|
|
112 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
113 static inline void stack_push_no_check(funge_stack * restrict stack, funge_cell value)
|
|
114 {
|
|
115 // This should only be used if that is true...
|
|
116 assert(stack->top < stack->size);
|
|
117 stack->entries[stack->top] = value;
|
|
118 stack->top++;
|
|
119 }
|
|
120
|
|
121 FUNGE_ATTR_FAST void stack_push(funge_stack * restrict stack, funge_cell value)
|
|
122 {
|
|
123 assert(stack != NULL);
|
|
124 assert(stack->top <= stack->size);
|
|
125
|
|
126 // Do we need to realloc?
|
|
127 if (FUNGE_UNLIKELY(stack->top == stack->size)) {
|
|
128 stack->entries = (funge_cell*)cf_realloc(stack->entries, (stack->size + ALLOCCHUNKSIZE) * sizeof(funge_cell));
|
|
129 if (FUNGE_UNLIKELY(!stack->entries)) {
|
|
130 stack_oom();
|
|
131 }
|
|
132 stack->size += ALLOCCHUNKSIZE;
|
|
133 }
|
|
134 stack->entries[stack->top] = value;
|
|
135 stack->top++;
|
|
136 }
|
|
137
|
|
138 FUNGE_ATTR_FAST inline funge_cell stack_pop(funge_stack * restrict stack)
|
|
139 {
|
|
140 assert(stack != NULL);
|
|
141
|
|
142 if (stack->top == 0)
|
|
143 return 0;
|
|
144
|
|
145 return stack->entries[--stack->top];
|
|
146 }
|
|
147
|
|
148 FUNGE_ATTR_FAST void stack_discard(funge_stack * restrict stack, size_t n)
|
|
149 {
|
|
150 assert(stack != NULL);
|
|
151
|
|
152 if (stack->top == 0) {
|
|
153 return;
|
|
154 } else if (stack->top > n) {
|
|
155 stack->top -= n;
|
|
156 } else {
|
|
157 stack->top = 0;
|
|
158 }
|
|
159 }
|
|
160
|
|
161
|
|
162 FUNGE_ATTR_FAST inline funge_cell stack_peek(const funge_stack * restrict stack)
|
|
163 {
|
|
164 assert(stack != NULL);
|
|
165
|
|
166 if (stack->top == 0) {
|
|
167 return 0;
|
|
168 } else {
|
|
169 return stack->entries[stack->top - 1];
|
|
170 }
|
|
171 }
|
|
172
|
|
173
|
|
174 FUNGE_ATTR_FAST inline funge_cell stack_get_index(const funge_stack * restrict stack, size_t index)
|
|
175 {
|
|
176 assert(stack != NULL);
|
|
177
|
|
178 if (stack->top == 0) {
|
|
179 return 0;
|
|
180 } else if (stack->top <= index) {
|
|
181 return 0;
|
|
182 } else {
|
|
183 return stack->entries[index - 1];
|
|
184 }
|
|
185 }
|
|
186
|
|
187 FUNGE_ATTR_FAST inline size_t stack_strlen(const funge_stack * restrict stack)
|
|
188 {
|
|
189 // TODO: Maybe scan two cells at once if we are using 32-bit cells on a
|
|
190 // 64-bit system?
|
|
191 for (size_t i = stack->top; i > 0; i--) {
|
|
192 if (stack->entries[i - 1] == 0)
|
|
193 return stack->top - i;
|
|
194 }
|
|
195 return stack->top;
|
|
196 }
|
|
197
|
|
198
|
|
199 /********************************
|
|
200 * Push and pop for data types. *
|
|
201 ********************************/
|
|
202
|
|
203 FUNGE_ATTR_FAST void stack_push_vector(funge_stack * restrict stack, const funge_vector * restrict value)
|
|
204 {
|
|
205 // TODO: Optimise
|
|
206 stack_push(stack, value->x);
|
|
207 stack_push(stack, value->y);
|
|
208 }
|
|
209
|
|
210 FUNGE_ATTR_FAST funge_vector stack_pop_vector(funge_stack * restrict stack)
|
|
211 {
|
|
212 // TODO: Optimise
|
|
213 funge_cell x, y;
|
|
214 y = stack_pop(stack);
|
|
215 x = stack_pop(stack);
|
|
216 return (funge_vector) { .x = x, .y = y };
|
|
217 }
|
|
218
|
|
219 FUNGE_ATTR_FAST void stack_push_string(funge_stack * restrict stack, const unsigned char * restrict str, size_t len)
|
|
220 {
|
|
221 assert(str != NULL);
|
|
222 assert(stack != NULL);
|
|
223 // Increment it once or it won't work
|
|
224 stack_prealloc_space(stack, len + 1);
|
|
225 {
|
|
226 const size_t top = stack->top + len;
|
|
227 for (ssize_t i = (ssize_t)len; i >= 0; i--)
|
|
228 stack->entries[top - (size_t)i] = str[i];
|
|
229 stack->top += len + 1;
|
|
230 }
|
|
231 }
|
|
232
|
|
233 FUNGE_ATTR_FAST unsigned char *stack_pop_string(funge_stack * restrict stack, size_t * restrict len)
|
|
234 {
|
|
235 funge_cell c;
|
|
236 size_t index = 0;
|
|
237 // FIXME: This may very likely be more than is needed.
|
|
238 unsigned char * buf = (unsigned char*)cf_malloc_noptr((stack->top + 1) * sizeof(char));
|
|
239 if (FUNGE_UNLIKELY(!buf)) {
|
|
240 *len = 0;
|
|
241 return NULL;
|
|
242 }
|
|
243
|
|
244 while ((c = stack_pop(stack)) != '\0') {
|
|
245 buf[index++] = (unsigned char)c;
|
|
246 }
|
|
247 buf[index] = '\0';
|
|
248 if (len)
|
|
249 *len = index;
|
|
250 return buf;
|
|
251 }
|
|
252
|
|
253 #ifdef UNUSED
|
|
254 FUNGE_ATTR_FAST unsigned char *stack_pop_sized_string(funge_stack * restrict stack, size_t len)
|
|
255 {
|
|
256 unsigned char * restrict x = (unsigned char*)cf_malloc_noptr((len + 1) * sizeof(char));
|
|
257 if (FUNGE_UNLIKELY(!x))
|
|
258 return NULL;
|
|
259
|
|
260 for (size_t i = 0; i < len; i++) {
|
|
261 x[i] = (unsigned char)stack_pop(stack);
|
|
262 }
|
|
263 x[len + 1] = '\0';
|
|
264 return x;
|
|
265 }
|
|
266 #endif
|
|
267
|
|
268
|
|
269 /***************
|
|
270 * Other stuff *
|
|
271 ***************/
|
|
272 FUNGE_ATTR_FAST void stack_dup_top(funge_stack * restrict stack)
|
|
273 {
|
|
274 // TODO: Optimise instead of doing it this way
|
|
275 funge_cell tmp = stack_peek(stack);
|
|
276 stack_push(stack, tmp);
|
|
277 // If it was empty, push a second zero.
|
|
278 if (stack->top == 1)
|
|
279 stack_push(stack, 0);
|
|
280 }
|
|
281
|
|
282 FUNGE_ATTR_FAST void stack_swap_top(funge_stack * restrict stack)
|
|
283 {
|
|
284 // TODO: Optimise instead of doing it this way
|
|
285 funge_cell a, b;
|
|
286 // Well this have to work logically...
|
|
287 a = stack_pop(stack);
|
|
288 b = stack_pop(stack);
|
|
289 stack_prealloc_space(stack, 2);
|
|
290 stack_push_no_check(stack, a);
|
|
291 stack_push_no_check(stack, b);
|
|
292 }
|
|
293
|
|
294
|
|
295 #ifndef NDEBUG
|
|
296 /*************
|
|
297 * Debugging *
|
|
298 *************/
|
|
299
|
|
300
|
|
301 // For use with call in gdb
|
|
302 void stack_dump(const funge_stack * stack) FUNGE_ATTR_UNUSED FUNGE_ATTR_COLD;
|
|
303
|
|
304 void stack_dump(const funge_stack * stack)
|
|
305 {
|
|
306 if (!stack)
|
|
307 return;
|
|
308 fprintf(stderr, "%zu elements:\n", stack->top);
|
|
309 for (size_t i = 0; i < stack->top; i++)
|
|
310 fprintf(stderr, "%" FUNGECELLPRI " ", stack->entries[i]);
|
|
311 fputs("\n", stderr);
|
|
312 }
|
|
313
|
|
314 #endif
|
|
315
|
|
316 #ifndef DISABLE_TRACE
|
|
317 // This is for tracing
|
|
318 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
319 void stack_print_top(const funge_stack * stack)
|
|
320 {
|
|
321 assert(stack != NULL);
|
|
322 if (stack->top == 0) {
|
|
323 fputs("\tStack is empty.\n", stderr);
|
|
324 } else {
|
|
325 fprintf(stderr, "\tStack has %zu elements, top 5 (or less) elements:\n\t\t", stack->top);
|
|
326 for (ssize_t i = (ssize_t)stack->top; (i > 0) && (i > ((ssize_t)stack->top - 5)); i--)
|
|
327 fprintf(stderr, "%" FUNGECELLPRI " ", stack->entries[i-1]);
|
|
328 fputs("\n", stderr);
|
|
329 }
|
|
330 }
|
|
331 #endif
|
|
332
|
|
333 /****************
|
|
334 * Stack-stacks *
|
|
335 ****************/
|
|
336
|
|
337 FUNGE_ATTR_FAST funge_stackstack * stackstack_create(void)
|
|
338 {
|
|
339 funge_stackstack * stackStack;
|
|
340 funge_stack * stack;
|
|
341
|
|
342 stackStack = (funge_stackstack*)cf_malloc(sizeof(funge_stackstack) + sizeof(funge_stack*));
|
|
343 if (FUNGE_UNLIKELY(!stackStack))
|
|
344 return NULL;
|
|
345
|
|
346 stack = stack_create();
|
|
347 if (FUNGE_UNLIKELY(!stack)) {
|
|
348 cf_free(stackStack);
|
|
349 return NULL;
|
|
350 }
|
|
351
|
|
352 stackStack->size = 1;
|
|
353 stackStack->current = 0;
|
|
354 stackStack->stacks[0] = stack;
|
|
355 return stackStack;
|
|
356 }
|
|
357
|
|
358 FUNGE_ATTR_FAST void stackstack_free(funge_stackstack * me)
|
|
359 {
|
|
360 if (FUNGE_UNLIKELY(!me))
|
|
361 return;
|
|
362
|
|
363 for (size_t i = 0; i < me->size; i++)
|
|
364 stack_free(me->stacks[i]);
|
|
365
|
|
366 cf_free(me);
|
|
367 }
|
|
368
|
|
369 #ifdef CONCURRENT_FUNGE
|
|
370 FUNGE_ATTR_FAST funge_stackstack * stackstack_duplicate(const funge_stackstack * restrict old)
|
|
371 {
|
|
372 funge_stackstack * stackStack;
|
|
373
|
|
374 assert(old != NULL);
|
|
375
|
|
376 stackStack = (funge_stackstack*)cf_malloc(sizeof(funge_stackstack) + old->size * sizeof(funge_stack*));
|
|
377 if (FUNGE_UNLIKELY(!stackStack))
|
|
378 return NULL;
|
|
379
|
|
380 for (size_t i = 0; i <= old->current; i++) {
|
|
381 stackStack->stacks[i] = stack_duplicate(old->stacks[i]);
|
|
382 if (FUNGE_UNLIKELY(!stackStack->stacks[i]))
|
|
383 return NULL;
|
|
384 }
|
|
385
|
|
386 stackStack->size = old->size;
|
|
387 stackStack->current = old->current;
|
|
388 return stackStack;
|
|
389 }
|
|
390 #endif
|
|
391
|
|
392
|
|
393 FUNGE_ATTR_FAST static void oom_stackstack(const instructionPointer * restrict ip)
|
|
394 {
|
|
395 diag_warn_format("Out of memory in stack-stack routine at x=%"
|
|
396 FUNGECELLPRI " y=%" FUNGECELLPRI ". Reflecting.",
|
|
397 ip->position.x, ip->position.y);
|
|
398 // Lets hope.
|
|
399 #ifdef CFUN_USE_GC
|
|
400 gc_collect_full();
|
|
401 #endif
|
|
402 }
|
|
403
|
|
404
|
|
405 /**
|
|
406 * Like stack_prealloc_space() above, except it returns false instead of exiting
|
|
407 * on OOM. Needed for stack stack begin/end.
|
|
408 */
|
|
409 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
410 static inline bool stack_prealloc_space_non_fatal(funge_stack * restrict stack, size_t minfree)
|
|
411 {
|
|
412 if ((stack->top + minfree) >= stack->size) {
|
|
413 size_t newsize = stack->size + minfree;
|
|
414 funge_cell* newentries;
|
|
415 // Round upwards to whole ALLOCCHUNKSIZEed blocks.
|
|
416 newsize += ALLOCCHUNKSIZE - (newsize % ALLOCCHUNKSIZE);
|
|
417 newentries = (funge_cell*)cf_realloc(stack->entries, newsize * sizeof(funge_cell));
|
|
418 if (FUNGE_UNLIKELY(!newentries)) {
|
|
419 return false;
|
|
420 }
|
|
421 stack->entries = newentries;
|
|
422 stack->size = newsize;
|
|
423 }
|
|
424 return true;
|
|
425 }
|
|
426
|
|
427 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
428 static inline void stack_zero_fill(funge_stack * restrict stack, size_t count)
|
|
429 {
|
|
430 stack_prealloc_space(stack, count);
|
|
431 memset(&stack->entries[stack->top], 0, count * sizeof(funge_cell));
|
|
432 stack->top += count;
|
|
433 }
|
|
434
|
|
435 // This does an in-order bulk copy of count elements between two stacks.
|
|
436 FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
|
|
437 static inline void stack_bulk_copy(funge_stack * restrict dest, const funge_stack * restrict src, size_t count)
|
|
438 {
|
|
439 stack_prealloc_space(dest, count);
|
|
440
|
|
441 // Figure out if we were asked to copy more items than actually exists:
|
|
442 if (count > src->top) {
|
|
443 // Push some initial zeros then.
|
|
444 size_t zero_count = count - src->top;
|
|
445 count -= zero_count;
|
|
446 stack_zero_fill(dest, zero_count);
|
|
447 }
|
|
448
|
|
449 // memcpy the rest.
|
|
450 memcpy(&dest->entries[dest->top], &src->entries[src->top - count], count*sizeof(funge_cell));
|
|
451 dest->top += count;
|
|
452 }
|
|
453
|
|
454 FUNGE_ATTR_FAST
|
|
455 bool stackstack_begin(instructionPointer * restrict ip, funge_cell count, const funge_vector * restrict storageOffset)
|
|
456 {
|
|
457 funge_stackstack *stackStack;
|
|
458 funge_stack *TOSS, *SOSS;
|
|
459
|
|
460 assert(ip != NULL);
|
|
461 assert(storageOffset != NULL);
|
|
462
|
|
463 // Set up variables
|
|
464 stackStack = ip->stackstack;
|
|
465
|
|
466 TOSS = stack_create();
|
|
467 if (FUNGE_UNLIKELY(!TOSS)) {
|
|
468 oom_stackstack(ip);
|
|
469 return false;
|
|
470 }
|
|
471 // Allocate enough space on the TOSS and reflect if not.
|
|
472 // This is count + 2 (storage offset)
|
|
473 if (FUNGE_UNLIKELY(!stack_prealloc_space_non_fatal(TOSS, ABS(count) + 2))) {
|
|
474 stack_free(TOSS);
|
|
475 oom_stackstack(ip);
|
|
476 return false;
|
|
477 }
|
|
478
|
|
479 // Extend by one
|
|
480 stackStack = cf_realloc(stackStack, sizeof(funge_stackstack) + (stackStack->size + 1) * sizeof(funge_stack*));
|
|
481 if (FUNGE_UNLIKELY(!stackStack)) {
|
|
482 stack_free(TOSS);
|
|
483 oom_stackstack(ip);
|
|
484 return false;
|
|
485 }
|
|
486 SOSS = stackStack->stacks[stackStack->current];
|
|
487
|
|
488 stackStack->size++;
|
|
489 stackStack->current++;
|
|
490 stackStack->stacks[stackStack->current] = TOSS;
|
|
491
|
|
492 if (count > 0) {
|
|
493 stack_bulk_copy(TOSS, SOSS, (size_t)count);
|
|
494 // Make it into a move.
|
|
495 if ((size_t)count > SOSS->top)
|
|
496 SOSS->top = 0;
|
|
497 else
|
|
498 SOSS->top -= (size_t)count;
|
|
499 } else if (count < 0) {
|
|
500 stack_zero_fill(SOSS, (size_t)(-count));
|
|
501 }
|
|
502 stack_push_vector(SOSS, &ip->storageOffset);
|
|
503 ip->storageOffset.x = storageOffset->x;
|
|
504 ip->storageOffset.y = storageOffset->y;
|
|
505 ip->stack = TOSS;
|
|
506 ip->stackstack = stackStack;
|
|
507 return true;
|
|
508 }
|
|
509
|
|
510
|
|
511 FUNGE_ATTR_FAST bool stackstack_end(instructionPointer * restrict ip, funge_cell count)
|
|
512 {
|
|
513 funge_stack *TOSS, *SOSS;
|
|
514 funge_stackstack *stackStack;
|
|
515 funge_vector storageOffset;
|
|
516
|
|
517 assert(ip != NULL);
|
|
518
|
|
519 // Set up variables
|
|
520 stackStack = ip->stackstack;
|
|
521 TOSS = stackStack->stacks[stackStack->current];
|
|
522 SOSS = stackStack->stacks[stackStack->current - 1];
|
|
523 storageOffset = stack_pop_vector(SOSS);
|
|
524 if (count > 0) {
|
|
525 // Since TOSS is discarded there is no need to update it's top pointer.
|
|
526 stack_bulk_copy(SOSS, TOSS, (size_t)count);
|
|
527 } else if (count < 0) {
|
|
528 stack_discard(SOSS, (size_t)(-count));
|
|
529 }
|
|
530 ip->storageOffset.x = storageOffset.x;
|
|
531 ip->storageOffset.y = storageOffset.y;
|
|
532
|
|
533 ip->stack = SOSS;
|
|
534 // FIXME: Maybe we shouldn't realloc here to reduce overhead.
|
|
535 // Make it one smaller
|
|
536 stackStack = (funge_stackstack*)cf_realloc(stackStack, sizeof(funge_stackstack) + (stackStack->size - 1) * sizeof(funge_stack*));
|
|
537 if (FUNGE_UNLIKELY(!stackStack)) {
|
|
538 oom_stackstack(ip);
|
|
539 return false;
|
|
540 }
|
|
541 stackStack->size--;
|
|
542 stackStack->current--;
|
|
543 ip->stackstack = stackStack;
|
|
544 stack_free(TOSS);
|
|
545 return true;
|
|
546 }
|
|
547
|
|
548
|
|
549 FUNGE_ATTR_FAST void stackstack_transfer(funge_cell count, funge_stack * restrict TOSS, funge_stack * restrict SOSS)
|
|
550 {
|
|
551 assert(TOSS != NULL);
|
|
552 assert(SOSS != NULL);
|
|
553 assert(TOSS != SOSS);
|
|
554
|
|
555 if (count > 0) {
|
|
556 while (count--) {
|
|
557 stack_push(TOSS, stack_pop(SOSS));
|
|
558 }
|
|
559 } else if (count < 0) {
|
|
560 while (count++) {
|
|
561 stack_push(SOSS, stack_pop(TOSS));
|
|
562 }
|
|
563 }
|
|
564 }
|