Skip to content

Commit f5c87e9

Browse files
committed
workflow: chapter 17 磁盘读写
1 parent bc944df commit f5c87e9

File tree

2 files changed

+375
-1
lines changed

2 files changed

+375
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
# 使用BIOS进行键盘输入和磁盘读写
2+
键盘输入是最基本的输入, 磁盘最最常用的存储设备. BIOS为这两种外设的I/O提供了最基本的中断例程
3+
4+
## int9中断例程对键盘输入的处理
5+
键盘输入将引发9号中断, BIOS提供了int9中断例程. CPU在9号中断发生后, 执行int9中断例程, 从60端口读出扫描码, 并将其转化为相应的ASCII码或状态信息, 存储在内存的指定空间(键盘缓冲区或状态字节)中
6+
7+
## 使用int 16h中断例程读取键盘缓冲区
8+
int 16h中断例程的0号功能:
9+
1. 检测键盘缓冲区中是否有数据
10+
2. 没有则继续做第1步
11+
3. 读取缓冲区第一个字单元中的键盘输入
12+
4. 将读取的扫描码送入ah, ASCII码送入al
13+
5. 将已读取的键盘输入从缓冲区中删除
14+
15+
BIOS的int 9中断例程和int 16h中断例程是一对相互配合的程序. int 9中断例程向键盘缓冲区中写入, int 16h中断例程从缓冲区中读出.
16+
17+
18+
编程, 接收用户的键盘输入, 输入"r","g","b", 将屏幕上的字符设置为红色,绿色, 蓝色;
19+
20+
```s
21+
assume cs:code
22+
code segment
23+
start: mov ah,0
24+
int 16h
25+
26+
mov ah,1
27+
cmp al,'r'
28+
je red
29+
cmp al,'g'
30+
je green
31+
cmp al,'b'
32+
je blue
33+
jmp short sret
34+
35+
red: shl ah, 1
36+
green: shl ah, 1
37+
38+
blue: mov bx,0b800h
39+
mov es,bx
40+
mov bx,1
41+
mov cx,2000
42+
s: and byte ptr es:[bx], 11111000b
43+
or es:[bx],ah
44+
add bx,2
45+
loop s
46+
47+
sret: mov ax,4c00h
48+
int 21h
49+
50+
code ends
51+
end start
52+
```
53+
54+
## 字符串输入
55+
最基本的字符串输入程序:
56+
1. 在输入的同事需要显示这个字符串
57+
2. 一般在输入回车符后,字符串输入结束
58+
3. 能够删除已经输入的字符
59+
60+
分析:
61+
1. 可以用栈的方式来管理字符串的存储空间
62+
2. 在输入回车符后,可以在字符串中加入0, 表示字符串结束
63+
3. 每次有新的字符输入和删除的时候, 应该重新显示字符串
64+
65+
过程:
66+
1. 调用int 16h读取键盘输入
67+
2. 如果是字符, 进入字符栈, 显示字符栈中所有字符; 继续执行1
68+
3. 如果是退格键, 从字符栈中弹出一个字符, 显示所有字符; 继续执行1
69+
4. 如果是enter键, 向字符栈中压入0, 返回
70+
71+
子程序: 字符栈的入栈,出栈,显示
72+
参数: (ah)=功能号, 0表示入栈,1表示出栈,2表示显示
73+
ds:si指向字符栈空间
74+
0: (al)=入栈字符
75+
1: (al)=返回字符
76+
2: (dh),(dl)=字符串在屏幕上显示的行,列位置
77+
78+
```s
79+
charstack: jmp short charstart
80+
81+
table dw charpush,charpop,charshow
82+
top dw 0 ;栈顶
83+
84+
charstart: push bx
85+
push dx
86+
push di
87+
push es
88+
89+
cmp ah,2
90+
ja sret
91+
mov bl,ah
92+
mov bh,0
93+
add bx,bx
94+
jmp word ptr table[bx]
95+
96+
charpush: mov bx,top
97+
mov [si][bx],al
98+
inc top
99+
jmp sret
100+
101+
charpop: cmp top 0
102+
je sret
103+
dec top
104+
mov bx,top
105+
mov al,[si][bx]
106+
jmp sret
107+
108+
charshow: mov bx,0b800h
109+
mov es,bx
110+
mov al,160
111+
mov ah,0
112+
mul dh
113+
mov di,ax
114+
add dl,dl
115+
mov dh,0
116+
add di,dx
117+
118+
mov bx,0
119+
120+
charshows: cmp bx,top
121+
jne noempty
122+
mov byte ptr es:[di], ' '
123+
jmp sret
124+
noempty: mov al,[si][bx]
125+
mov es:[di],al
126+
mov byte ptr es:[di+2], ' '
127+
inc bx
128+
add di,2
129+
jmp charshows
130+
131+
sret: pop es
132+
pop di
133+
pop dx
134+
pop bx
135+
ret
136+
```
137+
138+
接收字符串输入的子程序:
139+
```s
140+
141+
getstr: push ax
142+
143+
getstrs: mov ah,0
144+
int 16h
145+
cmp al,20h
146+
jb nochar ;ASCII码小于20h, 说明不是字符
147+
mov ah,0
148+
call charstack ;字符入栈
149+
mov ah,2
150+
call charstack ;显示栈中的字符
151+
jmp getstrs
152+
153+
nochar: cmp ah,0eh ;退格键的扫描码
154+
je backspace
155+
cmp ah,1ch ;enter键的扫描码
156+
je enter
157+
jmp getstrs
158+
159+
backspace: mov ah,1
160+
call charstack ;字符出栈
161+
mov ah,2
162+
call charstack ;显示栈中的字符
163+
jmp getstrs
164+
165+
enter: mov ah,0
166+
mov al,0
167+
call charstack ;0入栈
168+
mov ah,2
169+
call charstack ;显示栈中的字符
170+
pop ax
171+
ret
172+
```
173+
## 应用int 13h中断例程对磁盘进行读写
174+
175+
以3.5英寸软盘为例, 分为上下两面, 每面有80个磁道, 每个磁道又分为18个扇区, 每个扇区的大小为512个字节. `2面*80磁道*18扇区*512字节=1440KB`.
176+
177+
只能以`扇区`为单位对磁盘进行读写. 在读写扇区的时候, 要给出面号(0开始),磁道号(0开始),扇区号(1开始).
178+
179+
BIOS提供是访问磁盘的中断例程是int 13h. 读取0面0道1扇区的内容到0:200的程序如下:
180+
181+
入口参数:
182+
(ah)=int 13h的功能号(2 表示读扇区)
183+
(al)=读取的扇区数
184+
(ch)=磁道号
185+
(cl)=扇区号
186+
(dh)=磁头号(对于软盘即面号,因为一个面用一个磁头来读写)
187+
(dl)=驱动器号 软驱从0开始, 0:软驱A, 1:软驱B
188+
硬盘从80h开始, 80h:硬盘C, 81h: 硬盘D
189+
190+
ex:bx指向接收从扇区读入数据的内存区
191+
192+
返回参数:
193+
操作成功: (ah)=0, (al)=读入的扇区数
194+
操作失败: (ah)=出错代码
195+
```s
196+
mov ax,0
197+
mov es,ax
198+
mov bx,200h
199+
200+
mov al,1 ;读取的扇区数
201+
mov ch,0 ;磁道号
202+
mov cl,1 ;扇区号
203+
mov dl,0 ;驱动器号
204+
mov dh,0 ;磁头号(面号)
205+
mov ah,2 ;int 13h的功能号
206+
int 13h
207+
```
208+
209+
将0:200中的内容写入0面0道1扇区
210+
211+
入口参数:
212+
(ah)=int 13h的功能号(3 表示写扇区)
213+
(al)=写入的扇区数
214+
(ch)=磁道号
215+
(cl)=扇区号
216+
(dh)=磁头号(面)
217+
(dl)=驱动器号
218+
ex:bx指向将写入的磁盘的数据
219+
220+
返回参数:
221+
操作成功: (ah)=0, (al)=写入的扇区数
222+
操作失败: (ah)=出错代码
223+
```s
224+
mov ax,0
225+
mov ex,ax
226+
mov bx,200h
227+
228+
mov al,1
229+
mov ch,0
230+
mov cl,1
231+
mov dh,0
232+
mov dl,0
233+
234+
mov ah,3
235+
int 13h
236+
```
237+
238+
编程: 将当前屏幕的内容保存在磁盘上
239+
240+
分析: 1屏的内容占4000个字节, 需要8个扇区, 用0面0道的1-8扇区存储显存中的内容
241+
242+
```s
243+
assume cs:code
244+
code segment
245+
start:
246+
mov ax,0b800h
247+
mov ex,ax
248+
mov bx,0
249+
250+
mov al,8
251+
mov ch,0
252+
mov cl,1
253+
mov dh,0
254+
mov dl,0
255+
256+
mov ah,3
257+
int 13h
258+
259+
mov ax,4c00h
260+
int 21h
261+
code ends
262+
end start
263+
```
264+
## 实验17 编写包含多个功能子程序的中断例程
265+
266+
用面号, 磁道号, 扇区号来访问磁盘不太方便. 可以考虑对位于不同磁道,面上的所有扇区进行统一编号. 编号从0开始,一直到2879,我们称这个编号为逻辑扇区编号
267+
268+
`逻辑扇区编号 = (面号*80 + 磁道号)*18 + 扇区号-1`
269+
270+
根据逻辑扇区号算出物理编号:
271+
- int(): 描述性运算符, 取商
272+
- rem(): 描述性运算符, 取余数
273+
- 逻辑扇区号=(面号*80 + 磁道号)*18 + 扇区号-1
274+
- 面号=int(逻辑扇区号/1440)
275+
- 磁道号=int(rem(逻辑扇区号/1440)/18)
276+
- 扇区号=rem(rem(逻辑扇区号/1440)/18)+1
277+
278+
安装一个新的int 7ch中断例程, 实现通过逻辑扇区号对软盘进行读写
279+
280+
参数说明:
281+
1. 用ah寄存器传递功能号: 0表示读, 1表示写
282+
2. 用dx寄存器传递要读写的扇区的逻辑扇区号
283+
3. 用es:bx指向存储读出数据或写入数据的内存区
284+
285+
分析: 用逻辑扇区计算出面号, 磁道号, 扇区号后, 调用int 13h中断例程进行实际的读写
286+
(ah)=int 13h的功能号(3 表示写扇区)
287+
(al)=写入的扇区数
288+
(ch)=磁道号
289+
(cl)=扇区号
290+
(dh)=磁头号(面)
291+
(dl)=驱动器号
292+
293+
子程序:
294+
```s
295+
assume cs:code
296+
297+
stack segment
298+
db 128 dup (0)
299+
stack ends
300+
code segment
301+
302+
start: mov ax,stack
303+
mov ss,ax
304+
mov sp,128
305+
306+
push cs
307+
pop ds
308+
309+
mov ax,0
310+
mov es,ax
311+
312+
mov si,offset logical_sector ;设置ds:si指向源地址
313+
mov di,204h ;设置es:di指向目标地址
314+
mov cx,offset logical_sector_end-offset logical_sector ;设置cx为传输长度
315+
cld ;设置传输方向为正
316+
rep movsb
317+
318+
push es:[7ch*4]
319+
pop es:[200h]
320+
push es:[7ch*4+2]
321+
pop es:[202h] ;暂存BIOS原有的int7c中断例程地址
322+
323+
cli
324+
mov word ptr es:[7ch*4],204h
325+
mov word ptr es:[7ch*4+2],0 ;设置新的int7c中断例程的地址0:204h
326+
sti
327+
328+
mov ax,4c00h
329+
int 21h
330+
331+
logical_sector:
332+
jmp short run
333+
store db dup(16) 0
334+
table db 2,3 ;偏移地址表
335+
336+
run:
337+
cmp ah,1
338+
ja ok ;>1则退出
339+
340+
push ah
341+
push dx
342+
push bx
343+
mov ax,dx
344+
mov dx,0
345+
mov bx,1440
346+
div bx ;(ax)=面号, (dx)=余数
347+
mov store[0], al
348+
mov ax,dx
349+
mov bl,18
350+
div bl ;(al)=磁道号, (ah)=余数
351+
mov store[1], al
352+
add ah,1 ;(ah)=扇区号
353+
mov store[2], ah
354+
355+
mov al,1
356+
mov ch,store[1]
357+
mov cl,store[2]
358+
mov dh,store[0]
359+
mov dl,0
360+
361+
mov bh,0
362+
mov bl,ah ;将ah的值转移到bx中
363+
mov ah,table[bx] ;设置
364+
pop bx
365+
int 13h
366+
367+
pop dx
368+
pop ah
369+
ok: iret
370+
logical_sector_end: nop
371+
code ends
372+
end
373+
```

readme.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
13. [int指令](./doc/13-int指令)
2525
14. [端口](./doc/14-端口)
2626
15. [外中断](./doc/15-外中断)
27-
16. [直接定址表](./doc/16-直接定址表)
27+
16. [直接定址表](./doc/16-直接定址表)
28+
17. [使用BIOS进行键盘输入和磁盘读写](./doc/17-使用BIOS进行键盘输入和磁盘读写)

0 commit comments

Comments
 (0)