汇编语言程序设计(第三版)学习笔记

文章目录

汇编语言程序设计

第一章 基础知识

1.1 机器语言

机器语言是机器指令的集合。机器指令展开来讲就是一台机器可以正确执行的命令。
每一种微处理器的硬件设计内部结构不一样所以就会有相应的属于自己的机器指令集,也就是机器语言。

1.2 汇编语言

汇编语言的主体是汇编指令,汇编语言和机器指令的差别在于指令的表示方法上。汇编语言是机器指令便于记忆的书写格式。

寄存器:简单的讲是CPU中可以存储数据的器件,一个寄存器可以有多个寄存器。

1.3 汇编语言的组成

汇编语言有三种指令:

  • 汇编指令:机器码的助记符,有对应的机器码
  • 伪指令:没有对应的机器码,由编译器执行,计算机并不执行
  • 其他符号:如=,-,*,/等等 由编译器识别,机器并不识别

1.4 存储器

CPU是计算机的核心部件,它控制整个计算机的运行并进行运算。指令和数据在存储器中存放(也即内存),提供给CPU,使其工作。

1.5 指令和数据

指令和数据是应用上的概念,在内存和磁盘里面,指令和数据没有任何区别,都是二进制信息。

1.6 存储单元

存储器被划分为若干个存储单元,每个单元从0开始顺序编号。
电子计算机的最小存储单位是比特(bit),也就是一个二进制位,8bit组成一个Byte,即八个二进制位。
微型存储器一个存储单元可以存储一个字节(Byte)。

1KB = 1024 B 1MB = 1024KB 1GB = 1024MB 1TB = 1024MB

1.7 CPU对存储器的读写

CPU要进行数据的读写必须和外部器件(芯片)进行下面3类的信息交互。

  • 存储单元的地址
  • 器件的选择,读写命令的控制
  • 读或者写的数据

总线:计算机中专门连接CPU和其它芯片的导线,通常称为总线。
总线从逻辑上分为三类:地址总线,逻辑总线和数据总线。

读取数据:

  1. CPU通过地址总线将地址信息发出
  2. CPU通过控制总线发出内存读命令,选中存储器芯片,通知它,将要从中读取数据
  3. 存储器通过数据总线将数据传入CPU

1.8 地址总线

一个CPU有N根地址线,则可以说这个CPU的地址总线宽度是N,最多可以寻找2的N次方个内存单元。

1.9 数据总线

CPU与内存或者其他器件之间的数据传送是通过数据总线来实现的。数据总线的宽度决定了CPU和外界的数据传送速度。

1.10 控制总线

CPU对外部器件的控制是通过控制总线来进行的。控制总线的宽度决定了CPU对外部器件的控制能力。

1.11 内存地址空间

举例来讲,一个CPU的地址总线宽度是10,那么可以寻址1024个内存单元,这1024个内存单元构成这个CPU的内存地址空间。

1.12 主板

主板上有一些核心器件和一些主要部件,这些器件(CPU,存储器,外围芯片组,扩展插槽等等,扩张插槽上一般有RAM内存条和各类接口卡)通过总线相连。

1.13 接口卡

计算机上所有可用程序控制的设备,必须受到CPU的控制,直接控制这些设备进行工作的是插在扩展插槽上的接口卡,所以CPU通过接口卡可以间接控制各类外设。

1.14 各类存储器芯片

从读写属性上看,分为两类:随机存储器(RAM)和只读存储器(ROM)

功能和连接上看:

  • 随机存储器
    用于存放CPU绝大多数数据程序和数据,主随机存储器一般由两个位置的RAM组成,主板上的RAM和插在扩 展插槽的RAM
  • 装有BIOS(Basic Input/Ouput System)的的ROM
    在主板和某些接口卡上有存储相应BIOS的ROM。
  • 接口卡的RAM
    某些接口卡需要对大批输入,输出数据进行暂时存储,在其上装有RAM。最典型的是显卡上的RAM,一般称为显存。显示卡随时将显存中的数据向显示器输出。

第二章 寄存器

8086CPU有十四个寄存器:AX,BX,CX,DX,ESI,DI,SP,BP,IP,CS,SS,DS,ES,PSW。

2.1 通用寄存器

8086所有寄存器都是16位,可以存放两个字节。AX,BX,CX,DX存放一般性数据,被称作通用寄存器
通用寄存器都可以分为两个8位寄存器使用。例如AX的低八位构成了AL寄存器,高八位构成了AH寄存器。

2.2 字在存储器中的存储

8086CPU可以处理一下两种数据:

  • 字节:即为byte,一个字节由8个bit组成,可以存放在8位寄存器中
  • 字: 记为word,一个字由两个字节组成,分别称为高位字节和低位字节。字可以存放在16位寄存器中,“高对高,低对低”

2.3 几条汇编指令

代码实例:
mov ax,4E20H ax = 4e20h bx = 0000h
add ax,1406H ax = 6226h bx = 0000h
mov bx,2000H ax = 6226h bx = 2000h
add ax,bx ax = 8226h bx = 2000h
mov bx,ax ax = 8226h bx = 8226h
add ax,bx ax = ? bx = 8226h

最后一条语句计算的结果是1044ch,最高位不能取,所以ax = 044ch

同理,对于al寄存器,存储的数据超过8位的部分,会被舍弃,而不是存储在ah中

2.4 物理地址

所有的内存单元构成的存储空间是一个一维的线性空间,每个内存单元在这个空间中都有一个唯一的地址,我们将这个唯一的地址称为物理地址。

2.5 16位结构的CPU

8086CPU是16位的,16位CPU的特性:

  • 运算器最多一次可以处理16位的数据
  • 寄存器的最大宽度是16位
  • 寄存器和运算器之间的通络是16位

2.6 8086CPU给出物理地址的方法

8086CPU有20位地址总线,有1MB的寻址能力。而CPU是16位的,寻址能力只有64KB。
所以8086CPU采用16位地址合成的方法来形成一个20位的物理地址。也即物理地址=短地址*16+偏移地址

2.7 段的概念

内存中并没有段,段的划分来自于CPU。编程时可根据需要自行定义段。

ps:CPU可以使用不同的段地址和偏移地址形成同一个物理地址。

2.8 段寄存器

8086CPU有四个段寄存器:CS,DS,ES,SS,当CPU要访问内存时由这几个寄存器来提供内存单元的段地址。

2.9 CS和IP

CS为代码段寄存器,IP为指令指针寄存器,8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行
读取一条指令以后,IP的值自动增加,增加的长度为对应指令的机器码长度,使得CPU可以执行下一条指令。

在8086CPU加电启动时或复位后,CS=FFFFH IP=0000H,CPU从FFFF0H处开始读取第一条指令。

2.10 修改CS IP的值

最简单的修改CS和IP的指令:jmp指令

  • 同时修改CS IP的指令:jmp 段地址:偏移地址
  • 修改IP的值:jmp 某一合法寄存器

例子:
地址 指令
10000H mov ax,0123H
10003H mov ax,0000H
10006H mov bx,ax
10009H jmp bx
20000H mov ax,6622H
20003H jmp 1000:3
20006H mov cx,ax

2.12 代 码 段

对于8086机,编程时可以根据需要,将一组内存单元定义为一个段。我们可以将长度为N的一组代码,存放在一组地址连续,起始地址为16的倍数的内存单元中,从而定义代码段。只需要将CS IP指向代码段第一条指令的首地址,就可以执行代码。

实验一

debug的使用:

  • R: 查看改变CPU寄存器中的内容
    -r 查看寄存器的值,-r 寄存器 ,修改寄存器的值

  • D:查看内存中的内容
    -d “段地址/段寄存器:偏移地址”,列出从内存单元开始的128个内存单元的内容
    1.中间是指地址开始的128个内存单元的内容,用十六进制的格式输出,每行从十六的整数倍。每行的中间有"-",它将每行的输入分为两部分,便于查看。
    2.左边是每行的其实地址
    3.右边是每个内存单元的数据对应的可以显示的ASCII码字符。
    4.在使用-d "段地址:偏移地址"之后,再次使用-D,将列出后续的内容
    5.也可以使用 -d "段地址:偏移地址 结尾偏移地址"的格式,指定查看的范围。

  • E:改写内存中的内容
    1.“e 起始地址 数据 数据 数据 数据…"修改
    2.也可以采取提问的方式逐个修改以从1000:0单元开始为例:

    • 输入e 1000:0 ,按enter键
    • Debug显示起始地址1000:0010,和第一个单元1000:0010单元的原始内容,光标停在.后面提示输入想要写入的数据,输入数据,按下空格即可改写,不输入数据,直接空格,则不对当前内存单元改写。
    • 同样的进行下一个地址数据的修改
    • 修改完毕,按下enter键,E命令结束
  • U:将内存中的机器指令翻译成汇编指令
    U命令的输出显示分为三个部分,每一条机器指令的地址,机器指令,机器指令对应的汇编指令。

  • T:执行一条机器指令 debug中的T命令在执行修改寄存器SS的指令时,下一条指令一定会执行

  • A:以汇编指令格式在内存中写入一条机器指令

第三章 寄存器(内存访问)

3.1 内存中字的存储

CPU中,用一个16位寄存器来存储一个字。高对高,低对低。即高地址内存单元存放字型数据的高位字节,低地址内存单元存放数据的低位字节。

DS 和[address]

DS寄存器通常存放要访问数据的段地址。
例子:
mov bx,1000H
mov ds,bx
mov al,[0]

mov指令的三种用法:

  • 将数据直接送入寄存器
  • 将一个寄存器的内容送入另一个寄存器
  • 将一个内存单元的内容送入另一个寄存器

[…]表示一个内存单元,[…]中的0表示内存单元的偏移地址,段地址取DS。

ps:段寄存器不支持数据的直接传入,这是8086CPU的硬件设计问题。

3.3 mov,add和sub指令

mov 寄存器,数据 比如:mov ax,8
mov 寄存器,寄存器 比如: mov ax,bx
mov 寄存器,内存单元 比如: mov ax,[0]
mov 寄存器,段寄存器 比如;mov ax,ds
mov 内存单元,寄存器 比如: mov [0],ax
mov 内存单元,段寄存器 比如:mov [0],ds
mov 段寄存器,寄存器 比如:mov ds,ax
mov 段寄存器,内存单元 比如 mov ds,[0]

add和sub也支持以上操作(不含段寄存器)

3.5 栈

栈是一种具有特殊访问方式的存储空间,最后进入的数据,会最先出去,LIFO(Last In First Out ,后进先出)
两种基本操作:

  • 入栈:将一个新元素放到栈顶
  • 出栈:从栈顶取出一个元素

3.7 CPU提供的栈机制

基本指令:

  • PUSH(入栈)
  • POP(出栈)

这两个操作都是以字节为单位。

段寄存器SS,寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。

栈顶超界的问题

8086CPU不保证我们的栈操作不会超界,所以在编程时要自己注意合理分配和使用栈空间。

3.8 push pop指令

push和pop指令可以是如下格式:
push 寄存器/段寄存器/内存单元;
pop 寄存器/段寄存器/内存单元;

执行push时 先改变SP,后向SS:SP传送,执行pop时,先传送,后改变SP。

第四章 第一个程序

4.1 一个源程序从写出到执行的过程

第一步:编写汇编程序
第二步:对源程序进行编译连接
第三步:执行可执行文件中的程序

4.2 源程序

例子:
assume: cs:codesg
codesg segment
mov ax,0123h
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end

  1. 伪指令
    由编译器执行,编译器根据伪指令来进行相应的操作

    segment和end是成对使用的伪指令,segment定义段开始,end定义段结束。
    格式:
    段名 segment:
    段名 ends

    END
    end是一个汇编程序的结束标记

    ASSEME
    将有特殊用途的段和相关的寄存器关联起来

  2. 源程序中的程序
    源程序文件中的所有内容称为源程序,将源程序中最终由计算机执行,处理的指令或数据,称为程序。

  3. 标号
    一个标号代表一个地址

  4. 程序的结构
    1.定义段
    2.在段中写入指令
    3.指出程序结束的位置
    4.assume联系段和相应寄存器

  5. 程序返回
    mov ax,4c00h
    int 21h

  6. 语法错误和逻辑错误

4.3编辑源程序

使用DOS编辑:

  • 进入DOS方式,在mount操作以后运行Edit
  • Edit图形化界面中编辑程序

4.4 编译和连接

1.进入DOS方式,进入c:\masm目录,运行masm.exe
2.根据提示输入汇编文件名,如果不在masm文件夹下的话就要输入路径,按下enter键,得到obj文件
3.输入link,通过obj文件生成exe文件

4.5 以简化的方式编译和连接

1.简化编译 “masm c:\1;”,即masm 后面加上被编译的源程序文件的路径,文件名,在命令行的结尾加上分号
2.简化连接"link 1;",link 后面加上被连接的路径文件名,在命令行的结尾加上分号,按下enter键以后,自动生成exe文件

4.6 装载可执行文件并执行的程序(cmd命令行)

我们知道,DOS中,可执行文件中的程序P1若要运行,必须有一个正在运行的程序p2,将p1从可执行文件中加载进内存,将CPU的控制权交给它,p1才能得以运行;当p1运行完毕后,将控制权返还给p2。这个p2就是command 命令行。

4.7 程序执行时的跟踪

Debug将程序加载进入内存后,cx中存放的是程序的长度。

1.程序被加载以后,ds存放着程序所在的内存区的段地址,这个内存区的偏移地址为0。
2.这个内存区前256个字节中存放的是PSP,DOS用来和程序通信,后面是程序程序的段地址是ds+10H。

遇到int 21 使用 -p 执行

第五章 [BX] 和 LOOP 指令

5.1 [bx]

[bx]和内存单元
完整的表示一个内存单元,需要两种信息:1.内存单元的地址 2.内存单元的长度
[bx] 存放的数据作为一个偏移地址EA,段地址SA默认在ds中

5.2 LOOP指令

loop指令的格式; loop标号,CPU执行loop指令时,1.cx = cx - 1 2. 判断cx中的值

5.3 debug和汇编编译器对指令的不同处理

汇编源程序中,[…]表示内存单元,如果使用[常量]表示,必须要加上段寄存器,如果使用[寄存器]就不需要给出

5.4 loop 和 [bx] 的联合使用

assume cs:code
code segment

mov ax,0ffffh
mov ds,ax
mov bx,0

mov dx,0

mov cx,12

s: mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s
mov ax,4c00h
int 21h

code ends
end

5.5 段前缀的使用

assume cs:code

code segment
mov ax,0ffffh
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,12
s: mov dl,[bx]
mox es:[bx],dl
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end

第六章 包含多个段的程序

6.1 在代码段中使用数据

dw 定义字型数据,define word
dd 定义双字型结构 define double word
程序: assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
start: mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start

end start 指明了程序的结束位置(end)和开始位置(start)。

6.2 在代码段中使用栈

assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

start:mov ax,cs
mov ss,ax
mov sp,30h
mov bx,0
mov cx,8
s: push cs:[bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0:pop cs:[bx]
add bx,2
loop s0
mov ax,4c00h
int 21h

codesg ends

end start

利用栈实现数据的逆序存放

6.3 将数据,代码,栈放入不同的段

程序

assume cs:code,ds:data,ss:stack

data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends

stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends

code segment

start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,0
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,0
loop s0
mov 4c00h
int 21h
code ends

end start

一个段的地址可以由段名代表。

第七章 灵活定位内存地址

7.1 and和or指令

1.and 指令逻辑与指令,按位与运算,可以实现指定位置0

2.or指令:按位或运算,可以实现指定位置1

7.2 ascii码

7.3 以字符形式给出数据

程序
assume cs:code,ds:data

data segment
db ‘unIX’
db ‘foRK’

data ends

code segment

start: mov al,‘a’
mov bl,‘b’
mov ax,4c00h
int 21h
code ends
end start

7.4 小写字母和大写字母的转换

小写字母的ASCII码比大写字母大20h,可以使用or和and来简化转换 操作
大写字母 or 00010000b = 小写字母
小写字母 and 11101111b =大写字母

7.5 [bx+idata]

程序:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5

s:mov al,0[bx]
and al 11011111b
mov 5[bx],al
inc bx
loop s

7.6 SI 和 DI

si和di不能分为两个八位寄存器使用
使用ds:si指向要复制的源字符串,ds:di指向目的空间,使用一个循环来完成源字符串向目的空间的复制。

7. 7 [bx+si(+idata)]和[bx+di(+idata)]

第八章 数据处理的基本问题

8 .1 bx,bp,di和si

1.[]中,这四个寄存器可以单独出现,或者以四种组合的形式出现:bx和si\di或者bp和si\di

2.使用bp寄存器,段地址默认是ss

8.2 数据位置的表达

1.立即数
2.寄存器
3.段地址(SA)和偏移地址(EA)
在这里插入图片描述

8.3 待处理的数据长度

1.通过寄存器指明要处理的数据尺寸
2.使用X ptr 指明内存单元的长度
3.push 只进行字操作

8.4 div指令

div byte ptr ds:[0]
(al) = (ax) / ((ds)*16+0)的商
(ah)=(ax) /((ds)*16+0) 的余数

div word ptr es:[0]
(ax) = [(dx)10000h+(ax)]/(es16+0) 余数
(dx) = [(dx)*10000h+(ax)]/((es)*16+0) 商

div byte ptr ds:[(bx)+(si)+8]
(al) = (ax) / ((ds)*16+(bx)+(si)+8)的商
(ah)=(ax) /((ds)*16+(bx)+(si)+8) 的余数

div word ptr es:[(bx)+(si)+8]
(ax) = [(dx)10000h+(ax)]/(es16+(bx)+(si)+8) 余数
(dx) = [(dx)*10000h+(ax)]/((es)*16+(bx)+(si)+8) 商

8.5 dup

dup是一个操作符,和dw,dd,db配合使用来实现数据的重复

使用格式: db 重复次数 dup(字节型数据)
dw 重复次数 dup(字型数据)
dd 重复次数 dup(双字型数据)

第九章 转移指令的原理

9.1 操作符offset

offset 取标号的偏移地址

9.2 jmp 指令

jmp指令为无条件跳转,可以同时修改CS和IP
给出两种信息:
1.转移的目的地址
2.转移的距离

jmp short 标号

段内短转移,对IP的修改范围是-128到127,结束后,cs:ip指向标号处的指令,也就是IP+8位位移

jmp near 标号

段内近转移,对ip的修改范围是-32768-32767,用补码表示,也就是IP+16位位移

转移地址在指令中的jmp

”jmp far ptr 标号“ 实现的是段间转移,又称远转移。
cs = 标号所在段,ip = 标号在段中的偏移地址

转移地址在寄存器中的jmp指令

jmp 16位寄存器 ip = 16位寄存器

转移地址在内存中的指令

jmp word ptr 内存单元地址(段内地址)

功能:从内存地址处开始存放一个字,是转移的目的偏移地址

jmp dword ptr 内存单元地址(段间转移)

功能:从内存单元处开始存放两个字,高地址是目的段地址,低地址是目的偏移地址

9.3 jcxz 指令

jzcx是有条件转移指令,所有有条件转移指令都是短转移
jzcx 标号(如果cx = 0,转移到标号处执行)

第十章 CALL 和RET 指令

10.1 ret 和 retf

ret 指令,修改IP,从而实现近转移
1.IP= SS*16+SP
2.SP=SP+2

retf指令,修改CS和IP,实现远转移
1.IP= SS16+SP
2.SP=SP+2
3.CS= SS
16+SP
4.SP=SP+2

10.2 CALL

call指令:1.将当前的IP或CS和IP压入栈中 2.转移

10.3 依据位移进行转移的Call指令

call 标号(将当前的IP压入栈,转到标号处执行指令)
1.sp = sp+2
(ss*16+sp )= IP
2.IP=IP+16位位移

10.4 转移的目的地址在指令中的CALL指令

call far ptr 标号实现的是段间转移

1.sp = sp+2
(ss16+sp )= CS
sp = sp+2
(ss
16+sp )= IP
2.IP=标号在段中的偏移地址
CS=标号的段地址

10.5 转移地址在内存中的call指令

1.call word ptr 内存单元地址
push IP
jmp word ptr 内存单元地址
2.call dword ptr 内存单元地址
push CS
push IP
jmp dword ptr 内存单元地址

具有子程序框架的源程序框架
assume cs:code
code segment
main:

call sub1

mov ax,4c00h
int 21h

sub1:

call sub2

ret

sub2:

ret

code ends
end main

10.6 mul指令

1.乘数:全8位或全16位;全8位一个放在Al中,另一个放在8位reg或者内存单元;全16位,一个默认放在AX中,另一个放在16位reg或者内存字单元中。
2.结果:八位乘法,结果存放在AX中,16位乘法,结果高位存放在DX中,低位存放在AX中。

10.7 参数和结果传递

调用者将参数送入参数寄存器,从结果寄存器中渠道返回值;
子程序从参数寄存器中取到参数,将返回值送入结果寄存器;

assume cs:code

data segment
db ‘conversation’
data ends

code segment
start: mov ax,data
mov ds,ax
mov si,0
mov cx,12
call capital
mov ax,4c00h
int 21h
capital: and byte ptr [si],11011111b
inc si
loop capital
ret
code ends
end start

第十一章 标志寄存器

作用

  • 存储指令执行结果
  • 为CPU执行相关的指令提供依据
  • 控制CPU工作方式

11.1 ZF标志

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

11.2 PF标志

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

11.3 SF标志

flag的第7位是SF,符号标志位。记录相关指令执行后,其结果是否为负。为负,sf=1;非负,sf=0。
mov push pop 对标志寄存器没有影响

11.4 CF标志

flag的第0位是CF,一般情况下,它记录无符号数运算时,运算结果的进位和借位。

11.5 OF标志

溢出:针对有符号数而言
flag的第11位是OF,溢出标志位。记录有符号数的运算是否溢出,溢出OF=1,否则OF=0。

11.6 adc指令

adc是带进位加法指令,利用CF记录的进位值
格式: adc op1,op2
功能 op1 = op2 +op1 +CF

11.7 sbb指令

sbb是带借位减法指令,利用CF上记录的借位值
格式: sbb op1,op2
功能 op1 = op2 - op1 - CF

11.8 cmp指令

cmp 是比较指令,相当于不保存结果的减法。
格式: cmp op1,op2
功能 计算op1-op2 但是不保存结果,仅仅根据计算结果对标志寄存器进行设置

cmp ax,bx
对于无符号数来说,cmp指令执行以后,zf=1,ax=bx;zf=0,ax !=bx
cf = 1,ax<bx;cf = 0 ,ax>bx;

cmp ah,bh
对于有符号数来说,sf=1,of=0,逻辑的正负等于实际结果的正负,ah<bh
sf=1,of=1,溢出导致结果为负,逻辑值为正,ah>bh
sf=0,of=1,有溢出,溢出导致结果为正,逻辑值为负,ah<bh
sf=0,of=0,ah>=bh

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值