中断

CPU有这样一种功能:在执行完当前指令后,检测到cpu外部发送过来的或内部产生的一种特殊信息,且可以立即对接收到信息进行处理,这种信息呢,就叫中断信息。

内中断

当CPU内部有什么事情发生的时候,将产生需要马上处理的中断信息呢?对于8086来说,当cpu内部有下面的情况发生时,产生相应中断;

  1. 除法错误,比如,执行div指令产生的除法溢出;
  2. 单步执行;
  3. 执行into指令;
  4. 执行int指令。
    上面的四种中断源,在8086中中断类型码如下。
  5. 除法错误:0
  6. 单步执行:1
  7. 执行into指令:4
  8. 执行int指令,该指令的格式为int n,指令中的n为字节型立即数,是提供给CPU的中断类型码。
    cpu收到中断信息后,需要对中断信息进行处理,需要对不同的中断信息编写不同的处理程序。
    若要定位中断处理程序,需要知道它的段地址和偏移地址
    中断向量表就是中断向量的列表,中断向量就是中断处理程序的入口。
    enter description here

中断过程:

中断类型码->中断向量表->修改CS:IP->中断处理程序

  1. (从中断信息中)取得中断信息码;
  2. 标志寄存器的值入栈(因为在中断过程中要改变标志寄存器的值,所以先将其保存在栈中);
  3. 设置标志寄存器的第8位TF和第9位IF的值为0(以后解释)
  4. CS的内容入栈;
  5. IP的内容入栈;
  6. 从内存地址位中断类型码*4 和中断类型码*4+2的两个字单元中读取中断处理程序入口地址设置IP和CS。

中断处理程序

中断信息可能随时被检测到,所以中断处理程序必须一直存储在内存某段空间之中。中断处理程序入口地址,即中断向量,必须存储在对应表项中。

  1. 保存用到的寄存器;
  2. 处理中断
  3. 恢复用到的寄存器
  4. 用iret指令返回。
    iret:
    pop ip
    pop cs
    popf

经典除法中断(0号中断)

再实现

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
53
assume cs:code
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset do0;要存的中断处理程序

mov ax,0
mov es,ax
mov di,200h;存放地址(这里随便放在了中断表的后面空闲位置中)

mov cx,offset do0end-offset do0;存放的量(编译器可以进行简单运算)
cld; cld是来控制重复移动时候的si 和di的递增方式即+1(用std是-1)。
rep movsb;逐字节移动复制

mov ax,0
mov es,ax
mov word ptr es:[0*4],200h;写入中断向量表入口地址ip
mov word ptr es:[0*4+2],0;cs

mov ax,4c00h
int 21h;这只是将中断处理程序写入的结束

do0:
jmp short do0start
db "overflow!!!"
db 0ach
do0start:
mov ax,cs
mov ds,ax
mov si,201h;o开始处

mov ax,0b800h;老规矩的显存修改
mov es,ax
mov di,12*160+36*2
mov ah,[si+24]

mov cx,12
s:
mov al,[si]
mov es:[di],al
mov es:[di+1],ah
inc si
add di,2
loop s

mov ax,4c00h
int 21h;中断处理结束

do0end:
nop
code ends
end start

效果:
enter description here

int

int n ;引发中断过程 过程:

  1. 取中断类型码n
  2. 标志寄存器入栈,TF=0,IF=0
  3. CS、IP入栈
  4. (IP)=(n*4),(CS)=(n*4+2)
  5. 执行中断处理程序
    1
    2
    mov word ptr es:[aaaa*4],200h;写入中断向量表入口地址ip
    mov word ptr es:[aaaa*4+2],0;cs
    aaaa是多少就是int多少

BIOS/DOS提供的中断例程

在系统的ROM中存放着一套程序,称为BIOS,主要有:

  1. 硬件系统的检测和初始化程序;
  2. 外部中断和内部中断的中断例程;
  3. 用于对硬件设备进行I/O操作的中断例程;
  4. 其他和硬件系统相关的中断例程。
    DOS中也提供了中断例程,DOS的中断例程就是操作系统向程序员提供的编程资源。

BIOS中断例程应用

int 10h的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code
code segment
start:
mov ah,2
mov bh,0
mov dh,5
mov dl,12
int 10h;解释:ah=2表示调用第10h号中断例程的2号子程序,功能为设置光标位置可以提供光标所在的行号、列号和页号作为参数。bh=0,dh=5,dl=12,设置光标到第0页,第5行,第12列。

mov ah,9;在光标位置显示字符
mov al,'a'
mov bl,11001010b;同显存设置颜色属性
mov bh,0
mov cx,3;字符重复个数
int 10h

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

enter description here

DOS中断例程应用

经典:

1
2
mov ax,4c00h
int 21h

int 21h ; DOS提供的中断例程,包含了很多子程序
我们使用4ch号功能,即程序返回功能

1
2
3
mov ah,4ch;程序返回功能号
mov al,0;返回值
int 21h

又如:
在屏幕的5行12列显示字符串“Welcome to cloud”
enter description here

端口

在访问端口的时候,cpu通过端口地址来定位端口。因为端口所在的芯片和cpu通过总线相连,所以,端口地址和内存地址一样,通过地址总线来传送。在pc系统中,cpu最多可以定位64KB个不同的端口。则端口地址的范围为0~65535。

对端口的读写不能用mov,push,pop等内存读写指令。只能用inout这两条,分别用于从端口读取数据和向写入数据。

在in和out指令中,只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用al,访问16位端口时用ax

对0~255以内的端口读写:
in al,20h
out 20h,al

对256~65535端口读写,端口号放在dx中
mov dx,3f8h
in al,dx
out dx,al

CMOS-RAM

PC机中,有一个CMOS RAM芯片,一般简称位CMOS。特征如下:

  1. 含一个实时钟和一个有128个存储单元的RAM存储器(早期为64个字节)。
  2. 该芯片靠电池供电。所以关机后其内部的实时钟仍可正常工作,RAM中的信息不丢失。
  3. 128个字节的RAM中内部实时钟占用0~0dh单元保存信息,其余大部分单元用于保存系统配置信息,供系统启动时BIOS程序读取。BIOS也提供了相关的程序,使我们可以再开机的时候配置CMOS RAM中的系统信息。
  4. 该芯片内部有两个端口,端口地址为70h和71h。CPU通过这两个端口来读写CMOS RAM。
  5. 70h为地址端口,存放要访问的CMOS RAM单元的地址;71h为数据端口,存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据。可见,CPU对CMOS RAM的读写分两步进行,比如,读CMOS RAM的2号单元:
    1. 将2送入端口70h;
    2. 从端口71h读出2号单元的内容。

shl/shr

shr和shl是逻辑移位指令
shl逻辑左移

  1. 将一个寄存器或内部单元中的数据向左位移;
  2. 将最后移出的一位写入CF中;
  3. 最低位用0补充;
  4. 如果移动位数大于1时,必须将移动位数放在cl中。
    shr是逻辑右移指令,与shl相反。
  5. 将一个寄存器或内存单元的数据向右移位。
  6. 将最后移出的一位写入CF。
  7. 最高位用0补充。
    用法与shl一致。

实战

enter description here
在屏幕中间显示当前月份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
assume cs:code
code segment
start:
mov al,8
out 70h,al
in al,71h

mov ah,al
mov cl,4
shr ah,al;取前4位BCD码
and al,00001111b;取后4位BCD码

add ah,30h
add al,30h

mov bx,0b800h
mov es,bx
mov byte ptr es:[160*12+40*2],ah
mov byte ptr es:[160*12+40*2+2],al

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

enter description here

外中断

  • 可屏蔽中断
    sti:IF置为1,响应中断
    cli:IF置为0,不响应可屏蔽中断
  • 不可屏蔽中断
    8086CPU,不可屏蔽中断的中断类型码固定为2
    还是in,out

标志寄存器

FLAGS

作用

  1. 用来存储相关指令的某些执行结果
  2. 用来为CPU执行相关指令提供行为依据
  3. 用来控制CPU的相关工作方式
    enter description here
    空位为未定义保留位

ZF

  1. flag的第六位是ZF,零标志位
  2. 它记录相关指令执行后,其结果是否为0。
  3. 如果结果为0,那么zf=1;如果结果不为0,那么zf=0。

PF

  1. flag的第2位是PF,奇偶标志位
  2. 记录相关指令执行后,结果的所有bit位中1的个数是否是偶数。
  3. 如果结果bit位中有 偶数个1,则pf=1,奇数个1,pf=0。

SF

  1. flag的第7位是 SF(symbol flag),符号标志位
  2. 它记录相关指令执行后,结果是否为负。
  3. 如果为负,sf=1;非负,则sf=0。

CF

  1. flag的第0位是CF,进位标志位
  2. 一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。

OF

  1. flag的第11位是OF,溢出标志位
  2. OF记录了有符号数运算结果是否溢出。
  3. 如果溢出,OF=1,没有则OF=0。

adc

adc是带进位加法指令,它利用了CF位上的记录的进位值。
类add但带了CF
功能:对象1=对象1+对象2+CF

sub

很明显,之前有了带进位加法adc,这个就是带借位减法。
类sub但带了CF
功能:对象1=对象1-对象2-CF
enter description here

cmp

cmp是比较指令,cmp的功能相当于减法指令,但是不保存结果
功能:对象1-对象2,但不保存结果,不改变对象,只对标志寄存器进行重置
enter description here

test

Test命令将两个操作数进行逻辑与运算,并根据运算结果设置相关的标志位。但是,Test命令的两个操作数不会被改变。

条件转移指令

je(equl)

cmp后等于则转移(zf=1)

jne

cmp后不等于则转移(zf=0)


相对于无符号数

jb(below)

cmp后低于则转移(cf=1)

jnb

cmp后不低于则转移(cf=0)

ja(above)

cmp后高于则转移(cf=0&&zf=0)

jna

cmp不高于则转移(cf=1&&zf=1)


相对于有符号数

jl(less)

cmp后小于则转移

jnl

cmp后不小于则转移

jg(greater)

cmp后大于则转移

jng

cmp后不大于则转移

DF(definition flag)和串传送

df

也是flag之一
df=0 每次操作后si di递增
df=1 每次操作后si di递减
唯一可修改的标志位

cld

df清零

std

df置1

rep

rep movsb
相当于:
s:
movsb
loop s
(循环则需预先设置cx)

movsb

1、((es)*16+(di))=((ds)*16+(si))
2、如果df=0 则:
inc si
inc di
如果df=1 则:
dec si
dec di

movsw

按字操作,di和si增减单位为2字节
mov es:[di],word ptr ds:[si]
如果df=0 则:
add si,2
add di,2
如果df=1 则:
sub si,2
sub di,2

enter description here

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
code segment
start:
mov ax,data
mov ds,ax
mov si,0;指向W
mov es,ax
mov di,16;指向第一个0
mov cx,16
cld
rep movsb

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

pushf和popf

pushf的功能是将标志寄存器的值压栈,而popf就是将其出栈,扔入flag。
用于中断保护“现场”
popf可用于修改标志位

debug中显示的标志位

enter description here

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

模型机的总体设计

模型机指令系统

  • 指令格式
    • 双操作数指令
      enter description here
    • 单操作数指令
      enter description here
    • 转移指令
      enter description here
  • 寻址方式
    enter description here
    enter description here
  • 操作类型
    enter description here

CPU的组成

enter description here

运算器

  • 输入选择器/锁存器
    选择数据来源,送入ALU进行运算处理,或借道ALU进行传送。数据来源有:R0~R3、C、D、PC、SP、PSW、MDR。
  • ALU部件
    作各种算术,逻辑运算;由微命令M,S0,S1,S2,S3,C0选择操作功能。
  • 移位器
    作直接传送、左移、右移;由微命令实现直接、左、右移。

寄存器设置

  • 用于处理的寄存器:通用寄存器、暂存器
    • 通用寄存器组
      一组可编程访问的寄存器。在指令系统中为这些R分配了编号,有:R0~R3,PC,SP,PSW。
    • 暂存器
      用户不能直接访问的R,用来暂存信息,在指令系统中没有为它们分配编号,有C、D。 暂存器C:从主存中读取源操作数或源操作数地址时,使用它。 暂存器D:从主存中读取目的作数或目的操作数地址时,或中间运算结果时,使用它。
  • 用于控制的寄存器:指令寄存器IR、程序计数器PC、 程序状态字寄存器PSW
    • 指令寄存器IR
      用来存放现行运行指令,它的输出是产生微操作命令序列的主要逻辑依据。
    • 程序计数器PC
      PC提供后继指令地址,并送往与主存器相连接的地址寄存器(MAR)。
    • 程序状态字寄存器PSW(program status word)
      PSW的内容就是表现的现行程序的状态。 包括: 特征位:进位C、溢出V、零Z、负N,允许中断I等; 编程设定位。
  • 用作主存接口的寄存器:地址寄存器MAR、数据缓冲寄存器MBR(MDR)
    CPU访问主存时,首先送出地址码,然后送出/接收数据,需:
    A.当作用在MAR上的微命令EMAR为低电平时,MAR输出呈高阻态,与地址总线断开;
    B.当作用在MAR上的微命令EMAR为高电平时,MAR输出其内容(地址信息)送往地址总线;
    • 地址寄存器MAR
      读取指令/存取操作数/操作数地址时,CPU先将地址信息送入MAR,再由MAR经地址总线送往主存M,找到相应的主存单元。
    • 数据缓冲寄存器MBR(MDR)
      由控制命令R/W决定传送方向。
      R:由主存单元->数据总线->MDR
      W:由MDR->数据总线->数据单元

总线

  • 定义
    是一组能为多个部件分时共享的公共信息传送线路,及相应的控制逻辑。
    数据通路结构:数据传送结构,它是CPU总体结构的核心问题。 总线:是一组能为多个部件分时共享的公共信息传送线路,及相应的控制逻辑。
  • 总线类型
    ① CPU内总线: 模型机中是一组单向数据传送总线,是连接运算器、寄存器等CPU内部部件的总线。
    ② 部件间总线: 芯片间的连接总线,包含地址线与数据线两组。
    ③ 系统总线: 作用是计算机系统内各大部件进行信息交换的基础,分:地址总线AB、数据总线DB、控制总线CB
    ④ 外总线: 一台CS与其它设备相连接的总线。

时序系统

产生时序信号的部件称为时序发生器或时序系统,它由一个振荡器和一组计数分频器组成。

CPU的内部数据通路结构

模型机如何以内总线为纽带建立各部件间的数据传送通路,即CPU内部数据通路结构,这是CPU组成的核心问题。
模型机的结构为:单组、单向内总线、分立寄存器结构。

模型机数据通路框图

enter description here

  • 可编程寄存器
    R0~R3通用寄存器(000,001,010,011)、SP(100)堆栈指针、PSW(101)程序状态寄存器、PC程序计数器
  • 指令寄存器IR
    用来存放现行运行指令,它的输出是产生微操作命令序列的主要逻辑依据。
  • 暂存器
    C、D约定从主存中读源操作数、操作数地址时用C;从主存中取目的操作数、操作数地址或暂存时,用D
  • 主存接口寄存器MAR、MDR(MBR)
    • 地址寄存器MAR
      读取指令/存取操作数/操作数地址时,CPU先将地址信息送入MAR,再由MAR经地址总线送往主存M,找到相应的主存单元。
    • 数据缓冲寄存器MBR(MDR)
      由控制命令R/W决定传送方向。
      R:由主存单元->数据总线->MDR
      W:由MDR->数据总线->数据单元

各类信息的传送路径

  • 取指令地址
    enter description here
    enter description here
  • 指令信息的传递
    enter description here
  • 取操作数地址—①寄存器间址(R)
    enter description here
  • 取操作数地址—②自减型寄存器间址 -(R)
    enter description here
  • 取操作数地址—③自增型寄存器间址 (R)+
    enter description here
    两步走

微命令设置

  • 数据通路操作
    1) ALU输入选择:  Ri→A,Ri的取值:R0—R3,C,D,PC,SP Ri→B,Ri的取值:R0—R3,C,D,MDR,PSW 
    2) ALU功能选择: S0—S3,C0,M (6位线)
    3) 移位器功能选择:  00:直传DM、01:左移SL、10:右移SR、11:高低字节交换EX (2位线)
    4) 分配脉冲(打入到寄存器中的脉冲):  CPR0—CPR3, CPC,CPD,CPMDR,CPMAR,CPPSW, CPPC,CPSP
    
  • 访存操作相关的控制信号(系统总线及主存有关的微命令)
    EMAR(输出到AB)、EMDR(输出到DB)、SMDR(DB到MDR)、 R(M到CPU)、W(CPU到M)、SIR(M到IR)
    (1)读入CPU:EMAR、R、SMDR(除指令以外),或SIR(指令) 
    (2)写入M:EMAR、EMDR、W
    

主机与外部的数据通路 与 信息传送控制方式

主机与外设的连接模式:辐射型、总线型、通道型
对信息传送的控制方式:直接程序传送方式、程序中断方式、直接有储器访问方式

主机与外围设备的连接方式

  • 辐射型
    主机与各外围设备间有单独的数据通路;速度较快,**但**不易扩展。
    
  • 总线型
    各外设通过各自接口直接与公共的系统总线相连;结构简单,易扩展,**但**如果整个系统只有一组系统总线时,信息吞吐量有限,且速度较慢。
    
  • 通道型
    采取一种称为多种连接模式的部件去连接外设,这种部件称为通道;并行能力强。
    

信息传送的控制方式

  • 直接程序传送方式
    定义:CPU直接利用I/O指令程序实现I/O传送,在外设工作期间, CPU不执行与I/O无关的操作。
    主机状态:CPU处于查询—等待—执行状态。
    外设状态:在外设接口的状态字中设置两位表状态。

      空闲——外设不工作,00 
      工作(忙)——外设置在执行操作,01 
      结束(完成)——外设完成一次操作,10
    

    特点:CPU不能与外设并行地工作,因而CPU利用率低,并且CPU不能响应来自外部的随机请求。
    enter description here

  • 程序中断方式
    定义:CPU暂停执行现行程序,转去执行中断程序,以处理某些随机事态,并在处理完毕后自动恢复原程序的执行。
    实质:程序的切换过程。包括将断点,有关状态信息(PSW)压栈保护起来,然后根据中断号,从中断向量表中得到中断服务程序的地址,送入PC中。(这一切是硬件实现的,称为隐指令)
    特点:随机性
    软件组织与程序安排:将系统中接收的中断请求编制成中断服务模块存入主存,并将其入口地址写入中断向量表。
    硬件组织
    CPU方面:在PSW中设置一个中断位;在模型机中,外部请求只有IREQ0—IREQ7,因此在CPU内还应设置一个判优逻辑。
    接口方面:设置了中断接口。
    特点:可以处理随机的复杂事态,但程序的切换需花费一定时间,因此,其适用范围是中低速I/O操作与随机请求。
    enter description here

  • 直接存储器存取(DMA)方式
    定义:直接依靠硬件在主存与I/O设备进行简单成批数据传送的一种工作方式,在传送期间不需CPU的程序干预。
    实质:暂停执行程序。不存在断点,现场的保护,速度很快。
    程序组织与DMA初始化:在进行DMA传送前,应对DMA控制器进行初始化,主要初始化以下三个信息:
    送出控制字:操作类型(输入或输出),外设地址;
    送出主存缓冲区首址:传送或接收数据的起始地址;
    送出交换量:DMA是批量传送的,传送的数据块数目。
    硬件组织:由DMA控制器控制管理DMA传送。
    CPU方面:在CPU的时序系统中,设置专门的DMA周期。在此周期,总线由DMA控制。每当系统总线周期结束(完成一次总线传送)时,CPU对总线控制权转移作出判断,是继续由DMA控制器掌管,还是CPU收回其控制权,恢复正常程序执行。
    接口方面:设置DMA控制器与接口。
    特点:适用于高速外设与主存之间的简单批量数据传送。
    enter description here

时序控制方式 与 时序系统

时序控制方式

  • 同步控制方式
    定义:如果各项操作与统一的时序信号同步,称为同步控制。
    时间分配(基本特征):同步控制方式的基本特征是将操作时间划分为许多时钟周期,周期长度固定,每个时钟周期完成一步操作。
    同步定时:在许多操作中需要严格地同步定时,如同步打入脉冲。
    各部件间的协调:在CPU内,一般采用由CPU提供的统一时序信号来控制部件间信息的传送的。
    特点:时序关系比较简单,在时间安排利用上可能是不经济的。
  • 异步控制方式
    定义:异步控制是指各项操作按其需要选择不同的时间,不受统一的时钟周期的约束;各操作之间的衔接与各部件之间的信息交换采取应答方式。
    时间分配(基本特征):没有统一的节拍划分与同步定时脉冲,但存在着申请、响应、询问、回答一类的应答关系。
    主从设备的概念:申请使用总线,并获得批准后掌管总线控制权的设备,称为主设备,否则为从设备。
    特点:时间紧凑,能按不同部件,设备的实际需要分配时间,实现异步应答所需的控制比较复杂。
  • 实际应用中的一些变化
    在CPU或设备的内部普遍采用同步控制方式;对连接CPU、主存、外设的系统总线,有的采用同步,有的采用异步控制,但多采用异步控制。在实际应用中,同步控制甚至引入异步应答关系。

指令序列间的衔接方式

enter description here

同步控制的时序系统

定义及组成

时序系统:产生节拍,脉冲等时序信号的部件,称为时序系统。
时序系统的组成:一个振荡器(产生脉冲源);一组计数分频逻辑

时序划分层次

指令周期:读取并执行一条指令所需的时间,称为指令周期。一般不作为时序的一级。
CPU工作周期:在指令周期中的某一工作阶段所需的时间,称为一个工作周期。一般不同(FT、ST、DT、ET)。
时钟周期(节拍):是时序系统中最基本的时间分段。各节拍的长度相同(一般一个节拍是一个时钟周期)。
定时脉冲(工作脉冲):有的操作如打入R,还需严格的定时脉冲,以确定在哪一刻打入。

算术逻辑运算部件

加法单元

enter description here
Σi=(Ai ⊕ Bi)⊕ Ci-1
Ci = Ai Bi +(Ai ⊕ Bi)Ci-1
根据上式,得出一位全加器的逻辑电路图:
enter description here

串行加法器

  • 定义:如果每步只求一位和,将n位加分成n步实现,这样的加法器称为串行加法器。
  • 组成:一位全加器、移位寄存器(从低到高串行提供操作数相加)、寄存器、触发器(记录进位信号)
  • 组成图:enter description here
  • 特点:结构简单,速度极慢。

并行加法器

  • 定义:如果用n位全加器一步实现n位相加,即n位同步相加,这样的加法器称为并行加法器。
  • 组成:n位加法器+进位链(串行进位链、并行进位链)
    根据(指Ci-1)输入量提供时间的不同,将进位链分为带串行进位链的并行加法器,带并行进位链的并行加法器。
    加法器的运算速度不仅与全加器的运算速度有关,更主要的因素是取决于进位传递速度。
  • 进位信号的基本逻辑
    Ci  = AiBi +(Ai ⊕ Bi)Ci-1
    Gi = AiBi  进位产生函数(本地进位) 
    Pi = Ai ⊕ Bi 进位传递函数(传递进位)
  • 串行进位链
    关系式:
    C1 = G1 + P1C0
    C2 = G2 + P2G1
    ……
    Cn = Gn + PnGn-1
    enter description here
    特点:结构比较简单,运算速度比较慢。
  • 并行进位链
    关系式:
    C1 = G1 + P1C0
    C2 = G2 + P2G1 + P2P1C0
    ……
    Cn = Gn + PnGn-1 + …+ Pn…P1C0
    enter description here
    特点:结构复杂,运算速度快。
  • 组内并行,组间并行(实际为串行)的进位链
    分两级进行
    enter description here
    enter description here
    enter description here
    enter description here
    enter description here
    然后
    enter description here

ALU单元与多位ALU部件

  • 一位enter description here
  • 4位 74LK181N
    enter description here
    其总共由22个引脚,8个数据输入端:A0、A1、A2、A3,B0、B1、B2、B3,这八个都是低电平有效。
    S0、S1、S2、S3四个控制端,这四个控制端主要控制两个四位输入数据的运算,例如加、减、与、或。CN端处理进入芯片前进位值,M控制芯片的运算方式,包括算术运算和逻辑运算。
    F0、F1、F2、F3是四个二进制输出端,以一个四位二进制形式输出运算的结果。CN4记录运算后的进位。
    enter description here

    运算方法

    定点加减运算

    enter description here
    两位符号位判断溢出:11正常负数,10负溢出,00正常正数,01正溢出

    定点乘法运算

    一种乘法器是将n位乘法转换为 n次累加与移位循环,因而可用常规加法器实现。 另一类乘法器结构,称为阵列乘法器(一拍内实现多项部分积相加)。
  • 原码一位乘法
    寄存器分配与初始值:A,B,C三个寄存器
    A存放部分积累加和,初始值为0(双符号位00表示);
    B存放被乘数X(绝对值),此时符号位为双符号位00(在乘的过程中,B中的值一直保持不变);
    C存放乘数Y(绝对值),将符号位去掉;C寄存器的初始值是乘数Y的尾数(有效位数),以后每乘一次,将已处理的低位乘数右移舍去,同时将A寄存器的末位移入C寄存器的高位。
    例:
    X = 0.1101,Y =-0.1011,求 XY =? 设寄存器 A = 00.0000,B =|X|= 00.1101,C =|Y|= .1011。
    enter description here
    即1.10001111
  • 补码一位乘法
    寄存器分配与初始值:A,B,C三个寄存器
    A存放部分积累加和,初始值为0(双符号位00表示);
    B存放被乘数X补,(双符号位00、或11表示);
    C存放乘数Y补,单符号位(符号位参与运算),Y的末位添0,称为附加位Yn+1。
    例:
    X = -0.1101,Y = -0.1011,求[XY]补 = ?
    设 A = 00.0000,B = X补 = 11.0011,-B = -X补= 00.1101,C = Y补 = 1.0101。
    enter description here

    定点除法运算

    A,B,C三个寄存器;
    A初始值存放被除数(绝对值),以后存放各次余数,A取双符号位,从第一符号位判断是否够减,从而决定商值;
    B寄存器存放除数的绝对值,取双符号位;
    C存放商,取单符号位;商由末位置入,在每次置入新商时,原商同时左移一位。
    基本操作与上商:
    a. 第一步操作必为2r0-Y
    b. 以后各部根据如下条件进行:
    ri为正表示够减,即Qi=1,则第i+1步应为2ri-Y,
    ri为负表示不够减,即Qi=0,则第i+1步应为2ri+Y;
    c. 最后一步:若第n步(最后一步)余数为负,则需增加一步恢复余数,这增加的一步不移位,操作为rn+Y。
    例:
    X÷Y = -0.10110÷0.11111 = ?
    设A = |X| = 00.10110,B = |Y| = 00.11111,则B = 00.11111,-B=11.00001, C = |Q| = 0.00000。
    enter description here

    模型机的组合逻辑控制器

  • 定义:组合逻辑控制器的微命令是由组合逻辑电路来实现。每种微命令都需要一组逻辑电路,全机所有微命令所需的逻辑电路就构成了微命令发生器。
  • 工作原理:从主存读取的现行指令存放在IR中,其中,操作码与寻址方式代码分别经译码电路形成一些中间逻辑信号,送入微命令发生器,作为产生微命令的基本逻辑依据。微命令的形成还需考虑各种状态信息,如PSW所反映的CPU内部运行状态、由控制台(如键盘)产生的操作员控制命令、I/O设备与接口的有关状态、外部请求等等。

工作周期

模型机设置了六种工作周期状态,用六个周期状态触发器作为它们的标志。其中,四个工作周期(取指、源、目的、执行)用于指令的正常执行,两个工作周期(中断、DMA)用于I/O传送控制。

  • 取指周期FT:在FT中完成的操作是公共性操作;
  • 源周期ST——如果需要从主存中读取源操作数(非寄存器寻址),则进入ST。
  • 目的周期DT——如果需要从主存中读取目的地址或目的操作数(非寄存器寻址),则进入DT。
  • 执行周期ET——取得操作数后,CPU进入ET,这也是各类指令都需经历的最后一个工作阶段。
  • 中断周期IT——除了考虑指令的正常执行,还需考虑外部请求带来的变化。在响应中断请求之后,到执行中断服务程序之前,需要一个过渡期,称为中断周期IT。
  • DMA周期DMAT——响应DMA请求之后,CPU进入DMAT。在DMAT中,CPU交出系统总线的控制权,即MAR、MDR与系统总线断开(呈高阻态),改由DMA控制器控制系统总线,实现主存与外围设备间的数据直传。
    enter description here

    指令周期与操作时间表

    拟定各类指令的执行流程,也就是确定指令执行的具体步骤,即各类信息如何分步地按要求流动。拟定操作时间表,即给出实现上述流程所需的微操作命令序列。其中包含维持一个时钟周期的电位型微命令,以及短暂的脉冲型微命令。操作时间表还将表明出现各种微命令的逻辑条件与时间条件。

模型机的微程序控制器

寻址

bx(基址寄存器)bp(基址指针)si(源变址寄存器)di(目的变址寄存器)
enter description here

使用

以下

1
2
3
4
5
6
7
mov ax,[cx]
mov ax,[ax]
mov ax,[dx]
mov ax,[ds]

mov ax,[bx+bp]
mov ax,[si+di]

全错:(
其它寄存器不能用于寻址,不能基址+基址或变址+变址

注意bp

bp默认段寄存器为ss(栈段),且bp+ 的段寄存器也为ss


可单独使用

1
2
3
4
mov ax,[bx]
mov ax,[si]
mov ax,[di]
mov ax,[bp]

可基址搭配变址

1
2
3
4
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp+si]
mov ax,[bp+di]

加上立即数(idata)等等,详细分类以下

可见

enter description here

ptr

byte ptr或word ptr指明内存单元长度

1
2
mov word ptr ds:[0],1
inc byte ptr [bx];否则是将由地址bx和地址bx+1组成的一个字加一

push、pop一定针对字型(16)

div

其中
除数:8位和16位两种
被除数:默认放在 ax 或 dx和ax 中(dx存放高16位,ax存放低16位)
如果除数为8位,则ax中al存储商ah存储余数;如果除数为16位,则ax存储商dx存储余数

1
div byte ptr ds:[0]

即: (al)=(ax)/((ds)*16+0) 之商
(ah)=(ax)/((ds)*16+0) 之余

1
div word ptr ds:[0]

即: (ax)=[(dx)*10000h+(ax)]/((ds)*16+0) 之商
(dx)=[(dx)*10000h+(ax)]/((ds)*16+0) 之余

转移指令

即修改cs、ip的指令
段内转移:只修改ip,比如jmp ax
段间转移:同时修改cs:ip,比如jmp 1000:0
短转移:IP的修改范围为-128字节~127字节(2的8次方,8位)
近转移:IP的修改范围为-32768字节~32767字节(2的16次方,16位)

JMP标号转移

jmp short s

转到标号处执行命令
段内短转移
范围-128~127

1
2
jmp short s
s: ....

jmp near ptr s

转到标号处执行命令
段内近转移
范围-32768~32767

jmp far ptr s

转到标号处执行命令
段间转移,同时修改CS和IP

JMP地址转移

jmp [16位寄存器]

jmp word ptr 段寄存器:[偏移]

段内转移
从内存单元地址出开始存放着一个字,是转移的目的偏移地址,改变IP

jmp dword ptr 段寄存器:[偏移]

段间转移
从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址,同时改变CS、IP

JCXZ条件转移

短转移

1
if((cx)==0) jmp short 标号;

这么个意思

LOOP

循环,本质也是转移指令

1
2
(cx)--;
if((cx)!=0)jmp short 标号;

NOP

啥也不是,填充

OFFSET

1
2
mov di,offect s
s: ...

取标号地址


使用到栈时,单步debug(t)会覆盖栈ss,而(g)的结果正确
涉及单步debug时的中断原理

寻址

寻址方式是规定如何对地址字段作出解释,以找到操作数

  • 操作数的位置
    • 堆栈
      • 主存开辟(软堆栈)
      • CPU中的寄存器组组成(硬堆栈)
    • 寄存器
      • CPU寄存器
      • 外设寄存器
    • 存储器
      • 主存
      • 外存
        enter description here

CPU能够直接访问的操作数只能存放在主存储器或CPU内的寄存器中
x86具体操作

  • 立即寻址
    操作码字段后面的部分是操作数本身
  • 直接寻址类
    • 直接寻址(主存直接寻址)方式
      enter description here
    • 寄存器寻址(寄存器直接寻址)方式
      enter description here
  • 间接寻址类
    • 间接寻址(主存间接寻址)方式
      enter description here
    • 寄存器间接寻址方式
      enter description here
    • 自增、减型寄存器间址方式
      enter description here
      enter description here
    • 堆栈寻址
      enter description here
    • 多重间接寻址(主存多重间接寻址)方式
      enter description here
  • 变址类
    • 变址寻址
      enter description here
    • 基址寻址
      enter description here
      comparison
    • 基址加变址方式
      图略
    • 相对寻址(浮动编址)
      enter description here
    • 页面寻址
      enter description here

      指令(未完)

  • 传送类指令
    • 一般传送指令
    • 堆栈指令
    • 数据交换指令
  • 输入/输出(I/O)指令
    • 外围设备编址
      ① 对外围设备单独编址
      • 1)单独编址到设备级
      • 2)单独编址到寄存器级:
        ② 外围设备与主存储器统一编址

又换了个人
玉涵大大

inc

等于“++”

1
inc ax

loop

循环
先看例子:计算212

1
2
3
4
5
6
7
8
9
10
11
12
13
assume cs:codee

codee segment

start:mov ax,2
mov cx,11
s: add ax,ax
loop s

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

cx(计数寄存器)存放为循环次数,s为标识指令地址(即从哪里开始循环)
先向cx存次数,标号:执行循环语句,最后loop+标记
本质上是指令指针(IP)的移动,先(cx)减一,再判断其值若不为0则转至s标号处,为0则执行下一条

汇编中 ffffh 编译器认为是标识符,要写为 0ffffh

将ffff;0~ffff:b单元的数据的和,结果储存在dx中

先将8位数据赋值到另一个16位寄存器bx中,再作加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code
code segment

start:
mov ax,0ffffh
mov ds,ax

mov dx,0
mov cx,12

s: mov al,ds:[bx]
mov ah,0
add dx,ax
inc bx
loop s

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

段的应用

下两例:不将数据段栈段和代码段分开
技术算以下8个数据的和,结果放在ax中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
dw 0001h,0002h,0003h,0010h,0020h,0030h,0100h,0200h;写单位数据

start: ;指定开始执行正确代码
mov bx,0
mov ax,0

mov cx,8
s:add ax,cs:[bx];代码段的偏移地址为0、1、2、3的东西即目标数据
add bx,2
loop s

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

栈段
倒置数据

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
assume cs:code
code segment
dw 0001h,0002h,0003h,0004h,0005h,0006h,0007h,0008h;写单位数据
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;sp指向的最后一个零

start:
mov ax,cs
mov ss,ax;指令段-》栈段
mov sp,30h;48即(8+16)*2最后一个零

mov bx,0
mov cx,8
s:push cs:[bx];目标数据入栈
add bx,2
loop s

mov bx,0
mov cx,8
z:pop cx:[bx]
add bx,2
loop z

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


这两段代码认真debug看一下每一步


当然一般是分开的:

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
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
dw 0001h,0002h,0003h,0004h,0005h,0006h,0007h,0008h;写单位数据
datasg ends

stacksg segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;sp指向的最后一个零
stacksg ends

codesg segment

start:
mov ax,stacksg;不能直接写mov ss,stacksg
mov ss,ax;

mov sp,20h;48即(8+16)*2最后一个零

mov ax,datasg
mov ds,ax

mov bx,0
mov cx,8
s:push [bx];即ds:[bx]代替了cs:[bx]
add bx,2
loop s

mov bx,0
mov cx,8
z:pop [bx]
add bx,2
loop z

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

入栈
出栈

其它一些

debug中数据查看是每位反向的
debug默认输入16进制,编译时默认10进制

  • and
    常用
    1
    and al,10111111b
    对指定位置0
  • or
    常用
    1
    or al,10111111b
    对指定位置1
  • xor(异或)
    常用
    1
    xor ax,ax
    对寄存器置零
  • inc自增
  • dec自减
  • 小写字符=大写字符+20h

伪指令

  • dw定义字类型变量,一个字数据占2个字节(16位)单元,读完一个,偏移量加2
  • db定义字节类型变量,一个字节数据占1个字节(8位)单元,读完一个,偏移量加1
  • dd定义双字类型变量,一个双字数据占4个字节(32位)单元,读完一个,偏移量加4
    伪指令db、dw、dd都可以定义字符串,但最多的是用db来定义字符串,第一个原因是dw、dd定义的字符串到了内存中排序是相反的。
  • dup是一个操作符,在汇编语言中同db、dw、dd等一样,也是由编译器识别处理的符号。它是和db、dw、dd等数据定义伪指令配合使用的,用来进行数据的重复。
    dup的使用格式如下:
    db 重复的次数 dup (重复的字节型数据)。
    dw 重复的次数 dup (重复的字型数据)。
    dd 重复的次数 dup (重复的双字数据)。
    dup是一个十分有用的操作符,比如我们要定义一个容量为200个字节的栈段,如果不用dup,则需要大量的代码来进行定义。如果用dup的话:
    1
    2
    3
    stack segment
    db 200 dup (0)
    stack ends

对整数进行格式化

%[index$][标识][最小宽度]转换方式

  • index$
    格式化参数的索引
    1
    2
    String.formart("%1$s %2$s","中国","北京");// "中国 北京"
    String.formart("%2$s %1$s","中国","北京");// "北京 中国"
  • 标识

    ‘-‘

    在最小宽度内左对齐,不可以与“用0填充”同时使用

    ‘#’

    只适用于8进制和16进制,8进制时在结果前面增加一个0,16进制时在结果前面增加0x

    ‘+’

    结果总是包括一个符号(一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制)

    ‘ ‘

    正值前加空格,负值前加负号(一般情况下只适用于10进制,若对象为BigInteger才可以用于8进制和16进制)

    ‘0’

    结果将用零来填充
    简单补零:
    1
    System.out.println(String.format("%02d",1));
    即01
    补零数据自然左对齐(不共用!)

    ‘,’

    只适用于10进制,每3位数字之间用“,”分隔

    ‘(‘

    若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(同‘+’具有同样的限制)
  • 转换方式
    d 十进制 o 八进制 x/X 十六进制
    例:
    1
    2
    3
    4
    System.out.println(String.format("%1$,-9d|", -1234));
    System.out.println(String.format("%1$0(9d", -12));
    System.out.println(String.format("%1$0#5x", 34));
    System.out.println(String.format("%2$02x %1$06X", 28,29));
    OUT:
    1
    2
    3
    4
    -1,234   |
    (0000012)
    0x022
    1d 00001C

对浮点数进行格式化

%[index$][标识][最少宽度][.精度]转换方式

  • 其中转换方式:
    • e/E 带有效位数和指数的十进制数
    • f 普通浮点型
    • g/G 系统自动选择用普通表示方式还是科学计数法方式
    • a/A 带有效位数和指数的十六进制浮点数

      .精度 即小数点后位数

对字符、字符串进行格式化

%[index$][标识][最小宽度]c/s

1
2
3
4
5
6
7
String test = "hello word";
String str = String.format("|%-15s|", test);
System.out.println(str);

char tamp = 'h';
str = String.format("(%15c)", tamp);
System.out.println(str);

OUT:

1
2
|hello word     |
( h)

对日期类型进行格式化

以下日期和时间转换的后缀字符是为 ‘t’ 和 ‘T’ 转换定义的

以下转换字符用来格式化时间:

  • ‘H’ 24 小时制的小时,被格式化为必要时带前导零的两位数,即 00 - 23。
  • ‘I’ 12 小时制的小时,被格式化为必要时带前导零的两位数,即 01 - 12。
  • ‘k’ 24 小时制的小时,即 0 - 23。
  • ‘l’ 12 小时制的小时,即 1 - 12。
  • ‘M’ 小时中的分钟,被格式化为必要时带前导零的两位数,即 00 - 59。
  • ‘S’ 分钟中的秒,被格式化为必要时带前导零的两位数,即 00 - 60 (”60” 是支持闰秒所需的一个特殊值)。
  • ‘L’ 秒中的毫秒,被格式化为必要时带前导零的三位数,即 000 - 999。
  • ‘N’ 秒中的毫微秒,被格式化为必要时带前导零的九位数,即 000000000 - 999999999。
  • ‘p’ 特定于语言环境的 上午或下午 标记以小写形式表示,例如 “am” 或 “pm”。使用转换前缀 ‘T’ 可以强行将此输出转换为大写形式。
  • ‘z’ 相对于 GMT 的 RFC 822 格式的数字时区偏移量,例如 -0800。
  • ‘Z’ 表示时区缩写形式的字符串。Formatter 的语言环境将取代参数的语言环境(如果有)。
  • ‘s’ 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的秒数,即 Long.MIN_VALUE/1000 与 Long.MAX_VALUE/1000 之间的差值。
  • ‘Q’ 自协调世界时 (UTC) 1970 年 1 月 1 日 00:00:00 至现在所经过的毫秒数,即 Long.MIN_VALUE 与 Long.MAX_VALUE 之间的差值。

以下转换字符用来格式化日期:

  • ‘B’ 特定于语言环境的月份全称,例如 “January” 和 “February”。
  • ‘b’ 特定于语言环境的月份简称,例如 “Jan” 和 “Feb”。
  • ‘h’ 与 ‘b’ 相同。
  • ‘A’ 特定于语言环境的星期几全称,例如 “Sunday” 和 “Monday”
  • ‘a’ 特定于语言环境的星期几简称,例如 “Sun” 和 “Mon”
  • ‘C’ 除以 100 的四位数表示的年份,被格式化为必要时带前导零的两位数,即 00 - 99
  • ‘Y’ 年份,被格式化为必要时带前导零的四位数(至少),例如,0092 等于格里高利历的 92 CE。
  • ‘y’ 年份的最后两位数,被格式化为必要时带前导零的两位数,即 00 - 99。
  • ‘j’ 一年中的天数,被格式化为必要时带前导零的三位数,例如,对于格里高利历是 001 - 366。
  • ‘m’ 月份,被格式化为必要时带前导零的两位数,即 01 - 13。
  • ‘d’ 一个月中的天数,被格式化为必要时带前导零两位数,即 01 - 31
  • ‘e’ 一个月中的天数,被格式化为两位数,即 1 - 31。

以下转换字符用于格式化常见的日期/时间组合:

  • ‘R’ 24 小时制的时间,被格式化为 “%tH:%tM”
  • ‘T’ 24 小时制的时间,被格式化为 “%tH:%tM:%tS”。
  • ‘r’ 12 小时制的时间,被格式化为 “%tI:%tM:%tS %Tp”。上午或下午标记 (‘%Tp’) 的位置可能与语言环境有关。
  • ‘D’ 日期,被格式化为 “%tm/%td/%ty”。
  • ‘F’ ISO 8601 格式的完整日期,被格式化为 “%tY-%tm-%td”。
  • ‘c’ 日期和时间,被格式化为 “%ta %tb %td %tT %tZ %tY”,例如 “Sun Jul 20 16:17:00 EDT 1969”。 参考博客

基础

mov

1
2
3
4
5
6
mov ax,b  //寄存器,数据
mov bx,ax //寄存器,寄存器
mov ax,[bx]//寄存器,内存单元
mov [bx],ax//内存单元,寄存器(这里注意编译器中不能写mov ax,[0]会当作 mov ax,0 但debug中可以)
mov ds,ax //段寄存器,寄存器
mov axds//寄存器,段寄存器

add sub

1
2
3
4
add/sub ax,8H
add/sub ax,bx
add/sub ax,[bx]
add/sub [bx],ax

add ds,ax;错误,不能直接操作ds(数据段寄存器)

累加数据段中前三个字型数据

将123B0H~123BAH的内存单元定义为数据段

1
2
3
4
5
6
mov ax,123BH
mov ds,ax
mov ax,0//默认了ds中的123BH段地址
add ax,[0]
add ax,[2]
add ax,[4]

一个字型为两个字节型

寄存器CS和IP中存放着当前指令的段地址和偏移地址
同样 有段寄存器SS 用来存放栈顶的段地址
寄存器SP 存放栈顶的偏移地址
则:SS:SP 指向栈顶元素

PUSH POP

1
2
3
4
push ax
push [0]
pop ds
push es

以字为单元进行
则PUSH在存放数据时 SP先减二再取数据
POP 先拿数据再SP加二
没有溢出、出界检测

使用 sub ax,ax较mov ax,0进行清零操作更短(两个字节、三个字节)

1
2
3
4
5
6
7
8
9
10
mov ax,1000H
mov ds,ax
mov ax,2266H
mov [0],ax ;与以下等效

mov ax,1000H
mov ss,ax
mov sp,2
mov ax,2266H
push ax

开写

参考Win10(64位)安装汇编环境(MASM)
配置一下这个“DOSBox 0.74 Options.bat”最后

1
2
3
4
5
6
[autoexec]
# Lines in this section will be run at startup.
# You can put your MOUNT lines here.
mount d: d:\DOSBox\masm
d:
dir

然后
masm name.asm; (加分号!)
link name.obj;
name.exe

debug使用

  • r(register)寄存器操作
    • 只输入“r”来显示寄存器内容
    • “r ax”再输入值,可指定改变某个1寄存器值

  • d(dump)查看内存中的信息
    • 用“d 段地址:段偏移地址”显示从指定内存单元开始的128个单元的内容
    • 接着使用“d”命令,可列出后续的内容
    • 查看指定范围,用“d 段起始地址:起始偏移地址 结尾偏移地址”
      1
      d 1000:0 9

  • e(edit)改写内存中的内容
    • 用“e 起始地址(段地址:段偏移地址) 数据 数据…”来修改从起始地址开始的内存中的内容
    • 也可以用e命令以提问的方式来逐个地修改从某一地址开始的内存单元中的内容,以从1000:10单元开始为例,步骤如下:
      • 输入e 1000:10,按enter键
      • debug显示起始地址1000:0010,和第一个单元的原始内容:00,然后光标停在“.”后面提示输入想要写入的数据,此时有两种选择:一是输入数据,然后按空格键,即输入数据修改当前的内存单元;二位不输入数据,直接按空格键,则不对当前内存单元进行改写。
      • 当前单元处理完后(不论改写或者没有改写,只要按了空格键,就表示处理完成),debug将显示下一个内存单元的原始内容,并提示修改。
      • 所以希望改写的内存单元改写完成后,按enter键,e命令操作结束。


  • u(un-assembly)反汇编机器码
    • “u 段起始地址:起始偏移地址 结尾偏移地址 ”

  • t(trace)单步调试命令
    • 老规矩,一步一步走

  • a(assembly)以汇编指令形式写指令到内存中
    • 代替e命令的机器码指令,直接用汇编码写入

  • p执行汇编程序,单步跟踪。
    • 执行 int 21
    • t p区别
      • 单步跟踪命令(单步进入(step in)):t命令是单步执行,是真的单步执行,碰到任何地方,都会一步步执行,遇到子程序,也会进入里面一步步执行再返回
      • 单步执行命令(单步通过(step over)):p命令,大多数情况与t一样,只有当遇到call调用子程序的时候,p命令直接执行完这个程序,而t命令会进入里面一步步执行。
    • 结束debug一般”p”+”q”+”exit”

  • g执行汇编指令
    • 使用方法是:g [=起始地址] [断点地址],从起始地址开始执行到断点地址。如果不设置断点,则程序一直运行到中止指令停止;如果不设置起始地址,则会向下运行到断点地址

这个MOOC不得行啊
后面还是看的B站orz
回头来看,还是MOOC讲的系统

基本

补码运算

CPU基本结构


两种执行方式及特点


8086CPU结构

8086CPU寄存器





注意AF、OF区别


堆栈及操作

HEXO技巧

使用:

1
2
<img src="XXX" width="50%" height="50%" />
<img src="XXX" width=256 height=256 />

进行可控插入图片