|
| 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 | +``` |
0 commit comments