diff --git a/custom/01-be-gone/cf_demo1.c b/custom/01-be-gone/cf_demo1.c new file mode 100644 index 0000000..c6c8c55 --- /dev/null +++ b/custom/01-be-gone/cf_demo1.c @@ -0,0 +1,99 @@ +#include "pf_all.h" /* lots of stuff */ +#include "cf_helpers.h" /* panic, safeAlloc, to_C_string, {fprintf, stderr, ...} */ +#include /* errno */ + +/* + * put forward declarations here if necessary +*/ + + +/**************************************************************** +** Step 1: Put your own special glue routines here +** or link them in from another file or library. +****************************************************************/ + +/* exported functions */ + +static cell_t f4711( cell_t Val ) +{/* a quick way to check that custom worlds are available + */ + return 11 + 47*Val; +} + +static cell_t be_gone( cell_t fileName, cell_t fnLen ) +{/* Demonstrates passing strings from PForth to C. + Interprets the passed strings as file name and tries to delete the file. + Returns 0 or errno + */ + int res; + char* buf = to_C_string( fileName, fnLen ); + res = remove(buf); /* delete file in file system */ + if( res!=0 && errno!=0 ) + res = errno; + free(buf); + return res; +} + + +/**************************************************************** +** Step 2: Create CustomFunctionTable. +** Do not change the name of CustomFunctionTable! +** It is used by the pForth kernel. +****************************************************************/ + +#ifdef PF_NO_GLOBAL_INIT +/****************** +** If your loader does not support global initialization, then you +** must define PF_NO_GLOBAL_INIT and provide a function to fill +** the table. Some embedded system loaders require this! +** Do not change the name of LoadCustomFunctionTable()! +** It is called by the pForth kernel. +*/ +#define NUM_CUSTOM_FUNCTIONS (2) +CFunc0 CustomFunctionTable[NUM_CUSTOM_FUNCTIONS]; + +Err LoadCustomFunctionTable( void ) +{ + CustomFunctionTable[0] = f4711; + CustomFunctionTable[1] = be_gone; + return 0; +} + +#else +/****************** +** If your loader supports global initialization (most do.) then just +** create the table like this. +*/ +CFunc0 CustomFunctionTable[] = +{ + (CFunc0) f4711, + (CFunc0) be_gone +}; +#endif + + +/**************************************************************** +** Step 3: Add custom functions to the dictionary. +** Do not change the name of CompileCustomFunctions! +** It is called by the pForth kernel. +****************************************************************/ + +#if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL)) +Err CompileCustomFunctions( void ) +{ + Err err; + int i = 0; +/* Compile Forth words that call your custom functions. +** Make sure order of functions matches that in LoadCustomFunctionTable(). +** Parameters are: Name in UPPER CASE, Function, Index, Mode, NumParams +*/ + err = CreateGlueToC( "F4711" , i++, C_RETURNS_VALUE, 1 ); + if( err < 0 ) return err; + err = CreateGlueToC( "BE-GONE", i++, C_RETURNS_VALUE, 2 ); + if( err < 0 ) return err; + + return 0; +} +#else +Err CompileCustomFunctions( void ) { return 0; } +#endif diff --git a/custom/01-be-gone/demo.fth b/custom/01-be-gone/demo.fth new file mode 100644 index 0000000..1b6a99e --- /dev/null +++ b/custom/01-be-gone/demo.fth @@ -0,0 +1,18 @@ +\ f4711 is a clear indicator that compilation of custom functions was successful +." f4711( 0, 1, 10, 100, 1000 ) = ( " + 0 f4711 . ." , " + 1 f4711 . ." , " + 10 f4711 . ." , " + 100 f4711 . ." , " +1000 f4711 . ." )" +CR + +\ example of passing passing strings from PForth to custom C code +." be-gone: " +s" terrible_nuisance.asm" be-gone dup 0= +if + ." works." + drop +else + ." returns error=" . +then CR diff --git a/custom/01-be-gone/go-v0.sh b/custom/01-be-gone/go-v0.sh new file mode 100644 index 0000000..736283c --- /dev/null +++ b/custom/01-be-gone/go-v0.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# Compile pForth with custom code and show that this works. +# We assume a posix shell and system (but adaption should be easy to others). +# Note: This is the easiest solution but ignores PForths best practices set up in pfcustom.c +# Warning: This patches the existing source tree and might create confusion when not used on separate Git branch in case an error occurs. +# Tested on MSYS2-Cygwin, Linux, FreeBSD (X86_64 architecture each), NetBSD (i386 architecture) + +# save original C sources and copy demo sources. Thus we do not need to change the make file. +mv ../../csrc/pfcustom.c ../../csrc/pfcustom_c.original +cp ../cf_helpers.h ../../csrc/ +cp cf_demo1.c ../../csrc/pfcustom.c + +echo +echo "----------------------------------------" +echo "make pforth (skip standalone executable)" +echo "----------------------------------------" +MAKE_CMD=`./get-make-cmd.sh` +cd ../../platforms/unix/ +# We would not even need to define DPF_USER_CUSTOM since it is only used in the original pfcustom.c we overwrote. +PF_USER_CUSTOM="1" $MAKE_CMD clean pforth.dic + +# create a nuisance to delete +mv ../../csrc/cf_helpers.h ./terrible_nuisance.asm + +echo +echo "---------------------------" +echo "show that custom code works" +echo "---------------------------" +./pforth -q ../../custom/01-be-gone/demo.fth + +echo +echo "----------------------------" +echo "restore original source tree" +echo "----------------------------" +mv ../../csrc/pfcustom_c.original ../../csrc/pfcustom.c +$MAKE_CMD clean + +echo +echo "-----------------" +echo "That's all folks!" +echo "-----------------" diff --git a/custom/01-be-gone/go-v1.sh b/custom/01-be-gone/go-v1.sh new file mode 100644 index 0000000..cb89e04 --- /dev/null +++ b/custom/01-be-gone/go-v1.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# Compile pForth with custom code and show that this works. +# We assume a posix shell and system (but adaption should be easy to others). +# This improved version only compiles the custom code defined in CF_SOURCES. +# Warning: This patches the existing source tree and might create confusion when not used on separate Git branch in case an error occurs. +# Tested on MSYS2-Cygwin, Linux, FreeBSD (X86_64 architecture each), NetBSD (i386 architecture) + +# copy demo sources. Thus we do not need to change the make file. + +cp ../cf_helpers.h ../../csrc/ +cp cf_demo1.c ../../csrc/ + +echo +echo "----------------------------------------" +echo "make pforth (skip standalone executable)" +echo "----------------------------------------" +MAKE_CMD=`../get-make-cmd.sh` +cd ../../platforms/unix/ + +CF_SOURCES="cf_demo1.c" $MAKE_CMD clean pforth.dic + +# create a nuisance to delete +mv ../../csrc/cf_helpers.h ./terrible_nuisance.asm + +echo +echo "---------------------------" +echo "show that custom code works" +echo "---------------------------" +./pforth -q ../../custom/01-be-gone/demo.fth + +echo +echo "----------------------------" +echo "restore original source tree" +echo "----------------------------" +rm ../../csrc/cf_demo1.c +CF_SOURCES="cf_demo1.c" $MAKE_CMD clean + +echo +echo "-----------------" +echo "That's all folks!" +echo "-----------------" diff --git a/custom/02-argc-argv/cf_demo2.c b/custom/02-argc-argv/cf_demo2.c new file mode 100644 index 0000000..5d475cb --- /dev/null +++ b/custom/02-argc-argv/cf_demo2.c @@ -0,0 +1,122 @@ +#include "pf_all.h" /* lots of stuff */ +#include /* calloc */ +#include /* strlen */ + +/* + * put forward declarations here if necessary +*/ + +void fillScriptParams( int startItem, int NoItems, char *argv[] ); + +/**************************************************************** +** Step 1: Put your own special glue routines here +** or link them in from another file or library. +****************************************************************/ + +/* exported functions */ + +static cell_t cf_f4711( cell_t Val ) +{/* a quick way to check that custom words are available + */ + return 11 + 47*Val; +} + + +struct PascalString { + char* data; + size_t len; +} ; +typedef struct PascalString* PascalStringPtr; + +static struct { + PascalStringPtr args; + int cnt; +} ScriptParams; + +void fillScriptParams( int startItem, int NoItems, char *argv[] ) { + int i; + if( startItem < NoItems ) { + ScriptParams.cnt = NoItems - startItem; + ScriptParams.args = (PascalStringPtr) calloc( ScriptParams.cnt, sizeof(struct PascalString) ); + for( i=0; i caddr len' OR in case of error '--> 0 -1' + */ cell_t len = -1; /* assume something will go wrong */ + cell_t data = 0; /* dito */ + if( 0<=paramNo && paramNo < ScriptParams.cnt ) { + data = (cell_t) ScriptParams.args[paramNo].data; + len = ScriptParams.args[paramNo].len; + } + PUSH_DATA_STACK( (cell_t) data ); + return len; +} + + + +/**************************************************************** +** Step 2: Create CustomFunctionTable. +** Do not change the name of CustomFunctionTable! +** It is used by the pForth kernel. +****************************************************************/ + +#ifdef PF_NO_GLOBAL_INIT +#define NUM_CUSTOM_FUNCTIONS (3) +CFunc0 CustomFunctionTable[NUM_CUSTOM_FUNCTIONS]; + +Err LoadCustomFunctionTable( void ) +{ + CustomFunctionTable[0] = cf_f4711; + CustomFunctionTable[1] = cf_argc; + CustomFunctionTable[2] = cf_argv; + return 0; +} + +#else +CFunc0 CustomFunctionTable[] = +{ + (CFunc0) cf_f4711, + (CFunc0) cf_argc, + (CFunc0) cf_argv +}; +#endif + + +/**************************************************************** +** Step 3: Add custom functions to the dictionary. +** Do not change the name of CompileCustomFunctions! +** It is called by the pForth kernel. +****************************************************************/ + +#if (!defined(PF_NO_INIT)) && (!defined(PF_NO_SHELL)) +Err CompileCustomFunctions( void ) +{ + Err err; + int i = 0; +/* Compile Forth words that call your custom functions. +** Make sure order of functions matches that in LoadCustomFunctionTable(). +** Parameters are: Name in UPPER CASE, Function, Index, Mode, NumParams +*/ + err = CreateGlueToC( "F4711", i++, C_RETURNS_VALUE, 1 ); + if( err < 0 ) return err; + err = CreateGlueToC( "ARGC" , i++, C_RETURNS_VALUE, 0 ); + if( err < 0 ) return err; + err = CreateGlueToC( "ARGV" , i++, C_RETURNS_VALUE, 1 ); + if( err < 0 ) return err; + + return 0; +} +#else +Err CompileCustomFunctions( void ) { return 0; } +#endif diff --git a/custom/02-argc-argv/demo.correct_output b/custom/02-argc-argv/demo.correct_output new file mode 100644 index 0000000..8d115ed --- /dev/null +++ b/custom/02-argc-argv/demo.correct_output @@ -0,0 +1,4 @@ +argc=3 +argv[0] = '01: One FORTH to' +argv[1] = '02:find' +argv[2] = '03:them' diff --git a/custom/02-argc-argv/demo.fth b/custom/02-argc-argv/demo.fth new file mode 100644 index 0000000..fb19b5a --- /dev/null +++ b/custom/02-argc-argv/demo.fth @@ -0,0 +1,7 @@ +argc ." argc=" . cr + +argc 0 do + i dup + ." argv[" (.) type ." ] = '" + argv type ." '" cr +loop diff --git a/custom/02-argc-argv/diff-pf_main.c b/custom/02-argc-argv/diff-pf_main.c new file mode 100644 index 0000000..673b8bb --- /dev/null +++ b/custom/02-argc-argv/diff-pf_main.c @@ -0,0 +1,25 @@ +*** old-pf_main.c Wed Jan 1 21:13:11 2025 +--- new-pf_main.c Wed Jan 1 21:11:33 2025 +*************** +*** 58,63 **** +--- 58,64 ---- + } + #else + ++ void fillScriptParams( int startItem, int NoItems, char *argv[] ); /* forward declaration for cf_demo2.c */ + int main( int argc, char **argv ) + { + #ifdef PF_STATIC_DIC +*************** +*** 90,95 **** +--- 91,100 ---- + c = *s++; + switch(c) + { ++ case '-': /* call code in cf_demo2.c then skip remaining arguments */ ++ fillScriptParams( i+1, argc, argv ); ++ i = argc; ++ break; + case 'i': + IfInit = TRUE; + DicName = NULL; diff --git a/custom/02-argc-argv/go-v1.sh b/custom/02-argc-argv/go-v1.sh new file mode 100644 index 0000000..0e7d9b5 --- /dev/null +++ b/custom/02-argc-argv/go-v1.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Compile pForth with custom code and show that this works. +# We assume a posix shell and system (but adaption should be easy to others). +# This improved version only compiles the custom code defined in CF_SOURCES. +# Warning: This patches the existing source tree and might create confusion when not used on separate Git branch in case an error occurs. +# Tested on MSYS2-Cygwin, Linux, FreeBSD (X86_64 architecture each), NetBSD (i386 architecture) + +# copy demo sources. Thus we do not need to change the make file. + +cp cf_demo2.c ../../csrc/ +patch -bcN ../../csrc/pf_main.c diff-pf_main.c +# patch was created via: diff -c old-pf_main.c new-pf_main.c > diff-pf_main.c +# patch options: -b=backup, -c=context-diff (same as in diff), -N=ignore reversed or already applied patches + +echo +echo "----------------------------------------" +echo "make pforth (skip standalone executable)" +echo "----------------------------------------" +MAKE_CMD=`../get-make-cmd.sh` +cd ../../platforms/unix/ +CF_SOURCES="cf_demo2.c" $MAKE_CMD pforth.dic + +echo +echo "---------------------------" +echo "show that custom code works" +echo "---------------------------" +(./pforth -q ../../custom/02-argc-argv/demo.fth -- "01: One FORTH to" 02:find 03:them) | tee cf-demo.output +echo "- - - - - - - - - - - - - -" +echo "is this the expected output?" +diff -s cf-demo.output ../../custom/02-argc-argv/demo.correct_output + +echo +echo "----------------------------" +echo "restore original source tree" +echo "----------------------------" +rm cf-demo.output +rm ../../csrc/cf_demo2.c +mv ../../csrc/pf_main.c.orig ../../csrc/pf_main.c +CF_SOURCES="cf_demo2.c" $MAKE_CMD clean + +echo +echo "-----------------" +echo "That's all folks!" +echo "-----------------" diff --git a/custom/cf_helpers.h b/custom/cf_helpers.h new file mode 100644 index 0000000..6de3dff --- /dev/null +++ b/custom/cf_helpers.h @@ -0,0 +1,42 @@ +/* custom code for pforth (hence Custom Forth = cf) + This is a hack and for demonstration purposes only. + It simplifies a few things (like patching of Makefile) + but violates rules for production C code (e.g placing definitions in header files and terminating at the 1st sign of trouble). + Defines helper functions for several examples. +*/ + +#ifndef CF_HELPERS_H +#define CF_HELPERS_H + +#include /* fprintf() */ +#include /* exit() */ + +static void panic( const char* exitMsg ) +{/* Terminates program with panic message on stderr + Beware: might mess up the terminal sometimes :-/ (restarting pforth works fine though) + */ + fprintf(stderr, "\n====> panic! about to exit: %s\n", exitMsg); + exit(1); +} + +static void* safeAlloc( size_t bytes ) +{/* allocate memory and panic if that did not succees + */ + void* result = malloc(bytes); /* TODO: replace by calloc() ?! */ + if(result==NULL) + panic("can not allocate memory!"); + return result; +} + +static char* to_C_string( cell_t strData, cell_t iStrLen ) +{/* copy PForth string to C-string (zero terminated) + Don't forget to free() the result! + TODO: check if there is already defined a similar function in pforth + */ + char* buf = safeAlloc(iStrLen+1); + memcpy( buf, (void*)strData, iStrLen ); + buf[iStrLen] = 0; + return buf; +} + +#endif diff --git a/custom/get-make-cmd.sh b/custom/get-make-cmd.sh new file mode 100644 index 0000000..135f9f4 --- /dev/null +++ b/custom/get-make-cmd.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +os=`uname -o 2>/dev/null` +if test -z "$os" ; then + # NetBSD-uname does not implement '-o' option + os=`uname -s` +fi + +case "$os" in + "FreeBSD" | "NetBSD") + echo "gmake" + ;; + *) # e.g. "Msys" | "GNU/Linux" + echo "make" + ;; +esac diff --git a/platforms/unix/Makefile b/platforms/unix/Makefile index 0e4eacb..f5cbdc6 100644 --- a/platforms/unix/Makefile +++ b/platforms/unix/Makefile @@ -58,6 +58,13 @@ FULL_WARNINGS = \ -Wmissing-prototypes \ -Wmissing-declarations +# custom code options +ifndef CF_SOURCES + # note: setting CF_PARAM does not help: It only deactivates all definitions in pfcustom.c, which we only include when no custom code is compiled! + # We do not remove CF_PARAM from code and documentation until we can verify that Makefiles continue to work. + CF_SOURCES=pfcustom.c +endif + DEBUGOPTS = -g CCOPTS = $(WIDTHOPT) -x c -O2 $(FULL_WARNINGS) $(EXTRA_CCOPTS) $(DEBUGOPTS) @@ -81,7 +88,7 @@ PFINCLUDES = pf_all.h pf_cglue.h pf_clib.h pf_core.h pf_float.h \ pfcompil.h pfinnrfp.h pforth.h PFBASESOURCE = pf_cglue.c pf_clib.c pf_core.c pf_inner.c \ pf_io.c pf_io_none.c pf_main.c pf_mem.c pf_save.c \ - pf_text.c pf_words.c pfcompil.c pfcustom.c + pf_text.c pf_words.c pfcompil.c $(CF_SOURCES) PFSOURCE = $(PFBASESOURCE) $(IO_SOURCE) VPATH = .:$(CSRCDIR):$(CSRCDIR)/posix:$(CSRCDIR)/stdio:$(CSRCDIR)/win32_console:$(CSRCDIR)/win32