一二三章實驗環境配置見:https://blog.csdn.net/plus_re/article/details/60761467
第四章加入了deit等指令,需要添加一些exe文件:https://blog.csdn.net/Ghost_jzy/article/details/104572876
我有時進入dos,輸入數字有異常,多按幾次數字鍵左上角的numlock就好
整本書都放一起了,有點亂,鼠標放在標題,右側會顯示目錄導航~
除了兩個課設和研究實驗沒寫,其他都是一個個手打出來的,印象中只有實驗十七沒有上機實踐,其他都是沒問題的~
這本書我是學完計組,學操作系統之前看的。現在看來這本書有助於我理解操作系統,鞏固以前學過的計組
可以配合《匯編語言筆記》看
第二章
實驗一
任務一:
使用debug,將下面的程序段寫入內存,逐條執行,觀察每條指令執行后CPU中相關寄存器中內容的變化
1.進入debug模式,先查看寄存器的初始值和內存單元的初始情況
2.使用e指令將程序段存入內存中(使用A指令用匯編語言存入內存也行,使用e指令是機器碼存)
3.至此,程序已經載入內存,接下來用r指令修改IP和CS
4.運行程序之前,將內存的機器碼轉化為匯編指令,檢查程序是否輸入正確
5.沒問題,用t指令運行程序,以下是結果
任務二:
將下面3條指令寫入從2000:0開始的內存單元中,利用這三條指令計算2的8次方
1.先改IP和CS,再存程序(不知道機器碼,只能使用a指令,用匯編指令去存到內存
2.不出意外,按14次T(亂算的,我沒數去驗證,截圖省略了若干次按T的過程..)ax就是結果
任務三:
修改ROM的生產日期
1.用d指令,查看一定范圍的內存單元(段地址就是物理地址右移一位,右邊少個0,所以是fff0)
2.看了看網上答案,右下角的01/01/92似乎就是要找的日期(網上的ROM跟我的ROM剛好同年同月同日生嗎?太巧了吧?!)
3.試圖修改
4.屁用沒有!意料之中~因為我在步驟2看別人答案時,不小心看到他修改失敗了..
5.其實因為題目也說了是ROM,所以修改失敗,RAM才能改
任務四:
向內存從B8100H開始的單元填寫數據
1.根據題目照做
2.換個數據會發現右上角的圖案會變(忘記截圖了),我以為ROM數據被改了出現亂碼還是啥,再次查看ROM時發現,沒有被修改
3.原來那地方是顯存地址,有點意思
第三章
檢測點3.1
1.在debug中,用“d 0:0 1f”查看內存,結果如下..
這題注意ax的起始位置就好,我是搞錯了好幾次。ax段地址是1,乘16之后就是10,ax指向62
2.內存中情況如圖3.6所示。各寄存器的初始值:CS=2000H,IP=01,DS=1000H,AX=0,BX=0;
(1.寫出CPU執行指令的指令序列
(2.寫出CPU執行每條指令后,CS,IP和相關寄存器的值
(3.再次體會:數據和程序有區別嗎?如何確定內存中信息哪些是數據,哪些是程序?
執行次數 | CS | IP | DS | AX | BX |
---|---|---|---|---|---|
1 | 2000h | 3h | 0 | 6622h | 0 |
2 | ff0h | 100h | 0 | 6622h | 0 |
3 | ff0h | 103h | 0 | 2000h | 0 |
4 | ff0h | 105h | 2000h | 2000h | 0 |
5 | ff0h | 108h | 2000h | c389h | 0 |
6 | ff0h | 10bh | 2000h | ea66h | 0 |
指令和數據都沒區別。通過CS和IP訪存,CPU當作指令,通過DS訪存,CPU當作數據?
檢測點3.2
道理我都懂,就是沒想出來
實驗二
任務一:
執行下面的程序填寫實際結果
不知道這題意義是啥,程序起始位置隨便放在1000:0了
任務二:
分析為什么2000:0~2000:f的內容會變
我沒有悟性,不曉得
第四章 第一個程序
實驗三
任務一
將下面的程序保存為t1.ASM文件,並生成可執行文件t1.exe
沒啥好講的,先生成obj目標文件,再生成exe文件
任務二
用debug跟蹤t1.exe的執行過程,寫出每一步執行后,相關寄存器和棧頂的內容。
見下圖(執行INT指令的時候要用P命令,我用成T了,所以沒能正常返回,但是過程是對的):
任務三
PSP的頭兩個字節是CD 20,用Debug加載t1.exe,查看PSP的內容(PSP是程序前綴的數據區)
debug的時候,DS寄存器存的是075A,我們去這個地址看,確實開頭是CD20
第五章
實驗四
任務一
編程,向內存0:200~0:23F依次傳送數據0~63(3FH)
任務一二一起完成
任務二
編程,向內存0:200~0:23F依次傳送數據0~63(3FH),程序中只能使用9條指令,9條指令包括“mov ax,4c00H”和“int 21h”
目標內存區域跨度為64,剛好對應64個要輸入的數據,那么可以共用一個變量
1.dos里面運行edit,編寫九行代碼
2.編譯並連接
3.進入debug跟蹤,先看看0:200有沒有數據,確實沒有
4.再看看代碼是否正確存入
5.運行后,查看0:200
任務三
補全下列程序。上機調試,跟蹤運行結果
md寫錯了,其實還是有疑問的。
- 第一個空對了,cs代表着程序起始位置。第四章提到:程序加載后,DS寄存器存着內存區的段地址,此時指向PSP部分,后面的256字節才開始存程序,那么這一空能否寫成“ds+10H”?
- 第二個空,正確做法:將程序編譯連接,debug的時候會發現mov ax,4c00h前面所占內存為17H,所以答案為17H或者23(下圖漏寫H
第六章 包含多個段的程序
檢測點6.1
- 答案:mov cs:[bx],cx
- 答案:cs,24h,pop cs:[bx]
- 第二題:cs不用說。因為dw定義數據,因此最后一個有用數據的偏移地址為0E~0F(有用數據指的是dw出來是為了當數據, 不是為了單純開辟空間),隨后開辟了20字節,F+20(十進制)=23H,棧空的時候指向23H后面一個地址,即24H
實驗五 編寫,調試具有多個段的程序
1.跟蹤程序,回答問題
答案:data值不變,cs=076c,ss=076B,ds=075A,data段地址為X-1,stack段地址為X-2
第二三四空的答案(不同電腦可能答案不同)也印證了第五六空的結論,data和stack各占16字節,所以X減去16(十進制)和32(十進制)即可,由於段地址最后一位權值為16,因此分別為-1和-2
2.跟蹤程序,回答問題
答案:data值不變,cs=076c,ss=076B,ds=076A。data段地址為x-2,stack段地址為x-1。【N/16】+1,【】表示取整
這里雖然程序通過dw定義的stack和data空間都是4字節的,但是因為段地址起始位置一定是X:0的形式,換句話說,組成段空間的最小單位是16字節,所以跟上一題答案相同。至於為什么,似乎和數據對齊有關。
3.跟蹤程序,回答問題
答案:data不變。cs=076A,ss=076E,ds=076D,data段地址為x+3,stack段地址為x+4
4.將123題的end start改為end,哪個程序還能正常運行?
應該是只有第三個,前兩個CS:IP指向的都是data段的東西。第三個剛好把code段放在了最前面,執行到后面正常返回。
5.編寫code段的代碼
這題需要注意定義數據的時候用的是db,一個數據一字節,以下是答案
code segment
start:
mov bx,0
mov cx,8
s:
mov ax,a
mov ds,ax
mov dx,0
mov dl,ds:[bx]
mov ax,b
mov ds,ax
add dl,ds:[bx]
mov ax,c
mov ds,ax
mov ds:[bx],dl
inc bx
loop s
mov ax,4c00h
int 21h
code ends
在075D:00D0處可以看到我們存的數據
程序運行后再次查看,發現075D:00F0存儲着ab之和
6.編寫code段的代碼
這題是dw,一個數據兩字節。要逆序存儲棧空間,直接正序push就好了~
code segment
start:
mov ax,b
mov ss,ax
mov sp,10h
mov ax,a
mov ds,ax
mov ax,b
mov es,ax
mov cx,8
mov bx,0
s: push ds:[bx]
inc bx
inc bx
loop s
mov ax,4c00h
int 21h
code ends
下圖是運行程序前,棧為空
下圖是運行程序后,棧存儲着逆序數據
第七章 更靈活定位內存地址的方法
實驗六
1.將第七章所有程序上機調試,debug跟蹤。這題就不說了
2.編程,完成問題7.9中的程序
答案如下:
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,10H
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4
s0:push cx
mov si,0
mov cx,4
s:mov al,[bx+3+si]
and al,11011111b
mov [bx+3+si],al
inc si
loop s
add bx,16
pop cx
loop s0
mov ax,4c00H
int 21H
codesg ends
運行前先查看字符串初始值
運行后可以看到字符串前四個字母都是大寫
第八章
實驗七
將存年份的,存收入的,存雇員數的分別看作三個數組,將table看作一個二維數組一次性寫完比較難調試,感覺寄存器不夠用,都暈了,所以我是一個功能一個功能寫的,最后合並就好
功能一:復制年份到table里每個數組的前4位
codesg segment
start:
mov bx,0;bx指向table不同的數組
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s0:
mov si,0;數組里的下標
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc bp
add bx,10H
loop s0
mov ax,4c00h
int 21h
codesg ends
程序運行前查看內存
程序運行后查看內存,可知功能實現
功能二:復制收入到table里每個數組的5-8位
codesg segment
start:
mov bx,0;bx指向table不同的數組
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s1:
mov si,5
mov ax,es:[84+bp]
mov [bx+si],ax
add si,2
add bp,2
mov ax,es:[84+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s1
mov ax,4c00h
int 21h
codesg ends
程序運行前
程序運行后,可知功能實現
功能三:復制雇員數到table里的每個數組的A-B位
codesg segment
start:
mov bx,0;bx指向table不同的數組
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s1:
mov si,0AH
mov ax,es:[168+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s1
mov ax,4c00h
int 21h
codesg ends
程序運行前
程序運行后,可知功能實現
完整版答案:合並三個功能,並算出人均收入
assume cs:codesg
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,19754
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
codesg segment
start:
mov bx,0;bx指向table不同的數組
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s3:;開始復制雇員數
mov si,0AH;雇員數要復制到table數組下標A-B,數據不能以字母開頭,需要加0
mov ax,es:[168+bp];168表示雇員數數組的偏移地址
mov [bx+si],ax
add bp,2
add bx,10H
loop s3
mov bx,0
mov bp,0
mov cx,21
s2:;開始復制收入
mov si,5;收入要復制到table數組下標5-8
mov ax,es:[84+bp];84表示收入數組的偏移地址
mov [bx+si],ax
add si,2
add bp,2
mov ax,es:[84+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s2
mov bp,0
mov bx,0
mov cx,21
s1:;開始復制年份
mov si,0;年份要復制到數組下標0-3
mov al,es:[0+bp];0表示年份數組的偏移地址
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc bp
add bx,10H
loop s1
mov cx,21
mov bp,0
mov bx,0
mov di,0
s4:;開始計算人均收入
mov si,0DH
mov ax,es:[84+bp];被除數低字節存在ax
add bp,2
mov dx,es:[84+bp];被除數高字節存在dx
add bp,2
div word ptr es:[168+di];做除法,指出操作的數據大小為兩字節
add di,2
mov [bx+si],ax
add bx,10H
loop s4
mov ax,4c00h
int 21h
codesg ends
end start
程序運行前
程序運行后,看內存地址076A:00D0。5-8位是10 00 00 00是十進制16,A-B位是03 00是十進制3,D-E位是5,16/3取整就是5,答案正確!
第九章 轉移指令的原理
檢測點9.1
1.定義data段數據,使得CS:IP運行jmp后指向第一條指令
data segment
db 16 dup(0)
data ends
其實只要data:1和data:2存的是0就好了
2.補全程序,使得CS:IP運行jmp后指向第一條指令
mov [bx],bx;我本來寫的是mov [bx],0
mov [bx+2],cs;我本來寫的是mov [bx+2],offset start
因為我寫的兩個move指令沒有指出操作的是字還是字節,所以還是上面的答案合適
3.CPU執行后,CS=?,IP=?
CS=0006,IP=00BE
檢測點9.2
1.補全程序
s: mov cl,ds:[bx]
mov ch,0
jcxz ok
inc bx
jmp short s
檢測點9.3
1.補全程序
s: mov cl,[bx]
mov ch,0
inc cx;這題確實沒做出來,因為loop判斷時先將cx--,因此要在cx減1之前加上1
inc bx
loop s
實驗8 分析一個奇怪的程序
沒想到居然能運行。。s段作用就是把s2的"jmp short s1"復制到s開頭的兩個nop。於是程序執行完s段,跳轉至s開頭,又進行跳轉,但是復制的跳轉指令機器碼存的是IP的位移,存的是-8字節,因此歪打正着,往前八字節跳到mov ax,ac00h那兩行返回指令去了
實驗9 根據材料編程
有兩個需要注意的地方:這里是在顯存上修改內容,不需要太關注每時每刻地址上的內容,因為只要你屏幕顯示變了,顯存的內容也會變的。此外,不要從顯存的開頭B800:0000開始寫數據!(我從0開始寫,一直沒找到哪里有變化,也許是dos框框太小了?
assume cs:codesg,ds:datasg
datasg segment
db 'welcome to masm!'
db 02h,24h,71h
datasg ends
codesg segment
start:mov ax,datasg
mov ds,ax
mov ax,0B860h
mov es,ax
mov cx,3
mov bp,0
s:;一個s循環顯示一串字符,三行同一位置依次顯示
mov si,0
mov di,0
push cx
mov ah,[10h+bp]
inc bp
mov cx,16
s1:;一個s1循環顯示一個字符
mov al,ds:[si]
inc si
mov es:[di],ax
add di,2
loop s1
pop cx
loop s
mov ax,4c00h
int 21h
codesg ends
end start
;網上也有只寫了一個執行16次循環的,一次循環顯示一列字符,三個字符串在不同行同時顯示
執行結果
第十章 CALL和 RET指令
檢測點10.1
mov ax,1000H,mov ax,0000H;要注意先是pop IP,再pop cs
檢測點10.2
ax=6;當一條指令讀完之后,就指向下一條指令了,當執行call時,IP指向inc ax,於是將6壓入棧,最后將6出棧給ax
檢測點10.3
1010H;pop ax使得ax為8,add之后ax為16(十進制),bx為1000,於是相加為1010H
檢測點10.4
BH
檢測點10.5
第一題:ax=3;
第二題
實驗十
1.顯示字符串
assume cs:code
data segment
db 'welcome to masm!',0
data ends
code segment
start:mov dh,8;行號
mov dl,3;列號
mov cl,2;顏色
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
show_str:;要用到的寄存器有:ax和bx放乘數,bx存行下標,di存列下標,si存字符串下標,cx判斷是否為0
push ax
push es
push bx
push di
push si
mov ax,0B800H
mov es,ax
mov al,dh;准備乘法
mov bl,160
mul bl
mov bx,ax;行標放在了bx
dec dl;准備乘法
mov al,dl
push bx
mov bl,2
mul bl
mov di,ax;列標放在了di
pop bx
;bx,di行標列標已就緒
again:push cx
mov cl,ds:[si]
mov ch,0
jcxz ok;是0就退出程序,否則繼續執行
pop cx
mov al,ds:[si]
mov byte ptr es:[bx+di],al
inc si
inc di
mov byte ptr es:[bx+di],cl
inc di
jmp near ptr again
ok:
pop si
pop di
pop bx
pop es
pop ax
ret
code ends
end start
2.解決除法溢出問題
md,這題我也是麻了。分析一下公式:兩個大數相除的結果可以用兩個小數的除法得出。商=int(N/H)65536+[rem(H/N)65536+L]/N的商,余數=[rem(H/N)*65536+L]的余數,其中與65536相乘可以用左移16位,后面補零代替
assume cs:code
data segment
db 16 dup(0)
data ends
code segment
start:
mov ax,4240h;被除數低16位
mov dx,000Fh;被除數高16位
mov cx,0Ah;除數
mov bx,data
mov ds,bx
mov bx,data
mov ss,bx
mov sp,16
call divdw
mov ax,4c00h
int 21h
divdw:push dx
push ax
mov ax,dx;ax放被除數的高位
mov dx,0;dx放0
div cx;商在ax,余數在dx
push dx
;接着商*65536,就是左移16位,后面補零,此時也就是ax000...00,0就我不存了,存ax就好
;此時棧內:000fh,4240h,余數
pop bx;取出余數
pop di;取出被除數低16位4240h
push ax;此時棧內:000fh,商
mov dx,bx;
mov ax,di
div cx;計算(rem(H/N)*65536+L]/N,商在ax,余數在dx
;根據公式,此時的ax和前面的商*65536相加,商*65536已經在棧中
;開始返回結果
mov cx,dx
pop dx
;對了,函數結束之前還要清空棧
pop bx
ret
code ends
end start
運行結果和答案一致
3.數值顯示
show_str幾乎直接復制過來就好
assume cs:code
data segment
dw 123,12666,1,8,3,38
data ends
out segment
db 32 dup(0);dtoc函數就是把data的數字以字符的形式,寫到out這段內存中
out ends
stack segment
db 160 dup(0);因為這種方法求出來的余數是倒序的(比如123經過一系列除法,得出的余數依次是32),所以需要用棧,pop出來寫入out內存就是正序。
stack ends
code segment
start:
mov bx,data
mov es,bx;es指向data
mov bx,out
mov ds,bx;ds指向out
mov bx,stack
mov ss,bx
mov sp,160;ss:sp指向棧空間
;參數初始化
mov ax,es:[0]
mov si,0
call dtoc
mov ax,es:[2]
call dtoc
mov ax,es:[4]
call dtoc
mov ax,es:[6]
call dtoc
mov ax,es:[8]
call dtoc
mov ax,es:[10]
call dtoc
mov dh,8
mov dl,3
mov cl,2
mov si,0
call show_str
mov ax,4c00h
int 21h
dtoc:push bx
mov bx,0;事先放個0,出棧的時候好知道這時候已結束
push bx
mov bx,10;bx放除數
s:mov dx,0
div bx;dx余數,ax商
push dx
mov ax,ax
mov cx,ax
jcxz s1;如果商為,說明求余數結束,開始出棧寫內存
jmp short s
s1:pop ax
mov cx,ax
jcxz ok
add ax,30h
mov ds:[si],ax
inc si
jmp short s1
ok:pop bx
ret
show_str:;要用到的寄存器有:ax和bx放乘數,bx存行下標,di存列下標,si存字符串下標,cx判斷是否為0
push ax
push es
push bx
push di
push si
mov ax,0B800H
mov es,ax
mov al,dh;准備乘法
mov bl,160
mul bl
mov bx,ax;行標放在了bx
dec dl;准備乘法
mov al,dl
push bx
mov bl,2
mul bl
mov di,ax;列標放在了di
pop bx
;bx,di行標列標已就緒
again:push cx
mov cl,ds:[si]
mov ch,0
jcxz ok2;是0就退出程序,否則繼續執行
pop cx
mov al,ds:[si]
mov byte ptr es:[bx+di],al
inc si
inc di
mov byte ptr es:[bx+di],cl
inc di
jmp near ptr again
ok2:pop si
pop di
pop bx
pop es
pop ax
ret
code ends
end start
以下是程序運行結果,沒錯我偷懶了,輸出字符串沒分開,主要是題目也沒說輸出最終結果該是咋樣的
課程設計1
搞了一下午我不想寫了.....借口:反正核心的東西前面都實現了
第十一章 標志寄存器
檢測點11.1
注意push,mov之類的轉移指令不改變flag就行
sub al,al 110
mov al,l 110
push ax 110
pop bx 110
add al,bl 000
add al,10 010
mul al 010
檢測點11.2
sub al,al 00011
mov al,10h 00011
add al,90h 00101
mov al,80h 00101
add al,80h 11011
mov al,0FCh 11011
add al,05h 10000
mov al,7Dh 10000
add al,0Bh 01101
檢測點11.3
(1
cmp al,32
jb s0
cmp al,128
ja s0
(2
cmp al,32
jna s0
cmp al,128
jnb s0
檢測點11.4
- 執行到popf時,ax賦給flag標志寄存器,此時flag全零
- add執行后,zf=1,pf=1,cf=1,其余為零
- FFF0
0010
———————
1,0000
- FFF0
- pop ax后,ah為0000 0000 ,al為0100 0101
- 經過兩次and后,ax=0045H
實驗11編寫子程序
將大寫轉化為小寫,ascii與11011111b進行與運算即可
assume cs:code
datasg segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
datasg ends
code segment
start:
mov ax,datasg
mov ds,ax
mov si,0
call letterc
mov ax,4c00h
int 21h
letterc:;如果ascii≥97且≤122,就執行大寫轉化
cmp byte ptr ds:[si],0
je ok
cmp byte ptr ds:[si],97
jb next
cmp byte ptr ds:[si],122
ja next
transform:
and byte ptr [si],11011111b
next:inc si
jmp letterc
ok:ret
code ends
end start
實驗結果
第十二章 中斷
檢測點12.1
注意:中斷源是從0開始計數的,低位存的是偏移地址
1.
0號:00A7:1068
1號:0070:108B
2號:039D:0016
3號:0070:108B
2. 4n,4n+2
實驗十二 編寫0號中斷
assume cs:code
code segment
start:
;編寫程序
;復制程序到0000:0200處
;修改向量表
mov si,offset do0
mov ax,cs
mov ds,ax
mov di,0200h
mov ax,0
mov es,ax
mov cx,offset do0end-offset do0
cld
rep movsb
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0
mov ax,1000h
mov bh,1
div bh
mov ax,4c00h
int 21h
do0:jmp short do
db "divide error!";13字節
do:mov ax,0B800h
mov es,ax
mov si,160*12
mov cx,13
mov ax,cs
mov ds,ax
mov di,202h
s:mov al,ds:[di]
mov es:[si],al
inc di
inc si
inc si
loop s
mov ax,4c00h
int 21h
do0end:nop
code ends
end start
實驗結果,可以看到確實輸出了那行字~
第十三章 int指令
檢測點13.1
1.我寫的是65535.但是loop是短轉移指令,所以最大轉移是128(-128到127嘛,取最大絕對值)
2.程序裝載和修改向量表就沒寫了,只寫了中斷程序
push bp
mov bp,sp
dec cx
jcxz ok
add ss:[bp+2],bx;書上並沒有顯式指出段寄存器,我覺得還蠻奇怪的
ok:pop bp
iret
檢測點13.2
1.錯誤,網上說:FFFF:0 對應的指令只要在重新啟動的時候,就被重置為BIOS中的硬件系統檢測和初始化程序的指令[https://www.jianshu.com/p/4aed29e3bfc1]() 我也不懂!
2.錯誤,中斷例程19h是引導操作系統,dos操作系統不能自己引導自己
實驗十三 編寫,應用中斷例程
1.編寫int 7ch中斷例程,功能為顯示一個用0結束的字符串
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start:;安裝例程到0:200
mov ax,0
mov es,ax
mov di,200h
mov ax,cs
mov ds,ax
mov si,offset do0
mov cx,offset do0end-offset do0
cld
rep movsb
;更改向量表
mov ax,0
mov es,ax
mov word ptr es:[4*7ch],200h
mov word ptr es:[4*7ch+2],0
mov dh,10;行號
mov dl,10;列號
mov cl,2;顏色
mov ax,data
mov ds,ax;ds
mov si,0;ds:si指向字符串
int 7ch
mov ax,4c00h
int 21h
;編寫中斷例程
do0:push ax
push es
push di
push bx
mov bl,cl
mov ax,0B800h
mov es,ax
mov di,10*160+10*2;di是偏移地址
mov cx,16
s:mov al,ds:[si]
mov ch,0
mov cl,al
jcxz ok
mov es:[di],al
inc di
mov es:[di],bl
inc si
inc di
jmp short s
ok:pop bx
pop di
pop es
pop ax
iret
do0end:nop
code ends
end start
實驗結果
2.編寫並安裝int 7ch中斷例程,功能為完成loop指令
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start:;安裝例程到0:200
mov ax,0
mov es,ax
mov di,200h
mov ax,cs
mov ds,ax
mov si,offset do0
mov cx,offset do0end-offset do0
cld
rep movsb
;更改向量表
mov ax,0
mov es,ax
mov word ptr es:[4*7ch],200h
mov word ptr es:[4*7ch+2],0
mov ax,0b800h
mov es,ax
mov di,160*2
mov bx,offset s-offset se;bx存偏移量
mov cx,80
s:mov byte ptr es:[di],'!'
add di,2
int 7ch
se:nop
mov ax,4c00h
int 21h
;編寫中斷例程
do0:push bp
mov bp,sp
dec cx
jcxz ok
add ss:[bp+2],bx
ok:pop bp
iret
do0end:nop
code ends
end start
實驗結果
3.補全程序
mov bh,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
loop ok
第十四章 端口
檢測點14.1
1.
mov al,2
out 70h,al
in al,71h
2.
mov al,2
out 70h,al
mov al,0
out 71h,al
檢測點14.2
先邏輯左移1位得到2ax,經過兩次ax+=ax,得到8ax,再和前面的2ax相加即為所求
shl ax,1
mov bx,ax
add ax,ax
add ax,ax
add ax,bx
實驗十四 訪問CMOS RAM
assume cs:code
data segment
db "// ::",0
data ends
code segment
start:
mov bx,0b800h
mov es,bx
mov si,160*12
mov di,0
mov bx,data
mov ds,bx
;打印年
mov al,9
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印月
mov al,8
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印日
mov al,7
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印時
mov al,4
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印分
mov al,2
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印秒
mov al,0
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
mov ax,4c00h
int 21h
show:push ax
;打印函數,參數放在ax中
mov byte ptr es:[si],ah
add si,2
mov byte ptr es:[si],al
add si,2
mov al,ds:[di]
mov ch,0
mov cl,al
jcxz ok
mov es:[si],al
add si,2
inc di
ok:pop ax
ret
code ends
end start
實驗結果
第十五章 外中斷
檢測點15.1
1.
pushf
call dword ptr ds:[0]
2.
cli
mov word ptr es:[9*4],offest int9
mov es:[9*4+2],cs
sti
實驗十五
做了好久靠,關於這道題有個問題我一直沒想明白:es段寄存器是0,模擬調用int 9h時,為什么不可以是call dword ptr es:[200h], 段地址好像非得是cs才行,用es搞不了
assume cs:code
stack segment
db 128 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
push cs
pop ds
mov ax,0
mov es,ax
;為復制程序到0:204做准備
mov si,offset int9;ds:si指向源地址
mov di,204h;es:di指向目的地址
mov cx,offset int9end-offset int9;cx存放傳輸長度
cld;設置傳輸方向
rep movsb
;把原來中斷例程的地址先存起來
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
;設置向量表
cli
mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0
sti
;程序結束前把int 9h的中斷例程的入口地址恢復
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax,4c00h
int 21h
int9:push ax
push es
push di
push cx
in al,60h;讀入鍵盤緩沖區
pushf
call dword ptr cs:[200h];調用原來的int 9h例程
cmp al,9eh
jne ok;如果不是a松開,退出例程
mov ax,0b800h
mov es,ax
mov di,0
mov cx,2000
s:mov byte ptr es:[di],'A'
add di,2
loop s
ok:pop cx
pop di
pop es
pop ax
iret
int9end:nop
code ends
end start
實驗結果
第十六章 直接定址表
檢測點16.1
mov ax,a[si]
add word ptr b[0]
adc word ptr b[2]
add si,2
loop s
檢測點16.2
mov ax,data
mov es,ax
實驗十六 編寫包含多個功能子程序的中斷例程
一大坑:這道題不能直接按照書上的table直接定址(不能call word ptr table[bx]),下面分析原因。
debug忘記截圖了,這里借用一下網上的圖。(這是他的博客https://blog.csdn.net/zhangyuzuishuai/article/details/52331447
- 下圖是剛剛進入debug,安裝之前int 9ch例程的指令。call [bx+005E]表示的是call word ptr table[bx],這表示安裝前table在內存中的位置是cs:005E
- 下圖是安裝后,例程的指令。call指令沒有變
- 當CPU執行中斷例程的時候,執行到call時,去訪問cs:005E處的table,但是此時的cs已經改變了,所以是找不到table的真正位置的,因為我將中斷例程安裝到了0:200,table也復制過去了。
- table的真正位置在哪呢?例程中的第一條指令是jmp,后面才是table,所以執行例程時,應該去0:202找table。因此我用的是call word ptr cs:[202h+bx]
二大坑:數據標號在編譯的時候就變成了定值常數,不能直接table dw sub1,sub2,sub3,sub4
- 因為sub1,sub2....那些編號都代表着安裝前,sub1,sub2...的偏移地址。當執行中斷例程的時候,cs段地址已經改變了,因此這個偏移地址是不對的
- 於是我把int7c,sub1,sub2...全部復制過去了,利用int7c和sub1,sub2.....的相對距離調用程序
assume cs:code
stack segment
db 128 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
push cs
pop ds
mov ax,0
mov es,ax
;為復制程序到0:200做准備
mov si,offset int7c;ds:si指向源地址
mov di,200h;es:di指向目的地址
mov cx,offset int7cend-offset int7c;cx存放傳輸長度
cld;設置傳輸方向
rep movsb
;設置向量表
mov word ptr es:[124*4],200h
mov word ptr es:[124*4+2],0
;調用函數
;mov ah,0
;mov al,0
;mov ah,1
;mov al,5
mov ah,3
int 7ch
mov ax,4c00h
int 21h
int7c: jmp short begin;安裝后這條指令的地址是0:200
table dw offset sub1-offset int7c +200h
dw offset sub2-offset int7c +200h
dw offset sub3-offset int7c +200h
dw offset sub4-offset int7c +200h
begin: push bx
cmp ah,3
ja ok
mov bl,ah
mov bh,0
add bx,bx
call word ptr cs:[202h+bx];table 所在的位置是0:202
ok: pop bx
iret
sub1: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0;偏移地址
mov cx,2000;循環次數
sub1s: mov byte ptr es:[bx],' '
add bx,2
loop sub1s
pop es
pop cx
pop bx
sub2: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1;因為是奇地址,起始偏移地址為1
mov cx,2000
sub2s: and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret
sub3: push bx
push cx
push es
mov cl,4
shl al,cl;左移4位,為后面與運算做准備
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s: and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub3s
pop es
pop cx
pop bx
ret
sub4: push cx
push si
push di
push es
push ds
mov si,0b800h
mov es,si
mov ds,si
mov si,160;ds:si指向第n+1行
mov di,0;es:di指向第n行
cld
mov cx,24
sub4s: push cx;暫存cx,因為里層循環還需要用到
mov cx,160
rep movsb
pop cx
loop sub4s
mov cx,80
mov si,0
sub4sl: mov byte ptr [160*24+si],' ';處理最后一行
add si,2
loop sub4sl
pop ds
pop es
pop di
pop si
pop cx
ret
int7cend:nop
code ends
end start
第十七章 使用 BIOS進行鍵盤輸入和磁盤讀寫
檢測點17.1
對,如果沒有IF=1的指令,當IF=0時,將不能響應int 9中斷例程,無法向鍵盤緩沖區填寫字符。如果int 16h沒有檢測到鍵盤緩沖區的字符,而int 9例程又不能寫入,int 16h將一直執行(等待等不到的用戶輸入
實驗十七 應用int 13h中斷例程對磁盤進行讀寫
這道題好像沒給出具體的東西,似乎只是紙上談兵,不需要上機驗證?(沒有驗證,下面代碼不保證能運行哦
assume cs:code
code segment
start:
push cs
pop ds
mov ax,0
mov es,ax
;為復制程序到0:200做准備
mov si,offset int7c;ds:si指向源地址
mov di,200h;es:di指向目的地址
mov cx,offset int7cend-offset int7c;cx存放傳輸長度
cld;設置傳輸方向
rep movsb
;設置向量表
mov word ptr es:[124*4],200h
mov word ptr es:[124*4+2],0
;象征性地調用一下中斷例程7c,參數隨便設置的
mov es,0
mov bx,1
mov ax,0
mov dx,1
int 7c
mov 4c00h
int 21h
int7c: push ax
push bx
;用被除數為16位,放在ax中
mov ax,dx
mov bx,1440
div bx;商存在al,余數在ah
push al;這是面號,先存起來
mov bx,18
mov al,ah
mov ah,0
div bx;邏輯扇區號/1440的余數/18,商在al,余數在ah
push al;這是磁道號,先存起來
mov al,ah
mov ah,0
add ax,1
;ax為扇區號
;下面開始初始化int 13h的參數
mov cl,al;扇區
pop ch;磁道號
pop dh;面號
mov dl,0;讀寫軟盤
mov al,1;扇區數,隨便設置了一個
pop bx
pop ax
cmp ax,0
je read
cmp ax,1
je write
read: mov ah,2
int 13h
jmp short ok
write: mov ah,3
int 13h
ok: iret
int7cend:nop
code ends
end start