汇编4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
assume cs:codesg
codesg segment

mov ax,4c00h
int 21h

start:
mov ax,0
s:
nop
nop

mov di,offect s
mov si,offect s2
mov ax,cs:[si]
mov cs:[di],ax

s0:
jump short s

s1:
mov ax,0
int 21h
mov ax,0

s2:
jmp short s1
nop

codesg ends
end start

这段代码首先用
mov di,offect s
mov si,offect s2
mov ax,cs:[si]
mov cs:[di],ax
将s2处代码写到s处
然后接着执行s0,跳到s,执行被写入的jmp short s1
关键来了
汇编中jmp short X的本质是由编译器计算出当前地址举例要跳转标号的偏移值
再进行相对偏移;
所以jmp short s1实际是向前跳8个字节的意思(mov ax,0 int 21h mov ax,0这三条指令长8个字节)到原来的s1
但现在这句话被简单拷贝在了s处,电脑则也只是向前跳8个字节(不管前面到哪里),所以ip指针会向前执行到mov ax,4c00h处(mov ax,4c00h int 21h mov ax,0也是8个字节长)最后结束



另一个实用的程序
属性字节格式:

enter description here
(二进制)
16*16=256=2^8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
assume cs:code,ds:data

data segment
dw 1920,2080,2240,64;位置 共2*4=8
db 'welcome to dyinj';显示字符 共16
db 82h,0ach,0f9h;属性字节(不能字母开头)
data ends

code segment
start:
mov ax,data
mov ds,ax;数据段该去哪去哪

mov ax,0B800h;从此处开始的二维数组来代表屏幕显示(显存)
mov es,ax;附加段存字符显示段位置

mov cx,3;三行
xor di,di
xor si,si
s1:
;控制属性和位置的行循环
mov ax,di;ds:di行位置1920,+2=2080,+4=2240
mov bl,2
div bl;将ax除二
mov si,ax
mov ah,[si+24];24+二分之di为属性字符开始82h,0ach,0f9h 即属性码

mov si,ds:[6];列位置64
mov bp,[di];行位置1920,2080,2240

mov dx,cx;保存cx
mov bx,0

mov cx,16
s2:
;控制显示字符的列循环
mov al,[bx+8];welcome的w开始 即字符码

mov es:[bp+si],al;存入16位单字符码到显存低字节
mov es:[bp+si+1],ah;存入16位属性码到显存高字节
inc bx
add si,2
loop s2

mov cx,dx;还原保存的cx
add di,2
loop s1

mov ax,4c00h
int 21h
code ends
end start

ret/retf

和上一章不同在于用的是栈
pop栈中数据给指令指针
回忆:PUSH在存放数据时 SP先减二再取数据;POP 先拿数据再SP加二

ret

指令用栈中的数据,修改IP的内容,从而实现近转移

  1. (IP) = ((ss)*16 + (sp))
  2. (SP) = (sp) + 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume cs:code

stack segment
db 16 dup(0)
stack ends

code segment
mov ax, 4c00h
int 21h
start:
mov ax, stack
mov ss, ax

mov sp, 16;注此时不在栈中
mov ax, 0
push ax
mov bx, 0
ret
code ends

end start

retf

指令用栈中的数据,修改CS和IP的内容,从而实现远转移

  1. (IP) = ((ss) * 16 + (sp))
  2. (SP) = (sp) + 2
  3. (CS) = ((ss) * 16 + (sp))
  4. (SP) = (sp) + 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code

stack segment
db 16 dup(0)
stack ends

code segment
mov ax, 4c00h
int 21h
start:
mov ax, stack
mov ss, ax

mov sp, 16
mov ax, 0
push cs
push ax
mov bx, 0
retf
code ends

end start

call

call

  1. 将当前的ip或cs和ip压入栈中
  2. 转移

比jmp多了压栈

call s(标号)

(sp)=(sp)-2
((ss)*16+(sp))=ip
(ip)=(ip)+16位位移

16位位移=标号处地址-call指令后第一个字节的地址
范围-32768~332767

call far ptr s(标号)

(sp)=(sp)-2
((ss)*16+(sp))=(cs)
(sp)=(sp)-2
((ss)*16+(sp))=(ip)
(cs)=标号所在段地址
(ip)=标号所在段中的偏移地址

先压的是cs后压ip

call reg(16位寄存器)

(sp)=(sp)-2
((ss)*16+(sp))=(ip)
(ip)=(16位寄存器)

call word ptr s(标号)/call dword ptr s(标号)

函数

call和ret组合使用
求2的3次方存入bx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
assume cs:code
code segment
start:
mov ax,1
mov cx,3
call s
mov bx,ax

mov ax, 4c00h
int 21h
s:
add ax,ax
loop s
ret
code ends
end start

模块化

n的三次方

1
2
3
4
5
cube:
mov ax,bx
mul bx
mul bx
ret

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
assume cs:code

data segment
dw 1,2,3,4,5,6,7,8;单字节长
dd 0,0,0,0,0,0,0,0;双字节长
data ends

code segment
start:
mov ax,data
mov ds,ax
mov si,0;123开始
mov di,16;000开始

mov cx,8
s:
mov bx,[si];参数bx填入
call cube
mov [di],ax;得到返回值ax
mov [di+2],dx;返回值dx(如果是16位乘法,结果高位默认在dx中存放,低位在ax中存放。见下)
add si,2
add di,4
loop s

mov ax,4c00h
int 21h

cube:
mov ax,bx
mul bx
mul bx
ret

code ends
end start

mul

  1. 两个相乘的数:要么都是8位,要么都是16位。如果是8位,一个默认放在al中,另一个放在8位reg或内存字节单元中;如果是16位,一个在AX中,另一个在16位reg或内存字单元中。
  2. 结果,如果是8位乘法,结果默认放在ax中;如果是16位乘法,结果高位默认在dx中存放,低位在ax中存放。

格式:mul reg或mul 内存单元

1
2
3
mov al, 5h;AX = 0005h
mov bl, 10h
mul bl; AX = 0050h
---------------THEEND---------------