久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

Forth 系统实现

發(fā)布時(shí)間:2023/12/18 windows 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Forth 系统实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Forth 系統(tǒng)實(shí)現(xiàn)

原作者 Brad Rodriguez

編譯者 趙宇 張文翠

本文編譯自 Brad Rodriguez 《 Moving Forth 》,原文首次在 The Computer Journal #59 (January/February 1993) 上發(fā)表,現(xiàn)在可在下面網(wǎng)站上取得

http://www.zetetics.com/bj/papers/index.html

本文對(duì) Forth 語言在各種處理器上的各種實(shí)現(xiàn)方式進(jìn)行了深入的探討,盡管其中作為例子的處理器非常古老,但對(duì)于理解 Forth 系統(tǒng)仍有著很大的參考價(jià)值。譯文按原文結(jié)構(gòu)翻譯,省略了各部分的參考文獻(xiàn),其內(nèi)容和本文中所列出的源程序代碼都可以在上述網(wǎng)站上得到。

目錄

第一部分 Forth 內(nèi)核的設(shè)計(jì)決策

第二部分 內(nèi)核基準(zhǔn)測(cè)試和個(gè)案研究

第三部分 解密 DOES>

第四部分 匯編器還是 META 編譯器

第五部分 Z80 原語

第六部分 Z80 高級(jí)內(nèi)核

第七部分 8051 的 CamelForth

第八部分 MC6809 CamelForth

第一部分 Forth 內(nèi)核的設(shè)計(jì)決策

前言

每一個(gè)進(jìn)入 Forth 圈里的人都說或者聽說“把 Forth 移植到一個(gè)新 CPU 上是一件易如反掌的事情”。不過,就像其它許多“易如反掌”的事情一樣,卻沒有多少書面的資料告訴我們?nèi)绾稳プ?#xff01;所以,當(dāng) Bill Kibler 建議這個(gè)論文題目時(shí),我決定打破 Forth 編寫者只說不練的傳統(tǒng),給出一個(gè)白紙黑字的 Forth 實(shí)現(xiàn) , 包括為 MC6809 、 Intel 8051 和 Z80 實(shí)現(xiàn)的 Forth 系統(tǒng)。

在整個(gè)文檔中,我準(zhǔn)備為 MC6809 、 Intel 8051 和 Zilog Z80 實(shí)現(xiàn) Forth 系統(tǒng)。我會(huì)用 MC6809 來解釋一個(gè)簡(jiǎn)單的和傳統(tǒng)的 Forth 模型,此外,我還將公布一個(gè) MC6809 匯編器,把 6809 Forth 用于未來的 TCJ 計(jì)劃,把 8051 作為一個(gè)大學(xué)項(xiàng)目,其中也解釋了一些非常不同的決策。 Z80 Forth 是為所有的 TCJ CP/M 讀者和許多 TRS-80 的老朋友而編寫的。

有效的硬件

首先,我們必須選擇一個(gè) CPU 。不過,我不想陷入“Forth 運(yùn)行在這種 CPU 上比運(yùn)行在那種 CPU 上更有效”的爭(zhēng)論中,因?yàn)?CPU 的選擇通常還需要考慮其它因素,并且這篇論文的目標(biāo)之一就是想說明如何把 Forth 搬到任何一個(gè) CPU 上。

通常, 16 位 Forth 內(nèi)核需要 8K 字節(jié)的程序空間。對(duì)于一個(gè)能夠真正用來編譯 Forth 語言應(yīng)用的完整內(nèi)核來說,應(yīng)該至少有 1K 字節(jié)的 RAM 。如果想使用 Forth 的磁盤存儲(chǔ)器塊管理功能,還應(yīng)該再增加 3K 字節(jié)以上的 RAM 用于緩沖區(qū)。對(duì)于 32 位系統(tǒng),這些數(shù)值都需要加倍。

以上這些是一個(gè) Forth 內(nèi)核能夠運(yùn)行的最小要求。為了在硬件上運(yùn)行應(yīng)用程序,你還得按實(shí)際需要另外增加 PROM 和 RAM 的大小。

使用 16 位還是 32 位系統(tǒng)

實(shí)際的系統(tǒng)并不要求 Forth 的字長(zhǎng)度與 CPU 的字長(zhǎng)度一致。最小的、實(shí)際可用的 Forth 系統(tǒng)都使用 16 位模型,也就是說使用 16 位的整數(shù)和 16 位的地址。 Forth 的術(shù)語把這種尺寸稱為單元(CELL)而不是我們常說的“字”,因?yàn)椤白帧痹?Forth 中是指一個(gè) Forth 定義(可以簡(jiǎn)單地理解成其它高級(jí)語言的子程序名)。

所有的 8 位 CPU 幾乎都不加改變地支持 16 位的 Forth ,為此,要求編碼進(jìn)行雙字節(jié)算術(shù)運(yùn)算,盡管某些 8 位 CPU 也能夠直接支持其中的一些操作。

有一些技術(shù)可以在一個(gè)16位的機(jī)器上寫出 32 位的 Forth ,但是 16 位的 CPU 通常運(yùn)行 16 位的 Forth ,雖然我們也看到 32 位的 Forth 可以運(yùn)行在 Intel 8086/8088 上。

32 位的 CPU 通常運(yùn)行 32 位的 Forth 。實(shí)際應(yīng)用中,一個(gè)更小的模型幾乎不能節(jié)省代碼空間和處理器時(shí)間。但我也見到過為 MC68000 編寫的 16 位 Forth 。這個(gè)系統(tǒng)的代碼長(zhǎng)度縮小了 2 倍,因?yàn)楦呒?jí) Forth 定義變成了 16 位的地址串而不再使用 32 位的地址串。不過大多數(shù)的 MC68000 系統(tǒng)都有很多 RAM ,好象沒有必要進(jìn)行這樣的努力。

本文描述的所有例子都是運(yùn)行在 8 位 CPU 上的 16 位 Forth 系統(tǒng)。

串線編碼技術(shù)

“串線編碼”是 Forth 的標(biāo)志。一個(gè) Forth “串線”就是被執(zhí)行的子程序地址的列表。你可以把它們想象成一連串省略了 CALL 指令的子程序調(diào)用表。長(zhǎng)期以來,人們發(fā)明了多種串線形式,為了作出選擇,你必須理解所有這些串線形式是如何工作的,以及它們各自的優(yōu)缺點(diǎn)。

間接串線編碼( ITC )

間接串線編碼(ITC)技術(shù)是一種經(jīng)典的 Forth 串線編碼技術(shù),最早出現(xiàn)在 FIG-Forth 和 F83 系統(tǒng)中,并且在許多關(guān)于 Forth 的書中都有描述。后來的串線方式都是直接串線編碼方式的“發(fā)展”,所以,你需要先理解這個(gè)技術(shù)。

讓我們看一個(gè) Forth 字 SQUARE 的定義 :

: SQUARE DUP * ;

在一個(gè)典型的 ITC Forth 中,該定義在存儲(chǔ)器中的情況如圖 1 所示(首部將在以后討論,它保存編譯信息,但并不在串線中訪問)

圖 1 ITC Forth 定義的存儲(chǔ)器

假設(shè)在執(zhí)行某個(gè) Forth 字的時(shí)候遇到了字 SQUARE , Forth 的解釋指針 IP 將指向存儲(chǔ)器的一個(gè)單元,其中包含字 SQUARE 的地址,當(dāng)然更嚴(yán)格地應(yīng)該說,這個(gè)單元包含著 SQUARE 的“代碼域地址”。解釋器讀出這個(gè)地址并用這個(gè)地址讀出 SQUARE 的代碼域內(nèi)容。它還是一個(gè)地址 -- 這個(gè)地址是一個(gè)機(jī)器語言子程序,由這個(gè)子程序來執(zhí)行定義 SQUARE 。

我們可以把上面的描述通過偽碼表示如下:

(IP)-> W 讀取 IP 指向的存儲(chǔ)器內(nèi)容到 W 寄存器, W 現(xiàn)在有代碼域的地址;

IP+2->IP 增量 IP, 它就像一個(gè)程序計(jì)數(shù)器,而且假設(shè)串線中的地址是 2 個(gè)字節(jié)長(zhǎng);

(W) -> X 讀取由 W 指向的存儲(chǔ)器內(nèi)容到 X 寄存器, X 現(xiàn)在指向機(jī)器碼地址;

JP (X) 跳到 X 寄存器指向的地址執(zhí)行;

這里解釋了一個(gè)重要的、但卻很少有人說明的原理:進(jìn)入的 Forth 字的當(dāng)前地址保存在 W 寄存器中。 CODE 字不需要這個(gè)信息,但是其它類型的 Forth 字確實(shí)需要其中的信息。

如果 SQUARE 是由機(jī)器代碼寫成的,事情也就結(jié)束了:這些機(jī)器代碼被執(zhí)行,然后跳回到 Forth 解釋器 -- 由于 IP 已經(jīng)增量,它將指向下一個(gè)將被執(zhí)行的字。所以 Forth 解釋器通常被稱為 NEXT 。

可是, SQUARE 是一個(gè)高級(jí)的“冒號(hào)”定義 -- 它保持一個(gè)“串線”,或者說一個(gè)地址列表。為了執(zhí)行這個(gè)定義, Forth 解釋器必須在一個(gè)新的位置上重新啟動(dòng),這個(gè)位置就是 SQUARE 的參數(shù)域。當(dāng)然,解釋器必須保存舊的位置,以便 SQUARE 結(jié)束之后能夠恢復(fù)“另一個(gè)” Forth 字。這實(shí)際上與一個(gè)子程序調(diào)用沒有任何區(qū)別!

SQUARE 機(jī)器語言的動(dòng)作就是簡(jiǎn)單地把舊的 IP 值保存到堆棧上,并把 IP 指向新的位置,執(zhí)行解釋器,當(dāng) SQUARE 完成后彈出恢復(fù) IP 。(正如你看到的, IP 就是 Forth 高級(jí)定義的“程序計(jì)數(shù)器”),這個(gè)過程在不同的 Forth 版本中可能被稱為 DOCOLON 或者 ENTER :

PUSH IP 壓入“返回地址?!?;

W+2 -> IP W 已經(jīng)指向代碼域,所以 W+2 就是定義體的地址! ( 假設(shè)是 2 字節(jié)的地址,不同的 Forth 可能不同 )

JUMP NEXT 到解釋器 ( “ NEXT ” )

這樣的一段代碼被用于所有的高級(jí)(串線) Forth 定義!于是我們回答了兩個(gè)問題:

?? 為什么在 Forth 定義中用一個(gè)指針指向代碼段,而不是把代碼段本身直接嵌入到定義中。因?yàn)槿绻袛?shù)以百計(jì)的定義,就可以節(jié)省大量的空間;

?? 為什么這種方式被稱為“間接串線編碼”;

“從子程序返回”動(dòng)作由字 EXIT 完成,它被 Forth 的分號(hào)“;”編譯進(jìn)定義中(有些 Forth 系統(tǒng)使用 ;S 替代 EXIT)。 EXIT 執(zhí)行下列的機(jī)器語言:

POP IP 從“返回地址棧”彈出指針

JUMP interpreter 跳轉(zhuǎn)到解釋器

注意 ITC 的特點(diǎn):每個(gè) Forth 字都有一個(gè)單元的代碼域,冒號(hào)定義給定義中的每一個(gè)字編譯一個(gè)單元。 Forth 解釋器為了執(zhí)行機(jī)器代碼,必須實(shí)際執(zhí)行兩次間接才能取得下一個(gè)機(jī)器碼的地址(首先通過 IP ,然后通過 W)。

ITC 既不是代碼尺寸最小的、也不是執(zhí)行速度最快的串線技術(shù)。它可能只是最簡(jiǎn)單的技術(shù),盡管下面討論的另一種技術(shù)DTC 實(shí)際上也不是特別復(fù)雜。那么為什么有這么多的 Forth 系統(tǒng)都使用間接串線技術(shù)呢?主要是由于以前作為原始模型的 Forth 系統(tǒng)都使用間接串線技術(shù),而現(xiàn)在, DTC 技術(shù)卻用得最多。

那么什么時(shí)候應(yīng)該使用 ITC 技術(shù)呢?很明顯, ITC 形式能夠產(chǎn)生最純凈和最一致的定義:其中只有一種類型,這種類型就是地址。如果你正好就有這樣的需要,那 ITC 技術(shù)就是適合的。如果你的代碼關(guān)注定義的內(nèi)部, ITC 技術(shù)的簡(jiǎn)單性和單一性還能夠增加可移植性。

此外, ITC 是經(jīng)典的 Forth 模型,它可以非常好地用于教學(xué)。

最后,在某些缺少子程序調(diào)用指令的早期 CPU 上 -- 比如 1802 -- ITC 常常比 DTC 更有效。

直接串線編碼( DTC )

直接串線編碼(DTC)技術(shù)與 ITC 技術(shù)的差別只有一點(diǎn):不像 ITC 在代碼域中包含機(jī)器碼的地址, DTC 的代碼域中包含有實(shí)際的機(jī)器代碼本身。

注意,我并不是說在每個(gè)冒號(hào)定義中都包含全部的 ENTER 代碼。我的意思是:在“高級(jí)” Forth 字中,如圖 2 所示,代碼字段有一個(gè)子程序調(diào)用指令。例如,冒號(hào)定義中將包含一個(gè)對(duì) ENTER 子程序的調(diào)用。

圖 2 DTC Forth 定義的存儲(chǔ)

直接串線的 NEXT 偽代碼非常簡(jiǎn)單:

(IP) -> W 取 IP 指針指向的存儲(chǔ)器內(nèi)容到 W 寄存器中

IP+2 -> IP 增量 IP ( 假設(shè) 2 字節(jié)的地址 )

JP (W) 跳轉(zhuǎn)到 W 寄存器指向的地址執(zhí)行

DTC 的收益就是速度:解釋程序現(xiàn)在只需要執(zhí)行一次間接。在 Z80 上,這實(shí)際是把 NEXT 子程序 --Forth 內(nèi)核中最常用的代碼段 -- 從 11 個(gè)指令減少到了 7 個(gè)指令。

DTC 的成本是空間:在一個(gè) Z80 Forth 中,每個(gè)高級(jí)定義都將增加一個(gè)字節(jié)的長(zhǎng)度,因?yàn)?ITC 中 2 個(gè)字節(jié)的地址現(xiàn)在被 3 個(gè)字節(jié)的 CALL 調(diào)用指令所取代。當(dāng)然這個(gè)結(jié)論也不是廣泛適用的,在 32 位的 MC68000 Forth 中,可以用 4 字節(jié)的 BSR 指令代替 4 字節(jié)的地址,其中沒有任何差異。而在 Zilog 的 SUPER8 中,有一個(gè)直接用于 Forth DTC 的指令,它用一個(gè)字節(jié)的 ENTER 指令代替 2 字節(jié)的地址,使得在 SUPER8 上, DTC Forth 比 ITC Forth 的代碼還要小。

當(dāng)然 DTC 的 CODE 定義也縮短了 2 個(gè)字節(jié),因?yàn)樗鼈儾辉傩枰羔槨?

我一直以為 DTC Forth 的高級(jí)定義字必須在代碼域中使用子程序調(diào)用指令, Frank Sergeant的 Pygmy Forth [SER90] 提出可以使用更簡(jiǎn)單的跳轉(zhuǎn)指令,這樣更容易實(shí)現(xiàn),通常也更快。

Guy Kelly 對(duì) IBM PC 上實(shí)現(xiàn)的 Forth 系統(tǒng)進(jìn)行了很好的總結(jié),這也是我對(duì)所有 Forth 編寫者的建議。

在他研究的 19 個(gè) Forth 實(shí)現(xiàn)中,有 10 個(gè)使用了 DTC 技術(shù), 7 個(gè)使用了 ITC 技術(shù), 2 個(gè)使用了子程序串線技術(shù)(這種技術(shù)我們將在下面討論)。所以,我認(rèn)為所有新實(shí)現(xiàn)的 Forth 內(nèi)核都應(yīng)該使用直接串線技術(shù),而不要再使用間接串線技術(shù)了。

跳轉(zhuǎn)到 NEXT 還是對(duì) NEXT 使用內(nèi)嵌編碼?

Forth 的內(nèi)層解釋器 NEXT 是一個(gè)用于所有 CODE 定義的通用子程序。你可以編寫一個(gè)子程序,然后讓所有的 CODE 字跳轉(zhuǎn)到這個(gè)子程序上執(zhí)行(注意:跳轉(zhuǎn)到 NEXT 而不必通過子程序調(diào)用到 NEXT )。

然而, NEXT 的速度對(duì)于整個(gè) Forth 系統(tǒng)的速度來說是至關(guān)重要的,從這個(gè)角度考慮, NEXT 最好是內(nèi)嵌代碼,于是 NEXT 也可以被定義成一個(gè)匯編的宏。

這就是一個(gè)常見的速度 / 空間折衷問題:內(nèi)嵌的 NEXT 總是更快,但也總是更大。全部增加的尺寸數(shù)量是內(nèi)嵌擴(kuò)展需要的字節(jié)數(shù)乘以系統(tǒng)中 CODE 字的數(shù)量。當(dāng)然有時(shí)也根本不需要考慮折衷:在 MC6809 中,內(nèi)嵌的 NEXT 總是比一個(gè) JUMP 指令還要短!

子程序串線( STC )

一個(gè)高級(jí)的 Forth 定義字只不過是“要執(zhí)行的子程序的列表”,并不一定要通過解釋才能實(shí)現(xiàn)它們,你也可以通過簡(jiǎn)單地調(diào)用一系列子程序而得到同樣的效果:

SQUARE:

CALL DUP

CALL * ; 或者是一個(gè)合適的名字,因?yàn)橛行﹨R編器不支持把 * 作為子程序名

RET

圖 3 為匯編程序員解釋了 Forth 的 STC 串線技術(shù)。 [KOG82].

圖 3 DTC Forth 定義的存儲(chǔ)

STC 有一個(gè)統(tǒng)一的表示方式,冒號(hào)定義和 CODE 字沒有區(qū)別,“定義字”(這是 Forth 的專用術(shù)語,像 VARIABLE 、 CONSTANT 這樣一些可以用來定義新字的字被稱為定義字)像 DTC一樣處理 -- 代碼域用一個(gè)跳轉(zhuǎn)或者調(diào)用指令轉(zhuǎn)到其它地方的機(jī)器碼。

STC 的一個(gè)主要缺點(diǎn)是:子程序調(diào)用指令通常比簡(jiǎn)單的地址列表址大。比如在 Z80 上,冒號(hào)定義的尺寸將增大 50% -- 而你的應(yīng)用中大部分都是冒號(hào)定義。相比在32位的 MC68000 上,如果使用 4 字節(jié)的 BSR 代替 4 字節(jié)地址,代碼尺寸沒有任何增加,不過,如果你的代碼超過了64K ,一些地址就必須用 6 字節(jié)的 JSR 代替。

子程序串線可能比直接串線更快。在 STC 中節(jié)省了解釋器執(zhí)行的時(shí)間,但必須花費(fèi) Forth 字用于返回的 PUSH 、 POP 時(shí)間。而在 DTC Forth 中,只有高級(jí)定義才引起返回棧動(dòng)作,在 MC6809 或者 Zilog SUPER8 中, DTC 比 STC 更快。

STC 還有一個(gè)優(yōu)點(diǎn):它不需要 IP 寄存器。有些處理器 -- 像 Intel8051 -- 缺少地址寄存器,沒有虛擬機(jī) IP 寄存器可以真正地簡(jiǎn)化內(nèi)核并提高速度。

STC 的內(nèi)嵌擴(kuò)展、優(yōu)化、直接編譯

在一些古老的 8 位 CPU 上,幾乎每個(gè) Forth 原語都需要用幾個(gè)機(jī)器指令才能實(shí)現(xiàn),但是在更強(qiáng)大的 CPU 上,有時(shí) Forth 原語只需要一個(gè)機(jī)器指令。例如,在一個(gè) 32 位的 MC68000 上, DROP 可以簡(jiǎn)化為:

ADDQ #4,An 這里 An 是 Forth 的 PSP 參數(shù)棧寄存器

在一個(gè)子程序串線的 Forth 中,冒號(hào)定義中使用 DROP 將產(chǎn)生這樣的序列:

BSR ...

BSR DROP ...

DROP:

ADDQ #4,An

BSR ...

RTS

ADDQ 本來是一個(gè) 2 字節(jié)指令,我們?yōu)槭裁匆獙懸粋€(gè)對(duì)這個(gè) 2 字節(jié)指令的 4 字節(jié)子程序調(diào)用呢?在這種情況下,不論有多少個(gè) DROP ,通過子程序調(diào)用都不會(huì)產(chǎn)生任何的節(jié)省。而如果把 ADDQ 直接編碼到 BSR 流中,產(chǎn)生的代碼都會(huì)更小,運(yùn)行得更快。有些 Forth 編譯程序已經(jīng)實(shí)現(xiàn)了這樣的 CODE 字“內(nèi)嵌擴(kuò)展” [CUR93a] 。

內(nèi)嵌擴(kuò)展的缺點(diǎn)是:如果要把代碼反編譯回原始的代碼就會(huì)非常困難。如果僅僅是使用子程序串線,我們依然可以得到指向 Forth 字的指針(子程序的地址)。通過字指針,就可以得到它們的名字。但是如果指令字?jǐn)U展到內(nèi)嵌編碼中,所有的關(guān)于字來源的信息就全部丟失了。

除了速度和空間之外,內(nèi)嵌擴(kuò)展還有個(gè)優(yōu)點(diǎn):潛在的代碼優(yōu)化。例如: Forth 序列:

3 +

在 68000 STC 被編譯成:

BSR LIT

.DW 3

BSR PLUS

但是,使用內(nèi)嵌代碼,就可以把它優(yōu)化成一個(gè)機(jī)器指令。

Forth 編譯器優(yōu)化是一個(gè)廣闊的領(lǐng)域,也是 Forth 語言研究中一個(gè)非常活躍的領(lǐng)域,這里不能完全討論,可參見 [SCO89] 和 [CUR93b] 。優(yōu)化 STC 的最終結(jié)果是能夠產(chǎn)生“純”機(jī)器代碼的 Forth 編譯器,就像 C 或者 Fortran 編譯器一樣。

標(biāo)記串線編碼( TTC )

DTC 和 STC 技術(shù)的目標(biāo)是用一定的存儲(chǔ)器消耗為代價(jià)來增加 Forth 程序的執(zhí)行速度?,F(xiàn)在讓我們轉(zhuǎn)向 ITC 的另一個(gè)方向:運(yùn)行速度更慢、但代碼尺寸更小。

Forth 串線的目的是指定一系列將要執(zhí)行的 Forth 字(子程序)的地址。假設(shè)一個(gè) 16 位的 Forth 字最大只有 256 個(gè) Forth 字,那么每個(gè) Forth 字都可以用一個(gè) 8 位數(shù)來標(biāo)識(shí),我們就可以不使用 16 位的地址列表,而是用一系列的 8 位標(biāo)識(shí)或者稱為“標(biāo)記( TOKEN )”來代替地址,這樣冒號(hào)定義的代碼尺寸就減少了一半。

在一個(gè)標(biāo)記串線編碼的 Forth 系統(tǒng)中,需要有一個(gè)記錄所有 Forth 字的表格,如圖 4 所示。標(biāo)記值就是這個(gè)表項(xiàng)的索引,通過它來尋找一個(gè)指定標(biāo)記對(duì)應(yīng)的 Forth 字。這種方法為 Forth 解釋器增加了一次間接訪問,所以它比“地址串線”的 Forth 執(zhí)行速度更慢。

圖 4 DTC Forth 定義的存儲(chǔ)

標(biāo)記串線的基本優(yōu)點(diǎn)是尺寸很小。 TTC 技術(shù)在手持計(jì)算機(jī)和其它對(duì)尺寸要求嚴(yán)格的應(yīng)用中極為常見。同時(shí),使用統(tǒng)一的 Forth 字“入口”表也簡(jiǎn)化了分開編譯模塊的鏈接。

TTC 的缺點(diǎn)是:速度慢。 TTC 的 Forth 系統(tǒng)速度是所有技術(shù)中最慢的, TTC 編譯器也比其它技術(shù)的編譯器更復(fù)雜一些。如果你的應(yīng)用有多于 255 個(gè) Forth 字定義,則還需要一些其它的編碼方式來混合 8 位和更大的標(biāo)記。

說到 TOKEN 串線,也許會(huì)想到的情況是 32 位的 Forth 系統(tǒng)通過 TOKEN 串線而使用 16 位的 Forth 代碼,不過,實(shí)際上又有多少 32 位系統(tǒng)是存儲(chǔ)器尺寸受限的呢?

段串線編碼

由于曾經(jīng)有許多的 Intel 8086 派生系統(tǒng),我們也簡(jiǎn)單地提一下段串線技術(shù)。這種技術(shù)不再使用一個(gè) 64K 段內(nèi)的“一般”字節(jié)地址,而是使用節(jié)地址(在 Intel 8086 中,一個(gè)節(jié)的大小是 16 個(gè)字節(jié))。這樣,解釋器可以把這些地址裝入段寄存器,而不是通常的地址寄存器。這就允許 16 位的 Forth 模型可以有效地訪問 8086 的 1M 字節(jié)存儲(chǔ)器。

段串線模型的基本缺點(diǎn)是 16 字節(jié)大小的存儲(chǔ)器“粒度”,因?yàn)檫@種技術(shù)要求每個(gè) Forth 字必須在 16 字節(jié)的邊界上對(duì)齊,而一個(gè) Forth 字又具有隨機(jī)的長(zhǎng)度,所以平均每個(gè)字要浪費(fèi) 8 個(gè)字節(jié)。

寄存器分配

在討論了各種串線技術(shù)之后, CPU 寄存器的分配和使用就是至關(guān)重要的設(shè)計(jì)考慮了。這可能也是最困難的。 CPU 寄存器的可用性又會(huì)反過來決定我們使用哪種串線技術(shù),甚至決定我們使用哪種方式的存儲(chǔ)器映射。

經(jīng)典的 Forth 寄存器

經(jīng)典的 Forth 虛擬機(jī)模型有 5 個(gè)“虛擬寄存器”。它們是 Forth 原語的抽象實(shí)體。 NEXT 、 ENTER 、 EXIT 就是用這些抽象寄存器定義的。

每個(gè)寄存器的寬度都是一個(gè)單元,也就是說,在 16 位 Forth 系統(tǒng)中,它們都是 16 位寄存器。(以后你會(huì)看到,也有一些特例)。它們不一定全部都是 CPU 寄存器,如果你的 CPU 沒有足夠的寄存器,其中一些可以保存在存儲(chǔ)器中。本文將按照這些寄存器的重要性來描述,也就是說,在沒有足夠 CPU 物理寄存器的情況下,最后描述的寄存器應(yīng)該最先考慮被放置到存儲(chǔ)器中。

W 是工作寄存器 它可以被用來做很多事情。首先, W 寄存器應(yīng)該是一個(gè)地址寄存器,應(yīng)該能用 W 寄存器作為地址來讀取和寫入存儲(chǔ)器;也需要用 W 寄存器做算術(shù)運(yùn)算。在 DTC Forth 中,還要求能用 W 實(shí)現(xiàn)間接跳轉(zhuǎn)。W 寄存器在每個(gè) Forth 字中被解釋器使用,如果 CPU 只有一個(gè)寄存器,那你也必須把這個(gè)唯一的寄存器用于W 寄存器 ,而把其它的寄存器放到存儲(chǔ)器中,當(dāng)然,這種實(shí)現(xiàn)會(huì)使整個(gè)系統(tǒng)慢得令人難以置信。

IP 是解釋指針 它被每個(gè) Forth 字使用(通過 NEXT 、 ENTER 、 EXIT )。 IP 必須是一個(gè)地址寄存器,你也需要增量 IP。子程序串線的 Forth 系統(tǒng)不需要這個(gè)寄存器。

PSP 是參數(shù)棧指針(或者叫數(shù)據(jù)棧指針) 有時(shí)也簡(jiǎn)稱作 SP 。我使用 PSP 是由于“SP”通常都是 CPU 硬件寄存器的名字,而它們彼此是不能混淆的。大多數(shù) CODE 字需要使用這個(gè)寄存器。 PSP 必須是一個(gè)堆棧指針,或者是能夠增量和減量的地址寄存器。如果可以通過 PSP 進(jìn)行索引尋址則會(huì)為系統(tǒng)帶來有一些附加的好處。

RSP 是返回棧指針 有時(shí)也簡(jiǎn)稱RP。在 ITC 和 DTC 的 Forth 系統(tǒng)中, RSP被冒號(hào)定義使用,在 STC 的 Forth 系統(tǒng)中,它被所有的字使用。 RSP 必須是一個(gè)堆棧指針,或者是能夠增量和減量的地址寄存器。

如果可能,應(yīng)該把 W 、 IP 、 PSP 、 RSP 都放到實(shí)際的 CPU 物理寄存器中,其它的虛擬寄存器可以保存在存儲(chǔ)器中,當(dāng)然,如果所有的寄存器都保持在 CPU 硬件寄存器中,將帶來速度方面的好處。

X 寄存器是一個(gè)工作寄存器 不過這里并沒有把它作為一個(gè)經(jīng)典的 Forth 寄存器考慮,甚至在使用它作為二次間接的經(jīng)典 ITC 實(shí)現(xiàn)中也沒有被當(dāng)做經(jīng)典寄存器。在 ITC 中,必須能夠使用 X 寄存器實(shí)現(xiàn)間接跳轉(zhuǎn)。 X 寄存器也被幾個(gè) CODE 字作為算術(shù)運(yùn)算操作的目的地址,在不能使用存儲(chǔ)器作為操作數(shù)的處理器上是特別重要的。比如在 Z80 上,需要通過下面的方式來實(shí)現(xiàn)加法運(yùn)算(用偽碼表示):

POP W

POP X

X+W -> W

PUSH W

有時(shí)也定義另外一個(gè)寄存器 Y 。

UP 是用戶指針 它保持當(dāng)前任務(wù)的用戶區(qū)基地址。 UP 通常的用法是加上一個(gè)偏移量后在高級(jí) Forth 定義中使用它。如果 CPU 可以通過 UP 寄存器索引尋址, CODE 字就可以更簡(jiǎn)單和更快速地訪問用戶變量。如果你有多余的寄存器,可以用其中一個(gè)作為 UP 。單任務(wù)的 Forth 不需要 UP 。

如果需要 X ,則 X 應(yīng)該優(yōu)先于 UP 放入 CPU 物理寄存器。 UP 是 Forth 虛擬寄存器中最適合放入存儲(chǔ)器的。

硬件堆棧的使用

許多 CPU 把堆棧指針作為硬件的一部分用于中斷和子程序調(diào)用。如果把堆棧指針作為 Forth 的一個(gè)虛擬寄存器將會(huì)怎么樣呢?它應(yīng)該是 PSP 還是 RSP 呢?

這要根據(jù)具體情況來考慮。一般認(rèn)為在 ITC 和 DTC 的 Forth 中, PSP 的使用比 RSP 更加頻繁,如果你的 CPU 只有不多的寄存器, PUSH 和 POP 就會(huì)比顯式地引用存儲(chǔ)器速度更快,所以我們可以使用硬件堆棧作為參數(shù)棧。

另一方面,如果你的 CPU 有豐富的尋址方式,特別是允許進(jìn)行索引尋址,就應(yīng)該為 PSP 分配一個(gè)通用的地址寄存器,在這種情況下,應(yīng)該使用硬件堆棧作為返回棧。

這里的結(jié)論對(duì)下面的情況不合適。比如在 TMS320C25 中,硬件堆棧的深度只有 8 個(gè)單元,這對(duì)于 Forth 系統(tǒng)來說基本上沒有什么用途,所以它的硬件堆棧只能用于中斷, PSP 和 RSP 都必須是通用的地址寄存器。注意 ANS Forth 規(guī)范中指定最小的參數(shù)棧是 32 個(gè)單元,返回棧是 24 個(gè)單元,而我選擇的數(shù)據(jù)棧和返回棧都是 64 個(gè)單元。

有時(shí)你可能會(huì)遇到教條的說法,比如硬件堆?!氨仨毷菂?shù)?!被蛘摺氨仨毷欠祷貤!?。在這種情況下,你可以編寫幾個(gè) Forth 原語比如: SWAP 、 OVER 、 @ 、 ! 、 + 、 0= 來看看哪種情況代碼更小、速度更快。

順便說一下,如果要做這種測(cè)試,字 DUP 和 DROP 價(jià)值不高。

偶爾你也會(huì)得到有趣的結(jié)論! Gary Bergstrom 指出在 MC6809 的 DTC 實(shí)現(xiàn)中,用 MC6809 的用戶堆棧指針作為 IP 可以快幾個(gè)周期,這里 NEXT 變成了 POP 。他使用索引指針作為 Forth 的堆棧指針。

把棧頂元素( TOS )放入寄存器

如果能把參數(shù)棧棧頂元素 TOS 放到寄存器中,則 Forth 的性能會(huì)得到明顯改善。許多 Forth 字(比如 0= )將不再訪問堆棧,其它的 Forth 字做同樣的 PUSH 和 POP ,只不過在代碼中的位置不同。只有不多的 Forth 字(比如 DROP 和 2DROP )變得比較復(fù)雜 -- 你必須同時(shí)更新 TOS 的內(nèi)容。

把棧頂元素放到寄存器中之后,編寫 CODE 字時(shí)需要遵循這樣幾個(gè)規(guī)則:

?? 一個(gè)字從堆棧上移出一個(gè)項(xiàng)目時(shí),必須彈出“新”的 TOS 到寄存器中;

?? 一個(gè)字加入一個(gè)新的項(xiàng)目到堆棧上,必須把“舊”的 TOS 壓入棧上 ( 當(dāng)然,除非它被消耗掉 )

如果你的 CPU 至少有 6 個(gè)物理寄存器,我建議你保存 TOS 到其中一個(gè)寄存器中。我認(rèn)為 TOS 比 UP 更重要,但它的重要性又次于 W 、 IP 、 PSP 、 和 RSP 寄存器。 TOS 寄存器執(zhí)行了許多 X 寄存器的功能,如果這個(gè)寄存器可以實(shí)現(xiàn)存儲(chǔ)器尋址就更加有用。 PDP-11 、 Z8 、 MC68000 處理器都是很好的例子。

Guy Kelly [KEL92] 研究了 19 個(gè) IBM PC 上的 Forth 系統(tǒng),其中有 9 個(gè)使用了 TOS 寄存器。

我認(rèn)為, TOS 的想法沒有廣泛被接受的原因首先是下面一些錯(cuò)誤的見解:

?? 增加了指令;

?? 棧頂元素必須通過存儲(chǔ)器訪問。

?? 過分強(qiáng)調(diào)了PICK、ROLL 這些價(jià)值不高的字,說它們?cè)?TOS 情況下必須進(jìn)行重新編碼。

如果把兩個(gè)棧頂元素都放到寄存器中,結(jié)果會(huì)怎么樣呢?當(dāng)你這樣做的時(shí)候,操作效率是相同的。一個(gè) PUSH 仍然是一個(gè) PUSH ,不論你在此前和以后進(jìn)行了什么操作。另一方面,緩沖兩個(gè)堆棧元素卻增加了大量的代碼:一個(gè) PUSH 現(xiàn)在變成了一個(gè) PUSH 后隨一個(gè) MOVE 。把兩個(gè)元素緩沖到寄存器中,只有在 RTX2000 一類的 Forth 芯片上才有意義,其它的都是一些假想的、聽起來似乎非常聰明、但在實(shí)際應(yīng)用中沒有什么意義的優(yōu)化。

實(shí)際分配的一些例子

這里是一些不同的 CPU 寄存器分配實(shí)例,通過這個(gè)表,我們可以看出每個(gè) Forth 系統(tǒng)作者的寄存器分配考慮。

[1] F83. [2] Pygmy Forth.

圖 5 寄存器分配

“SP”指硬件堆棧指針?!癦page”是指保存在 6502 存儲(chǔ)器零頁的值,零頁幾乎和寄存器一樣有用,有時(shí)比寄存器更有用。比如,它們可以被用于存儲(chǔ)器尋址?!癋ixed” 指 Payne's 8051 Forth 有一個(gè)單一的、不可移動(dòng)的用戶區(qū), UP 是硬編碼的常數(shù)。

寄存器變窄

我們?cè)谏厦娴谋砀裰凶⒁獾搅耸裁雌婀值氖虑閱?#xff1f; 6502 Forth 是一個(gè) 16 位的模型,但是卻使用了 8 位的棧指針。

在實(shí)際情況下,使 PSP 、 RSP 和 UP 的尺寸小于 Forth 的單元尺寸是可能的。這是因?yàn)槎褩:陀脩魠^(qū)相對(duì)于整個(gè) CPU 可尋址存儲(chǔ)器來說比較小。每個(gè)堆??梢孕〉?64 個(gè)單元,而用戶區(qū)很少超過 128 個(gè)單元。你只需要簡(jiǎn)單地相信:

?? 這些數(shù)據(jù)區(qū)被限制在存儲(chǔ)器的一個(gè)小的區(qū)域中,可以使用短的地址訪問;

?? 高地址位用其它的方式提供,比如,通過頁面選擇的方式來提供;

在 6502 CPU 中,硬件堆棧被 CPU 的設(shè)計(jì)者限定在 RAM 的一個(gè)頁中(地址為 0x1xx )。8 位堆棧指針可以用作返回棧。參數(shù)棧保存在 RAM 的零頁中,通過一個(gè) 8 位索引寄存器間接訪問。

在 8051 中,你可以使用 8 位的寄存器 R0 和 R2 訪問外部 RAM ,并顯式地提供地址的高 8 位輸出到 PORT 2 。這就允許對(duì)兩個(gè)堆棧進(jìn)行“頁選擇”。

UP 與 PSP 的 RSP 是有明顯區(qū)別的:它只是簡(jiǎn)單地提供一個(gè)基地址,從來都不增量和減量。所以,它實(shí)際上只是提供這個(gè)虛擬寄存器的高位。低位必須借助某種索引技術(shù)來實(shí)現(xiàn)。例如,在 MC6809 中,你可以使用 DP 寄存器作為 UP 的高 8 位,然后使用直接頁面尋址模式去訪問這個(gè)頁面中的 256 個(gè)位置。這就強(qiáng)制用戶區(qū)域從 0x??00 開始,同時(shí)限制用戶區(qū)域長(zhǎng)度為 128 個(gè)單元, 這些都不是什么大問題。而在 Intel 8086 上,你還可以使用一個(gè)段寄存器作為用戶區(qū)的基地址。

參考文獻(xiàn)

[CUR93a] Curley, Charles, "Life in the FastForth Lane," awaiting publication in Forth Dimensions. Description of a 68000 subroutine-threaded Forth.

[CUR93b] Curley, Charles, "Optimizing in a BSR/JSR Threaded Forth," awaiting publication in Forth Dimensions. Single-pass code optimization for FastForth, in only five screens of code! Includes listing.

[KEL92] Kelly, Guy M., "Forth Systems Comparisons," Forth Dimensions XIII:6 (Mar/Apr 1992). Also published in the 1991 FORML Conference Proceedings . Both available from the Forth Interest Group, P.O. Box 2154, Oakland, CA 94621. Illustrates design tradeoffs of many 8086 Forths with code fragments and benchmarks -- highly recommended!

[KOG82] Kogge, Peter M., "An Architectural Trail to Threaded- Code Systems," IEEE Computer, vol. 15 no. 3 (Mar 1982). Remains the definitive description of various threading techniques.

[ROD91] Rodriguez, B.J., "B.Y.O. Assembler," Part 1, The Computer Journal #52 (Sep/Oct 1991). General principles of writing Forth assemblers.

[ROD92] Rodriguez, B.J., "B.Y.O. Assembler," Part 2, The Computer Journal #54 (Jan/Feb 1992). A 6809 assembler in Forth.

[SCO89] Scott, Andrew, "An Extensible Optimizer for Compiling Forth," 1989 FORML Conference Proceedings , Forth Interest Group, P.O. Box 2154, Oakland, CA 94621. Good description of a 68000 optimizer; no code provided.

Forth 實(shí)現(xiàn)

[CUR86] Curley, Charles, real-Forth for the 68000 , privately distributed (1986).

[JAM80] James, John S., fig-Forth for the PDP-11 , Forth Interest Group (1980).

[KUN81] Kuntze, Robert E., MVP-Forth for the Apple II , Mountain View Press (1981).

[LAX84] Laxen, H. and Perry, M., F83 for the IBM PC , version 2.1.0 (1984). Distributed by the authors, available from the Forth Interest Group or GEnie.

[LOE81] Loeliger, R. G., Threaded Interpretive Languages , BYTE Publications (1981), ISBN 0-07-038360-X. May be the only book ever written on the subject of creating a Forth-like kernel (the example used is the Z80). Worth it if you can find a copy.

[MPE92] MicroProcessor Engineering Ltd., MPE Z8/Super8 PowerForth Target , MPE Ltd., 133 Hill Lane, Shirley, Southampton, S01 5AF, U.K. (June 1992). A commercial product.

[PAY90] Payne, William H., Embedded Controller FORTH for the 8051 Family , Academic Press (1990), ISBN 0-12-547570-5. This is a complete "kit" for a 8051 Forth, including a metacompiler for the IBM PC. Hardcopy only; files can be downloaded from GEnie. Not for the novice!

[SER90] Sergeant, Frank, Pygmy Forth for the IBM PC , version 1.3 (1990). Distributed by the author, available from the Forth Interest Group. Version 1.4 is now available on GEnie, and worth the extra effort to obtain.

[TAL80] Talbot, R. J., fig-Forth for the 6809 , Forth Interest Group (1980).

第二部分 內(nèi)核基準(zhǔn)測(cè)試和個(gè)案研究

基準(zhǔn)測(cè)試

我們已經(jīng)回答了每個(gè)與 Forth 實(shí)現(xiàn)決策有關(guān)的問題,現(xiàn)在應(yīng)該是“編碼并查看結(jié)果”的時(shí)候了。不過,你肯定不想僅僅為了測(cè)試不同的方法就編寫許多個(gè)完整的 Forth 內(nèi)核。幸運(yùn)的是,僅僅編寫 Forth 內(nèi)核的小子集就可以得到一些相當(dāng)好的“感覺”。

Guy Kelly [KEL92] 研究了 19 個(gè)不同的 IBM PC 的下列一些代碼樣例:

?? NEXT …… 是鏈接“串線”中一個(gè)字到另一個(gè)字的“內(nèi)層解釋器”。用于每一個(gè) CODE 定義的結(jié)尾,是決定 Forth 執(zhí)行速度的一個(gè)最重要的因素。你已經(jīng)看到了它的 ITC 和 DTC 偽碼;在 STC 中,它就是 CALL/RET 指令。

?? ENTER …… 也稱為 DOCOL 或者 DOCOLON ,高級(jí)“冒號(hào)”定義代碼域動(dòng)作。它對(duì)于速度也是至關(guān)重要的;用于每個(gè)冒號(hào)定義的開始,在 STC 中不需要。

?? EXIT …… 在 FIG-Forth 中稱為 S; 。結(jié)束一個(gè)冒號(hào)定義執(zhí)行的代碼。它在每個(gè)冒號(hào)定義的結(jié)束處出現(xiàn),決定高級(jí)子程序的返回效率。在 STC 中它就是一個(gè) RET 機(jī)器指令。

NEXT 、 ENTER 和 EXIT 表現(xiàn)了串線機(jī)制的性能。它們都應(yīng)該通過實(shí)際的編碼來評(píng)估實(shí)現(xiàn)性能。它們也反映了實(shí)現(xiàn)時(shí) IP 、 W 和 RSP 寄存器分配策略是否正確。

?? DOVAR …… “變量”,對(duì)于所有 Forth 變量 VARIABLE 的代碼域動(dòng)作。

?? DOCON …… “常量”,對(duì)于所有 Forth 常量 CONSTANT 的代碼域動(dòng)作。

DOCON 、 DOVAR 和 ENTER 一起顯示了你可以得到一個(gè)正在執(zhí)行的字的參數(shù)域地址的效率。這反映了你對(duì) W 寄存器的選擇,在 DTC Forth 中,也指出應(yīng)該在代碼域中放一個(gè) JUMP 指令還是一個(gè) CALL 指令。

?? LIT …… “文字量”。這個(gè)字從 Forth 的高級(jí)串線中取一個(gè)單元值。有幾個(gè)字需要使用這樣的內(nèi)嵌參數(shù),這很好地顯示了它們的性能。它反映了你對(duì) IP 寄存器的選擇。

?? @ …… Forth 的存儲(chǔ)器讀取操作,顯示了從高級(jí) Forth 中訪問存儲(chǔ)器可以有多快。這個(gè)字常常從堆棧的 TOS 中受益。

?? ! …… Forth 的存儲(chǔ)器存操作,從另一方面反映了存儲(chǔ)器訪問的能力。它消耗堆棧的兩個(gè)項(xiàng)目,所以能反映參數(shù)棧的訪問效率。它也很好地說明了我們把 TOS 放在存儲(chǔ)器還是放在寄存器中的決策。

?? + …… 加法操作,是所有 Forth 算術(shù)和邏輯操作的典型代表。

以上是一個(gè)非常好的代碼樣例。我還增加了幾個(gè)附屬的測(cè)試:

?? DODEOS …… 是用 DOES> 構(gòu)建字的代碼域動(dòng)作,盡管它沒有反映 W 、 IP 和 RSP 的使用。我包含這個(gè)字是因?yàn)樗?Forth 內(nèi)核中最費(fèi)解的代碼,如果你可以編碼 DODOES 的邏輯,其它的任何東西就都不在話下了。 DODOES 的復(fù)雜性將在本文的后面描述。

?? SWAP …… 是一個(gè)簡(jiǎn)單的堆棧操作符,但能說明問題。

?? ROT …… 是一個(gè)更加復(fù)雜的堆棧操作符。它為你能簡(jiǎn)單地訪問參數(shù)棧給出一個(gè)好主意。 ROT 好像需要一個(gè)外加的臨時(shí)寄存器才能完成。如果你能夠在不使用 X 寄存器的情況下實(shí)現(xiàn) ROT ,則其它情況下也不會(huì)需要 X 寄存器。

?? 0= …… 是不多的幾個(gè)單目算術(shù)操作之一,是最有可能從“TOS 在寄存器中 ” 獲益的字之一。

?? +! …… 是最多被說明的操作,組合了堆棧訪問、算術(shù)、存儲(chǔ)器取和存儲(chǔ)器存。這是一個(gè)非常理想的用于標(biāo)準(zhǔn)測(cè)試的字,盡管比上面所列出的其它字使用頻率低。

以上所列的都是最常用的 Forth 字,努力優(yōu)化它們是值得的。我將給出一個(gè) MC6809 的偽碼例子。對(duì)于其它的處理器,我將解釋特別選擇的代碼片段。

個(gè)案研究 1 : MC6809

在 8 位 CPU 世界中, MC6809 是 Forth 程序員的甜蜜之夢(mèng)。它支持 2 個(gè)堆棧!還有另外 2 個(gè)地址寄存器和大量的只有 PDP-11 才有的正交尋址方式。正交的意思是指所有的地址寄存器有相同的選項(xiàng)和相同的工作方式,而兩個(gè) 8 位累加器可以作為一個(gè)單一的 16 位累加器使用,并具有許多 16 位操作指令。

MC6809 的程序員模型是

A - 8 bit 累加器

B - 8 bit 累加器 大多數(shù)算術(shù)操作以累加作為目的寄存器。它們也可以連接在一起作為一個(gè) 16 位的累加器 D ( A 是高 8 位, B 是低 8 位)。

X - 16 位索引寄存器

Y - 16 位索寄存器

S - 16 位堆棧指針

U - 16 位堆棧指針 所有用于 X 和 Y 寄存器的尋址模式也可以用于 S 和 U 寄存器。

PC - 16 位程序計(jì)數(shù)器

CC - 8 位條件標(biāo)志寄存器

DP - 8 位直接頁訪問寄存器

MC6800 系列的直接尋址模式可以使用一個(gè) 8 位寄存器訪問零頁存儲(chǔ)器的任何位置。 MC6809 允許對(duì)任何頁進(jìn)行直接尋址。DP 寄存器提供高 8 位地址(頁地址)。

有 2 個(gè)堆棧指針可供 Forth 使用,它們是等效的,但 CPU 設(shè)計(jì)者把 S 用于子程序調(diào)用和中斷。為一致起見,我們把 S 作為返回棧, U 作為參數(shù)棧。

W 和 IP 都要求使用地址寄存器,它們邏輯上用于 X 和 Y 寄存器,我們可以任意指定:

X => W 而 Y => IP 。

現(xiàn)在來選擇一個(gè)串線模型。我簡(jiǎn)單地舍棄 STC 和 TTC ,構(gòu)造一個(gè)“傳統(tǒng)”的 Forth 。性能上的限制因素是 NEXT 子程序。讓我們先看看它的 ITC 和 DTC 實(shí)現(xiàn):

ITC-NEXT:

LDX ,Y ++ (8) (IP) -> W, 增量 IP

JMP [,X] (6) (W) -> temp, 跳轉(zhuǎn)到臨時(shí)單元的地址

DTC-NEXT:

JMP [,Y++] (9) (IP)->temp, 增量 IP, 跳轉(zhuǎn)到臨時(shí)單元地址,臨時(shí)單元在 MC6809 的內(nèi)部。

NEXT 在 DTC 的 MC6809 中只有一條指令!這就意味著你可以用 2 個(gè)字節(jié)的內(nèi)嵌編碼,比 JMP NEXT 又快又好。作為比較,子程序串線是這樣的:

RTS (5) ... 在 CODE 字的結(jié)尾

JSR nextword (8) ... 在串線中下一個(gè) CODE 字的開始

STC 花費(fèi) 13 個(gè)周期用于串線下的一個(gè)字,而 DTC 只需要 9 個(gè)周期。這是由于子程序串線需要將返回地址彈出和壓棧,而 CODE 字卻不需要。

決定了使用 DTC 之后,你還有兩個(gè)選擇:高級(jí)定義字在它的代碼域中使用 JMP 還是 CALL ?決定的因素是我們?nèi)绾文芨斓氐玫胶竺娴膮?shù)域地址。讓我們注意一個(gè)冒號(hào)定義的 ENTER 編碼:

如果使用 JSR (Call):

JSR ENTER (8)

...

ENTER:

PULS W (7) 得到 JSR 之后的地址到 W 中

PSHS IP (7) 保存舊的 IP 到返回棧

TFR W,IP (6) 參數(shù)域地址 -> IP

NEXT (9) JMP [,Y++] 的匯編語言智能

以上總計(jì) 37 個(gè)周期

如果使用 JMP:

JMP ENTER (4)

...

ENTER:

PSHS IP (7) 保舊的 IP 到返回棧上

LDX -2,IP (6) 重新得到代碼域地址

LEAY 3,X (5) 加 3 存入 IP ( Y )寄存器中

NEXT (9)

以上總計(jì) 31 個(gè)周期

因?yàn)?MC6809 的尋址模式允許另外一級(jí)的間接,所以 6809 的 NEXT 不使用 W 寄存器。 ENTER 的 JMP 版本必須再次讀取代碼域的地址 -- NEXT 沒有在任何寄存器中留下這個(gè)地址。 JSR 可以通過彈出返回棧直接得到參數(shù)域地址。所以, JMP 版本更快。

不論哪一種方式, EXIT 都是一樣的:

EXIT:

PULS IP 從返回棧中彈出“保存的”IP

NEXT 繼續(xù) Forth 解釋

有些寄存器尚未分配。你可以把用戶指針放到存儲(chǔ)器中,這樣的 Forth 也運(yùn)行得很好。不過 DP 寄存器就浪費(fèi)了,而 DP 也沒有什么其它的用處。讓我們使用一個(gè)“技巧”來實(shí)現(xiàn),我們把 UP 的高位搬到 DP 中(它的低字節(jié)是 0 )。

還有一個(gè)沒有使用的寄存器 D 寄存器,許多算術(shù)操作需要這個(gè)寄存器。它應(yīng)該自由地作為一個(gè)可隨意使用的寄存器呢?還是應(yīng)該作為棧頂元素呢? MC6809 使用存儲(chǔ)器作為一個(gè)操作數(shù),所以并不需要第二個(gè)工作寄存器。如果臨時(shí)需要寄存器,把 D 壓入和彈出也很容易。

所以我們只能對(duì)兩種方式編寫測(cè)試程序,看看哪個(gè)更快。

NEXT 、 ENTER 和 EXIT 不使用堆棧,在各種情況下的代碼都是一樣的。

DOVAR 、 DOCON 和 LIT 在兩種情況下所用的時(shí)鐘周期數(shù)相同。這就解釋了我們以前談到的把 TOS 放到寄存器中僅僅改變 PUSH 或者 POP 的位置:

SWAP 、 ROT 、 0= 、 @ 特別是 + 通過把 TOS 放到寄存器中而加快 :

但是, ! 和 +! 卻由于 TOS 放到寄存器中而變慢 :

這些字變慢的原因是許多訪問存儲(chǔ)器的 Forth 字希望地址在棧頂,所以需要一個(gè)額外的 FTR 指令。這就是為什么 TOS 寄存器必須是一個(gè)地址寄存器。不幸的是, MC6809 的地址寄存器都用于更重要的 W 、 IP 、 PSP 和 RSP 了。不過,把 TOS 放到寄存器中對(duì)于 ! 和 !+ 的損失可以通過許多算術(shù)和堆棧操作運(yùn)行速度的提高而得到彌補(bǔ)。

個(gè)案研究 2 : 8051

如果說 MC6809 是 Forth 系統(tǒng)實(shí)現(xiàn)者的美夢(mèng),那 Intel 8051 簡(jiǎn)直就是 Forth 實(shí)現(xiàn)者的惡夢(mèng)了。它只有一個(gè)通用的地址寄存器,一種尋址模式,總是使用一個(gè) 8 位累加器。

所有的算術(shù)操作、許多的邏輯操作都必須使用累加器。一個(gè)唯一的 16 位操作是 INC DPTR 。硬件堆棧必須使用 128 字節(jié)的片內(nèi)寄存器文件,這樣的 CPU 簡(jiǎn)直就是一堆破銅爛鐵!

有些 8051 Forth 實(shí)現(xiàn)了一個(gè) 16 位的 Forth ,但是它們太慢而不能滿足我們的要求。讓我們進(jìn)行某些權(quán)衡,以產(chǎn)生一個(gè)更快的 8051 Forth 系統(tǒng)。

我們最初的想法是利用那個(gè)唯一的地址寄存器。所以我們用 8051 的程序計(jì)數(shù)器作為 IP -- 也就是說,我們構(gòu)造一個(gè)子程序串線的 Forth 系統(tǒng)。如果編譯器在所有可能的情況下都使用 2 字節(jié)的 ACALL 代替 3 字節(jié)的 LCALL ,多數(shù)的 STC 代碼將和 ITC/STC 一樣小。

子程序串線意味著返回棧指針就是硬件堆棧指針。片上寄存器文件共有 64 個(gè)單元空間,但是這些空間并不足以支持多任務(wù)堆棧。面對(duì)這種情況下你可以考慮以下幾個(gè)策略:

?? 限制這個(gè) Forth 系統(tǒng)為單任務(wù)系統(tǒng);

?? 在所有的 Forth 定義入口處把返回地址保存到一個(gè)外部 RAM 軟件堆棧中;

?? 在任務(wù)切換的時(shí)候把全部返回棧的內(nèi)容保存到外部 RAM 中。

第二種方法是最慢的!在每個(gè)任務(wù)切換的時(shí)候移動(dòng) 128 個(gè)字節(jié)比在每個(gè) Forth 字中移動(dòng)兩個(gè)字節(jié)要快得多?,F(xiàn)在我選擇 1 ,而將選擇 3 留作以后擴(kuò)充。

唯一一個(gè)真正的地址寄存器 DPTR 將要擔(dān)負(fù)多種使命。它就是 W ,多用途的工作寄存器。

實(shí)際上,還有兩個(gè)寄存器可以尋址外部存儲(chǔ)器: R0 和 R1 。它們僅僅提供 8 位地址,高 8 位將顯式地輸出到口 2 上。但是對(duì)于堆棧,這是一個(gè)可以容忍的限制,因?yàn)槲覀兛梢园讯褩O拗圃?256 字節(jié)空間。所以我們使用 R0 作為 PSP 。

同樣的 256 字節(jié)可以用于用戶數(shù)據(jù)區(qū),這使得 P2 (口 2 )成為用戶指針的高字節(jié),像 MC6809 一樣,而低字節(jié)隱含是 0.

于是 8051 的程序員模型就變成了:

寄存器地址 8051 名字 Forth 使用

0 R0 PSP 的低字節(jié)

1 R1

2 R2

3 R3

4 R4

5 R5

6 R6

7 R7

8-7Fh 120 字節(jié)的返回棧

81h SP RSP 的低字節(jié)(高位字節(jié) = 0 )

82-83h DPTR W 寄存器

A0h P2 UP 和 PSP 的高字節(jié)

E0h A

F0h B

注意我們僅僅使用了 BANK0 , 另外的 3 個(gè)寄存器 BANK 從 08H 到 1FH , 從 20H 到 2FH 的位尋址寄存器都沒有被 Forth 使用。使用 BANK0 可以為返回棧得到最大的連續(xù)空間。如果需要,返回棧還可以縮小。

在子程序串線的 Forth 系統(tǒng)中,不需要 NEXT、ENTER 和 EXIT 。

如何處理?xiàng)m斣啬?#xff1f;在 8051 中,有許多的寄存器,而存儲(chǔ)器操作卻非常昂貴。我們把 TOS 放到 R3:R2 中(按 INTEL 格式,R3 是高字節(jié))。注意,我們不能使用 B:A 寄存器對(duì) -- A 寄存器是一個(gè)漏斗,所有的寄存器引用都要通過它進(jìn)行。

8051 采用了“哈佛”體系結(jié)構(gòu):程序和數(shù)據(jù)在分開的存儲(chǔ)器中存放。(Z8 和 TMS320 是哈佛體系結(jié)構(gòu)的另外兩個(gè)例子)。但 8051 使用的是一種“野蠻”的退化形式:軟件沒有辦法從物理上向程序存儲(chǔ)器寫,這就意味著 Forth 的開發(fā)者只能使用下述兩個(gè)方式:

?? 交叉編譯全部程序,包括應(yīng)用程序,放棄實(shí)現(xiàn)一個(gè) 8051 交互式 Forth 的努力;

?? 使一部分或者全部的程序存儲(chǔ)器在數(shù)據(jù)空間可見,最簡(jiǎn)單的辦法就是使這兩個(gè)空間完全覆蓋。

相比 Z8 和 TMS320 就比較文明,它們?cè)试S向程序存儲(chǔ)器寫入。Forth 內(nèi)核的具體實(shí)現(xiàn)將在以后討論。

個(gè)案研究 3 : Z80

選擇討論 Z80 是因?yàn)樗欠钦?CPU 的一個(gè)極端例子,它有 4 個(gè)不同種類的地址寄存器,有些操作使用寄存器 A 作為目的寄存器,有些則可以是任意的8位寄存器,有些是 HL 寄存器對(duì),有些則可以是任意的16位寄存器,等等。有些操作(比如 EX DE, HL )卻只允許一種寄存器組合。

在 Z80 這類的 CPU 中(或者同樣在 8086 中), Forth 功能的指定必須仔細(xì)匹配 CPU 寄存器的能力。許多方案需要評(píng)估,而唯一的辦法常常就是對(duì)不同的決策方案編寫各種代碼進(jìn)行測(cè)試。為了避免本文變成為一堆“代碼列表”,我選擇了基于許多 Z80 編碼經(jīng)驗(yàn)的一種寄存器指定,它說明了這些選擇可以合理地解釋早期討論的一般原理。

我希望得到一個(gè)傳統(tǒng)的 Forth ,盡管我使用了直接串線技術(shù)。我需要全部的“經(jīng)典”虛擬寄存器。

忽略其它的寄存器集, Z80 的6個(gè)地址寄存器具有下列能力:

?? BC,DE - LD A 間接 , INC, DEC 也交換 DE/HL

?? HL - LD r 間接 , ALU 間接 , INC, DEC, ADD, ADC, SBC, 交換 W/TOS, JP 間接

?? IX,IY - LD r 間接 , ALU 間接 , INC, DEC, ADD, ADC,SBC, 交換 W/TOS, JP 間接 ( 全都很慢 )

?? SP - PUSH/POP 16 位 , ADD/ADC/SUB to HL/IX/IY

BC, DE, 和 HL 也可以作為位寄存器對(duì)來處理。

8 位寄存器 A 必須留作臨時(shí)寄存器,因?yàn)樵S多 ALU 操作和存儲(chǔ)器引用操作都使用它作為目的。

HL 無疑是最通用的寄存器,可以逐個(gè)試著用它作為每個(gè)虛擬寄存器。然而,由于它的通用性 -- 它是唯一可以讀取字格式和支持間接跳轉(zhuǎn)的寄存器 -- HL 應(yīng)該作為 Forth 的通用工作寄存器 W 。

由于 IX 、 IY 都有索引尋址模式并可用 ALU 操作,所以可以考慮用它們作為堆棧指針 。但是它們有兩個(gè)主要的問題:通用的堆棧指針 SP 寄存器沒有用,而 IX/IY 卻特別慢!

在 Forth 的兩個(gè)棧上都有許多 16 位的 PUSH / POP 類操作,對(duì)于 SP 來說,這些操作只需要一條指令,而 IX 或者 IY 操作卻需要 4 條指令。所以兩個(gè)堆棧之一應(yīng)該用 SP 實(shí)現(xiàn),這應(yīng)該是參數(shù)棧,因?yàn)樗氖褂妙l率比返回棧要高。

如何考慮 Forth 的 IP 寄存器呢?在大多數(shù)情況下, IP 都是從存儲(chǔ)器讀取并且自動(dòng)增量的,使用 IX/IY 作為 IP 不會(huì)比使用 BC/DE 有任何編程上的好處,考慮 IP 的速度,使用 BC/DE 對(duì)卻更快。讓我們把 IP 放到 DE 中:它可以與 HL 的內(nèi)容交換,而后者是通用的。

需要第二個(gè) Z80 寄存器對(duì)(不是 W )進(jìn)行 16 位的算術(shù)運(yùn)算。現(xiàn)在只有 BC 了,它可以用于尋址或者與 A 進(jìn)行 ALU 操作。但是,我們是用 BC 作為第二個(gè)工作寄存器“X”、還是作為棧頂元素呢?只有編碼才能得到結(jié)論。現(xiàn)在,讓我們樂觀地假定 BC = TOS 。

只剩下 RSP 和 UP 了,還有 IX 和 IY 寄存器沒有分配。 IX 和 IY 是等效的,我們?cè)O(shè) IX = RSP , IY = UP 。

于是, Z80 Forth 系統(tǒng)的寄存器分配如下

BC = TOS IX = RSP

DE = IP IY = UP

HL = W SP = PSP

現(xiàn)在讓我們看看 DTC 的 Forth 系統(tǒng) NEXT 編碼:

DTC-NEXT:

LD A,(DE) (7) (IP)->W, 增量 IP

LD L,A (4)

INC DE (6)

LD A,(DE) (7)

LD H,A (4)

INC DE (6)

JP (HL) (4) 跳轉(zhuǎn)到 W 中的地址

還可以有其它的版本(具有同樣的時(shí)鐘周期)

DTC-NEXT:

EX DE,HL (4) (IP)->W, 增量 IP

NEXT-HL:

LD E,(HL) (7)

INC HL (6)

LD D,(HL) (7)

INC HL (6)

EX DE,HL (4)

JP (HL) (4) 轉(zhuǎn)到 W 中的地址

注意單元是以低位字節(jié)優(yōu)先的方式存放在存儲(chǔ)器中的。同樣,盡管看起來把 IP 保存在 HL 寄存器中有許多好處,但實(shí)際上卻沒有。這是由于 Z80 不能進(jìn)行 JP (DE) 。 NEXT-HL 進(jìn)入將更短一些。

僅僅用于比較,讓我們看一下 ITC NEXT 。以前給出的偽代碼需要另一個(gè)臨時(shí)寄存器“X”,它的內(nèi)容用于間接跳轉(zhuǎn)。令 DE = X, BC = IP, TOS 保存在存儲(chǔ)器中。

ITC-NEXT:

LD A,(BC) (7) (IP)->W, 增量 IP

LD L,A (4)

INC BC (6)

LD A,(BC) (7)

LD H,A (4)

INC BC (6)

LD E,(HL) (7) (W)->X

INC HL (6)

LD D,(HL) (7)

EX DE,HL (4) 跳轉(zhuǎn)到 X 中的地址

JP (HL) (4)

這就把“W”加 1 并放到在 DE 寄存器中了。只要這是一致的,就不會(huì)有任何問題 -- 代碼知道在需要 W 的內(nèi)容時(shí)如何去找到它,以及如何調(diào)整它。

ITC 的 NEXT 是 11 個(gè)同期, DTC 是 7 個(gè)同期。 ITC 沒有將 TOS 保存在寄存器中的能力,所以我選擇 DTC 。

如果使用內(nèi)嵌編碼, DTC NEXT 在每個(gè) CODE 字中需要 7 個(gè)字節(jié)。一個(gè)直接跳轉(zhuǎn)到 NEXT 的子程序只需要 3 個(gè)字節(jié),但需要附加 10 個(gè)時(shí)鐘周期,這是一個(gè)特別的例子,我們選擇的是內(nèi)嵌方式的 NEXT 。但有時(shí) NEXT 特別大,或者存儲(chǔ)器很小,更謹(jǐn)慎的決策可能是使用 JMP 到 NEXT 。

現(xiàn)在讓我們來看 ENTER 的代碼。使用一個(gè) CALL ,可以彈出硬件堆棧以得到參數(shù)域地址:

CALL ENTER (17)

...

ENTER:

DEC IX (10) 把老的 IP 放到返回棧上

LD (IX+0),D (19)

DEC IX (10)

LD (IX+0),E (19)

POP DE (10) 參數(shù)域地址 -> IP

NEXT (38) 7 個(gè)機(jī)器指令的匯編語言宏

實(shí)際上這比 POP HL 快,然而使用最后的 6 個(gè)指令(不用 EXDE , HL ):

CALL ENTER (17)

...

ENTER:

DEC IX (10) 把老的 IP 放到返回棧上

LD (IX+0),D (19)

DEC IX (10)

LD (IX+0),E (19)

POP HL (10) 參數(shù)域地址 -> HL

NEXT-HL (34) 看上面的 DTC 的 NEXT 代碼

總計(jì) 119 個(gè)周期

當(dāng)使用 JP 時(shí), W 寄存器( HL )依然指向代碼域。代碼域是其后的 3 個(gè)字節(jié):

JP ENTER (10)

...

ENTER:

DEC IX (10) 把老的 IP 放到返回棧上 LD (IX+0),D (19)

DEC IX (10)

LD (IX+0),E (19)

INC HL ( 6) 參數(shù)域地址 -> IP

INC HL ( 6)

INC HL ( 6)

NEXT-HL (34)

總計(jì) 120 個(gè)周期

由于改變了 NEXT 的入口, IP 的新值就不必放入 DE 寄存器對(duì)了。

CALL 版本快了 1 個(gè)周期。在嵌入式系統(tǒng)應(yīng)用 Z80 時(shí),我們還可以使用單字節(jié)的 RST 指令來得到速度和空間的雙重收益,但是在基于 Z80 的個(gè)人計(jì)算機(jī)上,這個(gè)策略并不可用(操作系統(tǒng)使用了這個(gè)特性,即操作系統(tǒng)的系統(tǒng)調(diào)用是通過這個(gè)接口進(jìn)入的)。

個(gè)案研究 4 : INTEL 8086

Intel 的 8086 是另一個(gè)有教育意義的 CPU 。我們不再詳細(xì)討論設(shè)計(jì)過程,只是看一個(gè)新的用于 PC 的共享軟件: Pygmy Forth [SER90].

Pygmy 是一個(gè)直接串線的 Forth 系統(tǒng),棧頂元素保存在寄存器中。 8086 寄存器是這樣安排的:

AX = W DI = scratch

BX = TOS SI = IP

CX = scratch BP = RSP

DX = scratch SP = PSP

許多 8086 Forth 系統(tǒng)的實(shí)現(xiàn)使用 SI 寄存器作為 IP ,所以 NEXT 可以通過 LODSW 指令實(shí)現(xiàn)。在 Pygmy 的 DTC 實(shí)現(xiàn)中, NEXT 是這樣的:

NEXT:

LODSW

JMP AX

這已經(jīng)小得足以嵌入到每個(gè) CODE 字中了 。

高級(jí)“定義” Forth 字使用一個(gè) JMP (相對(duì))指令轉(zhuǎn)向它們的機(jī)器碼。 ENTER 子程序(在 Pygmy 中稱為 'docol' )因此需要從 W 中得到參數(shù)域地址。

ENTER:

XCHG SP,BP

PUSH SI

XCHG SP,BP

ADD AX,3 參數(shù)域地址 -> IP

MOV SI,AX

NEXT

注意交換兩個(gè)堆棧指針的 XCHG 用法,這允許對(duì)兩個(gè)堆棧都使用 PUSH 和 POP 指令,這比使用基于 BP 的直接尋址指令要快。

EXIT:

XCHG SP,BP

POP SI

XCHG SP,BP

NEXT

段模型

Pygmy Forth 是一個(gè)單段的 Forth 系統(tǒng),所有的代碼和數(shù)據(jù)都在一個(gè) 64K 字節(jié)的段中, 這相當(dāng)于 Turbo C 的緊縮模式。到目前為止,我們討論的 Forth 標(biāo)準(zhǔn)都假設(shè)所有的東西全部包含在單一的存儲(chǔ)器地址空間,使用同樣的讀寫操作符。然而, IMP PC Forth 開始使用多個(gè)段來處理 5 種不同的數(shù)據(jù),它們是:

CODE …… 機(jī)器代碼

LIST …… 高級(jí) Forth 串線 ( 所以這個(gè)段也稱為 THREADS)

HEAD …… Forth 字的首部

STACK …… 參數(shù)和返回棧

DATA …… 變量和用戶定義數(shù)據(jù)

這就允許 PC 機(jī)上的 Forth 突破 64K 字節(jié)的段限制,而又不需要在一個(gè) 16 位的 CPU 上實(shí)現(xiàn)一個(gè) 32 位的 Forth 系統(tǒng)。但是,實(shí)現(xiàn)一個(gè)多段的模型、分支到 Forth 核心等等內(nèi)容已經(jīng)遠(yuǎn)遠(yuǎn)超出了本文的討論范圍。

參考文獻(xiàn)

[KEL92] Kelly, Guy M., "Forth Systems Comparisons," Forth Dimensions XIII:6 (Mar/Apr 1992). Also published in the 1991 FORML Conference Proceedings . Both available from the Forth Interest Group, P.O. Box 2154, Oakland, CA 94621. Illustrates design tradeoffs of many 8086 Forths with code fragments and benchmarks -- highly recommended!

[MOT83] Motorola Inc., 8-Bit Microprocessor and Peripheral Data , Motorola data book (1983).

[SIG92] Signetics Inc., 80C51-Based 8-Bit Microcontrollers , Signetics data book (1992).

Forth 實(shí)現(xiàn)

[PAY90] Payne, William H., Embedded Controller FORTH for the 8051 Family , Academic Press (1990), ISBN 0-12-547570-5. This is a complete "kit" for a 8051 Forth, including a metacompiler for the IBM PC. Hardcopy only; files can be downloaded from GEnie. Not for the novice!

[SER90] Sergeant, Frank, Pygmy Forth for the IBM PC , version 1.3 (1990). Distributed by the author, available from the Forth Interest Group. Version 1.4 is now available on GEnie, and worth the extra effort to obtain.

[SEY89] Seywerd, H., Elehew, W. R., and Caven, P., LOVE-83Forth for the IBM PC , version 1.20 (1989). A shareware Forth using a five-segment model. Contact Seywerd Associates, 265 Scarboro Cres., Scarborough, Ontario M1M 2J7 Canada.

第三部分 解密 DOES>

更正

上一部分的 MC6809 設(shè)計(jì)決策中存在一個(gè)很大的錯(cuò)誤,在我編碼 Forth 字 EXECUTE 的時(shí)候,它變得非常明顯。

EXECUTE 引起一個(gè) Forth 字的執(zhí)行,它的地址在參數(shù)棧上。更精確地說:編譯地址、或者說代碼域地址在參數(shù)棧上給出。這可以是任何類型的 Forth 字: CODE 定義、冒號(hào)定義、 CONSTANT 、 VARIBLE 或者是定義字。與通常的 Forth 解釋過程不同的是,執(zhí)行字的地址在棧上給出,而不是通過“串線”給出(通過 IP 指定)。

在我們的直接串線 MC6809 中,這可以很容易地編碼 :

EXECUTE:

TFR TOS,W 把字的地址放到 W 中

PULU TOS 彈出新的 TOS

JMP ,W 跳到 W 給定的地址

注意:應(yīng)該是 JMP ,W 而不是 JMP [,W], 因?yàn)槲覀円呀?jīng)有了這個(gè)字的代碼地址,不是從高級(jí)線程中讀取的。如果 TOS 不在寄存器中, EXECUTE 可以更簡(jiǎn)單地實(shí)現(xiàn) JMP [,PSP++] ?,F(xiàn)在假設(shè)這個(gè)被執(zhí)行的字是一個(gè)冒號(hào)定義, W 將要指向它的代碼域,其中包含有 JMP ENTER 。 如下所示:

JMP ENTER

...

ENTER:

PSHS IP

LDX -2,IP 重新取得代碼域地址

LEAY 3,X

NEXT

這就是錯(cuò)誤所在!因?yàn)槲覀儾皇菑拇€中執(zhí)行這個(gè)字,所以 IP 并沒有指向代碼域地址的一個(gè)拷貝。記住: EXECUTE 字的地址來自于堆棧。這種方式的 ENTER 不能與 EXECUTE 一同工作,因?yàn)闆]有辦法得到將要執(zhí)行的字的地址。

這也同時(shí)提出了 DTC Forth 的一個(gè)新規(guī)則:如果 NEXT 沒有把將要執(zhí)行的字的地址放到一個(gè)寄存器中,你就必須在代碼域中使用 CALL 。

于是, MC6809 Forth 只好倒退回在代碼域中使用 JSR 的方法。但是, ENTER 是 Forth 中使用最多的代碼片斷,為了避免速度的損失,我完成了上一章中的“學(xué)生練習(xí)”。注意當(dāng)你交換 RSP 和 PSP 時(shí)發(fā)生了什么:

執(zhí)行新版本需要 31 個(gè)周期,這與我前面使用的 JMP 版本的時(shí)間一樣。其中的改進(jìn)是由于 JSR 版本的 ENTER 同時(shí)使用 Forth 的返回棧和 MC6809 子程序返回棧( JSR 棧)。使用兩個(gè)不同的堆棧指針意味著我們不必與IP“交換” TOS ,也就不需要任何的臨時(shí)寄存器了。

這也解釋了一個(gè)新 Forth 內(nèi)核通常的開發(fā)過程:先做出一些設(shè)計(jì)決策,然后寫出一些簡(jiǎn)單的代碼,再找出一個(gè) BUG 或者一個(gè)更好的方法做這件事情,改變某些設(shè)計(jì)策略,重新編寫示例代碼,重復(fù)這個(gè)過程直到滿意為止。

這給了我們一個(gè)教訓(xùn):把 EXECUTE 做為一個(gè)基準(zhǔn)測(cè)試字。

Carey Bloodworth of Van Buren, AR 指出了上一版本 MC6809 中的一個(gè)小的、但是讓我不好意思的錯(cuò)誤:

對(duì)于 0= 的“ TOS 在存儲(chǔ)器”版本,我應(yīng)該這樣編寫代碼:

LDD ,PSP

CMPD #0

這是為了測(cè)試 TOS 是否為 0 ??墒窃谶@種情況下, CMPD 指令完全是多余的,因?yàn)?LDD 指令在 D 寄存器為 0 時(shí)將設(shè)置 Zero 標(biāo)志。 TOS 在 D 寄存器的版本還是需要 CMPD 指令的,但是比 TOS 在存儲(chǔ)器版本執(zhí)行速度更快。

現(xiàn)在讓我們開始討論主題

什么是代碼域?

DOES 的概念看起來是 Forth 中最難懂和最神秘的一部分,不過 DOES 也是使 Forth 具有強(qiáng)大能力的一個(gè)原因 -- 在許多方面,它是先天面向?qū)ο蟮摹?DOES 的行為和能力也與 Forth 最閃亮的方面有著聯(lián)系:代碼域。

回憶第一部分, Forth 的定義體由兩個(gè)部分組成:代碼域和參數(shù)域。你可以從不同的方面來考察這兩個(gè)域:

?? 代碼域是這個(gè) Forth 字的動(dòng)作,參數(shù)域是與動(dòng)作有關(guān)的數(shù)據(jù);

?? 代碼域是一個(gè)子程序調(diào)用,參數(shù)域是調(diào)用后面的“內(nèi)嵌”參數(shù)(匯編程序員觀點(diǎn));

?? 代碼域是字類的單個(gè)“方法”,參數(shù)域是某個(gè)特別字的“實(shí)例變量”(面向?qū)ο蟪绦騿T的觀點(diǎn));

所有這些觀點(diǎn)都有著共同點(diǎn):

?? 代碼域子程序在調(diào)用時(shí)至少有一個(gè)參數(shù),它就是這個(gè)要執(zhí)行的 Forth 字的參數(shù)域地址,參數(shù)域可以包含有任何數(shù)目的參數(shù) ;

?? 只有幾個(gè)相對(duì)不多的特殊動(dòng)作,或者說,代碼域只引用為數(shù)不多的幾個(gè)特殊子程序(我們后面將會(huì)看到,這對(duì)于 CODE 例外)。我們可以回憶一下第 2 部分的 ENTER 子程序:這個(gè)通用的子程序被所有的 Forth 冒號(hào)定義引用 ;

?? 對(duì)參數(shù)域的解釋隱含地由代碼域的內(nèi)容去解釋?;蛘哒f,每個(gè)代碼域子程序希望參數(shù)域包含一定類型的數(shù)據(jù) ;

一個(gè)典型的 Forth 內(nèi)核有以下幾個(gè)預(yù)定義的代碼域子程序 .

Forth 之所以強(qiáng)大的原因在于 Forth 程序并不限于只能使用這些代碼域子程序(或者只能使用你的 Forth 系統(tǒng)內(nèi)核所提供的其它子程序集)。程序員可以定義新的代碼域子程序,可以定義一個(gè)新的參數(shù)域類型與之匹配。用面向?qū)ο蟪绦蛟O(shè)計(jì)方法的“行話”來說,可以創(chuàng)建新的“類”和“方法”(盡管每個(gè)類只有一個(gè)方法)。同時(shí),就像其它的 Forth 字一樣 -- 代碼域可以用匯編語言定義,也可以用高級(jí) Forth 字來定義。

為了理解代碼域的機(jī)制和參數(shù)是如何傳遞的,我們首先看看匯編語言(機(jī)器代碼)的情況。我們先考察間接串線(ITC)的情況,它是最容易理解的,然后再看看如何修改這些邏輯到直接串線(DTC)和子程序串線的(STC)上。最后,再看如何使用高級(jí) Forth 定義來描述代碼域的動(dòng)作。

Forth 的編寫者在使用術(shù)語時(shí)有些混亂,所以,我使用我自己的術(shù)語來解釋,如圖 1 所示。首部包含有字典信息,與一個(gè) Forth 字的執(zhí)行沒有關(guān)系。體是這個(gè)字的“工作”部分,包含有固定長(zhǎng)度的代碼域和可變長(zhǎng)度的參數(shù)域。對(duì)于任何一個(gè)給定的字,這兩個(gè)域在存儲(chǔ)器中的位置分別被稱為代碼域地址(CFA)和參數(shù)域地址(PFA)。一個(gè)字的代碼域地址就是這個(gè)字在存儲(chǔ)器中的位置。不要把這個(gè)與代碼域的內(nèi)容相混淆,在 ITC 中,內(nèi)容是另一個(gè)不同的地址。

需要明確的是:代碼域的內(nèi)容是另外一片存儲(chǔ)器的地址,在那一片存儲(chǔ)器中是機(jī)器代碼。我把這個(gè)地址稱為代碼地址。最后,當(dāng)討論 DTC 和 STC Forth 時(shí),我也引用“代碼域內(nèi)容”,它的含義比代碼域地址更多。

圖 1 一個(gè) ITC Forth 字

機(jī)器代碼動(dòng)作

Forth 的 CONSTANT 可能是最簡(jiǎn)單的機(jī)器代碼例子。讓我們考察一個(gè)法語的例子:

1 CONSTANT UN

2 CONSTANT DEUX

3 CONSTANT TROIS

執(zhí)行 UN 會(huì)把值 1 壓入堆棧,執(zhí)行 DEUX 把 2 壓入堆棧等等。(不要把參數(shù)棧和參數(shù)域混淆,它們是完全獨(dú)立的)

在 Forth 內(nèi)核中有一個(gè)字稱為 CONSTANT 。這并不是一個(gè)常數(shù)類的字本身,它是一個(gè)高級(jí) Forth 定義。 CONSTANT 是一個(gè)“定義字”:它在 Forth 字典中創(chuàng)建一個(gè)新字,通過它我們能夠創(chuàng)建新的“常數(shù)類”字 UN 、 DEUX 和 TROIS 。你也可以把它們理解成常數(shù)“類”的一個(gè)個(gè)“實(shí)例”。這三個(gè)字都有自己的代碼域,都指向同樣的 COSNTANT 動(dòng)作的機(jī)器代碼片斷。

這個(gè)代碼片斷應(yīng)該執(zhí)行什么動(dòng)作呢?圖 2 給出了這三個(gè)常數(shù)的存儲(chǔ)器表示。所有這三個(gè)字都指向共同的動(dòng)作子程序。這些字的區(qū)別在于它們的參數(shù)域,這里簡(jiǎn)單地包含有常數(shù)的值,或者用面向?qū)ο蟮恼f法是“實(shí)例變量”。所以,這三個(gè)字的動(dòng)作都應(yīng)該是讀取參數(shù)域的內(nèi)容,并把它們放到棧頂。這段代碼也隱含地知道參數(shù)域包含一個(gè)單元大小的值。

圖 2 三個(gè)常數(shù)

為了寫出做這件事情的機(jī)器代碼片斷,我們需要知道怎樣才能找到參數(shù)域的地址,之后 Forth 的解釋器就可以跳轉(zhuǎn)到機(jī)器代碼。那么,PFA 是如何傳遞給機(jī)器代碼子程序的呢?并且, Forth 解釋器的 NEXT 是如何編碼的呢?這依賴于不同的實(shí)現(xiàn)。為了寫出機(jī)器代碼動(dòng)作,我們首先需要理解 NEXT 。

ITC 的 NEXT 在第一部分已經(jīng)用偽碼描述了,以下是 MC6809 的實(shí)現(xiàn),使用 Y=IP,X=W:

NEXT: LDX ,Y++ ; (IP) -> W, IP+2 -> IP

JMP [,X] ; (W) -> temp, JMP (temp)

假設(shè)我們的高級(jí)串線中有這樣的代碼:

... SWAP DEUX + ...

當(dāng) NEXT 被執(zhí)行時(shí),使用 IP 解釋指針指向 DEUX “指令”(緊接在 SWAP 之后),圖 3 解釋了發(fā)生的事情。 IP (寄存器 Y )指向高級(jí)串線內(nèi)部的一個(gè)存儲(chǔ)器單元,它包含有 Forth 字 DEUX 的地址。更精確地說,這個(gè)單元包含有字 DEUX 的代碼域地址。于是,當(dāng)我們使用 Y 讀取一個(gè)單元時(shí),自動(dòng)增量 Y ,我們就得到了 DEUX 的代碼域地址。把它寫入 W (寄存器 X ), W 現(xiàn)在已經(jīng)指向了代碼域,是一個(gè)機(jī)器代碼片斷的地址。我們可以讀取這個(gè)單元的內(nèi)容,然后使用一條MC6809 指令跳轉(zhuǎn)到相應(yīng)的機(jī)器代碼處執(zhí)行。這個(gè)過程并沒有改變寄存器 X ,所以 W 仍然指向 DEUX 的 CFA ,我們就可以得到參數(shù)域地址,它在代碼域之后兩個(gè)字節(jié)的位置。

圖 3 ITC 在 NEXT 之前和之后的情況

所以,機(jī)器代碼片斷只需要把 W 加 2 ,讀取這個(gè)地址的單元內(nèi)容,把它壓到棧上。這個(gè)代碼片斷通常被稱為 DOCON

DOCON:

LDD 2,X ; 讀取 W+2 處的單元

PSHU D ; 把它放到參數(shù)棧是

NEXT ; ( 宏 ) 跳轉(zhuǎn)到下一個(gè)高級(jí)字

這個(gè)例子中, TOS 在存儲(chǔ)器中。注意前面的 NEXT 已經(jīng)把 IP 增加了 2 ,所以當(dāng) DOCON 做 NEXT 時(shí),它已經(jīng)指向了串線的下一個(gè)單元(“+” 的 CFA )。

通常, ITC Forth 會(huì)在 W 寄存器中留下參數(shù)域地址或者一些“鄰近”的地址。在這種情況下, W 包含有 CFA ,它在這個(gè) Forth 實(shí)現(xiàn)中總是 PFA - 2 。由于除了 CODE 之外的每類 Forth 字都需要使用參數(shù)域地址,許多 NEXT 實(shí)現(xiàn)方法都是增量 W 使它指向 PFA 。我們可以在 MC6809 上做一些小的改變:

NEXT:

LDX ,Y++ ; (IP) -> W, IP + 2 -> IP

JMP [,X++] ; (W) -> temp, JMP (temp), W+2 -> W

這使 NEXT 增加了 3 個(gè)周期,但是把參數(shù)域地址放入了 W 寄存器。對(duì)于代碼域子程序它做了些什么呢?

W=CFA W=PFA

DOCON:

LDD 2,X (6) LDD ,X (5)

PSHU D PSHU D

NEXT NEXT

DOVAR:

LEAX 2,X (5) ; 沒有操作

PSHU X PSHU X

NEXT NEXT

ENTER:

PSHS Y PSHS Y

LEAY 2,X (5) LEAY ,X (4, 比 TFR X,Y 快 )

NEXT NEXT

從 NEXT 增加 3 個(gè)周期的代價(jià)中我們得到了什么收益呢? DOCON 減少了 1 個(gè)周期, DOVAR 減少了 5 個(gè)周期, ENTER 減少了 1 個(gè)周期。 CODE 字不使用 W 中的值,所以它們沒有從自動(dòng)增量中受益。速度的增加或者損失要通過 Forth 字的混合執(zhí)行來考察。通常的規(guī)則是執(zhí)行最多的字是 CODE 字,這樣,在 NEXT 中增量 W 會(huì)有一點(diǎn)點(diǎn)速度上的損失 -- 當(dāng)然也節(jié)省了存儲(chǔ)器 -- 不過 DOCON , DOVAR 和 ENTER 只出現(xiàn)一次,得到的收益并不明顯。

說來說去,最好的結(jié)論還是依賴于具體的處理器。比如像 Z80 這樣的處理器只能通過字節(jié)訪問存儲(chǔ)器,它沒有自動(dòng)增量指令,所以通常的情況下,最好是保留 W 指向 IP+1 (從代碼域讀取的最后一個(gè)字節(jié))。而在有些機(jī)器上,自動(dòng)增量是“免費(fèi)的”,這時(shí)讓 W 指向參數(shù)域就是最方便的。

注意:在一個(gè)系統(tǒng)中決策必須一致。如果 NEXT 讓 W 在執(zhí)行時(shí)指向 PFA ,則 EXECUTE 也必須這樣做(這就是為什么我在本文的開頭拼命更正的原因)。

直接串線

直接串線和間接串線差不多,除了代碼域的內(nèi)容:它不再是一些機(jī)器代碼的地址,而是 JUMP 或者 CALL 。這樣做可能會(huì)使得代碼域更大 -- 比如在 MC6809 上要大 1 個(gè)字節(jié),但是,它省去了 NEXT 子程序中的一級(jí)間接。

在代碼域中選擇 JUMP 還是 CALL 指令依賴于機(jī)器碼子程序如何得到參數(shù)域地址。為了跳轉(zhuǎn)到代碼域,許多 CPU 要求把它的地址放在一個(gè)寄存器中。例如, Intel 8086 的間接跳轉(zhuǎn)指令是 JMP AX (或者其它的寄存器),在 Z80 上是 JP ( HL 或者 IX 或者 IY)。在這些處理器上, DTC 的 NEXT 包括兩個(gè)操作,在 MC6809 上將變成:

NEXT:

LDX ,Y++ ; (IP) -> W, IP + 2 -> IP

JMP ,X ; JMP (W)

在 Intel 8086 上,這兩條指令可以是 LODSW 和 JMP AX ,其中的影響可以通過圖 4 的 CASE1 說明。 DEUX 的代碼域地址是從高級(jí)串線中讀取的, IP 被增量。然后,不再進(jìn)行讀取操作,而是用一個(gè) JUMP 指令跳轉(zhuǎn)到代碼域。也就是說, CPU 直接跳轉(zhuǎn)到代碼域。 CFA 被留在 W 寄存器中,就像上面 ITC 的第一個(gè)例子。由于這個(gè)地址已經(jīng)在寄存器中了,我們可以簡(jiǎn)單地把 JUMP 放到 DOCON 的代碼域中, DOCON 的代碼片斷將和上面描述一樣地工作。

圖 4 DTC 中 NEXT 之前和之后的情況

不過,我們也許會(huì)注意到:在有些處理器上,比如 MC6809 和 PDP-11上,可以用一個(gè)指令來實(shí)現(xiàn)這個(gè) DTC NEXT

NEXT:

JMP [,Y++] ; (IP) -> temp, IP+2 -> IP, JMP (temp)

這也能使 CPU 跳轉(zhuǎn)到 DEUX 的代碼域。但其中有一個(gè)巨大的差異:任何寄存器中都沒有留下 CFA !那么機(jī)器代碼片斷如何得到參數(shù)域的地址呢?答案是:通過使用 CALL (或者 JSR )指令來替代 JUMP 。在許多 CPU 上, CALL 指令會(huì)把返回地址放到返回棧上 -- 這就是緊隨在 CALL 指令之后的地址 。

如圖 4 所示的 CASE2 ,這個(gè)地址就是我們所需要的參數(shù)域地址!所以 DOCON 要做的就是從返回棧得到地址 -- 滿足代碼域放置 JSR 的要求 -- 然后使用這個(gè)地址來讀取常量,于是:

DOCON:

PULS X ; 從返回棧彈出 PFA

LDD ,X ; 讀取參數(shù)域的單元

PSHU D ; 壓入?yún)?shù)棧

NEXT ; ( 宏 ) 轉(zhuǎn)到下一個(gè)高級(jí)字

把這個(gè)同 ITC 版本相比較。 DOCON 多了 1 個(gè)指令,但是 NEXT 少了 1 個(gè)指令。 DOVAR 和 NEXT 也多了 1 個(gè)指令:

DOVAR:

PULS X ; 彈出這個(gè)字的 PFA

PSHU X ; 把那個(gè)地址放到參數(shù)棧上

NEXT

ENTER:

PULS X ; 彈出這個(gè)字的 PFA

PSHS Y ; 壓入老的 IP

TFR X,Y ; PFA 變成了新的 IP

NEXT

現(xiàn)在回到本文的開頭,重新讀一下我的“更正”,看一看為什么我們不能通過 IP 來重新讀 CFA 。同時(shí)也要注意,把 Forth 的堆棧指針給 MC6809 的 U 寄存器而 S 保留的情況與這里討論的不同。

子程序串線

子程序串線(STC)和 DTC 非常相似,都是 CPU 直接跳轉(zhuǎn)到一個(gè) Forth 字的代碼域。但是現(xiàn)在不再有 NEXT 代碼,不再有 IP 寄存器,也沒有 W 寄存器。所以,只能在代碼域中使用 JSR 而不可能有其它的選擇,這是可以得到參數(shù)域地址的唯一辦法。這個(gè)過程如圖 5 所示。

圖 5 STC 的串線編碼

高級(jí)串線是被 CPU 執(zhí)行的一系列子程序調(diào)用。當(dāng)一個(gè) JSR DEUX 被執(zhí)行的時(shí)候,串線中下一個(gè)指令的地址被推進(jìn)返回棧。接著,在字 DEUX 中的 JSR DOCON 被執(zhí)行,它使得另一個(gè)返回地址 -- DEUX 的 PFA 被推入堆棧。 DOCON 可以彈出這個(gè)地址,使用它來讀取常數(shù),把常數(shù)保存在堆棧上,然后用一個(gè) RTS 指令返回到串線:

DOCON:

PULS X ; 從返回棧彈出 PFA

LDD ,X ; 讀取參數(shù)域單元

PSHU D ; 把它壓入?yún)?shù)棧

RTS ; 執(zhí)行下一個(gè)高級(jí)字

在子程序串線代碼中,我們?nèi)匀豢梢匝赜么a域和參數(shù)域這樣的術(shù)語。除了 CODE 和冒號(hào)定義之外的每一個(gè) Forth 字的類中,代碼域是被 JSR 或者 CALL 占用的空間(就像 DTC )一樣,而參數(shù)域就是它后面的空間。所以,在 MC6809 上, PFA 等于 CFA+3 。于是, CODE 和冒號(hào)定義的“參數(shù)域”含意變得有點(diǎn)兒模糊,在本文的后面可以看到這一點(diǎn)。

特例: CODE 字

在以上所有的一般性討論中,有一個(gè)明顯的例外,這就是 CODE 定義 -- 用匯編碼子程序定義的 Forth 字。用“匯編語言來定義一個(gè)字” -- 這個(gè)神奇的功能在 Forth 中很容易實(shí)現(xiàn),因?yàn)槊總€(gè) Forth 字都執(zhí)行一段 Forth 代碼。

包含 CODE 字的匯編代碼總是包含在一個(gè) Forth 字的體中,代碼域必須包含有要執(zhí)行的機(jī)器代碼的地址。所以機(jī)器代碼放在參數(shù)域中,代碼域包含了參數(shù)域的地址,如圖 6 所示。

圖 6 CODE 字

在直接或者子程序串線的 Forth 中,我們可以通過類推,把一個(gè) JUMP 放到代碼域中。代碼域也可以用 NOP 或者相同的結(jié)果填充。更好的是,機(jī)器代碼可以直接從代碼域開始,然后進(jìn)入?yún)?shù)域。從這一點(diǎn)看,代碼域和參數(shù)域就沒有區(qū)別了。這不應(yīng)該有任何疑問,因?yàn)槲覀儾⒉恍枰獙?duì)一個(gè) CODE 字做這樣的區(qū)分。但可能有一些反匯編器和一些聰明的編程技巧需要這一區(qū)分,我們?cè)谶@里就不討論它們了。

CODE 字 -- 不論是怎么實(shí)現(xiàn)的 -- 都是不需要向它傳遞參數(shù)域地址的機(jī)器代碼動(dòng)作。參數(shù)域不包含數(shù)據(jù),只是需要執(zhí)行的代碼。只有 NEXT 需要知道這個(gè)地址(或者代碼域地址),這樣它就可以直接跳到機(jī)器代碼。

使用 ;CODE

現(xiàn)在還有三個(gè)問題沒有回答:

?? 我們?nèi)绾蝿?chuàng)建一個(gè) Forth 字,使得能在它的參數(shù)域中含有一些任意的數(shù)據(jù)?

?? 我們?nèi)绾胃淖円粋€(gè)字的代碼域,以指向可選擇的機(jī)器代碼?

?? 我們?nèi)绾卧诖a片段與一個(gè)使用它的字隔離的情況下編譯(匯編)這個(gè)代碼片段?

對(duì)于第一個(gè)問題的回答是:寫一個(gè) Forth 字來做這一工作。在執(zhí)行的時(shí)候,因?yàn)檫@個(gè)字將在 Forth 字典中定義一個(gè)新的字,所以它被稱為“定義字”。

CONSTANT 就是一個(gè)定義字。一個(gè)定義字的所有“硬工作”都是由一個(gè)內(nèi)核字 CREATE 來完成的,它從輸入流中分析名字,為新字建立頭和代碼域,并把它鏈接到字典中。對(duì)程序員來說,剩下的工作就是構(gòu)造參數(shù)域了。

第二個(gè)、第三個(gè)問題的答案包含在兩個(gè)費(fèi)解的 Forth 字中,它們分別是 (;CODE) 和 ;CODE 。為了理解它們是如何工作的,我們來看看定義字 CONSTANT 實(shí)際上是如何用 Forth 高級(jí)定義來寫的。使用前面 MC6809 的例子:

: CONSTANT ( n -- )

CREATE / 創(chuàng)建一個(gè)新的字

, / 把 TOS 的值寫入字典,作為參數(shù)域的第 1 個(gè)單元

;CODE / 結(jié)束高級(jí)定義,開始匯編代碼

LDD 2,X / DOCON 的匯編代碼片斷

PSHU D

NEXT

END-CODE

這個(gè) Forth 字包含了兩個(gè)部分:從 CONSTANT 到 ;CODE 的任何事情都是在 COSNTANT 被訪問時(shí)執(zhí)行的高級(jí) Forth 代碼。而從 ;CODE 到 END-CODE 的事情都是常數(shù)的“子女” -- 常數(shù)類字比如 UN 和 DEUX -- 執(zhí)行時(shí)要執(zhí)行的機(jī)器代碼。實(shí)際上也就是從字 ;CODE 到 END-CODE 的代碼片段為常量類字將指向的機(jī)器代碼片斷。 ;CODE 表示一個(gè)高級(jí)定義的結(jié)束(;)和一個(gè)機(jī)器代碼定義的開始 (CODE) 。但是,它并不在字典中建立兩個(gè)分離的字,從 CONSTANT 到 END-CODE 的全部?jī)?nèi)容都保存在 CONSTANT 的參數(shù)域中,如圖 7 所示。

圖 7 ITC 的 ;CODE

Derick 和 Baker [DER82] 使用三個(gè)“時(shí)間階段”來幫助理解定義字的行為:

時(shí)間階段 1

是在 CONSTANT 被定義時(shí)的行為。這需要同時(shí)引用高級(jí)編譯器(對(duì)于第一個(gè)部分)和 Forth 匯編器(對(duì)于第二個(gè)部分)。這就是定義 CONSTANT 被加入字典的過程,如圖 7 所示。我們可以看到, ;CODE 這個(gè)編譯指示器是在第一個(gè)階段被執(zhí)行的。

時(shí)間階段 2

是字 CONSTANT 被執(zhí)行時(shí)的行為,這時(shí)一些常數(shù)類字被定義,比如:

2 CONSTANT DEUX

這個(gè)階段就是字 CONSTANT 被執(zhí)行、字 DEUX 被加入字典的時(shí)候。在這個(gè)階段 CONSTANT 的高級(jí)定義部分被執(zhí)行,包括字 (;CODE).

時(shí)間階段 3

是常數(shù)類執(zhí)行時(shí)的行為。在我們的例子中,這個(gè)階段就是 DEUX 被執(zhí)行而把值 2 推入堆棧的時(shí)候。這時(shí) CONSTANT 的機(jī)器代碼被執(zhí)行(回憶 DEUX 的代碼域動(dòng)作)

字 ;CODE 和 (;CODE) 的工作

;CODE 在時(shí)間階段 1 被執(zhí)行,這是 CONSTANT 被編譯的時(shí)候。它是一個(gè) Forth 立即字 -- IMMEDIATE 字 -- 這個(gè)字在 Forth 編譯時(shí)執(zhí)行。

;CODE 做以下三件事情:

?? 它把 Forth 字 (;CODE) 編譯到 CONSTANT

?? 它關(guān)閉 Forth 編譯器,同時(shí)

?? 它打開 Forth 匯編器

而 (;CODE) 是字 CONSTANT 的一部分,它在 CONSTANT 執(zhí)行的時(shí)候才被執(zhí)行(時(shí)間階段 2 ),它執(zhí)行以下動(dòng)作:

?? 它得到緊隨其后的機(jī)器代碼的地址,這可以通過從 Forth 返回棧中彈出 IP 而實(shí)現(xiàn);

?? 它把這個(gè)地址放到 CREATE 定義的字的代碼域中,通過 Forth 字 LAST (有時(shí)也稱為 LATEST )等到這個(gè)字的地址;

?? 它完成 EXIT 的動(dòng)作(也稱為 ;S ),這樣 Forth 的內(nèi)部解釋器就不會(huì)把后面的代碼作為 Forth 串線來執(zhí)行,這是結(jié)束 Forth 串線的高級(jí)“子程序返回”。

F83[LAX84] 解釋了它們?cè)?Forth 系統(tǒng)中的典型編碼:

: ;CODE

COMPILE (;CODE) / 編譯 (;CODE) 到定義中

?CSP [COMPILE] [ / 關(guān)閉 Forth 編譯器

REVEAL / ( 與 ";" 的行為類同 )

ASSEMBLER / 打開匯編器

; IMMEDIATE / 把這個(gè)字設(shè)為立即字

: (;CODE)

> / 彈出機(jī)器代碼地址

LAST @ NAME> / 得到最后一個(gè)字的 CA

! / 保存這個(gè)代碼地址到代碼域

; /

(;CODE) 字在兩個(gè)字當(dāng)中更加微妙。因?yàn)樗且粋€(gè)高級(jí) Forth 定義,在 CONSTANT 中后隨它的地址 -- 高級(jí)返回地址 -- 被壓入 Forth 返回棧中,所以在 (;CODE) 中彈出返回棧能夠得到后隨的機(jī)器代碼地址。同時(shí),從返回棧中彈出這個(gè)值使得一級(jí)高級(jí)子程序被返回“旁路”,這樣在 (;CODE) 退出的時(shí)候,它可以退到 CONSTANT 的調(diào)用者。這等效于返回到 COSNTANT ,并使得 CONSTANT 立即返回。通過圖 7 并跟蹤字 CONSTANT 和 (;CODE) 的執(zhí)行可以更加清楚地看到這是如何工作的。

直接和子程序串線

對(duì)于 DTC 和 STC ,;CODE 和 (;CODE) 的動(dòng)作與 ITC 相同,但是也有一個(gè)重要的例外:它不再保存一個(gè)地址,而在代碼域中放有 JUMP 或者 CALL 指令。對(duì)于一個(gè)絕對(duì) JUMP 或 CALL ,可能唯一要做的事情就是把地址保存在代碼域的最后,作為 JUMP 或者 CALL 指令的操作數(shù)。在 MC6809 的情況下,地址作為 3 字節(jié) JSR 指令的最后 2 個(gè)字節(jié)保存。但是某些 Forth 系統(tǒng)比如 Intel 8086 的 Pygmy Forth ,它們?cè)诖a域中使用相對(duì)轉(zhuǎn)移指令。在這種情況下,必須計(jì)算相對(duì)偏移量并把它們插入到分支指令中。

高級(jí) Forth 行為

你已經(jīng)看到了如何讓 Forth 字執(zhí)行一個(gè)指定的匯編語言代碼片段,如何向這個(gè)片斷傳遞字的參數(shù)域地址,但是我們?nèi)绾斡酶呒?jí) Forth 定義“寫出”子程序的行為呢?

每個(gè) Forth 字必須 -- 通過 NEXT 的行為 -- 執(zhí)行一些機(jī)器語言子程序。這就是代碼域的全部。因此,一個(gè)機(jī)器子程序、或者一系列子程序需要解決如何訪問高級(jí)行為的問題。我們稱這個(gè)子程序?yàn)?DODOES 。

這里有三個(gè)問題需要解決:

?? 我們?nèi)绾握业脚c這個(gè)字相關(guān)聯(lián)的高級(jí)行為子程序的地址?

?? 我們?nèi)绾螐臋C(jī)器代碼中為調(diào)用一個(gè)高級(jí)行為子程序而訪問 Forth 解釋器?

?? 我們?nèi)绾蜗蚰莻€(gè)子程序傳遞我們正在執(zhí)行的字的參數(shù)域地址?

對(duì)于第三個(gè)問題的回答是:很容易,用我們?yōu)橐粋€(gè)高級(jí) Forth 子程序在參數(shù)棧上傳遞參數(shù)的方法。我們的機(jī)器語言子程序在訪問高級(jí)串線之前必須把參數(shù)域地址推到堆棧上(從我們以前的工作看,我們知道機(jī)器語言如何能夠得到 PFA )

第二個(gè)問題的答案有一點(diǎn)困難?;旧衔覀兛梢韵?Forth 字 EXECUTE 那樣做一些事情來訪問一個(gè) Forth 字;或者也可能是 ENTER ,它訪問一個(gè)冒號(hào)定義。它們都是我們的“關(guān)鍵”核心字, DODOES 與此類似。

第一個(gè)問題好象有些難度。我們把高級(jí)子程序的地址放到哪里呢?記住:代碼域并不指向高級(jí)代碼,它必須指向機(jī)器代碼。在 Forth 的歷史上,人們?cè)?jīng)使用過以下兩種方法。

FIG-Forth 解決方案

FIG-Forth 用參數(shù)域的第一個(gè)單元來保存高級(jí)代碼的地址。 DODOES 子程序通過這個(gè)單元得到了參數(shù)域的地址,并把實(shí)際數(shù)據(jù)的地址(典型地 PFA+2 )推到棧上,取得高級(jí)子程序的地址,然后調(diào)用 EXECUTE 。這種方法存在兩個(gè)問題:

第一、參數(shù)域的結(jié)構(gòu)因機(jī)器代碼行為和高級(jí)代碼行為而不同。例如一個(gè)使用機(jī)器代碼的 CONSTANT 可以把它的代碼保存到 PFA ,但是一個(gè)使用高級(jí)定義的 CONSTANT 行為卻必須把它的數(shù)據(jù)保存在(典型地) PFA+2 。

第二、每個(gè)高級(jí)行為類的實(shí)例都增加了一個(gè)單元的開銷。也就是說,如果 CONSTANT 用于一個(gè)高級(jí)行為,程序中的每個(gè)常數(shù)都要增大一個(gè)單元!幸運(yùn)的是,聰明的 Forth 程序員很快就找到了解決這個(gè)問題的一種方法, FIG-Forth 方法就不再使用了。

現(xiàn)代的解決方案

大多數(shù) Forth 程序員都為每個(gè)高級(jí)行為子程序配置了一個(gè)不同的機(jī)器語言代碼片段。于是,一個(gè)高級(jí)常數(shù)就會(huì)有它自己的代碼域,它指向一個(gè)機(jī)器語言片段,其核心功能就是訪問 CONSTANT 的高級(jí)行為;一個(gè)高級(jí)變量的代碼域?qū)⒅赶蛞粋€(gè)“ STARTUP ”子程序來實(shí)現(xiàn)高級(jí)的 VARIBLE 行為,等等。

這種方法會(huì)導(dǎo)致代碼的大量重復(fù)嗎?不會(huì)的。因?yàn)檫@些機(jī)器語言片斷只是對(duì)通常的啟動(dòng)子程序 DODOES 的一個(gè)調(diào)用(不同于 FIG-Forth 的子程序),對(duì) DODOES 高級(jí)代碼的地址作為一個(gè)“內(nèi)嵌”子程序參數(shù)傳遞。這就意味著,高級(jí)代碼的地址被放到 JSR/CALL 指令之后。 DODOES 可以從 CPU 堆棧中彈出,然后通過一次讀取來得到這個(gè)地址。

實(shí)際上,我們還可以做得更簡(jiǎn)單。高級(jí)代碼自身是放在 JSR/CALL 指令之后的, DODOES 彈出 CPU 堆棧,直接得到這個(gè)地址。因?yàn)槲覀冎肋@是高級(jí) Forth 代碼,我們可以忽略代碼域,而只編譯高級(jí)串線……這就很方便地把 ENTER 的行為集成到了 DODOES 中。

現(xiàn)在每一個(gè)“定義”字都指向了一小部分機(jī)器代碼 – 沒有浪費(fèi)任何的參數(shù)域空間。這一小部分機(jī)器代碼是 JSR 或者 CALL 指令,后隨一個(gè)高級(jí)行為子程序。在 MC6809 的例子中,我們已經(jīng)使每個(gè)常數(shù)的兩個(gè)字節(jié)用一個(gè) 3 字節(jié)的 JSR 替代,它只出現(xiàn)一次。

使用這些策略使得在 Forth 內(nèi)核中包含了許多費(fèi)解的程序邏輯。所以,讓我們使用我們可信賴的 ITC MC6809 例子來看看實(shí)際上這是如何實(shí)現(xiàn)的:

圖 8 顯示了使用高級(jí)定義實(shí)現(xiàn)的 DEUX 常數(shù)。當(dāng) Forth 解釋器遇到 DEUX -- 也就是說當(dāng) Forth 的 IP 寄存器在 IP(1)時(shí) -- 它做通常的事情:它讀取包含在 DEUX 代碼域中的地址,跳轉(zhuǎn)到那個(gè)地址。在那個(gè)地址上是一個(gè) JSR DODOES 指令,于是立即發(fā)生第二個(gè)跳轉(zhuǎn) -- 這次是一個(gè)子程序調(diào)用 。

?

圖 8 ITC DODOES

DODOES 接著必須執(zhí)行下列動(dòng)作:

?? 把 DEUX 的參數(shù)域地址推到參數(shù)棧上,以備將來高級(jí)行為子程序使用。因?yàn)?JSR 指令并不改變?nèi)魏渭拇嫫?#xff0c;我們希望 DEUX 的參數(shù)域地址(或者“鄰近”的地址)仍然保留在 W 寄存器中;

?? 通過彈出 CPU 堆棧得到高級(jí)行為子程序的地址(回憶:彈出 CPU 堆??梢缘玫骄o隨在 JSR 指令之后的不論什么的地址)。這是一個(gè)高級(jí)串線,冒號(hào)定義的參數(shù)域部分;

?? 保存舊的 Forth 指令指針 -- IP(2) -- 到 Forth 返回棧上,因?yàn)?IP 寄存器要被用于執(zhí)行高級(jí)代碼。本質(zhì)上,DODOES 必須“嵌套” IP ,就像 ENTER 一樣。記住 Forth 的返回棧也許不同于 CPU 的子程序堆棧;

?? 把高級(jí)串線的地址放到 IP 中,這是圖 8 中的 IP(3) ;

?? 在新的位置上執(zhí)行 NEXT 以繼續(xù)高級(jí)解釋;

假設(shè)一個(gè)間接串線的 ITC MC6809 符合下列情況:

?? W 沒有被 NEXT 增量(也就是 W 將要包含 NEXT 進(jìn)入字的 CFA )

?? MC6809 的 S 寄存器是 Forth 的 PSP,U 寄存器是 Forth 的 RSP (也就是 CPU 的堆棧不是 Forth 的返回棧)

?? MC6809 的 Y 寄存器是 Forth 的 IP,X 是 Forth 的 W

回憶在這些條件下的 NEXT 定義:

NEXT:

LDX ,Y++ ; (IP) -> W, and IP + 2 -> IP

JMP [,X] ; (W) -> temp, JMP (temp)

DODOES 可以這樣寫:

DODOES:

LEAX 2,X ; 使 W 指向參數(shù)域

PSHU Y ; 把舊的 IP 壓入返回棧

PULS Y ; 從 CPU 堆棧上彈了新的 IP

PSHS X ; 壓入?yún)?shù)域地址 W 到參數(shù)棧上

NEXT ; 訪問高級(jí)解釋器

這些操作并沒有嚴(yán)格按順序進(jìn)行。當(dāng)然,只要恰當(dāng)?shù)臄?shù)據(jù)在恰當(dāng)?shù)臅r(shí)間內(nèi)進(jìn)入了恰當(dāng)?shù)亩褩?#xff08;或者進(jìn)入了恰當(dāng)?shù)募拇嫫?#xff09;,操作的順序并不要緊。在這里,我們實(shí)際上是利用了這樣一個(gè)事實(shí):在新的 IP 從 CPU 堆棧中彈出之前,老的 IP 可以壓入 Forth 的返回棧。

在某些處理器上, CPU 的堆棧被用于 Forth 的返回棧。對(duì)于這種情況,就需要一個(gè)臨時(shí)存儲(chǔ)器訪問步驟。同樣是上面的例子,如果我們必須選擇 S=RSP和 U=PSP 則 DODOES 就成了:

DODOES:

LEAX 2,X ; 讓 W 指向參數(shù)域

PSHU X ; 把參數(shù)域地址 W 壓入?yún)?shù)棧

PULS X ; 從 CPU 堆棧中彈出串線的地址

PSHS Y ; 把舊的 IP 壓入返回棧

TFR X,Y ; 把串線的地址放入 IP

NEXT ; 訪問高級(jí)解釋器

因?yàn)槲覀儽举|(zhì)上是在交換 IP 和返回棧/CPU 堆棧的內(nèi)容,所以我們就必須用 X 作為臨時(shí)寄存器。于是,我們?cè)谥匦率褂?X 寄存器之前就必須把 PFA -- (A)壓入堆棧。

我們就是要這樣一步一步地研究這些 DODOES 例子,追蹤兩個(gè)堆棧和全部寄存器的內(nèi)容。我自己就經(jīng)常研究自己編寫的 DODOES 子程序,以確信任何一個(gè)寄存器都沒有在錯(cuò)誤的時(shí)刻被亂用。

直接串線

DODOES 的邏輯在 DTC 中是一樣的。但是我的實(shí)現(xiàn)卻是不同的,這依賴于 DTC Forth 在一個(gè)字的代碼域中是使用 JMP 還是使用 CALL 。

在代碼域中使用 JMP。如果將要被執(zhí)行的字的地址可以在寄存器中得到,則一個(gè) DTC Forth 就可以在代碼域中使用 JMP,這就很像代碼域地址。從 DODOES 的觀點(diǎn)看,這與 ITC 是一樣的。

在我們的例子中, DODOES 知道 Forth 解釋器跳轉(zhuǎn)到了與 DEUX 相關(guān)的機(jī)器代碼,那個(gè)代碼是JSR 到 DODOES 。現(xiàn)在每個(gè)跳轉(zhuǎn)是使用直接跳轉(zhuǎn)還是使用間接跳轉(zhuǎn)并沒有什么關(guān)系,寄存器和堆棧的內(nèi)容是相同的。所以,DODOES 的代碼與 ITC 是相同的(當(dāng)然,NEXT 是不同的, W 也許要有不同的偏移量指向參數(shù)域)。

在 DTC 的 MC6809 中,我們從來就沒有顯式地讀取將要執(zhí)行字的 CFA ,所以 Forth 字必須在它的代碼域中包含一個(gè) JSR ,這樣我們就可以通過堆棧得到這個(gè)字的參數(shù)域地址,而不是從堆棧中得到。這種情況下的 DEUX 例子顯示在圖 9 中。

圖 9 DTC 的 DODOES

當(dāng) IP 在 IP(1)時(shí), Forth 解釋器跳轉(zhuǎn)到 DEUX 的代碼域(同時(shí)增量 IP)。在代碼域中,是一個(gè)到 DEUX 機(jī)器代碼片斷的 JSR ,在那里是第二個(gè) JSR ,到 DODOES 。于是兩個(gè)地址進(jìn)入了 CPU 堆棧。

第一個(gè) JSR 的返回地址是 DEUX 的參數(shù)域地址,第二個(gè) JSR 的返回地址 -- 在 CPU 堆棧的最上面 -- 是將要執(zhí)行的高級(jí)串線地址。 DODOES 必須確保舊的 IP 已經(jīng)壓入到返回棧, DEUX 的 PFA 壓入了參數(shù)堆棧,高級(jí)串線的地址被裝入到 IP 中。這些對(duì)于堆棧分配是非常敏感的!對(duì)于 S=PSP(CPU 堆棧)和 U=RSP , NEXT 和 DODOES 的代碼變成了:

NEXT:

LDX [,Y++] ; (IP) -> temp, IP+2 -> IP, JMP (temp)

DODOES:

PSHU Y ; 把舊的 IP 壓入返回棧

PULS Y ; 從 CPU 堆棧中彈出新的 IP 。注意: CPU 堆棧是參數(shù)棧,最頂?shù)脑噩F(xiàn)在正是我們需要的字的 PFA

NEXT ; 訪問高級(jí)解釋器

我們可以自己看一下 NEXT、DEUX、DODOES 壓入一項(xiàng)目 -- DEUX 的 PFA-- 到參棧的全過程。

子程序串線

圖 10 顯示了一個(gè) MC6809 STC 的 DEUX 高級(jí)行為的例子。在進(jìn)入 DODOES 的時(shí)候,三個(gè)數(shù)據(jù)被壓入了CPU/RETURN 的返回棧:“主串線”的返回地址、 DEUX 的 PFA、DEUX 的高級(jí)行為代碼的地址。DODOES 必須彈出最后兩個(gè),把 PFA 壓入?yún)?shù)棧,跳轉(zhuǎn)到行為代碼:

?

圖 10 STC 的 DODOES

MC6809 的 DODOES 現(xiàn)在是一個(gè) 3 指令的子程序。它甚至可以通過“把 JSR DODOES 變成內(nèi)嵌方法”來進(jìn)一步簡(jiǎn)化。也就是說用等效的機(jī)器代碼來代替 JSR DODOES 。由于簡(jiǎn)化了一個(gè) JSR ,也就簡(jiǎn)化了堆棧的處理:

PULS X ; 從 CPU 堆棧中彈出 PFA

PSHU X ; 把它壓入?yún)?shù)棧

…… ; DEUX 的其它高級(jí)串線

這里使用了 4 字節(jié)的顯式代碼代替了 3 字節(jié)的 JSR 指令,從而相當(dāng)有效地提高了執(zhí)行的速度。對(duì)于 MC6809 這也許是一個(gè)很好的選擇,對(duì)于像 8051 這樣的處理器, DODEOS 則顯得太長(zhǎng)了,大概應(yīng)該還是作為一個(gè)子程序?yàn)楹谩?

使用 DOES>

我們已經(jīng)學(xué)習(xí)了使用 ;CODE 去創(chuàng)建一個(gè) Forth 字,它的參數(shù)域中可以包含有任意的數(shù)據(jù),以及如何使一個(gè)字的代碼域指向新的機(jī)器代碼片斷。那么我們?nèi)绾尉幾g一個(gè)高級(jí)行為子程序并用一個(gè)新的字指向它呢?

答案依賴于兩個(gè) Forth 字 DOES> 和 (DOES>) ,它們是 ;CODE 和 (;CODE) 的高級(jí)定義等效。為了理解它們,讓我們看一個(gè)使用它們的例子:

: CONSTANT ( n -- )

CREATE / 創(chuàng)建新的字

, / 把 TOS 值加入字典作為參數(shù)域的第 1 個(gè)單元

DOES> / 結(jié)束 " 創(chuàng)建部分 " 開始 " 行為 " 部分

@ / 給出 PFA ,得到它的內(nèi)容

;

把這些與前面的 ;CODE 例子比較,可以看到 DOES> 執(zhí)行的功能與 ;CODE 類似。從 : CONSTANT 到 DOES> 的每個(gè)行為都是在 CONSTANT 字執(zhí)行時(shí)被訪問的。這是構(gòu)建一個(gè)“定義”字的參數(shù)域和代碼。從 DOES> 到 ; 的代碼是 COSNTANT 的“孩子”(比如 DEUX )被訪問時(shí)執(zhí)行的高級(jí)代碼,也就是代碼域?qū)⒁赶虻母呒?jí)代碼片斷。(我們會(huì)看到 JSR DODOES 包含在這個(gè)高級(jí)代碼片斷之前)。

與 ;CODE 一樣,“CREATE”和“ACTION”子句都在 Forth 字 CONSTANT 體中,如圖 11 所示。

圖 11 ITC 的 DODOES

回憶時(shí)間序列 1 、 2 、 3 ,字 DOES> 和 (DOES>) 做下例事情:

?? 它把 Forth 字 (DOES>) 編譯到 CONSTANT 中;

?? 它把一個(gè) JSR DODOES 編譯到 CONSTANT 中;

注意 DOES> 保持 Forth 編譯器一直運(yùn)行,這樣可以保證后面的高級(jí)代碼片斷繼續(xù)得到編譯。同樣,盡管 JSR DODOES 本身不是 Forth 代碼,但是像 DOES> 這樣的立即字可以使它編譯到 Forth 代碼中。

(DOES>) 是字 CONSTANT 的一部分,所以在 CONSTANT 被執(zhí)行的時(shí)候(時(shí)間序列 2 )執(zhí)行,它做下例事情:

?? 它通過從 Forth 的返回棧中彈出 IP 得到緊隨其后的機(jī)器碼的地址( JSR DODOES );

?? 它把這個(gè)地址放到被 CREATE 剛剛定義的字的代碼域中。

?? 它執(zhí)行 EXIT 行為,使得 CONSTANT 在這里中斷而不再執(zhí)行后面的代碼片斷。

(DOES>) 的行為和 (;CODE) 是一樣的!所以 Forth 系統(tǒng)并不需要另外定義一個(gè)新的字。例如 F83 系統(tǒng)在 ;CODE 和 DOES> 中同時(shí)使用 (;CODE) 。我也從現(xiàn)在開始使用 (;CODE) 代替 (DOES>).

你已經(jīng)看到了 (;CODE) 是如何工作的。 F83 是這樣定義 DOES> 的

: DOES>

COMPILE (;CODE) / 編譯 (;CODE) 到定義中

0E8 C, / CALL 指令的操作碼字節(jié)

DODOES HERE 2+ - , / 把相對(duì)轉(zhuǎn)移寫入 DODOES

; IMMEDIATE

這里 DODOES 是一個(gè)常數(shù),它保存有 DODOES 子程序的地址(實(shí)際使用的 F83 源代碼和這里所說的有一點(diǎn)點(diǎn)兒不同,因?yàn)?F83 使用的 META 編譯器有不同的要求)。

DOES> 不需要改變 CSP 或者 SMUDGE 位,因?yàn)?Forth 編譯器的狀態(tài)是 'on.' 。在 Intel 8086 的情況下, CALL 指令使用相對(duì)地址,因此,需要對(duì) DODOES 和 HERE 做一個(gè)算術(shù)運(yùn)算。在 MC6809 中, DOES> 看起來像這樣的:

: DOES>

COMPILE (;CODE) / 把 (;CODE) 編譯進(jìn)定義

0BD C, / JSR 擴(kuò)展操作碼

DODOES , / 操作數(shù): DODOES 的地址

; IMMEDIATE

你可以看到一個(gè)機(jī)器語言 JSR DODOES 是如何被編譯到高級(jí) (;CODE) 之后和高級(jí)行為之前的。

直接和間接串線

DTC 和 STC 中的唯一區(qū)別是代碼域必須修改以指向新的子程序。這是由 (;CODE) 完成的,所要求的改變已經(jīng)描述過了。 DOES> 沒有任何影響,除非你在 STC 中把 JSR DODOES 擴(kuò)展成為顯式的機(jī)器代碼。在這種情況下, DOES> 被修改成匯編“內(nèi)嵌”的機(jī)器代碼而不是 JSR DODOES 子程序。

思前想后

我們可能從來就沒有想到,這么幾行代碼會(huì)引出這么多的內(nèi)容。這也是為什么我特別贊賞 ;CODE 和 DOES> ,說實(shí)在的,我從來也沒有見到過用這么經(jīng)濟(jì)的方法就實(shí)現(xiàn)了這么復(fù)雜、強(qiáng)大和靈活的結(jié)構(gòu)。

參考文獻(xiàn)

[DER82] Derick, Mitch and Baker, Linda, Forth Encyclopedia, Mountain View Press (1982). A word-by-word description of fig- Forth in minute detail. Still available from the Forth Interest Group, P.O. Box 2154, Oakland CA 94621.

[LAX84] Laxen, H. and Perry, M., F83 for the IBM PC, version 2.1.0 (1984). Distributed by the authors, available from the Forth Interest Group or GEnie.

第四部分 匯編器還是 META 編譯器

撰寫本文的過程貫穿著一個(gè)指導(dǎo)思想:“保持最短”。本著這個(gè)原則,我把源程序列表安排到另外的地方,對(duì)此我表示歉意。現(xiàn)在,我們主要試圖討論以下話題:

你如何開始構(gòu)造一個(gè) Forth 系統(tǒng)?

你現(xiàn)在已經(jīng)知道了,主要的 Forth 程序代碼是高級(jí)串線,它們通常被編譯成一系列地址。在 FIG-Forth 時(shí)代和早期的 Forth 實(shí)踐中,匯編語言是唯一可用的程序設(shè)計(jì)語言工具。匯編語言對(duì)于編寫 Forth 的 CODE 字是非常好的,但是高級(jí)串線必須用一系列的 DW 偽指令來編寫。例如, Forth 字:

: MAX ( n n - n) OVER OVER < IF SWAP THEN DROP ;

必須寫成

DW OVER,OVER,LESS,ZBRAN

DW MAX2-$

DW SWAP

MAX2: DW DROP,SEMIS

后來,由于可以實(shí)際工作的 Forth 系統(tǒng)越來越普及, Forth 編寫者開始把 Forth 編譯器修改成為交叉編譯器,通過運(yùn)行在 CP/M (或者蘋果 II ,或者其它任何什么微機(jī)系統(tǒng))的 Forth 系統(tǒng),你就可以為其它 CPU 編寫 Forth 程序、更改 Forth 系統(tǒng)或者為那個(gè) CPU 編寫一個(gè)全新的 Forth 系統(tǒng)。

由于是從 Forth 內(nèi)部創(chuàng)建一個(gè)全新的 Forth 系統(tǒng),這種編譯器被稱為“META 編譯器”。計(jì)算機(jī)科學(xué)的學(xué)究們反對(duì)這樣的稱謂,所以有些 Forth 編寫者仍然使用“交叉編譯”和“重新編譯”的術(shù)語,這兩個(gè)術(shù)語之間的差異是:“重新編譯”只能為相同的 CPU 產(chǎn)生新的 Forth 系統(tǒng)。

現(xiàn)在大多數(shù) PC 機(jī)上的 Forth 都是通過 META 編譯產(chǎn)生的,但是,在嵌入式系統(tǒng)領(lǐng)域卻產(chǎn)生了意見分歧。

使用匯編器編寫 Forth 系統(tǒng)的觀點(diǎn)認(rèn)為:

?? META 編譯器神秘難懂,你必須完全理解一個(gè) META 編譯器然后才能使用它;

?? 一般的程序員都懂得匯編器;

?? 匯編器對(duì)于一個(gè)新的 CPU 總是可用的;

?? 匯編器處理許多優(yōu)化(比如長(zhǎng)短調(diào)用格式);

?? 匯編器處理前向引用和特殊的尋址模式,而許多 META 編譯器通常不能做到;

?? 匯編程序員可以使用熟悉的編輯器和調(diào)試工具;

?? 代碼的產(chǎn)生是完全可見的,沒有向程序員“隱藏”任何東西;

?? 改變 Forth 模型非常容易,而許多設(shè)計(jì)的考慮卻影響 META 編譯器的內(nèi)部;

使用 META 編譯器的觀點(diǎn)認(rèn)為:

?? 你是在編寫“正?!钡?Forth 代碼,它當(dāng)然易于閱讀和調(diào)試;

?? 一但你理解了你的 META 編譯器,你就可以很容易地把它移植到新的 CPU 上 ;

?? 你需要的唯一工具就是你的計(jì)算機(jī)上的 Forth 系統(tǒng);這一點(diǎn)對(duì)于沒有 PC 的人特別實(shí)用,因?yàn)楝F(xiàn)在的許多交叉匯編器要求 PC 機(jī)或者工作站。

我用各種方式編寫過幾個(gè) Forth 系統(tǒng),所以要作出選擇是很痛苦的。我傾向于使用 META 編譯器:我發(fā)現(xiàn) Forth 的 MAX 代碼比等效的匯編代碼易讀、易懂。反對(duì)使用 META 編譯器的觀點(diǎn)許多已經(jīng)被現(xiàn)代的“專業(yè)”編譯器所克服,如果你使用 Forth 工作,我強(qiáng)烈建議你考慮一個(gè)商業(yè)化產(chǎn)品。唉,公共的 META 編譯器(包括我自己的)依然落后于時(shí)代,笨拙同時(shí)神秘。

所以我準(zhǔn)備為 Forth 程序員提供基本的材料,告訴你作出自己的選擇。我將給出 META 形式的MC6809 代碼,為 F83 ( IBM PC CP/M ST )提供 META 編譯器。 Z80 代碼將使用 CP/M 匯編器寫成。 8051 代碼使用公共的 PC 交叉匯編器編寫。

使用 C 語言編寫 Forth 系統(tǒng)?

如果不討論“用C語言編寫 Forth 系統(tǒng)”這個(gè)新的方法,本文就將是不完全的。 C 語言比匯編器具有更好的可移植性 -- 理論上,你所要作的全部工作就是為任何 CPU 重新編譯相同的代碼。

這種方法的缺點(diǎn)是:

?? 在設(shè)計(jì)決策選擇上更缺少靈活性;比如,不可能實(shí)現(xiàn)直接串線編碼,也不可能優(yōu)化寄存器的分配;

?? 增加原語之后,你必須重新編譯 C 源代碼;

?? 某些 C 語言實(shí)現(xiàn)的 Forth 使用了效率很低的串線技術(shù),比如多個(gè) CASE 語句;

?? 大多數(shù) C 編譯器產(chǎn)生的代碼比匯編語言程序員的代碼低效;

但是對(duì)于那些 UNIX 系統(tǒng)和不支持匯編語言編程的 RISC 工作站來說,這卻是 Forth 得以運(yùn)行的唯一方法。最完全和廣泛使用的公共域 C 語言 Forth 系統(tǒng)是 TILE 。如果你沒有運(yùn)行 UNIX 系統(tǒng),你可以看一下文件 HENCE4TH_1.2.A 。

為了繼續(xù)以前的比較,還是先來看看 HENCE4TH 的 MAX 定義。為了清楚起見,我略去了字典頭:

_max()

{

OVER OVER LESS IF SWAP ENDIF DROP

}

不使用匯編器,用 C 語言編寫核心 CODE 定義,比如,這是 HENCE4TH 的 SWAP 定義:

_swap()

{

register cell i = *(dsp);

*(dsp) = *(dsp + 1);

*(dsp + 1) = i;

}

請(qǐng)注意:用 C 編寫 Forth 字有非常不同的技術(shù),所以這些字在 CForth 和 TILE 中可能差異非常大。

在 MC68000 或者 SPARC 工作站上,這樣的編碼可以產(chǎn)生非常好的代碼。不過,一但你計(jì)劃用 C 來實(shí)現(xiàn) Forth ,你也需要理解用匯編語言實(shí)現(xiàn) Forth 是怎樣工作的。所以請(qǐng)繼續(xù)閱讀本文。

參考文獻(xiàn)

[CAS80] Cassady, John J., METAForth: A Metacompiler for Fig- Forth , Forth Interest Group (1980).

[MIS90] HenceForth in C , Version 1.2, distributed by The Missing Link, 975 East Ave. Suite 112, Chico, CA 95926, USA (1990). This is a shareware product available from the GEnie Forth Roundtable.

[ROD91] Rodriguez, B.J., letter to the editor, Forth Dimensions XIII:3 (Sep/Oct 1991), p.5.

[ROD92] Rodriguez, B.J., "Principles of Metacompilation," Forth Dimensions XIV:3 (Sep/Oct 1992), XIV:4 (Nov/Dec 1992), and XIV:5 (Jan/Feb 1993). Note that the published code is for a fig-Forth variant and not F83. The F83 version is on GEnie as CHROMIUM.ZIP

[SER91] Sergeant, Frank, "Metacompilation Made Easy," Forth Dimensions XII:6 (Mar/Apr 1991).

[TAL80] Talbot, R.J., fig-Forth for 6809 , Forth Interest Group, P.O. Box 2154, Oakland, CA 94621 (1980).

[TIN91] Ting, C.H., "How Metacompilation Stops the Growth Rate of Forth Programmers," Forth Dimensions XIII:1 (May/Jun 1991), p.17.

第五部分 Z80 原語

我提交的代碼

最后,我準(zhǔn)備展示一個(gè)(我希望是) ANSI 兼容的編譯器 : CAMEL Forth ,包括它的全部源碼。作為一個(gè)很好的練習(xí) -- 也為了版權(quán)的原因 -- 我重新編寫了全部的代碼(你知道不看優(yōu)秀的代碼實(shí)例有多困難嗎?!)。當(dāng)然,我在不同的 Forth 系統(tǒng)上的經(jīng)驗(yàn)無疑影響著所選擇的設(shè)計(jì)策略。

由于空間所限,源代碼分成四個(gè)部分安裝(如果你已經(jīng)等不及了,可以去 GENIE 下載全部的文件)

?? Z80 Forth “原語”: 用匯編語言編寫

?? 8051 Forth “原語”: 也用匯編語言編寫

?? Z80/8051 高級(jí)內(nèi)核 同樣

?? 完全的 6809 內(nèi)核: 使用 META 編譯器的源文件

我計(jì)劃盡量使用公共軟件來實(shí)現(xiàn) CAMEL Forth :對(duì)于 Z80 ,使用 CP/M 下的 Z80R 匯編工具;對(duì)于 8051 ,使用 IBM PC 上的 A51 交叉匯編器,對(duì)于MC6809 ,使用我自己的 F83 For CP/M IBM PC ATARI ST 工具。

我這里的“核心”是指組成一個(gè)基本 Forth 系統(tǒng)的一系列字,包括編譯和解釋字。對(duì)于 CAMEL Forth 來說,這些就是 ANS Forth 規(guī)定的核心字再加上為了實(shí)現(xiàn)這些核心字所需要的非 ANSI 字。 Forth 核心通常由兩部分組成:一部分是用機(jī)器代碼寫成的(即 CODE 字),另一部分是高級(jí)定義字,用機(jī)器代碼寫成的字稱為“原語”,因?yàn)樵谧詈蟮姆治鲋?#xff0c;全部的 Forth 系統(tǒng)就是由這些字組成的。

嚴(yán)格地說,哪些字應(yīng)該用機(jī)器代碼來寫呢?選擇這些原語是一個(gè)有趣的任務(wù)。一個(gè)小原語集合可以簡(jiǎn)化移植,但性能肯定很糟。我聽說過只用 13 個(gè)原語就能夠定義 Forth 的情況 - 當(dāng)然這是一個(gè)很慢的 Forth 系統(tǒng)。 eForth 是一個(gè)以可移植性作為設(shè)計(jì)目標(biāo)的 Forth 系統(tǒng),它有 31 個(gè)原語。

而我的原則是這樣的:

?? 基本的算術(shù)、邏輯運(yùn)算以及存儲(chǔ)器操作用 CODE 實(shí)現(xiàn) ;

?? 如果一個(gè) Forth 字不能簡(jiǎn)單有效地用一系列的 Forth 字編寫,則它應(yīng)該用 CODE 實(shí)現(xiàn)(如 U<, RSHIFT 等) ;

?? 如果一個(gè)簡(jiǎn)單的字頻繁使用,則用 CODE 實(shí)現(xiàn)是值得的(如 NIP , TUCK );

?? 如果一個(gè)字用 CODE 編寫時(shí)并不需要多少字節(jié),則用 CODE 實(shí)現(xiàn);

?? 如果一個(gè)處理器包含有實(shí)現(xiàn)一個(gè)字所需要的功能,則用 CODE 編寫。比如,在 Z80 或者 8086 上,有 CMOVE 或者 SCAN 指令;

?? 如果一個(gè)字主要是搗弄堆棧上的參數(shù),但是邏輯非常簡(jiǎn)單,應(yīng)該用 CODE 實(shí)現(xiàn),這里參數(shù)可以放到寄存器中;

?? 如果一個(gè)字的控制和邏輯功能復(fù)雜,則它最好用高級(jí)定義實(shí)現(xiàn);

對(duì)于 Z80 的 CAMELForth ,我使用了大約 70 個(gè)原語(見表 1 )。

序號(hào)

名稱

進(jìn)入時(shí) -- 時(shí)堆棧

描述

核心字:這些是 ANS Forth 文檔要求的核心定義

1

!

x a-addr --

把一個(gè)單元數(shù)存入存儲(chǔ)器

2

+

1/u1 n2/u2 -- n3/u3

加法 n1+n2

3

+!

n/u a-addr --

加一個(gè)單元到存儲(chǔ)器

4

-

n1/u1 n2/u2 -- n3/u3

減法 n1-n2

5

<

n1 n2 – flag

測(cè)試 n1<n2, 有符號(hào)數(shù)

6

=

x1 x2 – flag

測(cè)試 x1=x2

7

>

n1 n2 – flag

測(cè)試 n1>n2, 有符號(hào)數(shù)

8

>R

x -- R: -- x

壓入返回棧

9

?DUP

x -- 0 | x x

如果棧頂元素非0則復(fù)制

10

@

a-addr – x

從存儲(chǔ)器中讀取一個(gè)單元

11

0<

n – flag

如果 TOS 為負(fù)則為真

12

0=

n/u – flag

如果 TOS=0 則為真

13

1+

n1/u1 -- n2/u2

加 1 到 TOS

14

1-

n1/u1 -- n2/u2

從 TOS 中減 1

15

2*

x1 -- x2

算術(shù)左移

16

2/

x1 -- x2

算術(shù)右移

17

AND

x1 x2 -- x3

邏輯 AND

18

CONSTANT

n --

定義一個(gè) Forth 常數(shù)

19

C!

c c-addr --

把字符存入存儲(chǔ)器

20

C@

c-addr – c

從存儲(chǔ)器讀取字符

21

DROP

x --

去除棧頂元素

22

DUP

x – x x

復(fù)制棧頂元素

23

EMIT

c --

向控制臺(tái)輸出字符

24

EXECUTE

Forth word 'xt'

執(zhí)行棧頂?shù)?Forth 字

25

EXIT

--

退出一個(gè)冒號(hào)字義

26

FILL

c-addr u c --

用字符填充存儲(chǔ)器

27

I

-- n R: y1 y2 -- y1 y2

得到內(nèi)層的循環(huán)計(jì)數(shù)

28

INVERT

x1-- x2

位反轉(zhuǎn)

29

J

-- n R: 4*y -- 4*y

得到第二個(gè)循環(huán)計(jì)數(shù)

30

KEY

-- c

從鍵盤輸入一個(gè)字符

31

LSHIFT

x1 u -- x2

邏輯左移 u 位

32

NEGATE

x1 -- x2

2 的補(bǔ)碼

33

OR

x1 x2 -- x3

邏輯 OR

34

OVER

x1 x2 -- x1 x2 x1

復(fù)制次棧項(xiàng)

35

ROT

x1 x2 x3 --x2 x3 x1

棧頂三元素旋轉(zhuǎn)

36

RSHIFT

x1 u -- x2

邏輯右移 u 位

37

R>

-- x R: x --

從返回棧頂彈出

38

R@

-- x R: x – x

讀取返回棧

39

SWAP

x1 x2 -- x2 x1

交換棧頂?shù)膬蓚€(gè)項(xiàng)目

40

UM*

u1 u2 – ud

無符號(hào) 16x16->32 乘法

41

UM/MOD

ud u1 -- u2 u3

無符號(hào) 32/16->16 除法

42

UNLOOP

-- R: sys1 sys2 --

退出循環(huán)參數(shù)

43

U<

u1 u2 – flag

測(cè)試 u1<n2, 無符號(hào)

44

VARIABLE

--

定義一個(gè) Forth 變量

45

XOR

x1 x2 -- x3

邏輯異或

擴(kuò)展字:這些可選擇的字也是 ANS Forth 文檔定義的

46

<>

x1 x2 – flag

測(cè)試不相等

47

BYE

i*x --

返回到 CP/M 操作系統(tǒng)

48

CMOVE

c-addr1 c-addr2 u --

從底移動(dòng)字節(jié)

49

CMOVE>

c-addr1 c-addr2 u --

從頂移動(dòng)字節(jié)

50

KEY?

-- flag

如果在鍵盤上按了鍵則返回真

51

M+

d1 n -- d2

加無符號(hào)數(shù)到雙精度數(shù)

52

NIP

x1 x2 -- x2

去除次棧頂

53

TUCK

x1 x2 -- x2 x1 x2

見堆棧圖示

54

U>

u1 u2 – flag

測(cè)試 u1>u2, 無符號(hào)

個(gè)人擴(kuò)展:這些字只屬于 CamelForth 實(shí)現(xiàn)

55

(do)

n1|u1 n2|u2 -- R:-- y1 y2 DO 的運(yùn)行時(shí)間代碼

56

(loop)

R: y1 y2 -- | y1 y2

LOOP 的運(yùn)行時(shí)間代碼

57

(+loop)

n -- R: y1 y2 -- | y1 y2 +LOOP 的運(yùn)行時(shí)間代碼

58

><

x1 -- x2

交換字節(jié)

59

?branch

x --

如果 TOS 為 0 則跳轉(zhuǎn)

60

BDOS

DE C – A

調(diào)用 CP/M BDOS 功能

61

branch

--

無條件分支

62

Lit

-- x

內(nèi)嵌文字常數(shù)放到堆棧上

63

PC!

c p-addr --

把字符輸出到口上

64

PC@

p-addr – c

從口輸入字符

65

RP!

a-addr --

設(shè)置返回棧指針

66

RP@

-- a-addr

得到返回棧指針

67

SCAN

ca1 u1 c -- ca2 u2

尋找匹配的字符

68

SKIP

ca1 u1 c -- ca2 u2

跳過匹配的字符

69

SP!

A-addr --

設(shè)置數(shù)據(jù)棧指針

70

SP@

-- a-addr

得到數(shù)據(jù)棧指針

71

S=

ca1 ca2 u – n

串比較 n<0: s1<s2, n=0: s1=s2, n>0: s1>s2

72

USER

n --

定義用戶變量 'n'

堆棧解釋

R: = 返回堆棧

c = 8位字符

flag = 布爾(0 或者 -1)

n = 有符號(hào)16位

u = 無符號(hào)16位

d = 無符號(hào)32位

ud = 無符號(hào)32位

+n = 無符號(hào)15位

x = 任何的單元值

i*x j*x = 任何的單元值

a-addr = 對(duì)齊的地址

ca = 字符地址

p-addr = I/O 口地址

y = 系統(tǒng)指定

在確定了 Forth 模型和它所使用的目標(biāo) CPU 之后,我依照下列過程進(jìn)行開發(fā):

?? 選擇一個(gè) ANSI 核心字子集作為原語;

?? 按照 ANSI 的描述,編寫這些字的匯編定義,加入處理器的初始化代碼;

?? 運(yùn)行匯編器,定位源程序的錯(cuò)誤;

?? 測(cè)試產(chǎn)生的匯編代碼。我通常的做法是加上幾行匯編代碼,使得程序能夠在初始化完成之后輸出一個(gè)字符,這是一個(gè)非常關(guān)鍵的測(cè)試,它保證了你的硬件、匯編器、下載器( EPROM 編程器或者其它什么東西)、串行通訊口統(tǒng)統(tǒng)工作正常! (只對(duì)嵌入式系統(tǒng))加入另外的匯編代碼段以讀取串口并回送,這樣就可以測(cè)試雙向通訊了;

?? 寫一個(gè)高級(jí) Forth 代碼片斷以輸出一個(gè)字符,這個(gè)代碼段只使用 Forth 原語(通常是這樣的: LIT 33h EMIT BYE ),這就可以測(cè)試 Forth 寄存器的初始化、堆棧和串線機(jī)制。這個(gè)階段的問題可以通過追蹤 NEXT 、初始化、數(shù)據(jù)堆棧的邏輯錯(cuò)誤來定位,比如把一個(gè)堆棧設(shè)置到了 ROM 中;

?? 寫一個(gè)冒號(hào)定義輸出一個(gè)字符,把這個(gè)定義包含在上面的高級(jí)定義片斷中,比如定義 : BLIP LIT 34 EMIT EXIT ; 然后測(cè)試代碼片段 LIT 33h EMIT BLIP BYE 。這個(gè)階段的問題通常與 DOCOLON 、 EXIT 、返回棧有關(guān)。

?? 現(xiàn)在可以編寫一些工具來輔助開發(fā),比如顯示堆棧上的 16 進(jìn)制數(shù)等等。列表 1 是一個(gè)簡(jiǎn)單的測(cè)試子程序,它運(yùn)行一個(gè)永不停止的存儲(chǔ)器 DUMP 動(dòng)作(這個(gè)代碼片斷在用戶的輸入鍵盤不能工作時(shí)也可以使用)。這個(gè)程序測(cè)試原語 DUP 、 EMIT 、 EXIT 、 C@ 、 >< 、 LIT 、 1+ 和 BRANCH ,也測(cè)試了幾級(jí)嵌套。但它不使用 DO …… LOOP ,因?yàn)檫@個(gè)結(jié)構(gòu)要正確工作通常比較難。當(dāng)這些代碼可以運(yùn)行后,你就會(huì)對(duì)自己的 Forth 模塊是否有效樹立一些信心。

接著測(cè)試其它的原語,其中 DO …… LOOP, UM/MOD, UM* 和 DODOES 必須嚴(yán)格,最后再加入高級(jí)定義。

閱讀源代碼!

如果你希望學(xué)習(xí)更多的 Forth 內(nèi)核工作原理和它的編寫方法,學(xué)習(xí)列表 2 。這個(gè)列表遵循了下面的一些 Forth 文檔表示格式:

WORD-NAME stack in -- stack out description

其中 WORD-NAME 是 Forth 可以識(shí)別的字,由于這些字經(jīng)常包含一些特殊的ASCII 字符,所以必須用一個(gè)近似的名字做為這個(gè)字的匯編語言標(biāo)號(hào),比如 OENPLUS 是字 1+ 的匯編語言標(biāo)號(hào)。

stack in 是這個(gè)字希望輸入的棧上參數(shù),最右邊總是棧頂元素, stack out 是這個(gè)字留在棧上的參數(shù)。

如果一個(gè)字影響返回棧,則會(huì)給出一個(gè)返回棧說明,用 R: 表示,比如:

stack in -- stack out R: stack in -- stack out

ANSI Forth 對(duì)數(shù)值堆棧參數(shù)給出了一個(gè)簡(jiǎn)化的表示,通常 n 是長(zhǎng)度為一個(gè)單元 CELL 的有符號(hào)數(shù), u 是長(zhǎng)度為一個(gè)單元的無符號(hào)數(shù), c 是一個(gè)字符,等等,見表 1 。

參考文獻(xiàn)

[1] Definition of a camel: a horse designed by committee.

[2] Ting, C. H., eForth Implementation Guide , July 1990, available from Offete Enterprises, 1306 South B Stret, San Mateo, CA 94402 USA.

[3] Z80MR, a Z80 Macro Assembler by Mike Rubenstein, is public-domain, available on the GEnie CP/M Roundtable as file Z80MR-A.LBR. Warning: do not use the supplied Z1.COM program, use only Z80MR and LOAD. Z1 has a problem with conditional jumps.

[4] A51, PseudoCorp's freeware Level 1 cross-assembler for the 8051, is available from the Realtime and Control Forth Board, (303) 278-0364, or on the GEnie Forth Roundtable as file A51.ZIP. PseudoCorp's commercial products are advertised here in TCJ.

Z80 CamelForth 的源代碼在下列站點(diǎn)上可用 ftp://ftp.zetetics.com/pub/forth/camel/cam80-12.zip .

第六部分 Z80 高級(jí)內(nèi)核

更正

在 TCJ#67 上發(fā)表的 CAMEL80.AZM 文件有兩個(gè)錯(cuò)誤。一個(gè)主要的錯(cuò)誤是 Forth 字 > 的宏定義名字頭長(zhǎng)度誤為 2 ,實(shí)際上應(yīng)該是 1 。另一個(gè)次要的錯(cuò)誤是 CP/M 的控制臺(tái) I/O 。 KEY 必須返回所打的字符,所以使用了 BDOS 功能 6 。 KEY?不能返回字符,使用 BDOS 功能 11 以測(cè)試當(dāng)前是否有鍵按下。不幸的是, BDOS 功能6不清除功能 11 檢測(cè)是按下的鍵。我現(xiàn)在重新編寫了 KEY? 以使用 BDOS 功能 6 。因?yàn)檫@是一個(gè)“破壞性”的測(cè)試,我就必須保持已經(jīng)“消耗”的鍵,并在下一次的 KEY 調(diào)用中返回。這個(gè)新的邏輯可以用于任何硬件只提供“破壞性”測(cè)試的場(chǎng)合。

高級(jí)定義

在上一次討論中,我沒有展開源代碼。每一個(gè)“原語”執(zhí)行一個(gè)小的、明確定義的功能。它幾乎全部是 Z80 匯編代碼,就算是我沒有說清楚為什么原語中包含了一個(gè)特別的字,我也希望讀者明白每一個(gè)字是做什么的。在這一部分里,我可就不能這樣“奢華”了:我將要給出 Forth 語言的邏輯。許多書中描述了 Forth 內(nèi)核,如果你希望完全掌握它,就請(qǐng)去買上一本。對(duì)于 TCJ 我將限制自己只給出編譯器和解釋器的關(guān)鍵字和給出清單 2.

文本解釋操作

文本解釋器或者稱為“外層解釋器”是一些從鍵盤上接收輸入并執(zhí)行所要求 Forth 操作的 Forth 代碼(這與地址或者“內(nèi)層解釋”器 NEXT 不同,后者執(zhí)行編譯之后的串線代碼)。理解這些代碼的最好方式是看 Forth 系統(tǒng)的啟動(dòng)。

CP/M 入口點(diǎn)(參看上一部分)檢測(cè)可用內(nèi)存的頂部,設(shè)置堆棧指針( PSP 、 RSP )和用戶指針( UP ),建立如圖 1 所示的存儲(chǔ)器映象,然后設(shè)置“內(nèi)層”解釋器指針( IP )以執(zhí)行 Forth 字 COLD 。

圖 1 Z80 CP/M CAMELForth 儲(chǔ)器映象

?

COLD 通過啟動(dòng)表初始化用戶變量,然后執(zhí)行字 ABORT 。(COLD 也試圖從 CP/M 命令行執(zhí)行 Forth 命令)。

ABORT 復(fù)位參數(shù)棧指針并執(zhí)行 QUIT 。

QUIT 復(fù)位返回棧指針、 LOOP 棧指針、解釋狀態(tài)、然后開始執(zhí)行命令(之所以要把名字這樣進(jìn)行區(qū)別是因?yàn)?QUIT 可以用于退出應(yīng)用程序,并返回到 Forth 的頂層。不像 ABORT, QUIT 保留了參數(shù)棧的內(nèi)容)。

QUIT 是一個(gè)無限循環(huán),它從鍵盤 ACCEPT 一行輸入,然后作為 Forth 命令調(diào)用 INTERPRET 。當(dāng)沒有編譯錯(cuò)誤時(shí), QUIT 在每一行之后打印一個(gè) Ok.

INTERPRET 幾乎是 ANS Forth 文檔 3.4 部分所給算法的逐字翻譯。它分析一個(gè)由空格分開的輸入串,試著用 FIND 把字串對(duì)應(yīng)一個(gè)已經(jīng)定義的 Forth 字。如果找到了一個(gè)字,則字或者被執(zhí)行(如果這是一個(gè) IMMEDIATE 立即字,或者是處于 STATE = 0 的“解釋狀態(tài)”)或者編譯到字典(如果在編譯狀態(tài), STATE<>0 )。如果沒有找到, INTERPRET 就試著把字串編譯成數(shù)字。如果成功, LITERAL 或者把它放到參數(shù)棧(如果在“解釋狀態(tài)”)或者編譯成一個(gè)在線文字量(如果在編譯狀態(tài))。如果這不是一個(gè) Forth 字也不是一個(gè)合法的數(shù),就顯示一個(gè)錯(cuò)誤信息,解釋器執(zhí)行 ABORT ,這個(gè)過程將一個(gè)字串一個(gè)字串地重復(fù),直到輸入行的結(jié)尾。

Forth 字典

那么,解釋器如何通過名字“找到”一個(gè) Forth 字呢?答案是: Forth 維護(hù)一個(gè)含有所有 Forth 名字的字典。每個(gè)名字都通過某種方式與它的可執(zhí)行代碼相關(guān)聯(lián)。

有許多辦法可以保存用于查找的名字串:一個(gè)簡(jiǎn)單的數(shù)組,一個(gè)鏈表,多重鏈表, HASH 表等。幾乎所有方法的都可以使用 -- Forth 的全部要求只是:如果你所查找的項(xiàng)目重名,那么最后定義的名字需要最先被找到。

我們也可以擁有幾個(gè)名字的集合(在新的 ANSI Forth 中,把這種集合稱為“詞匯表”)。這就允許你在不丟失一個(gè)名字原來意義的情況下再用這個(gè)名字。例如,你可以有一個(gè)整數(shù)的 + ,一個(gè)浮點(diǎn)的 + ,甚至是一個(gè)字符串的 + 。這在面向?qū)ο蟮南到y(tǒng)中被稱為“運(yùn)算符重載”。

每個(gè)字符串可以與它的可執(zhí)行代碼通過鄰近的物理存儲(chǔ)器相聯(lián)系 -- 比如,名字在可執(zhí)行代碼之前,這通常被稱為 Forth 字的首部。字符串也可以集中存放在不同的存儲(chǔ)器區(qū)域中,與可執(zhí)行代碼通過指針連接(這種情況稱為“分離的首部”)。

你甚至可以有無名的 Forth 代碼片斷,只要你永遠(yuǎn)不需要找到它們或者解釋它們。 ANSI 只要求 ANS Forth 字是可以找到的。

關(guān)于字典的設(shè)計(jì)策略可以寫成另一篇論文。 CAMEL Forth 使用的是最簡(jiǎn)單的策略:一個(gè)簡(jiǎn)單鏈表,定位在可執(zhí)行代碼之前。沒有字匯表,也許我可以在以后的 TCJ 論文中加入這一能力。

字的首部結(jié)構(gòu)

這里還有一個(gè)問題需要討論:在首部中需要什么數(shù)據(jù)?如何存儲(chǔ)它們?

最少的數(shù)據(jù)是名字、優(yōu)先位、(顯式的或者隱式的)到可執(zhí)行代碼的指針。為了簡(jiǎn)單起見, CAMEL Forth 把名字作為一個(gè)“計(jì)數(shù)字符串”存儲(chǔ)(一個(gè)字節(jié)的長(zhǎng)度,后面是 N 個(gè)字符)。早期的 Forth Inc. 產(chǎn)品只存儲(chǔ)名字串的長(zhǎng)度和前 3 外字符。 FIG-Forth 使用不同的緊縮方法,用最后一個(gè)字符的 MSB 位置 1 來標(biāo)識(shí)名字的最后一個(gè)字符,而不需要長(zhǎng)度字節(jié),其它的 Forth 系統(tǒng)也使用了緊縮字符串,我想甚至 C 風(fēng)格的 NULL 字符串也是可以使用的。

“優(yōu)先位”是一個(gè)標(biāo)志,用于指示這個(gè)字是一個(gè)立即字( IMMEDIATE ),這種字在編譯時(shí)也被執(zhí)行,以實(shí)現(xiàn) Forth 的編譯指示和控制結(jié)構(gòu)。也有其它方法實(shí)現(xiàn)編譯指示,例如,可以把它們放在一個(gè)單獨(dú)的字典中,等等。許多 Forth 系統(tǒng)直接把這個(gè)位保存在長(zhǎng)度字節(jié)中。我使用了一個(gè)分離的字節(jié),這樣可以把一個(gè)“通?!钡拇僮鞣糜谧执拿植僮?#xff08;比如在 FIND 中的 S= 和 WORDS 中的 TYPE )。

如果把名字保存在一個(gè)鏈表中,就需要有一個(gè)鏈。通常最后的字是在鏈表的前面,而鏈指向前一個(gè)字。這符合 ANSI (和大多數(shù)系統(tǒng))對(duì)重定義字的要求。 Charles Curley 研究了 LINK 域的放置位置,發(fā)現(xiàn)如果把這個(gè)域放置在名字之前(而不是像 FIG-Forth 那樣在名字之后),由可以加快編譯的速度。

圖 2 是 CAMELForth 字首部結(jié)構(gòu),并與 FIG-Forth F83 Pygmy Forth 系統(tǒng)的首部做了比較。 F83 和 Pyhmy 的 "VIEW" 字段可以作為一個(gè)例子,它說明了如何把其它有用的信息保存在 Forth 首部中。

注意:把一個(gè)“頭”(首部)和“體”(可執(zhí)行代碼部分)區(qū)別開來是非常重要的。它們并不需要存儲(chǔ)在一起。首部只是在編譯和解釋時(shí)才需要,一個(gè)“純的可執(zhí)行”Forth 系統(tǒng)并不需要全部的首部。但是,對(duì)于一個(gè)合法的 ANSI Forth 系統(tǒng)來說,首部必須存在 -- 至少是 ANSI Forth 字集中的那些字必須有首部。

當(dāng)從匯編代碼“編譯”一個(gè) Forth 系統(tǒng)時(shí),你可以定義宏來構(gòu)建這個(gè)首部(參看 CAMELZ80.AZM 的 HEAD IMMED )。在 Forth 環(huán)境中,首部和代碼域都是由 CREATE 構(gòu)建的。

編譯操作

我們已經(jīng)具備了理解 Forth 編譯器的足夠知識(shí)。字 :開始一個(gè)新的高級(jí)字定義,首先為字創(chuàng)建一個(gè)頭(CREATE),改變它的代碼域到“DOCOLON”(!COLON),然后轉(zhuǎn)為編譯狀態(tài)。

回想一下,在編譯狀態(tài)下,文本解釋器遇到的每一個(gè)字都編譯進(jìn)字典而不是立即執(zhí)行。這個(gè)過程一直繼續(xù),直到文本解釋器遇到了字“;”。這個(gè)字是一個(gè)立即字,它被立即執(zhí)行,編譯一個(gè) EXIT 到定義的結(jié)尾,然后切換到解釋狀態(tài)([) 。

同時(shí),“:”隱藏這個(gè)新字,而“;”把這個(gè)新字顯示出來(通過清除首部或者名字中的 smudge 位),這就允許 Forth 字可以按“自我優(yōu)先級(jí)”的方式重新定義。為了強(qiáng)制使這個(gè)被定義的字遞歸調(diào)用,需要使用字 RECURSE 。

我們可以看到, Forth“編譯器”與 C 或者 PASCAL 編譯器并沒有區(qū)別。 Forth 編譯器包含有不同F(xiàn)orth 字的動(dòng)作,這就使得改變或者擴(kuò)充編譯器變得很容易,但如果沒有一個(gè)“內(nèi)建”的編譯器,則創(chuàng)建一個(gè) Forth 應(yīng)用就會(huì)特別困難。

相關(guān)的字集

還有許多其它的 Forth 字,它們是:

實(shí)現(xiàn)編譯器或者解釋器的需要,或者

提供編程的方便性

但是有一個(gè)字集應(yīng)該引起特別的重視,那就是我放入文件 CAMEL80D.AZM 的那些字。

ANSI Forth 標(biāo)準(zhǔn)的一個(gè)目標(biāo)就是向應(yīng)用程序員隱藏 CPU 和相關(guān)的實(shí)現(xiàn)模型(直接或者間接串線、 16 位還是 32 位)。為了實(shí)現(xiàn)這個(gè)目的,需要向標(biāo)準(zhǔn)增加了幾個(gè)字。我把這個(gè)要求更向前推動(dòng)了一步,努力把這些模型相關(guān)問題包裝到內(nèi)核中。在理想的情況下,在文件 CAMEL80H.AZM 中的高級(jí) FORH 代碼對(duì)于所有的 CAMEL Forth 目標(biāo)應(yīng)該是相同(盡管不同的匯編程序會(huì)有不同的語法)。

單元尺寸的差異和字的對(duì)齊要求由 ANS Forth 字 ALIGN ALIGNED CELL+ CELLS CHAR+ CHARS 和我自己附加的字 CELL ( 等效于 1 CELLS, 但編譯之后會(huì)更小 ) 來管理。

字 COMPILE 、 !CF 、 CF 、 !COLON 和 EXIT 隱藏了串線模型的特性,比如:串線是如何表示的、代碼域是如何實(shí)現(xiàn)的。

當(dāng)你研究 Z80 直接串線和 8051 子程序串線時(shí),這些字的值就變得非常明顯:

按同樣的風(fēng)格,字 ,BRANCH 、 ,DEST 和 !DEST 隱藏了高級(jí)分支和循環(huán)操作符的實(shí)現(xiàn)。我試著發(fā)明 -- 不借用現(xiàn)有的 Forth 系統(tǒng) -- 最少的操作符集合,它可以因子化實(shí)現(xiàn)的差異。只有時(shí)間、專家評(píng)判和許多 CAMEL Forth 才可以說明我在這方面取得了多少成功。

到目前為止,我并沒有成功地把首部結(jié)構(gòu)中的不同因子化到相似的字集。FIND 和 CREATE 是和首部?jī)?nèi)容緊密相連的,我還沒有找到合適的子因子。我已經(jīng)開始了這方面的努力,通過字 NFA>LFA NFA>CFA IMMED? HIDE REVEAL 和 ANS Forth 字 >BODY IMMEDIATE. ,我將繼續(xù)這一工作。值得欣慰的是,現(xiàn)在可以把同樣的首部結(jié)構(gòu)用于所有的 CAMEL Forth 實(shí)現(xiàn)(因?yàn)樗鼈兌际亲止?jié)方式尋址的16位 Forth 系統(tǒng))

接下來我將要給出一個(gè) 8051 內(nèi)核,并說明 Forth 編譯器和解釋器是如何用于 哈佛體系結(jié)構(gòu)的(這種系統(tǒng)結(jié)構(gòu)的計(jì)算機(jī)把存儲(chǔ)器邏輯地分成代碼和數(shù)據(jù)兩個(gè)部分,比如 8051)。對(duì)于 8051 ,我會(huì)給出文件 CAMEL51 和 CAMEL51D ,但是沒有 CAMEL51H ,因?yàn)槌藚R編語言格式外,高級(jí)代碼不會(huì)與我這里討論的有什么差異,而本刊的編輯也需要發(fā)表其它的文章。好在所有的代碼都是可以下載的。

Link – 在 CamelForth 和 Fig-Forth 中,指向前一個(gè)字的長(zhǎng)度字節(jié)。在 Pygmy Forth 和 F83, 中,指向前一個(gè)字的 LINK 。

P – 優(yōu)先位,如果是 1 則為立即字,在 Pygmy 中沒有使用。

S - Smudge 位,阻止 FIND 找到這個(gè)字

1 – 在 Fig-Forth 和 F83 中,長(zhǎng)度字節(jié)和名字和最后一個(gè)字符的最高有效位(位 7 )用 1 來標(biāo)識(shí)

View – 在 Pygmy Forth 和 F83 中,是這個(gè)字所在源碼塊的編號(hào)

參考文獻(xiàn)

1. Derick, Mitch and Baker, Linda, Forth Encyclopedia , Mountain View Press, Route 2 Box 429, La Honda, CA 94020 USA (1982). Word-by-word description of Fig-Forth.

2. Ting, C. H., Systems Guide to fig-Forth , Offete Enterprises, 1306 South B Street, San Mateo, CA 94402 USA (1981).

3. Ting, C. H., Inside F83 , Offete Enterprises (1986).

4. Ewing, Martin S., The Caltech Forth Manual , a Technical Report of the Owens Valley Radio Observatory (1978). This PDP-11 Forth stored a length, four characters, and a link in two 16-bit words.

5. Sergeant, Frank, Pygmy Forth for the IBM PC , version 1.4 (1992). Distributed by the author, available from the Forth Interest Group (P.O. Box 2154, Oakland CA 94621 USA) or on GEnie.

6. J. E. Thomas examined this issue thoroughly when converting Pygmy Forth to an ANSI Forth. No matter what tricks you play with relinking words, strict ANSI compliance is violated. A regrettable decision on the part of the ANS Forth team.

7. In private communication.

The source code for Z80 CamelForth is now available on GEnie as CAMEL80.ARC in the CP/M and Forth Roundtables. Really. I just uploaded it. (Apologies to those who have been waiting.)

Z80 CamelForth 的源代碼可以從下列站點(diǎn)上得到 ftp://ftp.zetetics.com/pub/forth/camel/cam80-12.zip .

第七部分 8051 的 Camel Forth

在我們尊敬的編輯要求下,我給出了 8051 的 CAMEL Forth ,而用于 MC6809 的 Forth 也將很快完成。這個(gè) 8051 Forth 占用 6K 字節(jié)的程序存儲(chǔ)器。不過,全部的源代碼將占 TCJ 的 16 頁,所以這篇文章只給出了核心移植過程中的主要變化。我們將解釋高級(jí)代碼是如何按 8051 匯編器格式要求和子程序串線技術(shù)而修改的。

Z80 更正

在文件 CAMEL80H.AZM 中, DO 的定義是這樣給出的

['] xdo ,BRANCH . . .

它應(yīng)該是

['] xdo ,XT . . .

這是由于在 Z80 上沒有 consequence ( 在那里, ,BRANCH 和 ,XT 是等價(jià)的 ), 但在 8051 上,它是明顯的。

8051 CAMEL Forth 模型

在 #60 論文中,我匯總了 8051 Forth 的設(shè)計(jì)方法。再次說明: 8051 反映遲鈍的存儲(chǔ)器尋址實(shí)際上要求使用子程序串線。這就意味著硬件堆棧(在 8051 的寄存器文件中)就是返回棧。參數(shù)棧(也就是數(shù)據(jù)棧)在 256 字節(jié)的外部 RAM 中,使用 R0 作為這個(gè)堆棧的指針。從那篇文章開始,我發(fā)現(xiàn)了把棧頂元素( TOS )放到 DPTR 中比放在 R3 : R2 中更好。于是就有了這樣的程序員模型

這其中也包含了 Charles Curley [CUR93] 的思想。在像 8051 這樣寄存器豐富的機(jī)器上,我們可以把內(nèi)層循環(huán)索引放在寄存器中,以使得 LOOP 和 +LOOP 更快。 DO 必須向返回棧壓入兩個(gè)值:舊的循環(huán)索引和新的循環(huán)終值。 UNLOOP 當(dāng)然需要從返回棧得到循環(huán)索引 -- ANSI 把 UNLLOP 做為一個(gè)單獨(dú)的詞。注意 R6:R7 不是返回棧的棧頂元素,它只是內(nèi)層循環(huán)的索引。

P2 含有參數(shù)棧指針的高字節(jié)(允許 R0 尋址外部存儲(chǔ)器),它也是用戶指針的高字節(jié) -- UP 的低字節(jié)假設(shè)為 00 。我費(fèi)了很大的勁才明白當(dāng)從外部 ROM 執(zhí)行時(shí), P2 是不能讀的,所以我保存了一份 P2 的拷貝在寄存器 8 中。

對(duì) BRANCH 和 ?BRANCH 我有一個(gè)非常好的實(shí)現(xiàn)方法。由于 8051 模型是子程序串線,高級(jí) Forth 作為真正的機(jī)器代碼來編譯,所以 BRANCH 可以用一個(gè) SJMP (或者 AJMP 或者 LJMP )指令實(shí)現(xiàn)。 ?BRANCH 可以用一個(gè) JZ 指令實(shí)現(xiàn),只要 TOS 的零 / 非零標(biāo)志已經(jīng)放到了累加器中( A 寄存器)。用一個(gè)子程序 ZEROSENSE 做這個(gè)工作,所以 BRANCH 和 ?BRANCH 就變成了:

BRANCH:

SJMP dest

?BRANCH:

LCALL ZEROSENSE JZ dest

與此相似, LOOPSENSE 和 PLUSLOOPSENSE 允許 JZ 指令使用 LOOP 和 +LOOP 。對(duì)于這些情況,在 JZ 之后應(yīng)該調(diào)用 UNLOOP 以清除程序“FALLS OUT”循環(huán)時(shí)的返回棧。

在匯編語言源文件中的許多地方,我手工把 LCALL word RET 用更短更快的字 LJMP word替換 ,只要“字”不是一個(gè)返回棧操作符(比如 R> 或者 >R )。而只要有可能,字 LCALL 和 LJMP 就用 ACALL 和 AJMP 替代。

我用 Intel 字節(jié)順序?qū)懥?8051 內(nèi)核(低字節(jié)在先),之后我發(fā)現(xiàn)編譯到 LJMP 和 LCALL 的地址是高字節(jié)在先。為了避免重寫整個(gè)內(nèi)核,我為這些編譯 LCALLS 的字包含了一個(gè)字節(jié)交換的字: COMPILE, !CF 和 ,CF ( 它們都是Dependency 字匯集 ).

哈佛體系結(jié)構(gòu)

8051 使用哈佛體系結(jié)構(gòu),程序和數(shù)據(jù)保存在分開的存儲(chǔ)器中。在嵌入式系統(tǒng)中,它們分別是 ROM 和 RAM 。 ANS Forth 是第一個(gè)能夠適應(yīng)哈佛體系結(jié)構(gòu)限制的標(biāo)準(zhǔn)。簡(jiǎn)單地說, ANS Forth 規(guī)定:

?? 應(yīng)用程序只能夠訪問數(shù)據(jù)存儲(chǔ)器,同時(shí)

?? 所有訪問存儲(chǔ)器和構(gòu)造數(shù)據(jù)結(jié)構(gòu)的操作符必須在數(shù)據(jù)空間操作。

( 參看 ANS 文檔 section 3.3.3 [ANS94].) 包括下列 Forth 字: @ ! C@ C! DP HERE ALLOT , C, COUNT TYPE WORD (S") S" CMOVE

然而, Forth 編譯器還需要訪問程序空間(也稱為代碼或者指令空間)。 Forth 需要為程序空間和數(shù)據(jù)空間維護(hù)一個(gè)字典指針。所以我增加以下這些新的字:

I@ I! IC@ IC! IDP IHERE IALLOT I, IC, ICOUNT ITYPE IWORD (IS") IS" D->I I->D

這里前綴“I”表示指令(因?yàn)?P 和 C 在 Forth 中已經(jīng)有了其它的意義)。 ICOUNT 和 ITYPE 用于顯示已經(jīng)被編譯到 ROM 中的串。 IWORD 從數(shù)據(jù)空間復(fù)制 WORD 留下的字到代碼空間 -- 用于構(gòu)造 Forth 字頭和放到 ROM 中的串。 D->I 和 I->D 是與 CMOVE 等效的,它從 / 向代碼空間復(fù)制。

VARIABLE 必須定位到數(shù)據(jù)空間。所以它們不能使用傳統(tǒng)的辦法把數(shù)據(jù)緊接著代碼域存放。這里的方法是:在數(shù)據(jù)空間中數(shù)據(jù)的地址存放在代碼域之后?;旧?#xff0c;一個(gè) VARIABLE 就是一個(gè) CONSTANT ,它的值就是數(shù)據(jù)空間的地址。 ( 當(dāng)然,傳統(tǒng)的 CONSTANT 依然有效 )

CREATE 字,以及使用 CREATE …… DOES> 創(chuàng)建的字,必須按同樣的方式工作。以下是它們?cè)诔绦蚩臻g看起來的樣子:

CODE word: ...header... 8051 machine code

high-level: ...header... 8051 machine code

CONSTANT: ...header... LCALL-DOCON value

VARIABLE: ...header... LCALL-DOCON Data-adrs

CREATEd: ...header... LCALL-DOCON Data-adrs

注意 CONSTANT 必須替換 CREATE 存入的值,:必須 "un-allot" 所有這些值和 LCALL DOCON 。

S" 有特殊的問題。使用 S" 定義的串(“文本常數(shù)”)必須駐留在數(shù)據(jù)空間,在那里它們可以被 TYPE 和 EVALUATE 這樣一些字使用。但是我們希望這些字是定義的一部分,并且在 ROM Forth 環(huán)境中能夠駐留在 ROM 里。我們可以把字符串存儲(chǔ)在程序空間,引用的時(shí)候復(fù)制到 HERE ,但是 ANS 文檔不允許文本常數(shù)存在于這個(gè)“臨時(shí)”的存儲(chǔ)區(qū)域(參看 ANS 文檔 sections 3.3.3.4 和 3.3.3.6 [ANS93]) 。同時(shí),如果 WORD 把它的串地址在 HERE中返回 -- 就像 CAMEL Forth -- 則文本常數(shù)不能改變這個(gè)臨時(shí)區(qū)域。

我的解決方案是 S" 存儲(chǔ)串到代碼空間,但是也在數(shù)據(jù)空間為它永久地保留位置,當(dāng)引用時(shí),把它從代碼空間復(fù)制到這個(gè)數(shù)據(jù)空間。 ANS Forth 并沒有解決 哈佛體系結(jié)構(gòu)處理器的全部問題,有時(shí)像 C 一樣的“初始化數(shù)據(jù)區(qū)”可能也是需要的。

因?yàn)?." 從來也不能被程序員使用,它們可以存儲(chǔ)在代碼空間中,方法是使用字 (IS") 和 IS" 。 ( 它們是 " 老 " 的 (S") 和 S".) 。雖然內(nèi)核增加了兩個(gè)字,但是節(jié)省了許多數(shù)據(jù)空間。我計(jì)劃把有關(guān)串常數(shù)的字集中到 Dependency 字匯集,或者建立一個(gè)新的“HARDVARD”字匯集。

寫入程序空間

8051 并不能真正地寫入程序存儲(chǔ)器,沒有硬件信號(hào),也沒有硬件指令。在這種環(huán)境下, CAMELForth 解釋器可以工作,但是不能編譯新的字。我們可以設(shè)法讓某些存儲(chǔ)器同時(shí)出現(xiàn)在程序和數(shù)據(jù)空間。許多 8031 應(yīng)用說明給出了同時(shí)訪問數(shù)據(jù)和程序空間的方法,在硬件上通過組合一些信號(hào)實(shí)現(xiàn)。圖 1 給出了我對(duì)電路板的修改,這個(gè)電路板是 Blue Ridge Micros (2505 Plymouth Road, Johnson City, TN, 37601, USA, telephone 615-335-6696, fax 615-929-3164) 的 MCB8031. U1A 和 U1B 產(chǎn)生一個(gè)新的選通信號(hào),只要程序或者數(shù)據(jù)讀一個(gè)有效時(shí)就可以 EPROM 在 A15 為低時(shí)被選擇 ( 低 32K), RAM 在 A15 為高時(shí)有效 ( 高 32K) 。 當(dāng)然,你不能寫入 EPROM ,但是你可以從 RAM 中執(zhí)行程序!有一個(gè)缺點(diǎn): : 這使得 @ 和 I@ 等效,如果你在什么地方用錯(cuò)了它們,則并不能馬上發(fā)現(xiàn)。

圖 1 修改的 8051 電路圖

這些高級(jí)定義字修改的目的是實(shí)現(xiàn)對(duì) CAMEL Forth 在哈佛體系結(jié)構(gòu)和馮諾曼 體系結(jié)構(gòu)機(jī)器之間移植。對(duì)于后者,新的程序空間字可以簡(jiǎn)單地對(duì)應(yīng)到數(shù)據(jù)空間字,比如對(duì)于 Z80

IFETCH EQU FETCH

ISTORE EQU STORE

ITYPE EQU TYPE

等等

在下一篇文章中,我將要修改 8051 源代碼,使它能在 6809 上工作,這是一個(gè)通過不斷改進(jìn)而得到的真正可移植模型。

參考文獻(xiàn)

[ANS93] dpANS-6 draft proposed American National Standard for Information Systems - Programming Languages - Forth , June 30, 1993. "It is distributed solely for the purpose of review and comment and should not be used as a design document. It is inappropriate to claim compatibility with this draft standard." Nevertheless, for the last 16 months it's all we've had to go by.

[CUR93] Curley, Charles, Optimization Considerations , Forth Dimensions XIV:5 (Jan/Feb 1993), pp. 6-12.

8051 CamelForth 的源代碼可以從下列站點(diǎn)上得到 ftp://ftp.zetetics.com/pub/forth/camel/cam51-15.zip

第八部分 MC6809 Camel Forth

現(xiàn)在,我們將給出本文的最后部分,也就是許諾已久的 MOTOROLA 6809 ANSI CAMEL Forth 。這個(gè)實(shí)現(xiàn)是專門為 Scroungmaster II 處理器板而設(shè)計(jì)的。

與 Z80 和 8051 的 CAMELForth 不同, MC6809 Forth 是用我的“Chromium 2 Forth MATA 編譯器”生成的。你可以看到兩件事:

?? 首先、 MATA 編譯器在一個(gè)老的 Forth 系統(tǒng)上運(yùn)行(F83),所以源代碼中含有 16 x 64 的 Forth“SCREEN”。我試著把它轉(zhuǎn)為 ASCII 文件,但是原始的痕跡還是很明顯;

?? 第二、用于 META 編譯器的源代碼看起來很像一般的 Forth 代碼(我馬上就要討論,有一些小的變化),這樣,關(guān)于 1+ 的定義就變成了:

CODE 1+ 1 # ADDD, NEXT ;C

匯編器使用的是我以前討論過的 MC6809 匯編器。

我直接照著已經(jīng)出版的列表打入高級(jí)源代碼(轉(zhuǎn)換到 Forth 語法)。不幸的是,由于這中間隔了很長(zhǎng)時(shí)間,并且我有時(shí)參照 Z80 列表、有時(shí)又參照 8051 列表……結(jié)果是 HARVARD 體系結(jié)構(gòu)構(gòu)造字(比如 I@ IALLOC )沒有堅(jiān)持用在 MC6809 中。這對(duì)于非 HARVARD 結(jié)構(gòu)的 MC6809 并不重要,但是如果要把 Forth 代碼用于 HARVARD 結(jié)構(gòu),我就不得不再修改這些錯(cuò)誤。

另外,由于我是在已經(jīng)出版的列表基礎(chǔ)上工作的,我常常忘了給高級(jí)字定義寫上詳細(xì)的說明,不過,你可以從原來的列表中知道它們是如何工作的,當(dāng)然,我并不強(qiáng)制你這樣做。

MC6809 CAMEL Forth 的源代碼說明

MC6809 CAMEL Forth 模型把 TOS 放到 D 寄存器中,把S 棧指針用于參數(shù)棧, U 指針用于返回棧, Y 是解釋指針。 X 是 W 寄存器的臨時(shí)寄存器。 MC6809 直接頁指針 DPR 保存用戶指針的高字節(jié)(低字節(jié)假設(shè)是 0 )。

Scroungemaster II 單板上的 8K RAM 和 8K EPROM 按以下地址映象:

6000-797Fh RAM 字典 ( 用于新定義 )

7980-79FFh 終端輸入緩沖區(qū)

7A00-7A7Fh User 區(qū) (USER 變量 )

7A80-7AFFh 參數(shù)棧 ( 向下增長(zhǎng) )

7B00-7B27h HOLD 區(qū) ( 向下增長(zhǎng) )

7B28-7B7Fh PAD 區(qū) ( 通用緩沖區(qū) )

7B80-7BFFh 返回棧 (grows downward)

E000-FFFFh EPROM 中的 Forth 內(nèi)核

所有的 RAM 數(shù)據(jù)區(qū)通過用戶指針引用,它的開始地址是 UP-INIT :在我們這里是 7A00H (注意這個(gè)字的高字節(jié)和 UP-INIT-HI 的使用)。當(dāng) CAMEL Forth 開始的時(shí)候,它會(huì)把字典指針設(shè)置到 DP-INIT ,而且必須在 RAM 中,這樣你就可以向 Forth 字典中加入一個(gè)新的定義。這些都是由 META 編譯器的 EQU 指令指定的。這些 EQU 指令并不占用 MC6809 的核心空間,它們也不會(huì)出現(xiàn)在 MC6809 的 Forth 字典中。

DICTIONARY 告訴 MATA 編譯器在哪里編譯代碼,在我們的情況下是 E000-FFFFH 的 8K EPROM ,新的字典命名為 ROM ,然后 ROM 被指定到所選定的字典。(如果你熟悉 Forth 的詞匯表,你就會(huì)看到很強(qiáng)的相似性)。

字 AKA 定義一個(gè) Forth 字的同義詞。因?yàn)?MC6809 不是一個(gè)哈佛體系結(jié)構(gòu)計(jì)算機(jī),我們應(yīng)該把所有在源代碼中出現(xiàn)的 I@ 編譯成 @ ,其它的“帶 I 前綴”(指令空間)字也做同樣的處理。 AKA 將完成這個(gè)工作。這些同義詞像 EUQ 一樣,它們不出現(xiàn)在 MC6809 的字典中。

MATA 編譯器允許你使用前向引用,就是訪問那些還沒有定義的 Forth 字(你當(dāng)然需要在全部完成之前定義它們!)。這通常是自動(dòng)的,但是 AKA 要求你使用 PRESUME 明確地說明,比如:

PRESUME WORD AKA WORD IWORD

用于創(chuàng)建 IWORD 的同義詞。 @ ! HERE ALLOT 是 META 編譯器自動(dòng)定義的,我們不需要對(duì)這些詞使用 PRESUME 。

CODE 定義非常方便。注意你可以使用:

HERE EQU labelname

在 META 編譯中產(chǎn)生一個(gè)標(biāo)號(hào),(這是 META 編譯器的一個(gè)功能而不是匯編器的功能)。另外, ASM: 開始一個(gè)匯編代碼片段(也就是說,這不是一個(gè) CODE 字的一部分)。

下面的短語

HERE RESOLVES name

用于解決 META 編譯器使用的特定的前向引用(例如, MEATA 編譯器需要知道 DOCOLON 動(dòng)作的代碼在哪里)。你應(yīng)該使這些獨(dú)立。除此之外,你可以自由地在源代碼中加入 CODE 定義。

定義字和控制結(jié)構(gòu)(IMMEDIATE 立即字)的代碼更加難懂,其中的原因是這些字在 META 編譯期間也要執(zhí)行一些動(dòng)作。例如: MC6809 Forth 包含有標(biāo)準(zhǔn)字 COSNTANT 用于定義一個(gè)新的常數(shù)。但是許多 COSNTANT 定義也出現(xiàn)在 MC6809 內(nèi)核中。我們?cè)?META 編譯中也許需要定義一個(gè)新的 CONSTANT 。 EMULATE: 短語用于指示不同的 CONSTANT 沖突時(shí)如何動(dòng)作。這個(gè)短語是完全用 MEATA 編譯器字寫成的,所以看起來完全是含混不清。

與此類似, IF THEN 和其它同類的字包含 META 編譯短語用于構(gòu)造和解決 MC6809 映象的分歧。一些 META 編譯器把這些字隱藏在編譯器之中,這可以產(chǎn)生漂亮的目標(biāo)代碼,但是,如果你需要改變分支的方式,你就必須修改 META 編譯器。

我傾向于使這些動(dòng)作易于修改,所以我選擇 Chromium 放到目標(biāo)源代碼中。(最恐怖的例子是 TENDLOOP 和 TS" 的定義,它們實(shí)際是在目標(biāo)源代碼當(dāng)中擴(kuò)展了 META 編譯器的詞匯表。

如果你是一個(gè) Forth 和 META 編譯器的新手,最好的方法是接受這一切?!捌胀ā钡拿疤?hào)定義是很容易加入的,只需要參照 MC6809 其它部分源代碼就可以了。你甚至可以寫 CREATE …… DOES> 字義,只要你不在 META 編譯器中使用它們。

在一個(gè) 1MHz 的 MC6809 上,一行文本輸入需要明顯長(zhǎng)的時(shí)間去處理(粗略估計(jì)約為 1 秒鐘)。這其中的部分原因是由于解釋器的許多部分是使用高級(jí) Forth 編碼的,另一部分原因是 CAMEL Forth 使用了一個(gè)單鏈表結(jié)構(gòu)的字典。這些只影響編譯的速度而不會(huì)影響到執(zhí)行速度。

不過,延遲總是煩人的,也許有一天我會(huì)寫出一篇有關(guān)“加速 Forth”的論文。

現(xiàn)在,用戶指針 UP 不會(huì)改變。我們擁有一個(gè) UP 的目的是支持多任務(wù) -- 每個(gè)任務(wù)有它自己的用戶區(qū)、堆棧等等。我將很快針對(duì)這個(gè)問題開展工作。我也許會(huì)研究 SM II 的存儲(chǔ)器管理,為每個(gè)任務(wù)提供 32K 的私有字典。當(dāng)然,我會(huì)努力寫出一個(gè)真正的使用共享總線的多處理器 Forth 內(nèi)核。如果我活得足夠長(zhǎng),理所當(dāng)然地還應(yīng)該寫一個(gè)使用串行口的分布式 Forth 內(nèi)核。

MC6809 的 CAMEL Forth 版本 1.0 的源代碼在 GEnie 的 Forth Roundtable ,文件名稱為 CAM09-10.ZIP ,這個(gè)文件包含了 Chromium 2 meta 編譯器,是可以運(yùn)行的。只要有 F83 ,你就可以輸入:

F83 CHROMIUM.SCR

1 LOAD

BYE

這樣就可以裝入 META 編譯器,編譯MC6809 CAMEL Forth ,把結(jié)果寫入 Intel 格式的6809.HEX 中。注意:如果你使用的是 CP/M 或者 Atari ST 版本的 F83 ,則必須編輯 LOAD 屏幕以刪除 HEX 文件實(shí)用程序,因?yàn)檫@個(gè)程序只是為 MS-DOS 機(jī)而編寫的。我沒有測(cè)試 Chromium 2 使用的 CP/M 或者 Atari ST ,如果需要幫助,請(qǐng)與我聯(lián)系。

參考文獻(xiàn)

[ROD91] Rodriguez, B. J., "B.Y.O. Assembler," The Computer Journal #52 (Sep/Oct 1991) and #54 (Jan/Feb 1992).

[ROD92] Rodriguez, B. J., "Principles of Metacompilation," Forth Dimensions XIV:3 (Sep/Oct 1992), XIV:4 (Nov/Dec 1992), and XIV:5 (Jan/Feb 1993). Describes the "Chromium 1" metacompiler.

MC6809 CamelForth的源代碼可以從下列站點(diǎn)上得到 ftp://ftp.zetetics.com/pub/ forth /camel/cam09-10.zip .

總結(jié)

以上是生活随笔為你收集整理的Forth 系统实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

免费播放一区二区三区 | 日本一本二本三区免费 | 1000部啪啪未满十八勿入下载 | 免费中文字幕日韩欧美 | 亚洲va欧美va天堂v国产综合 | 亚洲综合久久一区二区 | 国产精品无码永久免费888 | 日日摸天天摸爽爽狠狠97 | 亚洲成av人在线观看网址 | 无码帝国www无码专区色综合 | 国产莉萝无码av在线播放 | 亚洲国产成人av在线观看 | 波多野42部无码喷潮在线 | www国产亚洲精品久久久日本 | 国产 浪潮av性色四虎 | 精品久久8x国产免费观看 | 一个人看的www免费视频在线观看 | 国产精品无套呻吟在线 | 国产精品怡红院永久免费 | 亚洲一区二区三区国产精华液 | 国产农村乱对白刺激视频 | 日本饥渴人妻欲求不满 | 欧美精品免费观看二区 | 国产一区二区三区影院 | 国产精品99久久精品爆乳 | 男人的天堂av网站 | 青草视频在线播放 | 亚洲欧美精品伊人久久 | 亚洲热妇无码av在线播放 | 国产成人无码一二三区视频 | 粉嫩少妇内射浓精videos | 免费无码的av片在线观看 | 国产美女极度色诱视频www | 国产精品无套呻吟在线 | 强奷人妻日本中文字幕 | 欧美人与动性行为视频 | 高清无码午夜福利视频 | 人人超人人超碰超国产 | 少妇激情av一区二区 | 色偷偷人人澡人人爽人人模 | 久久久久se色偷偷亚洲精品av | 97精品人妻一区二区三区香蕉 | 成人无码影片精品久久久 | 性欧美牲交xxxxx视频 | 欧美人妻一区二区三区 | 中文字幕乱码人妻二区三区 | 国产成人无码区免费内射一片色欲 | 丝袜足控一区二区三区 | 激情五月综合色婷婷一区二区 | 亚洲中文字幕无码中字 | 精品国产成人一区二区三区 | 18精品久久久无码午夜福利 | а√资源新版在线天堂 | 无码av中文字幕免费放 | 男女爱爱好爽视频免费看 | 天天爽夜夜爽夜夜爽 | 娇妻被黑人粗大高潮白浆 | 最近的中文字幕在线看视频 | 日韩亚洲欧美中文高清在线 | 色综合视频一区二区三区 | 一本大道久久东京热无码av | 欧美日本免费一区二区三区 | av无码电影一区二区三区 | 我要看www免费看插插视频 | 亚洲精品美女久久久久久久 | 狠狠色欧美亚洲狠狠色www | 亚洲 日韩 欧美 成人 在线观看 | 欧美黑人性暴力猛交喷水 | 美女张开腿让人桶 | 少妇无套内谢久久久久 | 国产又爽又猛又粗的视频a片 | 国产精品成人av在线观看 | 狂野欧美激情性xxxx | 免费无码一区二区三区蜜桃大 | 久久久国产一区二区三区 | 波多野结衣av在线观看 | 久久久久久久久蜜桃 | 国产亚洲人成在线播放 | 日本又色又爽又黄的a片18禁 | 免费视频欧美无人区码 | 色欲久久久天天天综合网精品 | 熟妇女人妻丰满少妇中文字幕 | 在线观看欧美一区二区三区 | 精品亚洲韩国一区二区三区 | 日日摸夜夜摸狠狠摸婷婷 | 一个人免费观看的www视频 | 欧美日本日韩 | 久久精品国产一区二区三区 | 内射白嫩少妇超碰 | 激情内射日本一区二区三区 | 国产免费久久精品国产传媒 | 兔费看少妇性l交大片免费 | 国产内射老熟女aaaa | 少妇性俱乐部纵欲狂欢电影 | 特级做a爰片毛片免费69 | 亚洲国产日韩a在线播放 | 久久亚洲中文字幕精品一区 | 无码人妻精品一区二区三区下载 | 亚洲成a人片在线观看无码3d | 国产偷自视频区视频 | 伊人久久大香线蕉av一区二区 | 中文字幕久久久久人妻 | a片免费视频在线观看 | 大肉大捧一进一出好爽视频 | 精品欧美一区二区三区久久久 | 欧美怡红院免费全部视频 | 日本一区二区更新不卡 | 无码中文字幕色专区 | 亚洲精品久久久久avwww潮水 | 亚洲日韩一区二区三区 | аⅴ资源天堂资源库在线 | 国产97在线 | 亚洲 | 免费无码一区二区三区蜜桃大 | 老熟妇乱子伦牲交视频 | 免费观看的无遮挡av | 亚洲一区二区三区偷拍女厕 | 丁香花在线影院观看在线播放 | 国产成人无码a区在线观看视频app | 国产精品99爱免费视频 | 久久久久se色偷偷亚洲精品av | 永久黄网站色视频免费直播 | 丁香啪啪综合成人亚洲 | 水蜜桃亚洲一二三四在线 | 三上悠亚人妻中文字幕在线 | 人人爽人人澡人人高潮 | 夜精品a片一区二区三区无码白浆 | 久久无码专区国产精品s | 日本一本二本三区免费 | 双乳奶水饱满少妇呻吟 | 亚洲国产精品毛片av不卡在线 | 天堂亚洲2017在线观看 | 亚洲日韩一区二区 | 国产亚洲欧美在线专区 | 中国女人内谢69xxxx | 久久久久se色偷偷亚洲精品av | 精品国产福利一区二区 | 内射白嫩少妇超碰 | 午夜精品久久久久久久久 | 久久精品人妻少妇一区二区三区 | 久久午夜夜伦鲁鲁片无码免费 | 人妻尝试又大又粗久久 | 日本饥渴人妻欲求不满 | 久久久久久a亚洲欧洲av冫 | 国产精品.xx视频.xxtv | 亚洲精品一区二区三区在线 | 亚洲成色在线综合网站 | 曰韩无码二三区中文字幕 | 奇米影视888欧美在线观看 | 欧美午夜特黄aaaaaa片 | 熟妇人妻中文av无码 | 精品国产麻豆免费人成网站 | 性欧美牲交xxxxx视频 | 欧美性黑人极品hd | 秋霞特色aa大片 | 亚洲欧美日韩综合久久久 | 九九在线中文字幕无码 | 国产艳妇av在线观看果冻传媒 | 国产熟女一区二区三区四区五区 | 亚洲成a人片在线观看无码 | 人妻中文无码久热丝袜 | 精品熟女少妇av免费观看 | 黄网在线观看免费网站 | 久久综合香蕉国产蜜臀av | 日日干夜夜干 | 久久久久成人片免费观看蜜芽 | 呦交小u女精品视频 | 最近的中文字幕在线看视频 | 国产在线无码精品电影网 | 国产成人亚洲综合无码 | 亚洲综合色区中文字幕 | 天堂а√在线地址中文在线 | 99久久精品日本一区二区免费 | 久在线观看福利视频 | 国产精品国产三级国产专播 | 日本精品人妻无码免费大全 | 中文字幕无码av波多野吉衣 | a在线观看免费网站大全 | 无码乱肉视频免费大全合集 | 特级做a爰片毛片免费69 | 成人毛片一区二区 | 麻花豆传媒剧国产免费mv在线 | 国产亚洲美女精品久久久2020 | 中文字幕乱妇无码av在线 | 狠狠亚洲超碰狼人久久 | 国产人妻久久精品二区三区老狼 | 亚洲理论电影在线观看 | 日日夜夜撸啊撸 | 日韩av无码一区二区三区 | 精品成人av一区二区三区 | 亚洲精品中文字幕 | 久久久久免费精品国产 | 日韩成人一区二区三区在线观看 | 特级做a爰片毛片免费69 | 丁香花在线影院观看在线播放 | 欧美老人巨大xxxx做受 | 久久久久成人精品免费播放动漫 | av无码不卡在线观看免费 | 亚洲毛片av日韩av无码 | 欧美国产亚洲日韩在线二区 | 国产人妻精品一区二区三区 | 曰本女人与公拘交酡免费视频 | 亚洲一区二区三区国产精华液 | 成人亚洲精品久久久久 | 亚洲色在线无码国产精品不卡 | 99久久精品国产一区二区蜜芽 | 荫蒂添的好舒服视频囗交 | 国产精品美女久久久网av | 桃花色综合影院 | 国产成人精品视频ⅴa片软件竹菊 | 帮老师解开蕾丝奶罩吸乳网站 | 久久国产精品精品国产色婷婷 | av无码电影一区二区三区 | 无码人妻丰满熟妇区毛片18 | 牲欲强的熟妇农村老妇女 | 午夜肉伦伦影院 | 天海翼激烈高潮到腰振不止 | 老太婆性杂交欧美肥老太 | 国产精品久免费的黄网站 | 国产精品无码一区二区桃花视频 | 欧洲欧美人成视频在线 | 国产激情综合五月久久 | 亚洲国产av美女网站 | 国产麻豆精品精东影业av网站 | 国内少妇偷人精品视频 | 综合激情五月综合激情五月激情1 | 黑人巨大精品欧美黑寡妇 | 好爽又高潮了毛片免费下载 | 精品日本一区二区三区在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 最近免费中文字幕中文高清百度 | 免费国产成人高清在线观看网站 | 亚洲成a人片在线观看无码 | 日本一本二本三区免费 | 欧美阿v高清资源不卡在线播放 | 国产午夜亚洲精品不卡 | 亚洲综合在线一区二区三区 | 永久免费观看美女裸体的网站 | 久久天天躁狠狠躁夜夜免费观看 | 久久国产精品_国产精品 | 无码福利日韩神码福利片 | 精品国产一区二区三区四区在线看 | 99久久99久久免费精品蜜桃 | 无码午夜成人1000部免费视频 | 国产无遮挡又黄又爽免费视频 | 樱花草在线社区www | 久久 国产 尿 小便 嘘嘘 | 性啪啪chinese东北女人 | 丁香花在线影院观看在线播放 | 欧美乱妇无乱码大黄a片 | 免费播放一区二区三区 | 中文字幕av日韩精品一区二区 | 国内少妇偷人精品视频免费 | 亚洲午夜久久久影院 | 中文字幕人妻无码一夲道 | 国产激情艳情在线看视频 | 久久99精品国产.久久久久 | 色欲av亚洲一区无码少妇 | 久久精品丝袜高跟鞋 | 人妻少妇精品视频专区 | 无码国产激情在线观看 | 无码人妻精品一区二区三区不卡 | 精品国产av色一区二区深夜久久 | 午夜福利不卡在线视频 | 午夜熟女插插xx免费视频 | 国产 浪潮av性色四虎 | 99久久精品午夜一区二区 | 国产亚洲人成a在线v网站 | 亚洲第一网站男人都懂 | 国内精品九九久久久精品 | 久久婷婷五月综合色国产香蕉 | 日韩精品无码一区二区中文字幕 | 男女下面进入的视频免费午夜 | 色婷婷欧美在线播放内射 | 欧美自拍另类欧美综合图片区 | 欧美性黑人极品hd | 精品国产青草久久久久福利 | 人人爽人人澡人人人妻 | 亚洲娇小与黑人巨大交 | 性欧美牲交xxxxx视频 | 亚洲va中文字幕无码久久不卡 | 亚洲日本一区二区三区在线 | 久久aⅴ免费观看 | 亚洲国产精品美女久久久久 | 久久综合九色综合欧美狠狠 | 亚洲综合精品香蕉久久网 | 亚洲国产精品久久人人爱 | 午夜福利不卡在线视频 | 亚洲色大成网站www | 在线欧美精品一区二区三区 | 成人免费视频在线观看 | 亚洲精品成人av在线 | 99久久精品国产一区二区蜜芽 | 欧美老人巨大xxxx做受 | 国产肉丝袜在线观看 | aⅴ在线视频男人的天堂 | 久久精品国产99久久6动漫 | 中文字幕无码热在线视频 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 亚洲精品久久久久中文第一幕 | 久久天天躁狠狠躁夜夜免费观看 | 无码纯肉视频在线观看 | 国色天香社区在线视频 | 亚洲色欲色欲天天天www | 日本成熟视频免费视频 | 免费男性肉肉影院 | 国产成人亚洲综合无码 | 激情内射亚州一区二区三区爱妻 | 免费看少妇作爱视频 | 久久精品国产日本波多野结衣 | 少妇被黑人到高潮喷出白浆 | 国产精品igao视频网 | 97久久国产亚洲精品超碰热 | 樱花草在线播放免费中文 | 国产真实乱对白精彩久久 | 美女黄网站人色视频免费国产 | 初尝人妻少妇中文字幕 | 成人一区二区免费视频 | 成人试看120秒体验区 | 午夜无码人妻av大片色欲 | 国内精品人妻无码久久久影院蜜桃 | 成人性做爰aaa片免费看不忠 | 草草网站影院白丝内射 | 老子影院午夜精品无码 | 特级做a爰片毛片免费69 | 久久国产精品萌白酱免费 | 国精品人妻无码一区二区三区蜜柚 | 久久午夜无码鲁丝片秋霞 | 久久99精品久久久久久 | 亚洲国产精品久久人人爱 | 国内少妇偷人精品视频免费 | 国产成人精品视频ⅴa片软件竹菊 | 久久综合九色综合欧美狠狠 | 无遮挡国产高潮视频免费观看 | 波多野结衣 黑人 | 丝袜美腿亚洲一区二区 | 日韩欧美中文字幕在线三区 | 国产麻豆精品一区二区三区v视界 | 欧美成人午夜精品久久久 | 国产免费久久久久久无码 | 嫩b人妻精品一区二区三区 | 久久精品国产一区二区三区肥胖 | 国产一精品一av一免费 | 澳门永久av免费网站 | 精品久久综合1区2区3区激情 | 久久久无码中文字幕久... | 午夜精品一区二区三区的区别 | 亚洲日本一区二区三区在线 | 亚洲精品国产a久久久久久 | 色窝窝无码一区二区三区色欲 | 久久伊人色av天堂九九小黄鸭 | 日本大香伊一区二区三区 | 无码帝国www无码专区色综合 | 性生交大片免费看女人按摩摩 | 久久99精品国产麻豆 | 人人妻人人澡人人爽欧美一区九九 | 无码国产乱人伦偷精品视频 | 欧美熟妇另类久久久久久不卡 | 天干天干啦夜天干天2017 | 成人免费无码大片a毛片 | 国产熟妇高潮叫床视频播放 | 精品久久久久香蕉网 | 亚洲码国产精品高潮在线 | 国产精品怡红院永久免费 | 色五月丁香五月综合五月 | 中文字幕乱妇无码av在线 | 国产suv精品一区二区五 | 一本久久a久久精品vr综合 | 精品国产aⅴ无码一区二区 | 一区二区三区乱码在线 | 欧洲 | 成人精品视频一区二区三区尤物 | 久久无码人妻影院 | 日韩欧美中文字幕公布 | 麻豆精品国产精华精华液好用吗 | av在线亚洲欧洲日产一区二区 | 欧洲精品码一区二区三区免费看 | 午夜精品久久久久久久 | 一个人看的www免费视频在线观看 | 亚洲熟妇自偷自拍另类 | 国产成人午夜福利在线播放 | 中文字幕人妻丝袜二区 | 色婷婷综合中文久久一本 | 全黄性性激高免费视频 | 亚洲va欧美va天堂v国产综合 | 久久久精品人妻久久影视 | 国产成人无码区免费内射一片色欲 | 国产亚洲tv在线观看 | 在线看片无码永久免费视频 | 国产成人综合在线女婷五月99播放 | 牲交欧美兽交欧美 | 娇妻被黑人粗大高潮白浆 | 人妻体内射精一区二区三四 | 日韩av无码一区二区三区 | 熟妇人妻激情偷爽文 | 夫妻免费无码v看片 | 一个人看的www免费视频在线观看 | 欧美 丝袜 自拍 制服 另类 | 国内揄拍国内精品少妇国语 | 久久久久人妻一区精品色欧美 | 欧美猛少妇色xxxxx | 国产精品怡红院永久免费 | 欧美激情一区二区三区成人 | 亚洲熟妇色xxxxx欧美老妇 | 久久久久成人片免费观看蜜芽 | а√天堂www在线天堂小说 | 麻豆精产国品 | 丰满肥臀大屁股熟妇激情视频 | 国产后入清纯学生妹 | 美女毛片一区二区三区四区 | 少妇性l交大片欧洲热妇乱xxx | 无码一区二区三区在线观看 | 亚洲精品国产品国语在线观看 | 国产69精品久久久久app下载 | 国产精品对白交换视频 | 亚洲精品国产品国语在线观看 | 久9re热视频这里只有精品 | 荫蒂添的好舒服视频囗交 | 理论片87福利理论电影 | 麻豆国产丝袜白领秘书在线观看 | 国产乱人伦偷精品视频 | 少妇无码av无码专区在线观看 | 又大又硬又黄的免费视频 | 亚洲一区av无码专区在线观看 | 久久亚洲中文字幕无码 | 人人爽人人爽人人片av亚洲 | 国内精品九九久久久精品 | 亚洲成av人在线观看网址 | www成人国产高清内射 | www国产亚洲精品久久久日本 | 亚洲s码欧洲m码国产av | 欧美国产亚洲日韩在线二区 | 欧美第一黄网免费网站 | 国产亚洲精品久久久久久大师 | 日产精品高潮呻吟av久久 | 久久久av男人的天堂 | 色婷婷香蕉在线一区二区 | 国产精品福利视频导航 | 无码成人精品区在线观看 | 丁香花在线影院观看在线播放 | 天天躁日日躁狠狠躁免费麻豆 | 国产精品-区区久久久狼 | 色欲久久久天天天综合网精品 | 精品国产成人一区二区三区 | 国产97色在线 | 免 | 国产亚洲精品久久久久久久 | 亚洲精品国产品国语在线观看 | 国产精品成人av在线观看 | www国产亚洲精品久久久日本 | 亚洲欧美日韩综合久久久 | 欧美xxxxx精品 | 亚洲精品一区二区三区在线观看 | 人妻少妇精品视频专区 | 人妻中文无码久热丝袜 | 强开小婷嫩苞又嫩又紧视频 | 亚洲人成影院在线观看 | 乱人伦中文视频在线观看 | 亚洲精品国产精品乱码不卡 | 国产精品va在线播放 | 国产精品第一国产精品 | 成人aaa片一区国产精品 | 熟女少妇在线视频播放 | 婷婷色婷婷开心五月四房播播 | 国产精品国产自线拍免费软件 | 2019午夜福利不卡片在线 | 日日摸日日碰夜夜爽av | 亚洲欧美日韩综合久久久 | 国产97人人超碰caoprom | 国产精品无码久久av | 麻豆国产97在线 | 欧洲 | 女人高潮内射99精品 | 全黄性性激高免费视频 | 久久午夜无码鲁丝片 | 四虎永久在线精品免费网址 | 亚洲精品成人福利网站 | 波多野42部无码喷潮在线 | 18无码粉嫩小泬无套在线观看 | 中文字幕+乱码+中文字幕一区 | 水蜜桃色314在线观看 | 青青青爽视频在线观看 | 成人免费视频视频在线观看 免费 | 色狠狠av一区二区三区 | 成年美女黄网站色大免费视频 | 澳门永久av免费网站 | 无遮挡国产高潮视频免费观看 | 18禁黄网站男男禁片免费观看 | 久久午夜无码鲁丝片秋霞 | 成人三级无码视频在线观看 | 国产精品毛片一区二区 | av在线亚洲欧洲日产一区二区 | 曰本女人与公拘交酡免费视频 | 亚洲精品一区二区三区大桥未久 | 亚洲理论电影在线观看 | 午夜精品久久久久久久 | 亚洲一区二区三区国产精华液 | 激情国产av做激情国产爱 | 超碰97人人做人人爱少妇 | 精品欧洲av无码一区二区三区 | 国产精品人人妻人人爽 | 大乳丰满人妻中文字幕日本 | 精品久久8x国产免费观看 | 在线播放亚洲第一字幕 | 中文字幕精品av一区二区五区 | 国产真实乱对白精彩久久 | 天天做天天爱天天爽综合网 | 精品国产一区二区三区四区在线看 | 狠狠色色综合网站 | 色噜噜亚洲男人的天堂 | 色老头在线一区二区三区 | 沈阳熟女露脸对白视频 | 国产精品久久久久久无码 | 欧美zoozzooz性欧美 | 国产精品国产三级国产专播 | 美女扒开屁股让男人桶 | 大肉大捧一进一出好爽视频 | 久久精品中文字幕一区 | 欧美大屁股xxxxhd黑色 | 亚洲欧美综合区丁香五月小说 | 2020久久超碰国产精品最新 | 综合激情五月综合激情五月激情1 | 一本精品99久久精品77 | 欧美激情综合亚洲一二区 | 又大又硬又爽免费视频 | 宝宝好涨水快流出来免费视频 | 国产农村乱对白刺激视频 | 午夜无码区在线观看 | 日本乱人伦片中文三区 | 国产成人无码午夜视频在线观看 | 麻豆国产97在线 | 欧洲 | 桃花色综合影院 | 人人妻人人澡人人爽精品欧美 | 岛国片人妻三上悠亚 | 老头边吃奶边弄进去呻吟 | 国产香蕉97碰碰久久人人 | 亚洲日韩av一区二区三区中文 | 亚洲欧美综合区丁香五月小说 | 婷婷五月综合缴情在线视频 | 国产精品人妻一区二区三区四 | 久久精品国产一区二区三区 | 亚洲 激情 小说 另类 欧美 | 免费看男女做好爽好硬视频 | 熟妇女人妻丰满少妇中文字幕 | 熟妇人妻中文av无码 | 成在人线av无码免观看麻豆 | 国产成人无码av片在线观看不卡 | 亚洲中文无码av永久不收费 | 久久国产自偷自偷免费一区调 | 国产一区二区不卡老阿姨 | 色欲av亚洲一区无码少妇 | 亚洲精品一区二区三区四区五区 | 扒开双腿疯狂进出爽爽爽视频 | 中文字幕av无码一区二区三区电影 | 少妇被粗大的猛进出69影院 | 国产精品无码一区二区三区不卡 | 妺妺窝人体色www婷婷 | 全球成人中文在线 | 亚洲人成网站色7799 | 丝袜美腿亚洲一区二区 | 午夜福利一区二区三区在线观看 | 中文亚洲成a人片在线观看 | 久久综合色之久久综合 | 曰本女人与公拘交酡免费视频 | 国产精品爱久久久久久久 | 免费观看的无遮挡av | 精品亚洲成av人在线观看 | 六十路熟妇乱子伦 | 国产精品18久久久久久麻辣 | 熟女少妇在线视频播放 | 无码任你躁久久久久久久 | 精品成在人线av无码免费看 | 动漫av一区二区在线观看 | 动漫av网站免费观看 | 亚洲国产一区二区三区在线观看 | 日本一区二区三区免费播放 | 熟女少妇人妻中文字幕 | 狠狠色欧美亚洲狠狠色www | yw尤物av无码国产在线观看 | 熟妇女人妻丰满少妇中文字幕 | 国产精品第一国产精品 | 1000部啪啪未满十八勿入下载 | 人人爽人人澡人人人妻 | 三上悠亚人妻中文字幕在线 | 成年美女黄网站色大免费全看 | 一本久道高清无码视频 | 中文字幕无码av波多野吉衣 | 久久精品人人做人人综合 | 大肉大捧一进一出好爽视频 | 中文无码成人免费视频在线观看 | 午夜精品久久久久久久 | 日韩av无码一区二区三区不卡 | av无码不卡在线观看免费 | 四虎永久在线精品免费网址 | 亚洲国产精品无码一区二区三区 | 色爱情人网站 | 2019nv天堂香蕉在线观看 | 成人精品视频一区二区三区尤物 | 亚洲色在线无码国产精品不卡 | 欧美丰满熟妇xxxx性ppx人交 | 未满小14洗澡无码视频网站 | 国内丰满熟女出轨videos | 白嫩日本少妇做爰 | www一区二区www免费 | 福利一区二区三区视频在线观看 | 国产香蕉97碰碰久久人人 | 亚洲精品美女久久久久久久 | 无码一区二区三区在线观看 | 婷婷色婷婷开心五月四房播播 | 天堂久久天堂av色综合 | 97色伦图片97综合影院 | 国产精品嫩草久久久久 | 婷婷丁香六月激情综合啪 | 老司机亚洲精品影院无码 | 欧美人与物videos另类 | 熟女体下毛毛黑森林 | 色五月丁香五月综合五月 | 最新国产麻豆aⅴ精品无码 | 福利一区二区三区视频在线观看 | 国产综合在线观看 | 国产人妻大战黑人第1集 | 99久久精品国产一区二区蜜芽 | 99久久99久久免费精品蜜桃 | 性色欲情网站iwww九文堂 | 嫩b人妻精品一区二区三区 | 亚洲成av人片在线观看无码不卡 | 强辱丰满人妻hd中文字幕 | 国产综合色产在线精品 | aⅴ亚洲 日韩 色 图网站 播放 | 国产亚洲视频中文字幕97精品 | 亚洲日本一区二区三区在线 | 久久久久免费精品国产 | 日本精品人妻无码77777 天堂一区人妻无码 | 少妇无码一区二区二三区 | 日本精品人妻无码免费大全 | 在线a亚洲视频播放在线观看 | 久久久精品人妻久久影视 | 国产亚洲欧美日韩亚洲中文色 | 国产成人无码av在线影院 | 成人无码视频在线观看网站 | 人妻中文无码久热丝袜 | 欧美精品国产综合久久 | 欧美人与禽zoz0性伦交 | 亚洲狠狠色丁香婷婷综合 | 日韩精品久久久肉伦网站 | 成人无码视频在线观看网站 | 中文精品无码中文字幕无码专区 | 国产av无码专区亚洲awww | 日日摸夜夜摸狠狠摸婷婷 | 国产亚洲精品久久久久久 | 一本大道久久东京热无码av | 精品日本一区二区三区在线观看 | 东京热一精品无码av | 欧美丰满少妇xxxx性 | 欧美xxxxx精品 | 又大又硬又黄的免费视频 | 国产精品.xx视频.xxtv | 野狼第一精品社区 | 99久久精品日本一区二区免费 | 成人毛片一区二区 | 日本大香伊一区二区三区 | 久久久久av无码免费网 | 亚洲国产精品毛片av不卡在线 | 国产超碰人人爽人人做人人添 | 久久久精品国产sm最大网站 | 一二三四在线观看免费视频 | 亚洲精品一区二区三区在线 | 久久久久亚洲精品男人的天堂 | 久久久亚洲欧洲日产国码αv | 无码一区二区三区在线 | 中文字幕av伊人av无码av | 超碰97人人做人人爱少妇 | 国产成人精品优优av | 久久午夜夜伦鲁鲁片无码免费 | 久久精品国产日本波多野结衣 | 欧美成人免费全部网站 | 国产在线无码精品电影网 | 中文字幕无线码免费人妻 | 99久久久无码国产aaa精品 | 熟妇女人妻丰满少妇中文字幕 | 日本熟妇人妻xxxxx人hd | 搡女人真爽免费视频大全 | 日韩人妻无码中文字幕视频 | 精品国产青草久久久久福利 | 亚洲乱亚洲乱妇50p | 中文字幕乱码人妻无码久久 | 亚洲 a v无 码免 费 成 人 a v | 久久精品国产一区二区三区肥胖 | 亚洲精品国产精品乱码不卡 | 伊在人天堂亚洲香蕉精品区 | 亚拍精品一区二区三区探花 | 亚洲а∨天堂久久精品2021 | 丰满妇女强制高潮18xxxx | 无码一区二区三区在线观看 | 国产精品人人爽人人做我的可爱 | 亚洲熟妇自偷自拍另类 | 色 综合 欧美 亚洲 国产 | 丰满护士巨好爽好大乳 | 日韩欧美成人免费观看 | 一二三四社区在线中文视频 | 97久久国产亚洲精品超碰热 | 国产无遮挡又黄又爽又色 | 亚洲精品久久久久avwww潮水 | www国产亚洲精品久久久日本 | 亚洲午夜无码久久 | 成人影院yy111111在线观看 | 国产精品第一区揄拍无码 | 麻豆精产国品 | 999久久久国产精品消防器材 | 青青草原综合久久大伊人精品 | 欧美放荡的少妇 | 亚洲乱码国产乱码精品精 | √8天堂资源地址中文在线 | 日日碰狠狠丁香久燥 | 亚洲欧美日韩国产精品一区二区 | 1000部啪啪未满十八勿入下载 | 久久久久久九九精品久 | 国产精品亚洲lv粉色 | 亚洲成av人影院在线观看 | 国产人妻久久精品二区三区老狼 | 国产suv精品一区二区五 | 欧美日本免费一区二区三区 | 久久综合香蕉国产蜜臀av | aⅴ亚洲 日韩 色 图网站 播放 | 国产麻豆精品精东影业av网站 | 亚洲熟女一区二区三区 | 精品无码国产一区二区三区av | 乱人伦人妻中文字幕无码久久网 | 亚洲自偷自偷在线制服 | 少妇被粗大的猛进出69影院 | 人妻少妇精品视频专区 | 人妻aⅴ无码一区二区三区 | 国产亚洲精品久久久闺蜜 | 亚洲小说图区综合在线 | 国产午夜无码视频在线观看 | 小鲜肉自慰网站xnxx | 福利一区二区三区视频在线观看 | 日本在线高清不卡免费播放 | 天堂久久天堂av色综合 | 999久久久国产精品消防器材 | 在线观看欧美一区二区三区 | 亚洲欧美日韩国产精品一区二区 | 国产精品国产三级国产专播 | 亚洲午夜久久久影院 | 在线观看欧美一区二区三区 | 国产人妻久久精品二区三区老狼 | 婷婷综合久久中文字幕蜜桃三电影 | 久久久久成人精品免费播放动漫 | av香港经典三级级 在线 | 亚洲性无码av中文字幕 | 无码午夜成人1000部免费视频 | 日本成熟视频免费视频 | 亚洲国产精品无码久久久久高潮 | 国产区女主播在线观看 | a在线观看免费网站大全 | 国产成人人人97超碰超爽8 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 老子影院午夜伦不卡 | 国产成人无码av片在线观看不卡 | 日欧一片内射va在线影院 | 日日躁夜夜躁狠狠躁 | 永久免费精品精品永久-夜色 | 国产精品理论片在线观看 | 天天av天天av天天透 | 久久久精品国产sm最大网站 | 男女性色大片免费网站 | 一本一道久久综合久久 | 在线 国产 欧美 亚洲 天堂 | 国产精品无码成人午夜电影 | 女人和拘做爰正片视频 | 久久精品女人天堂av免费观看 | 国产suv精品一区二区五 | 国产特级毛片aaaaaa高潮流水 | 亚洲中文无码av永久不收费 | 男女作爱免费网站 | 1000部啪啪未满十八勿入下载 | 乱中年女人伦av三区 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 最新版天堂资源中文官网 | 西西人体www44rt大胆高清 | 亚洲精品综合五月久久小说 | 国产一区二区三区四区五区加勒比 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 无码人妻av免费一区二区三区 | 久久这里只有精品视频9 | 扒开双腿吃奶呻吟做受视频 | 亚洲精品久久久久中文第一幕 | 在线观看欧美一区二区三区 | 亚洲男人av天堂午夜在 | 国产莉萝无码av在线播放 | 国色天香社区在线视频 | 无码人妻少妇伦在线电影 | 亚洲国产欧美在线成人 | 日韩精品久久久肉伦网站 | 天堂在线观看www | 日韩成人一区二区三区在线观看 | 久久久久久a亚洲欧洲av冫 | 又湿又紧又大又爽a视频国产 | 欧美日韩一区二区三区自拍 | 亚洲中文字幕av在天堂 | 午夜理论片yy44880影院 | av在线亚洲欧洲日产一区二区 | 亚洲日韩一区二区三区 | 中文字幕 亚洲精品 第1页 | a片在线免费观看 | 日韩精品a片一区二区三区妖精 | 在线看片无码永久免费视频 | 国产精品国产自线拍免费软件 | 无码吃奶揉捏奶头高潮视频 | 国产香蕉97碰碰久久人人 | 成年美女黄网站色大免费全看 | 欧美肥老太牲交大战 | 欧洲欧美人成视频在线 | 人妻少妇精品久久 | 久久亚洲精品中文字幕无男同 | 天堂а√在线地址中文在线 | 无遮挡啪啪摇乳动态图 | 狠狠亚洲超碰狼人久久 | 亚洲欧洲日本综合aⅴ在线 | 99精品久久毛片a片 | 欧美 亚洲 国产 另类 | 51国偷自产一区二区三区 | 欧美老熟妇乱xxxxx | 久久久精品成人免费观看 | 国产真人无遮挡作爱免费视频 | 97夜夜澡人人爽人人喊中国片 | 欧美激情一区二区三区成人 | 夜夜高潮次次欢爽av女 | 对白脏话肉麻粗话av | 2019nv天堂香蕉在线观看 | 一本色道久久综合狠狠躁 | 性做久久久久久久免费看 | 正在播放老肥熟妇露脸 | 久久久亚洲欧洲日产国码αv | 两性色午夜免费视频 | 一个人免费观看的www视频 | 精品一二三区久久aaa片 | 两性色午夜视频免费播放 | 97精品国产97久久久久久免费 | 欧美成人高清在线播放 | 激情亚洲一区国产精品 | 99精品久久毛片a片 | 亚拍精品一区二区三区探花 | 日韩人妻无码一区二区三区久久99 | 丰满诱人的人妻3 | 国产成人精品必看 | 免费观看又污又黄的网站 | 精品欧洲av无码一区二区三区 | 无套内谢的新婚少妇国语播放 | 欧美黑人乱大交 | 人妻与老人中文字幕 | 2019nv天堂香蕉在线观看 | 中文字幕 亚洲精品 第1页 | 伊人色综合久久天天小片 | 国产激情综合五月久久 | 国产免费观看黄av片 | 成人无码影片精品久久久 | 亚洲午夜福利在线观看 | 人人超人人超碰超国产 | 丰满护士巨好爽好大乳 | 内射老妇bbwx0c0ck | 久久精品国产99精品亚洲 | 无码国产乱人伦偷精品视频 | 伦伦影院午夜理论片 | 丝袜人妻一区二区三区 | 人人妻在人人 | 99久久99久久免费精品蜜桃 | 麻豆成人精品国产免费 | 国产精品爱久久久久久久 | 亚洲成a人一区二区三区 | 色综合久久久无码中文字幕 | 红桃av一区二区三区在线无码av | 久久精品中文字幕大胸 | 久久人人97超碰a片精品 | 精品久久综合1区2区3区激情 | 亚洲人成无码网www | 成人无码精品1区2区3区免费看 | 漂亮人妻洗澡被公强 日日躁 | 国产精品无码永久免费888 | 粗大的内捧猛烈进出视频 | 精品无码国产自产拍在线观看蜜 | 国产精品无码久久av | 少妇太爽了在线观看 | 国产成人一区二区三区别 | 色一情一乱一伦一区二区三欧美 | 成人毛片一区二区 | 色欲综合久久中文字幕网 | 欧美人与物videos另类 | 水蜜桃亚洲一二三四在线 | 国产午夜手机精彩视频 | 国产精品美女久久久 | 熟妇激情内射com | 无码av岛国片在线播放 | 久久亚洲中文字幕无码 | 粗大的内捧猛烈进出视频 | 成人影院yy111111在线观看 | 国产在线精品一区二区高清不卡 | 亚洲经典千人经典日产 | 99国产精品白浆在线观看免费 | 亚洲精品欧美二区三区中文字幕 | 国产后入清纯学生妹 | 亚洲色在线无码国产精品不卡 | 国产性生大片免费观看性 | 日韩亚洲欧美中文高清在线 | 国产成人精品久久亚洲高清不卡 | 欧美人与物videos另类 | 亚洲精品成a人在线观看 | 熟妇人妻无乱码中文字幕 | 狠狠色丁香久久婷婷综合五月 | 无码人妻av免费一区二区三区 | 亚洲无人区午夜福利码高清完整版 | 国产性生交xxxxx无码 | 亚洲国产精品无码久久久久高潮 | 久在线观看福利视频 | 中文字幕无码乱人伦 | 亚洲国产精品久久久久久 | 精品国产精品久久一区免费式 | 精品水蜜桃久久久久久久 | 人人超人人超碰超国产 | 欧美真人作爱免费视频 | 国产高清av在线播放 | 亚洲狠狠婷婷综合久久 | 两性色午夜免费视频 | 兔费看少妇性l交大片免费 | 欧美性生交xxxxx久久久 | 牲欲强的熟妇农村老妇女 | 精品国偷自产在线视频 | 亚洲成a人一区二区三区 | 永久免费精品精品永久-夜色 | 性欧美熟妇videofreesex | 国产精品99爱免费视频 | 亚洲成a人片在线观看无码3d | 国产精品无码一区二区桃花视频 | 天堂无码人妻精品一区二区三区 | 久久精品国产精品国产精品污 | 欧美人与动性行为视频 | 亚洲中文字幕成人无码 | 久久精品人人做人人综合试看 | 精品久久久久香蕉网 | 午夜丰满少妇性开放视频 | 少妇无码一区二区二三区 | 国语自产偷拍精品视频偷 | 久久成人a毛片免费观看网站 | 草草网站影院白丝内射 | 日韩精品无码一本二本三本色 | 狠狠综合久久久久综合网 | 国产欧美亚洲精品a | 久久午夜夜伦鲁鲁片无码免费 | 久久视频在线观看精品 | 亚洲一区二区三区四区 | 熟女少妇在线视频播放 | 国产激情一区二区三区 | 99久久精品无码一区二区毛片 | 人人澡人人妻人人爽人人蜜桃 | 人人妻在人人 | 18禁止看的免费污网站 | 亚洲国产精品无码久久久久高潮 | 扒开双腿吃奶呻吟做受视频 | 日韩少妇内射免费播放 | 国产后入清纯学生妹 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 精品国产麻豆免费人成网站 | 正在播放东北夫妻内射 | 国产av无码专区亚洲awww | 亚洲精品美女久久久久久久 | 久久国产自偷自偷免费一区调 | 图片小说视频一区二区 | 欧美大屁股xxxxhd黑色 | 野外少妇愉情中文字幕 | 日本乱人伦片中文三区 | 国产人妻精品一区二区三区不卡 | 国精产品一区二区三区 | 人人澡人摸人人添 | 无码毛片视频一区二区本码 | 国产手机在线αⅴ片无码观看 | 国产色xx群视频射精 | 欧美野外疯狂做受xxxx高潮 | 成人无码精品一区二区三区 | 国产sm调教视频在线观看 | 国产av一区二区三区最新精品 | 思思久久99热只有频精品66 | 日本一区二区三区免费高清 | 色老头在线一区二区三区 | 成人精品视频一区二区 | 久久zyz资源站无码中文动漫 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 国产美女极度色诱视频www | 国产成人无码a区在线观看视频app | 欧洲极品少妇 | 亚洲日本va中文字幕 | 男女猛烈xx00免费视频试看 | 麻花豆传媒剧国产免费mv在线 | 国产激情艳情在线看视频 | 亚洲人成影院在线无码按摩店 | 国产精品毛多多水多 | 色综合视频一区二区三区 | 无码人妻精品一区二区三区下载 | 自拍偷自拍亚洲精品10p | 久久人人爽人人人人片 | 久久综合九色综合欧美狠狠 | 波多野结衣高清一区二区三区 | 国产精品视频免费播放 | 久久综合网欧美色妞网 | 亚洲中文字幕av在天堂 | 日日橹狠狠爱欧美视频 | 国内揄拍国内精品人妻 | 在教室伦流澡到高潮hnp视频 | 欧美成人高清在线播放 | 中文字幕av无码一区二区三区电影 | 精品无码一区二区三区爱欲 | 日韩精品无码一本二本三本色 | 一本久道久久综合狠狠爱 | www国产亚洲精品久久网站 | 国产麻豆精品一区二区三区v视界 | 国产成人精品优优av | 中文字幕+乱码+中文字幕一区 | 扒开双腿疯狂进出爽爽爽视频 | 日韩精品一区二区av在线 | 丁香啪啪综合成人亚洲 | 国产xxx69麻豆国语对白 | 狠狠躁日日躁夜夜躁2020 | 亚洲日本va午夜在线电影 | 久久人人爽人人爽人人片av高清 | 中文字幕日韩精品一区二区三区 | 日欧一片内射va在线影院 | 无码成人精品区在线观看 | 丰满诱人的人妻3 | 日韩欧美中文字幕在线三区 | 无套内谢的新婚少妇国语播放 | 国产精品-区区久久久狼 | 丰满护士巨好爽好大乳 | 成年女人永久免费看片 | 欧美xxxxx精品 | 国产人妖乱国产精品人妖 | 爆乳一区二区三区无码 | 欧美性黑人极品hd | 国产成人无码区免费内射一片色欲 | 在线精品国产一区二区三区 | 亚洲色成人中文字幕网站 | 99精品国产综合久久久久五月天 | 蜜桃无码一区二区三区 | 欧美国产日韩亚洲中文 | 内射老妇bbwx0c0ck | 成在人线av无码免观看麻豆 | 国产精品亚洲综合色区韩国 | 国产亚洲精品久久久久久国模美 | 国产精品久久福利网站 | 久久五月精品中文字幕 | 成熟女人特级毛片www免费 | 乱中年女人伦av三区 | 亚洲日本va午夜在线电影 | 中文字幕+乱码+中文字幕一区 | 蜜桃臀无码内射一区二区三区 | 国产成人亚洲综合无码 | 国产精品亚洲五月天高清 | 国产一精品一av一免费 | 日韩欧美群交p片內射中文 | 久久成人a毛片免费观看网站 | 一区二区三区乱码在线 | 欧洲 | 97夜夜澡人人爽人人喊中国片 | 97人妻精品一区二区三区 | 天下第一社区视频www日本 | 欧美xxxxx精品 | 亚洲 欧美 激情 小说 另类 | 久久亚洲中文字幕精品一区 | 中文字幕乱码中文乱码51精品 | 在教室伦流澡到高潮hnp视频 | 国产激情综合五月久久 | 男女下面进入的视频免费午夜 | 在线精品亚洲一区二区 | 日产国产精品亚洲系列 | 无码人妻久久一区二区三区不卡 | 精品人人妻人人澡人人爽人人 | 无码国产色欲xxxxx视频 | 午夜成人1000部免费视频 | 四虎永久在线精品免费网址 | 国产精品美女久久久 | 国产性生大片免费观看性 | 国产人妖乱国产精品人妖 | 黄网在线观看免费网站 | 国产精品对白交换视频 | 99久久亚洲精品无码毛片 | 97久久超碰中文字幕 | 亚洲日本va午夜在线电影 | 波多野结衣 黑人 | 狠狠色噜噜狠狠狠狠7777米奇 | 狂野欧美性猛交免费视频 | 久久99久久99精品中文字幕 | 国产成人无码区免费内射一片色欲 | 亚洲日本在线电影 | 性色av无码免费一区二区三区 | 成人性做爰aaa片免费看 | 久久精品女人天堂av免费观看 | 色噜噜亚洲男人的天堂 | 狠狠色色综合网站 | 成人动漫在线观看 | 鲁鲁鲁爽爽爽在线视频观看 | 午夜福利一区二区三区在线观看 | 漂亮人妻洗澡被公强 日日躁 | 呦交小u女精品视频 | 无码乱肉视频免费大全合集 | 双乳奶水饱满少妇呻吟 | 日韩精品无码一区二区中文字幕 | 亚洲日本va中文字幕 | 丰满人妻精品国产99aⅴ | 人妻有码中文字幕在线 | 久热国产vs视频在线观看 | 精品国产av色一区二区深夜久久 | 无人区乱码一区二区三区 | 亚洲熟妇色xxxxx欧美老妇y | 久久 国产 尿 小便 嘘嘘 | 精品欧洲av无码一区二区三区 | 婷婷丁香六月激情综合啪 | 天堂亚洲2017在线观看 | 国产成人精品必看 | 99久久久无码国产aaa精品 | 国产香蕉尹人综合在线观看 | 成 人 免费观看网站 | 国产成人精品一区二区在线小狼 | 亚洲精品一区二区三区大桥未久 | 亚洲 欧美 激情 小说 另类 | 亚洲午夜无码久久 | 国产亚洲美女精品久久久2020 | 精品 日韩 国产 欧美 视频 | 国内老熟妇对白xxxxhd | 图片区 小说区 区 亚洲五月 | 久久午夜无码鲁丝片秋霞 | 免费无码一区二区三区蜜桃大 | 成人无码影片精品久久久 | 亚洲国产欧美国产综合一区 | 真人与拘做受免费视频 | 中文无码成人免费视频在线观看 | 国产成人一区二区三区别 | 男女作爱免费网站 | 人妻有码中文字幕在线 | 无码午夜成人1000部免费视频 | 久久精品国产99久久6动漫 | 色婷婷av一区二区三区之红樱桃 | 小鲜肉自慰网站xnxx | 免费乱码人妻系列无码专区 | 5858s亚洲色大成网站www | 国产精品对白交换视频 | 女人被男人躁得好爽免费视频 | 婷婷五月综合缴情在线视频 | 中文字幕无码av波多野吉衣 | 荫蒂被男人添的好舒服爽免费视频 | 国产午夜手机精彩视频 | 亚洲精品鲁一鲁一区二区三区 | 中文无码伦av中文字幕 | 亚洲无人区午夜福利码高清完整版 | 久久天天躁狠狠躁夜夜免费观看 | 午夜丰满少妇性开放视频 | 国产精品爱久久久久久久 | 亚洲呦女专区 | 日本大香伊一区二区三区 | 一本久久a久久精品vr综合 | 中文字幕av日韩精品一区二区 | 国产亚av手机在线观看 | 人妻插b视频一区二区三区 | 久久天天躁夜夜躁狠狠 | 国产精品第一国产精品 | 波多野结衣一区二区三区av免费 | 日本一卡2卡3卡四卡精品网站 | 日韩欧美群交p片內射中文 | 少妇人妻大乳在线视频 | 色欲av亚洲一区无码少妇 | 亚洲综合伊人久久大杳蕉 | 乱人伦人妻中文字幕无码 | 国产内射爽爽大片视频社区在线 | 青草视频在线播放 | 丰满人妻翻云覆雨呻吟视频 | 极品嫩模高潮叫床 | 人人爽人人澡人人高潮 | 熟妇人妻激情偷爽文 | 特黄特色大片免费播放器图片 | 亚洲 欧美 激情 小说 另类 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 欧美人与禽zoz0性伦交 | 一本色道久久综合亚洲精品不卡 | 丝袜足控一区二区三区 | 日本精品久久久久中文字幕 | 久久综合给久久狠狠97色 | 欧美日本精品一区二区三区 | 人妻天天爽夜夜爽一区二区 | 人妻中文无码久热丝袜 | 精品日本一区二区三区在线观看 | 熟妇女人妻丰满少妇中文字幕 | 熟妇人妻无码xxx视频 | 377p欧洲日本亚洲大胆 | 国产精品久久久av久久久 | 人妻熟女一区 | 日韩精品无码免费一区二区三区 | 国产成人精品无码播放 | 国内精品人妻无码久久久影院蜜桃 | 76少妇精品导航 | 超碰97人人做人人爱少妇 | 麻豆果冻传媒2021精品传媒一区下载 | 77777熟女视频在线观看 а天堂中文在线官网 | 成人免费视频一区二区 | 日韩欧美群交p片內射中文 | 欧美国产亚洲日韩在线二区 | 天天躁日日躁狠狠躁免费麻豆 | 曰本女人与公拘交酡免费视频 | 色综合久久久无码网中文 | 亚洲男人av香蕉爽爽爽爽 | 国产精品二区一区二区aⅴ污介绍 | 国产xxx69麻豆国语对白 | 黑人大群体交免费视频 | 亚洲欧美综合区丁香五月小说 | 黑人巨大精品欧美一区二区 | 国产精品国产三级国产专播 | 国产69精品久久久久app下载 | 无码人妻少妇伦在线电影 | 男女猛烈xx00免费视频试看 | 国产精品亚洲一区二区三区喷水 | 国产精品久免费的黄网站 | 亚洲s码欧洲m码国产av | 久在线观看福利视频 | 亚洲а∨天堂久久精品2021 | 性欧美videos高清精品 | 亚洲人亚洲人成电影网站色 | 国产无遮挡又黄又爽又色 | 国产免费久久精品国产传媒 | 四虎国产精品一区二区 | 又粗又大又硬毛片免费看 | 亚洲国产精品久久久久久 | 国内少妇偷人精品视频 | 欧美日韩视频无码一区二区三 | 国产性生大片免费观看性 | 亚洲综合无码久久精品综合 | 亚洲天堂2017无码 | 国产成人无码a区在线观看视频app | 日日摸夜夜摸狠狠摸婷婷 | 亚洲人成无码网www | 亚洲欧美日韩成人高清在线一区 | 99麻豆久久久国产精品免费 | 亚洲人成影院在线观看 | 任你躁在线精品免费 | 精品无人区无码乱码毛片国产 | 久久久精品人妻久久影视 | 一个人看的www免费视频在线观看 | 波多野42部无码喷潮在线 | 免费观看的无遮挡av | 午夜精品久久久久久久久 | 久久久久se色偷偷亚洲精品av | 色妞www精品免费视频 | 国产 浪潮av性色四虎 | 男女作爱免费网站 | 麻豆国产人妻欲求不满谁演的 | 欧洲熟妇精品视频 | 水蜜桃色314在线观看 | 国产亚洲美女精品久久久2020 | 无码帝国www无码专区色综合 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 人妻天天爽夜夜爽一区二区 | 亚洲综合无码久久精品综合 | 成人免费视频在线观看 | 亚洲国产欧美日韩精品一区二区三区 | 成人无码视频在线观看网站 | 国内老熟妇对白xxxxhd | 欧美性猛交内射兽交老熟妇 | 欧美兽交xxxx×视频 | 亚洲娇小与黑人巨大交 | 樱花草在线社区www | 人妻插b视频一区二区三区 | 亚洲一区二区三区无码久久 | 美女扒开屁股让男人桶 | 国产成人无码av片在线观看不卡 | 宝宝好涨水快流出来免费视频 | 色婷婷香蕉在线一区二区 | 亚洲色欲色欲天天天www | 亚洲无人区午夜福利码高清完整版 | 性做久久久久久久免费看 | 国产精品美女久久久 | 日本护士xxxxhd少妇 | 人妻少妇精品久久 | 爱做久久久久久 | 国产真人无遮挡作爱免费视频 | 亚洲国产av美女网站 | 精品一二三区久久aaa片 | 精品国精品国产自在久国产87 | 久久99精品国产.久久久久 | 婷婷丁香五月天综合东京热 | 性欧美熟妇videofreesex | 无码av最新清无码专区吞精 | 人人爽人人爽人人片av亚洲 | 人人妻人人藻人人爽欧美一区 | 午夜免费福利小电影 | 免费观看黄网站 | 国产艳妇av在线观看果冻传媒 | 久久久国产精品无码免费专区 | 国产精品久久久久久亚洲影视内衣 | 亚洲啪av永久无码精品放毛片 | 99精品国产综合久久久久五月天 | 人人爽人人澡人人高潮 | 亚洲精品一区二区三区在线观看 | 少妇厨房愉情理9仑片视频 | 国产午夜福利100集发布 | 人人妻人人澡人人爽精品欧美 | 国产人妻久久精品二区三区老狼 | 久久精品丝袜高跟鞋 | 三上悠亚人妻中文字幕在线 | 2019午夜福利不卡片在线 | 无码帝国www无码专区色综合 | 中文字幕 人妻熟女 | 中文精品无码中文字幕无码专区 | 在线成人www免费观看视频 | 国内精品久久毛片一区二区 | 国产乡下妇女做爰 | 精品久久久中文字幕人妻 | 在教室伦流澡到高潮hnp视频 | 亚洲小说春色综合另类 | 亚洲熟妇自偷自拍另类 | 老太婆性杂交欧美肥老太 | av无码久久久久不卡免费网站 | 国产又粗又硬又大爽黄老大爷视 | 久久99热只有频精品8 | 国产精品高潮呻吟av久久4虎 | 77777熟女视频在线观看 а天堂中文在线官网 | 成人精品天堂一区二区三区 | 国产黑色丝袜在线播放 | 午夜福利不卡在线视频 | 精品国精品国产自在久国产87 | 国产一区二区不卡老阿姨 | 国产sm调教视频在线观看 | 国产舌乚八伦偷品w中 | 青青青爽视频在线观看 | 狠狠色噜噜狠狠狠7777奇米 | 永久免费观看美女裸体的网站 | 无套内谢的新婚少妇国语播放 | 国产午夜无码视频在线观看 | 亚洲精品国产精品乱码视色 | 国内揄拍国内精品人妻 | 欧美午夜特黄aaaaaa片 | 性欧美熟妇videofreesex | 内射老妇bbwx0c0ck | 久久精品人人做人人综合 | аⅴ资源天堂资源库在线 | 精品无人区无码乱码毛片国产 | 国产绳艺sm调教室论坛 | 国产成人精品无码播放 | 亚洲爆乳精品无码一区二区三区 | 欧美野外疯狂做受xxxx高潮 | 久久综合九色综合97网 | 成人一区二区免费视频 | 精品国产一区二区三区av 性色 | 特黄特色大片免费播放器图片 | 久久精品成人欧美大片 | 日韩av无码一区二区三区不卡 | 国产精品无码一区二区三区不卡 | 无码纯肉视频在线观看 | 少妇无套内谢久久久久 | 亚洲毛片av日韩av无码 | 亚洲色无码一区二区三区 | 日日摸日日碰夜夜爽av | 国产97色在线 | 免 | 熟妇人妻无乱码中文字幕 | 欧美人妻一区二区三区 | 性欧美videos高清精品 | 性色欲网站人妻丰满中文久久不卡 | 中文字幕无线码免费人妻 | 欧美性猛交xxxx富婆 | 国产成人久久精品流白浆 | 欧美日韩视频无码一区二区三 | 日本在线高清不卡免费播放 | 国产欧美熟妇另类久久久 | 无码人妻av免费一区二区三区 | 成 人 网 站国产免费观看 | 波多野结衣av在线观看 | 国产精品久久久久久亚洲影视内衣 | 国内揄拍国内精品少妇国语 | 成人综合网亚洲伊人 | 欧美野外疯狂做受xxxx高潮 | 亚洲大尺度无码无码专区 | 久久综合网欧美色妞网 | 小sao货水好多真紧h无码视频 | 亚洲aⅴ无码成人网站国产app | 精品一区二区三区无码免费视频 | 特黄特色大片免费播放器图片 | 一本久久a久久精品亚洲 | 欧美激情一区二区三区成人 | 亚洲国产精品毛片av不卡在线 | 国产成人无码专区 | 中文字幕无码免费久久99 | 欧美日韩人成综合在线播放 | 亚洲高清偷拍一区二区三区 | 黄网在线观看免费网站 | 亚洲精品一区三区三区在线观看 | 十八禁真人啪啪免费网站 | 亚洲精品久久久久久一区二区 | 人妻无码αv中文字幕久久琪琪布 | 色综合久久久无码网中文 | 欧美丰满熟妇xxxx性ppx人交 | 日韩亚洲欧美中文高清在线 | 在线精品亚洲一区二区 | 国产人妻精品一区二区三区 | 国产乱子伦视频在线播放 | 久久国产劲爆∧v内射 | 无码免费一区二区三区 | 国产婷婷色一区二区三区在线 | 少妇被黑人到高潮喷出白浆 | 中文字幕乱码人妻无码久久 | 中文无码精品a∨在线观看不卡 | 日本va欧美va欧美va精品 | 97久久国产亚洲精品超碰热 | 日韩精品成人一区二区三区 | 玩弄少妇高潮ⅹxxxyw | 无码人妻久久一区二区三区不卡 | 午夜精品久久久内射近拍高清 | 久久综合网欧美色妞网 | 国产黑色丝袜在线播放 | 日韩av无码中文无码电影 | 全黄性性激高免费视频 | 久久99精品久久久久久 | 高清不卡一区二区三区 | 日韩精品成人一区二区三区 | 一本久久a久久精品vr综合 | 日韩亚洲欧美中文高清在线 | 特黄特色大片免费播放器图片 | aⅴ亚洲 日韩 色 图网站 播放 | 夜精品a片一区二区三区无码白浆 | 久久亚洲a片com人成 | 亚洲精品无码人妻无码 | 色狠狠av一区二区三区 | 久久精品人妻少妇一区二区三区 | 精品久久久无码人妻字幂 | 中文字幕无码热在线视频 | 嫩b人妻精品一区二区三区 | 成人综合网亚洲伊人 | 国产午夜无码视频在线观看 | 给我免费的视频在线观看 | 亚洲国产精华液网站w | 999久久久国产精品消防器材 | 久久精品成人欧美大片 | 亚洲精品一区三区三区在线观看 | 久久久久亚洲精品中文字幕 | 欧美 亚洲 国产 另类 | 激情内射日本一区二区三区 | 久久精品国产一区二区三区肥胖 | 日韩人妻无码一区二区三区久久99 | 少妇性俱乐部纵欲狂欢电影 | 亚洲七七久久桃花影院 | 国产真人无遮挡作爱免费视频 | 国产一区二区三区精品视频 | 国产成人无码av一区二区 | 国产一区二区三区精品视频 | 一本色道婷婷久久欧美 | 国产av无码专区亚洲a∨毛片 | 国产一区二区三区四区五区加勒比 | 老太婆性杂交欧美肥老太 | 欧美真人作爱免费视频 | 无码人妻丰满熟妇区毛片18 | 毛片内射-百度 | 亚洲精品中文字幕 | 久久久www成人免费毛片 | 无码国模国产在线观看 | 亚洲综合另类小说色区 | 欧美兽交xxxx×视频 | 一本一道久久综合久久 | 国产成人精品必看 | 久久综合狠狠综合久久综合88 | 欧美黑人乱大交 | 亚洲中文字幕在线无码一区二区 | 久久综合狠狠综合久久综合88 | 国产精品鲁鲁鲁 | 鲁鲁鲁爽爽爽在线视频观看 | 青草青草久热国产精品 | 亚洲熟妇自偷自拍另类 | 精品 日韩 国产 欧美 视频 | a片在线免费观看 | a片在线免费观看 | 久久午夜无码鲁丝片午夜精品 | 国产色xx群视频射精 | 亚洲欧美日韩综合久久久 | 久久视频在线观看精品 | aⅴ在线视频男人的天堂 | 午夜时刻免费入口 | 精品日本一区二区三区在线观看 | 日韩人妻无码中文字幕视频 | 国产乱人伦偷精品视频 | 亚洲国产精品美女久久久久 | 亚洲熟悉妇女xxx妇女av | 精品一区二区不卡无码av | 国产又爽又猛又粗的视频a片 | www国产精品内射老师 | 国产免费久久精品国产传媒 | 中文字幕色婷婷在线视频 | 性色av无码免费一区二区三区 | 国产精品鲁鲁鲁 | 无码人妻少妇伦在线电影 | 成人三级无码视频在线观看 | 久在线观看福利视频 | 国产97人人超碰caoprom | 成人精品天堂一区二区三区 | 成人免费视频在线观看 | 国产人妻精品午夜福利免费 | 亚洲人成无码网www | 久久精品国产一区二区三区肥胖 | 亚洲高清偷拍一区二区三区 | 久久久精品成人免费观看 | 国产精品99久久精品爆乳 | 日韩在线不卡免费视频一区 | 欧美日韩一区二区三区自拍 | 国产精品二区一区二区aⅴ污介绍 | 国产激情综合五月久久 | 性欧美牲交xxxxx视频 | 日韩精品久久久肉伦网站 | 亚洲日韩av一区二区三区四区 | 永久免费精品精品永久-夜色 | 东京热一精品无码av | 亚洲国产午夜精品理论片 | а天堂中文在线官网 | 亚洲综合精品香蕉久久网 | 亲嘴扒胸摸屁股激烈网站 | 国产成人无码一二三区视频 | 亚洲の无码国产の无码影院 | 无码人妻精品一区二区三区下载 | 欧美日韩综合一区二区三区 | 亚洲成av人影院在线观看 | 精品午夜福利在线观看 | 国产免费无码一区二区视频 | 妺妺窝人体色www在线小说 | 性生交片免费无码看人 | 俺去俺来也在线www色官网 | 扒开双腿吃奶呻吟做受视频 | 国产精品亚洲一区二区三区喷水 | 中文无码伦av中文字幕 | 激情国产av做激情国产爱 | 成年美女黄网站色大免费视频 | 色情久久久av熟女人妻网站 | 成人一在线视频日韩国产 | 亚洲乱码中文字幕在线 | 天天av天天av天天透 | 国产精品99久久精品爆乳 | 伊人久久婷婷五月综合97色 | 黑人粗大猛烈进出高潮视频 | 无码纯肉视频在线观看 | 熟妇人妻无码xxx视频 | 大肉大捧一进一出好爽视频 | 在线观看国产午夜福利片 | 人妻中文无码久热丝袜 | 日韩人妻系列无码专区 | 少妇久久久久久人妻无码 | 日韩欧美成人免费观看 | 亚洲国产日韩a在线播放 | 永久免费精品精品永久-夜色 | 国产精品美女久久久网av | 中文无码伦av中文字幕 | 成人亚洲精品久久久久软件 | 欧美成人午夜精品久久久 | 久久国产精品二国产精品 | 亚洲国产一区二区三区在线观看 | 亚洲色无码一区二区三区 | 久久综合色之久久综合 | 久久熟妇人妻午夜寂寞影院 | 久久99精品久久久久婷婷 | 99视频精品全部免费免费观看 | 国产精品美女久久久网av | 麻豆果冻传媒2021精品传媒一区下载 | 强伦人妻一区二区三区视频18 | 无码av岛国片在线播放 | 欧美人与善在线com | 鲁大师影院在线观看 | 久久亚洲精品中文字幕无男同 | 国产高清av在线播放 | 日韩精品一区二区av在线 | 极品尤物被啪到呻吟喷水 | 一区二区三区乱码在线 | 欧洲 | 97se亚洲精品一区 | 曰本女人与公拘交酡免费视频 | 久久综合香蕉国产蜜臀av | 宝宝好涨水快流出来免费视频 | 动漫av网站免费观看 | 超碰97人人射妻 | 中文字幕乱妇无码av在线 | 亚洲熟女一区二区三区 | 欧美 日韩 亚洲 在线 | 亚洲爆乳大丰满无码专区 | 无码吃奶揉捏奶头高潮视频 | 一区二区三区乱码在线 | 欧洲 | 午夜时刻免费入口 | 国产极品视觉盛宴 | 亚洲综合久久一区二区 | 99久久精品日本一区二区免费 | 伦伦影院午夜理论片 | 99精品无人区乱码1区2区3区 | 国产乡下妇女做爰 | 国产欧美精品一区二区三区 | 欧美国产日产一区二区 | 永久黄网站色视频免费直播 | 国产综合色产在线精品 | 亚洲熟悉妇女xxx妇女av | 久久综合九色综合欧美狠狠 | 欧美日韩一区二区免费视频 | 午夜精品久久久内射近拍高清 | 亚洲精品一区二区三区四区五区 | 午夜福利一区二区三区在线观看 | 久久久久国色av免费观看性色 | 精品乱码久久久久久久 | 亚洲熟妇色xxxxx欧美老妇 | 蜜桃视频韩日免费播放 | 天干天干啦夜天干天2017 | 亚洲无人区午夜福利码高清完整版 | √天堂资源地址中文在线 | 人妻无码久久精品人妻 | 黑人玩弄人妻中文在线 | 亚洲第一无码av无码专区 | 久久久久se色偷偷亚洲精品av | 国产一区二区三区精品视频 | 国产精品igao视频网 | 国产香蕉尹人视频在线 | 国产精品久久久久久久9999 | 亚洲国产精品一区二区美利坚 | 人妻中文无码久热丝袜 | 亚洲 a v无 码免 费 成 人 a v | 岛国片人妻三上悠亚 | 久久99精品国产麻豆 | 少妇人妻大乳在线视频 | 亚洲精品一区二区三区在线观看 | 欧美性生交xxxxx久久久 | 国产成人综合色在线观看网站 | 无码人妻黑人中文字幕 | 亚洲爆乳大丰满无码专区 | 亚洲国产一区二区三区在线观看 | 国产人妻久久精品二区三区老狼 | 色综合视频一区二区三区 | 亚洲码国产精品高潮在线 | 日产精品99久久久久久 | 免费看男女做好爽好硬视频 | 亚洲一区二区三区含羞草 | 久久精品女人天堂av免费观看 | 亚洲日韩精品欧美一区二区 | 国产莉萝无码av在线播放 | 久热国产vs视频在线观看 | 亚洲a无码综合a国产av中文 | 欧美精品免费观看二区 | 亚洲乱码中文字幕在线 | 激情综合激情五月俺也去 | 四虎4hu永久免费 | 又粗又大又硬毛片免费看 | 国产精品对白交换视频 | 少妇性l交大片欧洲热妇乱xxx | 十八禁真人啪啪免费网站 | 成人无码精品一区二区三区 | 国产午夜亚洲精品不卡 | 性啪啪chinese东北女人 | 无人区乱码一区二区三区 | 日韩精品乱码av一区二区 | 乱人伦人妻中文字幕无码久久网 | 亚洲欧美综合区丁香五月小说 | 无码av最新清无码专区吞精 | 成人精品天堂一区二区三区 | 人妻有码中文字幕在线 | 色综合天天综合狠狠爱 | 夫妻免费无码v看片 | 亚洲人亚洲人成电影网站色 | 亚洲欧洲日本综合aⅴ在线 | 国产黑色丝袜在线播放 | 国产综合在线观看 | 国产精品亚洲专区无码不卡 | 亚洲小说图区综合在线 | 亚洲狠狠婷婷综合久久 | 成人精品天堂一区二区三区 | 国产亚洲tv在线观看 | 娇妻被黑人粗大高潮白浆 | 亚洲熟妇色xxxxx欧美老妇y | 97精品国产97久久久久久免费 | 岛国片人妻三上悠亚 | 亚洲伊人久久精品影院 | 奇米影视888欧美在线观看 | 亚洲综合无码久久精品综合 | 久在线观看福利视频 | 国产精品久久久久9999小说 | 国产精品国产自线拍免费软件 | 国产一区二区三区精品视频 | 国产真实乱对白精彩久久 | 久青草影院在线观看国产 | 狠狠色欧美亚洲狠狠色www | 国产精品美女久久久久av爽李琼 | 丰满诱人的人妻3 |