《80X86汇编语言程序设计教程》十八 异常处理实例

时间:2022-09-01 07:59:42

1、  理论知识参考"《80X86汇编语言程序设计教程》十六 80386的中断和异常",这里演示异常处理,包括除法出错故障处理、溢出陷阱处理、段不存在故障处理、堆栈段出错故障处理、通用保护故障处理。逻辑功能,在屏幕上显示提示信息,按照用户选择模拟指定异常。

 

 

2、  源代码

   “386scd.asm”不再贴上来。参考"《80X86汇编语言程序设计教程》十三 任务内无特权级变换转移实例",演示代码如下:

 

  1 ;DosTest.Asm
2 ;演示异常处理,包括除法出错故障处理、溢出陷阱处理、段不存在故障处理、堆栈段出错故障处理、通用保护故障处理
3 ;逻辑功能:在屏幕上显示提示信息,按照用户选择模拟指定异常
4
5
6 include 386scd.asm ;文件386.scd含有有关结构、宏指令和符号常量定义
7 .386p
8
9 ;------------------------------------------
10 ;全局描述符表GDT
11 GDTSeg segment para use16
12 GDT label byte
13 ;空描述符
14 dummy DESCRIPTOR<>
15
16 ;规范数据段描述符,存在的可读写数据段
17 Normal DESCRIPTOR<0ffffh,0,0,ATDW,0>
18 Normal_Sel = Normal - GDT
19 EFFGDT label byte
20
21 ;临时任务代码段描述符(16位段,DPL = 0,RPL = 0)
22 TempCode DESCRIPTOR<0ffffh,TempCodeSeg,,ATCE,>
23 TempCode_Sel = TempCode - GDT
24
25 ;演示任务代码段描述符(16位段,DPL = 0,RPL = 0)
26 DemoCode DESCRIPTOR<DemoCodeLen,DemoCodeSeg,,ATCE,>
27 DemoCode_Sel = DemoCode - GDT
28
29 ;演示任务LDT段描述符(16位段,DPL = 0,RPL = 0)
30 DemoLDT DESCRIPTOR<DemoLDTLen - 1,DemoLDTSeg,,ATLDT,>
31 DemoLDT_Sel = DemoLDT - GDT
32
33 ;演示任务状态段TSS描述符(16位段,DPL = 0,RPL = 0)
34 DemoTSS DESCRIPTOR<DemoTSSLen -1,DemoTSSSeg,,AT386TSS,>
35 DemoTSS_Sel = DemoTSS - GDT
36
37 ;缓冲区数据段描述符(16位段,DPL = 0,RPL = 0)
38 XBuffer DESCRIPTOR<BufferLen -1,BufferSeg,,ATDW,>
39 XBuffer_Sel = XBuffer - GDT
40
41 ;读键盘任务LDT描述符(16位段,DPL = 0,RPL = 0)
42 GKeyLDT DESCRIPTOR<GKeyLDTLen -1,GKeyLDTSeg,,ATLDT,>
43 GKeyLDT_Sel = GKeyLDT - GDT
44
45 ;读键盘任务TSS描述符(16位段,DPL = 0,RPL = 0)
46 GKeyTSS DESCRIPTOR<GKeyTSSLen -1,GKeyTSSSeg,,AT386TSS,>
47 GKeyTSS_Sel = GKeyTSS - GDT
48
49 ;视频缓冲区段描述符(16位段,DPL = 0,RPL = 0)
50 VideoBuff DESCRIPTOR<80 * 25 * 2 - 1,0b800h,,ATDW,0>
51 VideoBuff_Sel = VideoBuff - GDT
52
53 ;显示陷阱处理程序代码段描述符(16位段,DPL = 0,RPL = 0)
54 EchoCode DESCRIPTOR<EchoCodeLen,EchoCodeSeg,,ATCE,>
55 EchoCode_Sel = EchoCode - GDT
56
57 ;显示出错码过程代码段描述符(16位段,DPL = 0,RPL = 0)
58 SubCode DESCRIPTOR<SubCodeLen,SubCodeSeg,,ATCE,>
59 SubCode_Sel = SubCode - GDT
60
61 ;其它中断或异常处理程序代码段描述符(16位段,DPL = 0,RPL = 0)
62 OtherCode DESCRIPTOR<OtherCodeLen,OtherCodeSeg,,ATCE,>
63 OtherCode_Sel = OtherCode - GDT
64
65 ;GDT中需要初始化基地址的描述符个数
66 GDTNum = ($ - EFFGDT)/(size DESCRIPTOR)
67
68 ;GDT段长度
69 GDTLen = $ - GDT
70 GDTSeg ends
71
72 ;------------------------------------------
73 ;中断描述符表IDT
74 IDTSeg segment para use16
75 IDT label byte
76
77 ;00H号陷阱门描述符(对应除法出错故障)
78 INT00 GATE<DIVBegin,Divide_Sel,0,AT386TGAT,0>
79
80 ;从01H~03H的3个陷阱门描述符
81 rept 3
82 GATE<OtherBegin,OtherCode_Sel,0,AT386TGAT,0>
83 endm
84
85 ;04H号陷阱门描述符(对应溢出陷阱)
86 INT04 GATE<OFBegin,OF_Sel,0,AT386TGAT,0>
87
88 ;从05H~0AH的6个陷阱门描述符
89 rept 6
90 GATE<OtherBegin,OtherCode_Sel,0,AT386TGAT,0>
91 endm
92
93 ;0BH号陷阱门描述符(对应不存在故障)
94 INT0B GATE<SNPBegin,SNP_Sel,0,AT386TGAT,0>
95
96 ;0CH号陷阱门描述符(对应堆栈段故障)
97 INT0C GATE<SSEBegin,SSE_Sel,0,AT386TGAT,0>
98
99 ;0DH号陷阱门描述符(对应通用保护故障)
100 INT0D GATE<GPBegin,GP_Sel,0,AT386TGAT,0>
101
102 ;从0E~EDH的240个陷阱门描述符
103 rept 254 - 14
104 GATE<OtherBegin,OtherCode_Sel,0,AT386TGAT,0>
105 endm
106
107 ;0FEH号陷阱门描述符(对应显示中断处理程序)
108 INTFE GATE<EchoBegin,EchoCode_Sel,0,AT386TGAT,0>
109
110 ;0FFH号任务门描述符(对应读键盘中断处理任务)
111 INTFF GATE<,GKeyTSS_Sel,0,ATTASKGAT,>
112
113 ;中断描述符表长度
114 IDTLen = $ - IDT
115 IDTSeg ends
116
117 ;------------------------------------------
118 ;读键盘任务LDT表段
119 GKeyLDTSeg segment para use16
120 GLDT label byte
121
122 ;代码段描述符
123 GKeyCode DESCRIPTOR<0ffffh,GKeyCodeSeg,,ATCE,>
124 GKeyCode_Sel = (GKeyCode - GLDT) + TIL
125
126 ;堆栈段描述符
127 GKeyStack DESCRIPTOR<GKeyStackLen - 1,GKeyStackSeg,,ATDWA,>
128 GKeyStack_Sel = (GKeyStack - GLDT) + TIL
129
130 ;该LDT中需要初始化基地址描述符个数
131 GKeyLDTNum = ($ - GLDT)/(size DESCRIPTOR)
132 GKeyLDTLen = $ - GLDT
133 GKeyLDTSeg ends
134
135 ;------------------------------------------
136 ;读键盘任务TSS表段
137 GKeyTSSSeg segment para use16
138 KTSS TASKSS<>
139 db 0ffh ;IO许可位结束标志
140 GKeyTSSLen = $ - GKeyTSSSeg
141 GKeyTSSSeg ends
142
143 ;------------------------------------------
144 ;读键盘任务堆栈段
145 GKeyStackSeg segment para use16
146 GKeyStackLen = 1024
147 db GKeyStackLen dup(0)
148 GKeyStackSeg ends
149
150 ;------------------------------------------
151 ;读键盘任务代码段
152 GKeyCodeSeg segment para use16
153 assume cs:GKeyCodeSeg,ds:RDataSeg,es:BufferSeg
154 GKeyBegin:
155 push   ds
156 push   es
157 push   fs
158 push   gs
159 mov ax,Normal_Sel
160 mov ss,ax ;准备转实模式
161 mov eax,cr0
162 and eax,0fffffffeh
163 mov cr0,eax ;转实模式
164 JUMP16 <seg GetKey>,<offset GetKey>
165 GetKey:
166 ;实模式下
167 mov ax,RDataSeg
168 mov ds,ax
169 mov ebp,esp ;保存esp到ebp
170 lss sp,dword ptr SPVar ;恢复实模式部分现场
171 lidt NORVIDTR
172 sti ;开中断
173 mov dx,offset Mess ;DS:DX=字符串缓冲区首址
174 mov ah,9 ;串以'$'为结束标志
175 int 21h ;显示提示信息
176 GetKey1:
177 mov ah,0 ;从键盘读一个字符,不回显
178 int 16h ;AL = ASCII码,AH = 扫描码
179 cmp al,1bh ;只有[ESC,0,4,B,C,D]有效
180 jz GetKey2
181 cmp al,'0'
182 jz GetKey2
183 cmp al,'4'
184 jz GetKey2
185 and al,11011111b ;小写转大写
186 cmp al,'B'
187 jb GetKey1
188 cmp al,'D'
189 ja GetKey1
190 GetKey2:
191 mov dl,al
192 mov ah,2 ;向标准输出设备写1字符
193 int 21h ;显示所按字符
194 mov ax,BufferSeg
195 mov es,ax
196 mov es:KeyASCII,dl ;保存ASCII码到缓冲区数据段
197 mov dx,offset FeedLine
198 mov ah,9
199 int 21h ;换行
200 cli ;准备返回保护模式
201 lidt fword ptr VIDTR
202 mov eax,cr0
203 or eax,1
204 mov cr0,eax ;返回保模式
205 JUMP16 <GKeyCode_Sel>,<offset GetKeyV>
206 GetKeyV:
207 ;在保护模式下
208 mov ax,GKeyStack_Sel
209 mov ss,ax
210 mov esp,ebp ;恢复ebp
211 pop gs
212 pop fs
213 pop es
214 pop ds ;恢复部分现场
215 iretd
216 jmp GKeyBegin ;切换任务时保存CS:EIP指向这条指令
217 GKeyCodeLen = $ - GKeyCodeSeg
218 GKeyCodeSeg ends
219
220 ;------------------------------------------
221 ;其它中断或异常处理程序代码段
222 OtherCodeSeg segment para use16
223 assume cs:OtherCodeSeg
224 OtherBegin:
225 mov si,offset MessOther
226 int 0feh ;显示提示信息
227 jmp $ ;进入无限循环
228 OtherCodeLen = $ - OtherCodeSeg
229 OtherCodeSeg ends
230
231 ;------------------------------------------
232 ;除法出错故障处理程序代码段
233 DIVCodeSeg segment para use16
234 assume cs:DIVCodeSeg
235 DIVBegin:
236 mov si,offset Mess0
237 mov di,0
238 int 0feh ;显示提示信息
239 shr ax,1 ;处理模拟的除法错误
240 iretd ;返回
241 DIVCodeLen = $ - DIVCodeSeg
242 DIVCodeSeg ends
243
244 ;------------------------------------------
245 ;溢出陷阱处理程序代码段
246 OFCodeSeg segment para use16
247 assume cs:OFCodeSeg
248 OFBegin:
249 mov si,offset Mess4
250 mov dl,0
251 int 0feh ;显示提示信息
252 iretd ;返回
253 OFCodeLen = $ - OFCodeSeg
254 OFCodeSeg ends
255
256 ;------------------------------------------
257 ;段不存在故障处理程序代码段
258 SNPCodeSeg segment para use16
259 assume cs:SNPCodeSeg
260 SNPBegin:
261 mov si,offset MessB
262 mov di,0
263 int 0feh ;显示提示信息
264 ;
265 pop eax ;弹出错误码
266 CALL16 SubCode_Sel,SubBegin;显示错误码
267 ;
268 ;按模拟的引起段不存在指令调整返回地址
269 pop eax
270 add eax,2
271 push eax
272 iretd
273 SNPCodeLen = $ - SNPCodeSeg
274 SNPCodeSeg ends
275
276 ;------------------------------------------
277 ;堆栈段故障处理程序代码段
278 SSECodeSeg segment para use16
279 assume cs:SSECodeSeg
280 SSEBegin:
281 mov si,offset MessC
282 mov di,0
283 int 0feh ;显示提示信息
284 ;
285 pop eax ;弹出错误码
286 CALL16 SubCode_Sel,SubBegin;显示错误码
287 ;
288 ;按模拟的引起堆栈段错误指令调整返回地址
289 pop eax
290 add eax,4
291 push eax
292 iretd
293 SSECodeLen = $ - SSEBegin
294 SSECodeSeg ends
295
296 ;------------------------------------------
297 ;通用保护故障处理程序代码段
298 GPCodeSeg segment para use16
299 assume cs:GPCodeSeg
300 GPBegin:
301 push   ebp
302 mov ebp,esp
303 push   eax
304 push   esi
305 push   edi ;保护现场
306 ;
307 mov si,offset MessD
308 mov di,0
309 int 0feh ;显示提示信息
310 ;
311 mov eax,[bp + 4] ;从堆栈中取出错误码
312 CALL16 SubCode_Sel,SubBegin;显示错误码
313 ;
314 pop edi ;恢复现场
315 pop esi
316 pop eax
317 add dword ptr[ebp + 8],2;按模拟的故障指令调整返回地址
318 pop ebp
319 add sp,4 ;废除堆栈中的出错码
320 iretd
321 GPCodeLen = $ - GPCodeSeg
322 GPCodeSeg ends
323
324 ;------------------------------------------
325 ;显示出错码过程代码段
326 SubCodeSeg segment para use16
327 assume cs:SubCodeSeg
328 SubBegin:
329 push    ax ;AX含出错码
330 push   cx
331 push   dx
332 push   si
333 push   di ;保护现场
334 mov si,offset ErrCode
335
336 ;把16位出错码转成4位十六进制数码的ASCII码并保存
337 mov dx,ax
338 mov cx,4 ;16位出错码,每次处理4位
339 SubR1:
340 rol dx,4 ;高4位转最低4位
341 mov al,dl
342 and al,0fh ;al清高4位留低4位
343 add al,30h ;转ASCII码
344 cmp al,'9'
345 jbe SubR2
346 add al,7
347 SubR2:
348 mov [si],al
349 inc si
350 loop SubR1
351 ;
352 mov si,offset ErrMess
353 mov di,80 * 2 ;在第二行首开始显示出错代码
354 int 0feh
355 pop di
356 pop si
357 pop dx ;恢复部分现场
358 pop cx
359 pop ax
360 retf
361 SubCodeLen = $ - SubCodeSeg
362 SubCodeSeg ends
363
364 ;------------------------------------------
365 ;实现显示的陷阱处理程序代码段
366 EchoCodeSeg segment para use16
367 assume cs:EchoCodeSeg
368 EchoBegin:
369 ;DS:SI指向显示信息串,ES:DI指向显示缓冲区
370 pushad ;保护现场
371 ;mov ax,DemoData_Sel
372 ;mov ds,ax
373 ;mov ax,VideoBuff_Sel
374 ;mov es,ax
375 cld
376 mov ah,7 ;属性
377 mov al,20h ;space
378 mov cx,80 ;一行80个字符
379 push   di
380 rep stosw ;清所在显示行
381 pop di
382 Echo1:
383 lodsb
384 or al,al ;判断串是否结束
385 jz Echo2
386 stosw ;显示指定信息串
387 jmp Echo1
388 Echo2:
389 popad ;恢复现场
390 iretd
391 EchoCodeLen = $ - EchoCodeSeg
392 EchoCodeSeg ends
393
394 ;------------------------------------------
395 ;缓冲区数据段
396 BufferSeg segment para use16
397 KeyASCII db ?
398 Buffer db 128 dup(?)
399 BufferLen = $ - BufferSeg
400 BufferSeg ends
401
402 ;------------------------------------------
403 ;演示任务LDT表段
404 DemoLDTSeg segment para use16
405 DLDT label byte
406
407 ;演示任务TSS段描述符
408 ToDemoTSS DESCRIPTOR<DemoTSSLen - 1,DemoTSSSeg,,ATDW,>
409 ToDemoTSS_Sel = (ToDemoTSS - DLDT) + TIL
410
411 ;0级堆栈段描述符(16位段,DPL = 0,RPL = 0)
412 DemoStack DESCRIPTOR<DemoStackLen - 1,DemoStackSeg,,ATDWA,>
413 DemoStack_Sel = (DemoStack - DLDT) + TIL
414
415 ;演示任务数据段描述符(16位段,DPL = 0,RPL = 0)
416 DemoData DESCRIPTOR<DemoDataLen - 1,DemoDataSeg,,ATDW,>
417 DemoData_Sel = (DemoData - DLDT) + TIL
418
419 ;除法出错故障处理程序代码段描述符
420 Divide DESCRIPTOR<DIVCodeLen - 1,DIVCodeSeg,,ATCE,>
421 Divide_Sel = (Divide - DLDT) + TIL
422
423 ;溢出陷阱处理程序代码段描述符
424 OverFlow DESCRIPTOR<OFCodeLen - 1,OFCodeSeg,,ATCE,>
425 OF_Sel = (OverFlow - DLDT) + TIL
426
427 ;段不存在故障处理程序代码段描述符
428 SNPCode DESCRIPTOR<SNPCodeLen - 1,SNPCodeSeg,,ATCE,>
429 SNP_Sel = (SNPCode - DLDT) + TIL
430
431 ;堆栈段出错故障处理程序代码段描述符
432 SSECode DESCRIPTOR<SSECodeLen - 1,SSECodeSeg,,ATCE,>
433 SSE_Sel = (SSECode - DLDT) + TIL
434
435 ;通用保护故障处理程序代码段描述符
436 GPCode DESCRIPTOR<GPCodeLen - 1,GPCodeSeg,,ATCE>
437 GP_Sel = (GPCode - DLDT) + TIL
438
439 ;为模拟段不存在故障而安排的数据段描述符
440 TestNPS DESCRIPTOR<0ffffh,0,,ATDW - 80H,>
441 TestNPS_Sel = (TestNPS - DLDT) + TIL
442
443 ;该LDT中需要初始化基地址的描述符个数
444 DemoLDTNum = ($ - DLDT)/(size DESCRIPTOR)
445
446 ;LDT字节长度
447 DemoLDTLen = $ - DLDT
448 DemoLDTSeg ends
449
450
451 ;------------------------------------------
452 ;演示任务任务状态段(TSS)
453 DemoTSSSeg segment para use16
454 DTSS TASKSS<>
455 db 0ffh ;IO许可位结束标志
456 DemoTSSLen = $ - DemoTSSSeg
457 DemoTSSSeg ends
458
459 ;------------------------------------------
460 ;演示任务堆栈
461 DemoStackSeg segment para use16
462 DemoStackLen = 1024
463 db DemoStackLen dup(0)
464 DemoStackSeg ends
465
466 ;------------------------------------------
467 ;演示任务数据段
468 DemoDataSeg segment para use16
469 Mess0 db 'Divide Error(Exception 0)',0
470 Mess4 db 'Overflow(Exception 4)',0
471 MessB db 'Segment Not Present(Exception 11)',0
472 MessC db 'Stack Segment(Exception 12)',0
473 MessD db 'General Protection(Exception 13)',0
474 MessOther db 'Other Execption',0
475 ErrMess db 'Error Code = '
476 ErrCode db 4 dup(0),'H',0
477 DemoDataLen = $ - DemoDataSeg
478 DemoDataSeg ends
479
480 ;------------------------------------------
481 ;演示任务的代码段
482 DemoCodeSeg segment para use16
483 assume cs:DemoCodeSeg
484 DemoBegin:
485 mov ax,DemoLDT_Sel
486 lldt ax ;装载演示任务IDTR
487 mov ax,DemoStack_Sel ;在演示任务LDT中,必须先装载IDTR
488 mov ss,ax ;置堆栈指针
489 mov esp,DemoStackLen
490 mov ax,ToDemoTSS_Sel ;把演示任务LDT选择子填入TSS
491 mov gs,ax
492 mov gs:DTSS.TRLDT,DemoLDT_Sel
493 ;装载TR
494 mov ax,DemoTSS_Sel
495 ltr ax
496 ;装载其它数据段寄存器
497 mov ax,DemoData_Sel
498 mov ds,ax
499 mov ax,VideoBuff_Sel
500 mov es,ax
501 mov ax,XBuffer_Sel
502 mov fs,ax
503 mov gs,ax
504 Simulate:
505 ;接受要模拟的异常类型号
506 int 0ffh
507 ;按接受的字符模拟异常
508 mov al,fs:KeyASCII
509 ;分情况处理
510 cmp al,1bh ;ESC键
511 jnz Demo
512 ;退出程序
513 jmp Over
514 Demo:
515 cmp al,'0'
516 jnz Demo4
517 ;模拟除法出错故障
518 mov ax,1000
519 mov cl,2
520 div cl ;Exception 0,该指令长2字节
521 jmp Simulate
522 Demo4:
523 cmp al,'4'
524 jnz DemoB
525 ;模拟溢出陷阱
526 mov al,100
527 add al,50
528 into ;Exception 4
529 jmp Simulate
530 DemoB:
531 cmp al,'B'
532 jnz DemoC
533 ;模拟段不存在故障
534 mov ax,TestNPS_Sel
535 mov gs,ax ;Exception 11,该指令长2字节
536 jmp Simulate
537 DemoC:
538 cmp al,'C'
539 jnz DemoD
540 ;模拟堆栈出错故障
541 mov ebp,esp
542 mov al,[ebp] ;Exception 12,该指令长4个字节
543 jmp Simulate
544 DemoD:
545 ;模拟通用保护故障
546 mov ax,DemoTSS_Sel
547 mov gs,ax ;Exception 13,该指令长2字节
548 jmp Simulate
549 Over:
550 ;转临时代码段
551 JUMP16 TempCode_Sel,<offset ToDOS>
552 DemoCodeLen = $ - DemoCodeSeg
553 DemoCodeSeg ends
554
555 ;------------------------------------------
556 ;临时代码段
557 TempCodeSeg segment para use16
558 assume cs:TempCodeSeg
559 Virtual:
560 ;转演示代码段,DemoCode_Sel必须处于GDT中
561 JUMP16 DemoCode_Sel,<offset DemoBegin>
562 ToDOS:
563 ;演示结束后准备返回实模式
564 mov ax,Normal_Sel
565 mov ds,ax
566 mov es,ax
567 mov fs,ax
568 mov gs,ax
569 mov ss,ax
570 mov eax,cr0
571 and eax,0fffffffeh
572 mov cr0,eax
573 JUMP16 <seg Real>,<offset Real>
574 TempCodeSeg ends
575
576
577 ;------------------------------------------
578 ;实模式下的数据段
579 RDataSeg segment para use16
580 VGDTR PDESC<GDTLen - 1,> ;GDT伪描述符
581 VIDTR PDESC<IDTLen - 1,> ;IDT伪描述符
582 NORVIDTR PDESC<3ffh,0> ;保存IDTR原值
583 SPVar dw ?
584 SSVar dw ?
585 Mess db 'Strike a key [0,4,B,C,D,ESC]:$';提示信息
586 FeedLine db 0ah,0dh,24h ;换行
587 RDataSeg ends
588 ;------------------------------------------
589 ;实模式下的代码段
590 RCodeSeg segment para use16
591 assume cs:RCodeSeg,ds:RDataSeg
592 start:
593 mov ax,RDataSeg
594 mov ds,ax
595 cld
596 ;初始化GDT
597 call   INIT_GDT
598 ;初始化IDT
599 call   INIT_IDT
600 ;初始化读键盘任务LDT
601 mov ax,GKeyLDTSeg
602 mov fs,ax
603 mov si,offset GLDT
604 mov cx,GKeyLDTNum
605 call   INIT_LDT
606 ;初始化演示任务LDT
607 mov ax,DemoLDTSeg
608 mov fs,ax
609 mov si,offset DLDT
610 mov cx,DemoLDTNum
611 call   INIT_LDT
612 ;初始化TSS
613 call   INIT_TSS
614 ;实模式堆栈保护
615 mov SSVar,ss
616 mov SPVar,sp
617 ;装载GDTR和切换到保护模式
618 lgdt   fword ptr VGDTR
619 sidt   NORVIDTR
620 cli
621 lidt   fword ptr VIDTR
622 mov eax,cr0
623 or eax,1
624 mov cr0,eax
625 JUMP16 <TempCode_Sel>,<offset Virtual>
626 Real:
627 ;又回到实模式
628 mov ax,RDataSeg
629 mov ds,ax
630 lss sp,dword ptr SPVar
631 lidt   NORVIDTR
632 sti
633 mov ax,4c00h
634 int 21h
635 ;------------------------------------------
636 ;初始化全局描述符表的子程序
637 ;(1)把定义时预置的段值转换成32位段基地址并置入描述符内相应字段
638 ;(2)初始化为GDTR准备的伪描述符
639 INIT_GDT proc near
640 push   ds
641 mov ax,GDTSeg
642 mov ds,ax
643 mov cx,GDTNum ;初始化描述符的个数
644 mov si,offset EFFGDT ;开始偏移
645 assume si:ptr DESCRIPTOR
646 INITG:
647 mov ax,[si].BaseL ;取出预置的段值
648 movzx eax,ax ;扩展到32位
649 shl eax,4
650 shld edx,eax,16 ;分解到2个16位寄存器
651 mov [si].BaseL,ax ;置入描述符相应字段
652 mov [si].BaseM,dl
653 mov [si].BaseH,dh
654 add si,size DESCRIPTOR ;调整到下一个描述符
655 loop   INITG
656 assume si:nothing
657 pop ds
658 ;
659 mov bx,16 ;初始化为GDTR准备的伪描述符
660 mov ax,GDTSeg
661 mul bx
662 mov word ptr VGDTR.Base,ax
663 mov word ptr VGDTR.Base + 2,dx
664 ret
665 INIT_GDT endp
666
667 ;------------------------------------------
668 ;初始化IDTR伪描述符子程序
669 INIT_IDT proc
670 mov bx,16
671 mov ax,IDTSeg
672 mul bx
673 mov word ptr VIDTR.Base,ax
674 mov word ptr VIDTR.Base + 2,dx
675 ret
676 INIT_IDT endp
677
678 ;------------------------------------------
679 ;初始化演示任务局部描述符表的子程序
680 ;把定义时预置的段值转换成32位段基地址并置入描述符内的相应字段
681 ;入口参数:FS:SI = 第一个要初始化的描述符
682 ; CX = 要初始化的描述符个数
683 INIT_LDT proc
684 assume si:ptr DESCRIPTOR
685 ILDT:
686 mov ax,fs:[si].BaseL
687 movzx eax,ax
688 shl eax,4
689 shld edx,eax,16
690 mov fs:[si].BaseL,ax
691 mov fs:[si].BaseM,dl
692 mov fs:[si].BaseH,dh
693 add si,size DESCRIPTOR
694 loop ILDT
695 assume si:nothing
696 ret
697 INIT_LDT endp
698 ;------------------------------------------
699 ;初始化TSS段子程序
700 INIT_TSS proc
701 mov ax,GKeyTSSSeg
702 mov fs,ax
703 mov si,offset KTSS
704 assume si:ptr TASKSS
705 mov fs:[si].TRLink,0 ;链接字 = 0
706 mov fs:[si].TRCR3,0 ;CR3 = 0
707 mov fs:[si].TRCS,GKeyCode_Sel ;CS
708 mov fs:[si].TREIP,GKeyBegin ;EIP
709 mov fs:[si].TRSS,GKeyStack_Sel ;SS
710 mov fs:[si].TRESP,GKeyStackLen ;ESP
711 mov fs:[si].TRES,Normal_Sel ;ES
712 mov fs:[si].TRDS,Normal_Sel ;DS
713 mov fs:[si].TRFS,Normal_Sel ;FS
714 mov fs:[si].TRGS,Normal_Sel ;GS
715 mov fs:[si].TRLDT,GKeyLDT_Sel ;LDT
716 assume si:nothing
717 ret
718 INIT_TSS endp
719 RCodeSeg ends
720 end start

 

 

3、  关于源代码的说明

  1)  一些一直就有的错误。

  2)  一次按键被我改成了循环按键,直到ESC键被按下时才退出程序,只有这样才能测试读键盘任务代码段最后在中断返回指令之后的跳转语句的作用。

  3)  这次的测试代码有点长了,实际上这里的异常处理可以写成一个模块,在以后的测试中可以使它避免系统奔溃问题,缺点是提供的出错信息太少。

 

 

4、  测试结果

《80X86汇编语言程序设计教程》十八 异常处理实例 

 

 

5、  测试说明

  1)  整体框架

    主程序先做一些初始化工作,然后切入保护模式进行模拟测试,它的主要工作是发出软中断0ffh接受用户输入,然后根据输入模拟出对应的异常(错误操作),然后继续等待用户输入,直到接受到ESC键返回到实模式从而退出程序。在模拟出异常时,它们被操作系统捕获,并调用中断向量描述符表IDT中对应的处理程序,这些程序由我们自己在程序中设定,来进行异常处理。

  2)  读键盘任务的实现

    读键盘采用了“软中断”(中断号0ffh)形式进入自行设计的处理程序,它被设计成一个独立的任务,“软中断”通过任务门来调用它。为了简化程序,在读取键盘时,切换到了实模式进行,读取完毕再切换回保护模式。期间,使用了DOS系统调用int 21H(参考“《80X86汇编语言程序设计教程》三 子程序设计与DOS功能调用”)以及BIOS键盘I/O程序调用(参考“《80X86汇编语言程序设计教程》四 输入输出与中断”)。关于通过任务门实现中断/异常与使用陷阱门和中断门的区别参考“《80X86汇编语言程序设计教程》十六 80386的中断和异常”。读键盘任务使用到了3个段,在软中断之前的“演示任务代码段”对它们已经赋值,这里有点不太明朗。此外,须特别注意的是在中断返回指令IRETD之后的JMP指令“jmp  GKeyBegin”,任务切换时,会自动保护现场,它保存的CS:EIP作为下次切换到该任务的入口点,在执行到IRETD时,CS:EIP执行下一条指令,也就是“jmp  GKeyBegin”,那么下次再“软中断”时,最开始执行的语句实际上是它,如果没有它,那么系统将陷入假死(关于任务切换参考“《80X86汇编语言程序设计教程》十二 任务状态段、控制门和控制转移”的“任务切换”相关介绍)。

  3)  异常处理程序一般说明

    各种异常处理程序通过陷阱门进行切换,在模拟异常发生时,处理器切入对应的异常处理程序中来,但是都不切换任务,异常处理作为任务的一部分存在。一般来说异常处理程序都要保护现场,这里是为了简化。需要注意的是,这里模拟的所有故障中,在切入故障处理程序时,CS:EIP指向的是引发故障的指令。所以,如果要在处理程序中避免重新执行故障指令,须自行调整指针值。

  4)  除法出错故障处理程序的实现

    模拟除法出错时使用了1000除以2,商500对于8位来说太大,所以抛出故障。故障处理程序先显示一条提示信息,随后把被除数右移一位(除以2)。由于返回时并没有调整返回地址指针,那么这条故障指令将被重新执行,从而再次触发故障,直到除法不再出错。

  5)  溢出陷阱处理程序的实现

    显示提示信息,然后返回。陷阱与故障不同,陷阱处理完毕后,返回地址指针指向的是引发陷阱指令的下一条指令(不一定是后一条指令)。

  6)  段不存在故障处理程序的实现

    在将段选择子送寄存器GS时引发,段选择中的P位(存在位)被故意置为了0。一般需要扫描GDT与IDT确认段存在,然后将P位改为1后返回。这里为了简单,直接提示信息,然后修改返回地址跳过故障引发指令而返回。该故障提供一个错误码(具体意义参考“《80X86汇编语言程序设计教程》十六 80386的中断和异常”),在进入处理程序时处于堆栈栈顶,在它下面的就是返回地址。

  7)  堆栈段出错故障处理程序的实现

    故意安排偏移超过段界限(刚好为“段长”,而界限为“段长 – 1”)的堆栈段访问指令来模拟堆栈段出错故障的产生。这里为了简化,采用同上一样的流程。

  8)  通用保护故障处理程序的实现

    模拟时故意将一个指向系统段描述符的选择子装入数据段寄存器GS来引发通用保护异常。流程同上。