汇编语言将十进制字符串转为十六进制
Author:Em1ya
Part1:Str to hex
基本思路:将10进制字符串的每一个字符提出,减去0x30转化位该位对应的数值,将数值与原结果*10相加
例如:1234的分解成为1+0*10=1,2+1*10=12,3+12*10=123,4+123*10=1234,在加完之后存入寄存器,就自动变成十六进制数
Part2:hex to Str
对于1步骤后的00000064,我们做如下处理
先将该数右移至最低的十六进制位为我们想要提取的位
所以,对于倒数第i项(i从0开始),应右移该寄存器4*i次
比如00000064的“6”那一位
向右移动4二进制位得到00000006
再对该数进行与0xf的操作,只保留最后一位
例如00101011&0xf
就是00001011只保留了最后一个十六进制位
若该位小于等于9,则ADD 30h,
若否,则加上57h(10+57h=87+10=97=ASCII a)
Part3:更好的优化
题目中*10的计算应该如何实现,可以参考lea的实现方式(猜想)
eax*10=eax*8+eax*2=eax<<3+eax<<1
如此可以大量减少运算时间,毕竟一次MUL或者DIV要消耗十分多的时钟周期
Part4:Code
.386
.model flat, stdcall
.stack 4096
option casemap:none
include D:\masm32\include\windows.inc
include D:\masm32\include\kernel32.inc
include D:\masm32\include\masm32.inc
includelib D:\masm32\lib\kernel32.lib
includelib D:\masm32\lib\masm32.lib
ExitProcess PROTO, dwEXITCODE: DWORD
;define your type here
.data
buf byte 20 DUP(0)
w byte "Please input",0
var DWORD 0
res BYTE 8 DUP(30h)
.code
main PROC
INVOKE StdOut, addr w
INVOKE StdIn,addr buf,20
MOV edx,0h
MOV ecx,0h
;ecx用来记录你在处理第几个数字
MOV eax,0h
;eax用来存放每一次计算的结果
L2:
MOV dl,byte PTR [buf+ecx]
;把第ecx个字符移到edx的低8位
CMP dl,0h
;看有没有到字符串末尾(ASCII 0)
JE L1
;有就跳出
SUB dl,30h
;没有的话把ASCII值变为数值
MOV ebx,eax
;把结果数值移到ebx来等待处理*10,向左移动3位相当于*8,移1位相当于*2,加起来相当于*10
SHL ebx,3
SHL eax,1
ADD eax,ebx
;*10完成,接下来把存在edx里的那一位拿过来加上
ADD eax,edx
;过程为“100”->1+0*10=1->1*10+0=10->10*10+0=100!
INC ecx
;取下一个字符
JMP L2
L1:
MOV var,eax
;保存十六进制值
MOV edx,7h
;找每次要处理的字符
MOV ecx,0h
;记录要第几位
L4:
MOV eax,var
MOV ebx,ecx
SHL ebx,2
;记录位移多少次,因为十六进制一位是四个二进制位,所以左移两次相当于*4
L6:
CMP ebx,0
;位移0次直接进行下一过程
JE L5
SHR eax,1
DEC EBX
JMP L6
;这一过程相当于第一次位移0次,最后一个十六进制为4,第二次移动4位,最后一十六进制位为6
L5:
AND eax,0fh
CMP eax,9h
;判断是数字还是字母
JLE L7
ADD eax,57h
JMP L8
L7:
ADD eax,30h
L8:
MOV [res+edx],al
INC ecx
CMP edx,0h
JE L3
DEC edx
JMP L4
L3:
INVOKE StdOut, addr res
INVOKE ExitProcess,0
main ENDP
END main
Part5:总结
要善于利用位运算的优势,对寄存器和内存进行快速操作