《匯編語言第四版檢測點和實驗》


一二三章實驗環境配置見: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

  1. 答案:mov cs:[bx],cx
  2. 答案: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
  • 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM