Forth

C 语言使你迷茫?Forth 可能是一个答案

程序语言 2010-05-25 22:27:28 阅读153 评论0   字号:   订阅

转自:http://www.embeddedsoft.cn/news-detail.aspx?Id=100

原标题 Lost at C? Forth May Be the Answer 

原作者 Tom Napier 和 Eric Krieg

原文引自 www.Forth.org

曾经有个时期,人们不害怕从 IBM 购买计算机,和那时一样,现在人们当然也不害怕用C语言来编写嵌入式系统程序。如果还要再选择一个的话,那通常是汇编语言,尽管时尚正在转向Java 。只有很少的程序员使用Forth ,这种语言组合了汇编语言的速度、灵活性和紧缩性,又具有C语言的结构化和易读性。这些为数不多的程序员还发现了 Forth 能够提高编程的生产率。

在这篇文章中,我们希望(再一次)介绍 Forth 。你将会惊异于不需要复杂的工具就可以如此之快和交互式地编写和测试嵌入式程序。

编写程序的第一步是设计程序行为的细节。有些人画流程图,有些人用程序设计性语言(PDL),通过与英语类似的方式描述操作的序列和测试条件。完成 这些之后,设计就被分成模块,每一块都被转成可执行的代码,全部的事情就是编译、连接、测试,这个迭代的过程可能会持续几个月。

如果PDL能够直接执行,就不需要把它翻译成另一种语言,那你该省去多少时间呀!如果你能交互式地测试每个程序模块,确认它能正确地工作,那不就更 方便了吗?再假设有一种语言,它可以执行得和其它的语言一样快、只要1K字节的运行开销、符合 ANSI标准、可以扩展以满足你应用程序的特殊需要,经过一到两个星期的熟悉,你每天可以编写出三倍于你同伴的代码,那么你对这种语言感兴趣吗?如果是, 请听如何用Forth来做到这些。

Forth 是什么?

从某种意义上说, Forth 不是一种语言,我们更应该把它看成一种为手头的任务编写应用语言的程序设计方法。你编写的大部分程序都是你工作的需要而不是编译器的需要。 Forth 支持你需要的任何操作和语法。

Forth 理解一定范围的原语字,它们处理全部正常的算术、逻辑和程序流操作,然而它也有一个确定的办法向语言加入新字。你可以确定哪些字能更好地描述你的应用,然 后你用现有的字定义这些字。一但你定义了一个新字,这个字就变成了语言的一部分,可以用来定义其它的字。最高级别的字就是程序本身。

在 Forth 中,每个事物是一个字或者是一个数,它们彼此被空格分开。 Forth没有词法分析,语法也很少。没有操作符,没有函数,没有过程,没有子程序,甚至没有程序,只有字和数。

每个字告诉计算机去执行一个清晰的精巧定义的操作。定义一个字之后,你就可以把它作为一个独立的元素来测试。在你开始测试的时候你不需要完成全部程序,你可以在键盘上输入任何一个字,执行它,看结果是不是你所需要的。

Forth 也是它自己程序的符号调试器,所以测试一个 Forth 程序比测试其它语言的程序更快。你用增量化的方式编写 Forth 定义、测试定义。一但确认一个字能够工作,就可以把它加入到你的程序中;一但你定义了最高级别的字,就可以结束编程工作而不需要进一步的调试。

尽管 Forth 程序通常是自顶向下设计的,但是你需要自底向上编写,它要求你在使用一个字之前先定义它。但是实际上, Forth 程序通常是从两端向中间编写的。开始的时候,你知道所需要的程序顶级行为,你也知道与硬件交互的字必须做的事情,于是就有中间的工作需要完成。

你也可以先给某个功能一个名字,在定义之前使用它(如果你需要测试编译,你就给它一个空的名字)。一个程序的顶级字可以是一个无限循环,它用字 GET.FRONT.PANEL.INPUT 开始,后面是字 CHECK.USER.INPUT.LIMITS ,所以我们可以用 Forth 做 PDL 。当然,你在这里假设 CHECK.USER.INPUT.LIMITS 是存在的,最后你还得定义这个字的精确行为。

把程序分成可管理的自我描述的小块是每个优良程序的行为。所不同的是,在 Forth 中,最后的结果是一个可执行的程序,而不是另一个漫长过程的开始。

Forth 是编译器吗?

Forth 是编译的,但是它的用户界面是解释的。 Forth 维护一个它所知道的所有字的字典。每个定义由定义这个字的那些字的地址列表组成(为使代码更短,在32位或者更长地址的机器上可以使用16位的标记而不是 实际的地址)。编译的过程就是把新的字和它们的定义加入到字典。

由于Forth把源程序中的每个字翻译成对应的地址, Forth 的编译器就很像是一个汇编器。图1是 Forth 编译器完整的流程图,如果把C语言编译器流程图同样地画出来,那会是一张 4' x 6' 的招贴海报。

 

图 1 Forth 编译器的完整的流程图

对于源程序中的每个字,这个循环都要执行一次

把Forth程序想像成全部是由子程序组成的,可能会对我们理解Forth系统有所帮助。由于每个字调用子程序,所以不需要 CALL 指令,它只是一个地址。在运行时,一个机器码片段读出下一个指令的地址,把当前程序计数器保存在返回栈上,执行这个调用。这个小小的开销对于每个字都要执 行一次,导致了 Forth 程序比优化的汇编程序要慢。

Forth 是如何工作的?

执行一个无休止的子程序调用序列并不是一件很有效率的事情。幸运的是,大约有60个字是用机器码字义的。每个定义最终都是由这些“原语字”组合而成的,它们执行某些真正的工作。

原语定义了一个虚拟的Forth机器,要把 Forth 移植到一个新的系统上,只有这些原语字需要重写。某些Forth运行在 DOS 和 Windows上,而在嵌入式应用中,这些由机器码定义的原语字就是操作系统。

Forth 在堆栈上传递参数。在一个字执行之前,所需要的参数必须在堆栈上。而在执行之后,如果有任何的结果,也留在堆栈上。

这与大多数现代计算机语言的行为精确地一致,但是现代计算机语言的堆栈通常是隐藏起来的。在 Forth 中,程序员知道堆栈上的内容,并能够直接处理它们。例如,Forth 原语字 SWAP 交换堆栈上的两个元素。大多数语言保存未决的操作,当你写下 C = A + B ,编译器把“ = ”和“ + ”操作放到未决的表中直到读到表达式的结尾。然后它重写这个表达式为“取 A ,取 B ,加,存入 C ”。

Forth 消去了中间过程,在 Forth 中,你把同样的操作写成是 A @ B @ + C ! 。这里的 @ 和 ! 是 Forth “读取”和“存储”操作的缩写, + 非常奇怪地表示加法。

幸运的是,只有不多的 Forth 字用这种密码表示。大多数的 Forth 接受多达 31 个字符,大多数的标准字描述了它们的功能。好的 Forth 程序是自解释的,所以应该尽量使你定义的字成为自描述的。调试这个字的方法是打入它的输入参数,后随这个字。它立即执行,就好像 Forth 是一个解释器,允许你测试堆栈上的结果。

一个堆栈元素典型地有 32 位(有些Forth系统为16 位)并且是无类型的,它可以表示一个有或者无符号的整数、一个地址、一个单精度的浮点数或者是一个布尔标志。你需要对此保持跟踪。

Forth 的哲学是许可而不是禁止。如果你有一个好的原因把布尔值加到地址上,Forth不会阻止你。对于这些事情,Forth中没有任何东西能够阻止你把一个错误的项目放到堆栈上。 Forth 非常快速而高效,但是你自己也得睁大眼睛。

创建一个新的字义

Forth 中最重要的字可能是冒号,它把编译器从运行模式切换到编译模式并创建一个新的字典项。在冒号之后的第一个字是将要定义的字的名字,定义接着名字之后。逻辑上,定义被一个分号结束,这将编译一个返回指令并把编译器切换到运行模式。

于是,一个完整的 Forth 定义看起来像下面这样:

: MAGNITUDE (X Y—vector magnitude) DUP * SWAP DUP * + SQRT ;

括号中的表达式是堆栈说明,它提醒程序员这个字的输入输出参数是什么。 DUP(复制)操作产生栈顶元素的另一个拷贝, * 是单精度乘法,SQRT得到一个数的平方根。

作为 Forth 灵活性方面的一个例子,假设你需要 C 语言的 ++ 操作符, Forth 与之最近的等效是 + ! ,它把一个指定的数加到一个变量中。如果你定义:

: ++ 1 SWAP +! ;

则 ALPHA ++ 加 1 到变量 ALPHA 中, Forth 与 C 语言的唯一区别是 Forth 不允许 ALPHA++ ,但是C语言允许,因为 Forth 并不分析表达式,它会把 ALPHA++ 作为一个定义字。

程序结构

Forth 是高度结构化的,如果你确实需要,当然也有办法编译一个GOTO,但你通常使用 IF ELSE THEN BEGIN UNTIL WHILE REPEAT DO 和 LOOP 来控制程序的流程。这些字把条件跳转指令编译到定义中。

Forth 的IF检查栈顶标志,这个标志是许多 Forth 比较操作字中的一个留下的。比如我们希望比较堆栈上的两个数,如果相等就执行选择1,如果不等就执行选择2, Forth 的语法是这样的:

= IF 选择 -1 ELSE 选择 -2 THEN.

(我在自己的程序中使用 ENDIF代替THEN ,因为我觉得 THEN 对于 Basic 来说不合理。Forth 允许这样的个人化选择,尽管你的老板或者伙伴不允许)

常数 变量 和串

源文件中的一个数作为立即数编译。一个命名的常数在编译时存储一个值并在运行时把这个值放到堆栈上。命名一个变量则编译一个存储空间。引用一个变量则把它的地址放到堆栈上以准备读写。一个 Forth 串是一个变量区,它的第一个字节是串的长度。

由于变量指示了它的地址,所以能够在使用这个变量之前处理这个地址。例如,如果你使用的 Forth 系统没有 ARRAY 数组结构,你可以定义一个, Forth 能够指定定义字的新类型。另外,你可以"伪造"数组。 BETA 7 + C@ 读取数组中的第八个字节,这个数组开始于 BETA 变量的地址。

Forth 源程序的一个不足是我们不清楚一个字表示的是变量还是函数。有些人使用这样的传统:用连字符表示变量名而用小数点表示函数名。由于好的 Forth 代码与英语非常相似,在视觉上不需要分析一个整行就可以区别代码和说明,许多用户就用大写字母表示代码而用小写字母表示注释。

Forth 硬件

Forth 几乎在每个现存的或者曾经存在过的微处理器上都有实现,但是有些芯片更适合 Forth 系统运行。很明显,那些与图 2 的 Forth 虚拟机更接近的芯片能更好地运行 Forth 。 Forth 需要两个堆栈,那些支持一个以上堆栈的芯片将运行得更快。由于 Forth 只需要不多的寄存器,所以硅片上如果有寄存器也只能浪费。

 

图 2 Forth 虚拟机结构

Forth 虚拟机具有 HARVARD 体系结构,实现时通常用一个分离的寄存器保存栈顶元素

最小的 Forth 系统执行 16 位或者 32 位长的算术运算,所以 Forth 在 8 位芯片上运行较慢。历史上, Motorola微处理器比Intel 处理器更适合运行 Forth 。 MC6809 和 MC68X0 是理想的 Forth 8 位芯片。

由于 Forth 虚拟机相对简单,它可以在一个门阵列中实现。最早的努力是 Charles Moore , Forth 的发明人,指导 Harris公司于 1989 年推出了 RTX2000 。这个 10MIPS 的单芯片 Forth 引擎使用哈佛体系结构,并把参数栈和返回栈放到芯片上。不幸的是,这款芯片在商业上没有成功,现在只用于一些专用的市场,比如人造卫星。

使用 Forth

现在有各种级别的商业和公共的 Forth 版本。对于一个 8 位或者 16 位处理器的嵌入式应用,最方便方法的是在 PC 机上编写 Forth 程序,然后再把最终的代码传送到目标系统上,由于开发系统能够使用全部的 DOS 或者 Windows 能力,在最终的产品中只需要包含一个小的运行时间包和程序自身,字典只是在编译和调试程序时才需要,在最终的产品里可以被去除。

由于 Forth 程序趋向于编译大约每行10个字节,一个 2000 行的程序加上 4K 字节的运行时间文件可以很容易地放到 32K 字节的 PROM 中。如果目标系统运行串行口并能在 RAM 中执行,那我更喜欢在目标系统上编译和设计。尽管这意味着需要为字典和编译器找到存储器空间,但是它大大有助于硬件测试。我的许多硬件问题都是这样解决 的:编写短的 Forth 程序来触发 I/O 位,再用一个示法器观察可疑的区域。

一个商业化的 Forth 系统有一个初始字典,它包含有 Forth 原语和需要实现 Forth 编译器的字。许多 Forth 系统有内建的编辑器,但是你可以使用任何方便的编译器。你也可能得到在操作系统上如 Windows 上开发 Forth 程序的库。对于一个使用单板PC的嵌入式应用, DOS 库就很有用了。

你也可以得到一个 Forth 扩展库,在程序需要它们的时候装入这些库。例如,简单的Forth只处理整数,而浮点是可选的。编写自己的库也很简单,我曾经用从 Delta Research 公司得到的 JForth 编写分析滤波器的程序。 JForth 支持 Windows 、下拉菜单、输入固件和控制参数滑块。然而,它不能处理复数,我在 20 分钟里就编写好了自己的浮点复数程序库。

在一个编写一系列相关控制仪器的团队中,应该有一个成员被指定编写硬件接口函数库,处理诸如用一致方式访问前台控制和显示等等工作。由于 Forth 允许程序员开发特殊的解决问题的方法,在一个大的项目中,你必须有好的文档和团队成员之间的紧密协调。使用 Forth 的公司应该为它们的程序员维护一个 Forth 内部扩展标准用于它们的产品和技术。

用 Forth 可以做什么?

简单地回答是:任何事情。其它的计算机语言限制你只能执行编译器的编写者认为你需要的操作。由于 Forth 是天然可扩展的,你可以做你需要的一切事情。如果所有的方案都不行,你还可以直接进入机器代码并创建你需要的任何数据结构。

JForth 甚至实现了C的结构,后者通常用于与主机的操作系统进行交互。我曾经需要一个结构,它写入30个命名的一维数组,作为一个单个的命名的两维数组。大家都说这用C实现起来很方便,但我从来就没有见到有人试着做过。

Forth 标准

自从 Charles Moore 1970 年发明 Forth 之后,出现了许多 Forth 标准和方言。Forth鼓励创新,所以总是有定制和改进它的倾向,就是在大家表面上接受了标准时也一样。我从1979 年开始从事古老的 FIG-Forth 编程,它已经非常古老甚至都无法改变。从那时开始,又有了 Forth-79 和 Forth-80 ,现在是一个 ANSI 的 Forth 标准( X3.215/1994 )。

如何比较 Forth 和 C ?

Forth 和C 都使得程序员能够在更高的级别上思维,并从较慢的汇编语言开发过程中解脱出来。 Forth 合理的文档顺序可以免去 C 语言中的原型说明。

全部的C 语言标准程序流控制( do if else while switch )在 Forth 中都存在,而且连名字都常常一致,所有重要的逻辑和算术操作也存在,条件比较、数组和联合都在 Forth 中支持, COSNTANT 替代了 #define , Forth 的直接堆栈处理省去了大多数的 C 语言 auto 变量。 Forth 字典的使用和 FORTGET 定义的能力比C语言的弱作用域操作能力更强大。你甚至可以比 C++ 更少痛苦地支持自己的数据类型。

Forth 假设你知道自己正在做的事情。它可以阻止你犯打字和结构不完整的错误,但是编译的错误代码手册通常只有一页,而不是整整一章。有人曾经说过:Forth 不能够标识语法错误,因为它不知道你准备使用什么语法。

在C语言中,你受到的保护更多。但即使如此,你还是必须做一些事情,如强制类型转换之类,来请求编译器帮助你做一些检查。

Forth 比 C 语言有这样一些优点:

?  开发环境更加简单。你不需要安装整个 Forth 开发包,因为 Forth 就是它自己的开发系统,在一个嵌入式应用中,也是它自己的操作环境。它提供了一个 OS 、源码编译器和你需要的全部调试程序,这些都放在一个360K字节的软盘上,结果是,你使用单一的工具集和单一的用户界面。你可以把这些与其它工具比较: 一个编译器、OS、一个调试器,可能还有一个目标调试程序,它们都来自不同的开发商,并且不是为彼此协同工作而设计的;

?  当你购买 Forth 时,你通常可以得到全部开发环境的源代码。相反,你可以试着让 Borland 或者 Microsoft 给你想要的、向后兼容的 C 语言进行更强的类型检测、模糊控制逻辑或者不同的浮点实现;

?  常常能够在目标系统上开发 Forth 程序。在我的 C 程序合同中,我使用 Sun 工作站运行 MAKE 来编译和连接执行代码。然后在一个目标机器上,我在目标系统上电之前下载代码并测试它。如果我想做一个调整,它将花费一个小时来完成全部的过程。使用 Forth ,我可以通过目标机的串行口来打入一个新字,把参数放到堆栈上,然后调用它来检查这个字是否工作。我可以简单地结合新字以截获对老字的调用;

?  使用编译器的扩展能力可以使你进行任何时尚的编码而不需要切换语言。 Forth 从一开始就已经是面向对象的、“沙箱支持”和平台独立的。加入数据结构或者操作符重载几乎窒息了 C++ ,但在 Forth 中却没有任何问题;

?  你可以比C语言更容易地进入汇编语言,所有的数据结构都可以从汇编语言中访问;

?  目标测试更容易。你可以使用与代码中一样的命令来交互式地检查和处理数据,在C语言中做同样的事情需要更多的知识,它需要许多的键盘输入来控制调试器。你 不需要一个目标操作系统, Forth 就是一个很好的 OS 。许多 Forth 支持多用户和多任务。因为每个任务有一个独立的参数栈和返回栈,所以任务的切换能够瞬时而高效。

?  Forth 在编译时分配存储器资源,它的执行时间是确定的。它不需要花费不确定的时间来整理存储器碎片。在一个实时OS 中,我选择不使用动态的存储器分配,但是如果你需要一些像 alloc() 和 free() 一类的操作,那也不是大问题,一页的代码足以实现这些功能。由于是基于堆栈的, Forth 可以用很少的开销进行中断服务,因为它不需要保存上下文。

不好的一面是, Forth 可能有些慢。在一个大的程序中,它可能比最新的 C 语言产生的代码占用更多的空间。然而,尽管用 Forth 编写的 "Hello world" 程序可以达到 2K 字节,但是它不需要装载更大的运行时间库。 Forth 鼓励程序员使用定点表示法,这可以极大地提高运行速度,但是在编码时需要更多地进行分析。

Forth 的最大缺点如同"第 22 条军规"。知道 Forth 的人不多,而人们又通常不愿意学习某些东西,除非其它的人都希望使用它。这就是盖茨先生的生活方式。

如果你能够说服你的老板让你使用 Forth ,它将成为你的秘密武器。工业经验显示 Forth 程序员可以达到C程序员 10 倍以上的生产率。

我们这里给出一个 Forth 和C语言差异的例子。这是一个嵌入式程序,使用板上的 PIC 驱动晶振。我们用 Forth 编写了一个程序以显示 PIC 程序员如何工作。下面的列表1是这个程序外层循环的 PDL 描述。列表 2 提供了可执行的 Forth 程序。这花了我 10 分钟时间(在一个实际的 Forth 程序中,这些代码将要被因子化成几个定义),而列表 3 是同样程序的 C 语言版本。

列表 1 一个抖动产生器的顶层程序 PDL 描述

Main Program:

HouseKeep (set ports, clear flags, set defaults)

Read upload bit (has user saved previous settings?)

If low

CopyPROM (load defaults from EEPROM)

Endif

ReadConfiguration (get former settings from EEPROM)

SetConfiguration (set board registers)

Beginloop: (Start of endless loop)

Read self-test bit

Read self-test number

If bit=0 and number <>0 (self test operation)

Case: (test number)

On 1 do test 1

On 2 do test 2

On 3 do test 3

On 4 do test 4

Endcase;

Else (normal operation)

Read interface flag (Check for faults or user input)

If set

Read status word (Identify faults or user input)

If fault flag, do soft reset, endif

If jitter flag <> jitter state, toggle state, endif

If calibration request, Calibrate, endif

If Bit 0, SetAmplitude, Endif

If Bit 1, SetBitRate, SetAmplitude, Endif

If Bit 2, SetBitRate, SetAmplitude, Endif

If Bit 3, SetFrequency, Endif

If parameters have changed

Update EEPROM

Endif

Clear interface flag

Endif

Endif

Endloop;

进一步学习

要学习更多 Forth 知识,最好的办法是加入非赢利的 Forth Interest Group (FIG) 组织。它们出版一本名为《Forth DIMENSIONS》的杂志,也销售图书和公共域版本的 Forth 系统软件。

经典但是有些过时的书是 Leo Brodie 的《 Starting Forth 》(中译本: 《Forth 语言入门》)。如果找不到它,可以从FIG购买。 Brodie 的《 Forth 思维方式》没有告诉你如何使用 Forth ,但对 Forth 和其它语言的结构和哲学思想进行了很好的考察。另一个好的入门书是 C. Kevin McCabe 的《 Forth 基础》第一卷。

为了用 Forth 实现一个嵌入式系统,你可以先得到一个公共域版本,你也可以根据你的处理器购买一个现成的版本。 Forth Inc. 提供基于 DOS 的版本,可用于80196、80186、68HC16 和 TMS320C31 ,它们也有用于68HC11和8051的 Windows 版本。

一些小的 Forth 开发商在《 Forth DIMENSIONS》 上作广告。

为什么不使用 Forth

人们常说 C 语言程序员很容易找到而 Forth 程序员却很难找。这是事实。很少的“程序员”知道 Forth ,但是,我们发现硬件工程师却常常熟悉它。有经验的工程师通常比职业程序员能写出更好的嵌入式代码,因为后者不熟悉硬件。

你需要知道你公司的目标是什么。如果你真的需要产品,那么你就需要 Forth 。这就是路。

------------------------------------------------

Tom Napier 曾是火箭科学家,健康学家和工程管理,最近九年时间从事宇宙飞船的通信设备开发,现在是顾问和作家。

你可以通过电子邮件与 Eric 联系 http://www.voicenet.com/~eric/Forth.htm

 

英文转自:http://web.archive.org/web/19990423140254/http://www.circuitcellar.com/DesignForum/features/9805022/TNtext.htm

Just as at one time, no one got fired for buying from IBM, now no one gets fired for programming embedded applications in C. If an alternative is considered, it’s usually assembly, although fashion is now swinging towards Java. Very few programmers use Forth, a language that combines the speed, versatility, and compactness of assembly language with the structure and the legibility of C. These few have found Forth to increase programming productivity.

In this article, we’d like to (re)introduce you to Forth. You’ll be surprised how quickly and interactively you can write and test embedded programs without using complex tools.

The first step in writing any program is to work out in detail what it has to do. Some people draw flowcharts, while others use a Program Design Language (PDL) that describes the sequence of operations and test conditions in an English-like manner. Once this is done, the design is broken up into modules, and each is converted into executable code. The whole thing is compiled, linked, and tested, an iterative process that can take months.

However, if the PDL could be executed and you didn’t need to translate it into another language, think how much time you could save. Wouldn’t it be more convenient if you could test each program module interactively to verify its operation? Suppose too that there’s a language that executes as quickly as any other language, has a run-time overhead of a thousand bytes, conforms to an ANSI standard, and can be extended to cope with any special requirements your application has. What if, after a week or two of familiarization, you could turn out at least three times as much finished code in a day as your fellow programmers? Would you be interested? If so, listen up to hear how you can do all this with Forth.

What is Forth?

In a sense, Forth is not a language but rather a programming methodology for writing an application language for the task in hand. You write the bulk of your program in terms of the job’s requirements, not the compiler’s edicts. Forth supports whatever operations and syntax you need.

Forth understands a range of primitive words that handle all the normal arithmetical, logical, and program flow operations. However, it also has a defined way of adding words to the language. You can decide what words best describe your application. You then define these words in terms of existing words. Once you’ve defined a word, it becomes part of the language and can be used to define other words. The highest level word is the program itself.

In Forth everything is either a word or a number. Both are separated by spaces. There is no parsing in Forth and very little syntax. There are no operators, no functions, no procedures, no subroutines, not even programs—just words and numbers.

Every word tells the computer to execute a clearly defined finished operation. After you define a word, you can test it as a stand-alone element. You don’t need to complete the program before you start testing. You can type any word at the keyboard, have it execute, and see if the result is what you expected.

Forth is its own symbolic debugger, so testing a Forth program is much faster than testing a program in another language. You write Forth incrementally, defining and testing words in turn. Once you’re sure a word works, you can incorporate it into your program. Once you’ve defined the highest level word, you have a finished program that should need no further debugging.

Although a Forth program is normally designed from the top level down, you write it from the bottom level up—it expects you to define words before you use them. In practice, however, Forth programs are often written from both ends towards the middle. At the outset, you know what top-level program behavior you need, and you know what the low-level hardware-interactive words have to do. It’s the stuff in the middle that needs working out.

You can assign names to functions and use these names before you define them. (Give them null definitions if you want to test compile.) The top-level word of a program might be an endless loop which starts with the word GET.FRONT.PANEL.INPUT followed by CHECK.USER.INPUT.LIMITS, thereby using Forth as its own PDL. Of course, having assumed the existence of CHECK.USER.INPUT.LIMITS, you eventually have to define exactly what this word should accomplish.

Breaking down a program into manageable and self-descriptive chunks is exactly what all good programmers do. The difference: in Forth, the end result is an executable program, not just the first step of a long process.

Is Forth a Compiler?

Forth is compiled, but its user interface behaves like an interpreter. It maintains a dictionary of all the words it knows. Each definition consists of a list of the addresses of the words that form the definition. (For code brevity, Forth on machines with 32-bit or longer addresses may use 16-bit tokens rather than addresses.) Compilation adds the new words and their definitions to the dictionary.

Because Forth compiles each word in the source one-for-one to an execution address, a Forth compiler resembles an assembler. Figure 1 shows the complete flowchart of a Forth compiler. Similar charts for C are 4’ x 6’ wall posters.

 

Figure 1—

Here’s the complete flowchart of a Forth compiler. The loop is executed once for each word in the source.

It may be helpful to think of a Forth program as consisting entirely of subroutines. Since every word calls a subroutine, there is no need for a CALL instruction, only an address. At run time, a machine code fragment fetches the next instruction address, saves the current program counter on the return stack, and executes the call. This tiny overhead is executed once for each word, making Forth marginally slower than an optimized assembly-language program.

How Forth Works

Executing an endless series of subroutine calls is not a very productive enterprise. Luckily, some 60 Forth words are defined in machine language. Every definition eventually calls a combination of these "primitives" and does some real work.

The primitives define a virtual Forth machine. To port Forth to a new system, only the primitives are rewritten. While some Forths run under DOS or Windows, in an embedded application the machine-code definitions of the primitives are the operating system.

Forth passes parameters onto a stack. Before a word is executed, the necessary parameters must be present on the stack. After execution, the results, if any, are left on the stack.

This is precisely what happens in most modern computer languages, but the stack is usually hidden. In Forth, the programmer is aware of what is on the stack and can manipulate it directly. For example, the primitive Forth word SWAP exchanges the top two elements of the stack. Most languages save pending operations. When you write C = A + B, the compiler puts the "equals" and "plus" operations on its pending list until it gets to the end of the expression. Then, it rewrites it as "Fetch A, Fetch B, Add, Store C."

Forth cuts out the middle step. In Forth, you write the same operation as A @ B @ + C !. The @ and ! are Forth’s shorthand for the "fetch" and "store" operations. The + , oddly enough, represents addition.

Luckily, only a handful of Forth words are this cryptic. Most Forths accept names up to 31 characters long, and most standard words describe their function. Good Forth is self-commenting, so make your words as self-descriptive as possible. At run time, any numbers you type are placed on top of the parameter stack. You debug a word by typing its input parameters, then the word. It executes immediately as if Forth were an interpreter, allowing you to check the results on the stack.

A stack element typically has 32 bits (some Forths use 16) and is untyped. It might represent a signed or unsigned integer, an address, a single-precision floating-point number, or a Boolean flag. You need to keep track.

Forth’s philosophy is to permit, not to impede. If you have a good reason to add a Boolean to an address, Forth won’t stop you. For that matter, there’s nothing in Forth that prevents you from putting the wrong number of items on the stack. Forth is fast and efficient, but you have to keep your eyes open.

Creating a New Definition

Perhaps the most important word in Forth is the colon, which switches the compiler from run mode to compile mode and creates a new dictionary definition. The first word in the source after a colon is the name of the word to be defined. The definition follows the name. Logically enough, the definition is terminated by a semi-colon. This compiles a return instruction and switches the compiler back to run mode.

Thus, a complete Forth definition might look like this:

: MAGNITUDE (X Y—vector magnitude) DUP * SWAP DUP * + SQRT ;

The expression in parentheses is the stack picture. It reminds the programmer what the word’s input and output parameters are. DUP (duplicate) generates a second copy of the top element on the stack, * is a single-precision multiplication, and SQRT takes the square root of a number.

As an example of Forth’s flexibility, suppose you had a hankering for C’s ++ operation. Forth’s nearest equivalent is +! which adds a specified number to a variable. If you define : ++ 1 SWAP +! ; then ALPHA ++ adds one to the variable ALPHA. What Forth does not allow, but C does, is writing this as ALPHA++. Since Forth does not parse expressions, it would read ALPHA++ as an undefined word.

Program Structure

Forth is highly structured. There is a way to compile a GOTO if you really want to, but normally you use the words IF, ELSE, THEN, BEGIN, UNTIL, WHILE, REPEAT, DO, and LOOP to control the flow of the program. These words compile conditional jumps into a definition.

Forth’s IF checks the top of the stack for a flag left by one of Forth’s many comparison words. To execute option 1 if the top two numbers on the stack are equal and option 2 if they are not, Forth’s syntax is:

= IF do-option-1 ELSE do-option-2 THEN.

(I use ENDIF in my programs because I feel that THEN is an illogical throw-back to BASIC. Forth condones such personal idiosyncrasies, even if your bosses and co-workers may not.)

Constants, Variables, and Strings

A number in the source is compiled as a literal. A named constant stores a value at compile time and puts that value on the stack when it is invoked. Naming a variable compiles a storage space. Using a variable puts that address on the stack, ready for a fetch or store operation. A Forth string is just a variable whose first byte specifies its length.

Since a variable supplies its address, it can be manipulated before being used. For example, if you were using a Forth that had no ARRAY structure, you could define one. Forth can specify new types of defining words. Alternatively, you could fake it. BETA 7 + C@ fetches the eighth byte in the array that starts at the address of the variable BETA.

One deficiency of Forth source is that it may be unclear whether a word represents a variable or a function. Some follow the convention of hyphenating variable names but splitting function names with periods. Since good Forth code can be quite English-like, it is handy to distinguish code from comments visually without needing to parse a line. Thus, many commonly use upper case for code and lower case for comments.

Hardware for Forth

Forth has been implemented on just about every microprocessor which has ever existed but some chips are more suitable than others. Obviously, the closer the chip architecture comes to the Forth virtual machine outlined in Figure 2, the better Forth runs. Forth needs two stacks so it runs faster on a chip that supports more than one. Since Forth needs few registers, a chip with many is just a lot of wasted silicon.

 

Figure 2—

The Forth virtual machine has a Harvard architecture. Hardware implementations frequently keep
the top stack element in a separate register.

Minimal Forths do arithmetic on 16- or 32-bit integers, so Forth runs slowly on eight-bit chips. Historically, Motorola microprocessors have been more suited to Forth than Intel ones. The MC6809 and the MC680X0 series were ideal 8-bit chips for Forth.

Since the Forth virtual machine is relatively simple, it can be implemented on a gate array. A pioneering effort by Charles Moore, the inventor of Forth, led Harris to introduce their RTX 2000 in 1989. This 10-MIPS single-chip Forth engine used a Harvard architecture with its parameter and return stacks on the chip. Unfortunately, it was not a commercial success and is now used only in niche markets such as processors on satellites.

Using Forth

Commercial and public-domain versions of Forth exist at all levels. For an embedded application on an 8- or 16-bit processor, it may be most convenient to write Forth programs on a PC and then transfer the finished code to the target system. While the development system may use the full facilities of DOS or Windows, there is no need for the finished product to contain more than a small run-time package and the program itself. Even the dictionary is only needed when compiling and debugging the program. It can be omitted from the final code.

Because Forth programs tend to compile to about 10 bytes per line, a 2000-line program plus a 4-KB run-time file easily fits in a 32-KB PROM. If the target system supports a serial port and executes from RAM, I prefer to compile and debug on the target. Even though this means finding memory space for the dictionary and the compiler, it helps immensely in testing the hardware. I’ve resolved many hardware bugs by programming a short Forth loop to wiggle a bit so I could follow a signal through the suspect area with an oscilloscope.

A commercial Forth comes with an initial dictionary that contains the Forth primitives and the words needed to implement the compiler. Many Forths have a built-in source editor, but you can use any editor you find convenient. You will probably be supplied with libraries of the operating system calls needed to develop Forth programs under OSs like Windows. For an embedded application using a single-card PC, a DOS function library is useful.

You can also get a library of Forth extensions, which you can load if your program requires them. For example, simple Forths process only integers, so floating-point arithmetic is optional. It’s easy to write your own libraries. I’ve been using JForth from Delta Research to write programs which analyze filters. JForth supports windows, pull-down menus, input gadgets, and slider control of parameters. However, it can’t handle complex numbers. I wrote my own floating-point complex arithmetic library in 20 minutes.

In a team writing programs to control a series of related instruments, one member should be assigned to write a library of hardware-interface functions to do things like access the front-panel controls and displays in a consistent manner. Because Forth lets programmers develop idiosyncratic ways of solving problems, you must have good documentation and close coordination between team members on a large project. Companies using Forth should maintain in-house standards for Forth extensions that relate to their products and teach these to all their programmers.

What Can You Do With Forth?

The simple answer: anything. Other computer languages limit you to the set of operations the compiler authors thought you’d need. Because Forth is naturally extensible, you can do what you need. If all else fails, you can easily drop into machine code and create any data structure you need.

JForth even implements C structs, which it uses to interact with the host operating system. I once needed a structure which was written to 30 named, one-dimensional arrays. It was read as a single, named, two-dimensional array. I’m told this is feasible in C, but I’ve never met anyone who wanted to try it.

Forth Standards

Since its invention by Charles Moore about 1970, many Forth standards and dialects have emerged. Forth encourages innovation so there has always been a tendency for Forth vendors to customize and improve it, even when ostensibly adhering to a standard. I do most of my professional Forth programming in 1979 vintage FIG-Forth on the grounds that it is already so obsolete that it won’t change. Since then, there was Forth-79 and Forth-83 and now there’s an ANSI standard for Forth (X3.215/1994).

How does Forth Compare with C?

Both Forth and C let you think at a higher level and spare you the slower development process of assembler. Forth’s logical compilation order eliminates the need for C prototypes within a file.

All the standard program controls of C (do, if, else, while, switch, etc.) are in Forth, often with the same names. All the important logical and mathematical operators are there, too. Conditional compilation, arrays, and unions are all supported with a Forth flair. Constants replace #defines, and Forth’s direct stack manipulation eliminates most need for auto variables. Forth’s use of vocabularies and its ability to FORGET definitions are more powerful than C’s weak scope operators. You can support your own data types with even less pain than in C++.

Forth assumes you know what you are doing. It protects you from typographical errors and incomplete structures, but the manual has only one page of compiler error codes, not a whole chapter. As someone once said, Forth can’t flag syntax errors since it doesn’t know what syntax you decided to use.

In C, you’re more protected. But then, you also have to do things like type casting to circumvent a patronizing compiler constantly checking up on you.

Forth has these advantages over C:

  1. The development environment is much simpler. You don’t need to install a whole development suite since Forth is its own development system and, in an embedded application, its own operating system. It offers an OS, source editor, and all the debug utilities you need, and they fit on one 360-KB floppy.
    As a result, you’re working with a single tool set and a single user interface. Compare this with having a compiler, OS, debugger, and maybe a target monitor program, all coming from different vendors and not designed to work together.
  2. When you buy Forth, you often get the source code for the whole development environment. Try telling Borland or Microsoft that you want to make a backward compatible variant of C to do stronger type checking, fuzzy logic, or different floating-point implementation.
  3. It’s often possible to develop a Forth program on the target system itself. In my present C contract, I use a Sun workstation to run Make and to compile and link a target executable. Then, on a target machine, I download the code before powering it up and testing it. If I want to make an adjustment, it takes an hour to go through the whole loop again.
    With Forth, I could type a new word right into the serial port of the target, push parameters onto the stack, then call it to see if it worked. I could easily "splice" the new word to intercept any calls to the old word.
  4. The extensibility of the compiler lets you follow any coding fad that comes along without switching languages. Forth has been object oriented, "sandbox supporting," and platform independent from the get go. Added data structures or operator overloading that would choke C++ would not be a problem in Forth.
  5. You can drop into assembly much easier than in C, and all data structures are accessible from assembler.
  6. Target testing is far easier. You can interactively examine and manipulate data using the same commands you used in the code. To do the same thing in C requires advanced knowledge. It takes lots of key pressing to dominate a debugger.
  7. You don’t need a target OS. Forth is at its best when it is the OS. Some Forths support multiple users and multitasking. Since each task has an independent parameter stack and return stack, task switching is essentially instantaneous.
  8. Forth allocates memory resources at compilation, which makes operation times determinate. It doesn’t spend an unknown time defragmenting memory.
    In a real-time OS, I prefer not to dynamically allocate memory, but if you want something akin to alloc() and free(), that’s up to you. A page of code would cover it. Forth can service interrupts with little latency since, being stack based, it doesn’t need to save the context.

On the negative side, Forth can be a little slower. In a large program, it may use slightly more code than newer C compilers. However, although a "hello world" program in Forth might run to 2 KB, it has no huge run-time libraries to load. Forth encourages programmers to use fixed-point notation, which can greatly speed execution, but requires more analysis during coding.

The biggest drawback of Forth is the Catch 22 that attends any nonconformist idea. Not many people know Forth, and people won’t usually learn something unless everyone else is already using it. That’s how Mr. Gates makes a living.

If you can persuade your boss to let you use Forth, it can be your secret weapon. Industry experience has shown that a Forth programmer is up to ten times more productive than a C programmer.

We’d like to show you some of the differences between Forth and C. For an embedded program using an onboard PIC to drive a jittering oscillator, we wrote an emulation program in Forth to show the PIC programmer how things should work. Listing 1, below, shows the PDL description of the outer loop of this program. Listing 2 offers the executable Forth. It took me about ten minutes. (In a real Forth program, this much code would be factored into several definitions.) Listing 3 presents the C version of the same program.

 

Listing 1— Here’s the top level of a program for a jitter generator in PDL  
Main Program:
HouseKeep (set ports, clear flags, set defaults)
Read upload bit (has user saved previous settings?)
If low
CopyPROM (load defaults from EEPROM)
Endif
ReadConfiguration (get former settings from EEPROM)
SetConfiguration (set board registers)
Beginloop: (Start of endless loop)
Read self-test bit
Read self-test number
If bit=0 and number <>0 (self test operation)
Case: (test number)
On 1 do test 1
On 2 do test 2
On 3 do test 3
On 4 do test 4
Endcase;
Else (normal operation)
Read interface flag (Check for faults or user input)
If set
Read status word (Identify faults or user input)
If fault flag, do soft reset, endif
If jitter flag <> jitter state, toggle state, endif
If calibration request, Calibrate, endif
If Bit 0, SetAmplitude, Endif
If Bit 1, SetBitRate, SetAmplitude, Endif
If Bit 2, SetBitRate, SetAmplitude, Endif
If Bit 3, SetFrequency, Endif
If parameters have changed
Update EEPROM
Endif
Clear interface flag
Endif
Endif
Endloop;

Going Further

The best way to learn more about Forth is to join the nonprofit Forth Interest Group (FIG). They publish a journal, Forth Dimensions , and sell books and public domain versions of Forth.

The classical, but now dated, book on Forth is Leo Brodie’s Starting Forth . If you can’t find it elsewhere, you can buy it from FIG. Brodie’s Thinking Forth won’t teach you how to use Forth from scratch but is a fine examination of the philosophy and structure of Forth and other languages. Another good beginner’s book is volume 1 of C. Kevin McCabe’s Forth Fundamentals .

To implement an embedded system in Forth, you can adapt a public-domain version. Alternatively, you can buy a ready-made system designed around your target processor. Forth Inc. advertises versions of their DOS-based chipForth for the 80196, 80186, 68HC16, and 320C31. They also have Forths running under Windows for the 68HC11 and 8051 processors.

A number of smaller Forth vendors advertise in Forth Dimensions .

Why Not Use Forth?

We’ve often been told that it’s easy to find C programmers and that hardly anyone uses Forth. This is true. Few "programmers" know Forth, but we’ve found that hardware engineers are often familiar with it. Engineers with programming experience generally write better embedded code than career programmers who are unfamiliar with hardware.

You need to ask what your company’s aim is. If you really want to get product out the door, then check Forth out. It’s the way to go.

Tom Napier has worked as a rocket scientist, health physicist, and engineering manager. He spent the last nine years developing space-craft communications equipment but is now a consultant and writer.

You may reach Eric via http://web.archive.org/web/20000128093128/http://www.voicenet.com/~eric/forth.htm .

References and Sources

Forth Dimensions (journal), books on Forth, public-domain versions of Forth
Forth Interest Group (FIG)
100 Dolores St., Ste. 183
Carmel, CA 93923
http://web.archive.org/web/20000128093128/http://www.forth.org/

Forth, Inc.
111 N. Sepulveda Blvd., Ste. 300
Manhattan Beach, CA 90266
http://web.archive.org/web/20000128093128/http://www.forth.com/

你可能感兴趣的:(Forth)