说明
nasm与vs的masm存在差异,如nasm源程序可以只有指令,而不需要段分布,主函数等等。以下是一个例子:
mov eax, 0
inc eax
通过nasm编译:
编译完成后生成一个test.bin:
打开这个test.bin就可以看到指令的机器码:
对应源代码在VS中的反汇编:
多了个66,什么意思?
修改nasm编译源程序:
得到的机器码编程:
还是有问题,不同的机器码,怎么在同一个X86CPU里面运行?
(2016.08.21更新)
66是Intel汇编中的Instruction Prefixes,即指令前缀。有好几种指令前缀类型,66属于Oprand-size override prefix。
Intel对于同一指令下可操作不同的操作数的情况,没有特意使用不同的机器码,而是通过前缀来区分。这就能解释上面nasm中两条mov指令的区别。
不过VS和nasm在同样的32位操作数下有不同的结果还是没能解释。
(2017.01.02更新)
这边使用的编译主机是64位的,在上述的代码中没有指定默认的系统,所以它使用的是64位的编译,因此为了得到与VS反汇编一致的结果,需要设置编译时的系统位数:
bits 32
mov eax, 0
inc eax
这样编译得到的结果如下:
以上的test.bin理论上CPU可以直接执行,但是在windows系统下是不识别的,为了解决这个问题,可以使用虚拟机,将bin文件直接放到虚拟机结束后跳转到的位置(0x7c00,实际上是硬盘第一个扇区被加载的位置),这样bin文件就可以直接执行。所以要做的就是将虚拟机使用的第一个扇区修改为我们需要的bin,就可以执行这个代码了。
下面是Helloworld的nasm源程序代码:
mov ax, 0xb800 ;表示的是显存起始位置(后面还会提到)
mov ds, ax
mov byte [0x00], 'H' ;打印 Hello World!
mov byte [0x01],0x07
mov byte [0x02], 'e'
mov byte [0x01],0x07
mov byte [0x04], 'l'
mov byte [0x01],0x07
mov byte [0x06], 'l'
mov byte [0x01],0x07
mov byte [0x08], 'o'
mov byte [0x01],0x07
mov byte [0x0C], 'w'
mov byte [0x01],0x07
mov byte [0x0E], 'o'
mov byte [0x01],0x07
mov byte [0x10], 'r'
mov byte [0x01],0x07
mov byte [0x12], 'l'
mov byte [0x01],0x07
mov byte [0x14], 'd'
mov byte [0x01],0x07
mov byte [0x16], '!'
mov byte [0x01],0x07
jmp $ ;让 CPU 挂这里
times 393 db 0
db 0x55, 0xaa ;虚拟机需要检测 MBR 标记
编译后获得二进制,将二进制写入虚拟机使用的硬盘中,具体见图:
PS:使用虚拟机运行二进制的例子来自《x86汇编语言 : 从实模式到保护模式》。