首页 > 编程语言 >《汇编语言》检测点和实验第四版

《汇编语言》检测点和实验第四版

时间:2023-03-12 16:33:12浏览次数:63  
标签:汇编语言 mov al si 第四版 ax 检测点 bx es

一二三章实验环境配置见: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

标签:汇编语言,mov,al,si,第四版,ax,检测点,bx,es
From: https://www.cnblogs.com/tangxibomb/p/17208424.html

相关文章

  • 汇编语言学习_5_包含外部文件
    第五节包含外部文件翻译自:https://asmtutor.com/外部包含文件允许我们从我们的程序中移动代码并将其放入单独的文件中。这种技术对于编写干净、易于维护的程序很有用。......
  • 汇编语言语句格式
    通常一个语句常占一行(支持续行符“\”)一个语句不超过132个字符,4个部分执行性语句:表达处理器指令,实现功能标号:硬指令助记符操作数,操作数;注释说明性语句:表达伪指令,控......
  • 汇编语言学习_3_计算字符串长度
    第三节计算字符串长度翻译自:https://asmtutor.com/背景知识为什么我们需要计算字符串的长度?好吧,sys_write要求我们向它传递一个指向我们要在内存中输出的字符串的指......
  • 汇编语言学习_4_函数
    第四节函数翻译自:https://asmtutor.com/介绍函数(子程序)函数/子程序(functions/subroutines)是可重用的代码片段,程序可以调用它们来执行各种可重复的任务。函数是使......
  • 汇编语言学习_2_正确的退出方式
    第二节正确的退出方式翻译自:https://asmtutor.com/背景知识在Lesson1中成功学习了如何执行系统调用之后,现在需要学习内核中最重要的系统调用之一sys_exit。请注......
  • 第十章 通过汇编语言了解程序的实际构成
        计算机CPU能直接解释运行的只有本地代码(机器语言)程序。用C语言等编写的源代码,需要通过各自的编译器编译后,转换成本地代码。    在加法运算的本地代码......
  • 第十章通过汇编语言了解程序的实际构成
    这一章所讲的的是通过汇编语言来理解程序的运行过程。汇编语言的源文件的扩展名通常用“.asm”来表示。汇编语言的源代码,是由转换成本地代码的指令和针对汇编器的伪指令构......
  • Intel汇编语言程序设计笔记
    ⦁2^8=2562^10=10242^16=65536[二进制]1111=F[16进制]⦁ 有符号二进制整数的最高有效位[MSB]表示数的符号,0=正数1=负数⦁ 数据的意义,由其数据类型决定,单纯的数字没......
  • 《程序是怎样跑起来的》·第十章 通过汇编语言了解程序的实际构成
    阅读正文前,让我们先回答下面的问题来热热身吧:本地代码的指令中,表示其功能的英语缩写称为什么汇编语言的源代码转换成本地代码的方式称为什么?本地代码转换成汇编语言的......
  • 微机原理与系统设计笔记4 | 汇编语言程序设计与其他指令
    打算整理汇编语言与接口微机这方面的学习记录。本部分介绍汇编语言程序设计以及一些跟程序设计密切相关的指令类。参考资料西电《微机原理与系统设计》周佳社西交......