第Ⅰ部分
1.編寫並安裝int 7ch中斷例程,功能為顯示一個用0結束的字符串,中斷例程安裝在 0:200處。
參數:(dh)= 行號,(dl)= 列號,(cl)= 顏色,ds:si指向字符串首地址。
Solution
第一部分實驗與匯編語言:實驗十二 編寫0號中斷的處理程序類似,這里就貼上代碼吧:
assume cs:code, ds:data
code segment
start:
jmp install
mov dh, 10
mov dl, 10
mov cl, 2
mov ax, data
mov ds, ax
mov si, 0
int 7ch
mov ax, 4c00h
int 21h
install:
mov ax, 0
mov es, ax
mov word ptr es:[4 * 7ch], 200h
mov word ptr es:[4 * 7ch + 2], 0;安裝中斷向量表
mov ax, cs
mov ds, ax
mov ax, 0
mov es, ax
mov si, 0
mov di, 200h
mov cx, offset do7chEnd - offset do7ch
cld
rep movsb
do7ch:
data segment
db "welcome to masm!", 0
data ends
push cx
push si
push di
mov ax, 0b800h
mov es, ax
mov ax, data
mov ds, ax
mov si, 0;源地址偏移
mov al, dh
mov bl, 160
mul bl ;dh * 160
add ax, dl
mov di, ax
trans:
mov cl, [si]
mov ch, 0
jcxz fin;如果cx = 0,表示已經處理完畢
mov byte ptr es:[di], cl
mov byte ptr es:[di + 1], 11000010B
inc si
add di, 2
jmp trans
fin:
pop di
pop si
pop cx
iret
do7chEnd:
nop
code ends
end start
需要注意的還是在 data segment 的放置,如果直接放在主程序中會出現意想不到的錯誤哦,必須將其放在子程序中。
運行效果:
第Ⅱ部分
編寫並安裝 int 7ch 中斷例程,功能為完成 loop 指令的功能。
參數:(cx)= 循環次數,(bx)= 位移。
Solution
程序總體涉及到多個部分,不過大體上跟之前一樣,只是在中斷處理程序上有不一樣的處理方式。
install子程序
先看中斷子程序是如何被安裝到指定位置的(為了方便觀看,這里放上了Notepad++中的代碼截圖):
這段代碼也不難,首先是保存相關寄存器,然后填寫中斷向量表,緊接着將中斷處理程序的代碼拷貝到指定地址,最后從棧中恢復寄存器的值。
do7ch中斷處理程序
首先是保存bp寄存器(保護工作),int 7ch實現loop指令的功能,在循環的時候需要做如下操作:
- dec cx;循環計數變量減一
- 如果cx為0,那么不再循環,執行循環代碼后面的程序
- 如果cx不為0,那么繼續循環,CS:IP指向循環體開始部分。
如何讓程序跑回循環開始的位置呢?這里bx寄存器已經保存了循環體代碼的長度,中斷處理過程如下:
所以,棧中是保存了調用程序的位置(IP),那么怎么訪問到IP呢,很簡單,[sp + 2]即可,最上層為bp(我們自己壓入的),然后就是IP,再下面是CS,因為bp的長度為2,所以往后偏移2字節即可獲得IP的值。(棧的內容如下圖)
完整程序:
assume cs:code
code segment
start:
call install ;調用中斷安裝程序
mov ax, 0b800h
mov es, ax
mov di, 160 * 12
mov bx, offset s - offset se
mov cx, 80
s:
mov byte ptr es:[di], '!'
add di, 2
int 7ch
se:
nop
mov ax, 4c00h
int 21h
install:
push ax
push es
push ds
push si
push di
push cx
mov ax, 0
mov es, ax
mov word ptr es:[4 * 7ch], 200h
mov word ptr es:[4 * 7ch + 2], 0;填寫中斷向量表
;將中斷處理程序拷貝到指定地址
mov ax, cs
mov ds, ax
mov ax, 0
mov es, ax ;目的段地址 0
mov si, offset do7ch;源偏移地址
mov di, 200h;目的偏移地址
mov cx, offset do7chEnd - offset do7ch;程序的長度
cld
rep movsb
pop cx
pop di
pop si
pop ds
pop es
pop ax
ret
do7ch:;從棧中取出子程序的偏移地址
push bp
mov bp, sp
dec cx
jcxz fin ;如果cx已經為0了,那么不再進行跳轉,順序執行即可
add [bp + 2], bx;修改IP指向使其指向s程序開始處
fin:
pop bp
iret
do7chEnd:
nop
code ends
end start
運行結果
還差最后一道題,明天更新~
-------------------------------我是分割線 9.22更-----------------------------------
第Ⅲ部分
下面的程序,分別在屏幕的第2、4、6、8行顯示4句英文詩,補全程序。
Solution
需要補充的地方並不多,首先要知道,這道題是為了讓我們熟悉使用BIOS和DOS提供的中斷例程編程。
int 10h中斷例程是 BIOS 提供的中斷例程。其中包含了多個和屏幕輸出相關的子程序。這里使用int 10h中斷例程的設置光標的功能來完成在屏幕上的定向輸出。
mov ah, 2 ;置光標
mov bh, 0 ;第0頁
mov dh, 5 ;dh中放行號
mov dl, 12 ;dl中放列號
int 10h
(ah)= 2表示調用第 10h 號中斷例程的2號子程序,功能為設置光標位置,可以提供光標所在的行號和頁號作為參數。
第一處需要填的就是調用int 10h 中斷例程需要指定的那些個參數:
int 21h中斷例程是 DOS 提供的中斷例程,其中包含了 DOS 提供給程序員在編程時調用的子程序。
在書中的13.7章節中使用了int 21h中斷例程在光標位置顯示字符串的功能:
ds:dx 指向字符串 ;要顯示的字符串需用"$"作為結束符
mov ah, 9 ;功能號9,表示在光標位置顯示字符串
int 21h
(ah)= 9 表示調用第21h號中斷例程的9號子程序,功能為在光標位置顯示字符串,可以提供要顯示字符串的地址作為參數。
第二處同樣是為調用中斷例程而設定參數:(設置需要輸出的字符串位置)
最后兩個需要填寫的位置:修改偏移地址,為下一次循環做准備。
完整程序
assume cs:code
code segment
s1: db 'Good,better,best,', '$'
s2: db 'Never let it rest,', '$'
s3: db 'Till good is better,', '$'
s4: db 'And better,best.', '$'
s: dw offset s1, offset s2, offset s3, offset s4
row:db 2,4,6,8
start:
mov ax, cs
mov ds, ax
mov bx, offset s
mov si, offset row
mov cx, 4
ok:
mov bh, 0 ;第0頁
mov dh, ds:[si] ;行號
mov dl, 0 ;列號
mov ah, 2 ;置光標
int 10h
mov dx, ds:[bx]
mov ah, 9
int 21h
inc si ;更改顯示位置
add bx, 2 ;改變偏移地址 2byte
loop ok
mov ax, 4c00h
int 21h
code ends
end start