输入输出

本章会介绍Forth在终端,硬盘的输入输出处理。

我们将会讨论硬盘访问 输出操作 字符串操作 输入操作 字符串输入等命令

1 输出运算符

通常使用EMIT从数字栈获取一个ASCII字符编码,然后在终端输出对应的ASCII字符。比如BASE为十进制的情况下
65 EMIT return-key 输出 A ok
66 EMIT return-key 输出 B ok

TYPE从数字栈中获取地址和字节数,输出对应内存包含的所有字符。
数字栈参数个数为(addr u --)

虽然在数字格式化输出中使用TYPE输出内容是并没有使用到地址和字节数参数,因为可以使用#>提供

通常我们提供TYPE一个保存字符串内容的内存地址参数,正如以前介绍我们将终端输入缓存存储到以TIB为为开始的内存地址,并且使用#TIB保存终端输入内容的字节数,

TIB   保存着终端输入内容的内存地址

#TIB 保存终端输入内容的字节数地址

因此我们可以使用如下语句打印终端输入内容
TIB #TIB @ TYPE其中TIB提供地址值,#TIB @提供输入的字节数

让我们看看."。在定义语句中,通常."用来将右边的字符串存储到字典存储中,直到下一个"终结符。另外还会保存字节的数量在字典的开始位置。测试如下
: TEST ." sample " ;
我们可以打印保存到TEST中的字符串如下
TEST >BODY CELL+ 1+ 7 TYPE
其中的TEST >BODY给出TEST内容的开始位置,然后CELL+ 1+移动到字符串开头的s字母的地址,


在曾经的鸡蛋计数中我们使用LABEL。其定义使用了嵌套的IF_THEN语句
我们可以使用TYPE重新定义。首先我们把所有标签存储到一个字符串数组中,如下
: "LABEL" ." REJECT SMALL MEDIUM LARGE XTRA LRGERROR;(需要注意其中的空格按个数存储到字典中因此使用空格保证每个字符串的相同长度)
可以使用下面的结构指向字符串的开始地址
"LABEL" >BODY CELL+ 1+
然后通过适当的偏移量我们可以输出数组的任何标签,比如我们可以输出第2个标签的内容加上面2x8即16个偏移量。

那么我们可以重新定义LABEL如下
: LABEL 8 * ['] "LABEL" BODY CELL+ 1+ + 8 TYPE SPACE ;

有时候这种字符串数组称为超级字符串。按照命名习惯,超级字符串通常使用引号包围在周围("LABEL")。事实上通常不使用这种方式,而是使用已定义的C"实现存储如下。

: "LABEL" C" REJECT  SMALL  MEDIUM LARGE XTRA LRGEROR "  ;
: LABEL 8 * "LABEL" 1+ + 8 TYPE SPACE ;

需要注意的是在LABEL获得参数不在0到5的时候会输出错误信息。因此需要对LABEL进行判断。重新定义如下
: LABEL 0 MAX 5 MIN LABEL ;

2 输出到硬盘

在前面我们说过BLOCK会拷贝一个块的数据到可用缓存,然后输出这个地址到数字栈,使用这个地址作为开始,然后使用固定长度,我们可以输出硬盘的内容。比如为了打印第一行可以使用下面的语句
CR 1 BLOCK 64 TYPE return-key
为了打印8行可以这样
CR 1 BLOCK 512 + 64 TYPE
这里面包含一个与TYPE相似的操作wordTRAILING 截取字符串的一部分到

3 字符串移动word

在内存中移动字符串或者数组数据非常容易,只需要使用三个参数,源地址 目标地址和字节数 如下

CMOVE (addr1 addr2 u --) 从addr1开始移动n个字节字符串到addr2,移动方式是byte-by-byte

CMOVE>  (addr1 addr2 u --) 从addr1开始移动n个字节字符串到addr2,移动方式是c-by-c

MOVE       (addr1 addr2 u --)addr2保存addr1开始的u个字节字符串

对于字符串移动运算符我们需要注意的
源地址在目的地址之前,地址在字节数之前
通常的组织是(source destination count --)

对比CMOVE CMOVE> MOVE的效果如下


210 BLOCK PAD 1024 CMOVE    (使用一个byte覆盖)
210 BLCOK PAD 1024 CMOVE>  (字符串整个移动)
210 BLOCK PAD 1024 MOVE      (cell移动)
QQ截图20160428140923.jpg

移动)

4 单字符输入

另外还可以使用KEY等待终端键盘上一个字符的输入,然后将输入字符的ASICC值存储到数字栈中。

需要注意的是KEY具有等待键盘输入的功能,因此在输入KEY return-key后,并没有输出ok,而是移动光标等待输入内容,如果输入字母A,那么会将A的ASCII码65存储到数字栈中

也可在定义中使用KEY。这样定义将会停止运行,等待KEY内容的输入,下面的语句将会等待输入内容

: BOLCKS (count --)
    SCR @ + SCR @ DO I LIST KEY DROP LOOP ;

我们可以在其中增加如果输入回车键就停止循环。

31 CONSTANT #EOL
: BLOCKS  (count -- )
  SCR @ +
  SCR @ DO  I LIST
        KEY #EOL =   IF LEAVE THEN
             LOOP ;

5 底层的输入命令

在字符串输入中还包含如下几个底层的words

ACCEPT  (c-addr  u1 -- u2 从终端键盘中接受u个字符然后存储到开始地址处,然后返回接受到的字符数量)
WORD   (c -- addr) 从字符输入流中获取一个word,使用参数c作为分割符,然后以字符数位开始和字符串内容存储到HERE的位置,返回存储的地址到数字栈 (截取输入流,存储到内存,返回内存地址)

ACCEPT也可以终止运行等待键盘的输入,期待给出数量的输入或者回车键才会进响应,然后将接受到的字符串存储到给出地址的位置返回接受的字符数量
TIB 80 ACCEPT
等待80个字符的输入,然后存储到TIB(Terminal Input Buffer)。

这个语句会在QUIT的定义中使用用来获取INTERPRET的输入内容

可以假设QUIT中包含如下定义
... TIB 80 ACCEPT #TIV ! INTERPRET ...
那么解释器INTERPRET如何从终端输入流中截取word呢?
通常使用如下语句BL WORD
WORD会扫描输入流使用第一个作为分隔符(BL)。然后将获取的word存储到解释器的缓存中,并且使用第一个个位置保存接受的数量。然后返回保存的地址到数字栈,解释器可以知道去内存查找位置
默认的WORD的缓存地址是HERE。

因此WORD会在终端输入流中查询分割符,然后将截取的字符串存储到WORD的缓存区,其中第一位保存字节数

WORD

在终端直接输入WORD,将会从TIB中进行扫描。移动过程中会移动缓存指针>IN。因此每次执行WORD,都会在输入流中查找下个WORD。另外WORD在>IN @的值大于#TIB@的值时将会停止扫描。

>IN称为相对指针。并不包含真正的地址而是保持一个真正地址的偏移量,通常以TIB作为起始地址计算,比如扫描STAR,过程中将会保持>IN值为5

WORD会将字符前面的空格忽略掉。
下面我们使用WORD定义一个TEXT
: TEXT ( delimiter -- ) PAD 258 Bl FILL WORD COUNT PAD SWAP MOVE
TEXT也会在输入流中扫描查找分割符,然后将获取的内容存储到PAD中,最为精彩的是TEXT在移动字符串前使用FILL清空了PAD,非常方便使用TYPE,例如
CREATE my-name 40 ALLOT
: I'M BL TEXT PAD my-name 40 MOVE ;
我们首先定义了一个my-name数组。第二个定义了I'M允许我们输入如下语句I'M EDWARD return-key

在这个定义中首先使用BL截取内容,然后将这个移动到pad中
而PAD my-name 40 MOVE又会移动pad的40字节内容到数组中

我们可以定义下面的GREET
: GREET ." HELLo, " my-name 40 -TRAILING TYPE ." , I Speak Forth. " ;

然而使用空格可能将双字符名字的分割,为了获取完整的输入,我们并不需要在换行前获取分隔符,因此可以使用1 TEXT
这样一来会获取整个输入内容,直到允许的大小
通过使用其他分隔符,我们可以获取一系列的字符串和存储到不同的数组中。

我们讲解的输入内容包括如下 数字(存储到数字栈) 字符串(查找字符)。
如果需要我们可以将ACCEPT放置到定义中我们可以获取任意的输入请求,

6 数字转换

在终端中输入数字后,Forth会自动将其转换为二进制的值存储到数字栈,Forth还允许将字符转换为二进制值存储到内存中

>NUMBER (ud1 c-addr1 u1 -- ud2 c-addr2 u2)

举例说明如下
: PLUS 0. BL WORD COUNT >NUMBER 2DROP DROP + ." = " . ;

PLUS提供了中缀允许格式
2 PLUS 13 return-key 输出= 15 ok
数字2存储到数字栈,然而3会作为字符串读取

可以使用>NUMBER创建特定数字转换规则,然后第一个转换字符的地址,可以判断连字符 点灯内容,可以判断转换后的数字

下面介绍一个接受任意字符的版本,其中: , - . /返回双字长数字其余返回单字长

其中的WITHIN在前面定义过
Wimbledon可以使用PUNCT包含一个标志位判断是否遇到上面的数字类型判断符号

数字输入程序

7 深入理解Word

上面只是介绍了使用WORD扫描终端输入内容,也就是TIB包含的ACCEPT从终端接受的输入内容

如果再次使用解释器调用BL WORD那么我们可以扫描终端输入,字符串,硬盘中的内容

为了实现这个功能通常将WORD使用其他的指针标志>IN。也就是标志内存(EVALUATE) 硬盘(LOAD INCLUDED)或者终端输入流

一个比较有意义的word是COUNT。WORD将接受的长度存储到缓存区,然后返回这个字节的地址到数字栈


WORD

而COUNT可以将计数值存储到数字栈然后增加地址值,


COUNT
COUNT  (addr -- addr+1 u) 将地址指向的长度存储到数字栈,然后自增字符地址。

8 字符串比较

COMPARE (c-addr1 u1 c-addr2 u2 -- n)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • C/C++输入输出流总结 前两天写C++实习作业,突然发现I/O是那么的陌生,打了好长时间的文件都没有打开,今天终...
    LuckTime阅读 1,706评论 0 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 浅谈C++常用输入输出 在编写C++程序的时候,经常因为输入输出头疼,所以在这里做一个小结,记录一下常用的输入输出...
    MinoyJet阅读 3,743评论 0 6
  • c++的输入输出不是建立在语言上的,而是由iostream和fstream文件中定义的一组模板类实现的,且这个类库...
    Tianql阅读 3,286评论 0 1
  • 链接:http://naotu.baidu.com/file/066f228731f401d54b19d033e6...
    LuckyS007阅读 229评论 0 0