The force: Forth 语言
- MD Document: 2/24/2016 8:49:24 AM by Jimbowhy
- CSDN发布:
这是一个可以靠美色搞钱的时代,这是一个可以靠权力搞钱的时代,可是我不乎,因为我都没有,还因为我知道这一切都不保鲜,TA们当然也更清楚,所以要趁早卖! - by Jimbowhy 3/3/2016 9:38:49 AM
本文是《VirtualNES虚拟机》关于 Forth 语言部分的延伸,是对原文的扩展。
计算机语言应该是易理解,因为它是人机交流的管道,如果人理解语言都成问题,那么还怎么能期待计算机很好地执行我们交待的程序呢?传统高组语言如C,特别是C++隐藏了太多细枝末节的东西,所以异常问题常常登门造访,让人防不胜防。
Forth 语言对我来说是一个全新的概念,初次接触它时,很容易就打破了我以往建立的计算机编程语言的观念。每一种计算机语言基本上都会有一个基本模型,即把程序运行的环境当作什么。传统的C语言看到的是 CPU 与内存的连接,还有在内存建立的堆栈结构。而 Forth 把 CPU 当作是 多个堆栈和内存之间的连接通道。
+------------+ +-------------+ +--------------+ +--------------+
| data-stack | | float-point | | return stack | | memory |
+------+-----+ +------+------+ +------+-------+ +--------------+
| | | | stack-layout |
| | | +--------------+
| +------+------+ | | .... |
| | | | +--------------+
+----------+ CPU +-----------+ | stack-layout |
| | +-------+------+
+------+------+ |
Forth | C +-------+------+
Virtual Machines | Virtual Machines | |
+------+------+ | CPU |
| memory | | |
+-------------+ +--------------+
然而开发属于自己计算机语言编译器确是件非常有趣的事,试想,将整个电脑系统当作一个经济体,那么操作系统就像是政府机构,编译工具则像是重工业,而应用软件则像是轻工业或服务业。David A. Wheeler 在他的一篇文章中陈述了自己实现一个 6502 平台的语言中的经过及经验 A-Lang: WAYS TO IMPLEMENT COMPUTER LANGUAGES ON 6502s。在他的另一篇文章中提到许多 6502 Language Implementation Approaches 语言实现,提到6502平台的两个Java虚拟机实现 VM02 和 NanoVM。还提到一本特别适合编译入门的教材《Crafting a Compiler》,已经添加目录好书签上传方便阅读。 其中特别介绍了 Forth 语言,特别强调它编译的程序运行快,Thinking Forth 项目提供了一本教材,可以下载彩色版的PDF文件。Brad Rodriguez 的 Moving Forth 讲述如何移植 Forth 到其它硬件平台,阅读时会提到“穿线”技术 the Threading Technique,请不要和线程混淆,Forth 是一种全新视角的计算机语言。
Forth 语言是 Charles Moore 在 20 世纪 60 年代发明的基于堆栈、交互式、具有简单性哲学思想的计算机编程语言和环境。实践中证明它特别适合千行代码数量级的嵌入式系统,能有效降低开发成本和增强系统可靠性,被广泛应用于各行业领域,国际天文学会于 1976 年接受了 Forth 作为标准语言。由于 Forth 的高可用性,CPU厂商也开始为 Forth 体系结构设计优化的堆栈计算机处理器芯片。
Forth 是一个交互式的程序设计环境,最初是为程序员在小型和微型计算机上开发应用程序而设计的,主要优点是软件开发快速、交互式、计算机硬件的高效使用等。与传统的高级语言工具不同,Forth 把编译器、编辑器、汇编器等等融为一体,这是优化的设计。这也使得它的内部结构对于新用户来说很特殊,但是 Forth 的简单性、高度模块化和交互式特性可以弥补初学者的陌生感,使得 Forth 非常易于学习和使用。在 Startgin Forth 中,是这样描述 Forth 的:
a high-level language
an assembly language
an operating system
a set of development tools
a software design philosophy
为了在PC机上一探 Forth 真容,可以使用 Tom Zimmer 的 Win32Forth,它可以在Win32平台上运行。在这 A Beginner’s Guide to Forth by J.V. Noble 下载 w32for42.exe。很不巧的是 Win32Forth 4.2 这个安装包程序是16位的,不能在Win7 x64平台运行,只能通过DOSBOX运行Win3.1来解包了。在本文发布时,Win32Forth 6.15 是最新版本,可以到 sourceforge 下载。使用 SwiftForth 也是一个不错的选择,还附带 Application Techniques 和 Programmer’s Handbook 电子书,评估版只是不含原代码。
Forth也被戏称为“只写”语言,因为它的语法风格非常怪异,常常代码写完后边作者本人都读不懂。其中一个原因是 Forth 将许多高级语言的功能分解了,以更精细的进行控制,所以在写 Forth 程序时,需要很好的语言组织能力,不仅写代码同时还要将自我解释的信息写进去,这才是一个优秀的程序人。
对于新手来说《Starting Forth by by Leo Brodie》 是非常棒的入门教材,作者也很用心在写作,对于那些稍为难懂的机器运行过程都以漫画的形式化简表达,Starting Forth 离线版已经制作打包,修改了眼睛保护背景色,可以在此下载。作者在书中还用了一种我之前从未见过的方法来解析负数的补码是什么,设想一个展馆,每天到访客流都有个限额,假设最大的额度就是255,刚好是一个字节可以存储的最大值。那么每有一个访客额度就会减一,即表示负一:
1111 1111 - 1 = 1111 1110
实际上使用的补码是2’补码,即在1’补码的基础上加一,也就是取反加一的补码形式,这样计算机可以用加法器就可以进行减法运算。
来自17世纪 G.W.Leibniz 莱布尼茨二进制算法手稿
史蒂芬·沃尔夫勒姆(Stephen Wolfram)即数学软件 Mathematica 和知识型计算引擎 Wolfram Alpha 的开发者。他的博客文章《探访莱布尼茨:与大师穿越时空的碰撞》(Dropping In on Gottfried Leibniz)引用了许多原始的手稿图片,上面这张图就是其中之一。莱布尼茨之后的数个世纪里,几乎没人用2进制做出些许成果,直到电子数字计算机的兴起。
掀起她盖头
Forth语言一个特点就是用词汇来描述程序,所以执行程序就可以理解为给计算机下达具有指令功能的单词。解释器接收到输入时,会进行检测,看输入内容是不是一个定义的词,是不是一个变量,是不是常量等等,如果是词汇定义,那么就执行它。为了更直观题解这一过程,可以先来了解秒号 ’ 即双引号按键上的那个符号,它就是用来查询词汇定义的内存地址的,知道词汇地址后,就可以通过执行指令来运行词汇程序,如下执行一个称为 GREET 的词,如果它定义的话:
' GREET EXECUTE
Forth系统的词汇其实是一个链表结构,每一个词汇定义都记录了可以执行代码的地址,和前后两个相邻的词汇,解释器通过线性查找来定位词汇代码在内存的地址,因此一个单词定义的典型结构包含如下内容:
name field
link field
code pointer field
data field
link field 就是存储相邻单词定义的数据单元,Code Pointer 则包含了单词定义的属性,是变量定义还是常量定义,还是可以执行词,都是这一块内容记录的。在解释器接收输入和完成执行指令的过程可以用以下的过程表达,:
: QUIT BEGIN (clear return stack)
(accept input)
INTERPRET
." ok " CR
AGAIN ;
这是一个典型的单词定义格式,INTERPRET 指示了解释器所在的位置。 BEGIN … AGAIN 构成了一个循环体,在循环体内不断地对返回堆栈进行清理,然后接收输入,然后将输入交给解释器进行处理,QUIT 这个单词则是 Forth 系统执行的。当前的位置可以用 HERE 这个词来取得,其它和解释器相关的单词功能如下:
' xxx ( -- addr ) Attempts to find the execution token of xxx in the dictionary.
['] compile time:( -- ) run time:( -- addr )
Compiles the execution token of the next word in the definition as a literal.
EXECUTE ( xt -- ) Executes the dictionary entry whose execution token is on the stack.
EXIT ( -- ) When compiled within a colon definition, terminates execution at that point.
QUIT ( -- ) Clears all stacks and returns control to the terminal. No message is given.
HERE ( -- addr ) Returns the next available dictionary location.
PAD ( -- addr ) Returns the beginning address of a scratchpad area.
SCR ( -- addr ) User variable. A pointer to the current block number (set by LIST).
BASE ( -- addr ) User variable. Number conversion base.
SP@ ( -- addr ) Return the address of the top of the stack before SP@ is executed.
TIB ( -- addr ) User variable. Contains the address of the start of the terminal input buffer.
#TIB ( -- addr ) User variable. Contains the size of the terminal input buffer.
SP0 ( -- addr ) User variable. Contains the address of the bottom of the parameter stack.
>IN ( -- addr ) User variable. A pointer to the current position in the input stream.
BLK ( -- addr ) User variable.