Windows64的nasm汇编详细教程,不是DOS!

这篇文章没有参考任何官方文档

是作者反汇编整理出来的

编译方法:

nasm  -f win64 -o 1.obj 1.asm
gcc 1.obj -o 1.exe
.\1

一、打印

        global main
        extern puts
SECTION .text
main:
        sub rsp, 20h
        mov rcx, message
        call puts
        add rsp, 20h
        ret
message:
        db "ASM!",0

等同于

        global main
        extern printf
section .text
main:
        sub rsp, 20h
        lea rcx, [message]
        call printf
        add rsp, 20h
        ret
message:
        db "ASM!",0

第一行:全局声明main主函数

第二行:声明外部函数,链接时用的

第三行:代码段开始,nasm特有

第四行:main函数

接下来:

为栈分配 32 字节空间(20h 十六进制表示 32)。这通常是为局部变量或函数调用准备栈空间。
将存储字符串 "ASM!" 的地址(即标签 `message` 的位置)放入寄存器 `rcx`,作为参数传递给函数。
调用外部函数 `puts`,该函数用于输出字符串到标准输出。

恢复栈指针,释放之前分配的 32 字节空间。
从函数返回。

 message部分

db "ASM!",0  ; 定义一个字节序列,包含字符串 "ASM!" 和字符串结束标志 0。

注意,printf是lea,puts是mov,功能差不多,但不能混用

二、简单的程序

global main
extern printf
extern scanf
extern system

SECTION .data
Format db "请输入用户名:",0
aS db "%s",0
byte_404012 db "请输入密码:",0
Buffer db "用户名和密码:",0
Command db "pause",0
string1 db "错误",0
string2 db "正确",0
Admin db "admin",0
Password db "123456",0

SECTION .bss
username resb 50
password resb 50

SECTION .text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 190

    ; 输出提示scanf用户名
    lea rcx, [Format]
    call printf

    ; 读取用户名
    lea rdx, [username]
    lea rcx, [aS]
    call scanf

    ; 输出提示scanf密码
    lea rcx, [byte_404012]
    call printf

    ; 读取密码
    lea rdx, [password]
    lea rcx, [aS]
    call scanf

    ; cmp用户名
    lea rsi, [Admin]
    lea rdi, [username]
    call compare_strings
    cmp rax, 1
    jne error

    ; cmp密码
    lea rsi, [Password]
    lea rdi, [password]
    call compare_strings
    cmp rax, 1
    jne error

    lea rcx, [string2]
    call printf
    jmp end

compare_strings:
    push rbp
    mov rbp, rsp
    sub rsp, 16
    xor rax, rax
    xor rcx, rcx
loop_compare:
    mov dl, [rsi + rcx]
    mov al, [rdi + rcx]
    cmp dl, al
    jne not_l
    cmp dl, 0
    je l
    inc rcx
    jmp loop_compare

not_l:
    mov rax, 0
    leave
    ret

l:
    mov rax, 1
    leave
    ret

end:
    ; 暂停system
    lea rcx, [Command]
    call system

    mov eax, 0
    add rsp, 190
    pop rbp
    ret

error:
    lea rcx, [string1]
    call printf
    jmp end

以下是对这段 NASM 汇编代码的逐行解释:

global main
extern printf
extern scanf
extern system

  • 声明全局符号 main,表示这是程序的入口点。同时声明外部函数 printfscanf 和 system,表示程序将调用这些外部库函数。

SECTION.data
Format db "请输入用户名:",0
aS db "%s",0
byte_404012 db "请输入密码:",0
Buffer db "用户名和密码:",0
Command db "pause",0
string1 db "错误",0
string2 db "正确",0
Admin db "admin",0
Password db "123456",0

  • .data 段用于存储初始化的数据。
    • Format 存储字符串 "请输入用户名:" 和字符串结束符 0
    • aS 存储格式化字符串 "%s",用于读取字符串输入。
    • byte_404012 存储字符串 "请输入密码:" 和字符串结束符 0
    • Buffer 存储字符串 "用户名和密码:" 和字符串结束符 0
    • Command 存储字符串 "pause",用于暂停程序执行。
    • string1 存储字符串 "错误" 和字符串结束符 0
    • string2 存储字符串 "正确" 和字符串结束符 0
    • Admin 存储字符串 "admin" 和字符串结束符 0,作为管理员用户名。
    • Password 存储字符串 "123456" 和字符串结束符 0,作为管理员密码。

SECTION.bss
username resb 50
password resb 50

  • .bss 段用于存储未初始化的数据。
    • username 预留 50 个字节的空间,用于存储用户输入的用户名。
    • password 预留 50 个字节的空间,用于存储用户输入的密码。

SECTION.text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 190

  • main 函数开始。
    • push rbp:将基址指针 rbp 压入栈,用于保存当前栈帧的基地址。
    • mov rbp, rsp:将栈指针 rsp 的值赋给 rbp,建立新的栈帧。
    • sub rsp, 190:为局部变量和临时数据在栈上分配 190 个字节的空间。

    ; 输出提示 scanf 用户名
    lea rcx, [Format]
    call printf

  • 输出提示用户输入用户名的字符串。
    • lea rcx, [Format]:将 Format 字符串的地址加载到寄存器 rcx 中,作为 printf 的参数。
    • call printf:调用 printf 函数输出字符串
    ; 读取用户名
    lea rdx, [username]
    lea rcx, [aS]
    call scanf

  • 读取用户输入的用户名。
    • lea rdx, [username]:将 username 的地址加载到寄存器 rdx 中,作为 scanf 的存储地址。
    • lea rcx, [aS]:将格式化字符串 "%s" 的地址加载到寄存器 rcx 中,作为 scanf 的格式参数。
    • call scanf:调用 scanf 函数读取用户输入的字符串,并存储到 username 中。

    ; 输出提示 scanf 密码
    lea rcx, [byte_404012]
    call printf

  • 输出提示用户输入密码的字符串。
    • lea rcx, [byte_404012]:将 byte_404012 字符串的地址加载到寄存器 rcx 中,作为 printf 的参数。
    • call printf:调用 printf 函数输出字符串。

    ; 读取密码
    lea rdx, [password]
    lea rcx, [aS]
    call scanf

  • 读取用户输入的密码。
    • lea rdx, [password]:将 password 的地址加载到寄存器 rdx 中,作为 scanf 的存储地址。
    • lea rcx, [aS]:将格式化字符串 "%s" 的地址加载到寄存器 rcx 中,作为 scanf 的格式参数。
    • call scanf:调用 scanf 函数读取用户输入的字符串,并存储到 password 中。

    ; cmp 用户名
    lea rsi, [Admin]
    lea rdi, [username]
    call compare_strings
    cmp rax, 1
    jne error

  • 比较用户输入的用户名和管理员用户名。
    • lea rsi, [Admin]:将管理员用户名 Admin 的地址加载到寄存器 rsi 中。
    • lea rdi, [username]:将用户输入的用户名 username 的地址加载到寄存器 rdi 中。
    • call compare_strings:调用 compare_strings 函数比较两个字符串。
    • cmp rax, 1:比较返回值 rax 是否为 1,表示两个字符串相等。
    • jne error:如果不相等,跳转到 error 标签处,表示用户名错误。

    ; cmp 密码
    lea rsi, [Password]
    lea rdi, [password]
    call compare_strings
    cmp rax, 1
    jne error

  • 比较用户输入的密码和管理员密码。
    • lea rsi, [Password]:将管理员密码 Password 的地址加载到寄存器 rsi 中。
    • lea rdi, [password]:将用户输入的密码 password 的地址加载到寄存器 rdi 中。
    • call compare_strings:调用 compare_strings 函数比较两个字符串。
    • cmp rax, 1:比较返回值 rax 是否为 1,表示两个字符串相等。
    • jne error:如果不相等,跳转到 error 标签处,表示密码错误。

lea rcx, [string2]
call printf
jmp end

  • 如果用户名和密码都正确,输出 "正确" 字符串。
    • lea rcx, [string2]:将字符串 "正确" 的地址加载到寄存器 rcx 中,作为 printf 的参数。
    • call printf:调用 printf 函数输出字符串。
    • jmp end:跳转到 end 标签处,结束程序。

compare_strings:
    push rbp
    mov rbp, rsp
    sub rsp, 16

  • compare_strings 函数开始。
    • push rbp:将基址指针 rbp 压入栈,用于保存当前栈帧的基地址。
    • mov rbp, rsp:将栈指针 rsp 的值赋给 rbp,建立新的栈帧。
    • sub rsp, 16:为局部变量和临时数据在栈上分配 16 个字节的空间。

    xor rax, rax
    xor rcx, rcx
loop_compare:
    mov dl, [rsi + rcx]
    mov al, [rdi + rcx]
    cmp dl, al
    jne not_l
    cmp dl, 0
    je l
    inc rcx
    jmp loop_compare

  • 比较两个字符串的循环部分。
    • xor rax, rax:将寄存器 rax 清零。
    • xor rcx, rcx:将寄存器 rcx 清零,用于作为字符串索引。
    • loop_compare:循环标签。
    • mov dl, [rsi + rcx]:将第一个字符串(由 rsi 指向)的当前字符加载到寄存器 dl 中。
    • mov al, [rdi + rcx]:将第二个字符串(由 rdi 指向)的当前字符加载到寄存器 al 中。
    • cmp dl, al:比较两个字符是否相等。
    • jne not_l:如果不相等,跳转到 not_l 标签处。
    • cmp dl, 0:比较当前字符是否为字符串结束符 0
    • je l:如果是字符串结束符,表示两个字符串相等,跳转到 l 标签处。
    • inc rcx:增加字符串索引 rcx
    • jmp loop_compare:跳转到循环开始处,继续比较下一个字符。

not_l:
    mov rax, 0
    leave
    ret

  • 如果两个字符串不相等,设置返回值为 0,并返回。
    • mov rax, 0:将返回值设置为 0。
    • leave:恢复栈帧,相当于 mov rsp, rbp; pop rbp
    • ret:从函数返回。

l:
    mov rax, 1
    leave
    ret

  • 如果两个字符串相等,设置返回值为 1,并返回。
    • mov rax, 1:将返回值设置为 1。
    • leave:恢复栈帧,相当于 mov rsp, rbp; pop rbp
    • ret:从函数返回。

end:
    ; 暂停 system
    lea rcx, [Command]
    call system

  • 暂停程序执行。
    • lea rcx, [Command]:将字符串 "pause" 的地址加载到寄存器 rcx 中,作为 system 的参数。
    • call system:调用 system 函数执行系统命令,这里是暂停程序。

    mov eax, 0
    add rsp, 190
    pop rbp
    ret

  • 结束程序。
    • mov eax, 0:将返回值设置为 0。
    • add rsp, 190:恢复栈指针,释放之前分配的空间。
    • pop rbp:恢复基址指针 rbp
    • ret:从函数返回。

error:
    lea rcx, [string1]
    call printf
    jmp end

  • 如果用户名或密码错误,输出 "错误" 字符串,并跳转到 end 标签处结束程序。
    • lea rcx, [string1]:将字符串 "错误" 的地址加载到寄存器 rcx 中,作为 printf 的参数。
    • call printf:调用 printf 函数输出字符串。
    • jmp end:跳转到 end 标签处,结束程序。

 三、文件操作

global main
extern fopen
extern fwrite
extern fclose
extern printf

SECTION .data
write_mode db "w", 0
file_name db "123.txt", 0
error_msg db "Error opening file", 0
hello_msg db "hello", 0
file_mode db "w",0
content db "Hello, this is a test content for file writing.",0
format db "File written successfully.",0
error db "Error opening file for writing."

SECTION .text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 48 ; 对应 sub rsp, 30h

    ; 设置 "w" 模式
    mov rdx, write_mode
    ; 设置文件名 "123.txt"
    mov rcx, file_name
    call fopen

    ; 将返回的文件指针存储到相对于 rbp - 8 的位置
    mov qword [rbp - 8], rax

    cmp qword [rbp - 8], 0
    jnz file_open_success

    ; 文件打开失败
    mov rcx, error_msg
    call printf
    mov eax, 1
    jmp end_program

file_open_success:
    mov rax, [rbp - 8]
    mov r9, rax ; Stream
    mov r8d, 5 ; ElementCount
    mov edx, 1 ; ElementSize
    mov rcx, hello_msg ; "hello"
    call fwrite

    mov rax, [rbp - 8]
    mov rcx, rax ; Stream
    call fclose

    mov eax, 0

end_program:
    add rsp, 48 ; 对应 add rsp, 30h
    pop rbp
    ret

以下是对这段 NASM 汇编代码的逐行解析:

global main
extern fopen
extern fwrite
extern fclose
extern printf

  • 声明全局符号 main,表示这是程序的入口点。同时声明外部函数 fopenfwritefclose 和 printf,表示程序将调用这些外部库函数。

SECTION.data
write_mode db "w", 0
file_name db "123.txt", 0
error_msg db "Error opening file", 0
hello_msg db "hello", 0
file_mode db "w",0
content db "Hello, this is a test content for file writing.",0
format db "File written successfully.",0
error db "Error opening file for writing."

  • .data 段用于存储初始化的数据。
    • write_mode 存储字符串 "w" 和字符串结束符 0,表示以写入模式打开文件。
    • file_name 存储字符串 "123.txt" 和字符串结束符 0,表示要打开的文件名。
    • error_msg 存储字符串 "Error opening file" 和字符串结束符 0,用于在文件打开失败时输出错误信息。
    • hello_msg 存储字符串 "hello" 和字符串结束符 0,将写入文件的内容。
    • file_mode 存储字符串 "w" 和字符串结束符 0,与 write_mode 重复,可能是冗余的。
    • content 存储一段较长的字符串,作为另一个可能写入文件的内容。
    • format 存储字符串 "File written successfully." 和字符串结束符 0,用于在文件写入成功时输出提示信息。
    • error 存储字符串 "Error opening file for writing." 和字符串结束符 0,与 error_msg 类似,可能是冗余的。

SECTION.text
main:
    push rbp
    mov rbp, rsp
    sub rsp, 48 ; 对应 sub rsp, 30h

  • main 函数开始。
    • push rbp:将基址指针 rbp 压入栈,用于保存当前栈帧的基地址。
    • mov rbp, rsp:将栈指针 rsp 的值赋给 rbp,建立新的栈帧。
    • sub rsp, 48:为局部变量和临时数据在栈上分配 48 个字节的空间。

    ; 设置 "w" 模式
    mov rdx, write_mode
    ; 设置文件名 "123.txt"
    mov rcx, file_name
    call fopen

  • 尝试以写入模式打开文件 "123.txt"。
    • mov rdx, write_mode:将写入模式字符串的地址加载到寄存器 rdx 中,作为 fopen 的模式参数。
    • mov rcx, file_name:将文件名字符串的地址加载到寄存器 rcx 中,作为 fopen 的文件名参数。
    • call fopen:调用 fopen 函数打开文件,并返回文件指针。

    ; 将返回的文件指针存储到相对于 rbp - 8 的位置
    mov qword [rbp - 8], rax

  • 将 fopen 返回的文件指针存储在栈上相对于 rbp - 8 的位置,以便后续使用。

    cmp qword [rbp - 8], 0
    jnz file_open_success

  • 检查文件是否成功打开。如果文件指针不为零,表示文件打开成功,跳转到 file_open_success 标签处继续执行;如果文件指针为零,表示文件打开失败。

    ; 文件打开失败
    mov rcx, error_msg
    call printf
    mov eax, 1
    jmp end_program

  • 如果文件打开失败,输出错误信息,设置返回值为 1,并跳转到 end_program 标签处准备结束程序。
    • mov rcx, error_msg:将错误信息字符串的地址加载到寄存器 rcx 中,作为 printf 的参数。
    • call printf:调用 printf 函数输出错误信息。
    • mov eax, 1:将返回值设置为 1。
    • jmp end_program:跳转到 end_program 标签处。

file_open_success:
    mov rax, [rbp - 8]
    mov r9, rax ; Stream
    mov r8d, 5 ; ElementCount
    mov edx, 1 ; ElementSize
    mov rcx, hello_msg ; "hello"
    call fwrite

  • 如果文件打开成功,将文件指针加载到寄存器中,设置写入参数,然后调用 fwrite 函数向文件写入内容。
    • mov rax, [rbp - 8]:将存储在栈上的文件指针加载到寄存器 rax 中。
    • mov r9, rax:将文件指针复制到寄存器 r9,作为 fwrite 的第一个参数(文件流指针)。
    • mov r8d, 5:设置写入的元素个数为 5,这里可能是错误的,因为 hello_msg 的长度并不是 5。
    • mov edx, 1:设置每个元素的大小为 1 字节。
    • mov rcx, hello_msg:将要写入的内容(字符串 "hello")的地址加载到寄存器 rcx 中。
    • call fwrite:调用 fwrite 函数向文件写入内容。

    mov rax, [rbp - 8]
    mov rcx, rax ; Stream
    call fclose

  • 关闭文件。
    • mov rax, [rbp - 8]:将存储在栈上的文件指针加载到寄存器 rax 中。
    • mov rcx, rax:将文件指针复制到寄存器 rcx,作为 fclose 的参数(文件流指针)。
    • call fclose:调用 fclose 函数关闭文件。
    mov eax, 0

  • 设置返回值为 0,表示程序正常结束。

end_program:
    add rsp, 48 ; 对应 add rsp, 30h
    pop rbp
    ret

  • end_program 标签处,恢复栈指针,恢复基址指针,并从函数返回。
    • add rsp, 48:恢复栈指针,释放之前分配的空间。
    • pop rbp:恢复基址指针 rbp
    • ret:从函数返回。

目录 前言i 第1章简介1 1.1 数制. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1 十进制. . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.2 二进制. . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.3 十六进制. . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 计算机结构. . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.1 内存. . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.2 CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2.3 CPU 80x86系列. . . . . . . . . . . . . . . . . . . . . 5 1.2.4 8086 16位寄存器. . . . . . . . . . . . . . . . . . . . . 6 1.2.5 80386 32位寄存器. . . . . . . . . . . . . . . . . . . . 7 1.2.6 实模式. . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.2.7 16位保护模式. . . . . . . . . . . . . . . . . . . . . . . 8 1.2.8 32位保护模式. . . . . . . . . . . . . . . . . . . . . . . 8 1.2.9 中断. . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 汇编语言. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.1 机器语言. . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.2 汇编语言. . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3.3 指令操作数. . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.4 基本指令. . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3.5 指示符. . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.3.6 输入和输出. . . . . . . . . . . . . . . . . . . . . . . . 14 1.3.7 调试. . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.4 创建一个程序. . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.4.1 第一个程序. . . . . . . . . . . . . . . . . . . . . . . . 16 1.4.2 编译器依赖. . . . . . . . . . . . . . . . . . . . . . . . 18 1.4.3 汇编代码. . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.4 编译C代码. . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.5 连接目标文件. . . . . . . . . . . . . . . . . . . . . . . 20 1.4.6 理解一个汇编列表文件. . . . . . . . . . . . . . . . . . 20 3 4 目录 1.5 骨架文件. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 第2章基本汇编语言23 2.1 整形工作方式. . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.1 整形表示法. . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.2 正负号延伸. . . . . . . . . . . . . . . . . . . . . . . . 25 2.1.3 补码运算. . . . . . . . . . . . . . . . . . . . . . . . . 28 2.1.4 程序例子. . . . . . . . . . . . . . . . . . . . . . . . . 29 2.1.5 扩充精度运算. . . . . . . . . . . . . . . . . . . . . . . 31 2.2 控制结构. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.1 比较. . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.2 分支指令. . . . . . . . . . . . . . . . . . . . . . . . . 33 2.2.3 循环指令. . . . . . . . . . . . . . . . . . . . . . . . . 36 2.3 翻译标准的控制结构. . . . . . . . . . . . . . . . . . . . . . . 36 2.3.1 If语句. . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.3.2 While循环. . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3.3 Do while循环. . . . . . . . . . . . . . . . . . . . . . . 37 2.4 例子:查找素数. . . . . . . . . . . . . . . . . . . . . . . . . . . 37 第3章位操作41 3.1 移位操作. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.1.1 逻辑移位. . . . . . . . . . . . . . . . . . . . . . . . . 41 3.1.2 移位的应用. . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.3 算术移位. . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.4 循环移位. . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.5 简单应用. . . . . . . . . . . . . . . . . . . . . . . . . 43 3.2 布尔型按位运算. . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.2.1 AND运算符. . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.2 OR运算符. . . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.3 XOR运算. . . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.4 NOT运算. . . . . . . . . . . . . . . . . . . . . . . . . 45 3.2.5 TEST指令. . . . . . . . . . . . . . . . . . . . . . . . . 45 3.2.6 位操作的应用. . . . . . . . . . . . . . . . . . . . . . . 45 3.3 避免使用条件分支. . . . . . . . . . . . . . . . . . . . . . . . 47 3.4 在C中进行位操作. . . . . . . . . . . . . . . . . . . . . . . . . 49 3.4.1 C中的按位运算. . . . . . . . . . . . . . . . . . . . . . 49 3.4.2 在C中使用按位运算. . . . . . . . . . . . . . . . . . . 50 3.5 Big和Little Endian表示法. . . . . . . . . . . . . . . . . . . . 51 3.5.1 什么时候需要在乎Little和Big Endian . . . . . . . . . 52 3.6 计算位数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.1 方法一. . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.2 方法二. . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.3 方法三. . . . . . . . . . . . . . . . . . . . . . . . . . . 55 目录5 第4章子程序57 4.1 间接寻址. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.2 子程序的简单例子. . . . . . . . . . . . . . . . . . . . . . . . 57 4.3 堆栈. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.4 CALL和RET指令. . . . . . . . . . . . . . . . . . . . . . . . . 60 4.5 调用约定. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 4.5.1 在堆栈上传递参数. . . . . . . . . . . . . . . . . . . . 62 4.5.2 堆栈上的局部变量. . . . . . . . . . . . . . . . . . . . 66 4.6 多模块程序. . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 4.7 C与汇编的接口技术. . . . . . . . . . . . . . . . . . . . . . . 71 4.7.1 保存寄存器. . . . . . . . . . . . . . . . . . . . . . . . 71 4.7.2 函数名. . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.7.3 传递参数. . . . . . . . . . . . . . . . . . . . . . . . . 72 4.7.4 计算局部变量的地址. . . . . . . . . . . . . . . . . . . 73 4.7.5 返回值. . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.7.6 其它调用约定. . . . . . . . . . . . . . . . . . . . . . . 73 4.7.7 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.7.8 在汇编程序中调用C函数. . . . . . . . . . . . . . . . . 78 4.8 可重入和递归子程序. . . . . . . . . . . . . . . . . . . . . . . 78 4.8.1 递归子程序. . . . . . . . . . . . . . . . . . . . . . . . 79 4.8.2 回顾一下C变量的储存类型. . . . . . . . . . . . . . . 79 第5章数组83 5.1 介绍. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 5.1.1 定义数组. . . . . . . . . . . . . . . . . . . . . . . . . 83 5.1.2 访问数组中的元素. . . . . . . . . . . . . . . . . . . . 84 5.1.3 更高级的间接寻址. . . . . . . . . . . . . . . . . . . . 86 5.1.4 例子. . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.1.5 多维数组. . . . . . . . . . . . . . . . . . . . . . . . . 91 5.2 数组/串处理指令. . . . . . . . . . . . . . . . . . . . . . . . . 93 5.2.1 读写内存. . . . . . . . . . . . . . . . . . . . . . . . . 94 5.2.2 REP前缀指令. . . . . . . . . . . . . . . . . . . . . . . 95 5.2.3 串比较指令. . . . . . . . . . . . . . . . . . . . . . . . 96 5.2.4 REPx前缀指令. . . . . . . . . . . . . . . . . . . . . . . 96 5.2.5 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 第6章浮点103 6.1 浮点表示法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.1.1 非整形的二进制数. . . . . . . . . . . . . . . . . . . . 103 6.1.2 IEEE浮点表示法. . . . . . . . . . . . . . . . . . . . . 105 6.2 浮点运算. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.2.1 加法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 6.2.2 减法. . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 6 目录 6.2.3 乘法和除法. . . . . . . . . . . . . . . . . . . . . . . . 109 6.2.4 分支程序设计. . . . . . . . . . . . . . . . . . . . . . . 109 6.3 数字协处理器. . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.1 硬件. . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 6.3.2 指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 6.3.3 样例. . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.3.4 二次方程求根公式. . . . . . . . . . . . . . . . . . . . 115 6.3.5 从文件中读数组. . . . . . . . . . . . . . . . . . . . . 118 6.3.6 查找素数. . . . . . . . . . . . . . . . . . . . . . . . . 120 第7章结构体与C++ 127 7.1 结构体. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 7.1.1 简介. . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 7.1.2 内存地址对齐. . . . . . . . . . . . . . . . . . . . . . . 128 7.1.3 位域s . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 7.1.4 在汇编语言中使用结构体. . . . . . . . . . . . . . . . 132 7.2 汇编语言和C++ . . . . . . . . . . . . . . . . . . . . . . . . . 134 7.2.1 重载函数和名字改编. . . . . . . . . . . . . . . . . . . 134 7.2.2 引用. . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 7.2.3 内联函数. . . . . . . . . . . . . . . . . . . . . . . . . 137 7.2.4 类. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 7.2.5 继承和多态. . . . . . . . . . . . . . . . . . . . . . . . 147 7.2.6 C++的其它特性. . . . . . . . . . . . . . . . . . . . . 153 附录A 80x86指令155 A.1 非浮点指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 A.2 浮点数指令. . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 索引163
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值