1 /* @(#) pf_core.c 98/01/28 1.5 */
2 /***************************************************************
5 ** This file has the main entry points to the pForth library.
8 ** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom
10 ** Permission to use, copy, modify, and/or distribute this
11 ** software for any purpose with or without fee is hereby granted.
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.
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 ***************************************************************/
41 /***************************************************************
43 ***************************************************************/
45 char gScratch[TIB_SIZE];
46 pfTaskData_t *gCurrentTask = NULL;
47 pfDictionary_t *gCurrentDictionary;
48 cell_t gNumPrimitives;
50 ExecToken gLocalCompiler_XT; /* custom compiler for local variables */
51 ExecToken gNumberQ_XT; /* XT of NUMBER? */
52 ExecToken gQuitP_XT; /* XT of (QUIT) */
53 ExecToken gAcceptP_XT; /* XT of ACCEPT */
55 /* Depth of data stack when colon called. */
58 /* Global Forth variables. */
59 cell_t gVarContext; /* Points to last name field. */
60 cell_t gVarState; /* 1 if compiling. */
61 cell_t gVarBase; /* Numeric Base. */
62 cell_t gVarByeCode; /* Echo input. */
63 cell_t gVarEcho; /* Echo input. */
64 cell_t gVarTraceLevel; /* Trace Level for Inner Interpreter. */
65 cell_t gVarTraceStack; /* Dump Stack each time if true. */
66 cell_t gVarTraceFlags; /* Enable various internal debug messages. */
67 cell_t gVarQuiet; /* Suppress unnecessary messages, OK, etc. */
68 cell_t gVarReturnCode; /* Returned to caller of Forth, eg. UNIX shell. */
70 /* data for INCLUDE that allows multiple nested files. */
71 IncludeFrame gIncludeStack[MAX_INCLUDE_DEPTH];
74 static void pfResetForthTask( void );
75 static void pfInit( void );
76 static void pfTerm( void );
78 #define DEFAULT_RETURN_DEPTH (512)
79 #define DEFAULT_USER_DEPTH (512)
81 #ifndef PF_DEFAULT_HEADER_SIZE
82 #define PF_DEFAULT_HEADER_SIZE (120000)
85 #ifndef PF_DEFAULT_CODE_SIZE
86 #define PF_DEFAULT_CODE_SIZE (300000)
89 /* Initialize globals in a function to simplify loading on
90 * embedded systems which may not support initialization of data section.
92 static void pfInit( void )
96 gCurrentDictionary = NULL;
98 gLocalCompiler_XT = 0;
99 gVarContext = (cell_t)NULL; /* Points to last name field. */
100 gVarState = 0; /* 1 if compiling. */
101 gVarEcho = 0; /* Echo input. */
102 gVarTraceLevel = 0; /* Trace Level for Inner Interpreter. */
103 gVarTraceFlags = 0; /* Enable various internal debug messages. */
104 gVarReturnCode = 0; /* Returned to caller of Forth, eg. UNIX shell. */
108 gVarBase = 10; /* Numeric Base. */
109 gDepthAtColon = DEPTH_AT_COLON_INVALID;
112 pfInitMemoryAllocator();
115 static void pfTerm( void )
120 /***************************************************************
122 ***************************************************************/
124 void pfDeleteTask( PForthTask task )
126 pfTaskData_t *cftd = (pfTaskData_t *)task;
127 FREE_VAR( cftd->td_ReturnLimit );
128 FREE_VAR( cftd->td_StackLimit );
132 /* Allocate some extra cells to protect against mild stack underflows. */
133 #define STACK_SAFETY (8)
134 PForthTask pfCreateTask( cell_t UserStackDepth, cell_t ReturnStackDepth )
138 cftd = ( pfTaskData_t * ) pfAllocMem( sizeof( pfTaskData_t ) );
139 if( !cftd ) goto nomem;
140 pfSetMemory( cftd, 0, sizeof( pfTaskData_t ));
142 /* Allocate User Stack */
143 cftd->td_StackLimit = (cell_t *) pfAllocMem((ucell_t)(sizeof(cell_t) *
144 (UserStackDepth + STACK_SAFETY)));
145 if( !cftd->td_StackLimit ) goto nomem;
146 cftd->td_StackBase = cftd->td_StackLimit + UserStackDepth;
147 cftd->td_StackPtr = cftd->td_StackBase;
149 /* Allocate Return Stack */
150 cftd->td_ReturnLimit = (cell_t *) pfAllocMem((ucell_t)(sizeof(cell_t) * ReturnStackDepth) );
151 if( !cftd->td_ReturnLimit ) goto nomem;
152 cftd->td_ReturnBase = cftd->td_ReturnLimit + ReturnStackDepth;
153 cftd->td_ReturnPtr = cftd->td_ReturnBase;
155 /* Allocate Float Stack */
157 /* Allocate room for as many Floats as we do regular data. */
158 cftd->td_FloatStackLimit = (PF_FLOAT *) pfAllocMem((ucell_t)(sizeof(PF_FLOAT) *
159 (UserStackDepth + STACK_SAFETY)));
160 if( !cftd->td_FloatStackLimit ) goto nomem;
161 cftd->td_FloatStackBase = cftd->td_FloatStackLimit + UserStackDepth;
162 cftd->td_FloatStackPtr = cftd->td_FloatStackBase;
165 cftd->td_InputStream = PF_STDIN;
167 cftd->td_SourcePtr = &cftd->td_TIB[0];
168 cftd->td_SourceNum = 0;
170 return (PForthTask) cftd;
173 ERR("CreateTaskContext: insufficient memory.\n");
174 if( cftd ) pfDeleteTask( (PForthTask) cftd );
178 /***************************************************************
179 ** Dictionary Management
180 ***************************************************************/
182 ThrowCode pfExecIfDefined( const char *CString )
184 ThrowCode result = 0;
185 if( NAME_BASE != (cell_t)NULL)
188 if( ffFindC( CString, &XT ) )
190 result = pfCatch( XT );
196 /***************************************************************
197 ** Delete a dictionary created by pfCreateDictionary()
199 void pfDeleteDictionary( PForthDictionary dictionary )
201 pfDictionary_t *dic = (pfDictionary_t *) dictionary;
204 if( dic->dic_Flags & PF_DICF_ALLOCATED_SEGMENTS )
206 FREE_VAR( dic->dic_HeaderBaseUnaligned );
207 FREE_VAR( dic->dic_CodeBaseUnaligned );
212 /***************************************************************
213 ** Create a complete dictionary.
214 ** The dictionary consists of two parts, the header with the names,
215 ** and the code portion.
216 ** Delete using pfDeleteDictionary().
217 ** Return pointer to dictionary management structure.
219 PForthDictionary pfCreateDictionary( cell_t HeaderSize, cell_t CodeSize )
221 /* Allocate memory for initial dictionary. */
224 dic = ( pfDictionary_t * ) pfAllocMem( sizeof( pfDictionary_t ) );
225 if( !dic ) goto nomem;
226 pfSetMemory( dic, 0, sizeof( pfDictionary_t ));
228 dic->dic_Flags |= PF_DICF_ALLOCATED_SEGMENTS;
230 /* Align dictionary segments to preserve alignment of floats across hosts.
231 * Thank you Helmut Proelss for pointing out that this needs to be cast
232 * to (ucell_t) on 16 bit systems.
234 #define DIC_ALIGNMENT_SIZE ((ucell_t)(0x10))
235 #define DIC_ALIGN(addr) ((((ucell_t)(addr)) + DIC_ALIGNMENT_SIZE - 1) & ~(DIC_ALIGNMENT_SIZE - 1))
237 /* Allocate memory for header. */
240 dic->dic_HeaderBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) HeaderSize + DIC_ALIGNMENT_SIZE );
241 if( !dic->dic_HeaderBaseUnaligned ) goto nomem;
242 /* Align header base. */
243 dic->dic_HeaderBase = DIC_ALIGN(dic->dic_HeaderBaseUnaligned);
244 pfSetMemory( (char *) dic->dic_HeaderBase, 0xA5, (ucell_t) HeaderSize);
245 dic->dic_HeaderLimit = dic->dic_HeaderBase + HeaderSize;
246 dic->dic_HeaderPtr = dic->dic_HeaderBase;
250 dic->dic_HeaderBase = 0;
253 /* Allocate memory for code. */
254 dic->dic_CodeBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) CodeSize + DIC_ALIGNMENT_SIZE );
255 if( !dic->dic_CodeBaseUnaligned ) goto nomem;
256 dic->dic_CodeBase = DIC_ALIGN(dic->dic_CodeBaseUnaligned);
257 pfSetMemory( (char *) dic->dic_CodeBase, 0x5A, (ucell_t) CodeSize);
259 dic->dic_CodeLimit = dic->dic_CodeBase + CodeSize;
260 dic->dic_CodePtr.Byte = ((uint8_t *) (dic->dic_CodeBase + QUADUP(NUM_PRIMITIVES)));
262 return (PForthDictionary) dic;
264 pfDeleteDictionary( dic );
268 /***************************************************************
269 ** Used by Quit and other routines to restore system.
270 ***************************************************************/
272 static void pfResetForthTask( void )
274 /* Go back to terminal input. */
275 gCurrentTask->td_InputStream = PF_STDIN;
278 gCurrentTask->td_StackPtr = gCurrentTask->td_StackBase;
279 gCurrentTask->td_ReturnPtr = gCurrentTask->td_ReturnBase;
280 #ifdef PF_SUPPORT_FP /* Reset Floating Point stack too! */
281 gCurrentTask->td_FloatStackPtr = gCurrentTask->td_FloatStackBase;
284 /* Advance >IN to end of input. */
285 gCurrentTask->td_IN = gCurrentTask->td_SourceNum;
289 /***************************************************************
290 ** Set current task context.
291 ***************************************************************/
293 void pfSetCurrentTask( PForthTask task )
295 gCurrentTask = (pfTaskData_t *) task;
298 /***************************************************************
300 ***************************************************************/
302 void pfSetQuiet( cell_t IfQuiet )
304 gVarQuiet = (cell_t) IfQuiet;
307 /***************************************************************
308 ** Query message status.
309 ***************************************************************/
311 cell_t pfQueryQuiet( void )
316 /***************************************************************
317 ** Top level interpreter.
318 ***************************************************************/
319 ThrowCode pfQuit( void )
326 exception = ffOuterInterpreterLoop();
344 pfReportThrow( exception );
345 pfHandleIncludeError();
351 return gVarReturnCode;
354 /***************************************************************
355 ** Include file based on 'C' name.
356 ***************************************************************/
358 cell_t pfIncludeFile( const char *FileName )
363 cell_t numChars, len;
366 fid = sdOpenFile( FileName, "r" );
369 ERR("pfIncludeFile could not open ");
375 /* Create a dictionary word named ::::FileName for FILE? */
376 pfCopyMemory( &buffer[0], "::::", 4);
377 len = (cell_t) pfCStringLength(FileName);
378 numChars = ( len > (32-4-1) ) ? (32-4-1) : len;
379 pfCopyMemory( &buffer[4], &FileName[len-numChars], numChars+1 );
380 CreateDicEntryC( ID_NOOP, buffer, 0 );
382 Result = ffIncludeFile( fid ); /* Also close the file. */
384 /* Create a dictionary word named ;;;; for FILE? */
385 CreateDicEntryC( ID_NOOP, ";;;;", 0 );
390 /***************************************************************
391 ** Output 'C' string message.
392 ** Use sdTerminalOut which works before initializing gCurrentTask.
393 ***************************************************************/
394 void pfDebugMessage( const char *CString )
402 sdTerminalOut( 0x0D );
403 sdTerminalOut( 0x0A );
404 pfDebugMessage( "DBG: " );
416 /***************************************************************
417 ** Print a decimal number to debug output.
419 void pfDebugPrintDecimalNumber( int n )
421 pfDebugMessage( ConvertNumberToText( n, 10, TRUE, 1 ) );
425 /***************************************************************
426 ** Output 'C' string message.
427 ** This is provided to help avoid the use of printf() and other I/O
428 ** which may not be present on a small embedded system.
429 ** Uses ioType & ioEmit so requires that gCurrentTask has been initialized.
430 ***************************************************************/
431 void pfMessage( const char *CString )
433 ioType( CString, (cell_t) pfCStringLength(CString) );
436 /**************************************************************************
437 ** Main entry point for pForth.
439 ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfInit )
442 pfDictionary_t *dic = NULL;
443 ThrowCode Result = 0;
444 ExecToken EntryPoint = 0;
447 Result = PF_USER_INIT;
448 if( Result < 0 ) goto error1;
453 /* Allocate Task structure. */
454 pfDebugMessage("pfDoForth: call pfCreateTask()\n");
455 cftd = pfCreateTask( DEFAULT_USER_DEPTH, DEFAULT_RETURN_DEPTH );
459 pfSetCurrentTask( cftd );
463 MSG( "PForth V"PFORTH_VERSION );
464 if( IsHostLittleEndian() ) MSG("-LE");
466 #if PF_BIG_ENDIAN_DIC
468 #elif PF_LITTLE_ENDIAN_DIC
471 if (sizeof(cell_t) == 8)
475 else if (sizeof(cell_t) == 4)
480 MSG( ", built "__DATE__" "__TIME__ );
483 /* Don't use MSG before task set. */
486 pfDebugMessage("SourceName = "); pfDebugMessage(SourceName); pfDebugMessage("\n");
490 #ifdef PF_NO_GLOBAL_INIT
491 if( LoadCustomFunctionTable() < 0 ) goto error2; /* Init custom 'C' call array. */
494 #if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL))
497 pfDebugMessage("Build dictionary from scratch.\n");
498 dic = pfBuildDictionary( PF_DEFAULT_HEADER_SIZE, PF_DEFAULT_CODE_SIZE );
503 #endif /* !PF_NO_INIT && !PF_NO_SHELL*/
507 pfDebugMessage("DicFileName = "); pfDebugMessage(DicFileName); pfDebugMessage("\n");
512 dic = pfLoadDictionary( DicFileName, &EntryPoint );
521 dic = pfLoadStaticDictionary();
524 if( dic == NULL ) goto error2;
531 pfDebugMessage("pfDoForth: try AUTO.INIT\n");
532 Result = pfExecIfDefined("AUTO.INIT");
535 MSG("Error in AUTO.INIT");
539 if( EntryPoint != 0 )
541 Result = pfCatch( EntryPoint );
546 if( SourceName == NULL )
548 pfDebugMessage("pfDoForth: pfQuit\n");
559 Result = pfIncludeFile( SourceName );
562 #endif /* PF_NO_SHELL */
564 /* Clean up after running Forth. */
565 pfExecIfDefined("AUTO.TERM");
566 pfDeleteDictionary( dic );
567 pfDeleteTask( cftd );
576 return Result ? Result : gVarByeCode;
579 MSG("pfDoForth: Error occured.\n");
580 pfDeleteTask( cftd );
581 /* Terminate so we restore normal shell tty mode. */
593 cell_t pfUnitTest( void )
595 cell_t numErrors = 0;
596 numErrors += pfUnitTestText();