% Content-encoding: UTF-8 \documentclass[ngerman]{article} \usepackage[utf8]{inputenc} \usepackage{multicol,babel} \setcounter{secnumdepth}{0} \setcounter{tocdepth}{0} \renewcommand{\reftextbefore}{auf der vorherigen Seite} \renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite} \renewcommand{\reftextafter}{auf der nächsten Seite} \renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite} \renewenvironment{lyxcode} {\par\begin{list}{}{ \setlength{\leftmargin}{0pt} \setlength{\rightmargin}{\leftmargin} \setlength{\listparindent}{0pt}% needed for AMS classes \raggedright \setlength{\itemsep}{0pt} \setlength{\parsep}{0pt} \normalfont\ttfamily}% \item[]} {\end{list}} \begin{document} \title{\emph{F}orth \emph{I}nspired \emph{C}ommand \emph{L}anguage (\emph{FICL}% \thanks{\protect\url{http://ficl.sourceforge.net}})} \author{Gerd Franzkowiak} \ifx\shorttitle\undefined\else \shorttitle{FICL} \fi \maketitle \begin{abstract} \noindent Auf der Suche nach einem kleinen FORTH--System für meine Embedded--Linux--Entwicklung stieß ich vor einiger Zeit auf FICL. Erfreulicherweise konnte ich auf einem ARM9--System mit \texttt{Clibc--Linux}% \footnote{Linux wurde compiliert mit der für eingebettete Systeme optimierten C library \texttt{Clibc}} ein ca. 100~KB großes statisches \emph{ficl} erzeugen. Dies schien mir ansprechend genug, um das System mal etwas näher zu beleuchten.\\ \\ \emph{Zitat:}\\ \textsl{\scriptsize Wenn die meisten sich schon armseliger Kleider und Möbel schämen, wie viel mehr sollten wir uns da erst armseliger Ideen und Weltanschauungen schämen.}{\scriptsize{} }\textit{\scriptsize }\\ \textit{\scriptsize (Albert Einstein)}{\scriptsize{} }{\scriptsize \par} \end{abstract} \begin{multicols}{2} \section{Einführung} Eigentlich ist \emph{FICL} nicht als 100\%--iges FORTH entstanden. Es lehnt sich aber stark an den ANS von 1994 und vermittelt mit der ersten Anwendung sofort FORTH--feeling. Entwickelt wurde \emph{FICL} von \emph{John~Sadler} und auf der $20^{th}$ FORML Conference 1989 vorgestellt. John~Sadler wollte ein Tool, welches für schnelle und effektive Entwicklungen in andere Anwendungen, vorrangig C/C++, einzubetten ist und auf verschiedenster Firmware nutzbar sein kann. Aufgrund dessen hat John den Kern komplett in ANSI--C geschrieben. Die Lizenz von \emph{FICL} ist im Stil der BSD--Lizenz und stellt somit kaum ein Hindernis für eine große Zahl von Projekten dar. Dementsprechend kann man \emph{FICL} auch als Bootloader für den Kernel von FreeBSD finden. \section{Erste Schritte} Mir ging es bei meinem ersten Versuch darum, einfach mal meinen Treiber, den ich für ein FPGA auf einer ARM9--Anwendung geschrieben hatte, anzusprechen. Gesagt, getan, nahm ich mir als Toolchain die ScratchBox von Nokia (GPL) zur Hand, schob die Quellen von ficl4.0.31 hinein, compilierte erfolgreich und kopierte es auf das genannte Board. Ich startete <./ficl> und --- siehe da; \textit{loading, Date, version} --- es lief. Mit der Eingabe von \emph{WORDS} fühlte ich mich auch gleich in der richtigen Umgebung. Es ist fast alles vorhanden, was man in FORTH so benötigt. Um nun meine Treiber mal anzusprechen, versuchte ich, \begin{lyxcode} s\textquotedbl{}~/dev/myfpga\textquotedbl{}~r/w~open-file~throw~Value~fd \end{lyxcode} einzugeben, und konnte mit \begin{lyxcode} s''~xxx''~fd~write-file~fd~close-file \end{lyxcode} erste Reaktionen erleben. \begin{quote} Das nahezu ideale Tool ! \end{quote} Welche Reaktionen das auf dem Board waren, ist hier nicht so wichtig, es wurden jedenfalls Tabellen--Werte im FPGA eingetragen. \section{Der Aufbau} \begin{center} \includegraphics[scale=0.5]{2009-02/ficl-system} \end{center} In FICL wird mit dem Start ein System angelegt, welches aus mindestens einer virtuellen Maschine, mit zugehörigen Dictionarys, besteht, aber auch aus mehreren VMs bestehen kann. Mit dem Anlegen eines FICL--Systems existiert automatisch ein ANS~Forth~CORE~word~set, welches dann, nach der entsprechenden Intitialisierung, die Dictionarys der virtuellen Maschine(n) anlegt. Das Schöne an den virtuellen Maschinen ist, dass jede einen eigenen Status hält und eigene I/O--Kanäle möglich sind. Darüber hinaus ist, zu des Programmierers Glück, FICL--Code auch noch \emph{thread~safe} und \emph{re--entrant}. Für denjenigen, welcher sich in FICL einarbeiten möchte, liegt eine umfangreiche Dokumentation vor und die Sourcen bieten natürlich beste Informationen. \subsection{Das Prinzip} FICL ist, entsprechend Anton Ertls Beschreibung% \footnote{\url{http://www.complang.tuwien.ac.at/forth/threaded-code.html}% } über Threaded Code als Switch Threading Forth entwickelt worden. Das Application Interface gestaltet sich recht einfach, gut und überschaubar. Ein Beispiel für den Aufruf aus anderen C--Applikationen kann so aussehen, wie es in \textit{main.c} vorliegt: \begin{lyxcode} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~\textquotedbl{}ficl.h\textquotedbl{}}{\scriptsize \par} {\scriptsize int~main(int~argc,~char~{*}{*}argv)~\{}{\scriptsize \par} {\scriptsize{}~~~~int~returnValue~=~0;}{\scriptsize \par} {\scriptsize{}~~~~char~buffer{[}256{]};}{\scriptsize \par} {\scriptsize{}~~~~ficlVm~{*}vm;}{\scriptsize \par} {\scriptsize{}~~~~ficlSystem~{*}system;}{\scriptsize \par} {\scriptsize{}~~~~system~=~ficlSystemCreate(NULL);~/{*}~System~anlegen~{*}/}{\scriptsize \par} {\scriptsize{}~~~~ficlSystemCompileExtras(system);}{\scriptsize \par} {\scriptsize{}~~~~vm~=~ficlSystemCreateVm(system);~/{*}~Virtual~Machine~erzeugen~{*}/}{\scriptsize \par} {\scriptsize{}~~~~returnValue~=~ficlVmEvaluate(vm,} {\scriptsize{}~~~~~~~~~~~~~~~~~~~~~~~~~\textquotedbl{}.ver~.(~\textquotedbl{}~\_\_DATE\_\_~\textquotedbl{}~)~cr~quit\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~~~while~(returnValue~!=~FICL\_VM\_STATUS\_USER\_EXIT)~\{}{\scriptsize \par} {\scriptsize{}~~~~~~~~fputs(FICL\_PROMPT,~stdout);}{\scriptsize \par} {\scriptsize{}~~~~~~~~fgets(buffer,~sizeof(buffer),~stdin);}{\scriptsize \par} {\scriptsize{}~~~~~~~~returnValue~=~ficlVmEvaluate(vm,~buffer);}{\scriptsize \par} {\scriptsize{}~~~~\}}{\scriptsize \par} {\scriptsize{}~~~~ficlSystemDestroy(system);}{\scriptsize \par} {\scriptsize{}~~~~return~0;}{\scriptsize \par} {\scriptsize \}~}{\scriptsize \par} \end{lyxcode} Genau auf diese Art und Weise ist man in einem C--Projekt auch in der Lage, mal einen kleinen interaktiven Part einzubinden und sich ein wenig im System umzusehen. Mit der Funktion \textsc{ficlSystemCreate} und einer angepassten Struktur, statt dem NULL--Pointer in der Parameterübergabe, bieten sich dann weitere Möglichkeiten eines eigenen Systems an. \begin{lyxcode} {\scriptsize struct~ficlSystemInformation~\{}{\scriptsize \par} {\scriptsize{}~int~size;~~~~~~~~~~~/{*}~structure~size~tag~for~versioning~{*}/}{\scriptsize \par} {\scriptsize{}~void~{*}context;~~~~~~/{*}~Initializes~VM's~context~pointer~-} {\scriptsize{}~~~~~~~~~~~~~~~~~~~~~~~~~~for~application~use~{*}/}{\scriptsize \par} {\scriptsize{}~int~dictionarySize;~/{*}~Size~of~system's~Dictionary,~in~cells~{*}/}{\scriptsize \par} {\scriptsize{}~int~stackSize;~~~~~~/{*}~Size~of~all~stacks~created,~in~cells~~{*}/}{\scriptsize \par} {\scriptsize{}~ficlOutputFunction~textOut;~~/{*}~default~textOut~function~{*}/}{\scriptsize \par} {\scriptsize{}~ficlOutputFunction~errorOut;~/{*}~textOut~function~used~for~errors~{*}/}{\scriptsize \par} {\scriptsize{}~int~environmentSize;~/{*}~Size~of~Environment~dictionary,~in~cells~{*}/}{\scriptsize \par} {\scriptsize \};~}{\scriptsize \par} \end{lyxcode} Zu erkennen ist, dass mit dieser Struktur schon eigene Ausgabe--Funktionen festgelegt werden können. Soll dies für jede VM erfolgen, so wird es über Callback--Funktionen der VMs realisiert. \subsection{Netzwerk--Anbindung} Schaut man sich z.B. FORTH--83--Systeme an, so war es schon eine Kleinigkeit, wenn man den Ein--/Ausgabestrom auf andere Hardware umlenken wollte. Es genügte schon, die Worte KEY?, KEY und EMIT anzupassen, und ein FORTH--System funktionierte beispielsweise über eine serielle Schnittstelle.\\ Bedenkt man, welche Klimmzüge C--Programme da vollziehen mussten und welche Software--Monster (PC--Irgendwas) da notwendig wurden, war FORTH immer schon entwicklerfreundlich.\\ Mit Netzwerkanbindungen, z.B. über einen TCP/IP--Stack, ist es natürlich nicht so einfach. Nicht einfach ASCII--Zeichen werden übertragen, sondern ein kompletter Protokollstapel muss durchlaufen werden.\\ FICL bietet mit seiner Methode jedoch ein recht elegantes Verfahren, was ein FORTH, sofern in dessen Umgebung ein TCP/IP--Stack vorhanden ist, schnell über ein Netzwerk bedienbar macht.\\ Meine Versuche waren das Anbinden des TCP/IP--Stacks an das FICL--System auf folgende Art: \begin{quote} \begin{lyxcode} {\scriptsize /{*}}{\scriptsize \par} {\scriptsize{}~{*}~my.c}{\scriptsize \par} {\scriptsize{}~{*}/}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~\textquotedbl{}ficl.h\textquotedbl{}}{\scriptsize \par} {\scriptsize /{*}}{\scriptsize \par} {\scriptsize{}~{*}~Example~of~server~using~TCP~protocol.}{\scriptsize \par} {\scriptsize{}~{*}/}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#include~}{\scriptsize \par} {\scriptsize \#define~SERV\_TCP\_PORT~65535}{\scriptsize \par} {\scriptsize \#define~SERV\_HOST\_ADDR~\textquotedbl{}\textquotedbl{}}{\scriptsize \par} {\scriptsize void~catcher();}{\scriptsize \par} {\scriptsize typedef~struct~\{}{\scriptsize \par} {\scriptsize{}~~FILE~~~{*}fptr;}{\scriptsize \par} {\scriptsize{}~~int~~~~newsockfd;}{\scriptsize \par} {\scriptsize \}~myVmSockPar;}{\scriptsize \par} {\scriptsize myVmSockPar~vmSockPar;}{\scriptsize \par} {\scriptsize int~~~~~~~~~sockfd;}{\scriptsize \par} {\scriptsize /{*}}{\scriptsize \par} {\scriptsize{}~{*}~Callback~function}{\scriptsize \par} {\scriptsize{}~{*}/}{\scriptsize \par} {\scriptsize void~myCbTextOut(ficlCallback~{*}pCB,~char~{*}text)~\{}{\scriptsize \par} {\scriptsize{}~~char~buffer{[}256{]};}{\scriptsize \par} {\scriptsize{}~~FICL\_IGNORE(pCB);}{\scriptsize \par} {\scriptsize{}~~if~(text~!=~NULL)~\{}{\scriptsize \par} {\scriptsize{}~~~~fputs(text,~stdout);}{\scriptsize \par} {\scriptsize{}~~\}}{\scriptsize \par} {\scriptsize{}~~else~\{}{\scriptsize \par} {\scriptsize{}~~~~fflush(stdout);}{\scriptsize \par} {\scriptsize{}~~\}}{\scriptsize \par} {\scriptsize{}~~return;}{\scriptsize \par} {\scriptsize \}}{\scriptsize \par} {\scriptsize /{*}}{\scriptsize \par} {\scriptsize{}~{*}Callback~function~for~TCP~connection}{\scriptsize \par} {\scriptsize{}~{*}/}{\scriptsize \par} {\scriptsize void~mySocketTextOut(ficlCallback~{*}pCB,~char~{*}text)~\{}{\scriptsize \par} {\scriptsize{}~~FICL\_IGNORE(pCB);}{\scriptsize \par} {\scriptsize{}~~if~(text~!=~NULL)~\{}{\scriptsize \par} {\scriptsize{}~~~~fputs(text,~vmSockPar.fptr);}{\scriptsize \par} {\scriptsize{}~~\}}{\scriptsize \par} {\scriptsize{}~~return;}{\scriptsize \par} {\scriptsize \}}{\scriptsize \par} {\scriptsize int~myVmSocketConnection(ficlVm~{*}vm,~int~vmsockfd)~\{}{\scriptsize \par} {\scriptsize{}~~char~~~buffer{[}256{]};}{\scriptsize \par} {\scriptsize{}~~int~~~~returnValue~=~0;}{\scriptsize \par} {\scriptsize{}~~vmSockPar.fptr~=~fdopen(vmsockfd,~\textquotedbl{}r+\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~/{*}~The~old~one~was~vmSetTextOut~{*}/}{\scriptsize \par} {\scriptsize{}~~ficlVmSetTextOut(vm,~\&mySocketTextOut);}{\scriptsize \par} {\scriptsize{}~~ficlVmSetErrorOut(vm,~\&mySocketTextOut);}{\scriptsize \par} {\scriptsize{}~~while(returnValue~!=~FICL\_VM\_STATUS\_USER\_EXIT)~\{}{\scriptsize \par} {\scriptsize{}~~~~fputs(FICL\_PROMPT,~vmSockPar.fptr);}{\scriptsize \par} {\scriptsize{}~~~~fgets(buffer,~sizeof(buffer),~vmSockPar.fptr);}{\scriptsize \par} {\scriptsize{}~~~~returnValue~=~ficlVmEvaluate(vm,~buffer);}{\scriptsize \par} {\scriptsize{}~~\}}{\scriptsize \par} {\scriptsize{}~~fclose(vmSockPar.fptr);}{\scriptsize \par} {\scriptsize{}~~close(vmsockfd);}{\scriptsize \par} {\scriptsize{}~~return~FICL\_VM\_STATUS\_USER\_EXIT;}{\scriptsize \par} {\scriptsize \}}{\scriptsize \par} {\scriptsize void~catcher()~\{}{\scriptsize \par} {\scriptsize{}~~close(vmSockPar.newsockfd);}{\scriptsize \par} {\scriptsize{}~~close(sockfd);}{\scriptsize \par} {\scriptsize{}~~exit(1);}{\scriptsize \par} {\scriptsize \}}{\scriptsize \par} {\scriptsize \#define~NUMBEROFSYSTEMS~2}{\scriptsize \par} {\scriptsize \#define~NUMBEROFVMS~2}{\scriptsize \par} {\scriptsize \#define~STARTUP~0}{\scriptsize \par} {\scriptsize int~main(int~argc,~char~{*}{*}argv)~\{}{\scriptsize \par} {\scriptsize{}~int~~~~~~~~netuse;}{\scriptsize \par} {\scriptsize{}~int~~~~~~~~returnValue~=~0;}{\scriptsize \par} {\scriptsize{}~int~~~~~~~~pid;}{\scriptsize \par} {\scriptsize{}~int~~~~~~~~state;}{\scriptsize \par} {\scriptsize{}~char~~~~~~~buffer{[}256{]};}{\scriptsize \par} {\scriptsize{}~char~~~~~~~{*}selstr~=~buffer;}{\scriptsize \par} {\scriptsize{}~int~~~~~~~~clilen;}{\scriptsize \par} {\scriptsize{}~ficlVm~~~~~{*}vm{[}NUMBEROFVMS{]};}{\scriptsize \par} {\scriptsize{}~ficlSystem~{*}system{[}NUMBEROFSYSTEMS{]};}{\scriptsize \par} {\scriptsize{}~ficlSystem~{*}showtest;}{\scriptsize \par} {\scriptsize{}~struct~sockaddr\_in~server\_addr;}{\scriptsize \par} {\scriptsize{}~struct~sockaddr\_in~client\_addr;}{\scriptsize \par} {\scriptsize{}~ficlSystemInformation~myFiclInfo;}{\scriptsize \par} {\scriptsize{}~system{[}STARTUP{]}~=~ficlSystemCreate(NULL);}{\scriptsize \par} {\scriptsize{}~ficlSystemCompileExtras(system{[}STARTUP{]});}{\scriptsize \par} {\scriptsize{}~buildMyInterface(system{[}STARTUP{]});}{\scriptsize \par} {\scriptsize{}~vm{[}STARTUP{]}~=~ficlSystemCreateVm(system{[}STARTUP{]});}{\scriptsize \par} {\scriptsize{}~signal(SIGINT,catcher);}{\scriptsize \par} {\scriptsize{}~returnValue~=~ficlVmEvaluate(vm{[}STARTUP{]},}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\textquotedbl{}.ver~.(~\textquotedbl{}~\_\_DATE\_\_~\textquotedbl{}~)~cr~quit\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~/{*}~cmd~line~argument~for~socket~connection~?~{*}/}{\scriptsize \par} {\scriptsize{}~if~(argc~>~1)~\{}{\scriptsize \par} {\scriptsize{}~~selstr~=~argv{[}1{]};}{\scriptsize \par} {\scriptsize{}~~netuse~=~strcmp(selstr,~\textquotedbl{}tcp\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~\}}{\scriptsize \par} {\scriptsize{}~else~\{}{\scriptsize \par} {\scriptsize{}~~netuse~=~-1;}{\scriptsize \par} {\scriptsize{}~\}}{\scriptsize \par} {\scriptsize{}~/{*}~Without~TCP/IP~?~{*}/}{\scriptsize \par} {\scriptsize{}~if(netuse~!=~0)~\{}{\scriptsize \par} {\scriptsize{}~~~/{*}~stdin/stout~loop~{*}/}{\scriptsize \par} {\scriptsize{}~~~while~(returnValue~!=~FICL\_VM\_STATUS\_USER\_EXIT)~\{}{\scriptsize \par} {\scriptsize{}~~~~~fputs(FICL\_PROMPT,~stdout);}{\scriptsize \par} {\scriptsize{}~~~~~fgets(buffer,~sizeof(buffer),~stdin);}{\scriptsize \par} {\scriptsize{}~~~~~returnValue~=~ficlVmEvaluate(vm{[}STARTUP{]},~buffer);}{\scriptsize \par} {\scriptsize{}~~~\}}{\scriptsize \par} {\scriptsize{}~\}}{\scriptsize \par} {\scriptsize{}~/{*}~Remote~via~TCP/IP~{*}/}{\scriptsize \par} {\scriptsize{}~else~\{}{\scriptsize \par} {\scriptsize{}~~~/{*}}{\scriptsize \par} {\scriptsize{}~~~~{*}~Open~a~TCP~socket~(an~Internet~stream~socket~).}{\scriptsize \par} {\scriptsize{}~~~~{*}/}{\scriptsize \par} {\scriptsize{}~~~if~((sockfd~=~socket(AF\_INET,~SOCK\_STREAM,~0))~<~0)}{\scriptsize \par} {\scriptsize{}~~~~~fprintf(~stderr,~\textquotedbl{}server:~can't~open~stream~socket~!\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~~/{*}}{\scriptsize \par} {\scriptsize{}~~~~{*}~Bind~our~local~address~so~that~the~client~can~send~to~us.}{\scriptsize \par} {\scriptsize{}~~~~{*}/}{\scriptsize \par} {\scriptsize{}~~~bzero(~(char~{*})~\&server\_addr,~sizeof(server\_addr));}{\scriptsize \par} {\scriptsize{}~~~server\_addr.sin\_family~~~~~~=~AF\_INET;}{\scriptsize \par} {\scriptsize{}~~~server\_addr.sin\_addr.s\_addr~=~htonl(INADDR\_ANY);}{\scriptsize \par} {\scriptsize{}~~~server\_addr.sin\_port~~~~~~~~=~htons(SERV\_TCP\_PORT);}{\scriptsize \par} {\scriptsize{}~~~if(bind(sockfd,~(struct~sockaddr~{*})~\&server\_addr,}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~sizeof(server\_addr))~<~0)}{\scriptsize \par} {\scriptsize{}~~~~~fprintf(~stderr,~\textquotedbl{}SERVER:~can't~bind~local~address~!\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~~listen(sockfd,~5);}{\scriptsize \par} {\scriptsize{}~~~while~(returnValue~!=~FICL\_VM\_STATUS\_USER\_EXIT)~\{}{\scriptsize \par} {\scriptsize{}~~~~~/{*}}{\scriptsize \par} {\scriptsize{}~~~~~~{*}~Wait~for~a~connection~from~client~process.}{\scriptsize \par} {\scriptsize{}~~~~~~{*}~This~is~an~example~of~a~concurrent~server.}{\scriptsize \par} {\scriptsize{}~~~~~~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~clilen~=~sizeof(client\_addr);}{\scriptsize \par} {\scriptsize{}~~~~~vmSockPar.newsockfd~=~accept(sockfd,~\textbackslash{}}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~~~~~~~~~(struct~sockaddr~{*})\&client\_addr,~\textbackslash{}}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~~~~~~~~~\&clilen);}{\scriptsize \par} {\scriptsize{}~~~~~if~(vmSockPar.newsockfd~<~0)}{\scriptsize \par} {\scriptsize{}~~~~~~~fprintf(~stderr,~\textquotedbl{}SERVER:~accept~error~!\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~~~~/{*}~Child~process~successfully~created~?~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~if(~(pid~=~fork())~<~0)}{\scriptsize \par} {\scriptsize{}~~~~~~~fprintf(~stderr,~\textquotedbl{}server:~fork~error~!\textquotedbl{});}{\scriptsize \par} {\scriptsize{}~~~~~/{*}~child~process~?~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~else~if~(pid~==~0)~\{}{\scriptsize \par} {\scriptsize{}~~~~~~~/{*}~close~listening~socket~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~~~close(sockfd);}{\scriptsize \par} {\scriptsize{}~~~~~~~/{*}~process~the~requests~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~~~returnValue~=~myVmSocketConnection(vm{[}STARTUP{]},}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~vmSockPar.newsockfd);}{\scriptsize \par} {\scriptsize{}~~~~~~~exit(returnValue);}{\scriptsize \par} {\scriptsize{}~~~~~\}}{\scriptsize \par} {\scriptsize{}~~~~~else~\{}{\scriptsize \par} {\scriptsize{}~~~~~~~pid~=~wait(\&state);}{\scriptsize \par} {\scriptsize{}~~~~~~~/{*}~child~process~terminated~with~error~?~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~~~if(WIFEXITED(state)~==~0)~\{}{\scriptsize \par} {\scriptsize{}~~~~~~~~~close(vmSockPar.newsockfd);}{\scriptsize \par} {\scriptsize{}~~~~~~~\}}{\scriptsize \par} {\scriptsize{}~~~~~~~else~\{}{\scriptsize \par} {\scriptsize{}~~~~~~~~~/{*}~8-bit~equality~?~{*}/}{\scriptsize \par} {\scriptsize{}~~~~~~~~~if(WEXITSTATUS(state)==(FICL\_VM\_STATUS\_USER\_EXIT\&0xFF))}{\scriptsize \par} {\scriptsize{}~~~~~~~~~~~returnValue~=~FICL\_VM\_STATUS\_USER\_EXIT;}{\scriptsize \par} {\scriptsize{}~~~~~~~\}}{\scriptsize \par} {\scriptsize{}~~~~~\}}{\scriptsize \par} {\scriptsize{}~~~\}}{\scriptsize \par} {\scriptsize{}~\}}{\scriptsize \par} {\scriptsize{}~ficlSystemDestroy(system{[}STARTUP{]});}{\scriptsize \par} {\scriptsize{}~return~0;}{\scriptsize \par} {\scriptsize \}}{\scriptsize \par} \end{lyxcode} \end{quote} Sicherlich kann man das eleganter ausführen, aber prinzpiell ist zu erkennen, dass nur die Parameter der der C--Funktionen \textsc{fgets} und \textsc{fputs} unterschiedlich sind.\\ Ausgewählt wird mit dem Kommandozeilenparameter {}``\emph{tcp}'', welcher Ein--/Ausgabepfad genutzt wird. Existiert {}``\emph{tcp}'', dann wird die Socket--Verbindung aufgebaut und eine entsprechende Ein--/Ausgabe--Schleife über die C--Funktion {}``\textsc{myVmSocketConnection}{}`` aufgerufen. In dieser Funktion ist auch ein Beispiel, wie die Ausgabe einer virtuellen Maschine zugeordnet wird.\\ Fehlerbetrachtungen und weitere Dinge sind hier nicht einbezogen. \subsection{Der Bootloader und FICL} FreeBSD hat als Bestandteil seines Boot--Loaders FICL integriert. Nicht anders als bei anderen Betriebssystemen in der x86--Welt besteht das Problem, dass im Master--Boot--Record nur 446 Bytes zur Verfügung stehen, und so wurde in BSD folgender Ablauf während des \textquotedblleft{}Bootstrap--Vorgangs\textquotedblright{} festgelegte: \begin{enumerate} \item % \begin{minipage}[t]{1\columnwidth}% \begin{description} \item [{boot0}] 446 Byte Boot--Manager im MBR \end{description} % \end{minipage} \item % \begin{minipage}[t]{1\columnwidth}% \begin{description} \item [{boot1}] 512 Byte Programm im Boot--Record eines Slices, einer Partition, welches Informationen enthält und boot2 sucht \item [{boot2}] Schnittstelle, welche den Loader oder einen Kernel laden kann. \end{description} % \end{minipage} \item % \begin{minipage}[t]{1\columnwidth}% \begin{description} \item [{Loader}] wie es das BSD--Handbuch beschreibt --- \emph{eine schöne und einfach zu bedienende Boot--Konfigurations--Schnittstelle} --- oder man kann auch FICL sagen. \end{description} % \end{minipage} \end{enumerate} \subsubsection{Loader--Ablauf} %\footnote{12.3.3.1.~Loader~Ablauf\protect \\ %\url{http://www.freebsd.org/doc/de\_DE.ISO8859-1/books/handbook/book.html%\#BOOT-INTRODUCTION}}} \noindent \textit{\footnotesize Der Loader sucht während seiner Initialisierung nach Konsolen und Laufwerken, findet heraus, von welchem Laufwerk er gerade bootet und setzt dementsprechend bestimmte Variablen. Dann wird ein Interpreter gestartet, der Befehle interaktiv oder von einem Skript empfangen kann.}{\footnotesize \par} \noindent \textit{\footnotesize Danach liest der Loader die Datei /boot/loader.rc aus, welche ihn standardmäßig anweist, /boot/defaults/loader.conf zu lesen, wo sinnvolle Standardeinstellungen für diverse Variablen festgelegt werden und wiederum /boot/loader.conf für lokale Änderungen an diesen Variablen ausgelesen wird. Anschließend arbeitet dann loader.rc entsprechend dieser Variablen und lädt die ausgewählten Module und den gewünschten Kernel.}{\footnotesize \par} \noindent \textit{\footnotesize In der Voreinstellung wartet der Loader 10 Sekunden lang auf eine Tastatureingabe und bootet den Kernel, falls keine Taste betätigt wurde. Falls doch eine Taste betätigt wurde, wird dem Benutzer eine Eingabeaufforderung angezeigt. Sie nimmt einen einfach zu bedienenden Befehlssatz entgegen, der es dem Benutzer erlaubt, Änderungen an Variablen vorzunehmen, Module zu laden, alle Module zu entladen oder schließlich zu booten bzw. neu zu booten.}{\footnotesize \par} Ich habe hier mal den Auszug aus dem BSD--Handbuch übernommen, da dieser eigentlich schon alles erklärt. Wissenswert in diesem Zusammenhang ist noch, dass der Loader in einem \textbf{\emph{B}}\emph{oo}\textbf{\emph{T}}\emph{ e}\textbf{\emph{X}}\emph{tender (BTX) ausgeführt wird. BTX ist ein Code--Teil, der den Prozessor in den Protected~Mode umschaltet, Descriptor--Tabellen anlegt und eine SysCall--Möglichkeit schafft. BTX ist somit eine Schutzumgebung für das Programm.~}% \footnote{Sollte sich jemand an meinen Leserbrief aus der VD 4/2006 {}``\textsl{Die Anwendung~--~Automatisierung}'' erinnern, so war dort der Gedanke aufgekommen, ein FORTH für Roboter zu verwenden. Ich denke, mit diesem Ansatz des BSD--Loaders sind schon ein paar kleine Voraussetzungen gegeben, welche aufgegriffen werden können.% } \section{Was geht und was geht nicht} Wie in meinen ersten Ausführungen schon dargelegt, ist fast alles, was ANS--FORTH so bietet, auch in FICL vorhanden. Ein paar kleine Einschränkungen gibt es jedoch. Die Worte KEY? und KEY existieren normal nicht, sind aber z.B. im BSD--Loader durch den Loader, der dann FICL einbindet, realisiert. \subsection{Ein FORTH--Wort in C} Auf meinem System habe ich z.B. KEY? mal eingebunden und wer die unterschiedlichen Möglichkeiten in .nixen und Linux kennt, der weiß, welche Herausforderung das werden kann. Ich habe es mal --- nicht portabel --- realisiert und möchte damit auch ein Beipiel aufzeigen, wie z.B. eine C--Funktion in FICL als FORTH--Wort eingebunden werden kann. \begin{enumerate} \item Code des Wortes/Primitives\\ % \begin{minipage}[t]{1\columnwidth}% \begin{lyxcode} {\scriptsize /{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}}{\scriptsize \par} {\scriptsize {*}{*}~functions~used~by~primitives~}{\scriptsize \par} {\scriptsize {*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}/}{\scriptsize \par} {\scriptsize static~int~arrived\_char~=~-1;}{\scriptsize \par} {\scriptsize int~availchar(void)~\{}{\scriptsize \par} {\scriptsize{}~~~char~buffer{[}1{]}~=~\textquotedbl{}\textquotedbl{};~~~/{*}~Buffer~bereitstellen~~~~~~{*}/}{\scriptsize \par} {\scriptsize{}~~~struct~termio~old,new;~/{*}~struct~termio~in~termio.h~{*}/}{\scriptsize \par} {\scriptsize{}~~~int~flags,~counted,~rval;~/{*}~deklariert~{*}/}{\scriptsize \par} {\scriptsize{}~~~if~(arrived\_char~!=~-1)}{\scriptsize \par} {\scriptsize{}~~~~~~return~FICL\_TRUE;~/{*}~char~from~previos~keypressed()~{*}/}{\scriptsize \par} {\scriptsize{}~~~/{*}~waitchar~OFF~{*}/}{\scriptsize \par} {\scriptsize{}~~~ioctl(0,TCGETA,\&old);~/{*}~Alte~Parameter~holen~{*}/}{\scriptsize \par} {\scriptsize{}~~~new~=~old;~/{*}~und~merken~{*}/}{\scriptsize \par} {\scriptsize{}~~~new.c\_lflag~=~\textasciitilde{}(ECHO|ICANON);~/{*}~Echo~ausschalten~{*}/}{\scriptsize \par} {\scriptsize{}~~~new.c\_cc{[}VMIN{]}~=~0;~/{*}~Pufferung~aus~{*}/}{\scriptsize \par} {\scriptsize{}~~~new.c\_cc{[}VTIME{]}~=~0;~/{*}~Warte~0/10~s~auf~Zeichen~{*}/}{\scriptsize \par} {\scriptsize{}~~~ioctl(0,TCSETA,\&new);~/{*}~Neue~Werte~setzen~{*}/}{\scriptsize \par} {\scriptsize{}~~~counted~=~read(0,~buffer,~1);}{\scriptsize \par} {\scriptsize{}~~~if~(counted==1)~\{}{\scriptsize \par} {\scriptsize{}~~~~~~arrived\_char~=~buffer{[}0{]};}{\scriptsize \par} {\scriptsize{}~~~~~~rval~=~FICL\_TRUE;}{\scriptsize \par} {\scriptsize{}~~~\}}{\scriptsize \par} {\scriptsize{}~~~else}{\scriptsize \par} {\scriptsize{}~~~~~~rval~=~FICL\_FALSE;}{\scriptsize \par} {\scriptsize{}~~~/{*}~waitchar~ON~{*}/}{\scriptsize \par} {\scriptsize{}~~~ioctl(0,TCSETA,\&old);~/{*}~Alte~Werte~setzen,~haengt~bash~{*}/}{\scriptsize \par} {\scriptsize{}~~~return~rval;}{\scriptsize \par} {\scriptsize \}~} \end{lyxcode} % \end{minipage} \item FORTH~primitive\\ % \begin{minipage}[t]{1\columnwidth}% \begin{lyxcode} \textrm{\scriptsize /{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}}{\scriptsize \par} \textrm{\scriptsize{}~{*}{*}~~~~~~~~~~~key?~-~check~for~a~character~from~stdin~(FACILITY)}{\scriptsize \par} \textrm{\scriptsize{}~{*}{*}}{\scriptsize \par} \textrm{\scriptsize{}~{*}{*}~key?~(~--~flag~)}{\scriptsize \par} \textrm{\scriptsize{}~{*}{*}}{\scriptsize \par} \textrm{\scriptsize{}~{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}{*}/}{\scriptsize \par} \textrm{\scriptsize static~void~ficlPrimitiveKeyQuestion(ficlVm~{*}vm)~\{}{\scriptsize \par} \textrm{\scriptsize \#if~FICL\_ROBUST~>~1}{\scriptsize \par} \textrm{\scriptsize{}~~~~FICL\_STACK\_CHECK(vm->dataStack,~0,~1);}{\scriptsize \par} \textrm{\scriptsize \#endif}{\scriptsize \par} \textrm{\scriptsize{}~~~~/{*}~But~here~do~the~right~thing.~{*}/}{\scriptsize \par} \textrm{\scriptsize{}~~~~ficlStackPushInteger(~vm->dataStack,~availchar()~);}{\scriptsize \par} \textrm{\scriptsize{}~~~~return;}{\scriptsize \par} \textrm{\scriptsize \}} \end{lyxcode} % \end{minipage} \item FORTH--Wort erstellen\\ % \begin{minipage}[t]{1\columnwidth}% \begin{lyxcode} {\scriptsize /{*}}{\scriptsize \par} {\scriptsize{}~{*}~Corresponding~ficlBuild~~calls...~}{\scriptsize \par} {\scriptsize{}~{*}/}{\scriptsize \par} {\scriptsize ficlBuild(pSys,~\textquotedbl{}KEY?\textquotedbl{},~ficlPrimitiveKeyQuestion,~FICL\_WORD\_DEFAULT);} \end{lyxcode} % \end{minipage} \end{enumerate} \subsection{Nicht ganz ausgereift} Nach einigen Experimenten bin ich auf ein Problem mit doppeltgenauen Stackwerten gestoßen. Meines Wissens erzeugt die Eingabe einer Zahl mit einem Punkt eine doppeltgenaue Zahl auf dem Stack, welche ihren high--Anteil auf dem TOS liegen hat und den low--Anteil auf dem NOS. Leider war die Reihenfolge vertauscht und ich habe mich bemüht, eine Lösung über ?NUMBER zu realisieren. Die Lösung funktioniert erst einmal, aber auch die Tatsache, dass der Punkt an beliebiger Stelle in der Zahl stehen kann, ist nicht realisiert und bedarf noch mehr Aufwand.\\ Darüber hinaus überschaue ich noch nicht alle Varianten, welche durch solch eine Änderung eintreten können.\\ Ich nahm daraufhin Kontakt mit John~Sadler auf, bekam aber bedauerlicherweise die Antwort, dass er gegenwärtig nicht mehr aktiv entwickelt --- okay --- kommt Zeit, kommt Lösung. Es ist ja \emph{open source}. \section{Fazit} FICL ist, auch wenn noch nicht alles ausgereift ist und angewendet wurde, ein sehr elegantes gut beschriebenes Software--Tool, welches gerade im Embedded--Bereich mit vorhandenen 32-Bit--Betriebssystemen sehr nützlich ist. Es überzeugt besonders durch seine Kompaktheit und die vorhandene Geschwindigkeit. \end{multicols} \end{document}