如何在不使用printf的情况下显示大于15(0Fh)的十六进制数?

时间:2016-01-03 12:26:58

标签: assembly x86 nasm

所以,这是一个恼人的问题: [最后一行包含问题,剩下的内容是更好地理解要求的背景。]

在汇编中,我们可以轻松使用printf函数来显示内容..它会自动将二进制数据正确转换为字符。

现在,在我的作业中,我需要使用十六进制数字并准确显示10个这样的数字之和。 我已经使用代码完成了分类并将ascii转换为十六进制数字(存储在半字节/ 4位中):

声明:

1)esi寄存器包含必须保存号码的存储位置。

2)temp是一个5字节的缓冲区,假设最多可以输入4个字符。

in:             ; loop to take one multidigit input

mov ebx, 1      ;set error flag
scan temp, 5    ;take input
mov ecx, [temp]
mov edi, 4      ;assumes 4 digit input, works for 0 to 4 digit input

test:           ;a validation check for input!!
cmp cl, 0Dh     ;if current byte contains '\r'
je hop          ;skip it

test0:
cmp cl, 030H    ;cl >= '0'
jge test9       ;if yes, check if cl is digit
xor ebx, ebx    ;otherwise, set error signal
jmp cont

test9:
cmp cl, 039h    ;cl <= '9'
jle cont        ;if yes, cl is a digit, case closed
jmp test10c     ;otherwise, check if cl has hex character

test10c:        ;for caps 'A'
cmp cl, 041h    ;cl >= 10 (in caps char hex)
jge test15c     ;if yes, check if cl is a caps hex character
xor ebx, ebx    ;otherwise, set error signal
jmp cont

test15c:
cmp cl, 046h    ;cl <= 15 (in hex)
jle conv        ;if yes, cl is a caps hex character, case closed
jmp test10s     ;otherwise check if cl has small hex character

test10s:
cmp cl, 061h    ;cl >= 10 (in small char hex)
jge test15s     ;if yes, check if cl is a small hex character
xor ebx, ebx    ;otherwise, set error signal
jmp cont

test15s:
cmp cl, 066h    ;cl <= 15 (in small char hex)
jle cont        ;if yes, cl is a small hex character, case closed
xor ebx, ebx    ;otherwise, set error signal

cont:
cmp ebx, 1      ;check for error
jne err         ;if error, jump to err (abort loop)
jmp hop         ;otherwise continue to next ascii character entered

hop:
rol ecx, 8      ;roll next character into cl
dec edi         ;there are only 4 roll-in operations until you repeat the number
jnz test        ;loop back to testing

end:
mov dword[temp], ecx    ;store number between call
call conv               ;convert number
mov ecx, [temp]         ;take converted number
mov dword[esi], ecx     ;store it in array
mov dword[temp], 0      ;store success signal
jmp quit

err:
mov dword[temp], 0FFh   ;store error signal

quit:                   ;return to caller
ret

conv:           ; loop to convert one multidigit input to specific format 
mov ecx, dword[temp]    ;take number
mov edi, 4              ;set number of rotations

fix:
cmp cl, 0Dh     ;check with '\r'
mov cl, 0       ;'\r' will be stored as 0, the numbers 0-15 will be stored as 1-16
jmp skip
cmp cl, 046h    ;check with 'F'
jle digitize    ;if less, convert caps character to digit
sub cl, 020h    ;if more, convert small character to caps
digitize:
cmp cl, 039h    ;check with '9'
jle norm        ;if less, convert ascii digit to normal digit
sub cl, 07h     ;if more, convert character to ascii digit
norm:
sub cl, 030h    ;convert ascii digit to hex digit
add cl, 01h     ;encode 0-15 as 1-16 to accomodate '\r'

skip:
rol ecx, 8      ;roll next character into cl
dec edi         ;there are only 4 roll-in operations until you repeat the number
jnz fix

ret

这里以特定方式编码了数字。您可以建议另外增加加法算法的简单性。

最后,引用问题:

“如何使用sys_write(函数编号4)将这些编码数字的总和正确写入stdout(文件描述符1)?”

1 个答案:

答案 0 :(得分:0)

虽然您似乎是在Linux下进行编码,但我选择提供十六进制格式化32位数字打印的例程,我只是在dos-box环境中将它们组合在一起。您需要使用与int 0x10, func E不同的机制来打印输出的每个字符。您还需要更改堆栈帧的内容,以允许推入堆栈的vars默认为4个字节,而不是2.即bp + 4(bp + 2 * 2)变为bp + 8( bp + 2 * 4)和ret 4(ret 2 * 2)变为ret 8(ret 2 * 4)

您也可能选择忘记手动担心附加溢出和进位标记 - 处理器不需要手持它的东西如此微不足道。

xor ebx, ebx  ; total = 0
mov eax, 300
add ebx, eax  ; total = total + 300
mov eax, 200
add ebx, eax  ; total = total + 200
; etc, etc

这种打印功能的流程图非常短。我建议你自己(手动)流程图代码,以帮助澄清你的算法。

首先,代码:

[section .text]
[bits 16]

EntryPoint:
    push    long 0xDEADBEEF
    call    printHexLong

    ; print CRLF
    mov     ah, 0xe
    mov     al, 10
    int     0x10
    mov     al, 13
    int     0x10

    push    long 3735928559
    call    printHexLong

    ret

;void printHexLong(ulong32 num) 
printHexLong
    push    bp
    mov     bp, sp

    mov     ebx, [bp+4]         ; get the input var
    mov     cx, 8               ; number of nibbles to do
.printLoop:
    rol     ebx, 4              ; 0x11223344 becomes 0x12233441
    mov     al, bl              ; al = 0x41
    and     al, 0xF             ; al = 0x01

    cmp     al, 10              ; will this nibble be 0..9 or will it be A..F ?
    jb      .oneToNine
    sub     al, 10              ; nibble is > 9, so it's A..F  - subtract 10 so it's 0..9, 
    add     al, 'A'             ; then add ascii value of 'A'
    jmp     .outputNibble

.oneToNine:
    add     al, '0'             ; nibble is < 10, so it's a digit 0..9

.outputNibble:
    mov     ah, 0xe             ; video bios int - output a single character in teletype mode
    int     0x10                ; output the nibble

    dec     cx                  ; decrement nibble counter
    jnz     .printLoop          ; any left? If so, go again.

    pop     bp                  ; restore original value of bp
    ret     4                   ;remove the input var from the stack

现在输出:

DEADBEEF
DEADBEEF