8086是16位寄存器和16位外部数据总线,20位地址总线,寻址为1MB的地址空间。
架构为基于累加器的寄存器-内存架构,可以对内存数直接操作,不同于MIPS的寄存器-寄存器架构运算数和结果都在寄存器中。这意味着,和MIPS相比,一方面寄存器-内存的架构使得8086中有许多可以直接操作内存的指令,避免了进行取-计算-存这样的步骤;另一方面,基于累加器的特点使得8086指令集中INC和DEC的方便高效尤为突出,运算指令集也很自然地呈现了不一样的结构。
AX | 乘除法的第一个操作数;乘除法返回的部分(16位,8位全部)结果; 串操作中load和store操作的寄存器(AX,AL); 系统调用功能号(AH); 可作为通用寄存器或寄存器传参进行函数调用时返回值存放处 |
|
---|---|---|
BX | 间接寻址的基址寄存器; 通用寄存器 |
|
CX | LOOP计数器; 移位寄存器(移位次数) |
|
DX | 16位乘法中乘积高16位,32位/16位被除数高位及余数; 部分系统调用内容 DL |
|
SP | 堆栈指针 | |
BP | 相对堆栈段的相对寻址的基址寄存器,相对SS段寻址; 堆栈传参的基址寄存器 |
|
SI | 串操作的源变址寄存器; 基址加变址相对寻址的变址寄存器 相对于DS寻址 |
|
DI | 串操基址加变址相对寻址的变址寄存器作的目的变址寄存器; 基址加变址相对寻址的变址寄存器 相对于DS或ES(串操作)寻址 |
段寄存器包括CS(代码段)、DS(数据段)、SS(堆栈段)、ES(附加段)
IP,当前代码在CS中的偏移。类似MIPS的PC。
8086有9个一位标志寄存器(标志位),放在16位的寄存器PSW(program status word)中。
其中特别注意,ZF表示结果是否为0,结果为0则ZF=1; SF表示结果是否为负,结果小于0则SF=1。
段式内存管理,相比之前OS中做的分页式内存管理更加灵活。
内存中(非堆栈段)存储内存由低到高增长,存储遵循低字在前、高字在后,低字节在前、高字节在后。
堆栈也遵循低字节在前、高字节在后,
PUSH | (1) SP -= 2 (2) ->[SP] |
POP | (1) [SP] -> (2) SP += 2 |
地址表示与寻址:
段地址: 偏移
地址 = 段地址 << 4 + 偏移
基本形式:
op dst src
第一个小程序:反向冒泡排序
STACK1 SEGMENT PARA STACK ;?
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK1 ENDS
DATA1 SEGMENT
TABLE_LEN DW 20
TABLE DW 200,300,400,10,20,0,1,8
DW 41H,40,42H,50,60,0FFFFH,2,3
DW 15H,0613H,1506H,1061H
DATA1 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1,DS:DATA1,SS:STACK1 ;ASSUME CS:CODE1 \n ASSUME DS:DATA1
MAIN PROC FAR ;main{
MOV AX,STACK1
MOV SS,AX
MOV SP,STACK_BTM
MOV AX,DATA1
MOV DS,AX ;init SS,SP,DS
LP1: MOV BX,1
MOV CX,TABLE_LEN
DEC CX ;--
MOV SI,OFFSET TABLE
LP2: MOV AX,[SI]
CMP AX,[SI+2]
JAE CONTINUE
XCHG AX,[SI+2]
MOV [SI],AX
MOV BX,0
CONTINUE: ADD SI,2
LOOP LP2
CMP BX,1
JZ PRINTRES
JMP SHORT LP1
; print hex results
PRINTRES: MOV CX,20
MOV SI,OFFSET TABLE
LP4: PUSH CX
MOV CX,4;pro counter for loop of rol
MOV BX,[SI]
LP3: PUSH CX
MOV CL,4
ROL BX,CL
MOV AL,BL
AND AL,0FH
ADD AL,30H
CMP AL,39H
JBE DISP
ADD AL,'A'-'9'-1
DISP: MOV DL,AL
MOV AH,2
INT 21H
POP CX
LOOP LP3
MOV DL,' '
MOV AH,2
INT 21H
POP CX
ADD SI,2
LOOP LP4
EXIT: MOV AX,4C00H
INT 21H ;return 0
MAIN ENDP ;}
CODE1 ENDS
END MAIN ;total end begin: main function
示例程序中将数组冒泡排序并按十六进制输出,使用了多重LOOP循环、相对寻址、跳转、系统调用。
第一次作业中写了将数组十进制、十六进制输出的代码,上面已经展示过16进制输出了,下面是十进制输出的代码片段。
; print results at base 10
PRINTRES:MOV CX,20
MOV SI,OFFSET TABLE
LP3: PUSH CX
MOV CX,5
MOV AX,[SI]
MOV BX,10
LP4: XOR DX,DX
DIV BX
OR DL,30H
PUSH DX
LOOP LP4
MOV CX,5
LP5: POP DX
MOV AH,2
INT 21H
LOOP LP5
MOV DL,' '
MOV AH,2
INT 21H
POP CX
ADD SI,2
LOOP LP3
第三次作业总结了几个关于数字进制转换的函数,包括输入输出。
CHANGEBASEIN PROC
;PARAM: input_string offset, base
;
PUSH BP
MOV BP,SP
;get params reversely
PUSH SI
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV SI,[BP+6]
MOV BX,[BP+4]
PUSH 0
READ_DIGIT:
XOR AX,AX
LODSB
CMP AL,'$'
JZ READ_OK
CMP AL,'A'
JB DGT2NUM
ADD AX,-'A'+0AH+'0'
DGT2NUM:SUB AX,30H
MOV CX,AX
POP AX
MUL BX
ADD AX,CX
PUSH AX
JMP READ_DIGIT
READ_OK:POP AX
MOV [BP+6],AX
POP DX
POP CX
POP BX
POP AX
POP SI
POP BP
RET 2
CHANGEBASEIN ENDP
CHANGEBASEOUT PROC
;PARAM: number, base
;
PUSH BP
MOV BP,SP
;get params reversely
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AX,[BP+6]
MOV BX,[BP+4]
XOR CX,CX
GET_DIGIT:
XOR DX,DX
DIV BX
CMP DX,0AH
JB DIGIT2CHAR
ADD DX,'A'-0AH-'0'
DIGIT2CHAR:ADD DX,30H
PUSH DX
INC CX
CMP AX,0
JNZ GET_DIGIT
POP_DIGITS:
POP DX
MOV AH,2
INT 21H
LOOP POP_DIGITS
POP DX
POP CX
POP BX
POP AX
POP BP
RET 4
CHANGEBASEOUT ENDP
CHANGEBASEIN将一个指定基的字符串转换为一个数,CHANGEBASEOUT将一个内存数以指定的进制输出。将这两个函数组合可以实现任意进制转换。
串操作的指令默认源是DS:SI,目的是ES:DI,默认load store寄存器是AX/AL。
插入
INSERT PROC
;PARAMS:OFFSET STR, char, pos
PUSH BP
MOV BP,SP
PUSH SI
PUSH AX
PUSH CX
MOV SI,[BP+8]
ADD SI,[BP+4]
XOR CX,CX
CLD
LP00:INC CX
LODSB
CMP AL,'$'
JNZ LP00
MOV DI,SI
DEC SI
STD
REP MOVSB
INC SI
MOV AX,[BP+6]
STOSB
POP CX
POP AX
POP SI
POP BP
RET 6
INSERT ENDP
删除
DELETE PROC
;PARAMS:OFFSET STR, pos
PUSH BP
MOV BP,SP
PUSH SI
PUSH DI
PUSH AX
MOV SI,[BP+6]
ADD SI,[BP+4]
MOV DI,SI
INC SI
CLD
LP9:MOV AL,[SI]
MOVSB
CMP AL,'$'
JNZ LP9
POP AX
POP DI
POP SI
POP BP
RET 4
DELETE ENDP
修改
MODIFY PROC
;PARAMS:OFFSET STR, char, pos
PUSH BP
MOV BP,SP
PUSH DI
PUSH AX
MOV DI,[BP+8]
ADD DI,[BP+4]
MOV AX,[BP+6]
STOSB
POP AX
POP DI
POP BP
RET 6
MODIFY ENDP
查找
FIND PROC
;PARAMS:OFFSET STR1, OFFSET STR2,LEN STR2
PUSH BP
MOV BP,SP
PUSH SI
PUSH DI
PUSH CX
MOV SI,[BP+8]
MOV [BP+8],0 ;RETURN VALUE
PUSH SI
LP10:POP SI
INC SI
PUSH SI
MOV DI,[BP+6]
MOV CX,[BP+4]
CLD
REPE CMPSB
CMP CX,0
JZ EQUAL
CMP [SI],'$'
JZ NOTEQ
JMP LP10
EQUAL:
INC [BP+8]
NOTEQ:
POP SI
POP CX
POP DI
POP SI
POP BP
RET 4;One return value, 1 for issub, 0 for not
FIND ENDP
字符串比较
STRCMP PROC
;PARAMS STR1,STR2,LEN1,LEN2
;RETURN 1 FOR STR1>STR2; 0 FOR EQUAL; -1 FOR STR1STR2
SMALL:DEC [BP+10]
OK: POP CX
POP DI
POP SI
POP BP
RET 6
STRCMP ENDP
大小写转换
TOUPPER PROC
;PARAMS:OFFSET STR
PUSH BP
MOV BP,SP
PUSH SI
PUSH AX
MOV SI,[BP+4]
XOR AX,AX
CLD
LOW2UP:LODSB
CMP AL,'$'
JZ L2UOK
CMP AL,'Z'
JBE LOW2UP
SUB [SI-1],20H
JMP LOW2UP
L2UOK:
POP AX
POP SI
POP BP
RET 2
TOUPPER ENDP
TOLOWER PROC
;PARAMS:OFFSET STR
PUSH BP
MOV BP,SP
PUSH SI
PUSH AX
XOR AX,AX
CLD
MOV SI,[BP+4]
UP2LOW:LODSB
CMP AL,'$'
JZ U2LOK
CMP AL,'a'
JAE UP2LOW
ADD [SI-1],20H
JMP UP2LOW
U2LOK:
POP AX
POP SI
POP BP
RET 2
TOLOWER ENDP
复制
COPY PROC
;PARAMS: SRC, DET
PUSH BP
MOV BP,SP
PUSH SI
PUSH DI
PUSH AX
MOV SI,[BP+6]
MOV DI,[BP+4]
CP: LODSB
DEC SI
MOVSB
CMP AL,'$'
JNZ CP
POP AX
POP DI
POP SI
POP BP
RET 4
COPY ENDP
输出字符串数组
OUTPUT_STR_ARRAY PROC
;DI ARRAY_HEAD
;DX ALL_STR_LEN
PUSH BP
MOV BP,SP
PUSH AX
PUSH DI
PUSH SI
PUSH DX
MOV DI,[BP+6]
MOV DX,[BP+4]
PUSH DX
MOV CX,DX
MOV AH,2
LP_HEAD: MOV DL,[DI]
INC DI
CMP DL,'$'
JNZ NOTEND
MOV DL,0AH
INT 21H
MOV DL,0DH
NOTEND: INT 21H
LOOP LP_HEAD
POP DX
POP DX
POP SI
POP DI
POP AX
POP BP
;RET
RET 4
OUTPUT_STR_ARRAY ENDP
; print results at base 10
PRINTRES PROC
;PARAMS:
;DX BUFF LEN
;SI HEAD OFFSET
PUSH BP
MOV BP,SP
PUSH CX
PUSH DX
PUSH BX
PUSH SI
MOV CX,[BP+6];MOV CX,DX
MOV SI,[BP+4]
LP3: PUSH CX
MOV CX,5
MOV AX,[SI]
MOV BX,10
LP4: XOR DX,DX
DIV BX
OR DL,30H
PUSH DX
LOOP LP4
MOV CX,5
LP5: POP DX
MOV AH,2
INT 21H
LOOP LP5
MOV DL,' '
MOV AH,2
INT 21H
POP CX
ADD SI,2
LOOP LP3
POP SI
POP BX
POP DX
POP CX
POP BP
RET 4
PRINTRES ENDP
字符串数组字典序排序
DICT_SORT PROC
;PARAMS: offset of head of array, buffer len, array len
PUSH BP
MOV BP,SP
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV DI,[BP+8] ;OFFSET BUFF
MOV DX,[BP+6] ;BUFF_LEN
PUSH DI
PUSH DX
CALL OUTPUT_STR_ARRAY
;get str head pointer
MOV DI,[BP+8]
MOV CX,4
MOV SI,OFFSET ADDR_STR
MOV BX,OFFSET STR_LEN
MOV [SI],DI
LP_HEAD2: MOV DL,[DI]
INC DI
CMP DL,'$'
JNZ LP_HEAD2
MOV AX,DI
SUB AX,[SI]
MOV [BX],AX
ADD BX,2
CMP CX,1
JBE LASTSTR
ADD SI,2
MOV [SI],DI
LASTSTR: LOOP LP_HEAD2
;MOV AH,2
;MOV DL,'a'
;INT 21H
;initiate index
MOV CX,[BP+4]
MOV AX,0
MOV SI,OFFSET INDEX
INDEX_INIT:MOV [SI],AX
ADD SI,2
INC AX
LOOP INDEX_INIT
LP1: MOV DX,1
MOV CX,4
DEC CX ;--
MOV BX,OFFSET INDEX
LP2: PUSH CX
MOV SI,[BX] ; index of current string
MOV DI,[BX+2] ;
SAL SI,1
SAL DI,1
MOV CX,[SI+OFFSET STR_LEN]
MOV SI,[SI+OFFSET ADDR_STR]
MOV DI,[DI+OFFSET ADDR_STR]
;CMP
CLD
REPE CMPSB
;MOV AH,0
;INT 16H
POP CX
JBE CONTINUE;STR1 <= STR2
MOV AX,[BX]
XCHG AX,[BX+2]
MOV [BX],AX
MOV DX,0
CONTINUE:ADD BX,2
LOOP LP2
CMP DX,1
JZ END
JMP SHORT LP1
END:
PUSH [BP+4]
PUSH OFFSET INDEX
CALL PRINTRES
MOV CX,[BP+4]
MOV DI,OFFSET TMP_BUFF
MOV BX,OFFSET INDEX
LP6: MOV SI,[BX]
PUSH CX
SAL SI,1
MOV CX,[SI+OFFSET STR_LEN]
MOV SI,[SI+OFFSET ADDR_STR]
CLD
REP MOVSB
POP CX
ADD BX,2
LOOP LP6
MOV CX,[BP+6]
MOV SI,OFFSET TMP_BUFF
MOV DI,[BP+8]
REP MOVSB
PUSH [BP+8]
PUSH [BP+6]
CALL OUTPUT_STR_ARRAY
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP BP
RET 6
DICT_SORT ENDP
procname PROC
...
RET
procname ENDP
如上形式可以定义一个函数(过程),可以使用寄存器传参数(如DX,DI)与返回值(如AX),也可使用堆栈传参。
第二次作业中多使用寄存器传参,第三次第四次中使用堆栈传参。
堆栈传参使用BP作为子程序寻址基址,注意PUSH BP之后立刻MOV BP,SP,不然会发生寻址错误。另外由于堆栈FILO的性质,参数压栈和取参数的顺序相反。
子程序看到的堆栈情况简化:
... | 父程序保存的堆栈内容 |
BP+4 | 最后一个参数 |
BP+2 | IP |
BP | 子程序PUSH的原BP |
... | 子程序保存的堆栈内容 |
堆栈传参尤其注意保护现场与堆栈平衡。
下面是两个递归调用的例子,分别是计算阶乘和计算斐波那契数。
STACK1 SEGMENT PARA STACK ;?
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK1 ENDS
DATA1 SEGMENT
X DW 4
DATA1 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1,DS:DATA1,SS:STACK1 ;ASSUME CS:CODE1 \n ASSUME DS:DATA1
MAIN PROC FAR ;main{
MOV AX,STACK1
MOV SS,AX
MOV SP,STACK_BTM
MOV AX,DATA1
MOV DS,AX ;init SS,SP,DS
PUSH 8
CALL FACT
;POP & PUSH
PUSH 10
CALL CHANGEBASEOUT
EXIT: MOV AX,4C00H
INT 21H ;return 0
MAIN ENDP ;}
FACT PROC
;PARAM :N
;RETURN N!
PUSH BP
MOV BP,SP
PUSH AX
PUSH BX
PUSH DX
CMP [BP+4],1
JBE END
PUSH [BP+4]
DEC [BP+4]
PUSH [BP+4]
CALL FACT
POP AX ;(N-1)!
POP BX ;N
MUL BX
MOV [BP+4],AX
END:
POP DX
POP BX
POP AX
POP BP
RET
FACT ENDP
CHANGEBASEOUT PROC
;PARAM: number, base
;
PUSH BP
MOV BP,SP
;get params reversely
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AX,[BP+6]
MOV BX,[BP+4]
XOR CX,CX
GET_DIGIT:
XOR DX,DX
DIV BX
CMP DX,0AH
JB DIGIT2CHAR
ADD DX,'A'-0AH-'0'
DIGIT2CHAR:ADD DX,30H
PUSH DX
INC CX
CMP AX,0
JNZ GET_DIGIT
POP_DIGITS:
POP DX
MOV AH,2
INT 21H
LOOP POP_DIGITS
POP DX
POP CX
POP BX
POP AX
POP BP
RET 4
CHANGEBASEOUT ENDP
CODE1 ENDS
END MAIN ;total end begin: main function
后续持续更新中,欢迎交流指正。