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 gVarEcho; /* Echo input. */
63 cell_t gVarTraceLevel; /* Trace Level for Inner Interpreter. */
64 cell_t gVarTraceStack; /* Dump Stack each time if true. */
65 cell_t gVarTraceFlags; /* Enable various internal debug messages. */
66 cell_t gVarQuiet; /* Suppress unnecessary messages, OK, etc. */
67 cell_t gVarReturnCode; /* Returned to caller of Forth, eg. UNIX shell. */
69 /* data for INCLUDE that allows multiple nested files. */
70 IncludeFrame gIncludeStack[MAX_INCLUDE_DEPTH];
73 static void pfResetForthTask( void );
74 static void pfInit( void );
75 static void pfTerm( void );
77 #define DEFAULT_RETURN_DEPTH (512)
78 #define DEFAULT_USER_DEPTH (512)
80 #ifndef PF_DEFAULT_HEADER_SIZE
81 #define PF_DEFAULT_HEADER_SIZE (120000)
84 #ifndef PF_DEFAULT_CODE_SIZE
85 #define PF_DEFAULT_CODE_SIZE (300000)
88 /* Initialize globals in a function to simplify loading on
89 * embedded systems which may not support initialization of data section.
91 static void pfInit( void )
95 gCurrentDictionary = NULL;
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. */
107 gVarBase = 10; /* Numeric Base. */
108 gDepthAtColon = DEPTH_AT_COLON_INVALID;
111 pfInitMemoryAllocator();
114 static void pfTerm( void )
119 /***************************************************************
121 ***************************************************************/
123 void pfDeleteTask( PForthTask task )
125 pfTaskData_t *cftd = (pfTaskData_t *)task;
126 FREE_VAR( cftd->td_ReturnLimit );
127 FREE_VAR( cftd->td_StackLimit );
131 /* Allocate some extra cells to protect against mild stack underflows. */
132 #define STACK_SAFETY (8)
133 PForthTask pfCreateTask( cell_t UserStackDepth, cell_t ReturnStackDepth )
137 cftd = ( pfTaskData_t * ) pfAllocMem( sizeof( pfTaskData_t ) );
138 if( !cftd ) goto nomem;
139 pfSetMemory( cftd, 0, sizeof( pfTaskData_t ));
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;
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;
154 /* Allocate Float Stack */
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;
164 cftd->td_InputStream = PF_STDIN;
166 cftd->td_SourcePtr = &cftd->td_TIB[0];
167 cftd->td_SourceNum = 0;
169 return (PForthTask) cftd;
172 ERR("CreateTaskContext: insufficient memory.\n");
173 if( cftd ) pfDeleteTask( (PForthTask) cftd );
177 /***************************************************************
178 ** Dictionary Management
179 ***************************************************************/
181 ThrowCode pfExecIfDefined( const char *CString )
183 ThrowCode result = 0;
184 if( NAME_BASE != (cell_t)NULL)
187 if( ffFindC( CString, &XT ) )
189 result = pfCatch( XT );
195 /***************************************************************
196 ** Delete a dictionary created by pfCreateDictionary()
198 void pfDeleteDictionary( PForthDictionary dictionary )
200 pfDictionary_t *dic = (pfDictionary_t *) dictionary;
203 if( dic->dic_Flags & PF_DICF_ALLOCATED_SEGMENTS )
205 FREE_VAR( dic->dic_HeaderBaseUnaligned );
206 FREE_VAR( dic->dic_CodeBaseUnaligned );
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.
218 PForthDictionary pfCreateDictionary( cell_t HeaderSize, cell_t CodeSize )
220 /* Allocate memory for initial dictionary. */
223 dic = ( pfDictionary_t * ) pfAllocMem( sizeof( pfDictionary_t ) );
224 if( !dic ) goto nomem;
225 pfSetMemory( dic, 0, sizeof( pfDictionary_t ));
227 dic->dic_Flags |= PF_DICF_ALLOCATED_SEGMENTS;
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.
233 #define DIC_ALIGNMENT_SIZE ((ucell_t)(0x10))
234 #define DIC_ALIGN(addr) ((((ucell_t)(addr)) + DIC_ALIGNMENT_SIZE - 1) & ~(DIC_ALIGNMENT_SIZE - 1))
236 /* Allocate memory for header. */
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;
249 dic->dic_HeaderBase = 0;
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);
258 dic->dic_CodeLimit = dic->dic_CodeBase + CodeSize;
259 dic->dic_CodePtr.Byte = ((uint8_t *) (dic->dic_CodeBase + QUADUP(NUM_PRIMITIVES)));
261 return (PForthDictionary) dic;
263 pfDeleteDictionary( dic );
267 /***************************************************************
268 ** Used by Quit and other routines to restore system.
269 ***************************************************************/
271 static void pfResetForthTask( void )
273 /* Go back to terminal input. */
274 gCurrentTask->td_InputStream = PF_STDIN;
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;
283 /* Advance >IN to end of input. */
284 gCurrentTask->td_IN = gCurrentTask->td_SourceNum;
288 /***************************************************************
289 ** Set current task context.
290 ***************************************************************/
292 void pfSetCurrentTask( PForthTask task )
294 gCurrentTask = (pfTaskData_t *) task;
297 /***************************************************************
299 ***************************************************************/
301 void pfSetQuiet( cell_t IfQuiet )
303 gVarQuiet = (cell_t) IfQuiet;
306 /***************************************************************
307 ** Query message status.
308 ***************************************************************/
310 cell_t pfQueryQuiet( void )
315 /***************************************************************
316 ** Top level interpreter.
317 ***************************************************************/
318 ThrowCode pfQuit( void )
325 exception = ffOuterInterpreterLoop();
343 pfReportThrow( exception );
344 pfHandleIncludeError();
350 return gVarReturnCode;
353 /***************************************************************
354 ** Include file based on 'C' name.
355 ***************************************************************/
357 cell_t pfIncludeFile( const char *FileName )
362 cell_t numChars, len;
365 fid = sdOpenFile( FileName, "r" );
368 ERR("pfIncludeFile could not open ");
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 );
381 Result = ffIncludeFile( fid ); /* Also close the file. */
383 /* Create a dictionary word named ;;;; for FILE? */
384 CreateDicEntryC( ID_NOOP, ";;;;", 0 );
389 /***************************************************************
390 ** Output 'C' string message.
391 ** Use sdTerminalOut which works before initializing gCurrentTask.
392 ***************************************************************/
393 void pfDebugMessage( const char *CString )
401 sdTerminalOut( 0x0D );
402 sdTerminalOut( 0x0A );
403 pfDebugMessage( "DBG: " );
415 /***************************************************************
416 ** Print a decimal number to debug output.
418 void pfDebugPrintDecimalNumber( int n )
420 pfDebugMessage( ConvertNumberToText( n, 10, TRUE, 1 ) );
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 ***************************************************************/
430 void pfMessage( const char *CString )
432 ioType( CString, (cell_t) pfCStringLength(CString) );
435 /**************************************************************************
436 ** Main entry point for pForth.
438 ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfInit )
441 pfDictionary_t *dic = NULL;
442 ThrowCode Result = 0;
443 ExecToken EntryPoint = 0;
446 Result = PF_USER_INIT;
447 if( Result < 0 ) goto error1;
452 /* Allocate Task structure. */
453 pfDebugMessage("pfDoForth: call pfCreateTask()\n");
454 cftd = pfCreateTask( DEFAULT_USER_DEPTH, DEFAULT_RETURN_DEPTH );
458 pfSetCurrentTask( cftd );
462 MSG( "PForth V"PFORTH_VERSION );
463 if( IsHostLittleEndian() ) MSG("-LE");
465 #if PF_BIG_ENDIAN_DIC
467 #elif PF_LITTLE_ENDIAN_DIC
470 if (sizeof(cell_t) == 8)
474 else if (sizeof(cell_t) == 4)
479 MSG( ", built "__DATE__" "__TIME__ );
482 /* Don't use MSG before task set. */
485 pfDebugMessage("SourceName = "); pfDebugMessage(SourceName); pfDebugMessage("\n");
489 #ifdef PF_NO_GLOBAL_INIT
490 if( LoadCustomFunctionTable() < 0 ) goto error2; /* Init custom 'C' call array. */
493 #if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL))
496 pfDebugMessage("Build dictionary from scratch.\n");
497 dic = pfBuildDictionary( PF_DEFAULT_HEADER_SIZE, PF_DEFAULT_CODE_SIZE );
502 #endif /* !PF_NO_INIT && !PF_NO_SHELL*/
506 pfDebugMessage("DicFileName = "); pfDebugMessage(DicFileName); pfDebugMessage("\n");
511 dic = pfLoadDictionary( DicFileName, &EntryPoint );
520 dic = pfLoadStaticDictionary();
523 if( dic == NULL ) goto error2;
530 pfDebugMessage("pfDoForth: try AUTO.INIT\n");
531 Result = pfExecIfDefined("AUTO.INIT");
534 MSG("Error in AUTO.INIT");
538 if( EntryPoint != 0 )
540 Result = pfCatch( EntryPoint );
545 if( SourceName == NULL )
547 pfDebugMessage("pfDoForth: pfQuit\n");
558 Result = pfIncludeFile( SourceName );
561 #endif /* PF_NO_SHELL */
563 /* Clean up after running Forth. */
564 pfExecIfDefined("AUTO.TERM");
565 pfDeleteDictionary( dic );
566 pfDeleteTask( cftd );
578 MSG("pfDoForth: Error occured.\n");
579 pfDeleteTask( cftd );
580 /* Terminate so we restore normal shell tty mode. */
592 cell_t pfUnitTest( void )
594 cell_t numErrors = 0;
595 numErrors += pfUnitTestText();