汇编语言_1 开始写程序;LOOP 指令

开始写程序

代码示例

写好汇编代码后,借助 masm.exe 程序编译,产生 .obj 文件,再使用 link.exe 连接生成可执行文件。

我们写的指令包括伪指令,没有对应的机器码的指令,由编译器处理;和汇编指令,编译为机器码。

一个汇编程序由多个段(至少一个)组成,我们知道段是我们自己定义的逻辑上以16B为单位的划分。

形如:

XXX segment #段开始
XXX ends 	#段结束
end			#汇编程序的结束标记

assume:假设某个寄存器和某个段相关联。

编写程序思路:

  1. 定义段。
abc segment

abc ends
end
  1. 编写实现功能
mov ax,2
add ax,ax
add ax,ax
add ax,ax
  1. 段与段寄存器关联。我们要指定这个段和哪个段寄存器相关联。
assume cs:abc

DOS 程序运行

DOS 是单任务操作系统。如果当前是P1程序在运行,想切换到P2,需要P1把P2加载入内存,把 CPU 控制权交给P2,然后P2才能运行,P1暂停运行直到P2结束运行控制权交还给P1。这个过程叫程序返回。

mov ax,4c00H
int 21H

这两条指令就是程序返回。这条指令不是编译器执行,而是CPU执行。

注意,前面的指令都要用 t 逐条执行,表示 step into。int 21 指令要用 p 命令执行,表示 step over。

因此第一个程序完整内容为:

assume cs:abc
abc segment
	mov ax,2
    add ax,ax
    add ax,ax
    add ax,ax
    mov ax,4c00H
	int 21H
abc ends
end

命名为.asm文件。

扫描二维码关注公众号,回复: 14873928 查看本文章

asm 文件用 masm.exe 编译,obj 文件用 link.exe 链接,或者直接用 ml 编译和链接。

编译是把文件编译为机器码文件,链接是把其和相关文件、相关库链接起来,方能生成可执行文件。

灵魂三问:

  1. 是什么程序把我们的程序加载进 CPU 并运行?DOS 的 command 命令行。如果是用 debug 调试运行的,那么就是 debug 加载程序并运行。
  2. CPU 如何使得程序得以运行?command 设置 CS:IP。
  3. 运行结束后到哪里去?回到 command。

输入文件名直接执行,如我们链接完的程序叫 1.exe,直接输入 1.exe 运行。输入 debug 1.exe 进入调试,可以逐行运行。

开始调试时可以发现,其他通用寄存器都初始化为0000,只有 cx 有值。这是因为 cx 初始化值为程序的长度。

然后可以发现CS=DS+0010H,段地址相差0010H,则物理地址相差 100H 即256,也就是 CPU 自动分配了256个字节给DS。这256字节里存放的东西叫 PSP,是 DOS 用于和程序通信的,了解即可。DS+256 才是存放程序的 CS.

因此 PSP 的段地址 SA 可从 DS 中得到。PSP 物理地址范围是 SA * 16+0~(SA+0010H) * 16+0-1.

(SA+16) * 16+0 开始是 CS 的范围了。

BS LOOP

正常情况下 mov ax, [0] 表示把 ds:0 的数据传入 ax。但是我们的编译器 (ml) 会把其翻译为 mov ax, 0 进而产生错误。

解决办法:在 ml 中我们先把数据传入 bx,然后 mov ax, [bx] 。或者 mov ax ds:[0] 。或者直接在 debug 中写程序,用 a ,这样写出的程序无该问题。

assume cs:codesg
codesg segment
proj:   mov ax, 2000H
        mov ds, ax
        mov bx, 1000H
        mov ax, [bx]
        inc bx
        inc bx
        mov [bx], ax
        inc bx
        inc bx
        mov [bx], ax
        inc bx
        mov [bx], al
        inc bx
        mov [bx], al

        mov ax, 4C00H
        int 21H

codesg ends
end proj

isc:字+1。

上面的函数做的操作:把 21000H 处的值赋给 ax,再赋给 21002H,21004H……

比如我最开始 21000H 值是 FF,21001H 值是 46,赋值结果为:

image-20230411141015170

就是复制了几遍值。

汇编中可以通过一个寄存器值实现 loop 循环,值=0时停止,!=0 一直继续。

例:计算 2^12.

assume cs:code
code segment
    mov ax, 2
    mov cx, 11
s: add ax, ax
    loop s
    mov ax, 4c00h
    int 21h
code ends
end

cx 是 loop 用的寄存器,自动每轮循环–,=0时退出循环。因此赋值多少就循环几次。

使用 loop 对 s 标号名里的内容进行循环。

例:123*236.

当然可以循环236次,但是更优的算法是ax不断加倍,算到 ax 的128位之后再循环剩余的次数。

注意16位寄存器和8位数据之间传递的关系,直接传可能报错,要传给 h or l。

还有一个要注意的地方:汇编中数据不能以字母开头,比如 ffffh 直接写不对,开头要加个0:0ffffh。

当然这样循环次数太多,写代码的时候我们可以用 loop 省略,debug 的时候还是要一行一行执行,有没有快捷方法?

image-20230411152703102

比如上面,我们可以看到 0014 偏移地址结束循环。

g 0014p 都可以执行完当前循环。

例:ffff0:ffff11 12个地址,把值全部累加到 ds 中。

我们知道 ds 是16位的寄存器,这12个地址里是12个字节数据,直接传入会出错。

那可不可以全部传入 ds 的低位 dl?不可以,会溢出。

那怎么办?可以先传入每个值给一个16位的中间寄存器, 那个寄存器加到 dx。

mov al, ds:[0]
mov ah, 0
add ds, ax

mov al, ds:[1]
mov ah, 0
add ds, ax

mov al, ds:[2]
mov ah, 0
add ds, ax

安全空间

我们随便去赋值,访问操作系统空间是有危险的。直接操作硬件就是会有这种风险。

内存空间中 0:200h 到 0:2ffh 是安全空间。以防万一可以先 d 一下看看里面的内容。

例:把 ffff:0 11 的值复制到 0020:0 11 处。

assume cs:code
code segment
    mov bx,0
    mov cx,12
s:  mov ax,0ffffh
    mov ds,ax
    mov dl,[bx]
    mov ax,0020h
    mov ds,ax
    mov [bx],dl
    inc bx
    loop s

    mov ax, 4c00h
    int 21h
code ends
end

不过这样换=两次 ds 比较麻烦。我们可以使用 es 附加寄存器存储。

assume cs:code
code segment
    mov bx,0
    mov cx,12
    mov ax,0ffffh
    mov ds,ax
    mov ax,0020h
    mov es,ax
s:  mov dl,[bx]
    mov es:[bx],dl
    inc bx
    loop s

    mov ax, 4c00h
    int 21h
code ends
end

猜你喜欢

转载自blog.csdn.net/jtwqwq/article/details/130095943