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