2

I plan to convert the X variable to decimal. I'm having a hard time using turbo assembler, can you give a hand?

code segment     ;inicio de un segmento unico
assume cs:code,ds:code,ss:code
org 100h       ;localidad de inicio del contador
main  proc     ;procedimiento principal

mov ax,cs
mov ds,ax   ; INICIO 

mov ax, x

mov ah,4ch ;comienzo del fin de programa
int 21h    ;fin del programa

main endp

x dw 0A92FH

code ends   ; fin del segmento de codigo
end main    ;fin del ensamble

Thanks a lot

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Darf
  • 2,495
  • 5
  • 26
  • 37
  • Do you mean to display X as a decimal value? You can always operate on hex values and it should work just fine. – Jesus Ramos Oct 22 '11 at 22:42
  • yes but, I know that I must to have another variable where it's always adding each value multiplied by 16 A92F -> (A * 16 ^ 3) + (9 * 16 ^ 2) + (2 * 16 ^ 1) + (F * 16 ^ 0) – Darf Oct 22 '11 at 22:47
  • Misleading title: this isn't doing ASCII hex -> integer. It's letting the assembler convert a hex constant in the source into a binary integer. – Peter Cordes Jun 01 '21 at 21:17

1 Answers1

8

When converting numbers to a printable format it's often easiest to start with the last digit.

Consider converting 123 to "123", how would we get the last digit? It's the remained when dividing by 10 (the base). So 123 % 10 gives us 3 and 123 / 10 = 12 conveniently gives us the correct number to work with in the next iteration. On x86 the "DIV" instruction is nice enough to give us both the quotient and remainder (in ax and dx respectively). All that remains is to store printable characters in the string.

Putting all this together you end up with something like the following (using nasm syntax):

; ConvertNumber
;   Input:
;     ax = Number to be converted
;     bx = Base
;   
;   Output:
;     si = Start of NUL-terminated buffer
;          containing the converted number
;          in ASCII represention.

ConvertNumber:
    push ax            ; Save modified registers
    push bx
    push dx
    mov si, bufferend  ; Start at the end
.convert:
    xor dx, dx         ; Clear dx for division
    div bx             ; Divide by base
    add dl, '0'        ; Convert to printable char
    cmp dl, '9'        ; Hex digit?
    jbe .store         ; No. Store it
    add dl, 'A'-'0'-10 ; Adjust hex digit
.store:
    dec si             ; Move back one position
    mov [si], dl       ; Store converted digit
    and ax, ax         ; Division result 0?
    jnz .convert       ; No. Still digits to convert
    pop dx             ; Restore modified registers
    pop bx
    pop ax
    ret

This requires a working buffer (16 for the case when base = 2 and an extra byte for the NUL terminator):

buffer: times 16 db 0
bufferend:
    db 0

Adding support for signed numbers is left as an exercise for the reader. Here is roughly the same routine adapted for 64-bit assembly.

Community
  • 1
  • 1
user786653
  • 29,780
  • 4
  • 43
  • 53
  • you should use `test ax,ax`, not `and`. `and` lengthens the critical path latency from `div` output to input in the loop. ([Test whether a register is zero with CMP reg,0 vs OR reg,reg?](https://stackoverflow.com/a/33724806)). At least it can macro-fuse with `jnz`, unlike `or`, on some CPUs. Also related: [How to convert a number to hex?](https://stackoverflow.com/a/53823757) for a table-lookup version specialized for power-of-2 bases, unlike yours which works for any base from 2..36. – Peter Cordes Jan 10 '19 at 06:51