前沿拓展:
索引越界
恢復(fù)軟件有時(shí)并不能完全正確恢復(fù)所有的文件,尤其是對(duì)格式化后的系統(tǒng)。
而且,EFS較加密系統(tǒng)是對(duì)文件加密后存儲(chǔ)的,加密后的存儲(chǔ)報(bào)婷格式肯定與普通文件不同。而且這種加密是基于文件系統(tǒng)類型的。
可能是恢復(fù)軟件不能正確處理這種加密文件的某些具體參數(shù)。具體細(xì)節(jié)我也不清楚。
NTFS分區(qū)看看,能不能直接打開。
課程輸入 02 是以課程輸入 01 為基礎(chǔ)講解的,通過一個(gè)簡(jiǎn)單的命令行實(shí)現(xiàn)用戶的命令輸入和計(jì)算機(jī)的處理和顯示。
— Alex Chadwick
課程輸入 02 是以課程輸入 01 為基礎(chǔ)講解的,通過一個(gè)簡(jiǎn)單的命令行實(shí)現(xiàn)用戶的命令輸入和計(jì)算機(jī)的處理和顯示。本文假設(shè)你已經(jīng)具備 課程11:輸入01 的**作系統(tǒng)代碼基礎(chǔ)。
1、終端
幾乎所有的**作系統(tǒng)都是以字符終端顯示啟動(dòng)的。經(jīng)典的黑底白字,通過鍵盤輸入計(jì)算機(jī)要執(zhí)行的命令,第二會(huì)提示你拼寫錯(cuò)誤,或者恰好得到你想要的執(zhí)行結(jié)果。這種方法有兩個(gè)主要優(yōu)點(diǎn):鍵盤和顯示器可以提供簡(jiǎn)易、健壯的計(jì)算機(jī)交互機(jī)制,幾乎所有的計(jì)算機(jī)系統(tǒng)都采用這個(gè)機(jī)制,這個(gè)也廣泛被系統(tǒng)管理員應(yīng)用。
早期的計(jì)算一般是在一棟樓里的一個(gè)巨型計(jì)算機(jī)系統(tǒng),它有很多可以輸命令的’終端’。計(jì)算機(jī)依次執(zhí)行不同來源的命令。
讓我們分析下真正想要哪些信息:
計(jì)算機(jī)打開后,顯示歡迎信息計(jì)算機(jī)啟動(dòng)后可以接受輸入標(biāo)志用戶從鍵盤輸入帶參數(shù)的命令用戶輸入回車鍵或提交按鈕計(jì)算機(jī)解析命令后執(zhí)行可用的命令計(jì)算機(jī)顯示命令的執(zhí)行結(jié)果,過程信息循環(huán)跳轉(zhuǎn)到步驟 2
這樣的終端被定義為標(biāo)準(zhǔn)的輸入輸出設(shè)備。用于(顯示)輸入的屏幕和打印輸出內(nèi)容的屏幕是同一個(gè)(LCTT 譯注:最早期的輸出打印真是“打印”到打印機(jī)/電傳機(jī)的,而用于輸入的終端只是鍵盤,除非做了回顯,否則輸出終端是不會(huì)顯示輸入的字符的)。也就是說終端是對(duì)字符顯示的一個(gè)抽象。字符顯示中,單個(gè)字符是最小的單元,而不是像素。屏幕被劃分成固定數(shù)量不同顏色的字符。我們可以在現(xiàn)有的屏幕代碼基礎(chǔ)上,先存儲(chǔ)字符和對(duì)應(yīng)的顏色,第二再用方法 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
這是文件終端的配置數(shù)據(jù)文件。我們有兩個(gè)主要的存儲(chǔ)變量:terminalBuffer 和 terminalScreen。terminalBuffer 保存所有顯示過的字符。它保存 128 行字符文本(1 行包含 128 個(gè)字符)。每個(gè)字符有一個(gè) ASCII 字符和顏色單元組成,初始值為 0x7f(ASCII 的刪除字符)和 0(前景色和背景色為黑)。terminalScreen 保存當(dāng)前屏幕顯示的字符。它保存 128×48 個(gè)字符,與 terminalBuffer 初始化值一樣。你可能會(huì)覺得我僅需要 terminalScreen 就夠了,為什么還要terminalBuffer,其實(shí)有兩個(gè)好處:
我們可以很容易看到字符串的變化,只需畫出有變化的字符。我們可以回滾終端顯示的歷史字符,也就是緩沖的字符(有限制)
這種獨(dú)特的技巧在低功耗系統(tǒng)里很常見。畫屏是很耗時(shí)的**作,因此我們僅在不得已的時(shí)候才去執(zhí)行這個(gè)**作。在這個(gè)系統(tǒng)里,我們可以任意改變 terminalBuffer,第二調(diào)用一個(gè)僅拷貝屏幕上字節(jié)變化的方法。也就是說我們不需要持續(xù)畫出每個(gè)字符,這樣可以節(jié)省一大段跨行文本的**作時(shí)間。
你總是需要嘗試去設(shè)計(jì)一個(gè)高效的系統(tǒng),如果在很少變化的情況下這個(gè)系統(tǒng)會(huì)運(yùn)行的更快。
其他在 .data 段的值得含義如下:
terminalStart 寫入到 terminalBuffer 的第一個(gè)字符terminalStop 寫入到 terminalBuffer 的最后一個(gè)字符terminalView 表示當(dāng)前屏幕的第一個(gè)字符,這樣我們可以控制滾動(dòng)屏幕temrinalColour 即將被描畫的字符顏色
terminalStart 需要保存起來的原因是 termainlBuffer 是一個(gè)環(huán)狀緩沖區(qū)。意思是當(dāng)緩沖區(qū)變滿時(shí),末尾地方會(huì)回滾覆蓋開始位置,這樣最后一個(gè)字符變成了第一個(gè)字符。因此我們需要將 terminalStart 往前推進(jìn),這樣我們知道我們已經(jīng)占滿它了。如何實(shí)現(xiàn)緩沖區(qū)檢測(cè):如果索引越界到緩沖區(qū)的末尾,就將索引指向緩沖區(qū)的開始位置。環(huán)狀緩沖區(qū)是一個(gè)比較常見的存儲(chǔ)大量數(shù)據(jù)的高明方法,往往這些數(shù)據(jù)的最近部分比較重要。它允許無限制的寫入,只保證最近一些特定數(shù)據(jù)有效。這個(gè)常常用于信號(hào)處理和數(shù)據(jù)壓縮算法。這樣的情況,可以允許我們存儲(chǔ) 128 行終端記錄,超過128行也不會(huì)有問題。如果不是這樣,當(dāng)超過第 128 行時(shí),我們需要把 127 行分別向前拷貝一次,這樣很浪費(fèi)時(shí)間。
顯示 Hellow world 插入到大小為5的循環(huán)緩沖區(qū)的示意圖。
環(huán)狀緩沖區(qū)是數(shù)據(jù)結(jié)構(gòu)一個(gè)例子。這是一個(gè)組織數(shù)據(jù)的思路,有時(shí)我們通過軟件實(shí)現(xiàn)這種思路。
之前已經(jīng)提到過 terminalColour 幾次了。你可以根據(jù)你的想法實(shí)現(xiàn)終端顏色,但這個(gè)文本終端有 16 個(gè)前景色和 16 個(gè)背景色(這里相當(dāng)于有 162 = 256 種組合)。 CGA 終端的顏色定義如下:
表格 1.1 – CGA 顏色編碼
序號(hào)顏色 (R, G, B)0黑 (0, 0, 0)1藍(lán) (0, 0, ?)2綠 (0, ?, 0)3青色 (0, ?, ?)4紅色 (?, 0, 0)5品紅 (?, 0, ?)6棕色 (?, ?, 0)7淺灰色 (?, ?, ?)8灰色 (?, ?, ?)9淡藍(lán)色 (?, ?, 1)10淡綠色 (?, 1, ?)11淡青色 (?, 1, 1)12淡紅色 (1, ?, ?)13淺品紅 (1, ?, 1)14** (1, 1, ?)15白色 (1, 1, 1)
我們將前景色保存到顏色的低字節(jié),背景色保存到顏色高字節(jié)。除了棕色,其他這些顏色遵循一種模式如二進(jìn)制的高位比特代表增加 ? 到每個(gè)組件,其他比特代表增加 ? 到各自組件。這樣很容易進(jìn)行 RGB 顏色轉(zhuǎn)換。
棕色作為替代色(黑**)既不吸引人也沒有什么用處。
我們需要一個(gè)方法從 TerminalColour 讀取顏色編碼的四個(gè)比特,第二用 16 比特等效參數(shù)調(diào)用 SetForeColour。嘗試你自己實(shí)現(xiàn)。如果你感覺麻煩或者還沒有完成屏幕系列課程,我們的實(shí)現(xiàn)如下:
.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、文本顯示
我們的終端第一個(gè)真正需要的方法是 TerminalDisplay,它用來把當(dāng)前的數(shù)據(jù)從 terminalBuffer拷貝到 terminalScreen 和實(shí)際的屏幕。如上所述,這個(gè)方法必須是最小開銷的**作,因?yàn)槲覀冃枰l繁調(diào)用它。它主要比較 terminalBuffer 與 terminalDisplay 的文本,第二只拷貝有差異的字節(jié)。請(qǐng)記住 terminalBuffer 是以環(huán)狀緩沖區(qū)運(yùn)行的,這種情況,就是從 terminalView 到 terminalStop,或者 128*48 個(gè)字符,要看哪個(gè)來的最快。如果我們遇到 terminalStop,我們將會(huì)假定在這之后的所有字符是 7f16 (ASCII 刪除字符),顏色為 0(黑色前景色和背景色)。
讓我們看看必須要做的事情:
加載 terminalView、terminalStop 和 terminalDisplay 的地址。對(duì)于每一行:對(duì)于每一列:如果 terminalView 不等于 terminalStop,根據(jù) terminalView 加載當(dāng)前字符和顏色否則加載 0x7f 和顏色 0從 terminalDisplay 加載當(dāng)前的字符如果字符和顏色相同,直接跳轉(zhuǎn)到第 10 步存儲(chǔ)字符和顏色到 terminalDisplay用 r0 作為背景色參數(shù)調(diào)用 TerminalColour用 r0 = 0x7f(ASCII 刪除字符,一個(gè)塊)、 r1 = x、r2 = y 調(diào)用 DrawCharacter用 r0 作為前景色參數(shù)調(diào)用 TerminalColour用 r0 = 字符、r1 = x、r2 = y 調(diào)用 DrawCharacter對(duì)位置參數(shù) terminalDisplay 累加 2如果 terminalView 不等于 terminalStop,terminalView 位置參數(shù)累加 2如果 terminalView 位置已經(jīng)是文件緩沖器的末尾,將它設(shè)置為緩沖區(qū)的開始位置x 坐標(biāo)增加 8y 坐標(biāo)增加 16
嘗試去自己實(shí)現(xiàn)吧。如果你遇到問題,我們的方案下面給出來了:
1、我這里的變量有點(diǎn)亂。為了方便起見,我用 taddr 存儲(chǔ) textBuffer 的末尾位置。
.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
2、從 yLoop 開始運(yùn)行。
mov y,#0
yLoop$:
2.1、
mov x,#0
xLoop$:
從 xLoop 開始運(yùn)行。
2.1.1、為了方便起見,我把字符和顏色同時(shí)加載到 char 變量了
teq view,stop
ldrneh char,[view]
2.1.2、這行是對(duì)上面一行的補(bǔ)充說明:讀取黑色的刪除字符
moveq char,#0x7f
2.1.3、為了簡(jiǎn)便我把字符和顏色同時(shí)加載到 col 里。
ldrh col,[screen]
2.1.4、 現(xiàn)在我用 teq 指令檢查是否有數(shù)據(jù)變化
teq col,char
beq xLoopContinue$
2.1.5、我可以容易的保存當(dāng)前值
strh char,[screen]
2.1.6、我用比特偏移指令 lsr 和 and 指令從切分 char 變量,將顏色放到 col 變量,字符放到 char 變量,第二再用比特偏移指令 lsr 獲取背景色后調(diào)用 TerminalColour 。
lsr col,char,#8
and char,#0x7f
lsr r0,col,#4
bl TerminalColour
2.1.7、寫入一個(gè)彩色的刪除字符
mov r0,#0x7f
mov r1,x
mov r2,y
bl DrawCharacter
2.1.8、用 and 指令獲取 col 變量的低半字節(jié),第二調(diào)用 TerminalColour
and r0,col,#0xf
bl TerminalColour
2.1.9、寫入我們需要的字符
mov r0,char
mov r1,x
mov r2,y
bl DrawCharacter
2.1.10、自增屏幕指針
xLoopContinue$:
add screen,#2
2.1.11、如果可能自增 view 指針
teq view,stop
addne view,#2
2.1.12、很容易檢測(cè) view 指針是否越界到緩沖區(qū)的末尾,因?yàn)榫彌_區(qū)的地址保存在 taddr 變量里
teq view,taddr
subeq view,#128*128*2
2.1.13、 如果還有字符需要顯示,我們就需要自增 x 變量第二到 xLoop 循環(huán)執(zhí)行
add x,#8
teq x,#1024
bne xLoop$
2.2、 如果還有更多的字符顯示我們就需要自增 y 變量,第二到 yLoop 循環(huán)執(zhí)行
add y,#16
teq y,#768
bne yLoop$
3、不要忘記最后清除變量
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、行打印
現(xiàn)在我有了自己 TerminalDisplay 方法,它可以自動(dòng)顯示 terminalBuffer 內(nèi)容到 terminalScreen,因此理論上我們可以畫出文本。但是實(shí)際上我們沒有任何基于字符顯示的例程。 第一快速容易上手的方法便是 TerminalClear, 它可以徹底清除終端。這個(gè)方法不用循環(huán)也很容易實(shí)現(xiàn)??梢試L試分析下面的方法應(yīng)該不難:
.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
現(xiàn)在我們需要構(gòu)造一個(gè)字符顯示的基礎(chǔ)方法:Print 函數(shù)。它將保存在 r0 的字符串和保存在 r1 的字符串長(zhǎng)度簡(jiǎn)單的寫到屏幕上。有一些特定字符需要特別的注意,這些特定的**作是確保 terminalView 是最新的。我們來分析一下需要做什么:
檢查字符串的長(zhǎng)度是否為 0,如果是就直接返回加載 terminalStop 和 terminalView計(jì)算出 terminalStop 的 x 坐標(biāo)對(duì)每一個(gè)字符的**作:檢查字符是否為新起一行如果是的話,自增 bufferStop 到行末,同時(shí)寫入黑色刪除字符否則拷貝當(dāng)前 terminalColour 的字符檢查是否在行末如果是,檢查從 terminalView 到 terminalStop 之間的字符數(shù)是否大于一屏如果是,terminalView 自增一行檢查 terminalView 是否為緩沖區(qū)的末尾,如果是的話將其替換為緩沖區(qū)的起始位置檢查 terminalStop 是否為緩沖區(qū)的末尾,如果是的話將其替換為緩沖區(qū)的起始位置檢查 terminalStop 是否等于 terminalStart, 如果是的話 terminalStart 自增一行。檢查 terminalStart 是否為緩沖區(qū)的末尾,如果是的話將其替換為緩沖區(qū)的起始位置存取 terminalStop 和 terminalView
試一下自己去實(shí)現(xiàn)。我們的方案提供如下:
1、這個(gè)是 Print 函數(shù)開始快速檢查字符串為0的代碼
.globl Print
Print:
teq r1,#0
moveq pc,lr
2、這里我做了很多配置。 bufferStart 代表 terminalStart, bufferStop 代表terminalStop, view 代表 terminalView,taddr 代表 terminalBuffer 的末尾地址。
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
3、和通常一樣,巧妙的對(duì)齊技巧讓許多事情更容易。由于需要對(duì)齊 terminalBuffer,每個(gè)字符的 x 坐標(biāo)需要 8 位要除以 2。
and x,bufferStop,#0xfe
lsr x,#1
4.1、我們需要檢查新行
charLoop$:
ldrb char,[string]
and char,#0x7f
teq char,#’n’
bne charNormal$
4.2、循環(huán)執(zhí)行值到行末寫入 0x7f;黑色刪除字符
mov r0,#0x7f
clearLine$:
strh r0,[bufferStop]
add bufferStop,#2
add x,#1
teq x,#128 blt clearLine$
b charLoopContinue$
4.3、存儲(chǔ)字符串的當(dāng)前字符和 terminalBuffer 末尾的 terminalColour 第二將它和 x 變量自增
charNormal$:
strb char,[bufferStop]
ldr r0,=terminalColour
ldrb r0,[r0]
strb r0,[bufferStop,#1]
add bufferStop,#2
add x,#1
4.4、檢查 x 是否為行末;128
charLoopContinue$:
cmp x,#128
blt noScroll$
4.5、設(shè)置 x 為 0 第二檢查我們是否已經(jīng)顯示超過 1 屏。請(qǐng)記住,我們是用的循環(huán)緩沖區(qū),因此如果 bufferStop 和 view 之前的差是負(fù)值,我們實(shí)際上是環(huán)繞了緩沖區(qū)。
mov x,#0
subs r0,bufferStop,view
addlt r0,#128*128*2
cmp r0,#128*(768/16)*2
4.6、增加一行字節(jié)到 view 的地址
addge view,#128*2
4.7、 如果 view 地址是緩沖區(qū)的末尾,我們就從它上面減去緩沖區(qū)的長(zhǎng)度,讓其指向開始位置。我會(huì)在開始的時(shí)候設(shè)置 taddr 為緩沖區(qū)的末尾地址。
teq view,taddr
subeq view,taddr,#128*128*2
4.8、如果 stop 的地址在緩沖區(qū)末尾,我們就從它上面減去緩沖區(qū)的長(zhǎng)度,讓其指向開始位置。我會(huì)在開始的時(shí)候設(shè)置 taddr 為緩沖區(qū)的末尾地址。
noScroll$:
teq bufferStop,taddr
subeq bufferStop,taddr,#128*128*2
4.9、檢查 bufferStop 是否等于 bufferStart。 如果等于增加一行到 bufferStart。
teq bufferStop,bufferStart
addeq bufferStart,#128*2
4.10、如果 start 的地址在緩沖區(qū)的末尾,我們就從它上面減去緩沖區(qū)的長(zhǎng)度,讓其指向開始位置。我會(huì)在開始的時(shí)候設(shè)置 taddr 為緩沖區(qū)的末尾地址。
teq bufferStart,taddr
subeq bufferStart,taddr,#128*128*2
循環(huán)執(zhí)行知道字符串結(jié)束
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
這個(gè)方法允許我們打印任意字符到屏幕。然而我們用了顏色變量,但實(shí)際上沒有設(shè)置它。一般終端用特性的組合字符去行修改顏色。如 ASCII 轉(zhuǎn)義(1b16)后面跟著一個(gè) 0 – f 的 16 進(jìn)制的數(shù),就可以設(shè)置前景色為 CGA 顏色號(hào)。如果你自己想嘗試實(shí)現(xiàn);在下載頁面有一個(gè)我的詳細(xì)的例子。
4、標(biāo)志輸入
現(xiàn)在我們有一個(gè)可以打印和顯示文本的輸出終端。這僅僅是說對(duì)了一半,我們需要輸入。我們想實(shí)現(xiàn)一個(gè)方法:ReadLine,可以保存文件的一行文本,文本位置由 r0 給出,最大的長(zhǎng)度由 r1 給出,返回 r0 里面的字符串長(zhǎng)度。棘手的是用戶輸出字符的時(shí)候要回顯功能,同時(shí)想要退格鍵的刪除功能和命令回車執(zhí)行功能。它們還需要一個(gè)閃爍的下劃線代表計(jì)算機(jī)需要輸入。這些完全合理的要求讓構(gòu)造這個(gè)方法更具有挑戰(zhàn)性。有一個(gè)方法完成這些需求就是存儲(chǔ)用戶輸入的文本和文件大小到內(nèi)存的某個(gè)地方。第二當(dāng)調(diào)用 ReadLine 的時(shí)候,移動(dòng) terminalStop 的地址到它開始的地方第二調(diào)用 Print。也就是說我們只需要確保在內(nèi)存維護(hù)一個(gè)字符串,第二構(gòu)造一個(gè)我們自己的打印函數(shù)。
按照慣例,許多編程語言中,任意程序可以訪問 stdin 和 stdin,它們可以連接到終端的輸入和輸出流。在圖形程序其實(shí)也可以進(jìn)行同樣**作,但實(shí)際幾乎不用。
讓我們看看 ReadLine 做了哪些事情:
如果字符串可保存的最大長(zhǎng)度為 0,直接返回檢索 terminalStop 和 terminalStop 的當(dāng)前值如果字符串的最大長(zhǎng)度大約緩沖區(qū)的一半,就設(shè)置大小為緩沖區(qū)的一半從最大長(zhǎng)度里面減去 1 來確保輸入的閃爍字符或結(jié)束符向字符串寫入一個(gè)下劃線寫入一個(gè) terminalView 和 terminalStop 的地址到內(nèi)存調(diào)用 Print 打印當(dāng)前字符串調(diào)用 TerminalDisplay調(diào)用 KeyboardUpdate調(diào)用 KeyboardGetChar如果是一個(gè)新行直接跳轉(zhuǎn)到第 16 步如果是一個(gè)退格鍵,將字符串長(zhǎng)度減 1(如果其大于 0)如果是一個(gè)普通字符,將它寫入字符串(字符串大小確保小于最大值)如果字符串是以下劃線結(jié)束,寫入一個(gè)空格,否則寫入下劃線跳轉(zhuǎn)到第 6 步字符串的末尾寫入一個(gè)新行字符調(diào)用 Print 和 TerminalDisplay用結(jié)束符替換新行返回字符串的長(zhǎng)度
為了方便讀者理解,第二第二自己去實(shí)現(xiàn),我們的實(shí)現(xiàn)提供如下:
快速處理長(zhǎng)度為 0 的情況
.globl ReadLine
ReadLine:
teq r1,#0
moveq r0,#0
moveq pc,lr
2、考慮到常見的場(chǎng)景,我們初期做了很多初始化動(dòng)作。input 代表 terminalStop 的值,view 代表 terminalView。Length 默認(rèn)為 0。
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
3、我們必須檢查異常大的讀**作,我們不能處理超過 terminalBuffer 大小的輸入(理論上可行,但是 terminalStart 移動(dòng)越過存儲(chǔ)的 terminalStop`,會(huì)有很多問題)。
cmp maxLength,#128*64
movhi maxLength,#128*64
4、由于用戶需要一個(gè)閃爍的光標(biāo),我們需要一個(gè)備用字符在理想狀況在這個(gè)字符串后面放一個(gè)結(jié)束符。
sub maxLength,#1
5、寫入一個(gè)下劃線讓用戶知道我們可以輸入了。
mov r0,#’_’
strb r0,[string,length]
6、保存 terminalStop 和 terminalView。這個(gè)對(duì)重置一個(gè)終端很重要,它會(huì)修改這些變量。嚴(yán)格講也可以修改 terminalStart,但是不可逆。
readLoop$:
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
7、寫入當(dāng)前的輸入。由于下劃線因此字符串長(zhǎng)度加 1
mov r0,string
mov r1,length
add r1,#1
bl Print
8、拷貝下一個(gè)文本到屏幕
bl TerminalDisplay
9、獲取最近一次鍵盤輸入
bl KeyboardUpdate
10、檢索鍵盤輸入鍵值
bl KeyboardGetChar
11、如果我們有一個(gè)回車鍵,循環(huán)中斷。如果有結(jié)束符和一個(gè)退格鍵也會(huì)同樣跳出循環(huán)。
teq r0,#’n’
beq readLoopBreak$
teq r0,#0
beq cursor$
teq r0,#’b’
bne standard$
12、從 length 里面刪除一個(gè)字符
delete$:
cmp length,#0
subgt length,#1
b cursor$
13、寫回一個(gè)普通字符
standard$:
cmp length,maxLength
bge cursor$
strb r0,[string,length]
add length,#1
14、加載最近的一個(gè)字符,如果不是下劃線則修改為下?lián)Q線,如果是則修改為空格
cursor$:
ldrb r0,[string,length]
teq r0,#’_’
moveq r0,#’ ‘
movne r0,#’_’
strb r0,[string,length]
15、循環(huán)執(zhí)行值到用戶輸入按下
b readLoop$
readLoopBreak$:
16、在字符串的結(jié)尾處存入一個(gè)新行字符
mov r0,#’n’
strb r0,[string,length]
17、重置 terminalView 和 terminalStop 第二調(diào)用 Print 和 TerminalDisplay 顯示最終的輸入
str input,[taddr,#terminalStop-terminalStart]
str view,[taddr,#terminalView-terminalStart]
mov r0,string
mov r1,length
add r1,#1
bl Print
bl TerminalDisplay
18、寫入一個(gè)結(jié)束符
mov r0,#0
strb r0,[string,length]
19、返回長(zhǎng)度
mov r0,length
pop {r4,r5,r6,r7,r8,r9,pc}
.unreq string
.unreq maxLength
.unreq input
.unreq taddr
.unreq length
.unreq view
5、終端:機(jī)器進(jìn)化
現(xiàn)在我們理論用終端和用戶可以交互了。最顯而易見的事情就是拿去測(cè)試了!刪除 main.s 里 bl U**Initialise 后面的代碼后如下:
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
這塊代碼集成了一個(gè)簡(jiǎn)易的命令行**作系統(tǒng)。支持命令:echo、reset、ok 和 cls。echo 拷貝任意文本到終端,reset 命令會(huì)在系統(tǒng)出現(xiàn)問題的是復(fù)位**作系統(tǒng),ok 有兩個(gè)功能:設(shè)置 OK 燈亮滅,最后 cls 調(diào)用 TerminalClear 清空終端。
試試樹莓派的代碼吧。如果遇到問題,請(qǐng)參照問題集錦頁面吧。
如果運(yùn)行正常,祝賀你完成了一個(gè)**作系統(tǒng)基本終端和輸入系列的課程。很遺憾這個(gè)教程先講到這里,但是我希望將來能制作更多教程。有問題請(qǐng)反饋至 awc32@cam.ac.uk 。
你已經(jīng)在建立了一個(gè)簡(jiǎn)易的終端**作系統(tǒng)。我們的代碼在 commandTable 構(gòu)造了一個(gè)可用的命令表格。每個(gè)表格的入口是一個(gè)整型數(shù)字,用來表示字符串的地址,和一個(gè)整型數(shù)字表格代碼的執(zhí)行入口。 最后一個(gè)入口是 為 0 的 commandStringEnd。嘗試實(shí)現(xiàn)你自己的命令,可以參照已有的函數(shù),建立一個(gè)新的。函數(shù)的參數(shù) r0 是用戶輸入的命令地址,r1 是其長(zhǎng)度。你可以用這個(gè)傳遞你輸入值到你的命令。也許你有一個(gè)計(jì)算器程序,或許是一個(gè)繪圖程序或國(guó)際象棋。不管你的什么點(diǎn)子,讓它跑起來!
via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input02.html
作者: Alex Chadwick 選題: lujun9972 譯者: guevaraya 校對(duì): wxy
本文由 LCTT 原創(chuàng)編譯, Linux** 榮譽(yù)推出
點(diǎn)擊“了解更多”可訪問文內(nèi)鏈接
拓展知識(shí):
索引越界
里面都是百度的軟件,這吧不是網(wǎng)友開的,都是百度工作人員發(fā)百度專用的軟件上去。。
比如我現(xiàn)在就用的:
百度貼把伴侶(實(shí)際上就是百度專用的瀏覽器。)
百度貼吧數(shù)據(jù)統(tǒng)計(jì)工具(就是把貼吧的影響力排名的軟件)
。。。。。。。。。。
還有很多,去那個(gè)吧里看看吧/。
原創(chuàng)文章,作者:九賢生活小編,如若轉(zhuǎn)載,請(qǐng)注明出處:http://xiesong.cn/36564.html