王爽版--汇编语言

计算机组成原理之机器

各进制以及各进制之间的互相转换

二进制和机器指令和汇编语言的关系

汇编指令:通过编译器将汇编指令转化为01串。

伪指令:由编译器执行,指示编译器这边怎么运行,那边怎么运行。没有对应的机器指令。

符号体系:有编译器执行,没有对应的机器指令。

机器指令存放的地方

存放于内存之中。

command–debug–u–d

内存在存储器中的编号从0开始,内存大小 以字节(一个存储单元)为单位。

地址线、数据线、控制线

地址线的多少决定寻址能力的大小。

表示一个字节需要八根控制线。

控制线决定了cpu能够对哪些部件进行控制。

测验:

一个cpu的寻址能力为8kB,那么他的地址总线宽度应该为13.

解答:寻址能力为8kB,即可以寻找8k个存储单元,其中一个存储单元为一个字节。即需要13个地址线去表示8k个地址。

进一步理解内存

几乎所有部件都有内存,包括键盘鼠标。

cpu将其他部件都视为内存,进行数据的交换。

rom断电情况下也是存有数据的,ram断电数据就都消失了。

AX=AH+AL h:high l:low ah和al是相互独立的,为了满足16位兼容8位

BX=BH+BL

CX=CH+CL

DX=DH+DL

通用寄存器,一般存放数据。

-a
-mov ax,4e20
-mov bh,ah
-r
-t
-r
-t

进一步理解ax,bx,cx,dx寄存器

mov ax,18h

mov ah,78h

add ax,8h

mov bx,ax

add ax,bx

结构:

ax = 0018h

ax = 7818h

ax = 7820h

bx = 7820h

ax = f040h

实验:

-a
-mov ax,18h
-mov ah,78h
-add ax,8h
-mov bx,ax
-add ax,bx
-r
-t
...

地址寄存器和地址的组合

cpu存放段地址信息的寄存器为:ds,es,ss,cs

cpu存放偏移地址信息的寄存器为:bx,sp,bp,si,di,ip

古老的cpu8086有20个地址线。寄存器只能表示16位。所以计算机引用了段地址和偏移地址。

基础地址=段地址*10H

物理地址=基础地址+偏移地址

CPU通过地址寄存器区分指令和数据

cpu将cs:ip所组合出来的地址里面的内容全部当作指令

指令执行的过程:

  1. cpu从cs:ip所组成的地址中读取指令,将这个指令存放到指令缓存器中
  2. ip=ip+所读指令的字节数
  3. 执行指令缓存器中的内容,再回到步骤1

检测题:

在下面的代码中,寄存器ip的值修改了几次,最终ip的值是多少?
  mov ax,bx
  sub ax,ax
  jmp ax        读取指令->ip=ip+所读指令的字节数->执行指令内容(再次修改ip)
答:4次,最终值为0

调试指令

r                              read 显示寄存器和寄存器中的值的键值对
r 寄存器名                      修改寄存器中的值
d 显示后面128组字节的值
d 段地址:偏移地址               显示该地址后的128组的值
d 段地址:偏移地址 F              显示该地址后的16组的值  即一行
u                              将后续的字节翻译成汇编指令,后面可以跟上参数段地址:偏移地址
a                              向内存中写入汇编指令,后面可以跟上参数段地址:偏移地址
e                              修改内存中的值,后面可以跟上参数段地址:偏移地址,可以连续内存的值一起修改,例如:e 10000 “abcd”
t                              执行当前指令

高位地址存放高位字节。

数据的读取

ds存放了段地址信息,ds又叫做段寄存器。

mov al,ds:[0]           //这句话的意思是将ds寄存器中的段地址和偏移地址0组合成的物理地址中的值赋给al,该值的长度取决于al的长度。  注:ds:[0],省略ds:在虚拟机中可以通过,但是在生产环境下是不可以省略的。
/*****将al中的数据送入内存单元10000h当中
mov bx,1000
mov ds,bx
mov ds:[0],al
******/

出栈    pop   字型数据

入栈    push  字型数据

栈指针永远指向栈顶元素。

和栈有关的寄存器:段寄存器ss 偏移地址寄存器sp

栈是先进后出的。

可以一直push或者pop,超过了你自己规定的栈大小,称之为越界。

一个栈最多能够存放多少个字型数据

sp偏移地址决定了栈的寻址能力,所以最大寻址能力为FFFF

所以最多能够存放 256*256/2个字型数据。

设置了最大的栈内存,push满了后再去push还是会越界,越界的部分会替换掉重叠的部分。

比如10000h会替换掉内存0000h的数据。

push ax ==> sp-2 ==> ax 存放到 ss:sp-2

pop ax ==> ss:sp 存放到 ax ==> sp+2

注:如果ss为2000,sp为0,push ax,其中ax=1111

则1fff:000e和1fff:000f不会变成1111.暂时猜想是越界问题。

猜想正确,因为越界问题所以造成循环,最后 2fff:fffe和2fff:ffff变成1111.

loop和cx寄存器

计算123*236
assume cs:code
code segment

    mov ax,0
    mov cx,123
addNumber:  add ax,236
    mov ax,1000H
    mov dx,2000H
    loop addNumber
    mov ax,4C00H
    int 21H

code ends
end

内存安全

mov dx:[26H],ax

这句话会报错。在运行汇编程序的时候,一般都是在保护模式下,所有很多数据受保护的。无法写入。

更不能在实模式下随便写入内存,因为这样可能会篡改了受保护的重要的数据。

可用内存范围 0000:0200H ~~ 0000:02FFH 256个字节

0000:7E00H ~~ 暂不确定,一般在这里不会超出范围。

定义数据空间

dw 是定义字型数据,db是定义字节型数据

START就是一个标号,标志程序的入口而已,程序加载到内存之后CS:IP会指向这个标号,从START指向的指令开始运行,这个标号不一定是START,你也可以用MAIN,但在程序的最后要用END MAIN来提示程序结束。

使用寄存器间接寻址时,只可以使用 BX, BP, SI, DI 这四个寄存器中的一个,不可以使用其它寄存器。

可以ds:[bx+si]或者ds:[bx+di],但是不可以ds:[bx:di]

ds和bx 相当于 ss和bp

assume cs:code

data segment
            dw 0123H,4567H,890AH
data ends

stack segment stack
            dw  0,0,0
stack ends

code segment
start:      mov ax,stack
            mov ss,ax
            mov sp,6

            mov ax,data
            mov ds,ax

            mov bx,0
            mov cx,3

movSegment:         push ds:[bx]
            add bx,2
            loop movSegment

            int 21H

code ends
end start

如果数据字段存放与内存中的大小小于16倍数,则利用00H补全到16的倍数。

上面程序加载后,如果code段的段地址为x,则data段的段地址为x-2,stack段的段地址为x-1.

mov word ptr ds:[0],1

inc word ptr ds:[0] //字型数据加1

div word ptr ds:[4] //必须加上word ptr,不然计算机判断不出来到底要以多大长度为单位处理数据。

div 除法指令

1.除数
    8bit,16bit 存在内存单元中或者寄存器中
2.被除数
    如果除数是8bit,那么被除数为16bit,存放在ax中
    如果除数是16bit,那么被除数为32bit,ax存放低16bit,dx存放高16bit
3.结果
    如果除数是8bit,那么al存商,ah存放余数
    如果除数是16bit,那么ax存商,dx存放余数。

dup 重复

stack segment stack

​ db 128 dup(0)

stack ends

定义一个栈,长度为128个字节。

db 256 dup(‘0’) 定义一个字符串,长度为256,‘0000000000000···’

段地址和偏移地址尽量按照这样搭配:ds:[bx] ds:[si] es:[di]

小程序

;题目:将year替换成年份,将summ替换成
assume cs:code,ds:data,ss:stack

data segment
        db          '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985','1986','1987'
        db          '1988','1989','1990','1991','1992','1993','1994','1995'         ;年份  长度为 84 byte

        dd          16,634,545622,2333,34523,5647,76864                             ;奖金  长度为 84 byte
        dd          24234,3563,6356,657567,84845,8484,6467
        dd          3563,2626,57547,3563,3232,232,566

        dw          2,4,543,12,4345,3454,5311                                       ;人数  长度为 42 byte
        dw          2,4,543,12,34534,222,33
        dw          2,4,543,12,123,3453,454
data ends

table segment              ;012345678901234
        db          21 dup('year summ ne ?? ')
table ends

stack segment stack
        db          128 dup(0)
stack ends

code segment
        start:      mov ax,stack        ;定义栈的大小为128字节
                    mov ss,ax
                    mov sp,128

                    mov ax,data         ;设置数据地址的起始位置,ds段寄存器
                    mov ds,ax           ;si=0,bx=168
                    mov si,0
                    mov bx,168

                    mov ax,table        ;设置ax 存放修改字段的其实位置。
                    mov es,ax
                    mov di,0

                    mov cx,21           ;循环21次

        inputTable: push ds:[si+0]      ;将19放入栈中(push默认字型数据)
                    pop es:[di]         ;将ye替换成19   
                    push ds:[si+2]      ;将75放入栈中
                    pop es:[di+2]       ;将ar替换成75   

                    mov ax,ds:[si+84]   
                    mov dx,ds:[si+86]   
                    mov es:[di+5],ax    
                    mov es:[di+7],dx    ;将su替换成0010H,mm替换成0000H(16=10H=0010H),并且将dx ax设置为被除数 即16=0000 0010H

                    push ds:[bx]        
                    pop es:[di+10]      ;将2替换掉ne

                    div word ptr es:[di+10] ;设置除数为2,其中2为字型数据,即16bit,他的被除数有dx,ax组成,即16=0000 0010H,得到商存在ax中

                    mov es:[di+13],ax       ;将商ax存入ne的后面??。

                    add si,4                ;将si指向第二个四字节数据  
                    add bx,2                ;将bx指向第二个两字节数据
                    add di,16               ;将di指向第二个year summ字符串
                    loop inputTable

                    int 21H
code ends

end start

offset 取出标号处的第一个偏移地址

jmp 标号处的地址 - jmp指令后的第一个字节的地址

jmp far ptr -32768~32767

jmp short ptr -128~127

jmp word ptr ds:[0]

jmp dword ptr ds:[0]

call 标号(将当前的IP压栈后,转到标号处执行指令),”call far ptr 标号”实现的是段间转移.

CPU执行”call far ptr 标号”时,相当于进行:

push CS

push IP

jmp far ptr 标号

call word ptr 内存单元地址,call dword ptr 内存单元地址

CPU执行”calldword ptr 内存单元地址”时,相当于:

push CS

push IP

jmp dword ptr 内存单元地址

jcxz

当cx为0的时候,跳转到标号处的第一个偏移地址。

loop是短转移,循环里面内容字节不能太多。

assume cs:code

code segment
                mov ax,4C00H
                int 21H

    start:      mov ax,0
    s:          nop
                nop
                mov di,OFFSET s
                mov si,OFFSET s2
                mov ax,cs:[si]
                mov cs:[di],ax

    s0:         jmp short s
    s1:         mov ax,0
                int 21H
                mov ax,0

    s2:         jmp short s1
                nop

code ends

end start

;程序可以正常运行吗?深刻理解运行过程。

0,2,4,6,8 偶数地址存放的是ascii码

1,3,5,7,9 奇数地址存放的是颜色

一行能写80个字符,共160个字节

一个dos窗口共25行,共4000个字节。

7号位置表示闪烁

6 red 背景色

5 green

4 blue

3 高亮

2 red 前景色

1 green

0 blue

;运行一遍然后查看结果,理解过程
;显示三行welcome to masm!
;第一行绿色,第二行绿底红字,第三行白底篮字
;利用显示内存显示,高位字节显示颜色,低位字节显示字符
assume cs:code,ds:data,ss:stack

data segment
           ;0123456789ABCDEF
        db  'welcome to masm!'
        db  00000010B
        db  00100100B
        db  01110001B
data ends

stack segment stack
        db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                jmp show_masm

    next:       mov ax,4C00H
                int 21H

    show_masm:  mov bx,data
                mov ds,bx

                mov bx,0B800H       ;第一位不能是字母,所以加了个0,B800是屏幕的第一个字节
                mov es,bx
                mov di,160*12+30*2

                mov cx,3
                mov bx,16

    showMasm:   push cx
                push di
                mov cx,16
                mov si,0
                mov dh,ds:[bx]


        showstring: mov dl,ds:[si]
                    mov es:[di],dx
                    add di,2
                    inc si
                    loop showstring


                pop di
                pop cx
                add di,160
                inc bx
                loop showMasm

                jmp next

code ends

end start

ret,retf

ret 近转移 相当于pop ip

retf 段间转移 相当于 pop ip,pop cs

call

在第二部ip=ip+指令字节数后执行push ip

call far ptr s 多做了一件事,push cs.

mul

8bit  al bl         mul byte ptr ds:[0]     ax
16bit ax bx         mul word ptr ds:[0]     dx,ax


div
ax      /  8bit = al ~ ah
dx,ax   / 16bit = ax ~ dx
;在指定的位置,用指定的颜色,显示用0结束的字符串。
;dh=行号;cl=颜色;ds:si字符串首地址
;‘Welcome to masm!’,0
assume cs:code,ds:data,ss:stack
data segment
    db 'Welcome to masm!',0 
data ends

stack segment stack
    db 128 dup(0)
stack ends

code segment
    start:  mov ax,stack        ;设置栈空间
            mov ss,ax
            mov sp,128

            mov dh,2            ;行号,列号,颜色
            mov dl,1
            mov cl,2

            call init_reg       ;调用初始化函数,设置数据区域为ds,设置屏幕区域为es

            mov si,0

            call show_str       ;调用显示结果函数,完成本题目的

            mov ax,4C00H
            int 21H
    ;----------------------------------------------------------------------------       
    init_reg:                   ;初始化函数,设置数据区域为ds,设置屏幕区域为es
                mov bx,data
                mov ds,bx

                mov bx,0B800H
                mov es,bx
    ret
    ;----------------------------------------------------------------------------       

    ;----------------------------------------------------------------------------       
    get_row:    
                mov al,160      ;获取显存的行偏移地址并存入di中
                mov bl,dh
                mul bl
                mov di,ax
    ret
    ;----------------------------------------------------------------------------       

    ;----------------------------------------------------------------------------       
    get_col:    
                mov al,2        ;获取显存的列偏移地址并存入di中
                mov bl,dl
                mul bl
                add di,ax
    ret
    ;----------------------------------------------------------------------------       

    ;----------------------------------------------------------------------------       
    show_str:                           ;显示结果函数
                call get_row            
                call get_col            

                mov dh,cl

                call show_string
    ret         
    ;----------------------------------------------------------------------------       

    ;----------------------------------------------------------------------------       
    show_string:
                ; push cx
                ; push dx
                ; push ds
                ; push es
                ; push si
                ; push di

                mov cx,0

                showString:
                            mov cl,ds:[si]
                            jcxz showStringRet
                            mov dl,cl
                            mov es:[di],dx
                            add di,2
                            inc si
                jmp showString

                showStringRet:
                            ; pop di
                            ; pop si
                            ; pop es
                            ; pop ds
                            ; pop dx
                            ; pop cx
    ret
code ends
end start
;程序:1234567/10
;因为会溢出产生不了正常结果,所以本程序自定义了怎样计算除法,ax存商,cx存余数
assume cs:code,ds:data,ss:stack
data segment
    dd  1234567
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
        start:  mov ax,stack
                mov ss,ax
                mov sp,128

                mov bx,data
                mov ds,bx

                call div_number

                mov ax,4C00H
                int 21H

long_div:       mov ax,dx
                mov dx,0
                div cx
                push ax
                mov ax,ss:[bp]
                div cx
                mov cx,dx
                pop dx
ret

div_number:
                mov ax,ds:[0]
                mov dx,ds:[2]

                mov cx,10
                push ax
                mov bp,sp

                call long_div
                ; add sp,2
ret

code ends
end start
;输出数据段对应的字符
assume cs:code,ds:data,ss:stack
data segment
    dw  1234,5678,65535,0
    db 10 dup(0)
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
        start:  mov ax,stack
                mov ss,ax
                mov sp,128

                call init_reg
                call show_number

                mov ax,4C00H
                int 21H

        init_reg:   mov bx,data
                    mov ds,bx
                    mov bx,0B800H
                    mov es,bx
        ret


        div_number:
                    push ax
                    push bx
                    push cx
                    push dx
                    push ds
                    push es
                    push si
                    push di

                    mov ax,ds:[si]
                    mov dx,0
                    mov bx,10

                divNumber:
                            div bx
                            add dx,30H
                            mov es:[di],dl
                            sub di,2
                            mov cx,ax
                            jcxz divNumberRet
                            mov dx,0
                            jmp divNumber

                divNumberRet:
                            pop ax
                            pop bx
                            pop cx
                            pop dx
                            pop ds
                            pop es
                            pop si
                            pop di
        ret     

        show_number: 
                    mov si,0
                    mov di,160*10 + 40*2
                    mov cx,4

        showNum:    call div_number
                    add si,2
                    add di,160
                    loop showNum
        ret

code ends
end start

标志位寄存器

cf :carry flag carry 进位 cy nc 移动指令不影响该寄存器。

zf : zero flag zr nz mul和div不影响该寄存器。

pf:parity 奇偶性 po 奇数 pe 偶数 1的个数是偶数就是pe

sf:sign 符号 ng 负数 pl 正数 ,mul不影响该寄存器

of overflow ov nv

of 在运算过程中决定,两个操作数会被当作有符号的处理,决定是否溢出 ,

cf 进位,把两个操作数当作无符号处理。

mov al,0FCH

add al,05H

上面结果是nv,未溢出,0FCH=1111 1100=-4 -4+5 显然没有溢出

adc,sbb

add ax,bx ax=ax+bx

adc ax,bx ax=ax+bx+carry

sbb ax,bx ax=ax-bx-carry

可以利用adc计算多位的加法,比如32 bit,128 bit等,这说明了内存中的值是没有任何含义的,可以根据

自己的情况决定他的用处。

je,jne,jb,jnb,ja,jna

je = jmp equal 其中jmp有范围 -128~127

b = below

a = above

DF

方向标志DF(Direction Flag)
用于串操作指令中,控制地址的变化方向:设置DF=0,存储器地址自动增加;

设置DF=1,存储器地址自动减少。

CLD指令复位方向标志:DF=0

STD指令置位方向标志:DF=1

DF标志和串传送指令
 DF 方向标志位,在串处理指令中,控制每次操作后si、di的增减
 df=0 每次操作后si、di递增
 df=1 每次操作后si、di递减
 movsb:
 相当于

mov es:[di],byte ptr ds:[si]
 如果 df=0
 inc di
 inc si
 如果 df=1
 dec di
 dec si

 movsw:
 相当于

 mov es:[di],word ptr ds:[si]
 如果 df=0
 add si,2
 add di,2
 如果 df=1
 sub si,2
 sub di,2

 一般来说,movsb和movsw都和rep配合使用,格式如下:
 rep movsb
 或者
 rep movsw
 相当于
 s:movsb
 loop s
 rep的作用是根据cx的值,重复执行rep后面的串传送指令

assume cs:code,ds:data,ss:stack
data segment
    db  128 dup(0)
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                call init_reg       ;ds=0B800H,es=0B800H
                call cpy_screen

                mov ax,4C00H
                int 21H

    cpy_screen: mov cx,24
                mov si,160
                mov di,0

    cpyScreenR: 
                push si
                push di
                mov cx,80

                cld             ;s: mov es:[di],word ptr ds:[si]
                rep movsw       ;   add si,2
                                ;   add di,2
                                ;loop s


                pop di
                pop si
                add si,160
                add di,160
                loop cpyScreenR
    ret

    init_reg:
                mov bx,0B800H
                mov ds,bx
                mov es,bx
    ret


code ends
end start

PSW

标志寄存器PSW(程序状态字寄存器PSW)
   标志寄存器PSW是一个16为的寄存器。它反映了CPU运算的状态特征并且存放某些控制标志。8086使用了16位中的9位,包括6个状态标志位和3个控制标志位。

15  14  13  12  11  10  09  08  07  06  05  04  03  02  01  00
                OF  DF  IF  TF  SF  ZF      AF      PF      CF

popf: pop PSW

pushf: push PSW

终端向量表

系统刚引导时,内存0x00000到0x0003FF共1KB的空间用于存放中断向量表。每个中断向量占用4个字节,共可存储256个中断向量。

8086的cpu 0x00200后面是没有中断向量的,可以用来写程序。

系统引导时,处在实模式下,只可寻址1MB,为什么要用4个字节来寻址中断呢处理程序?
     刚看到的时候,我也很纳闷。我们都知道编程的时候指针都是4个字节的,可以寻址4GB,在实模式下完全可以少用字节啊?其实实模式的内存选址方式是:段值 * 16 + 偏移值,就是内存地址。而这4个字节中,2个字节存储段值,两外两个存储偏移值。所以共有4个字节。

内存地址(十六进制)对应向量号(十六进制)中断用途
BASIC中断向量
0x3C4 - 0x3FFF1-FF未使用
0x218 - 0x3C386-F0BASIC程序运行时提供给BASIC解释程序作用
0x200 - 0x21780-85为BASIC保留
0x1E0 - 0x1FF78-7F未使用
0x1DC - 0x1DF77硬件中断15
0x1D8 - 0x1DB76硬件中断14
0x1D4 - 0x1D775硬件中断13
0x1D0 - 0x1D374硬件中断12
0x1CC - 0x1CF73硬件中断11
0x1C8 - 0x1CB72硬件中断10
0x1C4 - 0x1C771硬件中断9
0x1C0 - 0x1C370硬件中断
0x1A0 - 0x1BF68-6F未使用
0x180 - 0x19F60-67为用户程序保留的单元
0x128 - 0x17F4A-5F保留
0x124 - 0x12749指向键盘增强服务变换表
0x120 - 0x12348PC机使用,用于把PC机的键盘代码变换为标准的键盘代码
0x11C - 0x11F47保留
DOS中断向量
0x118 - 0x11B46第二硬盘参数块
0x114 - 0x11745保留
0x110 - 0x11344PC机使用,用于指向低分辩率图形字符参数表
0x108 - 0x10F42-43未使用
0x104 - 0x10741硬盘参数块
0x0C0 - 0x0CB34-40未使用
0x0CC - 0x0CF33鼠标中断
0x0C0 - 0x0CB30-32未使用
0x0BC - 0x0BF2F多路服务中断
0x0B8 - 0x0BB2E基本SHELL程序装入
0x0AC - 0x0B72B-2D未使用
0x0A8 - 0x0AB2AMicrosoft 网络接口
0x0A4 - 0x0A729快速写字符
0x0A0 - 0x0A328DOS安全使用
0x09C - 0x09F27终止并驻留程序
0x098 - 0x09B26绝对磁盘写功能
0x094 - 0x09725绝对磁盘读功能
0x090 - 0x09324严重错误处理(用户不能直接调用)
0x08C - 0x08F23Ctrl+Break 处理地址(用户不能直接调用)
0x088 - 0x08B22程序中止时DOS返回地址(用户不能直接调用)
0x084 - 0x08721DOS系统功能调用
0x080 - 0x08320DOS中断返回
数据表指针
0x07C - 0x07F1F图形字符扩展码
0x078 - 0x07B1E软盘参数块
0x074 - 0x0771D视频参数块
提供给用户的中断
0x070 - 0x0731C定时器控制的软中断
0x06C - 0x06F1BCtrl + Break控制的软中断
BIOS中断
0x068 - 0x06B1A时钟管理
0x064 - 0x06719引导装入程序–系统自举
0x060 - 0x06318BASIC入口代码–ROM BASIC入口代码
0x05C - 0x05F17打印机输出
0x058 - 0x05B16键盘I/O
0x054 - 0x05715盒式磁带I/O
0x050 - 0x05314RS-232串行通讯口I/O
0x04C - 0x04F13磁盘I/O
0x048 - 0x04B12测定存储器容量
0x044 - 0x04711设备检验
0x040 - 0x04310屏幕显示I/O
8259中断向量
0x03C - 0x03FFLPT2控制器中断–并行打印机(IRQ7)
0x038 - 0x03BE磁盘控制器中断–软磁盘(IRQ6)
0x034 - 0x037DLPT2控制器中断–硬磁盘(并行口)(IRQ5)
0x030 - 0x033C异步通信(primary)–串行通信接口1(IRQ4)
0x02C - 0x02FB异步通信(secondary)–串行通信接口2(IRQ3)
0x028 - 0x02BA彩色/图形(IRQ2)
0x024 - 0x0279键盘(IRQ1)
0x020 - 0x0238定时器(IRQ0)
8088中断向量
0x01C - 0x01F7保留
0x018 - 0x01B6保留
0x014 - 0x0175打印屏幕
0x010 - 0x0134溢出
0x00C - 0x00F3断点指令
0x008 - 0x00B2非屏蔽中断
0x004 - 0x0071单步(用于DEBUG)
0x000 - 0x0030除以零

中断过程

1.  取得终端类型码
2.  保存标志位寄存器    pushf
3.  将标志位寄存器的第八位TF和第九位IF设置为0
4.  push cs
5.  push ip
6.  cs = n*4+2       ip = n*4
iret
等同于:
pop ip
pop cs
popf

db ‘ad’ 和 db ‘a’,’d’ 在内存中显示的都是 61 64

TF,IF

TF(Trap Flag)陷阱标志:当TF被设置位1时,CPU进入单步模式,所谓单步模式就是CPU在每执行一步指令后都产生一个单步中断。主要用于程序的调试。8086/8088中没有专门用来置位和清零TF的命令,需要用其他办法。 

IF(Interrupt Flag)中断标志:决定CPU是否响应外部可屏蔽中断请求。IF为1时,CPU允许响应外部的可屏蔽中断请求。

中断过程

  1. 取中断类型码N
  2. 标志位寄存器入栈 IF=0 TF=0
  3. push cs, push ip
  4. cs=N*4+2 ip=N*4

0号终端较为特殊,是除数为0 ,程序直接退出了,其他的还会返回程序继续执行。

assume cs:code,ds:data,ss:stack
data segment
    db  128 dup(0)
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                call cpy_new_int0
                call set_new_int0

                int 0

                mov ax,4C00H
                int 21H


    set_new_int0:
                mov bx,0
                mov es,bx

                cli 
                mov word ptr es:[0*4],7E00H
                mov word ptr es:[0*4+2],0
                sti
                ret

    new_int0:   
                push bx
                push cx
                push dx
                push es

                mov bx,0B800H
                mov es,bx

                mov bx,0
                mov cx,2000
                mov dl,'!'

    show_asc:   mov es:[bx],dl
                add bx,2
                loop show_asc

                pop es
                pop dx
                pop cx
                pop bx
    iret

    new_int0_end:
                nop



    cpy_new_int0:
                mov bx,cs
                mov ds,bx
                mov si,OFFSET new_int0

                mov bx,0
                mov es,bx
                mov di,7E00H

                mov cx,OFFSET new_int0_end - new_int0
                cld
                rep movsb

                ret



code ends
end start
;安装int 7CH中断程序,功能为显示一个用0结束的字符串,终端程序安装在07E00H
;dh 行号
;dl 列号
;cl 颜色
;ds:si 指向字符串首地址
assume cs:code,ds:data,ss:stack
data segment
    db  'welcome to masm',0
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                call cpy_new_int7CH
                call set_new_int7CH

                call init_reg
                call show_masm


                mov ax,4C00H
                int 21H

    init_reg:   mov bx,data
                mov ds,bx
                mov bx,0B800H
                mov es,bx
    ret

    show_masm:  mov si,0
                mov dh,15
                mov dl,20
                mov cl,2
                int 7CH
    ret

    new_int7CH: call get_row
                call get_col
                call show_string
    iret

    show_string:push dx
                push ds
                push es
                push si
                push di

                mov dh,cl

    showString: mov dl,ds:[si]
                cmp dl,0
                je showStringRet
                mov es:[di],dx
                add di,2
                inc si
                jmp showString

    showStringRet:
                pop di
                pop si
                pop es
                pop ds
                pop dx
    ret

    get_col:    mov al,2
                mul dl
                add di,ax
    ret

    get_row:    mov al,160
                mul dh
                mov di,ax
    ret

    new_int7CH_end:
                nop

    cpy_new_int7CH:
                mov bx,cs
                mov ds,bx
                mov si,OFFSET new_int7CH

                mov bx,0
                mov es,bx
                mov di,7E00H

                mov cx,OFFSET new_int7CH_end - OFFSET new_int7CH
                cld 
                rep movsb
    ret

    set_new_int7CH:
                mov bx,0
                mov es,bx
                cli 
                mov word ptr es:[7CH*4],7E00H
                mov word ptr es:[7CH*4+2],0
                sti
    ret

code ends
end start

端口

in al,20H ;从20H端口读取一个字节

out 20H,al ;往20H端口中写入一个数据

in和out指令,只能使用ax或al存放从端口中的数据或发送到数据到端口中。

cpu能够读到的端口地址为 0–65535

端口直接和cpu通信。

cmos ram芯片包含了时钟和128个存储单元的ram存储器和电池。所以开机可以显示正确的北京时间。

cmos ram的地址端口为70H,数据端口为71H.

;读取cmos ram的2号单元内容
mov al,2
out 70H,al
in  al,71H
;向cmos ram的2号单元写入0
mov al,2
out 70H,al
mov al,0
out 71H,al

shl,shr

shl 向左移动一位,

shr 向右移动以为。

当al=1000 0000时候,shl al,1 会得到 al=0000 0000 并且CF=1

;在屏幕中显示当前的秒数,0~DH 内存地址保存的是时间信息
assume cs:code,ds:data,ss:stack
data segment
    db  128 dup(0)
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                call init_reg               

    testA:      call show_second
                jmp testA               ;不停的更新秒数

                mov ax,4C00H
                int 21H

    init_reg:   mov bx,0B800H
                mov es,bx
    ret

    show_second:    mov al,0
                out 70H,al          ;cpu发出命令,我需要端口70H中的0号单元的数据。
                in al,71H           ;存的是bcd码 二进制to十进制,比如12  那就是 0001 0010

                mov ah,al   
                shr ah,1
                shr ah,1
                shr ah,1
                shr ah,1
                and al,00001111B    ;到这里 ah为0001   al为0010
                add ah,30H          ;加上30H可以直接显示ascii码
                add al,30H

                mov di,160*10 +40*2
                mov es:[di],ah
                mov es:[di+2],al
    ret


    code ends
end start

外中断

外中断指的是外设产生的中断。

外中断分为可屏蔽中断,不可屏蔽中断。

““
不可屏蔽终端:终端类型码为2
1 pushf IF=0 TF=0
2 push cs
3 push ip
4 IP=8H CS=AH

可屏蔽中断:
1取得终端类型码,保存标志位
2 pushf
3 IF=0 TF=0
4 push cs ,push ip
5 IP=n*4 cs=n*4+2
““

键盘中断

键盘提供了三种基本类型的键

1)字符键:传送一个ASCII码字符给计算机。如A~Z,0~9,%,$ 等。
2)扩展功能键:产生一个动作。如Home,End,Enter等,不能用标准ASCⅡ码表示的特殊键或组合键。
F1~F10:3B~44H
↑ ↓ ← → :48 50 4B 4DH
PgDn PgUp  Ins Del: 50 48 4B 4D 52 53H
3)组合功能键:改变其它键所产生的字符码。如Alt,Ctrl,Shift等

字符码与扫描码

扫描码:键盘中断处理程序从8255的60H端口读取一个字节,该字节的低7位就是键的扫描码。键盘上的每一个键对应一个扫描码,从01H~51H。

字符码:BIOS键盘处理程序将所取得的扫描码转换成相应的字符码。大部分键的字符码为ASCII码,没有ASCII码的键其字符码为0或一个指定的操作(如屏幕打印等)。系统有自动转化的。键盘中断为int 9,如果自己写代码替换了int 9的中断向量指向,那么就需要你自己将扫描码等转化为字符码。

;键盘状态字节KB_FLAG:内存的0040:0017H单元,由类型 9的硬件键盘中断置入键盘的对应状态到该单元。低4位是Alt、Ctrl、Shift左、Shift右的标志位,这4位在相应键按下时置位,该键一抬起即复位。KB–FLAG的高4位是Ins、Caps Lock、Num Lock、Scroll Lock键的标志位,这些位在相应键奇次按下时置位,偶次按下时复位。
assume cs:code,ds:data,ss:stack
data segment
    db  128 dup(0)
data ends

stack segment stack
    db  128 dup(0)
stack ends

code segment
    start:      mov ax,stack
                mov ss,ax
                mov sp,128

                call init_reg               
                call showKWstatus               

                mov ax,4C00H
                int 21H

    init_reg:   mov bx,0B800H
                mov es,bx
                mov bx,40H
                mov ds,bx
    ret

    show_status:push cx
                push dx
                push ds
                push es
                push si
                push di

                mov cx,8

    showStatus: mov dx,0
                shl al,1
                adc dx,30H
                mov es:[di],dl
                add di,2
                loop showStatus

                pop di
                pop si
                pop es
                pop ds
                pop dx
                pop cx
    ret

showKWstatus:   mov si,17H

    testA:      mov al,ds:[si]
                mov di,160*10+40*2
                call show_status
                jmp testA
    ret

code ends
end start

描述内存长度的标号

NUMBER  db 1
mov NUMBER,80H
等价于:
NUMBER  db 1
mov bx,OFFSET NUMBER
mov word ptr cs:[bx],80H

还可以写成这种格式: cs:NUMBER[si]   等同于   cs:[OFFSET NUMBER + si]
data segment
    a   db  1,23
    b   dw  OFFSET a,   seg a,  OFFSET a,   seg b     ;offset获取偏移 地址,seg获取段地址
data ends

BIOS有一个键盘缓冲区,INT 9触发时,从60H端口读入键盘扫描码,保存到键盘缓冲区。INT 16H从键盘缓冲区取扫描,并转换ASCII码返回。等待输入就是当缓冲区为空时循环一直检查,直到有数据为止。

  1. 检测键盘缓冲区是否有数据
  2. 没有的话就继续检测
  3. 读取缓冲区第一个字型数据的键盘输入
  4. ah=扫描码 al=ascii
  5. 读取的字型数据删除
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
汇编语言(第3)》是我国计算机科学教育领域的-部重要的经典著作。   《汇编语言(第3)》可用作大学计算机专业本科生的汇编教材及希望深入学习计算机科学的读者的自学教材。本书自出以来,受到业内专家和高校教师、广大计算机专业学生和计算机科学与技术学习者的热烈欢迎和高度评价。100多所高校用作教材,取得了非常好的教学效果。很多人在网络上将其与国外同专业名著相并列向广大读者推荐。本书很大程渡地推动了我国计算机科学与技术教育的发展。   《汇编语言(第3)》出10年连续印刷了30次,成为本专业学生和广大学习者的必读书。为满足新的出需要,本社特请王爽老师对此书的第2进行了修订,出第3,以飨读者。书评赏析本书是国内原创的极少的经典书籍,通俗易懂,高屋建瓴,实践和理论相结合,看完之后对于许多知识点有种豁然开朗的感觉。这本书完全是-切以学生为中心的,活泼生趣,讲得非常不错!要是我们的课本都是这么样就好了,不像有些教材得那么晦涩难懂、就像看古文-样。要学汇编这本书是必不可少的!   《汇编语言(第3)》很好地把握了“教”与“学”的关系,确实是一本独-无二的汇编书籍,能极好地激发学习汇编的热情和勇气。想深入学习汇编语言的,这本教材是一个很好的选择,它打破了传统教材的教学顺序,但是能极大保证读者每章都能明白,做到真正的循序渐进。好久没有看到一本这么适合初学者的书了,作者的用心是良苦的。作者在排、布局和知识点方面都下了功夫,而且是大功夫,以至于全书的结构如此精良,能让初学者在短时间内上手,不会感到一丝吃力。非常值得推荐!王爽老师的这本书得是很有思想的,是一种教育的思想而不是单纯的知识积累。它能和国外经典教材并称于世绝非偶然,相信它能帮助每一个学习者更好地学习理解汇编语言

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值