Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu.Wang 2019-01-07 21:39:36 +08:00
commit ad64631baf
14 changed files with 1114 additions and 191 deletions

View File

@ -1,123 +1,92 @@
保护代码完整性(六):在 Git 上使用 PGP
======
> 我们继续我们的 PGP 实践系列,来看看签名标签的标签和提交,这可以帮你确保你的仓库没有被篡改。
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/global-network.jpg?itok=h_hhZc36)
在本系列教程中,我们提供了一个使用 PGP 的实用指南,包括基本概念和工具、生成和保护你的密钥。如果你错过了前面的文章,你可以查看下面的链接。在这篇文章中,我们谈一谈在 Git 中如何集成 PGP、使用签名的标签然后介绍签名提交最后添加签名推送的支持。
[第一部分:基本概念和工具][1]
[第二部分:生成你的主密钥][2]
[第三部分:生成 PGP 子密钥][3]
[第四部分:将主密钥移到离线存储中][4]
[第五部分:将子密钥移到硬件设备中][5]
- [第一部分:基本概念和工具][1]
- [第二部分:生成你的主密钥][2]
- [第三部分:生成 PGP 子密钥][3]
- [第四部分:将主密钥移到离线存储中][4]
- [第五部分:将子密钥移到硬件设备中][5]
Git 的核心特性之一就是它的去中心化本质 —— 一旦仓库克隆到你的本地系统,你就拥有了项目的完整历史,包括所有的标签、提交和分支。然而由于存在着成百上千的克隆仓库,如何才能验证你下载的仓库没有被恶意的第三方做过篡改?你可以从 GitHub 或一些貌似官方的位置来克隆它们,但是如果有些人故意欺骗了你怎么办?
或者在你参与的一些项目上发现了后门,而 "Author" 行显示是你干的,然而你很确定 [不是你干的][6],会发生什么情况?
或者在你参与的一些项目上发现了后门,而 “Author” 行显示是你干的,然而你很确定 [不是你干的][6],会发生什么情况?
为解决上述问题Git 添加了 PGP 集成。签名的标签通过确认它的内容与创建这个标签的开发者的工作站上的内容完全一致来证明仓库的完整性,而签名的提交几乎是不可能在不访问你的 PGP 密钥的情况下能够假冒你。
### 清单
* 了解签名的标签、提交、和推送(必要)
#### 清单
* 了解签名的标签、提交和推送(必要)
* 配置 git 使用你的密钥(必要)
* 学习如何签名标签和验证工作(必要)
* 配置 git 总是签名注释的标签(推荐)
* 学习如何签名提交和验证工作(必要)
* 学习标签如何签名和验证(必要)
* 配置 git 总是签名带注释标签(推荐)
* 学习提交如何签名和验证工作(必要)
* 配置 git 总是签名提交(推荐)
* 配置 gpg-agent 选项(必要)
#### 考虑事项
git 实现了 PGP 的多级集成,首先从签名标签开始,接着介绍签名提交,最后添加签名推送的支持。
##### 了解 Git 哈希
### 考虑事项
git 是一个复杂的东西,为了你能够更好地掌握它如何集成 PGP你需要了解什么是”哈希“。我们将它归纳为两种类型的哈希树哈希和提交哈希。
Git 实现了 PGP 的多级集成,首先从签名标签开始,接着介绍签名提交,最后添加签名推送的支持。
#### 了解 Git 哈希
Git 是一个复杂的东西,为了你能够更好地掌握它如何集成 PGP你需要了解什么是”哈希“。我们将它归纳为两种类型的哈希树哈希和提交哈希。
##### 树哈希
###### 树哈希
每次你向仓库提交一个变更对于仓库中的每个子目录git 都会记录它里面所有对象的校验和哈希 —— 内容blobs、目录trees、文件名和许可等等。它只对每次提交中发生变更的树和内容做此操作这样在只变更树的一小部分时就不必去重新计算整个树的校验和。
然后再计算和存储处于顶级的树的校验和,这样如果仓库的任何一部分发生变化,校验和将不可避免地发生变化。
##### 提交哈希
###### 提交哈希
一旦创建了树哈希git 将计算提交哈希,它将包含有关仓库和变更的下列信息:
* 树哈希的校验和
* 变更前树哈希的校验和(父级)
* 有关作者的信息名字、email、创作时间
* 有关提交者的信息名字、email、提交时间
* 提交信息
##### 哈希函数
###### 哈希函数
在写这篇文章时,虽然研究一种更强大的、抗碰撞的算法的工作正在进行,但 git 仍然使用的是 SHA1 哈希机制去计算校验和。注意git 已经包含了碰撞防范程序,因此认为对 git 成功进行碰撞攻击仍然是不可行的。
#### 注释标签和标签签名
#### 注释标签和标签签名
在每个 Git 仓库中,标签允许开发者标记特定的提交。标签可以是 “轻量级的” —— 几乎只是一个特定提交上的指针,或者它们可以是 “注释的”,它成为 git 树中自己的项目。一个注释的标签对象包含所有下列的信息:
* 成为标签的提交哈希的校验和
在每个 Git 仓库中,标签允许开发者标记特定的提交。标签可以是 “轻量级的” —— 几乎只是一个特定提交上的指针,或者它们可以是 “带注释的”,它自己将成为 git 树中的项目。一个带注释标签对象包含所有下列的信息:
* 成为标签的提交的哈希的校验和
* 标签名字
* 关于打标签的人的信息名字、email、打标签时间
* 标签信息
一个 PGP 签名的标签是一个带有将所有这些条目封装进一个 PGP 签名的注释标签。当开发者签名他们的 git 标签时,他们实际上是向你保证了如下的信息:
一个 PGP 签名的标签是一个带有将所有这些条目封装进一个 PGP 签名的带注释标签。当开发者签名他们的 git 标签时,他们实际上是向你保证了如下的信息:
* 他们是谁(以及他们为什么应该被信任)
* 他们在签名时的仓库状态是什么样:
* 标签包含提交的哈希
* 提交哈希包含了顶级树的哈希
* 顶级哈希包含了所有文件、内容和子树的哈希
* 标签包含的提交的哈希
* 提交的哈希包含了顶级树的哈希
* 顶级树哈希包含了所有文件、内容和子树的哈希
* 它也包含有关作者的所有信息
* 包含变更发生时的精确时间
当你克隆一个仓库并验证一个签名标签时,就是向你以密码方式保证仓库中的所有内容、包括所有它的历史,与开发者签名时在它的计算机上的仓库完全一致。
当你克隆一个仓库并验证一个签名的标签时,就是向你以密码方式保证:仓库中的所有内容、包括所有它的历史,与开发者签名时在它的计算机上的仓库完全一致。
#### 签名的提交
签名的提交与签名的标签非常类似 —— 提交对象的内容是 PGP 签名过的,而不是标签对象的内容。一个提交签名也给你提供了开发者签名时开发者树上的全部可验证信息。标签签名和提交 PGP 签名提供了有关仓库和它的完整历史的完全一致的安全保证。
签名的提交与签名的标签非常类似 —— PGP 签名的是提交对象的内容,而不是标签对象的内容。一个提交签名也给你提供了开发者签名时开发者树上的全部可验证信息。标签签名和提交 PGP 签名提供了有关仓库和它的完整历史的完全一致的安全保证。
#### 签名的推送
为了完整起见在这里包含了签名的推送这一功能因为在你使用这个功能之前需要在接收推送的服务器上先启用它。正如我们在上面所说过的PGP 签名一个 git 对象就是提供了开发者的 git 树当时的可验证信息,但不提供开发者对那个树意图相关的信息。
比如,你可以在你自己 fork 的 git 仓库的一个实验分支上尝试一个很酷的特性,为了评估它,你提交了你的工作,但是有人在你的代码中发现了一个恶意的 bug。由于你的提交是经过正确签名的因此有人可能将包含有恶意 bug 的分支推入到 master 分支中,从而在生产系统中引入一个漏洞。由于提交是经过你的密钥正确签名的,所以一切看起来都是合理合法的,而当 bug 被发现时,你的声誉就会因此而受到影响。
比如,你可以在你自己复刻的 git 仓库的一个实验分支上尝试一个很酷的特性,为了评估它,你提交了你的工作,但是有人在你的代码中发现了一个恶意的 bug。由于你的提交是经过正确签名的因此有人可能将包含有恶意 bug 的分支推入到 master 分支中,从而在生产系统中引入一个漏洞。由于提交是经过你的密钥正确签名的,所以一切看起来都是合理合法的,而当 bug 被发现时,你的声誉就会因此而受到影响。
`git push` 时,为了验证提交的意图而不仅仅是验证它的内容,添加了要求 PGP 推送签名的功能。
@ -125,47 +94,48 @@ Git 是一个复杂的东西,为了你能够更好地掌握它如何集成 PGP
如果在你的钥匙环上只有一个密钥,那么你就不需要再做额外的事了,因为它是你的默认密钥。
然而,如果你有多个密钥,那么你必须要告诉 git 去使用哪一个密钥。([fpr] 是你的密钥的指纹):
然而,如果你有多个密钥,那么你必须要告诉 git 去使用哪一个密钥。(`[fpr]` 是你的密钥的指纹):
```
$ git config --global user.signingKey [fpr]
```
注意:如果你有一个不同的 gpg2 命令,那么你应该告诉 git 总是去使用它,而不是传统的版本 1 的 gpg
注意:如果你有一个不同的 `gpg2` 命令,那么你应该告诉 git 总是去使用它,而不是传统的版本 1 的 `gpg`
```
$ git config --global gpg.program gpg2
```
#### 如何使用签名标签
创建一个签名的标签,只要传递一个简单地 -s 开关给 tag 命令即可:
创建一个签名的标签,只要传递一个简单地 `-s` 开关给 `tag` 命令即可:
```
$ git tag -s [tagname]
```
我们建议始终对 git 标签签名,这样让其它的开发者确信他们使用的 git 仓库没有被恶意地修改过(比如,引入后门):
##### 如何验证签名的标签
验证一个签名的标签,只需要简单地使用 verify-tag 命令即可:
验证一个签名的标签,只需要简单地使用 `verify-tag` 命令即可:
```
$ git verify-tag [tagname]
```
如果你要验证其他人的 git 标签,那么就需要你导入他的 PGP 公钥。请参考 “可信任的团队沟通” 一文中关于此主题的指导。
##### 在拉取时验证
如果你从项目仓库的其它 fork 中拉取一个标签git 将自动验证签名,并在合并操作时显示结果:
如果你从项目仓库的其它复刻中拉取一个标签git 将自动验证签名,并在合并操作时显示结果:
```
$ git pull [url] tags/sometag
```
合并信息将包含类似下面的内容:
```
Merge tag 'sometag' of [url]
@ -173,128 +143,123 @@ Merge tag 'sometag' of [url]
# gpg: Signature made [...]
# gpg: Good signature from [...]
```
#### 配置 git 始终签名注释的标签
#### 配置 git 始终签名带注释标签
很可能的是,你正在创建一个带注释标签,你应该去签名它。强制 git 始终签名带注释的标签,你可以设置一个全局配置选项:
很可能的是,你正在创建一个带注释的标签,你应该去签名它。强制 git 始终签名带注释的标签,你可以设置一个全局配置选项:
```
$ git config --global tag.forceSignAnnotated true
```
或者,你始终记得每次都传递一个 -s 开关:
或者,你始终记得每次都传递一个 `-s` 开关:
```
$ git tag -asm "Tag message" tagname
```
#### 如何使用签名提交
#### 如何使用签名提交
创建一个签名提交很容易,但是将它纳入到你的工作流中却很困难。许多项目使用签名提交作为一种 "Committed-by:” 的等价行,它记录了代码来源 —— 除了跟踪项目历史外,签名很少有人去验证。在某种意义上,签名的提交用于 ”篡改证据“,而不是 git 工作流的 ”篡改证明“。
创建一个签名的提交很容易,但是将它纳入到你的工作流中却很困难。许多项目使用签名的提交作为一种 “Committed-by:” 的等价行,它记录了代码来源 —— 除了跟踪项目历史外,签名很少有人去验证。在某种意义上,签名的提交用于 “篡改证据”,而不是 git 工作流的 “篡改证明”。
为创建一个签名的提交,你只需要 `git commit` 命令传递一个 `-S` 标志即可(由于它与另一个标志冲突,所以改为大写的 `-S`
为创建一个签名的提交,你只需要 `git commit` 命令传递一个 -S 标志即可(由于它与另一个标志冲突,所以改为大写的 -S
```
$ git commit -S
```
我们建议始终使用签名提交,并要求项目所有成员都这样做,这样其它人就可以验证它们(下面就讲到如何验证)。
##### 如何去验证签名的提交
验证签名的提交需要使用 verify-commit 命令:
验证签名的提交需要使用 `verify-commit` 命令:
```
$ git verify-commit [hash]
```
你也可以查看仓库日志,要求所有提交签名是被验证和显示的:
```
$ git log --pretty=short --show-signature
```
##### 在 git merge 时验证提交
##### 在 git 合并时验证提交
如果项目的所有成员都签名了他们的提交,你可以在合并时强制进行签名检查(然后使用 `-S` 标志对合并操作本身进行签名):
如果项目的所有成员都签名了他们的提交,你可以在合并时强制进行签名检查(然后使用 -S 标志对合并操作本身进行签名):
```
$ git merge --verify-signatures -S merged-branch
```
注意,如果有一个提交没有签名或验证失败,将导致合并操作失败。通常情况下,技术是最容易的部分 —— 而人的因素使得项目中很难采用严格的提交验证。
##### 如果你的项目在补丁管理上采用邮件列表
如果你的项目在提交和处理补丁时使用一个邮件列表,那么一般很少使用签名提交,因为通过那种方式发送时,签名信息将会丢失。对提交进行签名仍然是非常有用的,这样引用你托管在公开 git 树的其他人就能以它作为参考,但是上游项目接收你的补丁时,仍然不能直接使用 git 去验证它们。
如果你的项目在提交和处理补丁时使用一个邮件列表,那么一般很少使用签名提交,因为通过那种方式发送时,签名信息将会丢失。对提交进行签名仍然是非常有用的,这样其他人就能引用你托管在公开 git 树作为参考,但是上游项目接收你的补丁时,仍然不能直接使用 git 去验证它们。
尽管,你仍然可以签名包含补丁的电子邮件。
#### 配置 git 始终签名提交
你可以告诉 git 总是签名提交:
```
git config --global commit.gpgSign true
```
或者你每次都记得给 `git commit` 操作传递一个 -S 标志(包括 —amend
或者你每次都记得给 `git commit` 操作传递一个 `-S` 标志(包括 `—amend`)。
#### 配置 gpg-agent 选项
GnuPG agent 是一个守护工具,它能在你使用 gpg 命令时随时自动启动,并运行在后台来缓存私钥的密码。这种方式让你只需要解锁一次密钥就可以重复地使用它(如果你需要在一个自动脚本中签署一组 git 操作,而不需要重复输入密钥,这种方式就很方便)。
GnuPG agent 是一个守护工具,它能在你使用 gpg 命令时随时自动启动,并运行在后台来缓存私钥的密码。这种方式让你只需要解锁一次密钥就可以重复地使用它(如果你需要在一个自动脚本中签署一组 git 操作,而不重复输入密钥,这种方式就很方便)。
为了调整缓存中的密钥过期时间,你应该知道这两个选项:
* default-cache-ttl如果在 time-to-live 过期之前再次使用同一个密钥,这个倒计时将重置成另一个倒计时周期。缺省值是 60010 分钟)。
* `default-cache-ttl`(秒):如果在 TTL 过期之前再次使用同一个密钥,这个倒计时将重置成另一个倒计时周期。缺省值是 60010 分钟)。
* `max-cache-ttl`(秒):自首次密钥输入以后,不论最近一次使用密钥是什么时间,只要最大值的 TTL 倒计时过期,你将被要求再次输入密码。它的缺省值是 30 分钟。
* max-cache-ttl自首次密钥输入以后不论最近一次使用密钥是什么时间只要最大值的 time-to-live 倒计时过期,你将被要求再次输入密码。它的缺省值是 30 分钟。
如果你认为这些缺省值过短(或过长),你可以编辑 `~/.gnupg/gpg-agent.conf` 文件去设置你自己的值:
如果你认为这些缺省值过短(或过长),你可以编辑 ~/.gnupg/gpg-agent.conf 文件去设置你自己的值:
```
# set to 30 minutes for regular ttl, and 2 hours for max ttl
default-cache-ttl 1800
max-cache-ttl 7200
```
##### 额外好处:与 ssh 一起使用 gpg-agent
##### 补充:与 ssh 一起使用 gpg-agent
如果你创建了一个 [A](验证)密钥,并将它移到了智能卡,你可以将它用到 ssh 上,为你的 ssh 会话添加一个双因子验证。为了与 agent 沟通你只需要告诉你的环境去使用正确的套接字文件即可。
首先,添加下列行到你的 ~/.gnupg/gpg-agent.conf 文件中:
首先,添加下列行到你的 `~/.gnupg/gpg-agent.conf` 文件中:
```
enable-ssh-support
```
接着,添加下列行到你的 .bashrc 文件中:
接着,添加下列行到你的 `.bashrc` 文件中:
```
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
```
为了让改变生效,你需要 kill 掉正在运行的 gpg-agent 进程,并重新启动一个新的登入会话:
为了让改变生效,你需要杀掉正在运行的 gpg-agent 进程,并重新启动一个新的登入会话:
```
$ killall gpg-agent
$ bash
$ ssh-add -L
```
最后的命令将列出代表你的 PGP Auth 密钥的 SSH注释应该会在结束的位置显示 cardno:XXXXXXXX表示它来自智能卡
为了启用 ssh 的基于密钥的登入,只需要在你要登入的远程系统上添加 `ssh-add -L` 的输出到 ~/.ssh/authorized_keys 中。祝贺你,这将使你的 SSH 登入凭据更难以窃取。
为了启用 ssh 的基于密钥的登入,只需要在你要登入的远程系统上添加 `ssh-add -L` 的输出到 `~/.ssh/authorized_keys` 中。祝贺你,这将使你的 SSH 登入凭据更难以窃取。
此外,你可以从公共密钥服务器上下载其它人的基于 PGP 的 ssh 公钥,这样就可以赋予他登入 ssh 的权利:
作为一个福利,你可以从公共密钥服务器上下载其它人的基于 PGP 的 ssh 公钥,这样就可以赋予他登入 ssh 的权利:
```
$ gpg --export-ssh-key [keyid]
```
如果你有让开发人员通过 ssh 来访问 git 仓库的需要,这将让你非常方便。下一篇文章,我们将提供像保护你的密钥那样保护电子邮件帐户的小技巧。
@ -305,14 +270,14 @@ via: https://www.linux.com/blog/learn/pgp/2018/3/protecting-code-integrity-pgp-p
作者:[KONSTANTIN RYABITSEV][a]
译者:[qhwdw](https://github.com/qhwdw)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://www.linux.com/users/mricon
[1]:https://www.linux.com/blog/learn/2018/2/protecting-code-integrity-pgp-part-1-basic-pgp-concepts-and-tools
[2]:https://www.linux.com/blog/learn/pgp/2018/2/protecting-code-integrity-pgp-part-2-generating-and-protecting-your-master-pgp-key
[3]:https://www.linux.com/blog/learn/pgp/2018/2/protecting-code-integrity-pgp-part-3-generating-pgp-subkeys
[4]:https://www.linux.com/blog/learn/pgp/2018/3/protecting-code-integrity-pgp-part-4-moving-your-master-key-offline-storage
[5]:https://www.linux.com/blog/learn/pgp/2018/3/protecting-code-integrity-pgp-part-5-moving-subkeys-hardware-device
[1]:https://linux.cn/article-9524-1.html
[2]:https://linux.cn/article-9529-1.html
[3]:https://linux.cn/article-9607-1.html
[4]:https://linux.cn/article-10402-1.html
[5]:https://linux.cn/article-10415-1.html
[6]:https://github.com/jayphelps/git-blame-someone-else

View File

@ -1,23 +1,25 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-10420-1.html)
[#]: subject: (4 cool new projects to try in COPR for December 2018)
[#]: via: (https://fedoramagazine.org/4-try-copr-december-2018/)
[#]: author: (Dominik Turecek https://fedoramagazine.org)
2018 年 12 月值得尝试的 4个很酷的 COPR 新项目
COPR 仓库中 4 个很酷的新软件2018.12
======
![](https://fedoramagazine.org/wp-content/uploads/2017/08/4-copr-945x400.jpg)
COPR 是个人软件仓库[集合][1],它不在 Fedora 中。某些软件不符合允许轻松打包的标准。或者它可能不符合其他 Fedora 标准尽管它是自由开源的。COPR 可在 Fedora套件之外提供这些项目。COPR 中的软件不受 Fedora 基础设施的支持,或由项目自己签名。但是,这是尝试新的或实验性软件的一种很好的方法。
COPR 是软件的个人存储库的[集合][1],它包含那些不在标准的 Fedora 仓库中的软件。某些软件不符合允许轻松打包的标准。或者它可能不符合其他 Fedora 标准尽管它是自由开源的。COPR 可以在标准的 Fedora 包之外提供这些项目。COPR 中的软件不受 Fedora 基础设施的支持,或者是由项目自己背书的。但是,它是尝试新的或实验性软件的一种很好的方法。
这是 COPR 中一组新的有趣项目。
### MindForger
[MindForger][2] 是 Markdown 编辑器和笔记本。除了你希望的 Markdown 编辑器的功能之外MindForger 还允许你将单个文件拆分为多个笔记。组织笔记并在文件之间移动、搜索它们都很容易。我已经使用 MindForger 一段时间来记录学习笔记了,现在可以在 COPR 中找到它啦。
[MindForger][2] 是一个 Markdown 编辑器和笔记本。除了你预期的 Markdown 编辑器的功能之外MindForger 还允许你将单个文件拆分为多个笔记。组织笔记并在文件之间移动、搜索它们都很容易。我已经使用 MindForger 一段时间来记录学习笔记了,现在可以在 COPR 中找到它啦。
![][3]
#### 安装说明
@ -31,7 +33,7 @@ sudo dnf install mindforger
### Clingo
[Clingo][4] 是使用[回答集编程][5] ASP 建模语言解决逻辑问题的程序。使用 ASP你可以将问题声明为一个 Clingo 逻辑程序然后解决。最后Clingo 以逻辑模型的形式产生问题的解决方案,称为回答集。
[Clingo][4] 是使用[回答集编程][5]ASP建模语言解决逻辑问题的程序。使用 ASP你可以将问题声明为一个逻辑程序,然后 Clingo 来解决。最后Clingo 以逻辑模型的形式产生问题的解决方案,称为回答集。
#### 安装说明
@ -44,7 +46,8 @@ sudo dnf install clingo
### SGVrecord
[SGVrecord][6] 是一个用于录制屏幕的简单工具。它允许你捕获整个屏幕或仅选择其中的一部分。此外有没有声音都可以进行录制。Sgvrecord 以 WebM 格式生成文件。
[SGVrecord][6] 是一个用于录制屏幕的简单工具。它允许你捕获整个屏幕或仅选择其中的一部分。此外有没有声音都可以进行录制。SGVrecord 以 WebM 格式生成文件。
![][7]
#### 安装说明
@ -77,7 +80,7 @@ via: https://fedoramagazine.org/4-try-copr-december-2018/
作者:[Dominik Turecek][a]
选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
@ -90,4 +93,4 @@ via: https://fedoramagazine.org/4-try-copr-december-2018/
[5]: https://en.wikipedia.org/wiki/Answer_set_programming
[6]: https://github.com/yucefsourani/sgvrecord
[7]: https://fedoramagazine.org/wp-content/uploads/2018/12/SGVrecord.png
[8]: https://facebook.github.io/watchman/
[8]: https://facebook.github.io/watchman/

View File

@ -1,3 +1,4 @@
zgj1024 is translating
My Lisp Experiences and the Development of GNU Emacs
======

View File

@ -1,3 +1,5 @@
Translating By Guevaraya
Linux/Unix App For Prevention Of RSI (Repetitive Strain Injury)
======
![workrave-image][1]

View File

@ -0,0 +1,232 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 1 OK01)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html)
[#]: author: (Robert Mullins http://www.cl.cam.ac.uk/~rdm34)
Computer Laboratory Raspberry Pi: Lesson 1 OK01
======
The OK01 lesson contains an explanation about how to get started and teaches how to enable the 'OK' or 'ACT' **LED** on the Raspberry Pi board near the RCA and USB ports. This light was originally labelled OK but has been renamed to ACT on the revision 2 Raspberry Pi boards.
### 1 Getting Started
I am assuming at this point that you have already visited the [Downloads][1] page, and got the necessary GNU Toolchain. Also on the downloads page is a file called OS Template. Please download this and extract its contents to a new directory.
### 2 The Beginning
```
The '.s' file extension is commonly used for all forms of assembly code, it is up to us to remember this is ARMv6.
```
Now that you have extracted the template, create a new file in the 'source' directory called 'main.s'. This file will contain the code for this operating system. To be explicit, the folder structure should look like:
```
build/
(empty)
source/
main.s
kernel.ld
LICENSE
Makefile
```
Open 'main.s' in a text editor so that we can begin typing assembly code. The Raspberry Pi uses a variety of assembly code called ARMv6, so that is what we'll need to write in.
Copy in these first commands.
```
.section .init
.globl _start
_start:
```
As it happens, none of these actually do anything on the Raspberry Pi, these are all instructions to the assembler. The assembler is the program that will translate between assembly code that we understand, and binary machine code that the Raspberry Pi understands. In Assembly Code, each line is a new command. The first line here tells the Assembler[1] where to put our code. The template I provided causes the code in the section called .init to be put at the start of the output. This is important, as we want to make sure we can control which code runs first. If we don't do this, the code in the alphabetically first file name will run first! The .section command simply tells the assembler which section to put the code in, from this point until the next .section or the end of the file.
```
In assembly code, you may skip lines, and put spaces before and after commands to aid readability.
```
The next two lines are there to stop a warning message and aren't all that important.[2]
### 3 The First Line
Now we're actually going to code something. In assembly code, the computer simply goes through the code, doing each instruction in order, unless told otherwise. Each instruction starts on a new line.
Copy the following instruction.
```
ldr r0,=0x20200000
```
```
ldr reg,=val puts the number val into the register named reg.
```
That is our first command. It tells the processor to store the number 0x20200000 into the register r0. I shall need to answer two questions here, what is a register, and how is 0x20200000 a number?
```
A single register can store any integer between 0 and 4,294,967,295 inclusive on the Raspberry Pi, which might seem like a large amount of memory, but it is only 32 binary bits.
```
A register is a tiny piece of memory in the processor, which is where the processor stores the numbers it is working on right now. There are quite a few of these, many of which have a special meaning, which we will come to later. Importantly there are 13 (named r0,r1,r2,...,r9,r10,r11,r12) which are called General Purpose, and you can use them for whatever calculations you need to do. Since it's the first, I've used r0 in this example, but I could very well have used any of the others. As long as you're consistent, it doesn't matter.
0x20200000 is indeed a number. However it is written in Hexadecimal notation. To learn more about hexadecimal expand the box below:
```
Hexadecimal is an alternate system for writing numbers. You may only be aware of the decimal system for writing numbers in which we have 10 digits: 0,1,2,3,4,5,6,7,8 and 9. Hexadecimal is a system with 16 digits: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e and f.
![567 is 5 hundreds, 6 tens and 7 units.][2]
You may recall being taught how decimal numbers work in terms of place value. We say that the rightmost digits is the 'units' digits, the next one left is the 'tens' digit, the next is the 'hundreds' digit, and so on. What this actually meant is, the number is 100 × the value in the 'hundreds' digit, plus 10 × the value in the 'tens' digit, plus 1 × the value in the units digit.
![567 is 5x10^2+6x10^1+7x10^0][3]
More mathematically, we can now spot the pattern and say that the rightmost digit is the 100=1s digit, the next left is the 101=10s digit, the next is 102=100s digit, and so on. We have all agreed on the system that 0 is the lowest digit, 1 is the next and so on. But what if we used a different number instead of 10 in these powers? Hexadecimal is just the system in which we use 16 instead.
![567 = 5x10^2+6x10^1+7x10^0 = 2x16^2+3x16^1+7x16^0][4]
The mathematics to the right shows that the number 567 in decimal is equivalent to the number 237 in hexadecimal. Often when we need to be clear about what system we're using to write numbers in we put 10 for decimal and 16 for hexadecimal. Since it's difficult to write small numbers in assembly code, we use 0x instead to represent a number in hexadecimal notation. So 0x237 means 23716.
So where do a,b,c,d,e and f come in? Well, in order to be able to write every number in hexadecimal, we need extra digits. For example 916 = 9×160 = 910, but 1016 = 1×161 + 1×160 = 1610. So if we just used 0,1,2,3,4,5,6,7,8 and 9 we would not be able to write 1010, 1110, 1210, 1310, 1410, 1510. So we introduce 6 new digits such that a16 = 1010, b16 = 1110, c16 = 1210, d16 = 1310, e16 = 1410, f16 = 1510
So, we now have another system for writing numbers. But why did we bother? Well, it turns out that since computers always work in binary, hexadecimal notation is very useful because every hexadecimal digit is exactly four binary digits long. This has the nice side effect that a lot of computer numbers are round numbers in hexadecimal, even though they're not in decimal. For example, in the assembly code just above I used the number 2020000016. If I had chose to write this in decimal it would have been 53896806410, which is much less memorable.
To convert numbers from decimal to hexadecimal I find the following method easiest:
![Conversion example][5]
1. Start with the decimal number, say 567.
2. Divide by 16 and calculate the remainder. For example 567 ÷ 16 = 35 remainder 7.
3. The remainder is the last digit of the answer in hexadecimal, in the example this is 7.
4. Repeat steps 2 and 3 again with the result of the last division until the result is 0. For example 35 ÷ 16 = 2 remainder 3, so 3 is the next digit of the answer. 2 ÷ 16 = 0 remainder 2, so 2 is the next digit of the answer.
5. Once the result of the division is 0, you can stop. The answer is just the remainders in the reverse order to which you got them, so 56710 = 23716.
To convert hexadecimal numbers back to decimal, it is easiest to expand out the number, so 23716 = 2×162 + 3×161 +7 ×160 = 2×256 + 3×16 + 7×1 = 512 + 48 + 7 = 567.
```
So our first command is to put the number 2020000016 into r0. That doesn't sound like it would be much use, but it is. In computers, there are an awful lot of chunks of memory and devices. In order to access them all, we give each one an address. Much like a postal address or a web address this is just a means of identifying the location of the device or chunks of memory we want. Addresses in computers are just numbers, and so the number 2020000016 happens to be the address of the GPIO controller. This is just a design decision taken by the manufacturers, they could have used any other address (providing it didn't conflict with anything else). I know this address only because I looked it up in a manual[3], there is no particular system to the addresses (other than that they are all large round numbers in hexadecimal).
### 4 Enabling Output
![A diagram showing key parts of the GPIO controller.][6]
Having read the manual, I know we're going to need to send two messages to the GPIO controller. We need to talk its language, but if we do, it will obligingly do what we want and turn on the OK LED. Fortunately, it is such a simple chip, that it only needs a few numbers in order to understand what to do.
```
mov r1,#1
lsl r1,#18
str r1,[r0,#4]
```
```
mov reg,#val puts the number val into the register named reg.
lsl reg,#val shifts the binary representation of the number in reg by val places to the left.
str reg,[dest,#val] stores the number in reg at the address given by dest + val.
```
These commands enable output to the 16th GPIO pin. First we get a necessary value in r1, then send it to the GPIO controller. Since the first two instructions are just trying to get a value into r1, we could use another ldr command as before, but it will be useful to us later to be able to set any given GPIO pin, so it is better to deduce the value from a formula than write it straight in. The OK LED is wired to the 16th GPIO pin, and so we need to send a command to enable the 16th pin.
The value in r1 is needed to enable the LED pin. The first line puts the number 110 into r1. The mov command is faster than the ldr command, because it does not involve a memory interaction, whereas ldr loads the value we want to put into the register from memory. However, mov can only be used to load certain values[4]. In ARM assembly code, almost every instruction begins with a three letter code. This is called the mnemonic, and is supposed to hint at what the operation does. mov is short for move and ldr is short for load register. mov moves the second argument #1 into the first r1. In general, # must be used to denote numbers, but we have already seen a counterexample to this.
The second instruction is lsl or logical shift left. This means shift the binary representation for the first argument left by the second argument. In this case this will shift the binary representation of 110 (which is 12) left by 18 places (making it 10000000000000000002=26214410).
If you are unfamiliar with binary, expand the box below:
```
Just like hexadecimal binary is another way of writing numbers. In binary we only have 2 digits, 0 and 1. This is useful for computers because we can implement this in a circuit by saying that electricity flowing through the circuit means 1, and not means 0. This is how computers actually work and do maths. Despite only having 2 digits binary can still be used to represent every number, it just takes a lot longer.
![567 in decimal = 1000110111 in binary][7]
The image shows the binary representation of the number 56710 which is 10001101112. We use 2 to denote numbers written in binary.
One of the quirks of binary that we make heavy use of in assembly code is the ease by which numbers can be multiplied or divided by powers of 2 (e.g. 1,2,4,8,16). Normally multiplications and divisions are tricky operations, however these special cases are very easy, and so are very important.
![13*4 = 52, 1101*100=110100][8]
Shifting a binary number left by **n** places is the same as multiplying the number by 2 **n**. So, if we want to multiply by 4, we just shift the number left 2 places. If we want to multiply by 256 we could shift it left by 8 places. If we wanted to multiply by a number like 12, we could instead multiply it by 8, then separately by 4 and add the results (N × 12 = N × (8 + 4) = N × 8 + N × 4).
![53/16 = 3, 110100/10000=11][9]
Shifting a binary number right by **n** places is the same as dividing the number by 2 **n**. The remainder of the division is the bits that were lost when shifted right. Unfortunately dividing by a binary number that is not an exact power of 2 is very difficult, and will be covered in [Lesson 9: Screen04][10].
![Binary Terminology][11]
This diagram shows common terminology used with binary. A bit is a single binary digit. A nibble is 4 binary bits. A byte is 2 nibbles, or 8 bits. A half is half the size of a word, 2 bytes in this case. A word refers to the size of the registers on a processor, and so on the Raspberry Pi this is 4 bytes. The convention is to number the most significant bit of a word 31, and the least significant bit as 0. The top, or high bits refer to the most significant bits, and the low or bottom bits refer to the least significant. A kilobyte (KB) is 1000 bytes, a megabyte is 1000 KB. There is some confusion as to whether this should be 1000 or 1024 (a round number in binary). As such, the new international standard is that a KB is 1000 bytes, and a Kibibyte (KiB) is 1024 bytes. A Kb is 1000 bits, and a Kib is 1024 bits.
The Raspberry Pi is little endian by default, meaning that loading a byte from an address you just wrote a word to will load the lowest byte of the word.
```
Once again, I only know that we need this value from reading the manual[3]. The manual says that there is a set of 24 bytes in the GPIO controller, which determine the settings of the GPIO pin. The first 4 relate to the first 10 GPIO pins, the second 4 relate to the next 10 and so on. There are 54 GPIO pins, so we need 6 sets of 4 bytes, which is 24 bytes in total. Within each 4 byte section, every 3 bits relates to a particular GPIO pin. Since we want the 16th GPIO pin, we need the second set of 4 bytes because we're dealing with pins 10-19, and we need the 6th set of 3 bits, which is where the number 18 (6×3) comes from in the code above.
Finally the str 'store register' command stores the value in the first argument, r1 into the address computed from the expression afterwards. The expression can be a register, in this case r0, which we know to be the GPIO controller address, and another value to add to it, in this case #4. This means we add 4 to the GPIO controller address and write the value in r1 to that location. This happens to be the location of the second set of 4 bytes that I mentioned before, and so we send our first message to the GPIO controller, telling it to ready the 16th GPIO pin for output.
### 5 A Sign Of Life
Now that the LED is ready to turn on, we need to actually turn it on. This means sending a message to the GPIO controller to turn pin 16 off. Yes, turn it off. The chip manufacturers decided it made more sense[5] to have the LED turn on when the GPIO pin is off. Hardware engineers often seem to take these sorts of decisions, seemingly just to keep OS Developers on their toes. Consider yourself warned.
```
mov r1,#1
lsl r1,#16
str r1,[r0,#40]
```
Hopefully you should recognise all of the above commands, if not their values. The first puts a 1 into r1 as before. The second shifts the binary representation of this 1 left by 16 places. Since we want to turn pin 16 off, we need to have a 1 in the 16th bit of this next message (other values would work for other pins). Finally we write it out to the address which is 4010 added to the GPIO controller address, which happens to be the address to write to turn a pin off (28 would turn the pin on).
### 6 Happily Ever After
It might be tempting to finish now, but unfortunately the processor doesn't know we're done. In actuality, the processor never will stop. As long as it has power, it continues working. Thus, we need to give it a task to do forever more, or the Raspberry Pi will crash (not much of a problem in this example, the light is already on).
```
loop$:
b loop$
```
```
name: labels the next line name.
b label causes the next line to be executed to be label.
```
The first line here is not a command, but a label. It names the next line loop$. This means we can now refer to the line by name. This is called a label. Labels get discarded when the code is turned into binary, but they're useful for our benefit for referring to lines by name, not number (address). By convention we use a $ for labels which are only important to the code in this block of code, to let others know they're not important to the overall program. The b (branch) command causes the next line to be executed to be the one at the label specified, rather than the one after it. Therefore, the next line to be executed will be this b, which will cause it to be executed again, and so on forever. Thus the processor is stuck in a nice infinite loop until it is switched off safely.
The new line at the end of the block is intentional. The GNU toolchain expects all assembly code files to end in an empty line, so that it is sure you were really finished, and the file hasn't been cut off. If you don't put one, you get an annoying warning when the assembler runs.
### 7 Pi Time
So we've written the code, now to get it onto the pi. Open a terminal on your computer and change the current working directory to the parent directory of the source directory. Type make and then press enter. If any errors occur, please refer to the troubleshooting section. If not, you will have generated three files. kernel.img is the compiled image of your operating system. kernel.list is a listing of the assembly code you wrote, as it was actually generated. This is useful to check that things were generated correctly in future. The kernel.map file contains a map of where all the labels ended up, which can be useful for chasing around values.
To install your operating system, first of all get a Raspberry PI SD card which has an operating system installed already. If you browse the files in the SD card, you should see one called kernel.img. Rename this file to something else, such as kernel_linux.img. Then, copy the file kernel.img that make generated onto the SD Card. You've just replaced the existing operating system with your own. To switch back, simply delete your kernel.img file, and rename the other one back to kernel.img. I find it is always helpful to keep a backup of you original Raspberry Pi operating system, in case you need it again.
Put the SD card into a Raspberry Pi and turn it on. The OK LED should turn on. If not please see the troubleshooting page. If so, congratulations, you just wrote your first operating system. See [Lesson 2: OK02][12] for a guide to making the LED flash on and off.
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html
作者:[Robert Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: http://www.cl.cam.ac.uk/~rdm34
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/hexadecimal1.png
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/hexadecimal2.png
[4]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/hexadecimal3.png
[5]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/hexadecimal4.png
[6]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/gpioController.png
[7]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/binary1.png
[8]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/binary2.png
[9]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/binary3.png
[10]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen04.html
[11]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/binary4.png
[12]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok02.html

View File

@ -0,0 +1,68 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 2 OK02)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok02.html)
[#]: author: (Robert Mullins http://www.cl.cam.ac.uk/~rdm34)
Computer Laboratory Raspberry Pi: Lesson 2 OK02
======
The OK02 lesson builds on OK01, by causing the 'OK' or 'ACT' LED to turn on and off repeatedly. It is assumed you have the code for the [Lesson 1: OK01][1] operating system as a basis.
### 1 Waiting
Waiting is a surprisingly useful part of Operating System development. Often Operating Systems find themselves with nothing to do, and must delay. In this example, we wish to do so in order to allow the LED flashing off and on to be visible. If you just turned it off and on, it would not be visible, as the computer would be able to turn it off and on many thousands of times per second. In later lessons we will look at accurate waiting, but for now it is sufficient to simply waste time.
```
mov r2,#0x3F0000
wait1$:
sub r2,#1
cmp r2,#0
bne wait1$
```
```
sub reg,#val subtracts the number val from the value in reg.
cmp reg,#val compares the value in reg with the number val.
Suffix ne causes the command to be executed only if the last comparison determined that the numbers were not equal.
```
The code above is a generic piece of code that creates a delay, which thanks to every Raspberry Pi being basically the same, is roughly the same time. How it does this is using a mov command to put the value 3F000016 into r2, and then subtracting 1 from this value until it is 0. The new commands here are sub, cmp, and bne.
sub is the subtract command, and simply subtracts the second argument from the first.
cmp is a more interesting command. It compares the first argument with the second, and remembers the result of the comparison in a special register called the current processor status register. You don't really need to worry about this, suffice to say it remembers, among other things, which of the two numbers was bigger or smaller, or if they were equal.[1]
bne is actually just a branch command in disguise. In the ARM assembly language family, any instruction can be executed conditionally. This means that the instruction is only run if the last comparison had a certain result. We will use this extensively later for interesting tricks, but in this case we use the ne suffix on the b command to mean 'only branch if the last comparison's result was that the values were not equal'. The ne suffix can be used on any command, as can several other (16 in all) conditions such as eq for equal and lt for less than.
### 2 The All Together
I mentioned briefly last time that the status LED can be turned off again by writing to an offset of 28 from the GPIO controller instead of 40 (i.e. str r1,[r0,#28]). Thus, you need to modify the code from OK01 to turn the LED on, run the wait code, turn it off, run the wait code again, and then include a branch back to the beginning. Note, it is not necessary to re-enable the output to GPIO 16, we need only do that once. If you're being efficient, which I strongly encourage, you should be able to reuse the value of r1. As with all lessons, a full solution to this can be found on the [download page][2]. Be careful to make sure all of your labels are unique. When you write wait1$: you cannot label another line wait1$.
On my Raspberry Pi it flashes about twice a second. this could easily be altered by changing the value we set r2 to. However, unfortunately we can't precisely predict the speed this runs at. If you didn't manage to get this working see our trouble shooting page, otherwise, congratulations.
In this lesson we've learnt two more assembly commands, sub and cmp, as well as learning about conditional execution in ARM.
In the next lesson, [Lesson 3: OK03][3] we will evaluate how we're coding, and establish some standards so that we can reuse code, and if necessary, work with C or C++ code.
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok02.html
作者:[Robert Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: http://www.cl.cam.ac.uk/~rdm34
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/downloads.html
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok03.html

View File

@ -0,0 +1,383 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 3 OK03)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok03.html)
[#]: author: (Robert Mullins http://www.cl.cam.ac.uk/~rdm34)
Computer Laboratory Raspberry Pi: Lesson 3 OK03
======
The OK03 lesson builds on OK02 by teaching how to use functions in assembly to make more reusable and rereadable code. It is assumed you have the code for the [Lesson 2: OK02][1] operating system as a basis.
### 1 Reusable Code
So far we've made code for our operating system by typing the things we want to happen in order. This is fine for such tiny programs, but if we wrote the whole system like this, the code would be completely unreadable. Instead we use functions.
```
A function is a piece of code that can be reused to compute a certain kind of answer, or perform a certain action. You may also hear them called procedures, routines or subroutines. Although these are all different, people rarely use the correct term.
You should already be happy with the concept of a function from mathematics. For example the cosine function applied to a number gives another number between -1 and 1 which is the cosine of the angle. Notationally we write cos(x) to be the cosine function applied to the value x.
In code, functions can take multiple inputs (including none), give multiple outputs (including none), and may cause side effects. For example a function might create a file on the file system, named after the first input, with length based on the second.
```
![Function as black boxes][2]
```
Functions are said to be 'black boxes'. We put inputs in, and outputs come out, but we don't need to know how they work.
```
In higher level code such as C or C++, functions are part of the language itself. In assembly code, functions are just ideas we have.
Ideally we want to be able to set our registers to some input values, branch to an address, and expect that at some point the code will branch back to our code having set the registers to output values. This is what a function is in assembly code. The difficulty comes in what system we use for setting the registers. If we just used any system we felt like, each programmer may use a different system, and would find other programmers' work hard to understand. Further, compilers would not be able to work with assembly code as easily, as they would not know how to use the functions. To prevent confusion, a standard called the Application Binary Interface (ABI) was devised for each assembly language which is an agreement on how functions should be run. If everyone makes functions in the same way, then everyone will be able to use each others' functions. I will teach that standard here, and from now on I will code all of my functions to meet the standard.
The standard says that r0,r1,r2 and r3 will be used as inputs to a function in order. If a function needs no inputs, then it doesn't matter what value it takes. If it needs only one it always goes in r0, if it needs two, the first goes in r0, and the second goes on r1, and so on. The output will always be in r0. If a function has no output, it doesn't matter what value r0 takes.
Further, it also requires that after a function is run, r4 to r12 must have the same values as they had when the function started. This means that when you call a function, you can be sure the r4 to r12 will not change value, but you cannot be so sure about r0 to r3.
When a function completes it has to branch back to the code that started it. This means it must know the address of the code that started it. To facilitate this, there is a special register called lr (link register) which always holds the address of the instruction after the one that called this function.
Table 1.1 ARM ABI register usage
| Register | Brief | Preserved | Rules |
| -------- | ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| r0 | Argument and result | No | r0 and r1 are used for passing the first two arguments to functions, and returning the results of functions. If a function does not use them for a return value, they can take any value after a function. |
| r1 | Argument and result | No | |
| r2 | Argument | No | r2 and r3 are used for passing the second two arguments to functions. There values after a function is called can be anything. |
| r3 | Argument | No | |
| r4 | General purpose | Yes | r4 to r12 are used for working values, and their value after a function is called must be the same as before. |
| r5 | General purpose | Yes | |
| r6 | General purpose | Yes | |
| r7 | General purpose | Yes | |
| r8 | General purpose | Yes | |
| r9 | General purpose | Yes | |
| r10 | General purpose | Yes | |
| r11 | General purpose | Yes | |
| r12 | General purpose | Yes | |
| lr | Return address | No | lr is the address to branch back to when a function is finished, but this does have to contain the same address after the function has finished. |
| sp | Stack pointer | Yes | sp is the stack pointer, described below. Its value must be the same after the function has finished. |
Often functions need to use more registers than just r0 to r3. But, since r4 to r12 must stay the same after the method has run, they must be saved somewhere. We save them on something called the stack.
![Stack diagram][3]
```
A stack is a metaphor we use in computing for a method of storing values. Just like in a stack of plates, you can only remove items from the top of a stack, and only add items to the top of the stack.
The stack is a brilliant idea for storing registers on when functions are running. For example if I have a function which needs to use registers r4 and r5, it could place the current values of those registers on a stack. At the end of the method it could take them back off again. What is most clever is that if my function had to run another function in order to complete and that function needed to save some registers, it could put those on the top of the stack while it ran, and then take them off again at the end. That wouldn't affect the values of r4 and r5 that my method had to save, as they would be added to the top of the stack, and then taken off again.
The terminology we used to refer to the values put on the stack by a particular method is that methods 'stack frame'. Not every method needs a stack frame, some don't need to store values.
```
Because the stack is so useful, it has been implemented in the ARMv6 instruction set directly. A special register called sp (stack pointer) holds the address of the stack. When items are added to the stack, the sp register updates so that it always holds the address of the first item on the stack. push {r4,r5} would put the values in r4 and r5 onto the top of the stack and pop {r4,r5} would take them back off again (in the correct order).
### 2 Our First Function
Now that we have some idea about how functions work, let's try to make one. For a basic first example, we are going to make a function that takes no input, and gives an output of the GPIO address. In the last lesson, we just wrote in this value, but it would be better as a function, since it is something we might need to do often in a real operating system, and we might not always remember the address.
Copy the following code into a new file called 'gpio.s'. Just make the new file in the 'source' directory with 'main.s'. We're going to put all functions related to the GPIO controller in one file to make them easier to find.
```
.globl GetGpioAddress
GetGpioAddress:
ldr r0,=0x20200000
mov pc,lr
```
```
.globl lbl makes the label lbl accessible from other files.
mov reg1,reg2 copies the value in reg2 into reg1.
```
This is a very simple complete function. The .globl GetGpioAddress command is a message to the assembler to make the label GetGpioAddress accessible to all files. This means that in our main.s file we can branch to the label GetGpioAddress even though it is not defined in that file.
You should recognise the ldr r0,=0x20200000 command, which stores the GPIO controller address in r0. Since this is a function, we have to give the output in r0, so we are not as free to use any register as we once were.
mov pc,lr copies the value in lr to pc. As mentioned earlier lr always contains the address of the code that we have to go back to when a method finishes. pc is a special register which always contains the address of the next instruction to be run. A normal branch command just changes the value of this register. By copying the value in lr to pc we just change the next line to be run to be the one we were told to go back to.
A reasonable question would now be, how would we actually run this code? A special type of branch bl does what we need. It branches to a label like a normal branch, but before it does it updates lr to contain the address of the line after the branch. That means that when the function finishes, the line it will go back to will be the one after the bl command. This makes a function running just look like any other command, it simply runs, does whatever it needs to do, and then carries on to the next line. This is a really useful way of thinking about functions. We treat them as 'black boxes' in that when we use them, we don't need to think about how they work, we just need to know what inputs they need, and what outputs they give.
For now, don't worry about using the function, we will use it in the next section.
### 3 A Big Function
Now we're going to implement a bigger function. Our first job was to enable output on GPIO pin 16. It would be nice if this was a function. We could simply specify a pin and a function as the input, and the function would set the function of that pin to that value. That way, we could use the code to control any GPIO pin, not just the LED.
Copy the following commands below the GetGpioAddress function in gpio.s.
```
.globl SetGpioFunction
SetGpioFunction:
cmp r0,#53
cmpls r1,#7
movhi pc,lr
```
```
Suffix ls causes the command to be executed only if the last comparison determined that the first number was less than or the same as the second. Unsigned.
Suffix hi causes the command to be executed only if the last comparison determined that the first number was higher than the second. Unsigned.
```
One of the first things we should always think about when writing functions is our inputs. What do we do if they are wrong? In this function, we have one input which is a GPIO pin number, and so must be a number between 0 and 53, since there are 54 pins. Each pin has 8 functions, numbered 0 to 7 and so the function code must be too. We could just assume that the inputs will be correct, but this is very dangerous when working with hardware, as incorrect values could cause very bad side effects. Therefore, in this case, we wish to make sure the inputs are in the right ranges.
To do this we need to check that r0 <= 53 and r1 <= 7. First of all, we can use the comparison we've seen before to compare the value of r0 with 53. The next instruction, cmpls is a normal comparison instruction that will only be run if r0 was lower than or the same as 53. If that was the case, it compares r1 with 7, otherwise the result of the comparison is the same as before. Finally we go back to the code that ran the function if the result of the last comparison was that the register was higher than the number.
The effect of this is exactly what we want. If r0 was bigger than 53, then the cmpls command doesn't run, but the movhi does. If r0 is <= 53, then the cmpls command does run, and so r1 is compared with 7, and then if it is higher than 7, movhi is run, and the function ends, otherwise movhi does not run, and we know for sure that r0 <= 53 and r1 <= 7.
There is a subtle difference between the ls (lower or same) and le (less or equal) as well as between hi (higher) and gt (greater) suffixes, but I will cover this later.
Copy these commands below the above.
```
push {lr}
mov r2,r0
bl GetGpioAddress
```
```
push {reg1,reg2,...} copies the registers in the list reg1,reg2,... onto the top of the stack. Only general purpose registers and lr can be pushed.
bl lbl sets lr to the address of the next instruction and then branches to the label lbl.
```
These next three commands are focused on calling our first method. The push {lr} command copies the value in lr onto the top of the stack, so that we can retrieve it later. We must do this because when we call GetGpioAddress, we will need to use lr to store the address to come back to in our function.
If we did not know anything about the GetGpioAddress function, we would have to assume it changes r0,r1,r2 and r3, and would have to move our values to r4 and r5 to keep them the same after it finishes. Fortunately, we do know about GetGpioAddress, and we know it only changes r0 to the address, it doesn't affect r1,r2 or r3. Thus, we only have to move the GPIO pin number out of r0 so it doesn't get overwritten, but we know we can safely move it to r2, as GetGpioAddress doesn't change r2.
Finally we use the bl instruction to run GetGpioAddress. Normally we use the term 'call' for running a function, and I will from now. As discussed earlier bl calls a function by updating the lr to the next instruction's address, and then branching to the function.
When a function ends we say it has 'returned'. When the call to GetGpioAddress returns, we now know that r0 contains the GPIO address, r1 contains the function code and r2 contains the GPIO pin number. I mentioned earlier that the GPIO functions are stored in blocks of 10, so first we need to determine which block of ten our pin number is in. This sounds like a job we would use a division for, but divisions are very slow indeed, so it is better for such small numbers to do repeated subtraction.
Copy the following code below the above.
```
functionLoop$:
cmp r2,#9
subhi r2,#10
addhi r0,#4
bhi functionLoop$
```
```
add reg,#val adds the number val to the contents of the register reg.
```
This simple loop code compares the pin number to 9. If it is higher than 9, it subtracts 10 from the pin number, and adds 4 to the GPIO Controller address then runs the check again.
The effect of this is that r2 will now contain a number from 0 to 9 which represents the remainder of dividing the pin number by 10. r0 will now contain the address in the GPIO controller of this pin's function settings. This would be the same as GPIO Controller Address + 4 × (GPIO Pin Number ÷ 10).
Finally, copy the following code below the above.
```
add r2, r2,lsl #1
lsl r1,r2
str r1,[r0]
pop {pc}
```
```
Argument shift reg,lsl #val shifts the binary representation of the number in reg left by val before using it in the operation before.
lsl reg,amt shifts the binary representation of the number in reg left by the number in amt.
str reg,[dst] is the same as str reg,[dst,#0].
pop {reg1,reg2,...} copies the values from the top of the stack into the register list reg1,reg2,.... Only general purpose registers and pc can be popped.
```
This code finishes off the method. The first line is actually a multiplication by 3 in disguise. Multiplication is a big and slow instruction in assembly code, as the circuit can take a long time to come up with the answer. It is much faster sometimes to use some instructions which can get the answer quicker. In this case, I know that r2 × 3 is the same as r2 × 2 + r2. It is very easy to multiply a register by 2 as this is conveniently the same as shifting the binary representation of the number left by one place.
One of the very useful features of the ARMv6 assembly code language is the ability to shift an argument before using it. In this case, I add r2 to the result of shifting the binary representation of r2 to the left by one place. In assembly code, you often use tricks such as this to compute answers more easily, but if you're uncomfortable with this, you could also write something like mov r3,r2; add r2,r3; add r2,r3.
Now we shift the function value left by a number of places equal to r2. Most instructions such as add and sub have a variant which uses a register rather than a number for the amount. We perform this shift because we want to set the bits that correspond to our pin number, and there are three bits per pin.
We then store the the computed function value at the address in the GPIO controller. We already worked out the address in the loop, so we don't need to store it at an offset like we did in OK01 and OK02.
Finally, we can return from this method call. Since we pushed lr onto the stack, if we pop pc, it will copy the value that was in lr at the time we pushed it into pc. This would be the same as having used mov pc,lr and so the function call will return when this line is run.
The very keen may notice that this function doesn't actually work correctly. Although it sets the function of the GPIO pin to the requested value, it causes all the pins in the same block of 10's functions to go back to 0! This would likely be quite annoying in a system which made heavy use of the GPIO pins. I leave it as a challenge to the interested to fix this function so that it does not overwrite other pins values by ensuring that all bits other than the 3 that must be set remain the same. A solution to this can be found on the downloads page for this lesson. Functions that you may find useful are and which computes the Boolean and function of two registers, mvns which computes the Boolean not and orr which computes the Boolean or.
### 4 Another Function
So, we now have a function which takes care of the GPIO pin function setting. We now need to make a function to turn a GPIO pin on or off. Rather than having one function for off and one function for on, it would be handy to have a single function which does either.
We will make a function called SetGpio which takes a GPIO pin number as its first input in r0, and a value as its second in r1. If the value is 0 we will turn the pin off, and if it is not zero we will turn it on.
Copy and paste the following code at the end of 'gpio.s'.
```
.globl SetGpio
SetGpio:
pinNum .req r0
pinVal .req r1
```
```
alias .req reg sets alias to mean the register reg.
```
Once again we need the .globl command and the label to make the function accessible from other files. This time we're going to use register aliases. Register aliases allow us to use a name other than just r0 or r1 for registers. This may not be so important now, but it will prove invaluable when writing big methods later, and you should try to use aliases from now on. pinNum .req r0 means that pinNum now means r0 when used in instructions.
Copy and paste the following code after the above.
```
cmp pinNum,#53
movhi pc,lr
push {lr}
mov r2,pinNum
.unreq pinNum
pinNum .req r2
bl GetGpioAddress
gpioAddr .req r0
```
```
.unreq alias removes the alias alias.
```
Like in SetGpioFunction the first thing we must do is check that we were actually given a valid pin number. We do this in exactly the same way by comparing pinNum (r0) with 53, and returning immediately if it is higher. Once again we wish to call GetGpioAddress, so we have to preserve lr by pushing it onto the stack, and to move pinNum to r2. We then use the .unreq statement to remove our alias from r0. Since the pin number is now stored in r2 we want our alias to reflect this, so we remove the alias from r0 and remake it on r2. You should always .unreq every alias as soon as it is done with, so that you cannot make the mistake of using it further down the code when it no longer exists.
We then call GetGpioAddress, and we create an alias for r0 to reflect this.
Copy and paste the following code after the above.
```
pinBank .req r3
lsr pinBank,pinNum,#5
lsl pinBank,#2
add gpioAddr,pinBank
.unreq pinBank
```
```
lsr dst,src,#val shifts the binary representation of the number in src right by val, but stores the result in dst.
```
The GPIO controller has two sets of 4 bytes each for turning pins on and off. The first set in each case controls the first 32 pins, and the second set controls the remaining 22. In order to determine which set it is in, we need to divide the pin number by 32. Fortunately this is very easy, at is the same as shifting the binary representation of the pin number right by 5 places. Hence, in this case I've named r3 as pinBank and then computed pinNum ÷ 32. Since it is a set of 4 bytes, we then need to multiply the result of this by 4. This is the same as shifting the binary representation left by 2 places, which is the command that follows. You may wonder if we could just shift it right by 3 places, as we went right then left. This won't work however, as some of the answer may have been rounded away when we did ÷ 32 which may not be if we just ÷ 8.
The result of this is that gpioAddr now contains either 2020000016 if the pin number is 0-31, and 2020000416 if the pin number is 32-53. This means if we add 2810 we get the address for turning the pin on, and if we add 4010 we get the address for turning the pin off. Since we are done with pinBank, I use .unreq immediately afterwards.
Copy and paste the following code after the above.
```
and pinNum,#31
setBit .req r3
mov setBit,#1
lsl setBit,pinNum
.unreq pinNum
```
```
and reg,#val computes the Boolean and function of the number in reg with val.
```
This next part of the function is for generating a number with the correct bit set. For the GPIO controller to turn a pin on or off, we give it a number with a bit set in the place of the remainder of that pin's number divided by 32. For example, to set pin 16, we need a number with the 16th bit a 1. To set pin 45 we would need a number with the 13th bit 1 as 45 ÷ 32 = 1 remainder 13.
The and command computes the remainder we need. How it does this is that the result of an and operation is a number with 1s in all binary digits which had 1s in both of the inputs, and 0s elsewhere. This is a fundamental binary operation, and is very quick. We have given it inputs of pinNum and 3110 = 111112. This means that the answer can only have 1 bits in the last 5 places, and so is definitely between 0 and 31. Specifically it only has 1s where there were 1s in pinNum's last 5 places. This is the same as the remainder of a division by 32. It is no coincidence that 31 = 32 - 1.
![binary division example][4]
The rest of this code simply uses this value to shift the number 1 left. This has the effect of creating the binary number we need.
Copy and paste the following code after the above.
```
teq pinVal,#0
.unreq pinVal
streq setBit,[gpioAddr,#40]
strne setBit,[gpioAddr,#28]
.unreq setBit
.unreq gpioAddr
pop {pc}
```
```
teq reg,#val checks if the number in reg is equal to val.
```
This code ends the method. As stated before, we turn the pin off if pinVal is zero, and on otherwise. teq (test equal) is another comparison operation that can only be used to test for equality. It is similar to cmp but it does not work out which number is bigger. If all you wish to do is test if to numbers are the same, you can use teq.
If pinVal is zero, we store the setBit at 40 away from the GPIO address, which we already know turns the pin off. Otherwise we store it at 28, which turns the pin on. Finally, we return by popping the pc, which sets it to the value that we stored when we pushed the link register.
### 5 A New Beginning
Finally, after all that work we have our GPIO functions. We now need to alter 'main.s' to use them. Since 'main.s' is now getting a lot bigger and more complicated, it is better design to split it into two sections. The '.init' we've been using so far is best kept as small as possible. We can change the code to reflect this easily.
Insert the following just after _start: in main.s:
```
b main
.section .text
main:
mov sp,#0x8000
```
The key change we have made here is to introduce the .text section. I have designed the makefile and linker scripts such that code in the .text section (which is the default section) is placed after the .init section which is placed at address 800016. This is the default load address and gives us some space to store the stack. As the stack exists in memory, it has to have an address. The stack grows down memory, so that each new value is at a lower address, thus making the 'top' of the stack, the lowest address.
```
The 'ATAGs' section in the diagram is a place where information about the Raspberry Pi is stored such as how much memory it has, and what its default screen resolution is.
```
![Layout diagram of operating system][5]
Replace all the code that set the function of the GPIO pin with the following:
```
pinNum .req r0
pinFunc .req r1
mov pinNum,#16
mov pinFunc,#1
bl SetGpioFunction
.unreq pinNum
.unreq pinFunc
```
This code calls SetGpioFunction with the pin number 16 and the pin function code 1. This has the effect of enabling output to the OK LED.
Replace any code which turns the OK LED on with the following:
```
pinNum .req r0
pinVal .req r1
mov pinNum,#16
mov pinVal,#0
bl SetGpio
.unreq pinNum
.unreq pinVal
```
This code uses SetGpio to turn off GPIO pin 16, thus turning on the OK LED. If we instead used mov pinVal,#1, it would turn the LED off. Replace your old code to turn the LED off with that.
### 6 Onwards
Hopefully now, you should be able to test what you have made on the Raspberry Pi. We've done a large amount of code this time, so there is a lot that can go wrong. If it does, head to the troubleshooting page.
When you get it working, congratulations. Although our operating system does nothing more than it did in [Lesson 2: OK02][1], we've learned a lot about functions and formatting, and we can now code new features much more quickly. It would be very simple now to make an Operating System that alters any GPIO register, which could be used to control hardware!
In [Lesson 4: OK04][6], we will address our wait function, which is currently imprecise, so that we can gain better control over our LED, and ultimately over all of the GPIO pins.
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok03.html
作者:[Robert Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: http://www.cl.cam.ac.uk/~rdm34
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok02.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/functions.png
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/stack.png
[4]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/binary3.png
[5]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/osLayout.png
[6]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok04.html

View File

@ -0,0 +1,161 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 4 OK04)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok04.html)
[#]: author: (Robert Mullins http://www.cl.cam.ac.uk/~rdm34)
Computer Laboratory Raspberry Pi: Lesson 4 OK04
======
The OK04 lesson builds on OK03 by teaching how to use the timer to flash the 'OK' or 'ACT' LED at precise intervals. It is assumed you have the code for the [Lesson 3: OK03][1] operating system as a basis.
### 1 A New Device
The timer is the only way the Pi can keep time. Most computers have a battery powered clock to keep time when off.
So far, we've only looked at one piece of hardware on the Raspberry Pi, namely the GPIO Controller. I've simply told you what to do, and it happened. Now we're going to look at the timer, and I'm going to lead you through understanding how it works.
Just like the GPIO Controller, the timer has an address. In this case, the timer is based at 2000300016. Reading the manual, we find the following table:
Table 1.1 GPIO Controller Registers
| Address | Size / Bytes | Name | Description | Read or Write |
| -------- | ------------ | ---------------- | ---------------------------------------------------------- | ---------------- |
| 20003000 | 4 | Control / Status | Register used to control and clear timer channel comparator matches. | RW |
| 20003004 | 8 | Counter | A counter that increments at 1MHz. | R |
| 2000300C | 4 | Compare 0 | 0th Comparison register. | RW |
| 20003010 | 4 | Compare 1 | 1st Comparison register. | RW |
| 20003014 | 4 | Compare 2 | 2nd Comparison register. | RW |
| 20003018 | 4 | Compare 3 | 3rd Comparison register. | RW |
![Flowchart of the system timer's operation][2]
This table tells us a lot, but the descriptions in the manual of the various fields tell us the most. The manual explains that the timer fundamentally just increments the value in Counter by 1 every 1 micro second. Each time it does so, it compares the lowest 32 bits (4 bytes) of the counter's value with the 4 comparison registers, and if it matches any of them, it updates Control / Status to reflect which ones matched.
For more information about bits, bytes, bit fields, and data sizes expand the box below.
```
A bit is a name for a single binary digit. As you may recall, a single binary digit is either a 1 or a 0.
A byte is the name we give for a collection of 8 bits. Since each bit can be one of two values, there are 28 = 256 different possible values for a byte. We normally interpret a byte as a binary number between 0 and 255 inclusive.
![Diagram of GPIO function select controller register 0.][3]
A bit field is another way of interpreting binary. Rather than interpreting it as a number, binary can be interpreted as many different things. A bit field treats binary as a series of switches which are either on (1) or off (0). If we have a meaning for each of these little switches, we can use them to control things. We have actually already met bitfields with the GPIO controller, with the setting a pin on or off. The bit that was a 1 was the GPIO pin to actually turn on or off. Sometimes we need more options than just on or off, so we group several of the switches together, such as with the GPIO controller function settings (pictured), in which every group of 3 bits controls one GPIO pin function.
```
Our goal is to implement a function that we can call with an amount of time as an input that will wait for that amount of time and then return. Think for a moment about how we could do this, given what we have.
I see there being two options:
1. Read a value from the counter, and then keep branching back into the same code until the counter is the amount of time to wait more than it was.
2. Read a value from the counter, add the amount of time to wait, store this in one of the comparison registers and then keep branching back into the same code until the Control / Status register updates.
```
Issues like these are called concurrency problems, and can be almost impossible to fix.
```
Both of these strategies would work fine, but in this tutorial we will only implement the first. The reason is because the comparison registers are more likely to go wrong, as during the time it takes to add the wait time and store it in the comparison register, the counter may have increased, and so it would not match. This could lead to very long unintentional delays if a 1 micro second wait is requested (or worse, a 0 microsecond wait).
### 2 Implementation
```
Large Operating Systems normally use the Wait function as an opportunity to perform background tasks.
```
I will largely leave the challenge of creating the ideal wait method to you. I suggest you put all code related to the timer in a file called 'systemTimer.s' (for hopefully obvious reasons). The complicated part about this method, is that the counter is an 8 byte value, but each register only holds 4 bytes. Thus, the counter value will span two registers.
The following code blocks are examples.
```
ldrd r0,r1,[r2,#4]
```
```
ldrd regLow,regHigh,[src,#val] loads 8 bytes from the address given by the number in src plus val into regLow and regHigh .
```
An instruction you may find useful is the ldrd instruction above. It loads 8 bytes of memory across 2 registers. In this case, the 8 bytes of memory starting at the address in r2 would be copied into r0 and r1. What is slightly complicated about this arrangement is that r1 actually holds the highest 4 bytes. In other words, if the counter had a value of 999,999,999,99910 = 11101000110101001010010100001111111111112, r1 would contain 111010002 and r0 would contain 110101001010010100001111111111112.
The most sensible way to implement this would be to compute the difference between the current counter value and the one from when the method started, and then to compare this with the requested amount of time to wait. Conveniently, unless you wish to support wait times that were 8 bytes, the value in r1 in the example above could be discarded, and only the low 4 bytes of the counter need be used.
When waiting you should always be sure to use higher comparisons not equality comparisons, as if you try to wait for the gap between the time the method started and the time it ends to be exactly the amount requested, you could miss the value, and wait forever.
If you cannot figure out how to code the wait function, expand the box below for a guide.
```
Borrowing the idea from the GPIO controller, the first function we should write should be to get the address of this system timer. An example of this is shown below:
.globl GetSystemTimerBase
GetSystemTimerBase:
ldr r0,=0x20003000
mov pc,lr
Another function that will prove useful would be one that returns the current counter value in registers r0 and r1:
.globl GetTimeStamp
GetTimeStamp:
push {lr}
bl GetSystemTimerBase
ldrd r0,r1,[r0,#4]
pop {pc}
This function simply uses the GetSystemTimerBase function and loads in the counter value using ldrd like we have just learned.
Now we actually want to code our wait method. First of all, we need to know the counter value when the method started, which we can now get using GetTimeStamp.
delay .req r2
mov delay,r0
push {lr}
bl GetTimeStamp
start .req r3
mov start,r0
This code copies our method's input, the amount of time to delay, into r2, and then calls GetTimeStamp, which we know will return the current counter value in r0 and r1. It then copies the lower 4 bytes of the counter's value to r3.
Next we need to compute the difference between the current counter value and the reading we just took, and then keep doing so until the gap between them is at least the size of delay.
loop$:
bl GetTimeStamp
elapsed .req r1
sub elapsed,r0,start
cmp elapsed,delay
.unreq elapsed
bls loop$
This code will wait until the requested amount of time has passed. It takes a reading from the counter, subtracts the initial value from this reading and then compares that to the requested delay. If the amount of time that has elapsed is less than the requested delay, it branches back to loop$.
.unreq delay
.unreq start
pop {pc}
This code finishes off the function by returning.
```
### 3 Another Blinking Light
Once you have what you believe to be a working wait function, change 'main.s' to use it. Alter everywhere you wait to set the value of r0 to some big number (remember it is in microseconds) and then test it on the Raspberry Pi. If it does not function correctly please see our troubleshooting page.
Once it is working, congratulations you have now mastered another device, and with it, time itself. In the next and final lesson in the OK series, [Lesson 5: OK05][4] we shall use all we have learned to flash out a pattern on the LED.
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok04.html
作者:[Robert Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: http://www.cl.cam.ac.uk/~rdm34
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok03.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/systemTimer.png
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/gpioControllerFunctionSelect.png
[4]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok05.html

View File

@ -0,0 +1,108 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 5 OK05)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok05.html)
[#]: author: (Robert Mullins http://www.cl.cam.ac.uk/~rdm34)
Computer Laboratory Raspberry Pi: Lesson 5 OK05
======
The OK05 lesson builds on OK04 using it to flash the SOS Morse Code pattern (...---...). It is assumed you have the code for the [Lesson 4: OK04][1] operating system as a basis.
### 1 Data
So far, all we've had to do with our operating system is provide instructions to be followed. Sometimes however, instructions are only half the story. Our operating systems may need data.
```
Some early Operating Systems did only allow certain types of data in certain files, but this was generally found to be too restrictive. The modern way does make programs a lot more complicated however.
```
In general data is just values that are important. You are probably trained to think of data as being of a specific type, e.g. a text file contains text, an image file contains an image, etc. This is, in truth, just an idea. All data on a computer is just binary numbers, how we choose to interpret them is what counts. In this example we're going to store a light flashing sequence as data.
At the end of 'main.s' copy the following code:
```
.section .data
.align 2
pattern:
.int 0b11111111101010100010001000101010
```
```
.align num ensures the address of the next line is a multiple of 2num .
.int val outputs the number val .
```
To differentiate between data and code, we put all the data in the .data. I've included this on the operating system memory layout diagram here. I've just chosen to put the data after the end of the code. The reason for keeping our data and instructions separate is so that if we eventually implement some security on our operating system, we need to know what parts of the code can be executed, and what parts can't.
I've used two new commands here. .align and .int. .align ensures alignment of the following data to a specified power of 2. In this case I've used .align 2 which means that this data will definitely be placed at an address which is a multiple of 22 = 4. It is really important to do this, because the ldr instruction we used to read memory only works at addresses that are multiples of 4.
The .int command copies the constant after it into the output directly. That means that 111111111010101000100010001010102 will be placed into the output, and so the label pattern actually labels this piece of data as pattern.
```
One challenge with data is finding an efficient and useful representation. This method of storing the sequence as on and off units of time is easy to run, but would be difficult to edit, as the concept of a Morse - or . is lost.
```
As I mentioned, data can mean whatever you want. In this case I've encoded the Morse Code SOS sequence, which is ...---... for those unfamiliar. I've used a 0 to represent a unit of time with the LED off, and a 1 to represent a unit of time with the LED on. That way, we can write some code which just displays a sequence in data like this one, and then all we have to do to make it display a different sequence is change the data. This is a very simple example of what operating systems must do all the time; interpret and display data.
Copy the following lines before the loop$ label in 'main.s'.
```
ptrn .req r4
ldr ptrn,=pattern
ldr ptrn,[ptrn]
seq .req r5
mov seq,#0
```
This code loads the pattern into r4, and loads 0 into r5. r5 will be our sequence position, so we can keep track of how much of the pattern we have displayed.
The following code puts a non-zero into r1 if and only if there is a 1 in the current part of the pattern.
```
mov r1,#1
lsl r1,seq
and r1,ptrn
```
This code is useful for your calls to SetGpio, which must have a non-zero value to turn the LED off, and a value of zero to turn the LED on.
Now modify all of your code in 'main.s' so that each loop the code sets the LED based on the current sequence number, waits for 250000 micro seconds (or any other appropriate delay), and then increments the sequence number. When the sequence number reaches 32, it needs to go back to 0. See if you can implement this, and for an extra challenge, try to do it using only 1 instruction (solution in the download).
### 2 Time Flies When You're Having Fun...
You're now ready to test this on the Raspberry Pi. It should flash out a sequence of 3 short pulses, 3 long pulses and then 3 more short pulses. After a delay, the pattern should repeat. If it doesn't work please see our troubleshooting page.
Once it works, congratulations you have reached the end of the OK series of tutorials.
In this series we've learnt about assembly code, the GPIO controller and the System Timer. We've learnt about functions and the ABI, as well as several basic Operating System concepts, and also about data.
You're now ready to move onto one of the more advanced series.
* The [Screen][2] series is next and teaches you how to use the screen with assembly code.
* The [Input][3] series teaches you how to use the keyboard and mouse.
By now you already have enough information to make Operating Systems that interact with the GPIO in other ways. If you have any robot kits, you may want to try writing a robot operating system controlled with the GPIO pins!
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok05.html
作者:[Robert Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: http://www.cl.cam.ac.uk/~rdm34
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok04.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html
[3]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: translator: ( dianbanjiu )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )

View File

@ -1,63 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (The Linux command line can fetch fun from afar)
[#]: via: (https://opensource.com/article/18/12/linux-toy-remote)
[#]: author: (Jason Baker https://opensource.com/users/jason-baker)
The Linux command line can fetch fun from afar
======
Use these tools to access weather, reading material, and more from remote locations.
![](https://opensource.com/sites/default/files/styles/image-full-size/public/uploads/linux-toy-remote.png?itok=mHm9POPi)
We're almost to the end of our 24-day-long Linux command-line toys advent calendar. Hopefully, you've been following along, but if not, start back at [the beginning][1] and work your way through. You'll find plenty of games, diversions, and oddities for your Linux terminal.
And while you may have seen some toys from our calendar before, we hope theres at least one new thing for everyone.
Today's toy (or actually, collection of toys) is a little different. So far I've mostly tried to focus on toys that are self-contained, and completely usable under an open source license. But I've gotten some great suggestions from readers which utilize an open source tool to access something remotely that may or may not be open source. Today, I'll round up a few of those.
The first one is a total classic: use Telnet to watch an ASCII-rendition of Star Wars. Chances are that Telnet is already installed on your system, so all you'll need to do is run:
```
$ telnet towel.blinkenlights.nl
```
I feel like I first saw this one over a decade ago, so it's a bit amazing to me that it's still alive and online. If you've never watched it, set aside some time and go check it out. You won't regret it.
![](https://opensource.com/sites/default/files/uploads/linux-toy-star-wars.png)
Next, Opensource.com contributor [Manuel Dewald][2] suggested a way to fetch your local weather from the terminal. It's easy, and only requires that you have **curl** (or, well, **** **wget** ) installed.
```
$ curl wttr.in
```
![](https://opensource.com/sites/default/files/uploads/linux-toy-weather.png)
Finally, while you can spend the holidays reading your favorite sites (including Opensource.com) from your favorite [command-line web browser][3], there are a few of my favorite sites that are more easily browsed with a dedicated client. Two of these include Reddit and Hacker News, for which there are clients that have been recommended to me that you may wish to try, mostly available under open source licenses. I've poked around with [haxor-news][4] (Hacker News) and [rtv][5] (Reddit), and both seem pretty cool.
Do you have a favorite command-line toy that we should have included? It's a little late to submit a suggestion for this year, but we'd still love to feature some cool command-line toys in the new year. Let me know in the comments below, and I'll check it out. And let me know what you thought of today's amusement.
Be sure to check out yesterday's toy, [Watch YouTube videos at the Linux terminal][6], and come back tomorrow for another!
--------------------------------------------------------------------------------
via: https://opensource.com/article/18/12/linux-toy-remote
作者:[Jason Baker][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/jason-baker
[b]: https://github.com/lujun9972
[1]: https://opensource.com/article/18/12/linux-toy-boxes
[2]: https://opensource.com/users/ntlx
[3]: https://opensource.com/article/16/12/web-browsers-linux-command-line
[4]: https://github.com/donnemartin/haxor-news
[5]: https://github.com/michael-lazar/rtv
[6]: https://opensource.com/article/18/12/linux-toy-youtube-dl

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: translator: ( WangYueScream)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )

View File

@ -0,0 +1,63 @@
[#]: collector: (lujun9972)
[#]: translator: (geekpi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (The Linux command line can fetch fun from afar)
[#]: via: (https://opensource.com/article/18/12/linux-toy-remote)
[#]: author: (Jason Baker https://opensource.com/users/jason-baker)
能从远程获得乐趣的 Linux 命令
======
使用这些工具从远程了解天气、阅读资料等。
![](https://opensource.com/sites/default/files/styles/image-full-size/public/uploads/linux-toy-remote.png?itok=mHm9POPi)
我们即将结束为期 24 天的 Linux 命令行玩具日历。希望你有一直在看,如果没有,请回到[开始][1],从头看过来。你会发现 Linux 终端有很多游戏、消遣和奇怪之处。
虽然你之前可能已经看过我们日历中的一些玩具,但我们希望每个人都遇见一个新事物。
今天的玩具(实际是玩具集合)有点不同。到目前为止,我主要是想把重点放在那些独立的玩具上,并且完全可在开源许可下使用。但是我从读者那里得到了一些很好的建议,利用开源工具远程访问一些开源或者不开源的东西。今天,我将介绍其中的一些。
第一个是经典之作:使用 Telnet 观看星球大战的 ASCII 演绎版本。你的系统可能已经安装了 Telnet因此你只需运行
```
$ telnet towel.blinkenlights.nl
```
我第一次看到它是十年之前,因此我对于它还存在优点惊奇。如果你还没看过,请留出一点时间看一下。你不会后悔的。
![](https://opensource.com/sites/default/files/uploads/linux-toy-star-wars.png)
接下来Opensource.com 的撰稿人 [Manuel Dewald][2] 提出了一种从终端获取当地天气的方法。它很简单,你只需安装 **curl**(或者,**wget**)。
```
$ curl wttr.in
```
![](https://opensource.com/sites/default/files/uploads/linux-toy-weather.png)
最后,在假期中虽然你可以从[命令行 Web 浏览器][3]浏览你喜欢的网站(包括 Opensource.com但有一些我最喜欢的网站可以通过专用客户端更轻松地浏览。其中两个是 Reddit 和 Hacker News我被推荐过一些它们的客户端你可能也想尝试它们都使用开源许可。我尝试过 [haxor-news][4] Hacker News 和 [rtv][5] Reddit它们都还不错。
你有特别喜欢的命令行小玩具需要我介绍的吗?提交今年的建议有点晚了,但我们仍然希望在新的一年里有一些很酷的命令行玩具。请在下面的评论中告诉我,我会查看的。让我知道你对今天的玩具有何看法。
一定要看看昨天的玩具,[在 Linux 终端收看 Youtube 视频][2],明天还要再来!
--------------------------------------------------------------------------------
via: https://opensource.com/article/18/12/linux-toy-remote
作者:[Jason Baker][a]
选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/jason-baker
[b]: https://github.com/lujun9972
[1]: https://opensource.com/article/18/12/linux-toy-boxes
[2]: https://opensource.com/users/ntlx
[3]: https://opensource.com/article/16/12/web-browsers-linux-command-line
[4]: https://github.com/donnemartin/haxor-news
[5]: https://github.com/michael-lazar/rtv
[6]: https://opensource.com/article/18/12/linux-toy-youtube-dl