Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu.Wang 2019-02-13 18:01:35 +08:00
commit 8e308ed561
4 changed files with 1047 additions and 1053 deletions

View File

@ -0,0 +1,136 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-10533-1.html)
[#]: subject: (DNS and Root Certificates)
[#]: via: (https://lushka.al/dns-and-certificates/)
[#]: author: (Anxhelo Lushka https://lushka.al/)
DNS 和根证书
======
> 关于 DNS 和根证书你需要了解的内容。
由于最近发生的一些事件我们Privacy Today 组织)感到有必要写一篇关于此事的短文。它适用于所有读者,因此它将保持简单 —— 技术细节可能会在稍后的文章发布。
### 什么是 DNS为什么它与你有关
DNS 的意思是<ruby>域名系统<rt>Domain Name System</rt></ruby>,你每天都会接触到它。每当你的 Web 浏览器或任何其他应用程序连接到互联网时,它就很可能会使用域名。简单来说,域名就是你键入的地址:例如 [duckduckgo.com][1]。你的计算机需要知道它所导向的地方,会向 DNS 解析器寻求帮助。而它将返回类似 [176.34.155.23][2] 这样的 IP —— 这就是连接时所需要知道的公开网络地址。 此过程称为 DNS 查找。
这对你的隐私、安全以及你的自由都有一定的影响:
#### 隐私
由于你要求解析器获取域名的 IP因此它会确切地知道你正在访问哪些站点并且由于“物联网”通常缩写为 IoT甚至它还知道你在家中使用的是哪个设备。
#### 安全
你可以相信解析器返回的 IP 是正确的。有一些检查措施可以确保如此,在正常情况下这一般不是问题。但这些可能措施会被破坏,这就是写作本文的原因。如果返回的 IP 不正确,你可能会被欺骗引向了恶意的第三方 —— 甚至你都不会注意到任何差异。在这种情况下,你的隐私会受到更大的危害,因为不仅会被跟踪你访问了什么网站,甚至你访问的内容也会被跟踪。第三方可以准确地看到你正在查看的内容,收集你输入的个人信息(例如密码)等等。你的整个身份可以轻松接管。
#### 自由
审查通常是通过 DNS 实施的。这不是最有效的方法,但它非常普遍。即使在西方国家,它也经常被公司和政府使用。他们使用与潜在攻击者相同的方法;当你查询 IP 地址时,他们不会返回正确的 IP。他们可以表现得就好像某个域名不存在或完全将访问指向别处。
### DNS 查询的方式
#### 由你的 ISP 提供的第三方 DNS 解析器
大多数人都在使用由其互联网接入提供商ISP提供的第三方解析器。当你连接调制解调器时LCTT 译注:或宽带路由器),这些 DNS 解析器就会被自动取出,而你可能从来没注意过它。
#### 你自己选择的第三方 DNS 解析器
如果你已经知道 DNS 意味着什么,那么你可能会决定使用你选择的另一个 DNS 解析器。这可能会改善这种情况,因为它使你的 ISP 更难以跟踪你,并且你可以避免某些形式的审查。尽管追踪和审查仍然是可能的,但这种方法并没有被广泛使用。
#### 你自己(本地)的 DNS 解析器
你可以自己动手,避免使用别人的 DNS 解析器的一些危险。如果你对此感兴趣,请告诉我们。
### 根证书
#### 什么是根证书?
每当你访问以 https 开头的网站时,你都会使用它发送的证书与之通信。它使你的浏览器能够加密通信并确保没有人可以窥探。这就是为什么每个人都被告知在登录网站时要注意 https而不是 http。证书本身仅用于验证是否为某个域所生成。以及
这就是根证书的来源。可以其视为一个更高的级别,用来确保其下的级别是正确的。它验证发送给你的证书是否已由证书颁发机构授权。此权限确保创建证书的人实际上是真正的运营者。
这也被称为信任链。默认情况下,你的操作系统包含一组这些根证书,以确保该信任链的存在。
#### 滥用
我们现在知道:
* DNS 解析器在你发送域名时向你发送 IP 地址
* 证书允许加密你的通信,并验证它们是否为你访问的域生成
* 根证书验证该证书是否合法,并且是由真实站点运营者创建的
**怎么会被滥用呢?**
* 如前所述,恶意 DNS 解析器可能会向你发送错误的 IP 以进行审查。它们还可以将你导向完全不同的网站。
* 这个网站可以向你发送假的证书。
* 恶意的根证书可以“验证”此假证书。
对你来说,这个网站看起来绝对没问题;它在网址中有 https如果你点击它它会说已经通过验证。就像你了解到的一样对吗**不对!**
它现在可以接收你要发送给原站点的所有通信。这会绕过想要避免被滥用而创建的检查。你不会收到错误消息,你的浏览器也不会发觉。
**而你所有的数据都会受到损害!**
### 结论
#### 风险
* 使用恶意 DNS 解析器总是会损害你的隐私,但只要你注意 https你的安全性就不会受到损害。
* 使用恶意 DNS 解析程序和恶意根证书,你的隐私和安全性将完全受到损害。
#### 可以采取的动作
**不要安装第三方根证书!**只有非常少的例外情况才需要这样做,并且它们都不适用于一般最终用户。
**不要被那些“广告拦截”、“军事级安全”或类似的东西营销噱头所吸引**。有一些方法可以自行使用 DNS 解析器来增强你的隐私,但安装第三方根证书永远不会有意义。你正在将自己置身于陷阱之中。
### 实际看看
**警告**
有位友好的系统管理员提供了一个现场演示,你可以实时看到自己。这是真事。
**千万不要输入私人数据!之后务必删除证书和该 DNS**
如果你不知道如何操作,那就不要安装它。虽然我们相信我们的朋友,但你不要随便安装随机和未知的第三方根证书。
#### 实际演示
链接在这里:<http://https-interception.info.tm/>
* 设置所提供的 DNS 解析器
* 安装所提供的根证书
* 访问 <https://paypal.com> 并输入随机登录数据
* 你的数据将显示在该网站上
### 延伸信息
如果你对更多技术细节感兴趣,请告诉我们。如果有足够多感兴趣的人,我们可能会写一篇文章,但是目前最重要的部分是分享基础知识,这样你就可以做出明智的决定,而不会因为营销和欺诈而陷入陷阱。请随时提出对你很关注的其他主题。
这篇文章来自 [Privacy Today 频道][3]。[Privacy Today][4] 是一个关于隐私、开源、自由哲学等所有事物的组织!
所有内容均根据 CC BY-NC-SA 4.0 获得许可。([署名 - 非商业性使用 - 共享 4.0 国际][5])。
--------------------------------------------------------------------------------
via: https://lushka.al/dns-and-certificates/
作者:[Anxhelo Lushka][a]
选题:[lujun9972][b]
译者:[wxy](https://github.com/wxy)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://lushka.al/
[b]: https://github.com/lujun9972
[1]: https://duckduckgo.com
[2]: http://176.34.155.23
[3]: https://t.me/privacytoday
[4]: https://t.me/joinchat/Awg5A0UW-tzOLX7zMoTDog
[5]: https://creativecommons.org/licenses/by-nc-sa/4.0/

View File

@ -1,911 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (Guevaraya)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 11 Input02)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html)
[#]: author: (Alex Chadwick https://www.cl.cam.ac.uk)
Computer Laboratory Raspberry Pi: Lesson 11 Input02
======
The Input02 lesson builds on Input01, by building a simple command line interface where the user can type commands and the computer interprets and displays them. It is assumed you have the code for the [Lesson 11: Input01][1] operating system as a basis.
### 1 Terminal 1
```
In the early days of computing, there would usually be one large computer in a building, and many 'terminals' which sent commands to it. The computer would take it in turns to execute different incoming commands.
```
Almost every operating system starts life out as a text terminal. This is typically a black screen with white writing, where you type commands for the computer to execute on the keyboard, and it explains how you've mistyped them, or very occasionally, does what you want. This approach has two main advantages: it provides a simple, robust control mechanism for the computer using only a keyboard and monitor, and it is done by almost every operating system, so is widely understood by system administrators.
Let's analyse what we want to do precisely:
1. Computer turns on, displays some sort of welcome message
2. Computer indicates its ready for input
3. User types a command, with parameters, on the keyboard
4. User presses return or enter to commit the command
5. Computer interprets command and performs actions if command is acceptable
6. Computer displays messages to indicate if command was successful, and also what happened
7. Loop back to 2
One defining feature of such terminals is that they are unified for both input and output. The same screen is used to enter inputs as is used to print outputs. This means it is useful to build an abstraction of a character based display. In a character based display, the smallest unit is a character, not a pixel. The screen is divided into a fixed number of characters which have varying colours. We can build this on top of our existing screen code, by storing the characters and their colours, and then using the DrawCharacter method to push them to the screen. Once we have a character based display, drawing text becomes a matter of drawing a line of characters.
In a new file called terminal.s copy the following code:
```
.section .data
.align 4
terminalStart:
.int terminalBuffer
terminalStop:
.int terminalBuffer
terminalView:
.int terminalBuffer
terminalColour:
.byte 0xf
.align 8
terminalBuffer:
.rept 128*128
.byte 0x7f
.byte 0x0
.endr
terminalScreen:
.rept 1024/8 core.md Dict.md lctt2014.md lctt2016.md lctt2018.md LICENSE published README.md scripts sources translated 768/16
.byte 0x7f
.byte 0x0
.endr
```
This sets up the data we need for the text terminal. We have two main storages: terminalBuffer and terminalScreen. terminalBuffer is storage for all of the text we have displayed. It stores up to 128 lines of text (each containing 128 characters). Each character consists of an ASCII character code and a colour, all of which are initially set to 0x7f (ASCII delete) and 0 (black on a black background). terminalScreen stores the characters that are currently displayed on the screen. It is 128 by 48 characters, similarly initialised. You may think that we only need this terminalScreen, not the terminalBuffer, but storing the buffer has 2 main advantages:
1. We can easily see which characters are different, so we only have to draw those.
2. We can 'scroll' back through the terminal's history because it is stored (to a limit).
You should always try to design systems that do the minimum amount of work, as they run much faster for things which don't often change.
The differing trick is really common on low power Operating Systems. Drawing the screen is a slow operation, and so we only want to draw thing that we absolutely have to. In this system, we can freely alter the terminalBuffer, and then call a method which copies the bits that change to the screen. This means we don't have to draw each character as we go along, which may save time in the long run on very long sections of text that span many lines.
The other values in the .data section are as follows:
* terminalStart
The first character which has been written in terminalBuffer.
* terminalStop
The last character which has been written in terminalBuffer.
* terminalView
The first character on the screen at present. We can use this to scroll the screen.
* temrinalColour
The colour to draw new characters with.
```
Circular buffers are an example of an **data structure**. These are just ideas we have for organising data, that we sometimes implement in software.
```
![Diagram showing hellow world being inserted into a circular buffer of size 5.][2]
The reason why terminalStart needs to be stored is because termainlBuffer should be a circular buffer. This means that when the buffer is completely full, the end 'wraps' round to the start, and so the character after the very last one is the first one. Thus, we need to advance terminalStart so we know that we've done this. When wokring with the buffer this can easily be implemented by checking if the index goes beyond the end of the buffer, and setting it back to the beginning if it does. Circular buffers are a common and clever way of storing a lot of data, where only the most recent data is important. It allows us to keep writing indefinitely, while always being sure there is a certain amount of recent data available. They're often used in signal processing or compression algorithms. In this case, it allows us to store a 128 line history of the terminal, without any penalties for writing over 128 lines. If we didn't have this, we would have to copy 127 lines back a line very time we went beyond the 128th line, wasting valuable time.
I've mentioned the terminalColour here a few times. You can implement this however you, wish, however there is something of a standard on text terminals to have only 16 colours for foreground, and 16 colours for background (meaning there are 162 = 256 combinations). The colours on a CGA terminal are defined as follows:
Table 1.1 - CGA Colour Codes
| Number | Colour (R, G, B) |
| ------ | ------------------------|
| 0 | Black (0, 0, 0) |
| 1 | Blue (0, 0, ⅔) |
| 2 | Green (0, ⅔, 0) |
| 3 | Cyan (0, ⅔, ⅔) |
| 4 | Red (⅔, 0, 0) |
| 5 | Magenta (⅔, 0, ⅔) |
| 6 | Brown (⅔, ⅓, 0) |
| 7 | Light Grey (⅔, ⅔, ⅔) |
| 8 | Grey (⅓, ⅓, ⅓) |
| 9 | Light Blue (⅓, ⅓, 1) |
| 10 | Light Green (⅓, 1, ⅓) |
| 11 | Light Cyan (⅓, 1, 1) |
| 12 | Light Red (1, ⅓, ⅓) |
| 13 | Light Magenta (1, ⅓, 1) |
| 14 | Yellow (1, 1, ⅓) |
| 15 | White (1, 1, 1) |
```
Brown was used as the alternative (dark yellow) was unappealing and not useful.
```
We store the colour of each character by storing the fore colour in the low nibble of the colour byte, and the background colour in the high nibble. Apart from brown, all of these colours follow a pattern such that in binary, the top bit represents adding ⅓ to each component, and the other bits represent adding ⅔ to individual components. This makes it easy to convert to RGB colour values.
We need a method, TerminalColour, to read these 4 bit colour codes, and then call SetForeColour with the 16 bit equivalent. Try to implement this on your own. If you get stuck, or have not completed the Screen series, my implementation is given below:
```
.section .text
TerminalColour:
teq r0,#6
ldreq r0,=0x02B5
beq SetForeColour
tst r0,#0b1000
ldrne r1,=0x52AA
moveq r1,#0
tst r0,#0b0100
addne r1,#0x15
tst r0,#0b0010
addne r1,#0x540
tst r0,#0b0001
addne r1,#0xA800
mov r0,r1
b SetForeColour
```
### 2 Showing the Text
The first method we really need for our terminal is TerminalDisplay, one that copies the current data from terminalBuffer to terminalScreen and the actual screen. As mentioned, this method should do a minimal amount of work, because we need to be able to call it often. It should compare the text in terminalBuffer with that in terminalDisplay, and copy it across if they're different. Remember, terminalBuffer is a circular buffer running, in this case, from terminalView to terminalStop or 128*48 characters, whichever comes sooner. If we hit terminalStop, we'll assume all characters after that point are 7f16 (ASCII delete), and have colour 0 (black on a black background).
Let's look at what we have to do:
1. Load in terminalView, terminalStop and the address of terminalDisplay.
2. For each row:
1. For each column:
1. If view is not equal to stop, load the current character and colour from view
2. Otherwise load the character as 0x7f and the colour as 0
3. Load the current character from terminalDisplay
4. If the character and colour are equal, go to 10
5. Store the character and colour to terminalDisplay
6. Call TerminalColour with the background colour in r0
7. Call DrawCharacter with r0 = 0x7f (ASCII delete, a block), r1 = x, r2 = y
8. Call TerminalColour with the foreground colour in r0
9. Call DrawCharacter with r0 = character, r1 = x, r2 = y
10. Increment the position in terminalDisplay by 2
11. If view and stop are not equal, increment the view position by 2
12. If the view position is at the end of textBuffer, set it to the start
13. Increment the x co-ordinate by 8
2. Increment the y co-ordinate by 16
Try to implement this yourself. If you get stuck, my solution is given below:
1.
```
.globl TerminalDisplay
TerminalDisplay:
push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
x .req r4
y .req r5
char .req r6
col .req r7
screen .req r8
taddr .req r9
view .req r10
stop .req r11
ldr taddr,=terminalStart
ldr view,[taddr,#terminalView - terminalStart]
ldr stop,[taddr,#terminalStop - terminalStart]
add taddr,#terminalBuffer - terminalStart
add taddr,#128*128*2
mov screen,taddr
```
I go a little wild with variables here. I'm using taddr to store the location of the end of the textBuffer for ease.
2.
```
mov y,#0
yLoop$:
```
Start off the y loop.
1.
```
mov x,#0
xLoop$:
```
Start off the x loop.
1.
```
teq view,stop
ldrneh char,[view]
```
I load both the character and the colour into char simultaneously for ease.
2.
```
moveq char,#0x7f
```
This line complements the one above by acting as though a black delete character was read.
3.
```
ldrh col,[screen]
```
For simplicity I load both the character and colour into col simultaneously.
4.
```
teq col,char
beq xLoopContinue$
```
Now we can check if anything has changed with a teq.
5.
```
strh char,[screen]
```
We can also easily save the current value.
6.
```
lsr col,char,#8
and char,#0x7f
lsr r0,col,#4
bl TerminalColour
```
I split up char into the colour in col and the character in char with a bitshift and an and, then use a bitshift to get the background colour to call TerminalColour.
7.
```
mov r0,#0x7f
mov r1,x
mov r2,y
bl DrawCharacter
```
Write out a delete character which is a coloured block.
8.
```
and r0,col,#0xf
bl TerminalColour
```
Use an and to get the low nibble of col then call TerminalColour.
9.
```
mov r0,char
mov r1,x
mov r2,y
bl DrawCharacter
```
Write out the character we're supposed to write.
10.
```
xLoopContinue$:
add screen,#2
```
Increment the screen pointer.
11.
```
teq view,stop
addne view,#2
```
Increment the view pointer if necessary.
12.
```
teq view,taddr
subeq view,#128*128*2
```
It's easy to check for view going past the end of the buffer because the end of the buffer's address is stored in taddr.
13.
```
add x,#8
teq x,#1024
bne xLoop$
```
We increment x and then loop back if there are more characters to go.
2.
```
add y,#16
teq y,#768
bne yLoop$
```
We increment y and then loop back if there are more characters to go.
```
pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
.unreq x
.unreq y
.unreq char
.unreq col
.unreq screen
.unreq taddr
.unreq view
.unreq stop
```
Don't forget to clean up at the end!
### 3 Printing Lines
Now we have our TerminalDisplay method, which will automatically display the contents of terminalBuffer to terminalScreen, so theoretically we can draw text. However, we don't actually have any drawing routines that work on a character based display. A quick method that will come in handy first of all is TerminalClear, which completely clears the terminal. This can actually very easily be achieved with no loops. Try to deduce why the following method suffices:
```
.globl TerminalClear
TerminalClear:
ldr r0,=terminalStart
add r1,r0,#terminalBuffer-terminalStart
str r1,[r0]
str r1,[r0,#terminalStop-terminalStart]
str r1,[r0,#terminalView-terminalStart]
mov pc,lr
```
Now we need to make a basic method for character based displays; the Print function. This takes in a string address in r0, and a length in r1, and simply writes it to the current location at the screen. There are a few special characters to be wary of, as well as special behaviour to ensure that terminalView is kept up to date. Let's analyse what it has to do:
1. Check if string length is 0, if so return
2. Load in terminalStop and terminalView
3. Deduce the x-coordinate of terminalStop
4. For each character:
1. Check if the character is a new line
2. If so, increment bufferStop to the end of the line storing a black on black delete character.
3. Otherwise, copy the character in the current terminalColour
4. Check if we're at the end of a line
5. If so, check if the number of characters between terminalView and terminalStop is more than one screen
6. If so, increment terminalView by one line
7. Check if terminalView is at the end of the buffer, replace it with the start if so
8. Check if terminalStop is at the end of the buffer, replace it with the start if so
9. Check if terminalStop equals terminalStart, increment terminalStart by one line if so
10. Check if terminalStart is at the end of the buffer, replace it with the start if so
5. Store back terminalStop and terminalView.
See if you can implement this yourself. My solution is provided below:
1.
```
.globl Print
Print:
teq r1,#0
moveq pc,lr
```
This quick check at the beginning makes a call to Print with a string of length 0 almost instant.
2.
```
push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
bufferStart .req r4
taddr .req r5
x .req r6
string .req r7
length .req r8
char .req r9
bufferStop .req r10
view .req r11
mov string,r0
mov length,r1
ldr taddr,=terminalStart
ldr bufferStop,[taddr,#terminalStop-terminalStart]
ldr view,[taddr,#terminalView-terminalStart]
ldr bufferStart,[taddr]
add taddr,#terminalBuffer-terminalStart
add taddr,#128*128*2
```
I do a lot of setup here. bufferStart contains terminalStart, bufferStop contains terminalStop, view contains terminalView, taddr is the address of the end of terminalBuffer.
3.
```
and x,bufferStop,#0xfe
lsr x,#1
```
As per usual, a sneaky alignment trick makes everything easier. Because of the aligment of terminalBuffer, the x-coordinate of any character address is simply the last 8 bits divided by 2.
4.
1.
```
charLoop$:
ldrb char,[string]
and char,#0x7f
teq char,#'\n'
bne charNormal$
```
We need to check for new lines.
2.
```
mov r0,#0x7f
clearLine$:
strh r0,[bufferStop]
add bufferStop,#2
add x,#1
teq x,#128 blt clearLine$
b charLoopContinue$
```
Loop until the end of the line, writing out 0x7f; a delete character in black on a black background.
3.
```
charNormal$:
strb char,[bufferStop]
ldr r0,=terminalColour
ldrb r0,[r0]
strb r0,[bufferStop,#1]
add bufferStop,#2
add x,#1
```
Store the current character in the string and the terminalColour to the end of the terminalBuffer and then increment it and x.
4.
```
charLoopContinue$:
cmp x,#128
blt noScroll$
```
Check if x is at the end of a line; 128.
5.
```
mov x,#0
subs r0,bufferStop,view
addlt r0,#128*128*2
cmp r0,#128*(768/16)*2
```
Set x back to 0 and check if we're currently showing more than one screen. Remember, we're using a circular buffer, so if the difference between bufferStop and view is negative, we're actually wrapping around the buffer.
6.
```
addge view,#128*2
```
Add one lines worth of bytes to the view address.
7.
```
teq view,taddr
subeq view,taddr,#128*128*2
```
If the view address is at the end of the buffer we subtract the buffer length from it to move it back to the start. I set taddr to the address of the end of the buffer at the beginning.
8.
```
noScroll$:
teq bufferStop,taddr
subeq bufferStop,taddr,#128*128*2
```
If the stop address is at the end of the buffer we subtract the buffer length from it to move it back to the start. I set taddr to the address of the end of the buffer at the beginning.
9.
```
teq bufferStop,bufferStart
addeq bufferStart,#128*2
```
Check if bufferStop equals bufferStart. If so, add one line to bufferStart.
10.
```
teq bufferStart,taddr
subeq bufferStart,taddr,#128*128*2
```
If the start address is at the end of the buffer we subtract the buffer length from it to move it back to the start. I set taddr to the address of the end of the buffer at the beginning.
```
subs length,#1
add string,#1
bgt charLoop$
```
Loop until the string is done.
5.
```
charLoopBreak$:
sub taddr,#128*128*2
sub taddr,#terminalBuffer-terminalStart
str bufferStop,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
str bufferStart,[taddr]
pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
.unreq bufferStart
.unreq taddr
.unreq x
.unreq string
.unreq length
.unreq char
.unreq bufferStop
.unreq view
```
Store back the variables and return.
This method allows us to print arbitrary text to the screen. Throughout, I've been using the colour variable, but no where have we actually set it. Normally, terminals use special combinations of characters to change the colour. For example ASCII Escape (1b16) followed by a number 0 to f in hexadecimal could set the foreground colour to that CGA colour number. You can try implementing this yourself; my version is in the further examples section on the download page.
### 4 Standard Input
```
By convention, in many programming languages, every program has access to stdin and stdout, which are an input and and output stream linked to the terminal. This is still true on graphical programs, though many don't use it.
```
Now we have an output terminal that in theory can print out text and display it. That is only half the story however, we want input. We want to implement a method, ReadLine, which stores the next line of text a user types to a location given in r0, up to a maximum length given in r1, and returns the length of the string read in r0. The tricky thing is, the user annoyingly wants to see what they're typing as they type it, they want to use backspace to delete mistakes and they want to use return to submit commands. They probably even want a flashing underscore character to indicate the computer would like input! These perfectly reasonable requests make this method a real challenge. One way to achieve all of this is to store the text they type in memory somewhere along with its length, and then after every character, move the terminalStop address back to where it started when ReadLine was called and calling Print. This means we only have to be able to manipulate a string in memory, and then make use of our Print function.
Lets have a look at what ReadLine will do:
1. If the maximum length is 0, return 0
2. Retrieve the current values of terminalStop and terminalView
3. If the maximum length is bigger than half the buffer size, set it to half the buffer size
4. Subtract one from maximum length to ensure it can store our flashing underscore or a null terminator
5. Write an underscore to the string
6. Write the stored terminalView and terminalStop addresses back to the memory
7. Call Print on the current string
8. Call TerminalDisplay
9. Call KeyboardUpdate
10. Call KeyboardGetChar
11. If it is a new line character go to 16
12. If it is a backspace character, subtract 1 from the length of the string (if it is > 0)
13. If it is an ordinary character, write it to the string (if the length < maximum length)
14. If the string ends in an underscore, write a space, otherwise write an underscore
15. Go to 6
16. Write a new line character to the end of the string
17. Call Print and TerminalDisplay
18. Replace the new line with a null terminator
19. Return the length of the string
Convince yourself that this will work, and then try to implement it yourself. My implementation is given below:
1.
```
.globl ReadLine
ReadLine:
teq r1,#0
moveq r0,#0
moveq pc,lr
```
Quick special handling for the zero case, which is otherwise difficult.
2.
```
string .req r4
maxLength .req r5
input .req r6
taddr .req r7
length .req r8
view .req r9
push {r4,r5,r6,r7,r8,r9,lr}
mov string,r0
mov maxLength,r1
ldr taddr,=terminalStart
ldr input,[taddr,#terminalStop-terminalStart]
ldr view,[taddr,#terminalView-terminalStart]
mov length,#0
```
As per the general theme, I do a lot of initialisations early. input contains the value of terminalStop and view contains terminalView. Length starts at 0.
3.
```
cmp maxLength,#128*64
movhi maxLength,#128*64
```
We have to check for unusually large reads, as we can't process them beyond the size of the terminalBuffer (I suppose we CAN, but it would be very buggy, as terminalStart could move past the stored terminalStop).
4.
```
sub maxLength,#1
```
Since the user wants a flashing cursor, and we ideally want to put a null terminator on this string, we need 1 spare character.
5.
```
mov r0,#'_'
strb r0,[string,length]
```
Write out the underscore to let the user know they can input.
6.
```
readLoop$:
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
```
Save the stored terminalStop and terminalView. This is important to reset the terminal after each call to Print, which changes these variables. Strictly speaking it can change terminalStart too, but this is irreversible.
7.
```
mov r0,string
mov r1,length
add r1,#1
bl Print
```
Write the current input. We add 1 to the length for the underscore.
8.
```
bl TerminalDisplay
```
Copy the new text to the screen.
9.
```
bl KeyboardUpdate
```
Fetch the latest keyboard input.
10.
```
bl KeyboardGetChar
```
Retrieve the key pressed.
11.
```
teq r0,#'\n'
beq readLoopBreak$
teq r0,#0
beq cursor$
teq r0,#'\b'
bne standard$
```
Break out of the loop if we have an enter key. Also skip these conditions if we have a null terminator and process a backspace if we have one.
12.
```
delete$:
cmp length,#0
subgt length,#1
b cursor$
```
Remove one from the length to delete a character.
13.
```
standard$:
cmp length,maxLength
bge cursor$
strb r0,[string,length]
add length,#1
```
Write out an ordinary character where possible.
14.
```
cursor$:
ldrb r0,[string,length]
teq r0,#'_'
moveq r0,#' '
movne r0,#'_'
strb r0,[string,length]
```
Load in the last character, and change it to an underscore if it isn't one, and a space if it is.
15.
```
b readLoop$
readLoopBreak$:
```
Loop until the user presses enter.
16.
```
mov r0,#'\n'
strb r0,[string,length]
```
Store a new line at the end of the string.
17.
```
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
mov r0,string
mov r1,length
add r1,#1
bl Print
bl TerminalDisplay
```
Reset the terminalView and terminalStop and then Print and TerminalDisplay the final input.
18.
```
mov r0,#0
strb r0,[string,length]
```
Write out the null terminator.
19.
```
mov r0,length
pop {r4,r5,r6,r7,r8,r9,pc}
.unreq string
.unreq maxLength
.unreq input
.unreq taddr
.unreq length
.unreq view
```
Return the length.
### 5 The Terminal: Rise of the Machine
So, now we can theoretically interact with the user on the terminal. The most obvious thing to do is to put this to the test! In 'main.s' delete everything after bl UsbInitialise and copy in the following code:
```
reset$:
mov sp,#0x8000
bl TerminalClear
ldr r0,=welcome
mov r1,#welcomeEnd-welcome
bl Print
loop$:
ldr r0,=prompt
mov r1,#promptEnd-prompt
bl Print
ldr r0,=command
mov r1,#commandEnd-command
bl ReadLine
teq r0,#0
beq loopContinue$
mov r4,r0
ldr r5,=command
ldr r6,=commandTable
ldr r7,[r6,#0]
ldr r9,[r6,#4]
commandLoop$:
ldr r8,[r6,#8]
sub r1,r8,r7
cmp r1,r4
bgt commandLoopContinue$
mov r0,#0
commandName$:
ldrb r2,[r5,r0]
ldrb r3,[r7,r0]
teq r2,r3
bne commandLoopContinue$
add r0,#1
teq r0,r1
bne commandName$
ldrb r2,[r5,r0]
teq r2,#0
teqne r2,#' '
bne commandLoopContinue$
mov r0,r5
mov r1,r4
mov lr,pc
mov pc,r9
b loopContinue$
commandLoopContinue$:
add r6,#8
mov r7,r8
ldr r9,[r6,#4]
teq r9,#0
bne commandLoop$
ldr r0,=commandUnknown
mov r1,#commandUnknownEnd-commandUnknown
ldr r2,=formatBuffer
ldr r3,=command
bl FormatString
mov r1,r0
ldr r0,=formatBuffer
bl Print
loopContinue$:
bl TerminalDisplay
b loop$
echo:
cmp r1,#5
movle pc,lr
add r0,#5
sub r1,#5
b Print
ok:
teq r1,#5
beq okOn$
teq r1,#6
beq okOff$
mov pc,lr
okOn$:
ldrb r2,[r0,#3]
teq r2,#'o'
ldreqb r2,[r0,#4]
teqeq r2,#'n'
movne pc,lr
mov r1,#0
b okAct$
okOff$:
ldrb r2,[r0,#3]
teq r2,#'o'
ldreqb r2,[r0,#4]
teqeq r2,#'f'
ldreqb r2,[r0,#5]
teqeq r2,#'f'
movne pc,lr
mov r1,#1
okAct$:
mov r0,#16
b SetGpio
.section .data
.align 2
welcome: .ascii "Welcome to Alex's OS - Everyone's favourite OS"
welcomeEnd:
.align 2
prompt: .ascii "\n> "
promptEnd:
.align 2
command:
.rept 128
.byte 0
.endr
commandEnd:
.byte 0
.align 2
commandUnknown: .ascii "Command `%s' was not recognised.\n"
commandUnknownEnd:
.align 2
formatBuffer:
.rept 256
.byte 0
.endr
formatEnd:
.align 2
commandStringEcho: .ascii "echo"
commandStringReset: .ascii "reset"
commandStringOk: .ascii "ok"
commandStringCls: .ascii "cls"
commandStringEnd:
.align 2
commandTable:
.int commandStringEcho, echo
.int commandStringReset, reset$
.int commandStringOk, ok
.int commandStringCls, TerminalClear
.int commandStringEnd, 0
```
This code brings everything together into a simple command line operating system. The commands available are echo, reset, ok and cls. echo copies any text after it back to the terminal, reset resets the operating system if things go wrong, ok has two functions: ok on turns the OK LED on, and ok off turns the OK LED off, and cls clears the terminal using TerminalClear.
Have a go with this code on the Raspberry Pi. If it doesn't work, please see our troubleshooting page.
When it works, congratulations you've completed a basic terminal Operating System, and have completed the input series. Unfortunately, this is as far as these tutorials go at the moment, but I hope to make more in the future. Please send feedback to awc32@cam.ac.uk.
You're now in position to start building some simple terminal Operating Systems. My code above builds up a table of available commands in commandTable. Each entry in the table is an int for the address of the string, and an int for the address of the code to run. The last entry has to be commandStringEnd, 0. Try implementing some of your own commands, using our existing functions, or making new ones. The parameters for the functions to run are r0 is the address of the command the user typed, and r1 is the length. You can use this to pass inputs to your commands. Maybe you could make a calculator program, perhaps a drawing program or a chess program. Whatever ideas you've got, give them a go!
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html
作者:[Alex Chadwick][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://www.cl.cam.ac.uk
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/circular_buffer.png

View File

@ -1,142 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (DNS and Root Certificates)
[#]: via: (https://lushka.al/dns-and-certificates/)
[#]: author: (Anxhelo Lushka https://lushka.al/)
DNS and Root Certificates
======
Due to recent events we (as in we from the Privacy Today group) felt compelled to write an impromptu article on this matter. Its intended for all audiences so it will be kept simple - technical details may be posted later.
### What Is DNS And Why Does It Concern You?
DNS stands for Domain Name System and you encounter it daily. Whenever your web browser or any other application connects to the internet, it will most likely do so using a domain. A domain is simply the address you type: i.e. [duckduckgo.com][1]. Your computer needs to know where this leads to and will ask a DNS resolver for help. It will return an IP like [176.34.155.23][2]; the public network address you need to know to connect. This process is called a DNS lookup.
There are certain implications for both your privacy and your security as well as your liberty:
#### Privacy
Since you ask the resolver for an IP for a domain name, it knows exactly which sites youre visiting and, thanks to the “Internet Of Things”, often abbreviated as IoT, even which appliances you use at home.
#### Security
Youre trusting the resolver that the IP it returns is correct. There are certain checks to ensure it is so, under normal circumstances, that is not a common source of issues. These can be undermined though and thats why this article is important. If the IP is not correct, you can be fooled into connecting to malicious 3rd parties - even without ever noticing any difference. In this case, your privacy is in much greater danger because, not only are the sites you visit tracked, but the contents as well. 3rd parties can see exactly what youre looking at, collect personal information you enter (such as passwords), and a lot more. Your whole identity can be taken over with ease.
#### Liberty
Censorship is commonly enforced via DNS. Its not the most effective way to do so but it is extremely widespread. Even in western countries, its routinely used by corporations and governments. They use the same methods as potential attackers; they will not return the correct IP when you ask. They could act as if the domain doesnt exist or direct you elsewhere entirely.
### Ways DNS lookups can happen
#### 3rd Party DNS Resolvers Hosted By Your ISP
Most people are using 3rd party resolvers hosted by their Internet Service Provider. When you connect your modem, they will automatically be fetched and you might never bother with it at all.
#### 3rd Party DNS Resolver Of Your Choice
If you already knew what DNS means then you might have decided to use another DNS resolver of your choice. This might improve the situation since it makes it harder for your ISP to track you and you can avoid some forms of censorship. Both are still possible though, but the methods required are not as widely used.
#### Your Own (local) DNS Resolver
You can run your own and avoid some of the possible perils of using others. If youre interested in more information drop us a line.
### Root Certificates
#### What Is A Root Certificate?
Whenever you visit a website starting with https, you communicate with it using a certificate it sends. It enables your browser to encrypt the communication and ensures that nobody listening in can snoop. Thats why everybody has been told to look out for the https (rather than http) when logging into websites. The certificate itself only verifies that it has been generated for a certain domain. Theres more though:
Thats where the root certificate comes in. Think of it as the next higher level that makes sure the levels below are correct. It verifies that the certificate sent to you has been authorized by a certificate authority. This authority ensures that the person creating the certificate is actually the real operator.
This is also referred to as the chain of trust. Your operating system includes a set of these root certificates by default so that the chain of trust can be guaranteed.
#### Abuse
We now know that:
* DNS resolvers send you an IP address when you send a domain name
* Certificates allow encrypting your communication and verify they have been generated for the domain you visit
* Root certificates verify that the certificate is legitimate and has been created by the real site operator
**How can it be abused?**
* A malicious DNS resolver can send you a wrong IP for the purpose of censorship as said before. They can also send you to a completely different site.
* This site can send you a fake certificate.
* A malicious root certificate can “verify” this fake certificate.
This site will look absolutely fine to you; it has https in the URL and, if you click it, it will say verified. All just like you learned, right? **No!**
It now receives all the communication you intended to send to the original. This bypasses the checks created to avoid it. You wont receive error messages, your browser wont complain.
**All your data is compromised!**
### Conclusion
#### Risks
* Using a malicious DNS resolver can always compromise your privacy but your security will be unharmed as long as you look out for the https.
* Using a malicious DNS resolver and a malicious root certificate, your privacy and security are fully compromised.
#### Actions To Take
**Do not ever install a 3rd party root certificate!** There are very few exceptions why you would want to do so and none of them are applicable to general end users.
**Do not fall for clever marketing that ensures “ad blocking”, “military grade security”, or something similar**. There are methods of using DNS resolvers on their own to enhance your privacy but installing a 3rd party root certificate never makes sense. You are opening yourself up to extreme abuse.
### Seeing It Live
**WARNING**
A friendly sysadmin provided a live demo so you can see for yourself in realtime. This is real.
**DO NOT ENTER PRIVATE DATA! REMOVE THE CERT AND DNS AFTERWARDS!**
If you do not know how to, dont install it in the first place. While we trust our friend you still wouldnt want to have the root certificate of a random and unknown 3rd party installed.
#### Live Demo
Here is the link: <http://https-interception.info.tm/>
* Set the provided DNS resolver
* Install the provided root certificate
* Visit <https://paypal.com> and enter random login data
* Your data will show up on the website
### Further Information
If you are interested in more technical details, let us know. If there is enough interest, we might write an article but, for now, the important part is sharing the basics so you can make an informed decision and not fall for marketing and straight up fraud. Feel free to suggest other topics that are important to you.
This post is mirrored from [Privacy Today channel][3]. [Privacy Today][4] is a group about all things privacy, open source, libre philosophy and more!
All content is licensed under CC BY-NC-SA 4.0. ([Attribution-NonCommercial-ShareAlike 4.0 International][5]).
--------------------------------------------------------------------------------
via: https://lushka.al/dns-and-certificates/
作者:[Anxhelo Lushka][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://lushka.al/
[b]: https://github.com/lujun9972
[1]: https://duckduckgo.com
[2]: http://176.34.155.23
[3]: https://t.me/privacytoday
[4]: https://t.me/joinchat/Awg5A0UW-tzOLX7zMoTDog
[5]: https://creativecommons.org/licenses/by-nc-sa/4.0/

View File

@ -0,0 +1,911 @@
[#]: collector: (lujun9972)
[#]: translator: (guevaraya )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Computer Laboratory Raspberry Pi: Lesson 11 Input02)
[#]: via: (https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html)
[#]: author: (Alex Chadwick https://www.cl.cam.ac.uk)
计算机实验室 树莓派开发: 课程11 输入02
======
课程输入02是以课程输入01基础讲解的通过一个简单的命令行实现用户的命令输入和计算机的处理和显示。本文假设你已经具备 [课程11输入01][1] 的操作系统代码基础。
### 1 终端
```
早期的计算一般是在一栋楼里的一个巨型计算机系统,他有很多可以输命令的'终端'。计算机依次执行不同来源的命令。
```
几乎所有的操作系统都是以字符终端显示启动的。经典的黑底白字,通过键盘输入计算机要执行的命令,然后会提示你拼写错误,或者恰好得到你想要的执行结果。这种方法有两个主要优点:键盘和显示器可以提供简易,健壮的计算机交互机制,几乎所有的计算机系统都采用这个机制,这个也广泛被系统管理员应用。
让我们分析下真正想要哪些信息:
1. 计算机打开后,显示欢迎信息
2. 计算机启动后可以接受输入标志
3. 用户从键盘输入带参数的命令
4. 用户输入回车键或提交按钮
5. 计算机解析命令后执行可用的命令
6. 计算机显示命令的执行结果,过程信息
7. 循环跳转到步骤2
这样的终端被定义为标准的输入输出设备。用于输入的屏幕和输出打印的屏幕是同一个。也就是说终端是对字符显示的一个抽象。字符显示中,单个字符是最小的单元,而不是像素。屏幕被划分成固定数量不同颜色的字符。我们可以在现有的屏幕代码基础上,先存储字符和对应的颜色,然后再用方法 DrawCharacter 把其推送到屏幕上。一旦我们需要字符显示,就只需要在屏幕上画出一行字符串。
新建文件名为 terminal.s 如下:
```
.section .data
.align 4
terminalStart:
.int terminalBuffer
terminalStop:
.int terminalBuffer
terminalView:
.int terminalBuffer
terminalColour:
.byte 0xf
.align 8
terminalBuffer:
.rept 128*128
.byte 0x7f
.byte 0x0
.endr
terminalScreen:
.rept 1024/8 core.md Dict.md lctt2014.md lctt2016.md lctt2018.md LICENSE published README.md scripts sources translated 768/16
.byte 0x7f
.byte 0x0
.endr
```
这是文件终端的配置数据文件。我们有两个主要的存储变量terminalBuffer 和 terminalScreen。terminalBuffer保存所有显示过的字符。它保存128行字符文本1行包含128个字符。每个字符有一个 ASCII 字符和颜色单元组成初始值为0x7fASCII的删除键和 0前景色和背景色为黑。terminalScreen 保存当前屏幕显示的字符。它保存128x48的字符与 terminalBuffer 初始化值一样。你可能会想我仅需要terminalScreen就够了为什么还要terminalBuffer其实有两个好处
1. 我们可以很容易看到字符串的变化,只需画出有变化的字符。
2. 我们可以回滚终端显示的历史字符,也就是缓冲的字符(有限制)
你总是需要尝试去设计一个高效的系统,如果很少变化的条件这个系统会运行的更快。
独特的技巧在低功耗系统里很常见。画屏是很耗时的操作因此我们仅在不得已的时候才去执行这个操作。在这个系统里我们可以任意改变terminalBuffer然后调用一个仅拷贝屏幕上字节变化的方法。也就是说我们不需要持续画出每个字符这样可以节省一大段跨行文本的操作时间。
其他在 .data 段的值得含义如下:
* terminalStart
写入到 terminalBuffer 的第一个字符
* terminalStop
写入到 terminalBuffer 的最后一个字符
* terminalView
表示当前屏幕的第一个字符,这样我们可以控制滚动屏幕
* temrinalColour
即将被描画的字符颜色
```
循环缓冲区是**数据结构**一个例子。这是一个组织数据的思路,有时我们通过软件实现这种思路。
```
![显示 Hellow world 插入到大小为5的循环缓冲区的示意图。][2]
terminalStart 需要保存起来的原因是 termainlBuffer 是一个循环缓冲区。意思是当缓冲区变满时,末尾地方会回滚覆盖开始位置,这样最后一个字符变成了第一个字符。因此我们需要将 terminalStart 往前推进这样我们知道我们已经占满它了。如何实现缓冲区检测如果索引越界到缓冲区的末尾就将索引指向缓冲区的开始位置。循环缓冲区是一个比较常见的高明的存储大量数据的方法往往这些数据的最近部分比较重要。它允许无限制的写入只保证最近一些特定数据有效。这个常常用于信号处理和数据压缩算法。这样的情况可以允许我们存储128行终端记录超过128行也不会有问题。如果不是这样当超过第128行时我们需要把127行分别向前拷贝一次这样很浪费时间。
之前已经提到过 terminalColour 几次了。你可以根据你的想法实现终端颜色但这个文本终端有16个前景色和16个背景色这里相当于有16²=256种组合。[CGA][3]终端的颜色定义如下:
表格 1.1 - CGA 颜色编码
| 序号 | 颜色 (R, G, B) |
| ------ | ------------------------|
| 0 | 黑 (0, 0, 0) |
| 1 | 蓝 (0, 0, ⅔) |
| 2 | 绿 (0, ⅔, 0) |
| 3 | 青色 (0, ⅔, ⅔) |
| 4 | 红色 (⅔, 0, 0) |
| 5 | 品红 (⅔, 0, ⅔) |
| 6 | 棕色 (⅔, ⅓, 0) |
| 7 | 浅灰色 (⅔, ⅔, ⅔) |
| 8 | 灰色 (⅓, ⅓, ⅓) |
| 9 | 淡蓝色 (⅓, ⅓, 1) |
| 10 | 淡绿色 (⅓, 1, ⅓) |
| 11 | 淡青色 (⅓, 1, 1) |
| 12 | 淡红色 (1, ⅓, ⅓) |
| 13 | 浅品红 (1, ⅓, 1) |
| 14 | 黄色 (1, 1, ⅓) |
| 15 | 白色 (1, 1, 1) |
```
棕色作为替代色(黑黄色)既不吸引人也没有什么用处。
```
我们将前景色保存到颜色的低字节,背景色保存到颜色高字节。除过棕色,其他这些颜色遵循一种模式如二进制的高位比特代表增加 ⅓ 到每个组件其他比特代表增加⅔到各自组件。这样很容易进行RGB颜色转换。
我们需要一个方法从TerminalColour读取颜色编码的四个比特然后用16比特等效参数调用 SetForeColour。尝试实现你自己实现。如果你感觉麻烦或者还没有完成屏幕系列课程我们的实现如下
```
.section .text
TerminalColour:
teq r0,#6
ldreq r0,=0x02B5
beq SetForeColour
tst r0,#0b1000
ldrne r1,=0x52AA
moveq r1,#0
tst r0,#0b0100
addne r1,#0x15
tst r0,#0b0010
addne r1,#0x540
tst r0,#0b0001
addne r1,#0xA800
mov r0,r1
b SetForeColour
```
### 2 文本显示
我们的终端第一个真正需要的方法是 TerminalDisplay它用来把当前的数据从 terminalBuffe r拷贝到 terminalScreen 和实际的屏幕。如上所述,这个方法必须是最小开销的操作,因为我们需要频繁调用它。它主要比较 terminalBuffer 与 terminalDisplay的文本然后只拷贝有差异的字节。请记住 terminalBuffer 是循环缓冲区运行的,这种情况,从 terminalView 到 terminalStop 或者 128*48 字符集,哪个来的最快。如果我们遇到 terminalStop我们将会假定在这之后的所有字符是7f16 (ASCII delete)背景色为0黑色前景色和背景色
让我们看看必须要做的事情:
1. 加载 terminalView terminalStop 和 terminalDisplay 的地址。
2. 执行每一行:
1. 执行每一列:
1. 如果 terminalView 不等于 terminalStop根据 terminalView 加载当前字符和颜色
2. 否则加载 0x7f 和颜色 0
3. 从 terminalDisplay 加载当前的字符
4. 如果字符和颜色相同直接跳转到10
5. 存储字符和颜色到 terminalDisplay
6. 用 r0 作为背景色参数调用 TerminalColour
7. 用 r0 = 0x7f (ASCII 删除键, 一大块), r1 = x, r2 = y 调用 DrawCharacter
8. 用 r0 作为前景色参数调用 TerminalColour
9. 用 r0 = 字符, r1 = x, r2 = y 调用 DrawCharacter
10. 对位置参数 terminalDisplay 累加2
11. 如果 terminalView 不等于 terminalStop不能相等 terminalView 位置参数累加2
12. 如果 terminalView 位置已经是文件缓冲器的末尾,将他设置为缓冲区的开始位置
13. x 坐标增加8
2. y 坐标增加16
Try to implement this yourself. If you get stuck, my solution is given below:
尝试去自己实现吧。如果你遇到问题,我们的方案下面给出来了:
1.
```
.globl TerminalDisplay
TerminalDisplay:
push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
x .req r4
y .req r5
char .req r6
col .req r7
screen .req r8
taddr .req r9
view .req r10
stop .req r11
ldr taddr,=terminalStart
ldr view,[taddr,#terminalView - terminalStart]
ldr stop,[taddr,#terminalStop - terminalStart]
add taddr,#terminalBuffer - terminalStart
add taddr,#128*128*2
mov screen,taddr
```
我这里的变量有点乱。为了方便起见,我用 taddr 存储 textBuffer 的末尾位置。
2.
```
mov y,#0
yLoop$:
```
从yLoop开始运行。
1.
```
mov x,#0
xLoop$:
```
从yLoop开始运行。
1.
```
teq view,stop
ldrneh char,[view]
```
为了方便起见,我把字符和颜色同时加载到 char 变量了
2.
```
moveq char,#0x7f
```
这行是对上面一行的补充说明读取黑色的Delete键
3.
```
ldrh col,[screen]
```
为了简便我把字符和颜色同时加载到 col 里。
4.
```
teq col,char
beq xLoopContinue$
```
现在我用teq指令检查是否有数据变化
5.
```
strh char,[screen]
```
我可以容易的保存当前值
6.
```
lsr col,char,#8
and char,#0x7f
lsr r0,col,#4
bl TerminalColour
```
我用 bitshift比特偏移 指令和 and 指令从 char 变量中分离出颜色到 col 变量和字符到 char变量然后再用bitshift比特偏移指令后调用TerminalColour 获取背景色。
7.
```
mov r0,#0x7f
mov r1,x
mov r2,y
bl DrawCharacter
```
写入一个彩色的删除字符块
8.
```
and r0,col,#0xf
bl TerminalColour
```
用 and 指令获取 col 变量的最低字节然后调用TerminalColour
9.
```
mov r0,char
mov r1,x
mov r2,y
bl DrawCharacter
```
写入我们需要的字符
10.
```
xLoopContinue$:
add screen,#2
```
自增屏幕指针
11.
```
teq view,stop
addne view,#2
```
如果可能自增view指针
12.
```
teq view,taddr
subeq view,#128*128*2
```
很容易检测 view指针是否越界到缓冲区的末尾因为缓冲区的地址保存在 taddr 变量里
13.
```
add x,#8
teq x,#1024
bne xLoop$
```
如果还有字符需要显示,我们就需要自增 x 变量然后循环到 xLoop 执行
2.
```
add y,#16
teq y,#768
bne yLoop$
```
如果还有更多的字符显示我们就需要自增 y 变量,然后循环到 yLoop 执行
```
pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
.unreq x
.unreq y
.unreq char
.unreq col
.unreq screen
.unreq taddr
.unreq view
.unreq stop
```
不要忘记最后清除变量
### 3 行打印
现在我有了自己 TerminalDisplay方法它可以自动显示 terminalBuffer 到 terminalScreen因此理论上我们可以画出文本。但是实际上我们没有任何基于字符显示的实例。 首先快速容易上手的方法便是 TerminalClear 它可以彻底清除终端。这个方法没有循环很容易实现。可以尝试分析下面的方法应该不难:
```
.globl TerminalClear
TerminalClear:
ldr r0,=terminalStart
add r1,r0,#terminalBuffer-terminalStart
str r1,[r0]
str r1,[r0,#terminalStop-terminalStart]
str r1,[r0,#terminalView-terminalStart]
mov pc,lr
```
现在我们需要构造一个字符显示的基础方法:打印函数。它将保存在 r0 的字符串和 保存在 r1 字符串长度简易的写到屏幕上。有一些特定字符需要特别的注意,这些特定的操作是确保 terminalView 是最新的。我们来分析一下需要做啥:
1. 检查字符串的长度是否为0如果是就直接返回
2. 加载 terminalStop 和 terminalView
3. 计算出 terminalStop 的 x 坐标
4. 对每一个字符的操作:
1. 检查字符是否为新起一行
2. 如果是的话,自增 bufferStop 到行末,同时写入黑色删除键
3. 否则拷贝当前 terminalColour 的字符
4. 加成是在行末
5. 如果是,检查从 terminalView 到 terminalStop 之间的字符数是否大于一屏
6. 如果是terminalView 自增一行
7. 检查 terminalView 是否为缓冲区的末尾,如果是的话将其替换为缓冲区的起始位置
8. 检查 terminalStop 是否为缓冲区的末尾,如果是的话将其替换为缓冲区的起始位置
9. 检查 terminalStop 是否等于 terminalStart 如果是的话 terminalStart 自增一行。
10. 检查 terminalStart 是否为缓冲区的末尾,如果是的话将其替换为缓冲区的起始位置
5. 存取 terminalStop 和 terminalView
试一下自己去实现。我们的方案提供如下:
1.
```
.globl Print
Print:
teq r1,#0
moveq pc,lr
```
这个是打印函数开始快速检查字符串为0的代码
2.
```
push {r4,r5,r6,r7,r8,r9,r10,r11,lr}
bufferStart .req r4
taddr .req r5
x .req r6
string .req r7
length .req r8
char .req r9
bufferStop .req r10
view .req r11
mov string,r0
mov length,r1
ldr taddr,=terminalStart
ldr bufferStop,[taddr,#terminalStop-terminalStart]
ldr view,[taddr,#terminalView-terminalStart]
ldr bufferStart,[taddr]
add taddr,#terminalBuffer-terminalStart
add taddr,#128*128*2
```
这里我做了很多配置。 bufferStart 代表 terminalStart bufferStop代表terminalStop view 代表 terminalViewtaddr 代表 terminalBuffer 的末尾地址。
3.
```
and x,bufferStop,#0xfe
lsr x,#1
```
和通常一样,巧妙的对齐技巧让许多事情更容易。由于需要对齐 terminalBuffer每个字符的 x 坐标需要8位要除以2。
4.
1.
```
charLoop$:
ldrb char,[string]
and char,#0x7f
teq char,#'\n'
bne charNormal$
```
我们需要检查新行
2.
```
mov r0,#0x7f
clearLine$:
strh r0,[bufferStop]
add bufferStop,#2
add x,#1
teq x,#128 blt clearLine$
b charLoopContinue$
```
循环执行值到行末写入 0x7f黑色删除键
3.
```
charNormal$:
strb char,[bufferStop]
ldr r0,=terminalColour
ldrb r0,[r0]
strb r0,[bufferStop,#1]
add bufferStop,#2
add x,#1
```
存储字符串的当前字符和 terminalBuffer 末尾的 terminalColour然后将它和 x 变量自增
4.
```
charLoopContinue$:
cmp x,#128
blt noScroll$
```
检查 x 是否为行末128
5.
```
mov x,#0
subs r0,bufferStop,view
addlt r0,#128*128*2
cmp r0,#128*(768/16)*2
```
这是 x 为 0 然后检查我们是否已经显示超过1屏。请记住我们是用的循环缓冲区因此如果 bufferStop 和 view 之前差是负值,我们实际使用是环绕缓冲区。
6.
```
addge view,#128*2
```
增加一行字节到 view 的地址
7.
```
teq view,taddr
subeq view,taddr,#128*128*2
```
如果 view 地址是缓冲区的末尾,我们就从它上面减去缓冲区的长度,让其指向开始位置。我会在开始的时候设置 taddr 为缓冲区的末尾地址。
8.
```
noScroll$:
teq bufferStop,taddr
subeq bufferStop,taddr,#128*128*2
```
如果 stop 的地址在缓冲区末尾,我们就从它上面减去缓冲区的长度,让其指向开始位置。我会在开始的时候设置 taddr 为缓冲区的末尾地址。
9.
```
teq bufferStop,bufferStart
addeq bufferStart,#128*2
```
检查 bufferStop 是否等于 bufferStart。 如果等于增加一行到 bufferStart。
10.
```
teq bufferStart,taddr
subeq bufferStart,taddr,#128*128*2
```
如果 start 的地址在缓冲区的末尾,我们就从它上面减去缓冲区的长度,让其指向开始位置。我会在开始的时候设置 taddr 为缓冲区的末尾地址。
```
subs length,#1
add string,#1
bgt charLoop$
```
循环执行知道字符串结束
5.
```
charLoopBreak$:
sub taddr,#128*128*2
sub taddr,#terminalBuffer-terminalStart
str bufferStop,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
str bufferStart,[taddr]
pop {r4,r5,r6,r7,r8,r9,r10,r11,pc}
.unreq bufferStart
.unreq taddr
.unreq x
.unreq string
.unreq length
.unreq char
.unreq bufferStop
.unreq view
```
保存变量然后返回
这个方法允许我们打印任意字符到屏幕。然而我们用了颜色变量但实际上没有设置它。一般终端用特性的组合字符去行修改颜色。如ASCII转移1b16后面跟着一个0-f的16进制的书就可以设置前景色为 CGA颜色。如果你自己想尝试实现在下载页面有一个我的详细的例子。
### 4 标志输入
```
按照惯例,许多编程语言中,任意程序可以访问 stdin 和 stdin他们可以连接到终端的输入和输出流。在图形程序其实也可以进行同样操作但实际几乎不用。
```
现在我们有一个可以打印和显示文本的输出终端。这仅仅是说了一半我们需要输入。我们想实现一个方法Readline可以保存文件的一行文本文本位置有 r0 给出,最大的长度由 r1 给出,返回 r0 里面的字符串长度。棘手的是用户输出字符的时候要回显功能,同时想要退格键的删除功能和命令回车执行功能。他们还想需要一个闪烁的下划线代表计算机需要输入。这些完全合理的要求让构造这个方法更具有挑战性。有一个方法完成这些需求就是存储用户输入的文本和文件大小到内存的某个地方。然后当调用 ReadLine 的时候,移动 terminalStop 的地址到它开始的地方然后调用 Print。也就是说我们只需要确保在内存维护一个字符串然后构造一个我们自己的打印函数。
让我们看看 ReadLine做了哪些事情
1. 如果字符串可保存的最大长度为0直接返回
2. 检索 terminalStop 和 terminalStop 的当前值
3. 如果字符串的最大长度大约缓冲区的一半,就设置大小为缓冲区的一半
4. 从最大长度里面减去1来确保输入的闪烁字符或结束符
5. 向字符串写入一个下划线
6. 写入一个 terminalView 和 terminalStop 的地址到内存
7. 调用 Print 大约当前字符串
8. 调用 TerminalDisplay
9. 调用 KeyboardUpdate
10. 调用 KeyboardGetChar
11. 如果为一个新行直接跳转到16
12. 如果是一个退格键将字符串长度减一如果其大约0
13. 如果是一个普通字符,将他写入字符串(字符串大小确保小于最大值)
14. 如果字符串是以下划线结束,写入一个空格,否则写入下划线
15. 跳转到6
16. 字符串的末尾写入一个新行
17. 调用 Print 和 TerminalDisplay
18. 用结束符替换新行
19. 返回字符串的长度
为了方便读者理解,然后然后自己去实现,我们的实现提供如下:
1.
```
.globl ReadLine
ReadLine:
teq r1,#0
moveq r0,#0
moveq pc,lr
```
快速处理长度为0的情况
2.
```
string .req r4
maxLength .req r5
input .req r6
taddr .req r7
length .req r8
view .req r9
push {r4,r5,r6,r7,r8,r9,lr}
mov string,r0
mov maxLength,r1
ldr taddr,=terminalStart
ldr input,[taddr,#terminalStop-terminalStart]
ldr view,[taddr,#terminalView-terminalStart]
mov length,#0
```
考虑到常见的场景我们初期做了很多初始化动作。input 代表 terminalStop 的值view 代表 terminalView。Length 默认为 0.
3.
```
cmp maxLength,#128*64
movhi maxLength,#128*64
```
我们必须检查异常大的读操作,我们不能处理超过 terminalBuffer 大小的输入(理论上可行但是terminalStart 移动越过存储的terminalStop会有很多问题)。
4.
```
sub maxLength,#1
```
由于用户需要一个闪烁的光标,我们需要一个备用字符在理想状况在这个字符串后面放一个结束符。
5.
```
mov r0,#'_'
strb r0,[string,length]
```
写入一个下划线让用户知道我们可以输入了。
6.
```
readLoop$:
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
```
保存 terminalStop 和 terminalView。这个对重置一个终端很重要它会修改这些变量。严格讲也可以修改 terminalStart但是不可逆。
7.
```
mov r0,string
mov r1,length
add r1,#1
bl Print
```
写入当前的输入。由于下划线因此字符串长度加1
8.
```
bl TerminalDisplay
```
拷贝下一个文本到屏幕
9.
```
bl KeyboardUpdate
```
获取最近一次键盘输入
10.
```
bl KeyboardGetChar
```
检索键盘输入键值
11.
```
teq r0,#'\n'
beq readLoopBreak$
teq r0,#0
beq cursor$
teq r0,#'\b'
bne standard$
```
如果我们有一个回车键,循环中断。如果有结束符和一个退格键也会同样跳出选好。
12.
```
delete$:
cmp length,#0
subgt length,#1
b cursor$
```
从 length 里面删除一个字符
13.
```
standard$:
cmp length,maxLength
bge cursor$
strb r0,[string,length]
add length,#1
```
写回一个普通字符
14.
```
cursor$:
ldrb r0,[string,length]
teq r0,#'_'
moveq r0,#' '
movne r0,#'_'
strb r0,[string,length]
```
加载最近的一个字符,如果不是下换线则修改为下换线,如果是空格则修改为下划线
15.
```
b readLoop$
readLoopBreak$:
```
循环执行值到用户输入按下
16.
```
mov r0,#'\n'
strb r0,[string,length]
```
在字符串的结尾处存入一新行
17.
```
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
mov r0,string
mov r1,length
add r1,#1
bl Print
bl TerminalDisplay
```
重置 terminalView 和 terminalStop 然后调用 Print 和 TerminalDisplay 输入回显
18.
```
mov r0,#0
strb r0,[string,length]
```
写入一个结束符
19.
```
mov r0,length
pop {r4,r5,r6,r7,r8,r9,pc}
.unreq string
.unreq maxLength
.unreq input
.unreq taddr
.unreq length
.unreq view
```
返回长度
### 5 终端: 机器进化
现在我们理论用终端和用户可以交互了。最显而易见的事情就是拿去测试了!在 'main.s' 里UsbInitialise后面的删除代码如下
```
reset$:
mov sp,#0x8000
bl TerminalClear
ldr r0,=welcome
mov r1,#welcomeEnd-welcome
bl Print
loop$:
ldr r0,=prompt
mov r1,#promptEnd-prompt
bl Print
ldr r0,=command
mov r1,#commandEnd-command
bl ReadLine
teq r0,#0
beq loopContinue$
mov r4,r0
ldr r5,=command
ldr r6,=commandTable
ldr r7,[r6,#0]
ldr r9,[r6,#4]
commandLoop$:
ldr r8,[r6,#8]
sub r1,r8,r7
cmp r1,r4
bgt commandLoopContinue$
mov r0,#0
commandName$:
ldrb r2,[r5,r0]
ldrb r3,[r7,r0]
teq r2,r3
bne commandLoopContinue$
add r0,#1
teq r0,r1
bne commandName$
ldrb r2,[r5,r0]
teq r2,#0
teqne r2,#' '
bne commandLoopContinue$
mov r0,r5
mov r1,r4
mov lr,pc
mov pc,r9
b loopContinue$
commandLoopContinue$:
add r6,#8
mov r7,r8
ldr r9,[r6,#4]
teq r9,#0
bne commandLoop$
ldr r0,=commandUnknown
mov r1,#commandUnknownEnd-commandUnknown
ldr r2,=formatBuffer
ldr r3,=command
bl FormatString
mov r1,r0
ldr r0,=formatBuffer
bl Print
loopContinue$:
bl TerminalDisplay
b loop$
echo:
cmp r1,#5
movle pc,lr
add r0,#5
sub r1,#5
b Print
ok:
teq r1,#5
beq okOn$
teq r1,#6
beq okOff$
mov pc,lr
okOn$:
ldrb r2,[r0,#3]
teq r2,#'o'
ldreqb r2,[r0,#4]
teqeq r2,#'n'
movne pc,lr
mov r1,#0
b okAct$
okOff$:
ldrb r2,[r0,#3]
teq r2,#'o'
ldreqb r2,[r0,#4]
teqeq r2,#'f'
ldreqb r2,[r0,#5]
teqeq r2,#'f'
movne pc,lr
mov r1,#1
okAct$:
mov r0,#16
b SetGpio
.section .data
.align 2
welcome: .ascii "Welcome to Alex's OS - Everyone's favourite OS"
welcomeEnd:
.align 2
prompt: .ascii "\n> "
promptEnd:
.align 2
command:
.rept 128
.byte 0
.endr
commandEnd:
.byte 0
.align 2
commandUnknown: .ascii "Command `%s' was not recognised.\n"
commandUnknownEnd:
.align 2
formatBuffer:
.rept 256
.byte 0
.endr
formatEnd:
.align 2
commandStringEcho: .ascii "echo"
commandStringReset: .ascii "reset"
commandStringOk: .ascii "ok"
commandStringCls: .ascii "cls"
commandStringEnd:
.align 2
commandTable:
.int commandStringEcho, echo
.int commandStringReset, reset$
.int commandStringOk, ok
.int commandStringCls, TerminalClear
.int commandStringEnd, 0
```
这块代码集成了一个简易的命令行操作系统。支持命令echoresetok 和 cls。echo 拷贝任意文本到终端reset命令会在系统出现问题的是复位操作系统ok 有两个功能:设置 OK 灯亮灭,最后 cls 调用 TerminalClear 清空终端。
试试树莓派的代码吧。如果遇到问题,请参照问题集锦页面吧。
如果运行正常祝贺你完成了一个操作系统基本终端和输入系列的课程。很遗憾这个教程先讲到这里但是我希望将来能制作更多教程。有问题请反馈至awc32@cam.ac.uk。
你已经在建立了一个简易的终端操作系统。我们的代码在 commandTable 构造了一个可用的命令表格。每个表格的入口是一个整型数字,用来表示字符串的地址,和一个整型数字表格代码的执行入口。 最后一个入口是 为 0 的commandStringEnd。尝试实现你自己的命令可以参照已有的函数建立一个新的。函数的参数 r0 是用户输入的命令地址r1是其长度。你可以用这个传递你输入值到你的命令。也许你有一个计算器程序或许是一个绘图程序或国际象棋。不管你的什么电子让它跑起来
--------------------------------------------------------------------------------
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html
作者:[Alex Chadwick][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/guevaraya)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://www.cl.cam.ac.uk
[b]: https://github.com/lujun9972
[1]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html
[2]: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/images/circular_buffer.png
[3]: https://en.wikipedia.org/wiki/Color_Graphics_Adapter