x86 32位机器码,24 21字节
changelog:-3个字节:用@peter ferrie用DAS hack替换标准的add / cmp / jbe / add
64位:仍为24个字节。长模式删除了DAS操作码。
16位模式:默认操作数大小为16位,但问题说明本质上为32位。包括硬编码的8个十六进制数字。
字节反转,bswap然后按标准顺序手动int-> hex(最重要的半字节在前,以升序将十六进制数字写入char输出缓冲区)。这避免了展开循环以在字节vs.内的半字节之间切换顺序。跨字节。
可以void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);像x86-64 System V一样调用,但不能在64位模式下使用。(它需要EDI中的输出指针用于stosb。输入号可以位于ECX或EAX以外的任何寄存器中。)
1 lehex:
2 00000000 0FCE bswap esi
3 00000002 6A08 push 8 ; 8 hex digits
4 00000004 59 pop ecx
5 .loop: ;do{
6 00000005 C1C604 rol esi, 4 ; rotate high nibble to the bottom
7
8 00000008 89F0 mov eax, esi
9 0000000A 240F and al, 0x0f ; isolate low nibble
10 0000000C 3C0A cmp al, 10 ; set CF according to digit <= 9
11 0000000E 1C69 sbb al, 0x69 ; read CF, set CF and conditionally set AF
12 00000010 2F das ; magic, which happens to work
13
14 00000011 AA stosb ; *edi++ = al
15 00000012 E2F1 loop .loop ; }while(--ecx)
16
17 00000014 C3 ret
大小= 0x15 = 21字节
TIO FASM 32位x86测试用例,带有一个asm调用程序,该调用程序使用write系统调用在两次调用两次以将2个字符串附加到缓冲区中之后写入输出。测试所有十六进制数字0..F,包括数字与字母之间的边界处的9和A。
该DAS黑客 - 86具有半进位标志,用于开展低四位的。对于打包BCD之类的东西很有用,例如DAS指令,打算在减去两个2位数的BCD整数后使用。由于AL的低位字节不在0-9范围内,因此我们绝对在这里滥用它。
注意手册中“操作”部分的“ if (old_AL > 99H) or (old_CF = 1)THEN” AL ← AL − 60H;部分;sbb 始终在此处设置CF,以便始终发生该部分。那和大写字母的ASCII范围是促使选择sub al, 0x69
cmp 0xD, 0xA 没有设置CF
- sbb
0xD - 0x69包装为AL = 0xA4作为DAS的输入。(并设置CF,清除AF)
- DAS的第一部分没有AL-= 6(因为4> 9为false且AF = 0)
- 第二部分为AL-= 0x60,剩下的
0x44是的ASCII码'D'
与数字:
cmp 0x3, 0xA 设置CF
- sbb
3 - 0x69 - 1= AL = 0x99并设置CF和AF
- DAS的第一部分中没有AL-= 6(9> 9为假,但设置了AF),保留0x93
- 第二部分AL-= 0x60,保留0x33,即的ASCII码
'3'。
0x6a在SBB中减去将为每个<= 9的数字设置AF,因此所有数字都遵循相同的逻辑。并保留每个字母十六进制数字清除它。即正确利用DAS的9 / A拆分处理。
通常(为了提高性能),您将使用查找表进行标量循环,或者可能使用无分支2x lea和cmp/cmov条件加法。但是2字节al, imm8指令是代码大小的一大胜利。
x86-64版本version:and al, 0xf和之间只是不同的部分stosb。
;; x86-64 int -> hex in 8 bytes
10 0000000C 0430 add al, '0'
11 0000000E 3C39 cmp al, '9'
12 00000010 7602 jbe .digit
13 00000012 0427 add al, 'a'-10 - '0' ; al = al>9 ? al+'a'-10 : al+'0'
14 .digit:
请注意,add al, '0' 总是运行,而条件增加只会增加之间的区别'a'-10和'0',使其只是if代替if/ else。
经过测试,可以使用与C答案相同的main调用方(使用和)进行工作。char buf[8]printf("%.8s\n", buf)