10.21汇编

Posted on Oct 21, 2020

In These Arms

今天看了两章,下午由于一些事耽搁了,本来其实还可以看更多,但总体还是满意的

第十一章是讲标志寄存器,说实话这个感觉主要靠记,而我还真记不下来

标志寄存器被称为flag,其中共有9个标志,存在0,2,4,6,7,8,9,10,11位中,别的位无意义。

ZF

零标志位,形如and ax,0,sub ax,ax这样的产生运算了的指令之后会根据运算的结果改变值,当结果位零时zf置1,反之置0。传送指令大多不对zf产生影响

PF

奇偶标志位,仍是产生运算时改变,若一的个数位偶数pf置1,反之置0。

SF

符号标志位,检测相关指令执行后结果是否为负,若为负sf置1,反之置0。

CF

进位标志位,若出现进位或借位则置1,常配合adcsbb(带位运算)使用

比如若要进行32位运算,就可以

add ax,cx
adc bx,dx
;ax,cx为低位,bx,dx为高位
;sbb也类似

所以可以看出,cf主要涉及无符号数运算

OF

针对有符号数的溢出标志位

cmp指令

cmp 操作对象1,操作对象2进行操作对象2-操作对象1的运算,但不保留结果,目的是影响标志寄存器flag。配合条件转移语句即可实现if的效果

指令含义检测的标志位
je等于则转移zf=1
jne不等于则转移zf=0
jb低于则转移cf=1
jnb不低于则转移cf=0
ja高于则转移cf=0且zf=0
jna不高于则转移cf=1或zf=1

e=equal,n=not,b=below,a=above,很好记

DF和串传送指令

这个就厉害了,df是方向标志位,在串处理指令中控制si,di的增减

df=0 si++,di++

df=1 si--,di--

cld将df置0,std将df置1

而movsb则可以做到

mov es:[di],byte ptr ds:[si];这个指令当然是不对的,内存不能直接传内存,只是表达效果
inc di
inc si
;假设df=0

的效果

movsw是以字为单位移动,也差不多。

一般来讲,为了控制移动的长度,会加上rep,如rep movsb,当cx==0是结束转移,类似与loop

pushf和popf

将标志寄存器压栈、出栈以直接访问标志寄存器。

实验11

;convert every and only lower-case to capital letter
assume ds:data,ss:stack,cs:code

data segment
    db "auSDFGofh gQWERaiSDoGsu ASdSDhf1y45gSD192459 te0g13894g!!!",0
data ends

stack segment
    dd 4 dup(0)
stack ends

code segment
main:
    ;sreg init
    mov ax,data
    mov ds,ax
    mov ax,stack
    mov ss,ax
    mov sp,16

    call letterc

    mov ax,4c00h
    int 21h

letterc:
    push ax
    push bx
    push cx

    mov bx,0
    mov ch,0
    mov cl,ds:[bx]
    s_letterc:
        jcxz end_of_string_letterc
        mov al,ds:[bx]
        mov cl,al
        cmp al,97
        jb not_lowercase_letterc
        cmp al,122
        ja not_lowercase_letterc
        and al,11011111b
        mov ds:[bx],al
        not_lowercase_letterc:
        inc bx
    jmp short s_letterc
    end_of_string_letterc:

    pop cx
    pop bx
    pop ax
ret
code ends

end main

一个比较简单的实验

内中断

内中断没有我想象中的难,cpu实现内中断的方法是通过中断向量表记录个中断处理程序的段地址和偏移地址。中断向量表的段地址就是零,偏移地址从0000到03FF,总共可以存储256个中断向量。每个向量占两个字,高地址字存段地址,低地址字存偏移地址。cpu根据中断码到向量表中查询目标地址,以执行中断程序。

过程为:

取得中断码N
pushf
TF=0,IF=0
push CS
push IP
(IP)=(N*4),(CS)=(N*4+2)

中断处理程序就可以通过

保存寄存器
处理
恢复寄存器
iret
iret类似与
pop IP
pop CS
popf

由此就实现了中断后返回

浅谈TF

单步中断是如何做到的?如果TF为1,cpu就进行单步中断(中断码为1)。如果执行中断程序之前不对TF置零,显然就麻烦了——永远都会处于单步中断的过程中。所以中断前要对TF置零。由此,其实debug的调试原理就很好理解了。提供TF标志位也就是为了提供单步调试功能

实验12

;display 'divide error!' in the middle of the screen when the div overflow error occured
assume cs:code

code segment
main:
    ;---------------------------------setup err0---------------------------------
    mov ax,0
    mov es,ax
    mov ax,cs
    mov ds,ax
    mov cx,offset end_of_err0-err0
    mov si,offset err0
    mov di,200h
    cld
    rep movsb
    ;set err vector
    mov ax,0
    mov es,ax
    mov es:[0*4+0],word ptr 200h
    mov es:[0*4+2],word ptr 0h
    ;---------------------------------setup err0---------------------------------
    ;trigger the err0
    mov ax,0fffh
    mov cx,1
    div cx

    mov ax,4c00h
    int 21h
err0:
    jmp short err0_start
    db 'divide error!'
    err0_start:
    ;------------------------------clear the screen------------------------------
    mov bx,0b800h
    mov es,bx
    mov bx,0
    mov cx,2000
    s_clear:
        mov word ptr es:[bx],0
        add bx,2
    loop s_clear
    ;------------------------------clear the screen------------------------------
    ;--------------------------dispaly error information-------------------------
    mov ax,0
    mov ds,ax
    mov ax,0202h
    mov si,ax
    mov ax,0b800h
    mov es,ax
    mov di,160*12+80-12
    mov cx,13
    mov ah,00100100b
    s0_err0:
        mov al,ds:[si]
        mov es:[di].0,al
        mov es:[di].1,ah
        inc si
        add di,2
    loop s0_err0
    ;--------------------------dispaly error information-------------------------
    ;wat:jmp short wat
    mov ax,4c00h
    int 21h
end_of_err0:nop
code ends

end main

仍然算比较简单