Более эффективный способ вывода целого числа в чистой сборке

Я хочу вывести целое число, используя чистую сборку. Я использую nasm на 64-битной машине Linux. На данный момент я ищу способ вывода целых чисел для отладки компилятора, но я хочу использовать тот же код для написания ОС, что также является причиной того, что я просто не использую printf(). После долгих поисков и разочарований я придумал этот код

    SECTION .data
var:    db  "      ",10,0

    SECTION .text
global main
global _printc
global _printi

main:
    mov rax, 90
    push    rax
    call    _printi

    xor rbx, rbx
    mov rax, 1
    int 0x80

_printi:
    pushf
    push    rax
    push    rbx
    push    rcx
    push    rdx

    mov rax, [rsp+48]
    mov rcx, 4
.start:
    dec rcx
    xor rdx, rdx
    mov rbx, 10
    div rbx
    add rdx, 48
    mov [var+rcx], dl
    cmp rax, 0
    jne .start

    mov rax, [var]
    push    rax
    call    _printc
    pop rax

    pop rdx
    pop rcx
    pop rbx
    pop rax
    popf
    ret

_printc:
    push    rax
    push    rbx
    push    rcx
    push    rdx

    mov rax, [rsp+40]
    mov [var], rax
    mov rax, 4
    mov rbx, 1
    mov rcx, var
    mov rdx, 4
    int 0x80

    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret

Обратите внимание, что при переходе на разработку ОС я заменю вызовы 0x80 вызовами BIOS.

Мой вопрос в том, как еще больше оптимизировать или даже улучшить этот код. Моей первой мыслью было бы заменить выталкивание всех регистров по отдельности, но нет никакой 64-битной pusha инструкции ...


person Sean Kelleher    schedule 07.11.2010    source источник


Ответы (1)


Вот некоторые возможные изменения в распорядке:

_printi:
    pushf
    push    rax
    push    rbx
    push    rcx
    push    rdx

    mov rax, [rsp+48]
    mov rcx, 4
    mov rbx, 10 ; --moved outside the loop
.start:
    dec rcx
    xor rdx, rdx
    div rbx
    add rdx, 48
    mov [var+rcx], dl
    cmp rax, 0
    jne .start

    ; mov rax, [var] -- not used
    ; push    rax -- not used
    call    _printc
    ; pop rax -- not used

    pop rdx
    pop rcx
    pop rbx
    pop rax
    popf
    ret

Также я отметил некоторые ограничения в алгоритме. Если число больше 9999, код продолжит вставлять цифры вне выделенного пространства, перезаписывая некоторые другие данные. Подпрограмму нельзя полностью повторно использовать, т.е. если вы напечатаете 123, то 9 будет равно 129.

person Guffa    schedule 07.11.2010
comment
@eZanmoto: Что касается push и pop, подумайте, что делает код. Вы помещаете значение в rax, которое не используется ни _printc, ни позже в коде. Подпрограмма _printc сама сохраняет rax, поэтому вам не нужно будет нажимать и выталкивать ее, даже если это понадобится позже. Если _printc используется только _printi, вы можете встроить его код, сэкономив вам вызов и набор толчков и всплывающих окон. - person Guffa; 07.11.2010
comment
Извините за мое незнание, но я все еще не понимаю ... Я вставляю данные в переменную var, чтобы их можно было передать как параметр в подпрограмме _printc. Я знаю, что _printc в настоящее время используется только _printi, но я намерен использовать его в библиотеке функций при сборке компилятора и ОС. Но вы правы, что касается моего первоначального вопроса о более эффективном способе вывода целого числа, гораздо лучше просто использовать _printc inline, спасибо за ответ и извините за двусмысленность вопроса. - person Sean Kelleher; 07.11.2010
comment
@eZanmoto: Понятно ... вы получаете первую часть строки, передаете ее в качестве параметра _printc, который возвращает ее на место, перезаписывая символы теми же символами ... Это было настолько бессмысленно, что я не видел этого. :) - person Guffa; 07.11.2010
comment
Да, извините ... Я никогда раньше не программировал на ассемблере, поэтому я не знаю никаких идиом или правильного способа что-либо сделать ... Как будто я проделал много разборки, но на самом деле писать это совсем другое дело. .. Я считаю, что я буду включать его в другие файлы сборки с помощью директивы %include, поэтому вместо того, чтобы запоминать имя переменной, которое я решил, я могу поместить значение или символ, который я хочу вывести в стек и вызвать _printc или _printi, в зависимости от того, какая функциональность мне нужна ... И снова будет легко заменить вызов linux 0x80 вызовом BIOS ... - person Sean Kelleher; 07.11.2010