如何在 Mac 上的 x86 程序集 (NASM) 中打印有符号整数

How to print signed integer in x86 assembly (NASM) on Mac

我在 x86 汇编中发现了一个 implementation 的无符号整数转换,我尝试将其插入,但作为汇编的新手并且还没有调试环境,很难理解为什么它不起作用。我还希望它能处理有符号整数,这样它就可以从系统调用中捕获错误消息。

想知道是否可以说明如何修复此代码以打印带符号整数,而不使用 printf,而是使用 this 答案提供的 strprn

%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax

%define EXIT 0x2000001
%define EXIT_STATUS 0

%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)

; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
strsz:
  xor     rcx, rcx                ; zero rcx
  not     rcx                     ; set rcx = -1 (uses bitwise id: ~x = -x-1)
  xor     al,al                   ; zero the al register (initialize to NUL)
  cld                             ; clear the direction flag
  repnz scasb                     ; get the string length (dec rcx through NUL)
  not     rcx                     ; rev all bits of negative -> absolute value
  dec     rcx                     ; -1 to skip the null-term, rcx contains length
  mov     rdx, rcx                ; size returned in rdx, ready to call write
  ret

; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
strprn:
  push    rdi                     ; push string address onto stack
  call    strsz                   ; call strsz to get length
  pop     rsi                     ; pop string to rsi (source index)
  mov     rax, WRITE              ; put write/stdout number in rax (both 1)
  mov     rdi, 1        ; set destination index to rax (stdout)
  syscall                         ; call kernel
  ret

; mov ebx, 0xCCCCCCCD
itoa:
  xor rdi, rdi
  call itoal
  ret

; itoa loop
itoal:
  mov ecx, eax                    ; save original number

  mul ebx                         ; divide by 10 using agner fog's 'magic number'
  shr edx, 3                      ;

  mov eax, edx                    ; store quotient for next loop

  lea edx, [edx*4 + edx]          ; multiply by 10
  shl rdi, 8                      ; make room for byte
  lea edx, [edx*2 - '0']          ; finish *10 and convert to ascii
  sub ecx, edx                    ; subtract from original number to get remainder

  lea rdi, [rdi + rcx]            ; store next byte

  test eax, eax
  jnz itoal

exit:
  mov a, EXIT_STATUS ; exit status
  mov i, EXIT ; exit
  syscall

_main:
  mov rdi, msg
  call strprn
  mov ebx, -0xCCCCCCCD
  call itoa
  call strprn
  jmp exit

section .text
msg: db  0xa, "  Hello Whosebug!!!", 0xa, 0xa, 0

通过这项工作,可以将有符号整数正确打印到 STDOUT,因此您可以记录寄存器值。

我在您已经链接的 How do I print an integer in Assembly Level Programming without printf from the c library? 上的回答表明,将一个整数序列化为 ASCII 十进制的内存会为您提供一个长度,因此您在这里没有用 strlen 的(自定义版本) .

(你的 msg 有一个 assemble 时间常数长度,所以不使用它是愚蠢的。)

要打印有符号整数,请执行以下逻辑:

if (x < 0) {
    print('-');   // or just was_negative = 1
    x = -x;
}
unsigned_intprint(x);

Unsigned 涵盖 abs(most_negative_integer) 案例,例如在 8 位中 - (-128) 溢出到 -128 有符号。但是,如果您将条件 neg 的结果视为 unsigned,那么它是正确的,所有输入都没有溢出。

与其实际打印 - 本身,只需保存起始数字为负数的事实 并将 - 粘贴在 - 前面生成最后一个数字后的其他数字。对于不是 2 的幂的基数,普通算法只能以相反的打印顺序生成数字,

我的带有系统调用答案的 x86-64 打印整数将输入视为无符号,因此您应该简单地将其与一些符号处理代码一起使用。它是为 Linux 编写的,但替换 write 系统调用号将使它在 Mac 上运行。它们具有相同的调用约定和 ABI。


顺便说一句,xor al,al 严格来说比 xor eax,eax 差,除非你特别 想要 保留 RAX 的高 7 字节。只有完整寄存器的异或归零是 .

此外,repnz scasb快;对于大字符串,每个时钟大约进行 1 次比较。

对于最多 16 个字节的字符串,您可以使用带有 pcmpeqb / pmovmskb / bsf 的单个 XMM 向量来查找第一个零字节,无需循环。 (SSE2 是 x86-64 的基准)。