x86汇编适用于哪些CPU?

时间:2018-06-18 11:35:59

标签: assembly x86 nasm x86-64 masm

我花了几年时间用C和Java编写代码。我喜欢如何让程序显示一个消息框或执行一些文件操作,它适用于每个(我认为?)CPU没有任何戏剧性。虽然我从未编写任何高级编码。

我想学习汇编,仅适用于Windows。我相信我应该学习一些叫做x86 asm的东西。

我的问题:如果我不想做太疯狂或模糊的事情,我创建的程序是否会在每个CPU上运行?我的重点是你家里的平均电脑或服务器。

我有很多人告诉我,我必须从许多信誉良好的来源为特定的CPU选择一个特定的架构,但我会看到代码示例,这些代码示例可以在几个不同的CPU上工作......信息冲突!

修改 我只是想编写一些有趣的程序,而不必担心我的朋友John Doe在他的计算机上使用它时会遇到麻烦。我可以很容易地在C中编写一个程序来显示消息,ping google.com以及其他任何内容而不必担心CPU。 asm真的不一样吗? o.O

EDIT2: 也许我感到困惑,因为我不知道在C语言的Windows上编码的正常(没什么特别的)程序是哪种CPU只是英特尔和AMD?

4 个答案:

答案 0 :(得分:4)

IBM PC历史上围绕英特尔处理器发展,从80 86 开始,然后经历801 86 ,802 86 (仍然是16位)只有),803 86 (32位扩展),80486,奔腾(有时称为80586),奔腾II( 686 有时用于此系列CPU),然后后来,命名/编号不再那么简单了。

从这些" x 86 "平台名称已创建,因为所有早期模型都以" 86" 86之前的数字被修改了。

" x86" CPU系列在设计新模型的方式上非常具体 - 几乎完全向后兼容。因此,80186和80286 CPU可以按原样运行8086机器代码,它们使用了所有原始的8086指令,并在此基础上引入了新的扩展。

第一个截然不同的CPU是80386,它引入了32位寄存器和保护模式的新32位模式(286"保护模式"大部分被忽略,只有16位)。仍然可以实现向后兼容性,因为80386上电后将以16位模式启动,运行速度更快的80286,只需更多指令,然后代码可以根据需要将其切换到32位模式(通常现代操作系统启动程序将,在OS加载过程的早期阶段)。

然后80486和下一个Intel CPU再次向后兼容80386,只是扩展了原始指令集(自80486DX CPU和Pentiums以来,每个x86 CPU现在都默认内置了浮点单元,尽管它采用了现代x86和它#39; s已过时...对于较旧的80386和80486SX CPU,必须为HW FPU购买单独的x87协处理器芯片。

与此同时,英特尔试图通过推出全新的64位平台(" Itanium"或" IA-64"), NOT 向后兼容。不出所料,市场确实对采用率犹豫不决,因为所有旧的" x86" SW没有为此工作。

稍后AMD推出了自己的64位扩展,这次是围绕" x86"以类似的方式传统386扩展286,从客户的角度来看确实工作得更好(虽然这对于汇编程序员而言更为棘手,例如mov eax,1会修改整个64位rax寄存器,自动将前32位归零等等......其中一些古怪的规则"使原始32位指令集的64位扩展成为可能,从长远来看很好地工作,即使它在第一次阅读时可能感觉有点破解。)

如此现代" x86" PC确实包含单个芯片中的三个主要不同的CPU,过时的16位80286,非常老的32位" 686"以及当前的64位" x86-64"变种。在集合的观点上,所有这些都共享基本的指令和语法,所以如果你学习" 686"完整的指令集,x86-64源代码看起来很熟悉。只要您只使用指令集的基本子集(如" 686"子集),您的二进制文件就可以在所有15-20岁的x86 PC上的特定操作系统上运行。

与此同时,AMD(以及Cyrix和其他试图在" x86"世界中与英特尔竞争的制造商)正在生产与英特尔二进制兼容的CPU。有时他们试图引入一些扩展,例如" 3Dnow"来自AMD,但它们很少被程序员使用,并且通常被放弃。唯一的例外是目前的64位模式,由AMD和英特尔设计,最终不得不放弃并将其从AMD复制到他们的CPU中(因为他们的Itanium IA-64不被市场接受,因为缺少向后兼容性)。

但如果您喜欢游戏开发人员,追求最高性能,则必须在运行时检查CPU型号/功能,并根据指令扩展的可用性,为特定CPU提供不同的二进制变体,例如最多使用SSE2指令,能够在所有64位CPU上运行,以及使用SSE4和AVX512等新扩展的其他变体,这些扩展仅适用于拥有最新CPU的少数消费者。

默认情况下,许多C编译器在x86目标32或64位模式下(主要取决于操作系统或项目设置)并且仅使用非常有限的指令集,例如大多数" 686"只有,或者x86_64甚至没有SSE1 / 2,因此它们的二进制文件可以在任何常见的x86 PC上运行。虽然代码是次优的,但不使用当前CPU的现代功能。

如果你正在瞄准" x86"这也是你可以学到的第一件事。汇编知识,从基本指令集开始,也许只有80386,学习原理,然后看看它是如何扩展的(跳过16位80286,那只是无用的折磨,32位模式要简单得多)要学习,那么如果你真的很好奇,你可以尝试看看16位有什么不同,32位经验会让它变得更容易,但它毫无意义。)

64位比32位模式更加棘手/复杂,但它并没有那么糟糕,如果你愿意的话,你甚至可以开始使用它(无论是16位都是棘手的)。 / p> 顺便说一句,你应该继续使用C-lib进行I / O并赢得API调用,即你需要C编译器。从纯ASM访问Win API有点棘手(如果您只是学习ASM基础知识,只是一种不必要的分心),并且您无法在现代操作系统下直接访问HW,因此我无法做到/ O没有OS API服务,不像旧的16位模式,没有保护,你可以自由访问硬件外设。您可以从C包装器调用您的asm函数,然后可以处理I / O,内存管理和其他OS API相关的事情,专注于asm函数中的纯算法和asm编程(这也是汇编的方式)在实际项目中使用,你今天没有在asm中编写整个应用程序,你将它保存在C / C ++中,并且在你使用C / C ++原型并确定特定的瓶颈之后,只重写ASM中性能最关键的部分。通过剖析..没有必要在汇编中编写其他部分,比如文件读取等,太麻烦而没有任何好处)。

BT,C是便携式语言。如果你只使用标准的C库,它几乎可以在任何东西上工作(SOURCE),但只有在你为特定的目标平台BINARY进行COMPILE之后才能使用它。

Windows是一个非常小的世界,大多数限于x86(IA-64和DEC Alpha都有Windows变种,它们不能运行x86二进制文件,但那些是专业工作的专业机器,对于上市)。因此,如果您使用默认选项编译Win32 x86可执行文件,则将在99%的Windows计算机上运行(次优),仅使用有限的x86指令子集。如果您打开编译器以使用CPU的某些现代功能,那么生成的二进制文件可能在John Doe的PC上不再有效,如果他有x86 CPU并不支持您的某个功能机器代码确实使用。

对于许多应用程序来说,这个默认子集已经绰绰有余了,您不需要为扩展指令烦恼。只有少数应用程序需要最高性能,例如游戏,CFD或其他科学计算...您可以安全地忽略以+ 5-10%性能损失运行的普通Web浏览器(并且通常在驱动程序/某些地方某些地方有些罚款)为您当前的CPU提供了一段代码,它可以处理主要的性能问题,例如解码视频/等等,因此即使使用通用" x86"目标编译的Web浏览器也将受益于这些。

你不需要生成9999种asm代码变体,如果你只想让它运行,基本上你只需要32或64位变体(取决于目标,我不知道Windows世界,但使用现代操作系统,您可以安全地定位64位,这类似于90%以上的用户),使用基本的x86指令,这将在任何地方工作"无处不在" (在x86窗口上)。

但是没有什么意义可以像这样使用asm(除了教育目的,完全有意义地从这开始),因为这样的asm的性能将是次优的,所以你已经可以使用C或C ++来获得类似的结果。为了有意义的使用,您还必须学习现代扩展,对可用功能进行运行时检查,并使用特定CPU类型的最佳机器代码动态加载函数的正确变量。这是你可能必须编写相同功能的9999个变体的部分(它不是那么糟糕,通常4-7个变体可能会覆盖大多数可用的CPU,一个兼容性回退仅使用基本指令集,然后几个更专业,如SSE3,+ SSE4,AVX1 / 2,AVX512等。)。另外,需要最高性能的现实世界SW非常罕见,即使大多数简单的游戏都完全没有次优的二进制文件,只有当你在像虚幻引擎开发者这样的前沿工作时,你必须关心那些微调。 。大部分时间收益都不值得投资。

毕竟,现在有太多的SW在使用,用C#,Java甚至JavaScript(或PHP ......我真的提到过吗?现在觉得很脏),显然性能不是很好一个问题,否则大部分会在一段时间后重写为C ++,以提高性能。

答案 1 :(得分:3)

您一直声称用C编写的程序可以被其他人在计算机上使用而没有任何问题。只有当他们在SPARC工作站上运行Windows而不是Linux或OS X,OpenBSD或Solaris时才会这样......

您必须用来在其他操作系统上显示窗口或进行网络I / O(“ping google.com”)的C库是不同的。特别是ping是非常不便携的东西,因为发送ICMP echo-r​​equest数据包通常是特权操作。 (例如,Linux上的ping可执行文件是setuid-root,因此它可以强制执行速率限制。)您可能使用system("ping google.com")而不是在自己的程序中使用网络套接字。一个更好的例子是发出HTTP请求,因为任何进程都可以打开TCP连接。

可以编写可移植的C程序,但你必须努力使它们可移植。人们需要源代码,以便他们可以为自己的系统编译它。 (这就是为什么Unix具有以源代码形式分发软件的传统:每个人都需要使用自己的库版本为自己的系统编译它。)

编译的二进制文件只能在他们编译的目标平台上工作,例如x86-64 Windows。这样的二进制文件不能在ARM Windows或32位x86 Windows上运行。

在asm中编写时,特定于目标平台(包括CPU ,而不仅仅是哪些系统调用和库可用)。因此,您将编写32位x86 Windows的代码,而不仅仅是“for Windows”,C编译器能够从同一来源生成x86 32位或x86-64 64位二进制文​​件。 / p>

如果您首先关心的是32位x86 Windows,那么这一点并没有太大的区别。 (每个“普通”Windows计算机都可以运行32位x86二进制文件,因此,如果您希望可以移植到其他Windows计算机,那么就应该这样做。)

例如,让我们使用Godbolt compiler explorer (source+asm)来查看一个简单的C程序如何编译为两个不同的x86平台的不同asm:

#include <stdio.h>
int main() { puts("Hello World"); }

为x86-64 Linux(x86-64 System V调用约定)编译此asm,gcc -O3(Intel-syntax模式而不是AT&amp; T,Godbolt传递它-masm=intel):< / p>

.LC0:
    .string "Hello World"
main:
    sub     rsp, 8                   # align the stack before a call
    mov     edi, OFFSET FLAT:.LC0
    call    puts                     # first arg passed in RDI
    xor     eax, eax                 # eax = 0 = return value.
    add     rsp, 8                   # restore the stack
    ret

但32位x86 Windows的MSVC将其编译为此asm:

$SG5328 DB        'Hello World', 00H
EXTRN   _puts:PROC
_main   PROC
    push     OFFSET $SG5328
    call     _puts                    ; first arg passed on the stack
    add      esp, 4                   ; clean up args
    xor      eax, eax                 ; eax = 0 = return value.
    ret      0
_main   ENDP

汇编语法不同(MASM与GAS),但这不重要。 重要的事情是指向字符串文字的指针在堆栈上传递(使用push)而不是寄存器。

puts的asm符号名称在Windows上以_为前缀,但在Linux上没有。

由于这是32位代码,因此堆栈槽的宽度仅为4个字节,而不是8个。

这适用于两个平台上都可用的ISO {C puts等功能。在Windows上,您可以在WinAPI DLL中调用MessageBoxA,但Linux没有“本机”图形API。对于大多数桌面,您需要一个X11库,但不是每个人都使用X11。

由于您已经了解C,因此查看编译器输出是开始学习asm的好方法。 见Matt Godbolt的CppCon2017演讲“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid” 。 (还有关编写微小函数的进一步建议,编译成有趣的asm:How to remove "noise" from GCC/clang assembly output?)。

答案 2 :(得分:2)

让我们一步一步地采取行动:

  • 如果处理器相同,asm代码是相同的,操作系统无关紧要,因此您不必专注于Windows。
  • 是的,你必须选择一个特定的架构(或者你应该)。每个架构都有不同的指令集。进一步的说明,你必须了解架构(寄存器,地址,总线......),以便设计一个合适的程序,所以绝对是非常重要的。
  • asm并不容易,实际上非常难,所以如果目标是复杂的任务(服务器?),硬转变成几乎不可能。如果这是一个问题(或架构),您可以尝试更高级别,例如C。
  • 可以在不同的架构中使用二进制文件吗?是的,但这只是巧合(或兼容,如英特尔8085上的英特尔8080)。

答案 3 :(得分:-1)

Assembler code don't work on CPUs. It needs to be transformed to machine code by the program called assembler. That transformation is usually simple (usually, the assembler program transforms text containing assembler code into some object code; the linker then agglomerates several object files into an executable - and resolve relocations; that executable contains machine code).

There are several assembler syntaxes for x86 (e.g. nasm vs gas).

And there are several variants of x86 (e.g. some processors, but not all, accept extensions like AVX, and there is 32 bits vs 64 bits).

At last, a user-land x86 program for Windows is some executable which won't work on the same x86 architecture (or even the same hardware) running some other operating system (e.g. Linux), because the ABI, the system calls, the executable format (PE on Windows, ELF on Linux) are different. Read Operating Systems: Three Easy Pieces for more about OSes.