正如问题所示,我必须编写一个MASM程序来将整数转换为二进制。我尝试了很多不同的方法,但它们都没有帮助我。我正在研究的最终代码如下。我在 Visual Studio 中调试代码时出现访问内存冲突错误。
如何解决错误的任何帮助,如果我在正确的轨道上,将不胜感激。第一个代码是我的C ++代码,它将char数组传递给.asm
文件以转换为二进制文件。
#include <iostream>
using namespace std;
extern "C"
{
int intToBin(char*);
}
int main()
{
char str[17] = { NULL };
for (int i = 0; i < 16; i++)
{
str[i] = '0';
}
cout << "Please enter an integer number :";
cin >>str;
intToBin(str);
cout << " the equivilant binaryis: " << str << endl;
return 0;
}
和.asm
文件如下:
.686
.model small
.code
_intToBin PROC ;name of fucntion
start:
push ebp ; save base pointer
mov ebp, esp ; establish stack frame
mov eax, [ebp+8] ; stroing char value into eax
mov ebx, [ebp+12]; adress offset of char array
mov edx,32768 ;storin max 16bit binary in edx
mov ecx,17 ; since its a 16 bit , we do the loop 17 times
nextBite:
test eax,edx ;testing if eax is equal to edx
jz storeZero ;if it is 0 is to be moved into bl
mov bl,'1' ;if not 1 is moved into bl
jmp storeAscBit ;then jump to store ascii bit
storeZero:
mov bl,'0' ;moving 0 into bl register
storeAscBit:
mov [di ],bl ;moving bl (either 1 or 9) into [di]
inc edx ;increasing edx stack by 1 point to go to next bt
shr edx,1 ;shfiting right 1 time so the 1 comes to second
loop nextBite ; do the whole step again
EndifReach:
pop ebp
_intToBin ENDP
END
答案 0 :(得分:2)
这是解释某些术语的高级答案。
整数值是整数值,在数学中它是纯粹抽象的东西。数字&#34; 5&#34;不是你在显示器上看到的(那个数字5(图形图像或&#34;字形&#34;)代表人类(和一些受过训练的动物)的基础10(十进制)格式的值5识别字形模式; 值5 本身纯粹是抽象)。
当你在C ++中使用int
时,它并不是完全抽象的,它更难以连接到金属中。它是32位(在大多数平台上)的整数值。
但是,抽象的描述更接近真理,而不是将其想象为人类的十进制格式。
int a = 12345; // decimal number
此处a
包含值12345,而不是格式。它不知道它是在源代码中输入的十进制字符串。
int a = 0x3039; // hexadecimal number
将编译为完全相同的机器代码,对于CPU来说,它仍然是(a == 12345)
。最后:
int a = 0b0011000000111001; // binary number
也是一回事。它仍然是相同的12345
值,只是用不同的格式编写。
最后一个二进制形式最接近用于存储值的CPU。它以32位(低/高电压电池/电线)存储,因此,如果您要测量特定电池/电线上的电压,您会看到&#34; 0&#34;前18位的电压电平,然后2位,&#34; 1&#34;电压电平,然后其余部分就像上面的二进制格式一样......两个最低有效位为&#34; 0&#34;和&#34; 1&#34;。
同样大多数的CPU电路都没有意识到特定位的特定值,这又是&#34;解释&#34;那个0/1,由代码完成。许多CPU算法如add
或sub
从右到左工作&#34;&#34;在所有位上,不知道当前处理的位表示最终整数值,例如2 13 值(即第14个最低有效位)。
获取这些位,并计算这些位值的十进制/十六进制/二进制表示的字符串,当你给那些&#34; 1&#34; s它们的值。那么它就变成了文本 "12345"
。
如果您以不同的方式处理这些32位,例如LED显示面板的ON / OFF LED灯的表示,那么一旦您将其从CPU发送到显示器,LED显示面板将打开相应的LED灯,并不关心这些位在被视为12345
时会形成 int
值。
只有极少数CPU指令以某种方式工作,他们需要知道特定位的特定值。
您希望&#34;将十进制整数(输入)转换为二进制。&#34;
因此,让我们了解输入内容和输出内容。输入来自std::cin
,因此用户将输入字符串。
但是如果你愿意的话:
int inputNum;
std::cin >> inputNum;
当用户无法输入正确的数字时,您将以已转换的整数值(32位,见上文)(或无效的std::cin
状态结束,可能不是您处理此问题的任务。)
如果您的号码在int
中,那么二进制转换已由clib
完成,当时它将用户输入字符串编码为32位整数。
现在您可以使用C prototype创建asm函数:
void formatToBinary(uint16_t value, char result[17]);
这意味着你将给它uint16_t
(无符号16位)整数值,并指向内存中17个保留字节的指针,在那里你将写入'0'
和'1'
个ASCII字符,以及通过另一个0
值终止它(对于这个值的粗略描述,请按照我的问题中的评论中的第一个链接)。
如果你必须把输入作为字符串,即
char str[17];
std::cin > str;
然后,您将在str
(在&#34; 12345&#34;输入之后)字节中输入值:'1'
(十进制49),'2'
,{{1} },'3'
,'4'
,'5'
。 (注意最后一个是零,非ASCII数字0
=值'0'
)。
首先需要将这些ASCII字节转换为整数值(在C ++ 48
中可能有所帮助,或者转换/格式化的其他少数函数之一)。在ASM中,检查SO以查询问题&#34;如何输入整数&#34;。
一旦你将它转换为整数值,你可以按照上面描述的相同方式进行(此时它已经编码为16或32位,因此输出它的字符串表示应该很容易)
你可能仍会遇到一些棘手的问题,比如你不想输出前导零等......但如果你理解这是如何运作的话,所有这些都应该很容易。
在这种情况下,您的ASM函数原型可能只有atoi
才能将字符串指针重用为输入和输出。
您的void convertToBinary(char*);
看起来很奇怪,因为这意味着ASM会返回int intToBin(char*);
..但为什么?这个整数值,没有绑定到任何特定的格式,所以它同时是二进制/八进制/十进制/六进制。取决于你如何显示它。因此,您不需要它,您只需要表示二进制形式值的字符串,即int
。并且你没有给你输入的号码(除非它从字符串中取出)。
从任务描述和你的技能水平我认为你可以在C ++中将输入转换为char *
(即int
)。
std::cin >> int_variable;
它仍然有点脆弱,例如他初始化&#34; bin&#34;以这种方式,它包含32个空格,第33个值为零(C字符串的空终止符)。然后在代码中他确实修改了32个字节,所以第33个零仍然在那里工作。如果你要调整他的代码以跳过前导零,那么它会打破&#34;打破&#34;通过显示缓冲区的剩余部分,因为他没有明确设置空终止符。
这是如何在Assembly中编写性能代码,准确了解所发生的一切,而不是设置已设置/等的值的常用方法。在你学习的过程中,我会建议你在&#34;防守&#34;方式,而不是做一些浪费的事情,如果出现一些错误,它将作为安全网工作,所以我会在 mov eax, num // ◄■■ THE NUMBER.
lea edi, bin // ◄■■ POINT TO VARIABLE "BIN".
mov ecx, 32 // ◄■■ NUMBER IS 32 BITS.
conversion:
shl eax, 1 // ◄■■ GET LEFTMOST BIT.
jc bit1 // ◄■■ IF EXTRACTED BIT == 1
mov [edi], '0'
jmp skip
bit1:
mov [edi], '1'
skip :
inc edi // ◄■■ NEXT POSITION IN "BIN".
loop conversion
后添加mov byte ptr [edi],0
再次明确设置终结符。
但它实际上不是很快,因为它使用的是分支。 CPU不是那样的,解码新指令是一项代价高昂的任务,如果不确定,将执行哪些指令,它只是在一条路径前解码,如果猜错了,它会抛出它,并解码正确的路径,但这意味着执行中会暂停几个周期,直到新路径的第一条指令被完全解码并准备好执行。
因此,在编写性能编码时,您希望避免难以预测的分支(最终loop
很容易为CPU预测,因为它始终循环,直到ecx为{{1}之后的最终退出})。在这种情况下,许多可能的方法之一可以是:
loop
正如您所看到的,现在除了循环之外没有分支,CPU分支预测将更加平滑地处理这一点,并且性能会更好。
与Cody讨论的双字变体(NASM语法,32b目标):
0
没有对其进行分析,只是验证它会返回正确的结果。
答案 1 :(得分:1)
接下来是使用“ atoi ”将字符串转换为数字,然后使用程序集将数字转换为二进制的示例:
#include "stdafx.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{ char str[6]; // ◄■■ NUMBER IN STRING FORMAT.
int num; // ◄■■ NUMBER IN NUMERIC FORMAT.
char bin[33] = " "; // ◄■■ BUFFER FOR ONES AND ZEROES.
cout << "Enter a number: ";
cin >> str; // ◄■■ CAPTURE NUMBER AS STRING.
num = atoi(str); // ◄■■ CONVERT STRING TO NUMBER.
__asm {
mov eax, num // ◄■■ THE NUMBER.
lea edi, bin // ◄■■ POINT TO VARIABLE "BIN".
mov ecx, 32 // ◄■■ NUMBER IS 32 BITS.
conversion:
shl eax, 1 // ◄■■ GET LEFTMOST BIT.
jc bit1 // ◄■■ IF EXTRACTED BIT == 1
mov [edi], '0'
jmp skip
bit1:
mov [edi], '1'
skip :
inc edi // ◄■■ NEXT POSITION IN "BIN".
loop conversion
}
cout << bin;
return 0;
}
答案 2 :(得分:1)
你可以在没有任何循环的情况下完成所有事情,使用SIMD并行执行所有操作。
十进制字符串 - &gt;整数部分很复杂,但有a complete atoi()
implementation in asm使用SSE4.2 PCMPISTRI,然后是一些shuffle,然后PMADDWD将数字乘以它们的地方值。对于Haswell,IACA表示它有大约64个周期的延迟,因此短整数可能不会更快。
整数 - &gt; base 2字符串部分要简单得多,只能使用SSE2有效地完成。
这使用与Evgeny Kluev's answer on a question about doing the inverse of PMOVMSKB相同的技术,将位模式转换为0 / -1元素的向量:广播 - 重排输入字节,使每个向量元素包含您想要的位(加上邻居) 。并且只留下零或1,然后与全零向量进行比较。
此版本仅需要SSE2,因此它适用于可以运行64位操作系统的每个CPU,以及一些仅32位的CPU(如早期的Pentium4)。使用SSSE3可以更快(一个PSHUFB而不是三个shuffle来获得我们想要的低和高字节)。你可以做8位 - &gt; MMX一次8个字节。
我不会尝试将其从NASM转换为MASM语法。我实际测试了这个,它的确有效。我可能只是在尝试转换时引入语法错误。 x86 32位System V调用约定与影响此代码AFAIK的任何方式的32位Windows cdecl调用约定没有区别。
;;; Tested and works
;; nasm syntax, 32-bit System V (or Windows cdecl) calling convention:
;;;; char *numberToBin(uint16_t num, char buf[17]);
;; returns buf.
ALIGN 16
global numberToBin
numberToBin:
movd xmm0, [esp+4] ; 32-bit load even though we only care about the low 16 bits.
mov eax, [esp+8] ; buffer pointer
; to print left-to-right, we need the high bit to go in the first (low) byte
punpcklbw xmm0, xmm0 ; llhh (from low to high byte elements)
pshuflw xmm0, xmm0, 00000101b ; hhhhllll
punpckldq xmm0, xmm0 ; hhhhhhhhllllllll
; or with SSSE3:
; pshufb xmm0, [shuf_broadcast_hi_lo] ; SSSE3
pand xmm0, [bitmask] ; each input bit is now isolated within the corresponding output byte
; compare it against zero
pxor xmm1,xmm1
pcmpeqb xmm0, xmm1 ; -1 in elements that are 0, 0 in elements with any non-zero bit.
paddb xmm0, [ascii_ones] ; '1' + (-1 or 0) = '0' or 1'
mov byte [eax+16], 0 ; terminating zero
movups [eax], xmm0
ret
section .rodata
ALIGN 16
;; only used for SSSE3
shuf_broadcast_hi_lo:
db 1,1,1,1, 1,1,1,1 ; broadcast the second 8 bits to the first 8 bytes
db 0,0,0,0, 0,0,0,0 ; broadcast the first 8 bits to the second 8 bytes
bitmask: ; select the relevant bit within each byte, from high to low for printing
db 1<<7, 1<<6, 1<<5, 1<<4
db 1<<3, 1<<2, 1<<1, 1<<0
db 1<<7, 1<<6, 1<<5, 1<<4
db 1<<3, 1<<2, 1<<1, 1<<0
ascii_ones:
times 16 db '1'
使用PSHUFLW在第二个shuffle步骤中进行反转在旧CPU(第一代Core2及更早版本)上进行反转速度更快,因为128b shuffle缓慢,因为只有低64位的快速移动速度很快。 (与使用PUNPCKLWD / PSHUFD相比)。请参阅Agner Fog的Optimizing Assembly guide以了解有关编写高效asm的更多信息,以及x86标记wiki中的其他链接。
(Thanks to clang for spotting the possibility)。
如果你在循环中使用它,你可以将向量常量加载到向量寄存器中,而不是每次都重新加载它们。
从asm,你可以称之为
sub esp, 32
push esp ; buf[] on the stack
push 0xfba9 ; use a constant num for exmaple
call numberToBin
add esp, 8
;; esp is now pointing at the string
或者使用asm中的注释原型从C或C ++中调用它。