-
Notifications
You must be signed in to change notification settings - Fork 115
Example of adding custom C functions #187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 17 commits
8aa0723
cb40ed0
3efb3b3
70e74ae
1bd41cf
09dffd0
6889a15
aeff369
20fdba6
f605544
866422e
03a4d0a
122ab9b
409cee3
6daae79
ea826e9
e0dd86f
1d3e5e4
82b212b
5cb19e1
b9e5235
912172e
128666c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| #include "pf_all.h" /* lots of stuff */ | ||
| #include "cf_helpers.h" /* to_C_string */ | ||
| #include<errno.h> /* errno */ | ||
| #include<limits.h> /* PATH_MAX */ | ||
| #include<stdlib.h> /* malloc */ | ||
| #include<stdio.h> /* asprintf, sprintf */ | ||
| #include<string.h> /* strerror */ | ||
| #include<sys/stat.h> /* struct stat, stat */ | ||
|
|
||
| /* | ||
| * 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 words are available | ||
| */ | ||
| return 11 + 47*Val; | ||
| } | ||
|
|
||
| static cell_t FileInfo( cell_t path_caddr, cell_t path_len ) { | ||
| char* path = to_C_string( path_caddr, path_len ); | ||
| struct stat info; | ||
| const char* fmtErr = "error{ id=%i, desc='%s', path='%s' }"; | ||
| const char* fmtDir = "directory{ path='%s' }"; | ||
| const char* fmtFile = "file{ size=%i, path='%s' }"; | ||
| char* result; | ||
| /* MSYS/Cygwin may warn than asprintf() is not defined but compile and run just fine :-/ */ | ||
| if( stat(path, &info) == -1 ) | ||
| asprintf( &result, fmtErr, errno, strerror(errno), path ); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not go through the Forth output path so it will not work over a serial port. Demonstrating custom C calling does not require calling complex host functions.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just to clarify: asprintf() allocates a buffer of correct size into which it sprintf's the formatted output.
That's what is done here. |
||
| else { | ||
| if( S_ISDIR(info.st_mode) ) | ||
| asprintf( &result, fmtDir, path ); | ||
| else | ||
| asprintf( &result, fmtFile, info.st_size, path ); | ||
| } | ||
| PUSH_DATA_STACK( (cell_t) result ); | ||
| return (cell_t)strlen(result); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. path is not getting freed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added FREE-C word. |
||
| } | ||
|
|
||
|
|
||
| /**************************************************************** | ||
| ** 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] = FileInfo; | ||
| return 0; | ||
| } | ||
|
|
||
| #else | ||
| /****************** | ||
| ** If your loader supports global initialization (most do.) then just | ||
| ** create the table like this. | ||
| */ | ||
| CFunc0 CustomFunctionTable[] = | ||
| { | ||
| (CFunc0) f4711, | ||
| (CFunc0) FileInfo | ||
| }; | ||
| #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( "FILE-INFO", i++, C_RETURNS_VALUE, 2 ); | ||
| if( err < 0 ) return err; | ||
|
|
||
| return 0; | ||
| } | ||
| #else | ||
| Err CompileCustomFunctions( void ) { return 0; } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| \ 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 | ||
|
guberathome marked this conversation as resolved.
Outdated
|
||
| : SHOW-FILE-INFO | ||
| ." " FILE-INFO type cr | ||
| ; | ||
| ." FILE-INFO: " cr | ||
| s" Makefile" SHOW-FILE-INFO | ||
| s" ../../examples" SHOW-FILE-INFO | ||
| s" fileNotHere" SHOW-FILE-INFO | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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/ | ||
| CUSTOM_SOURCES="cf_demo1.c" | ||
| export CUSTOM_SOURCES | ||
|
|
||
| echo | ||
| echo "----------------------------------------" | ||
| echo "make pforth (skip standalone executable)" | ||
| echo "----------------------------------------" | ||
| MAKE_CMD=`../get-make-cmd.sh` | ||
| cd ../../../platforms/unix/ | ||
|
|
||
| $MAKE_CMD pforth.dic # we just need a PForth executable+dictionary | ||
|
|
||
| echo | ||
| echo "---------------------------" | ||
| echo "show that custom code works" | ||
| echo "---------------------------" | ||
| ./pforth -q ../../examples/custom/01-parameter-passing/demo.fth | ||
|
|
||
| echo | ||
| echo "----------------------------" | ||
| echo "restore original source tree" | ||
| echo "----------------------------" | ||
| rm ../../csrc/cf_helpers.h | ||
| rm ../../csrc/cf_demo1.c | ||
| $MAKE_CMD clean | ||
|
|
||
| echo | ||
| echo "-----------------" | ||
| echo "That's all folks!" | ||
| echo "-----------------" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /* 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 <stdlib.h> /* malloc */ | ||
| #include <string.h> /* memcpy */ | ||
|
|
||
| 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! | ||
| */ | ||
| char* buf = malloc(iStrLen+1); | ||
| if( buf != NULL ) { | ||
| memcpy( buf, (void*)strData, iStrLen ); | ||
| buf[iStrLen] = 0; | ||
| } | ||
| return buf; | ||
| } | ||
|
|
||
| #endif /* CF_HELPERS_H */ | ||
|
guberathome marked this conversation as resolved.
Outdated
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
Uh oh!
There was an error while loading. Please reload this page.