0

我在 x86 汇编中找到了无符号整数转换的实现,我尝试将其插入,但对汇编来说是新手并且还没有调试环境,很难理解为什么它不起作用。我还希望它与有符号整数一起使用,以便它可以从系统调用中捕获错误消息。

想知道是否可以显示如何修复此代码以打印有符号整数,而不使用 printf 而是使用答案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 StackOverflow!!!", 0xa, 0xa, 0

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

4

1 回答 1

1

我对如何在没有来自 c 库的 printf 的情况下在汇编级编程中打印整数的回答?您已经链接的内容表明,将整数作为 ASCII 十进制序列化到内存中会给您一个长度,因此您在这里没有使用(自定义版本)strlen

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

要打印有符号整数,请实现以下逻辑:

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

无符号涵盖了这种abs(most_negative_integer)情况,例如在 8 位- (-128)溢出到有-128符号时。但是,如果您将该条件的结果neg视为unsigned,则它是正确的,所有输入都没有溢出。

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

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


顺便说一句,除非您特别保留 RAX 的高 7 个字节,否则xor al,al严格来说更糟。只有完整寄存器的异或归零作为归零习惯有效地处理xor eax,eax

还有repnz scasb不快;对于大字符串,每个时钟大约 1 次比较。

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

于 2019-03-22T06:36:28.517 回答