relicense to 0BSD
[pforth] / csrc / pf_core.c
CommitLineData
8e9db35f
PB
1/* @(#) pf_core.c 98/01/28 1.5 */
2/***************************************************************
3** Forth based on 'C'
4**
5** This file has the main entry points to the pForth library.
6**
7** Author: Phil Burk
8** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom
9**
1f99f95d
S
10** Permission to use, copy, modify, and/or distribute this
11** software for any purpose with or without fee is hereby granted.
12**
13** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
15** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
16** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
17** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
18** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8e9db35f
PB
21**
22****************************************************************
23** 940502 PLB Creation.
24** 940505 PLB More macros.
25** 940509 PLB Moved all stack handling into inner interpreter.
26** Added Create, Colon, Semicolon, HNumberQ, etc.
27** 940510 PLB Got inner interpreter working with secondaries.
28** Added (LITERAL). Compiles colon definitions.
29** 940511 PLB Added conditionals, LITERAL, CREATE DOES>
30** 940512 PLB Added DO LOOP DEFER, fixed R>
31** 940520 PLB Added INCLUDE
32** 940521 PLB Added NUMBER?
33** 940930 PLB Outer Interpreter now uses deferred NUMBER?
34** 941005 PLB Added ANSI locals, LEAVE, modularised
35** 950320 RDG Added underflow checking for FP stack
36** 970702 PLB Added STACK_SAFETY to FP stack size.
37***************************************************************/
38
39#include "pf_all.h"
40
41/***************************************************************
42** Global Data
43***************************************************************/
44
2d8c3ec4 45char gScratch[TIB_SIZE];
8e9db35f
PB
46pfTaskData_t *gCurrentTask = NULL;
47pfDictionary_t *gCurrentDictionary;
2d8c3ec4 48cell_t gNumPrimitives;
8e9db35f 49
2d8c3ec4
PB
50ExecToken gLocalCompiler_XT; /* custom compiler for local variables */
51ExecToken gNumberQ_XT; /* XT of NUMBER? */
52ExecToken gQuitP_XT; /* XT of (QUIT) */
53ExecToken gAcceptP_XT; /* XT of ACCEPT */
8e9db35f
PB
54
55/* Depth of data stack when colon called. */
2d8c3ec4 56cell_t gDepthAtColon;
8e9db35f
PB
57
58/* Global Forth variables. */
59cell_t gVarContext; /* Points to last name field. */
60cell_t gVarState; /* 1 if compiling. */
61cell_t gVarBase; /* Numeric Base. */
62cell_t gVarEcho; /* Echo input. */
63cell_t gVarTraceLevel; /* Trace Level for Inner Interpreter. */
64cell_t gVarTraceStack; /* Dump Stack each time if true. */
65cell_t gVarTraceFlags; /* Enable various internal debug messages. */
66cell_t gVarQuiet; /* Suppress unnecessary messages, OK, etc. */
67cell_t gVarReturnCode; /* Returned to caller of Forth, eg. UNIX shell. */
68
69/* data for INCLUDE that allows multiple nested files. */
2d8c3ec4
PB
70IncludeFrame gIncludeStack[MAX_INCLUDE_DEPTH];
71cell_t gIncludeIndex;
8e9db35f
PB
72
73static void pfResetForthTask( void );
74static void pfInit( void );
75static void pfTerm( void );
76
8e9db35f
PB
77#define DEFAULT_RETURN_DEPTH (512)
78#define DEFAULT_USER_DEPTH (512)
28475a0f
PB
79
80#ifndef PF_DEFAULT_HEADER_SIZE
1981b813 81#define PF_DEFAULT_HEADER_SIZE (120000)
28475a0f
PB
82#endif
83
84#ifndef PF_DEFAULT_CODE_SIZE
85#define PF_DEFAULT_CODE_SIZE (300000)
86#endif
8e9db35f
PB
87
88/* Initialize globals in a function to simplify loading on
89 * embedded systems which may not support initialization of data section.
90 */
91static void pfInit( void )
92{
93/* all zero */
94 gCurrentTask = NULL;
95 gCurrentDictionary = NULL;
96 gNumPrimitives = 0;
97 gLocalCompiler_XT = 0;
98 gVarContext = (cell_t)NULL; /* Points to last name field. */
99 gVarState = 0; /* 1 if compiling. */
100 gVarEcho = 0; /* Echo input. */
101 gVarTraceLevel = 0; /* Trace Level for Inner Interpreter. */
102 gVarTraceFlags = 0; /* Enable various internal debug messages. */
103 gVarReturnCode = 0; /* Returned to caller of Forth, eg. UNIX shell. */
104 gIncludeIndex = 0;
105
106/* non-zero */
107 gVarBase = 10; /* Numeric Base. */
108 gDepthAtColon = DEPTH_AT_COLON_INVALID;
109 gVarTraceStack = 1;
110
111 pfInitMemoryAllocator();
112 ioInit();
113}
114static void pfTerm( void )
115{
116 ioTerm();
117}
118
119/***************************************************************
120** Task Management
121***************************************************************/
122
123void pfDeleteTask( PForthTask task )
124{
125 pfTaskData_t *cftd = (pfTaskData_t *)task;
126 FREE_VAR( cftd->td_ReturnLimit );
127 FREE_VAR( cftd->td_StackLimit );
128 pfFreeMem( cftd );
129}
130
131/* Allocate some extra cells to protect against mild stack underflows. */
132#define STACK_SAFETY (8)
133PForthTask pfCreateTask( cell_t UserStackDepth, cell_t ReturnStackDepth )
134{
135 pfTaskData_t *cftd;
136
137 cftd = ( pfTaskData_t * ) pfAllocMem( sizeof( pfTaskData_t ) );
138 if( !cftd ) goto nomem;
139 pfSetMemory( cftd, 0, sizeof( pfTaskData_t ));
140
141/* Allocate User Stack */
142 cftd->td_StackLimit = (cell_t *) pfAllocMem((ucell_t)(sizeof(cell_t) *
143 (UserStackDepth + STACK_SAFETY)));
144 if( !cftd->td_StackLimit ) goto nomem;
145 cftd->td_StackBase = cftd->td_StackLimit + UserStackDepth;
146 cftd->td_StackPtr = cftd->td_StackBase;
147
148/* Allocate Return Stack */
149 cftd->td_ReturnLimit = (cell_t *) pfAllocMem((ucell_t)(sizeof(cell_t) * ReturnStackDepth) );
150 if( !cftd->td_ReturnLimit ) goto nomem;
151 cftd->td_ReturnBase = cftd->td_ReturnLimit + ReturnStackDepth;
152 cftd->td_ReturnPtr = cftd->td_ReturnBase;
153
154/* Allocate Float Stack */
155#ifdef PF_SUPPORT_FP
156/* Allocate room for as many Floats as we do regular data. */
157 cftd->td_FloatStackLimit = (PF_FLOAT *) pfAllocMem((ucell_t)(sizeof(PF_FLOAT) *
158 (UserStackDepth + STACK_SAFETY)));
159 if( !cftd->td_FloatStackLimit ) goto nomem;
160 cftd->td_FloatStackBase = cftd->td_FloatStackLimit + UserStackDepth;
161 cftd->td_FloatStackPtr = cftd->td_FloatStackBase;
162#endif
163
164 cftd->td_InputStream = PF_STDIN;
165
166 cftd->td_SourcePtr = &cftd->td_TIB[0];
167 cftd->td_SourceNum = 0;
168
169 return (PForthTask) cftd;
170
171nomem:
172 ERR("CreateTaskContext: insufficient memory.\n");
173 if( cftd ) pfDeleteTask( (PForthTask) cftd );
174 return NULL;
175}
176
177/***************************************************************
178** Dictionary Management
179***************************************************************/
180
529bb4e2 181ThrowCode pfExecIfDefined( const char *CString )
8e9db35f 182{
529bb4e2 183 ThrowCode result = 0;
8e9db35f
PB
184 if( NAME_BASE != (cell_t)NULL)
185 {
186 ExecToken XT;
187 if( ffFindC( CString, &XT ) )
188 {
189 result = pfCatch( XT );
190 }
191 }
192 return result;
193}
194
195/***************************************************************
196** Delete a dictionary created by pfCreateDictionary()
197*/
198void pfDeleteDictionary( PForthDictionary dictionary )
199{
200 pfDictionary_t *dic = (pfDictionary_t *) dictionary;
201 if( !dic ) return;
202
203 if( dic->dic_Flags & PF_DICF_ALLOCATED_SEGMENTS )
204 {
205 FREE_VAR( dic->dic_HeaderBaseUnaligned );
206 FREE_VAR( dic->dic_CodeBaseUnaligned );
207 }
208 pfFreeMem( dic );
209}
210
211/***************************************************************
212** Create a complete dictionary.
213** The dictionary consists of two parts, the header with the names,
214** and the code portion.
215** Delete using pfDeleteDictionary().
216** Return pointer to dictionary management structure.
217*/
218PForthDictionary pfCreateDictionary( cell_t HeaderSize, cell_t CodeSize )
219{
220/* Allocate memory for initial dictionary. */
221 pfDictionary_t *dic;
222
223 dic = ( pfDictionary_t * ) pfAllocMem( sizeof( pfDictionary_t ) );
224 if( !dic ) goto nomem;
225 pfSetMemory( dic, 0, sizeof( pfDictionary_t ));
226
227 dic->dic_Flags |= PF_DICF_ALLOCATED_SEGMENTS;
228
229/* Align dictionary segments to preserve alignment of floats across hosts.
230 * Thank you Helmut Proelss for pointing out that this needs to be cast
231 * to (ucell_t) on 16 bit systems.
232 */
233#define DIC_ALIGNMENT_SIZE ((ucell_t)(0x10))
234#define DIC_ALIGN(addr) ((((ucell_t)(addr)) + DIC_ALIGNMENT_SIZE - 1) & ~(DIC_ALIGNMENT_SIZE - 1))
235
236/* Allocate memory for header. */
237 if( HeaderSize > 0 )
238 {
239 dic->dic_HeaderBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) HeaderSize + DIC_ALIGNMENT_SIZE );
240 if( !dic->dic_HeaderBaseUnaligned ) goto nomem;
241/* Align header base. */
242 dic->dic_HeaderBase = DIC_ALIGN(dic->dic_HeaderBaseUnaligned);
243 pfSetMemory( (char *) dic->dic_HeaderBase, 0xA5, (ucell_t) HeaderSize);
244 dic->dic_HeaderLimit = dic->dic_HeaderBase + HeaderSize;
245 dic->dic_HeaderPtr = dic->dic_HeaderBase;
246 }
247 else
248 {
249 dic->dic_HeaderBase = 0;
250 }
251
252/* Allocate memory for code. */
253 dic->dic_CodeBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) CodeSize + DIC_ALIGNMENT_SIZE );
254 if( !dic->dic_CodeBaseUnaligned ) goto nomem;
255 dic->dic_CodeBase = DIC_ALIGN(dic->dic_CodeBaseUnaligned);
256 pfSetMemory( (char *) dic->dic_CodeBase, 0x5A, (ucell_t) CodeSize);
257
258 dic->dic_CodeLimit = dic->dic_CodeBase + CodeSize;
259 dic->dic_CodePtr.Byte = ((uint8_t *) (dic->dic_CodeBase + QUADUP(NUM_PRIMITIVES)));
260
261 return (PForthDictionary) dic;
262nomem:
263 pfDeleteDictionary( dic );
264 return NULL;
265}
266
267/***************************************************************
268** Used by Quit and other routines to restore system.
269***************************************************************/
270
271static void pfResetForthTask( void )
272{
273/* Go back to terminal input. */
274 gCurrentTask->td_InputStream = PF_STDIN;
275
276/* Reset stacks. */
277 gCurrentTask->td_StackPtr = gCurrentTask->td_StackBase;
278 gCurrentTask->td_ReturnPtr = gCurrentTask->td_ReturnBase;
279#ifdef PF_SUPPORT_FP /* Reset Floating Point stack too! */
280 gCurrentTask->td_FloatStackPtr = gCurrentTask->td_FloatStackBase;
281#endif
282
283/* Advance >IN to end of input. */
284 gCurrentTask->td_IN = gCurrentTask->td_SourceNum;
285 gVarState = 0;
286}
287
288/***************************************************************
289** Set current task context.
290***************************************************************/
291
292void pfSetCurrentTask( PForthTask task )
293{
294 gCurrentTask = (pfTaskData_t *) task;
295}
296
297/***************************************************************
298** Set Quiet Flag.
299***************************************************************/
300
301void pfSetQuiet( cell_t IfQuiet )
302{
303 gVarQuiet = (cell_t) IfQuiet;
304}
305
306/***************************************************************
307** Query message status.
308***************************************************************/
309
310cell_t pfQueryQuiet( void )
311{
312 return gVarQuiet;
313}
314
315/***************************************************************
316** Top level interpreter.
317***************************************************************/
318ThrowCode pfQuit( void )
319{
320 ThrowCode exception;
321 int go = 1;
322
323 while(go)
324 {
325 exception = ffOuterInterpreterLoop();
326 if( exception == 0 )
327 {
328 exception = ffOK();
329 }
330
331 switch( exception )
332 {
333 case 0:
334 break;
335
336 case THROW_BYE:
337 go = 0;
338 break;
339
340 case THROW_ABORT:
341 default:
342 ffDotS();
343 pfReportThrow( exception );
344 pfHandleIncludeError();
345 pfResetForthTask();
346 break;
347 }
348 }
349
350 return gVarReturnCode;
351}
352
353/***************************************************************
354** Include file based on 'C' name.
355***************************************************************/
356
357cell_t pfIncludeFile( const char *FileName )
358{
359 FileStream *fid;
360 cell_t Result;
361 char buffer[32];
362 cell_t numChars, len;
363
364/* Open file. */
365 fid = sdOpenFile( FileName, "r" );
366 if( fid == NULL )
367 {
368 ERR("pfIncludeFile could not open ");
369 ERR(FileName);
370 EMIT_CR;
371 return -1;
372 }
373
374/* Create a dictionary word named ::::FileName for FILE? */
375 pfCopyMemory( &buffer[0], "::::", 4);
376 len = (cell_t) pfCStringLength(FileName);
377 numChars = ( len > (32-4-1) ) ? (32-4-1) : len;
378 pfCopyMemory( &buffer[4], &FileName[len-numChars], numChars+1 );
379 CreateDicEntryC( ID_NOOP, buffer, 0 );
380
381 Result = ffIncludeFile( fid ); /* Also close the file. */
382
383/* Create a dictionary word named ;;;; for FILE? */
384 CreateDicEntryC( ID_NOOP, ";;;;", 0 );
385
386 return Result;
387}
388
389/***************************************************************
390** Output 'C' string message.
391** Use sdTerminalOut which works before initializing gCurrentTask.
392***************************************************************/
393void pfDebugMessage( const char *CString )
394{
395#if 0
396 while( *CString )
397 {
398 char c = *CString++;
399 if( c == '\n' )
400 {
401 sdTerminalOut( 0x0D );
402 sdTerminalOut( 0x0A );
403 pfDebugMessage( "DBG: " );
404 }
405 else
406 {
407 sdTerminalOut( c );
408 }
409 }
410#else
411 (void)CString;
412#endif
413}
414
415/***************************************************************
416** Print a decimal number to debug output.
417*/
418void pfDebugPrintDecimalNumber( int n )
419{
420 pfDebugMessage( ConvertNumberToText( n, 10, TRUE, 1 ) );
421}
422
423
424/***************************************************************
425** Output 'C' string message.
426** This is provided to help avoid the use of printf() and other I/O
427** which may not be present on a small embedded system.
428** Uses ioType & ioEmit so requires that gCurrentTask has been initialized.
429***************************************************************/
430void pfMessage( const char *CString )
431{
432 ioType( CString, (cell_t) pfCStringLength(CString) );
433}
434
435/**************************************************************************
436** Main entry point for pForth.
437*/
529bb4e2 438ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfInit )
8e9db35f
PB
439{
440 pfTaskData_t *cftd;
441 pfDictionary_t *dic = NULL;
529bb4e2 442 ThrowCode Result = 0;
8e9db35f
PB
443 ExecToken EntryPoint = 0;
444
445#ifdef PF_USER_INIT
446 Result = PF_USER_INIT;
447 if( Result < 0 ) goto error1;
448#endif
449
450 pfInit();
451
452/* Allocate Task structure. */
453 pfDebugMessage("pfDoForth: call pfCreateTask()\n");
454 cftd = pfCreateTask( DEFAULT_USER_DEPTH, DEFAULT_RETURN_DEPTH );
455
456 if( cftd )
457 {
458 pfSetCurrentTask( cftd );
459
460 if( !gVarQuiet )
461 {
462 MSG( "PForth V"PFORTH_VERSION );
463 if( IsHostLittleEndian() ) MSG("-LE");
464 else MSG("-BE");
465#if PF_BIG_ENDIAN_DIC
466 MSG("/BE");
467#elif PF_LITTLE_ENDIAN_DIC
468 MSG("/LE");
469#endif
470 if (sizeof(cell_t) == 8)
471 {
472 MSG("/64");
473 }
474 else if (sizeof(cell_t) == 4)
475 {
476 MSG("/32");
477 }
478
479 MSG( ", built "__DATE__" "__TIME__ );
480 }
481
482/* Don't use MSG before task set. */
483 if( SourceName )
484 {
485 pfDebugMessage("SourceName = "); pfDebugMessage(SourceName); pfDebugMessage("\n");
486 }
487
488
489#ifdef PF_NO_GLOBAL_INIT
490 if( LoadCustomFunctionTable() < 0 ) goto error2; /* Init custom 'C' call array. */
491#endif
492
493#if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL))
494 if( IfInit )
495 {
496 pfDebugMessage("Build dictionary from scratch.\n");
28475a0f 497 dic = pfBuildDictionary( PF_DEFAULT_HEADER_SIZE, PF_DEFAULT_CODE_SIZE );
8e9db35f
PB
498 }
499 else
500#else
501 TOUCH(IfInit);
502#endif /* !PF_NO_INIT && !PF_NO_SHELL*/
503 {
504 if( DicFileName )
505 {
506 pfDebugMessage("DicFileName = "); pfDebugMessage(DicFileName); pfDebugMessage("\n");
507 if( !gVarQuiet )
508 {
509 EMIT_CR;
510 }
511 dic = pfLoadDictionary( DicFileName, &EntryPoint );
512 }
513 else
514 {
515 if( !gVarQuiet )
516 {
517 MSG(" (static)");
518 EMIT_CR;
519 }
520 dic = pfLoadStaticDictionary();
521 }
522 }
523 if( dic == NULL ) goto error2;
524
525 if( !gVarQuiet )
526 {
527 EMIT_CR;
528 }
529
530 pfDebugMessage("pfDoForth: try AUTO.INIT\n");
531 Result = pfExecIfDefined("AUTO.INIT");
532 if( Result != 0 )
533 {
534 MSG("Error in AUTO.INIT");
535 goto error2;
536 }
537
538 if( EntryPoint != 0 )
539 {
540 Result = pfCatch( EntryPoint );
541 }
542#ifndef PF_NO_SHELL
543 else
544 {
545 if( SourceName == NULL )
546 {
547 pfDebugMessage("pfDoForth: pfQuit\n");
548 Result = pfQuit();
549 }
550 else
551 {
552 if( !gVarQuiet )
553 {
554 MSG("Including: ");
555 MSG(SourceName);
556 MSG("\n");
557 }
558 Result = pfIncludeFile( SourceName );
559 }
560 }
561#endif /* PF_NO_SHELL */
562
563 /* Clean up after running Forth. */
564 pfExecIfDefined("AUTO.TERM");
565 pfDeleteDictionary( dic );
566 pfDeleteTask( cftd );
567 }
568
569 pfTerm();
570
571#ifdef PF_USER_TERM
572 PF_USER_TERM;
573#endif
574
575 return Result;
576
577error2:
578 MSG("pfDoForth: Error occured.\n");
579 pfDeleteTask( cftd );
580 /* Terminate so we restore normal shell tty mode. */
581 pfTerm();
582
583#ifdef PF_USER_INIT
584error1:
585#endif
586
587 return -1;
588}
589
590
591#ifdef PF_UNIT_TEST
592cell_t pfUnitTest( void )
593{
594 cell_t numErrors = 0;
595 numErrors += pfUnitTestText();
596 return numErrors;
597}
598#endif