8086 汇编

微机原理

本文用于记录一下微机原理8086汇编作业😁

8086/8088指令系统

一、内存寻址

直接寻址

物理地址=段地址×16+偏移地址

  • 段地址由段寄存器(如DS)提供。
  • 偏移地址是指令中的地址值(在[1234H]中的1234H)。

在指令 MOV AL, [1234H] 中,如果假设 DS = 2000H,那么:

物理地址=2000H×10H+1234H=21234H

乘以 10H 是因为 x86 架构中的段地址是以 16 字节为单位的,所以需要乘以 10H(或者说 16)来将段地址转换成字节为单位的地址。

简单地说,DS * 16段地址转换成字节地址,然后再加上偏移地址,就得到了完整的物理地址。

注:

凡是含有 BP 的寻址方式中,都是对堆栈区的存储单元寻址的,因此其段地址由段寄存器 SS 提供

二、移位指令

1 逻辑左移指令 SHL

SHL des, CL/1

image-20231015201508602

  • 移位数=1,直接写入指令;大于1时,应送入 CL 预先设定。

  • 无符号数要增倍时,可使用 SHL 指令。

2 逻辑右移指令 SHR

SHR des, CL/1

image-20231015201744513

  • 移位数 =1,直接写入指令;大于1时,应送入 CL 预先设定。

  • 无符号数要减半时,可使用 SHR 指令。

3 算术左移指令 SAL

与逻辑左移指令功能完全相同

SAL des, CL/1

image-20231015201508602

  • 移位数=1,直接写入指令;大于1时,应送入 CL 预先设定。

  • 带符号数要增倍时,可使用 SAL 指令。

4 算术右移指令 SAR

SAR des, CL/1

image-20231015202158143

  • 移位数 =1,直接写入指令;大于1时,应送入 CL 预先设定。

  • 带符号数要减半时,可使用 SAR 指令。

在 8086 汇编语言中,当处理带符号数时,通常使用 SAR 指令进行右移(带符号右移),而处理无符号数时,使用 SHR 指令进行右移(逻辑右移)

8086/8088 汇编

一、 伪指令

1 符号定义伪指令

EQU 伪指令

<symbol> EQU <expression>

指令功能: 给符号名赋值

PORT EQU 88H;	定义符号 PORT 表示 88H
DATA EQU PORT+2;	定义符号 DATA 表示 PORT+2

注:

  • EQU 定义的符号,不能重新再定义,除非用 PURGE 伪指令解除;

  • 如果在表达式中用了其他符号,必须事先定义。

解除符号定义伪指令

PURGE symbol1, symbol2, ...

指令功能: 撤消EQU 赋值的符号名, 以便于赋予新值。

PURGE PORT, DATA;	撤销 EQU 对 PORT, DATA 的赋值

等号(赋值)伪指令

<symbol> = <expression>

指令功能: 与 EQU 类似,唯一的区别是 “=” 可以随时对符号名赋新值, 而不必使用 PURGE 伪指令。

PORT = 88H;	定义符号 PORT 表示 88H
DATA = PORT +2;	定义符号 DATA 表示 PORT+2

2 内存数据定义伪指令

数据定义语句

变量名 命令 参数1, 参数2, ...

功能: 分配存储单元,存入指定的数据。

数据定义语句的命令有:

  • DB: 定义字节数据

  • DW: 定义字数据

  • DD: 定义双字数据

  • DQ: 定义8字节数据

  • DT: 定义10字节数据

复制操作符

<复制次数> DUP 数据1, 数据2, ... 

功能:汇编程序把所定义的数据按先后次序连续分配存储空间,所起的名称只代表第一个单元的字符。

VAR1 DB 32H, 'ABC'
VAR2 DW 1234H, 40H, 'AB'
     DD 12345678H
     DB ?, 11000011B
ARRY1 DB 2 DUP (0, 1)
ARRY2 DW 2 DUP (?, 1)

补充- 补码、原码、反码

一. 机器数和机器数的真值

1、机器数

一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用机器数的最高位存放符号,正数为0,负数为1。

比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是0000 0011。如果是 -3 ,就是 100 00011 。

那么,这里的 0000 0011 和 1000 0011 就是机器数。

2、机器数的真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。

例如上面的有符号数 1000 0011,其最高位1代表负,其真正数值是 -3,而不是形式值131(1000 0011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值

例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

原码, 反码, 补码的基础概念和计算方法

在探求为何机器要使用补码之前,让我们先了解原码、反码和补码的概念。对于一个数,计算机要使用一定的编码方式进行存储,原码、反码、补码是机器存储一个具体数字的编码方式。

1、原码

**原码就是符号位加上真值的绝对值,**即用第一位表示符号,其余位表示值。比如:如果是8位二进制:

[+1]原= 0000 0001

[-1]原= 1000 0001

第一位是符号位,因为第一位是符号位,所以8位二进制数的取值范围就是:(即第一位不表示值,只表示正负。)

[1111 1111 , 0111 1111]

[ − 127 , 127 ] [-127 , 127] [127,127],因为根据定义数字 0 的原码有两种不同形式

[+0]原 = 00000000B, [-0]原 = 10000000B

2、反码

反码的表示方法是:

正数的反码是其本身;

负数的反码是在其原码的基础上符号位不变,其余各个位取反

[+1] = [0000 0001]原 = [0000 0001]反

[-1] = [1000 0001]原 = [1111 1110]反

可见如果一个反码表示的是负数,人脑无法直观的看出来它的数值。通常要将其转换成原码再计算。

根据定义数字 0 的反码同样也有两种不同形式

[+0]反 = 00000000B, [-0]原反= 11111111B

3、补码

补码的表示方法是:

正数的补码就是其本身;

负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。(也即在反码的基础上+1)

[+1] = [0000 0001]原= [0000 0001]反= [0000 0001]补

[-1] = [1000 0001]原= [1111 1110]反= [1111 1111]补

对于负数,补码表示方式也是人脑无法直观看出其数值的。通常也需要转换成原码再计算其数值。

将补码变为原码和将原码变为补码操作方法一致。

参考

原码、反码、补码 - 知乎 (zhihu.com)

微机作业

DosBox DeBug 指令记录

编译 .asm 文件,链接 .obj 文件生成 .exe 文件

masm prob1.asm 
link prob1;

image-20231021153500996

开始 DeBug

debug prob1.exe

# 常用 DeBug 指令
U - U (Unassemble) 反汇编一段内存, 将内存中的机器指令翻译成汇编指令
R - R (Registers) 查看和修改寄存器的值
D - D (Dump) 用于查看内存的内容
T - T (Trace) 按照程序的正常执行顺序逐条执行指令,但是不进入子程序
G - G (Go) 用于运行程序
Q - Q (Quit) 退出调试

补充

D DS:0000 - 查看数据段中偏移地址为0000的数据
D SS:0000 - 查看堆栈段中偏移地址为0000的数据 

作业 问题1

a、b、c、s 为带符号的字数据,求 s = ( a ∗ b + c ) / a s=(a*b+c)/a s=(ab+c)/a,结果的商存放在 s 中,余数 不计。s 的偏移地址是自己学号的和

汇编代码如下:

DATA SEGMENT
     ORG 0009H   ; 设置偏移地址,定义 s 后 s 的偏移地址为09H

     S DW ?
     A DW 1234H  ; 定义字数据A  
     B DW 5678H  ; 定义字数据B
     C DW 9ABCH  ; 定义字数据C
     A_B DD ? 	  ; 定义双字数据用于储存 a*b 的结果
DATA ENDS

CODE SEGMENT
    ASSUME CS: CODE, DS: DATA
START:
     MOV AX, DATA      ; 将数据段地址加载至AX
     MOV DS, AX        ; 段地址送至DS

     MOV AX, [A]    ; 将A的值加载到AX
     IMUL WORD PTR [B]       ; 带符号数B X AX -> DX, AX

     MOV [A_B], AX	; 将 a*b 的结果的低16位 送至 A_B 的低16位
     MOV [A_B+2], DX	; 将 a*b 的结果的高16位 送至 A_B 的高16位	

     MOV AX, [C]		; 将C的值送至AX
     CWD 			; 将C扩展为32位带符号数,DX储存其高16位,AX储存其低16位

     ADD AX, [A_B]	; C的低16位和A_B的低16位相加
     ADC DX, [A_B+2]	; 带进位加法,考虑低16位相加的进位

     IDIV WORD PTR [A]       ; DX, AX / A -> AX(商),DX(余数)
     MOV [S], AX       ; 将运算结果商储存到变量S中
     ; 程序结束
     MOV AH, 4CH  ; 设置DOS功能号,表示程序结束
     INT 21H      ; 调用DOS中断

CODE ENDS
    END START
    

分析

首先利用 ORG 指令将程序起始地址设置为 09H,即本人学号的学号的和

在数据段中首先定义 S,则其偏移地址即为 09H

AB 相乘后为32位双字数据,且为带符号数,所以不能直接和 C 相加,需要先将 C 扩展为双字数据然后相加,使用 CWD 指令

相加时先将两者的低16位相加,再考虑低16位的进位再将高16位相加得到结果,使用 ADC 指令

s, a, b, c 都为带符号数,乘法采用 IMUL,除法采用 IDIV

DeBug

debug prob1.exe

U 反汇编一段内存,将内存中的机器指令翻译成汇编指令。这里我们可以看到 S 的偏移地址为 0009H

image-20231021205959868

R 查看寄存器中的数据,D 查看内存中的数据

image-20231021210134544

T 单步执行

image-20231021211156650

D DS:0009 查看数据段中偏移地址为 0009H 的数据

image-20231021210801929

G 运行程序,查看 S 的值

image-20231021211032225

验证结果 s = ( a ∗ b + c ) / a s = (a*b+c)/a s=(ab+c)/a

a ∗ b = 1234 H × 5678 H = 06260060 H a*b = 1234H \times 5678H = 06260060H ab=1234H×5678H=06260060H 结果正确

c = 9 A B C H c = 9ABCH c=9ABCH 为带符号数且为复数其原码为 E 544 H E544H E544H,其数值绝对值为 6544 6544 6544
s = ( ( a ∗ b + c ) / a = 06260060 H − 6544 H ) / 1234 H = 5672 H s = ((a*b+c)/a = 06260060H - 6544H)/1234H = 5672H s=((ab+c)/a=06260060H6544H)/1234H=5672H
验证结果正确

image-20231021212212761

作业问题2_1

设 BX=84F0H,若 BX 为无符号数,求BX/2

汇编代码如下

DATA SEGMENT
    B DW 84F0H  ; 定义字数据B=84F0H
    RESULT DW ? ; 用于储存结果的变量
DATA ENDS

CODE SEGMENT
    ASSUME CS: CODE, DS: DATA
START:
    MOV AX, DATA ; 将数据段地址加载到AX
    MOV DS, AX   ; 设置DS指向数据段

    LEA DI, B    ;
    MOV AX, [DI] ; 将B的值加载到AX
    SHR AX, 1    ; 将AX逻辑右移一位,无符号数除以2 
    MOV RESULT, AX  ; 将结果储存到RESULT变量中
    ; 程序结束
    MOV AH, 4CH  ; 设置DOS功能号,表示程序结束
    INT 21H      ; 调用DOS中断

CODE ENDS
    END START

验证结果正确,无符号数逻辑右移实现减半

image-20231021214520115

image-20231021214332809

作业问题2_2

设 BX=84F0H,若 BX 为带符号数,求BX/2

汇编代码如下:

DATA SEGMENT
    B DW 84F0H  ; 定义字数据B=84F0H
    RESULT DW ? ; 用于储存结果的变量
DATA ENDS

CODE SEGMENT
    ASSUME CS: CODE, DS: DATA
START:
    MOV AX, DATA ; 将数据段地址加载到AX
    MOV DS, AX   ; 设置DS指向数据段

    LEA DI, B    ;
    MOV AX, [DI] ; 将B的值加载到AX
    SAR AX, 1    ; 将AX算术右移一位,带符号数除以2 
    MOV RESULT, AX  ; 将结果储存到RESULT变量中
    ; 程序结束
    MOV AH, 4CH  ; 设置DOS功能号,表示程序结束
    INT 21H      ; 调用DOS中断

CODE ENDS
    END START

验证结果正确,带符号数算术右移实现减半

image-20231021215414234

作业问题2_3

设 BX=84F0H,把BX中的数按每4位压入堆栈,且高位先进栈,即第一次8进栈,第二次4进栈……

汇编代码如下:

DATA SEGMENT
    BX_VAL DW 84F0H     ; 定义字数据储存BX的值
    TMP DW 0F000H       ; 定义字数据用于和BX相与
    SHR_NUM DB 0CH      ; 定义字节数据表示逻辑右移的位数
    COUNT DW 05H		; 定义字数据表示循环的次数
DATA ENDS

STACK_SEG SEGMENT STACK
    DW 10 DUP(?)       ; 定义堆栈段
STACK_SEG ENDS

CODE SEGMENT
    ASSUME CS: CODE, DS: DATA, SS: STACK_SEG
START: 
    MOV AX, DATA ; 将数据段地址加载到AX
    MOV DS, AX   ; 设置DS指向数据段
	
    MOV AX, STACK_SEG ; 将堆栈段地址加载至AX
    MOV SS, AX      ; 设置SS指向堆栈段
    MOV SP, OFFSET STACK_SEG + 10 ; 设置堆栈指针,指向堆栈段的顶部
    
    MOV CX, 04H         ; 循环次数送至CX
    
PUSH_LOOP:
    MOV BX, BX_VAL      ;   将字数据加载到BX
    MOV CL, SHR_NUM     ;   将逻辑右移位数送至CL

    ; 第一轮循环F000H和BX相与得到BX的高4位,第二轮0F00H和BX相与得到BX的次高4位,以此类推...
    AND BX, TMP         ;   
    ; 第一次右移12位后BX为0008H,第二次右移8位后BX为0004H,第三次为000FH,...
    SHR BX, CL          ;
    
    MOV CL, 04H         ; 
    SHR TMP, CL         ; 和BX相与的值右移4位,以分别从高到低到得到BX的每个4位
    SUB SHR_NUM, 04H     ; BX右移的位数-4
    PUSH BX             ; 将BX压入堆栈
	
    DEC COUNT			; 循环次数-1
    MOV CX, COUNT		; 循环次数送至CX

LOOP PUSH_LOOP          ; 

    ; 程序结束
    MOV AH, 4CH  ; 设置DOS功能号,表示程序结束
    INT 21H      ; 调用DOS中断

CODE ENDS
    END START
	

验证结果正确

image-20231021220645307

作业问题3

A、B为带符号32位数,分别存放 于DX,AX和BX,CX中。编程求:当 A>B 时,转移到标志x处执行,否则转移到y处执行

汇编代码如下:

DATA SEGMENT
    A DD 12345678H   ; 定义双字数据A
    B DD 9ABCDEF0H   ; 定义双字数据B
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE, DS:DATA
START:
    MOV AX, DATA      ; 将数据段地址加载至AX
    MOV DS, AX        ; 段地址送至DS

    MOV DX, DWORD PTR [A+2]  ; 将A的高16位加载至DX
    MOV AX, DWORD PTR [A]    ; 将A的低16位加载至AX
    MOV BX, DWORD PTR [B+2]  ; 将B的高16位加载至BX
    MOV CX, DWORD PTR [B]    ; 将B的低16位加载至CX

    CMP DX, BX       ; 比较A和B的高16位
    JG  FLAGX        ; 带符号数,若A的高16位大于B的高16位,则跳转至FLAGX

    JL  FLAGY        ; 带符号数,若A的高16位小于B的高16位,则跳转至FLAGY

    CMP AX, CX       ; 若A的高16位等于B的高16位,比较低16位
    JA  FLAGX        ; 无符号数,若A的低16位大于B的低16位,则跳转至FLAGX
    JBE FLAGY        ; 无符号数,若A的低16位小于B的低16位,则跳转至FLAGY

FLAGY:
    ; 在这里执行当A<=B时的操作
    JMP END_PROGRAM

FLAGX:
    ; 在这里执行当A>B时的操作

END_PROGRAM:
    ; 程序结束
    MOV AH, 4CH
    INT 21H
    
CODE ENDS
    END START

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值