更优雅的打印字符串

目标:给定一个以0x00结尾的字符串在内存中的起始地址,通过调用一个函数,把整个字符串输出到屏幕上。

ds和es是段寄存器,si和di是变址寄存器

通过段寄存器DS以及源索引寄存器SI来保存字符串的起始地址,即DS:SI,而SI指向第一个字符出现的位置,取出一个字符SI就加1。通过段寄存器ES以及目标索引寄存器DI,保存写入的目标地址,也就是显存的地址,ES的值位0xb800,DI寄存器的初始值位0x0000,写入一个字节就加1, 通过循环将DS:SI指向的字符串依次取出,并写入到ES:DI指向的目标地址,直到遇到0x00结束循环

Screenshot_20220110_220208.png

EFLAGS寄存器

第0位CF位,进位标志

第2位PF位,偶数标志位,当计算结果是偶数时,PF会被置1

第6位是ZF位,Zero Flag,零标志位,独立结果为0时,ZF会被置1

第7位是SF位,也就是符号位,比如减法结果为负值,他就会被置1

第11位是OF位,也是溢出位,当计算结果存在溢出时,它会被置为1

Screenshot_20220110_220309.png

标志位的用法,要结合条件转移指令使用

JCC-条件转移指令

jz Jump if zero(ZF=1)

jnz Jump if not zero(ZF=0)

cmp-比较指令

cmp dest目的(reg/mem) source源(reg/mem/imm)

比较两个数,改变标志寄存器的值

Screenshot_20220110_221101.png

section-分段

section name align=16/32 vstart=0

Screenshot_20220110_221417.png

align:加align与不加align的区别在于,如果定义多个section,而某些section的内容不足16字节,编译器就会给你补齐到16字节,而不加align参数就没有这个效果

Screenshot_20220110_221540.png

vstart:这个参数的用途是设置段内的偏移地址的基准地址,如果不设置这个参数,那么在编译阶段,所有的偏移地址都是按照程序头部来算的,而默认的程序头部基准地址为0x0000,如果我们使用了标号,那么标号的偏移地址,也就是汇编地址,是跟程序加载到内存的偏移地址保持一致的。在真机运行环境,我们的程序会被加载到0x7c00位置,并且段寄存器如ES,DS,SS等都默认初始化位0x0000,但是程序的起始位置却是0x7c00,这就导致程序里的标号不对了。解决办法有两种,一种是把DS,ES,SS都初始化为0x7c00,另外一种是使用vstart参数,让vstart=0x7c00,汇编地址在计算的时候会自动加上0x7c00。

call指令

call 标号

类似函数调用,call 后面跟一个标号、寄存器或者内存地址,标号的本质也是个内存地址偏移。标号的本质也是一个内存地址偏移。我们把常用的一段代码放到标号后面,形成类似函数的代码块。需要注意的是,在代码块的最后,要加一个ret指令,就是return。代码先顺序执行,遇到call指令调用代码块,遇到ret返回调用点,汇编用寄存器来保存传入传出参数。

and-与运算指令

and dest目的(reg/mem) source源(reg/mem/imm)

Screenshot_20220110_223603.png

or-或运算指令

or dest目的(reg/mem) source源(reg/mem/imm)

Screenshot_20220110_223654.png

or bh,0x000,通过ZF可以判断bh是否为0

not-非运算指令

not reg/mem

Screenshot_20220110_223836.png

xor-异或运算指令

xor dest目的(reg/mem) source源(reg/mem/imm)

Screenshot_20220110_223927.png

影响ZF标志位,也可以通过ZF判断两个数是否相等

打印字符串

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
NUL equ 0x00
SETCHAR equ 0x07
VIDEOMEM equ 0xb800
STRINGLEN equ 0xffff

section code align=16 vstart=0x7c00
mov si, SayHello
xor di, di
call PrintString
mov si, SayByeBye
call PrintString
jmp End

PrintString:
.setup:
mov ax, VIDEOMEM
mov es, ax

mov bh, SETCHAR
mov cx, STRINGLEN

.printchar:
mov bl, [ds:si]
inc si
mov [es:di], bl
inc di
mov [es:di], bh
inc di
or bl, NUL
jz .return
loop .printchar

.return
ret

SayHello db 'Hi there,I am Ansore!'
db 0x00
SayByeBye db 'I think you can handle it, bye!'
db 0x00

End: jmp End
times 510-($-$$) db 0
db 0x55, 0xaa

运行结果

Screenshot_20220111_210706.png