Forth 语言概要

Forth 语言概要

原作者 Peter Knaggs

原文标题《 Forth: An underview 》

本文的目的是给出 Forth 编程系统的一个整体轮廓,包括语言的历史、突出的特点和使用方法,讨论了最常用的实现方法,但更重要的是讨论了 Forth 编程的方法和哲学思想。

1.Forth 的来历

按照 Forth 语言的发明人 Charles Moore ( Chuck )的说法:

我第一次把各种想法组合成一个整体之后,它在“第三代计算机” IBM1130 上工作得如此有效,以致于我认为它是“第四代语言”,理所当然地应该被称为 FOURTH 。不过,当时的 IBM 计算机只允许 5 个字符的标识符。所以 FOURTH 变成了 Forth ,这是一个非常好的的文字游戏。

2. 历史

Forth 语言是 60 年代发明的,当时 Moore 在斯坦福线性加速器中心做计算机程序员工作,后来变成了一个自由工程师。当时的工程技术人员为了控制编译程序必须学习大量的方法,包括连接器、汇编器、伪指令以及高级语言,开发这个新工具的目标就是为了克服这些问题。

第一个称为 Forth 的程序大约是在 1970 年编写的。第一个完整的实现是 1971 年在一台 PDP-11 上完成的,用于亚利桑那州国家射电天文台的 11 米射电望远镜。这个系统的责任是定向和跟踪望远镜,收集数据并记录到磁带上,支持一个交互式的图形终端,供天文学家分析以前记录的数据。系统的多任务特点允许这些功能同时执行,没有时间上和其它方面的冲突。

这个系统取得了巨大的成功,世界各地的天文学家开始请求提供软件拷贝。Forth的使用范围迅速也扩大,国际天文学会于 1976 年接受了 Forth 作为标准语言。

这个应用的成功还促使 Moore 和 Elizabeth Rather ( ("Bess") 于 1973 年成立了 Forth, Inc. 公司,目标是开发这种语言的商业应用。 Forth, Inc. 在小型机上开发了多用户的 Forth 版本,可以用于从数据库到图像处理等科学和应用领域。和第一个应用一样,它们需要不同工具的组合。

1977 年,公司为刚刚出现的 8 位处理器开发了一个称为“microForth”的版本,对小型机上的“miniForth”做了扩展和补充。1979 年,称为“PolyForth”的产品替代了这些系统。从此以后,它就成了市场上销售量最大的 Forth 系统。

“microForth”系统在美国、欧洲和日本的嵌入式微处理器应用中取得了成功,由此产生了欧洲 Forth 用户团体 (EFUG) ;1978年,位于北加利佛尼亚的计算机爱好者成立了 Forth 兴趣组织 (FIG).

FIG 从天文台得到 Forth 系统,再用这个系统开发出一个简单的模型,然后在几个不同的系统上实现(那当然是得到了 Forth,Inc 的许可之后)并以非常低的价格销售。这个模型后来变成了著名的 FIG-Forth 模型。所有这些使得对 Forth 的兴趣迅速增大。 FIG 目前在 15 个国家中有 60 个分支。

3. 特点

Forth 是一个交互式的程序设计环境,最初是为程序员在小型和微型计算机上开发应用程序而设计的。最早用于科学和工业应用,比如仪器、机器人、过程控制、图形和图像处理、人工智能和商业应用。 Forth 的主要优点是软件开发快速、交互式、计算机硬件的高效使用等。

Forth 常常被作为一种语言,这当然也是它最常见的方面,然而 Forth 所提供的功能比传统的程序设计语言要多。传统的高级语言工具包含有许多分离的工具(编译器、编辑器、汇编器等等), Forth 把它们融为一体。与传统的高级语言相比,它是经过深思熟虑的,完全没有传统高级语言那么复杂的语法特性。

最早实现的 Forth 是一个独立的系统,它包含独立操作系统的一般功能,还包括有编辑器、编译器、汇编器、调试器和其它工具。一个单一的、简单的、一致的规则支配这些能力。今天,尽管还能在许多处理器上见到独立的版本,但更多的版本是与 MSDOS 、 WINDOWS、UNIX 一类操作系统协同工作的。

Forth 并不起源于其它已有的程序设计语言,所以它的外观和内部特点对于新用户来说是特殊的。但是 Forth 的简单性、高度模块化和交互式特性可以弥补初学者的陌生感,使得 Forth 非常易于学习和使用。一个新的程序员通常需要花一些时间来掌握 Forth 巨大的命令集,通过一个月左右的全面使用,程序员就可以理解它的整个内部工作机理,所学到的东西要比传统操作系统和编译器多得多。

Forth 与传统语言最大的不同是它的可扩展性。 Forth 的编程过程就是定义新的字,字实际上就是语言的新命令。字可以用一系列以前定义的字来定义,这个过程与教育孩子的过程相似:我们总是用孩子们以前理解的概念来教给孩子们新的概念,而这些字被称为“高级定义”。同样,新的字也可以用汇编代码定义,因为在多数 Forth 实现中都包括宿主处理器的汇编器,而这些字被称为 CODE 字。

可扩展性的结果是我们在开发一个应用的同时,也间接地开发了一个特殊的、针对这一类应用的“面向应用的语言”,它可以用于或者经过修改之后被用于相似的应用。

Forth 的可扩展性并不仅仅是为语言自身增加新的命令,所以不要把定义字与传统高级语言定义函数、过程等同。可扩展性还能够创建一个可以定义其它字的字,这种字被称为“定义字”。在创建这样一个定义字的时候,程序员能够指定它所创建的字在编译时间、运行时间或者这两种状态下的特殊行为。这个能力允许我们定义特殊的数据类型,并对其行为和结构实施完全的控制。又由于这种字的运行时间行为可以用高级语言或者汇编语言来定义,所以由定义字创建的字将具有与其它 Forth 字一样的性能。系统也允许我们增加一个新的“编译指示符”以实现特殊类型的循环或者其它的控制结构,比如 CASE 结构。

4. 编程

Forth 语言和英语非常相似,它的基本元素是已命名的数据项、过程,以及创建用户化数据项的定义字的能力。过程和定义字可以使用以前定义的字或者汇编代码来定义,它们与其它语言的子程序相似,也与其它语言的命令等效。 Forth 允许我们在键盘上打入一个函数的名字,这个函数将被立即执行。然而,如果我们把功能的名字放到定义中,将编译成对于这个函数的引用。

高级字是由其它字的集合来定义的,我们可以把这个过程想象成是其它语言的宏或者词典中的英语定义。新的字被加入到它们可以使用的存储器中,其定义被加入到字典中。在一个 Forth 字的命名规则中,只有很少的几个字符不能作为命名字符使用。许多程序组织使用更好的命名约定,通过可打印的字符以增加程序的可读性。

当遇到一个字的时候, Forth 就通过字典搜索希望找到这个字的定义,如果找到,这个字定义的功能或者被立即执行,或者作为引用而被编译到新的定义中。然而,如果在字典中没有找到这个字,系统就试着把它转换成一个数。如果转换成功,就把它放在堆栈上。如果不能转换成数字,就显示这个字并打印出一个错误的信息来报告这个字是系统所不知道的。

Forth 坚持“结构化程序设计”原理:

•  字必须在引用之前被定义;

•  逻辑流限制只有顺序、条件和循环,有专门的字用于实现常用的程序控制结构;

•  程序员使用许多小的、独立的模块(字)来实现最大的可测试性和可靠性;

这种方法有两个明显的优点

•  新的字总是用以前定义和测试过的字来构造,所以调试更容易。模块可以单独执行以测试它的功能;

•  固有的模块性使 Forth 成为一个“设计性语言”,允许自顶向下的设计同时保持自底向上的测试。一个字可以在不同的程序中使用,但是它的功能只需要定义一次;

这些都保证了 Forth 软件能够快速和有效地被开发,同时,如果管理得当,也可以作为自身文档的基础。

Forth 的 5 个主要元素决定了它的特点:

•  一个字典;

•  两个堆栈,一个是参数栈,另一个是用于嵌套的返回栈;

•  键盘(输入流)解释器;

•  一个汇编器;

•  虚拟存储;

用 Forth 开发的项目

以下是一些使用 Forth 开发的项目

Atari 的许多投币游戏机都是用 Forth 开发的。他们相信开发高性能代码的最快方法是使用 Forth ,加上使用汇编语言来重写最内层的循环。

按照最近与著名的 Infocom 高级游戏开发者的谈话,他们的游戏解释器也是用 Forth 写成的。

Unison World 为 CP/M 操作系统开发了许多游戏,全部都是用 FIG-Forth 编写的。按他们技术总监 Marc de Groot 的说法,把一个基于 Z80 的游戏移植到 6502 或者 MC6809 上,典型的时间不多于 3 个月。

美国 Federal Express (联邦快递)公司的手持式行李分捡系统的程序是用 Forth 编写的。项目经理 Gene Farrar 说,他可以按需要在几个星期内升级新固件,相比而言,其它的小组维护 C 语言代码则至少需要 6 个月的时间。

1990 年 11 月哥伦比亚航天飞机的 4 个载荷中有 3 个是用 Forth 编程的。按设计团队之一Johns Hopkins University's Applied Physics Laboratory的 John Hayes 说法,他们选择 Forth 来解决飞行中的硬件问题,灵活性是一个主要的因素。

SUN 公司把 Forth 用于它的工作站可编程 Boot ROM 中,以每天 500-1000 台的速度发货,是可编程 Forth 系统中数量最大的。 1994 年 IEEE 1275-1994 将 Forth 用于 BOOT (初始配置) 固件的标准,这也是 POWER PC 通用硬件平台的基础。

在电影《终结者 II :世界末日》中, Cyberdyne Systems T1000 系统的特技效果是基于 Morphing 软件包 Forth 产生的。

5. 字典

Forth 程序存储在字典中。字典占据了系统存储器的很大部分,它由一个串线链接的可变长度的项目组成,每个项目定义了一个字。每个定义的内容根据字的类型(数据项、常数、操作序列等)而有所不同,字典是可扩展的。

字是由“定义字”加入字典的,最常用的定义字是:(冒号)。当冒号执行的时候,它为后面的字建立一个字典项,然后进入“编译”模式。有许多不同的编译方法,最常用的是“串线编码”,这种方法把定义编译成一系列以前定义字的地址引用。定义由;(分号)结束。下面就是一个定义:

: NETWORK ( -- ) OPEN LINK TRXT. ECHO CLOSE LINK ;

图 1 编译的字典项

当一个名字项被编译到字典中的时候(称为定义的首部),它包含一个指向字典中前一个首部的指针。新字的名字加入字典(这里就是 NETWORK ),接着一个指向名字为“( : )”子程序调用的指针编译到字典中作为定义的第一部分,这个指针指向一段在解释定义体时需要执行的代码。当然,这里所说的不是唯一的编译技术,但它的应用最为普遍,这种技术称为间接串线编码,因为定义中的第一个项目是一段代码的引用,这段代码知道如何解释定义的其它部分。

定义的其它部分称为这个定义的体。在编译模式下,系统将依次寻找每个字的首部。每个首部地址依次放到定义体中,这样就产生了一个地址列表。最后在到达;时,一个称为“EXIT”的子程序地址被编译进定义。 EXIT 子程序用来将控制返回到调用字,就像一个子程序返回一样。

6. 堆栈

Forth 维护两个下推式堆栈,下推式堆栈也称为后进先出列表,它们提供了 Forth 字之间通信以及逻辑控制的有效机制。

尽管两个堆栈的结构是相同的,但它们的用途却大不相同。与用户/程序员关系最为密切的是数据栈,它保存有调用字之间传递的参数,以替代传统语言的参数列表,同时也是一个实现定义重用的有效内部机制。第二个堆栈是返回栈,用于保存嵌套定义的返回地址,偶尔也用于临时保存其它的数据。

数据栈的使用导致了一种操作数位于操作符后面的“后缀”表示方法,这种后缀表示方法常常被称为 RPN 或者“逆波兰表示法”,用以纪念 Lukasiewicz 教授,上个世纪 20 年代时, Lukasiewicz 教授在华沙大学工作,为 Sentential Calculus 开发了这种方法。

作为一个例子,我们来看一下字 BLANK 。这个字希望堆栈上有一个地址和一个数,字 BLANK 将从指定地址开始填充指定数目的 ASCII 空格,于是:

PAD 25 BLANK

将向 Forth 系统的便笺区填入25个空格,这里地址是由字 PAD 放到堆栈上的。应用字通常按相似的方法定义,比如:

100 SAMPLES

将定义一个用于保存 100 个测量值的数据数组。算术操作也从堆栈上取值并将结果保存到堆栈上。例如, + 操作把栈顶的两个数相加,并用它们的和替代栈顶。由于操作的结果仍然留在栈顶,所以连续的操作就可以组合在一起而不需要临时存储变量。例如,表达式

tempn + ((reading % interval) / interval) * (tempn1 - tempn)

变成:

reading interval % interval / tempn1 tempn - * tempn +

7. 解释器

Forth 本质上是一个解释系统,在这样的系统中,程序的执行是由数据项而不是由机器代码控制的。解释系统通常很慢,但是 Forth 却通过两级解释而保持了实时应用所需要的高速度。当然,我们这样说是假设系统使用普通的串线编码方法,如果使用编译技术来产生本地代码,就根本不会有任何速度上的损失了。

•  第一个解释器是文本解释器,它扫描从终端(或者从海量存储器)得到的字符串,并在字典中查找每一个字。如果找到了这个字,就通过第二级地址解释器执行。

•  第二级解释器是“地址解释器”。尽管不是所有的系统都用这种方法实现,但这种方法却是最早的和最基本的方法,地址解释器通过执行每个定义点的程序来处理地址(或者标记)串,这些定义点是由:(冒号)创建并编译到定义中的。

7.1 文本解释器

上电之后。 Forth 启动一个称为 QUIT 的无限循环。这就是 Forth 文本解释器,也称为键盘解释器。

: QUIT ( -- ) BEGIN RESET QUERY INTERPRET AGAIN ;

RESET 清除堆栈, QUERY 等待用户从键盘(或者从海量存储设备)输入一个命令, INTERPRET 寻找字典匹配,然后执行。 BEGIN 和 AGAIN 是程序控制字,用于构造无限循环。

QUIT 循环提供了语言的“交互式”特点,如果输入命令,它就可以立即被执行,而执行的结果可以使用相同的文本解释器来观察。与其它的编辑 - 编译 - 链接 - 测试程序开发过程相比,一个新的定义可以在极短的时间里被反复地测试和排错。

7.2 地址解释器

定义项首部的最后一个字段是指向功能执行的代码指针,这个字段所在的地址被称为“代码域地址”,简称 CFA 。对于所有的 Forth 高级定义字(使用 : 冒号定义的字),这个地址就是地址解释器的地址,它指向字 (:) 。

地址解释器有一个寄存器 I ,其中包含被执行列表的下一个入口的地址。这个入口就是某个字的 CFA 地址,这个字被当前正在执行的高级定义字所调用。正是 CFA 决定了一个字的属性(或者类型)。

在下面的例子中,字 A 调用字 B ,字 B 调用字 X 等等。 CFA 保存在中间寄存器 W 中。采用这种两级间接寻址和字典列表结构的 Forth 编码方式也被称为“间接串线编码”。

图 2 间接串线编码

地址解释器读入 I 并使用列表中的下一个地址装入 W ,然后从 W 读出并执行由它指示的代码。 I 自动增量指向列表中的下一个入口,因为第一个入口是 CFA ,所以 I 现在指向了定义体。这对于查找表一类的数据结构非常有用,因为定义体就是表元素的存储区。在高级定义的情况下,代码域地址指向子程序 (:) ,它把当前的 I 保存到返回栈上,然后从 W 装入 I 并重复这一过程。在高级定义的结尾,字 EXIT 被执行,恢复原来的 I 值,程序像以前一样继续执行。这三个动作“读 I 指针,保存 I 指针,恢复 I 指针”是地址解释器得以工作的基本机制。

地址解释器有三个重要的特点:

•  特别快。尽管实际的速度依赖于特定的实现,但专业化的实现是高度优化的,每个地址通常要求 1-2 个机器字。这相比纯的汇编代码只增加 0%-50% 的开销。在许多基准程序测试中,好的 Forth 实现远远胜过诸如 BASIC 或者 LISP 一类的解释语言,可以与其它的编译高级语言相比;

•  它使得 Forth 定义极其紧缩,每一个引用仅仅需要一个单元(可以把单元理解成机器字,但是“字”在 Forth 中是指一个定义项)。作为比较,我们可以考虑一下许多高级语言产生的子程序调用结构:一个 CALL 或者 JSR 指令,后随地址,在这之前和之后,还有参数序列的访问指令。

•  使用返回栈保存地址的方法对于程序员是透明的,字也可以按需要嵌套许多层,这样的高级定义字序列可以被自动处理而不需要特殊的代码。

Forth 系统中的许多字都是用 : 定义并通过地址解释器进行解释的,而许多 Forth 系统本身也是用这种方法定义的。

8. 汇编器

大多数 Forth 系统都包含一个宏汇编器,为它们所在的 CPU 进行汇编语言编程。通过使用定义字 CODE ,程序员可以创建由实际的机器代码组成的定义。 CODE 定义可以用来完成 I/O 、算术原语和其它依赖机器(时间紧迫)的处理功能。当程序员使用 CODE 时,他就和使用其它汇编器一样对 CPU 有完全的控制能力, CODE 定义的字以机器速度全速运行。

这是 Forth 的一个重要特点。它允许那些明显地依赖机器的代码通过机器无关接口变换成为可管理的片断。当需要把应用移植到不同的处理器上时,我们仅仅需要重新编写 CODE 字,这些 CODE 字就可以严格地按相同的方法与其它 Forth 字进行交互。

Forth 汇编器本身也是极其紧缩的(典型地为 1K 字节),它们可以和编译器、编辑器以及其它编程工具一起驻留在系统中。这就意味着程序员可以打入很短的 CODE 定义,然后立即执行它们,这个能力在调试客户硬件时特别有用。

9. 虚拟存储

Forth 最后一个独特的组成元素是它按数据和源程序虚拟存储器的方式使用磁盘(或者其它海量存储器)。在地址解释器的情况下,这种方法在历史上是典型的 Forth 方式,但现在并不通用。磁盘被分成 1024 字节大小的块,在内存中则准备两个或者多个缓冲区,数据在引用时被自动读入缓冲区。每个块有固定的块号,直接对应于本地存储系统的物理位置。任何时候程序需要访问时,它都会在内存中找到数据,并不需要显式的读写操作。

这样一个面向块的磁盘管理方法对于 Forth 独立系统的实现是简单和高效的,结果是块为独立和协同驻留 Forth 实现中的源程序和数据处理提供了一个完全透明的机制。

在源程序块中的定义通过 LOAD 命令编译到内存中。许多系统包含一个编辑器,它把每个块组织成 64 字符 16 行的显示模式并提供命令来修改源程序。

源块在历史上曾经是 Forth 风格的一个重要组成元素。就像 Forth 的定义可以被视为自然语言中的句子,一个块相当于是一个段落,通常包含有与一个主题相关的定义,比如“向量处理”。在块文本的顶行含有这个主题的注释说明,应用程序可以按实际需要来选择装入块。

块也被用于存储数据。小的记录可以组合进一个块,大的记录也可以分布在几个块中。程序员可以根据应用的需要分配块,独立的 Forth 系统还能够按磁头运动最少的原则来组织数据以优化系统,还有几个开发商提供了基于 Forth 块的文件和数据库系统。

在主机操作系统之上运行的 Forth 版本使用文件系统来实现块机制,另外也提供基于操作系统环境的一般文件操作方法。

10. 数据结构

所有的高级字共享相同的数据结构,这种字的首部含有文本解释器识别一个字所需要的数据信息,见图 3 所示。

图 3 定义的首部

“LINK”字段包含字典中前一个命令的首部地址,在搜索字典时需要这个地址。

“名字长度”字段包含名字中全部字符的数量,后随这些字符,在解释过程中进行名字匹配时需要这些数据。许多 Forth 系统仅仅存储名字的前3个字符,这可以在占用空间最小的情况下避免名字冲突,而许多系统的名字长度允许在 3-31 个字符之间选择。

本文解释器从字典的最后一个入口开始处理,并沿着链表向后查找直到匹配名字为止。现在还有许多系统提供散列算法,它们把字典分成几个表,每次只查找其中的一个,这种方式可以大大地节省查找时间。

“代码指针”是这个字将被执行的第一个指令的 CFA ,它形成了定义体的第一个入口。它也是代码的地址,这个地址解释定义的其它部分,对于不同的数据结构将指向不同的代码。

图 4 不同的数据类型所指向的行为也不同

字的其它部分也就是定义体,它依不同的数据类型而不同,如图 4 所示:

•  在汇编 (CODE) 定义中,体中含有定义命令行为的机器操作码列表,因此 CFA 含有这个体的地址;

•  在诸如 VARIBLE 和 CONSTANT 这样的数据结构命令中,字义的体中含有实际的数据。 CFA 含有处理这些数据所需要的子程序的入口地址,用户定义数据结构的例子如图 5 所示。

•  在高级 : (冒号)定义中,字的体中含有构成这个定义的所有以前定义字的地址列表。 CFA 是地址解释器 (:) 的地址。

11. 程序举例

下面是一个典型的 Forth 源程序块。这个应用程序用来控制 8 个 LED 提示灯,它也说明了 Forth 定义是如何组合以形成应用环境的。

图 5 一个源程序块

注意第一行 (LINE 0) 的注释说明了这个块中所列出的其它字的属性,这一行通常被称为索引行。多数 Forth 系统都有一个命令字来查找块的第一行以发现哪个块是应用程序所需要的。

所有的 LED 通过一个位于地址 40H 的 8 位口来访问。这个位置通过第一行的常数定义给出,于是这个口可以通过名字来引用,而如果以后硬件地址改变了,我们也仅仅需要调整这个常数。字 LIGHTS 在堆栈上返回这个地址,定义 LIGHT 得到栈顶值并把它发送到设备上。这个值的属性是位掩码,每一位对应一个 LED 。于是命令:

255 LIGHTS

将点亮所有的灯,而命令

0 LIGHTS

将关闭所有的灯。如果硬件变化了,我们也只需要改写字 LIGHTS 以适应新的硬件,而应用程序的其它部分则不需要做任何改变。

行 4-6 含有对我们终端输入进行的处理,终端输入是为了能够进行简单的诊断以确认各个方面都能够工作正常。这里 DELAY 含有以微秒为单位的延时时间,执行字 DELAY 返回这个变量的地址。 DELAY 的两个值是由 FAST 和 SLOW 定义的,使用 Forth 的操作!(读作 STORE )从栈顶得到地址和值,并把值存储到相应的地址中。

定义 COUNT 运行一个从 0-255 的循环(在字 DO 和 LOOP 之间)。字 I 把循环的当前值放到栈顶,然后发送给 LIGHTS 。接着系统等待由 DELAY 指定的时间。

字 @ (读作 FETCH )从 DELAY 提供的地址读取一个值,并把这个值交给 MS 等待指定的毫秒。执行 COUNT 的结果就是以指定的速率从 0 到 255 之间计数。可以这样调用:

SLOW COUNTS 或者

FAST COUNTS

8-10 行提供单独命名灯的能力。在这个应用中,它们用作灯的指示器。字 LAMP 是一个使用位掩码作为参数的定义字。字 CREATE 把一个首部编译进字典,同时字,(逗号)把掩码放到定义体中。字 DOES> 把代码地址放到新字的 CFA 中。于是当新字执行时,它的动作就是获得所定义的新字体中的第一个项目的内容。

行9和10使用 LAMP来命名特别的指示灯。当其中的一个字比如 POWER 执行时,掩码就返回到栈顶。实际上,这种行为与 Forth 字 COSNTANT 一样。但是 LAMP 是一个例子,像这样使用 CREATE …… DOES> 定义字的能力是 Forth 语言最强大的功能之一,它允许我们定义基于数据结构的“智能应用”。

最后,行 13-15 ,我们有了控制灯面板的字。 LAMPS 是一个变量,它包含有灯的当前状态。字 LAMP-ON 得到一个掩码(由其中一个 LAMP 字提供),然后点亮灯。 LAMP-OFF 将指定的灯关闭,同时改变 LAMPS 的状态。

在应用程序的其它地方,灯的名字和 LAMP-ON 、 LAMP-OFF 可能才是需要直接执行的字。用法是:

POWER LAMP-ON 或

SAMPLING LAMP-OFF

在系统上编译这个代码块的时间大约是半秒,包括从磁盘上读入的时间。这样程序员就有可能简单地打入一个定义然后立即执行它。另外我们总是具有与外部设备直接通信的能力。我们可以首先打入:

HEX FF 40 OUTPUT

来看看是不是所有的灯都亮。如果不是,可以假定有些地址不对,因为上面的句子是把全 1 直接发送给设备。这种类型的交互在与硬件打交道的时候特别有用,它们能够缩短硬件的开发时间。

12. 扩展

由于 Forth 是可扩展的,大量的开发商为 Forth 语言的应用提供许多扩展产品。这些扩展包括自动化的文档、终端驱动、磁盘文件管理、数据库管理、目标编译器、交叉编译器和 META 编译器。

一个目标编译器允许我们使用一个主机 CPU 比如PC机来编写系统程序。程序可以通过键盘编辑和交互式地测试,之后可以把这些程序编译到目标环境中并产生对应的 ROM 代码。目标编译器的一个功能就是剔除目标系统中不需要的代码,比如编辑器、编译器和汇编器。通过这些工作,运行系统的 ROM 开销可以从开发系统所占用的 8K 字节减少到大约 600 个字节。

交叉编译器与目标编译器类似,它允许用户在主机上开发和测试代码,然后交叉编译器将像目标编译器一样编译 Forth 系统,只是交叉编译器是使用不同的机器语言、为不同的目标 CPU 产生代码。这个特点允许快速地为新的 CPU 开发新的 Forth 系统,由于 Forth 系统主要是用 Forth 语言编写的,所以我们只需要为新的处理器编写一个汇编器,然后为新的处理器进行 Forth 系统的交叉编译就可以了。大多数的 Forth 系统就是用这种方式开发的,结果, Forth 通常成为一个新处理器上的首选语言之一。

写一个 Forth 编译器的过程也被称为 META 编译。

13. 多任务

多任务在 Forth 中从一开始时就有了。这些系统不仅仅是语言处理器,而且包含操作系统的特点,许多集成系统完全是独立运行的,执行所有操作系统的功能。

Forth 的操作环境运行速度快,所以基于 Forth 的系统就能够支持多用户和多任务,甚至在那些看似不能实现这些功能的计算机硬件上也是这样。例如,一个电话交换板的生产商在一个 Z80 上执行 50 个任务。在 IBM PC 上也有几个多任务产品,有些甚至能够支持多用户。在一般支持多用户操作的计算机上,可以支持的用户数就更多了,一个运行在单个 MC68000 处理器上的数据库应用系统能够支持 100 个终端更新和数据库请示,而运行速度却没有明显的降低。

多用户系统可以支持多个程序员同时工作,每个程序员都有自己的私有字典、堆栈和一系列变量来控制任务。私有字典连接到一个共享的、可再入的、包含全部标准 Forth 功能的字典。私有字典可以用来开发应用程序代码,这些代码以后可以加入到共享字典中。

图 6 给出了 Forth 系统实现多任务的典型方法。这是一个“轮循调度算法” 调度器,是 Forth 系统中实现多任务最常用的方法。然而,有一些实现使用时间片调度或者基于优先级的调度以及其它可剥夺的算法,在这样的系统中每个任务都有自己的用户区以保存控制变量、私有字典和堆栈。用户区的第一个字段是 STATUS 变量。这个变量有两个可能的值:唤醒或者休眠。

图 6 在循环调度队列中的 4 个任务

可以被选择调度执行的任务必须处于唤醒状态。当任务被选择执行时它的状态就被复位到休眠状态。任务一直执行直到调度器执行到字 PAUSE ,这将把任务的状态置为唤醒,控制重新返回到调度器。当调度器再次轮循到这个任务时,将从 PAUSE 的指令之后继续执行。

除了 PAUSE 字之外,任务也可以执行 STOP 字将控制返回到调度器。这与 PAUSE 类似,但是任务的状态并不切换到唤醒状态。这样,当调度器再次轮循到这个任务时,当前的状态仍然是休眠。这意味着这个任务不能再被执行了,除非它的状态被另外的任务或者中断设置为唤醒。

系统以这种方式编程并允许中断。当一个中断发生时,一些与机器有关的代码将指定的任务状态置为唤醒,这样当调度器再次到达这些任务时它们将被执行。当任务执行时, STATUS 变量被置为休眠,这就允许一个任务执行时可以发生中断。因此,如果一个任务执行了 STOP 指令而它的状态没有改变,在它执行中一个中断设置了它的状态为唤醒,它就可以再次被调度,于是我们能够缓冲一个中断。然而,这也就意味着当一个任务主动放弃处理器并希望下一次调度继续执行时,它必须把状态设为唤醒。

轮循调度器使用存储在 LINK 用户变量中的地址作为下一个任务的地址。如果这个任务的 STATUS 是唤醒的,该任务将被调度执行,否则,调度器取出它的 LINK 地址转到下一个任务。

这种方法有两个主要问题:

•  当一个任务为了其它任务可以运行而主动放弃处理器时,从放弃到再次得到处理器的时间是不确定的;

•  中断设置一个任务为唤醒状态到这个任务实际执行之间的时间也是不可知的。

通过小心地使用 PAUSE ,再加上 Forth 的运行速度很快,已经在主要的应用系统中克服了这些问题。

14.Forth 硬件

早在1980年,ROCKWELL公司就生产了65F11处理器,这是一个6502处理器的变型,它在6502芯片的 ROM 中固化了 Forth 原语,这个芯片被成功地应用到了许多嵌入式系统中。

1981年, Chuck Moore开始设计一个 Forth 虚拟机芯片。这个工作首先在 Forth,INC. 进行,后来专门成立了一家公司来开发这个芯片。 1984 完成设计, 1985 年生产了第一个样片。后来, HARRIS(INTERSIL) ,Johns Hopkins University 和其它人也开发 Forth 硬件。基于 Forth 的芯片提供了极高的性能,一般可与 RSIC 芯片相比,但是它没有传统 RISC 的程序设计复杂性。

15. 标准

Forth 界认识到需要一个标准,使得应用和扩展都以此为基础。第一个这样的标准从上个世纪 70 年代中期 FIG(Forth 兴趣组织 ) 开始,这个标准后来变成了 FIG-Forth 标准。

第一个主要的 Forth 标准来自于 1977 年在 Utrecht 召开的一次会议,与会者提出了一个原始的标准并同意以后继续举行会议讨论, 1978 年的会议包括了 FIG 成员,经过一系列由用户和开发商参加的会议,产生了一个更加广泛的标准,称为 Forth-79.

尽管 Forth-79 标准的影响力是巨大的,但是许多 Forth 用户和开发商发现这个标准中有一些致命缺点。 1983 年,两次进一步的会议产生了 Forth-83 标准。

受到 Forth-83 标准被广泛接受的鼓舞,一组用户和开发商从 1986 开始讨论美国国家标准的可行性。美国国家标准 Forth 技术委员会,也称为 ANS ASC X3/X3J14 委员会,于 1987 年召开了第一次会议,目标是“达到一个可接受的标准,被 Forth 产品主要开发商接受,对现有在用产品的不利影响最小”。 1994 年,也就是大约 7 年之后,新的标准最终产生了,这是所有标准中走得最远的。使用来自 5 个国家的说明,通过开发商向社会公开。两年之后,国际标准化组织接受这个标准为国际标准。

16. 研讨会和定期出版物

由于人们使用 Forth 进行工作并不断做出有益的扩展,所以 Forth 语言也在不断地发展。有三个年会关注着 Forth 语言的发展:

Forth Modification Laboratory (FORML)

held at Mt. View in California and organised by the Forth Interest Group.

Rochester Forth Conference (RFC)

held at the University of Rochester in New York and organised by the Institute for Applied Forth Research Inc.

European Forth Conference (euroForth)

held in a different location each year. This is the only conference to provide a refereed (peer reviewed) section. Originally organised by the European Forth Interest Group, but now organised by various European based Forth vendors.

对于 Forth 的许多新的思想最先在这些 Forth 相关的杂志上出现:

Forth Dimensions (ISSN 0884-0822)

published six times a year by the Forth Interest Group for its members.

Journal of Forth Application and Research (ISSN 0738-2022)

published irregularly by the Institute for Applied Forth Research Inc. This is the only peer review (refereed) journal directed to the Forth language. The Journal is now published electronically on the Internet. A paper version of the Journal will be published at irregular intervals.

17. 总结

FROTH 可以被看成是有两个堆栈(参数栈和返回栈)的、有可扩展汇编语言的处理器。 Forth 解释器可以看成是这个抽象处理器的完全宏汇编器和完全集成的操作系统。

这就给我们提供了一个交互式的调试环境,使我们可以增加新的宏(高级定义)和新的指令(低级 CODE 定义)。它甚至允许我们通过定义新的数据类型(使用定义字 CREATE …… DOES> )扩展宏系统本身。因为这个解释器也是一个全功能的操作系统,所以程序员只需要学习一种工具。

Forth 有四个直接的优点:亲善、直接、可扩展和经济。它还有两个间接的优点:可理解和符号化。

Forth 不仅仅是一种语言,它更是一种解决问题的哲学思想。这可以用缩写 K.I.S.S 来概括( Keep It Simple and Stupid ,保持它的简洁和易用)。我们可以引用 Jerry Boutelle (owner of Nautilus Systems in Santa Cruz, California) 的话,当被问及“使用 Forth 是怎么影响你的思维”时,他回答:

“Forth 以许多方式改变了我的思维。从学习 Forth 开始,我已经使用了各种语言编程,包括汇编、 BASIC 、 FORTRAN 。我发现我一直在使用 Forth 的问题分解方式,通过创建字的方式把它们组合在一起。例如,在处理字符串的时候,我会像 Forth 的 CMOVE 、 -TRAILING 、 FILL 那样来定义子程序。更基本地, Forth 坚定了我对简单性的信念。在面对一个问题的时候,许多人使用特别复杂的工具,但是简单的工具也可以使用而且更有用。我试着简化我生活的方方面面。”

这里我引用中国哲学家老子的一句话:

“为学日益,为道日损”

或者引用 Antoine Lavoisier (1789) 的话:

“把科学和语言分离是不可能的,因为每一项自然科学总是包含三件事情:科学所基于的一系列现象,在头脑中命名这些现象的抽象概念,表达这些概念的词汇。为了说明一个概念,我们需要一个词汇;为了描绘一个现象,我们需要一个概念。所有这三件事情都反映了同一个现实。”

这就是包含在 Forth 语言背后的哲学。

18. 参考文献

略 ( 可参见原文 )

你可能感兴趣的:(forth)