Как напечатать целое число со знаком в сборке x86 (NASM) на Mac - PullRequest
0 голосов
/ 22 марта 2019

Я обнаружил реализацию преобразования целых чисел без знака в сборке 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, чтобы можно было регистрировать значения регистров.

1 Ответ

1 голос
/ 22 марта 2019

Мой ответ на Как вывести целое число в Программирование на уровне сборки без printf из библиотеки c? , который вы уже связали, показывает, что сериализация целого числа в память как десятичного числа 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 , он будет корректным без переполнения для всех входных данных.

Вместо фактической печати -, просто сохраните тот факт, что начальное число было отрицательным , и вставьте - перед другими цифрами после генерации последней. Для баз, которые не имеют степеней 2, обычный алгоритм может генерировать цифры только в обратном порядке печати,

Мое целое число для печати x86-64 с ответом syscall обрабатывает ввод как неподписанный, поэтому вы должны просто использовать его с некоторым кодом для обработки знаков. Он был написан для Linux, но замена номера системного вызова write сделает его работающим на Mac. У них одинаковые правила вызова и ABI.


И, кстати, xor al,al строго хуже, чем xor eax,eax, если только вы специально не хотите сохранить старшие 7 байтов RAX. эффективно обрабатывает только нулевое значение полных регистров .

Кроме того, repnz scasb - это не быстро; примерно 1 сравнение в час для больших строк.

Для строк длиной до 16 байт вы можете использовать один вектор XMM с pcmpeqb / pmovmskb / bsf, чтобы найти первый нулевой байт без цикла. (SSE2 является базовой для x86-64).

...