# Converting binary into ASCII

Does anyone have any good snippets or thoughts on converting a binary value into ASCII code? I have had some difficulty developing, google help only leads to calculators but I want to know the process. Any help is appreciated.

• : Does anyone have any good snippets or thoughts on converting a
: binary value into ASCII code? I have had some difficulty
: developing, google help only leads to calculators but I want to know
: the process. Any help is appreciated.

Binary values? Are we talking a bit-string here? Or just print the value of EAX as a number?

Best Regards,
Richard

The way I see it... Well, it's all pretty blurry

• : Binary values? Are we talking a bit-string here? Or just print the
: value of EAX as a number?
:
: Best Regards,
: Richard
:
: The way I see it... Well, it's all pretty blurry

Binary values meaning, if I had any binary number in AX I want to convert that into its associated ASCII characters on the display. Actually getting the data to display is not my issue, but doing the conversion is where I need help. So for example if I had AX = 1010010111000011b = A5C3h and I would place the value of 41354333h at a known address. Does this make sense? I am teaching myself assembly so if I need to clarify some missing data let me know, thanks.
• : Binary values meaning, if I had any binary number in AX I want to
: convert that into its associated ASCII characters on the display.
:

I know of a nice algorithm for that

: Actually getting the data to display is not my issue, but doing the
: conversion is where I need help. So for example if I had AX =
: 1010010111000011b = A5C3h and I would place the value of 41354333h
: at a known address. Does this make sense?
:
To be honest, no... But if I am correct in assuming you need the conversion algorithm from AX->number string, then I don't need to understand.

I could give you the code, but something tells me you'd rather do that yourself (I wanted to do it by myself).
The general idea is to [italic]first[/italic] create the String in memory, and after that print it

The logic of creating the string in memory:
[code]
AX contains the value
Divide AX by the System Base (10 for decimal, 16 for hex, 2 for binary, ..)
Take the remainder after division and add it to '0' (character 0)
Store this value at the correct position in a string
Do this loop again, only then with AX being the quotient of the divison above, until AX=0
[/code]
So, in a more code-like format:
[code]
AX = number
char = '0' + AX % System_Base ;% is modulus: remainder after division
Store char
AX = AX / System_Base ;Integer divide: drop the decimals, without rounding
repeat process while AX != 0
You might have to store a 0 char aswell (0 termination for string)
[/code]
Note that the line "char = '0' + AX % System_Base" only works for System_Base <= 10. This is because 0-9 are nicely after eachother in the ASCII table. However, for hexadecimal you'd want it to continue with A after 9, and this won't happen. The easy workaround is to create a 'character map', like:
[code]
char charMap[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char PrintChar = charMap[AX % System_Base];
[/code]
(C-style code)
I however advise you try a simple conversion function for Base=10 first, which avoids the use of this character map because of the ASCII table orderning of characters '0' to '9'

Now, I guess the 'Store char' part is a bit fuzzy. Let me give you a hint:
If you have a piece of memory to store the string (consider AX=125):
[code]
|------------------------| 17* bytes of memory

So what you'd do is store 0 character:
|0---------------------16|
|
0

And then start putting the characters from the loop in reverse order
(right to left):
|0------------------15-16|
| |
'5' 0

|0---------------14-15-16|
| | |
'2''5' 0

|0------------13-14-15-16|
| | | |
'1''2''5' 0

* 17, because longest number from AX is with base 2: 1111 1111 1111 1111
= 16 characters long, and then a 0 termination = 17
[/code]
Then to print the number, use the address of byte 13 as a pointer to a zero-terminated string.

Say 0x300 is the address (an offset, relative to the segment base) of the start of the data area reserved for outputting your string.
Then 0x300+13 is the start of the number string.

Hmm... I know, I know. It is really fuzzy Sorry. Guess it would be a lot easier to explain if I just gave you the code - but I want to give you the chance to write it yourself. You can just ask for the code if it is too fuzzy and then I'll post it for you.

Best Regards,
Richard

The way I see it... Well, it's all pretty blurry
• : : Binary values meaning, if I had any binary number in AX I want to
: : convert that into its associated ASCII characters on the display.
: :
:
: I know of a nice algorithm for that
:
: : Actually getting the data to display is not my issue, but doing the
: : conversion is where I need help. So for example if I had AX =
: : 1010010111000011b = A5C3h and I would place the value of 41354333h
: : at a known address. Does this make sense?
: :
: To be honest, no... But if I am correct in assuming you need the
: conversion algorithm from AX->number string, then I don't need to
: understand.
:
: I could give you the code, but something tells me you'd rather do
: that yourself (I wanted to do it by myself).
: The general idea is to [italic]first[/italic] create the String in
: memory, and after that print it
:
: The logic of creating the string in memory:
: [code]:
: AX contains the value
: Divide AX by the System Base (10 for decimal, 16 for hex, 2 for binary, ..)
: Take the remainder after division and add it to '0' (character 0)
: Store this value at the correct position in a string
: Do this loop again, only then with AX being the quotient of the divison above, until AX=0
: [/code]:
: So, in a more code-like format:
: [code]:
: AX = number
: char = '0' + AX % System_Base ;% is modulus: remainder after division
: Store char
: AX = AX / System_Base ;Integer divide: drop the decimals, without rounding
: repeat process while AX != 0
: You might have to store a 0 char aswell (0 termination for string)
: [/code]:
: Note that the line "char = '0' + AX % System_Base" only works for
: System_Base <= 10. This is because 0-9 are nicely after eachother in
: the ASCII table. However, for hexadecimal you'd want it to continue
: with A after 9, and this won't happen. The easy workaround is to
: create a 'character map', like:
: [code]:
: char charMap[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
: char PrintChar = charMap[AX % System_Base];
: [/code]:
: (C-style code)
: I however advise you try a simple conversion function for Base=10
: first, which avoids the use of this character map because of the
: ASCII table orderning of characters '0' to '9'
:
: Now, I guess the 'Store char' part is a bit fuzzy. Let me give you a
: hint:
: If you have a piece of memory to store the string (consider AX=125):
: [code]:
: |------------------------| 17* bytes of memory
:
: So what you'd do is store 0 character:
: |0---------------------16|
: |
: 0
:
: And then start putting the characters from the loop in reverse order
: (right to left):
: |0------------------15-16|
: | |
: '5' 0
:
: |0---------------14-15-16|
: | | |
: '2''5' 0
:
: |0------------13-14-15-16|
: | | | |
: '1''2''5' 0
:
: * 17, because longest number from AX is with base 2: 1111 1111 1111 1111
: = 16 characters long, and then a 0 termination = 17
: [/code]:
: Then to print the number, use the address of byte 13 as a pointer to
: a zero-terminated string.
:
: Say 0x300 is the address (an offset, relative to the segment base)
: of the start of the data area reserved for outputting your string.
: Then 0x300+13 is the start of the number string.
:
: Hmm... I know, I know. It is really fuzzy Sorry. Guess it would
: be a lot easier to explain if I just gave you the code - but I want
: to give you the chance to write it yourself. You can just ask for
: the code if it is too fuzzy and then I'll post it for you.
:
: Best Regards,
: Richard
:
: The way I see it... Well, it's all pretty blurry

Thank you for the detailed post, I understand what you have explained with the string in memory - but have lost me in an assembly limbo with the character mapping, could you explain further. I don't usually like to ask for source code, but I think that I would benefit from reviewing the actual working logic in this case. Thanks.
• [code]
charMap db "01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"

[color=green];This code assumes ds == es == ss[/color]
PrintNumber: [color=green];;Takes AX as number[/color]
push bp
mov bp, sp
mov di, sp
dec di [color=Green];Set di to last character of the buffer[/color]
sub sp, 17 [color=green];Reserve stack space for printnumber[/color]

[color=green];Store 0 termination[/color]
mov [di], byte 0
dec di

[color=green];Set direction flag - sto/lod/mov functions will decrease si/di as[/color]
[color=green]; they go[/color]
std

mov bx, 10 [color=green];Change this to the system base you want[/color]
[color=green];With the charMap, up to base 36 is supported[/color]
.Loop:
xor dx, dx
div bx [color=green];divide dx:ax=0:ax by system (default 10)[/color]
xchg ax, dx
[color=green];ax contains remainder, dx qoutient (eXCHanGed)[/color]
mov al, [charMap + ax] [color=green];Get character to store from character map[/color]
stosb [color=green];Store remainder to ES:DI[/color]
xchg ax, dx [color=green];[/color]
or ax, ax [color=green];} While quotient != 0[/color]
jnz .Loop [color=green]; [/color]

inc di [color=Green];Last character was written to di+1[/color]

[color=green];Now it's time to print. DI points to the string[/color]
[color=green];So... I don't know what print routine you are using?[/color]
[color=green];1) puts(char* string)[/color]
[color=green];;push di[/color]
[color=green];;call _puts ;Note the C calling convention: prepended _[/color]
[color=green];;pop di[/color]
[color=green];[/color]
[color=green];2) Custom print function using ds:si[/color]
[color=green];;mov si, di[/color]
[color=green];;call customPrint[/color]

[color=green];Clean up[/color]
cld [color=green];Reset direction flag
;TODO: A safe way to do guarantee it's unchanged
; would be to push/pop the FLAGS register[/color]
mov sp, bp
pop bp
ret
[/code]
I hope I didn't make a mistake [color=green];)[/color]
Best Regards,
Richard

The way I see it... Well, it's all pretty blurry
• [color=Blue]
This seems a bit :-) long.

This code will convert the AL as a binary (high 4 bits muts be zeroes) into a HEX character:
[code]
CMP AL, 10
SBB AL, 69h
DAS
[/code]
[/color]
• : [color=Blue]
: This seems a bit :-) long.
:
: This code will convert the AL as a binary (high 4 bits muts be
: zeroes) into a HEX character:
: [code]:
: CMP AL, 10
: SBB AL, 69h
: DAS
: [/code]:
: [/color]
Nice, I like that! Now what's the trick for the high 4 bits???
• : [color=Blue]
: This seems a bit :-) long.
:
: This code will convert the AL as a binary (high 4 bits muts be
: zeroes) into a HEX character:
: [code]:
: CMP AL, 10
: SBB AL, 69h
: DAS
: [/code]:
: [/color]

I never understood how to use the ASCII/Decimal adjust instructions.
Still don't tbh.

My code is a bit longer - but the working part isn't that much longer at all. Much of it is setting up a stack frame. The actual calculating part is 8 instructions that loop. But even with your shorter version of the code, you still need to create a loop somewhere (I'm not sure how, because - as I said - I still don't understand your code :P).

And an added benefit is that my code works with radix 2 to 36.

But, could you please explain how the ASCII/Decimal adjustments work? I've looked on Wikipedia and the Intel
• [color=Blue]
Maybe no need for loops, just use macros, so code will be readable.
[/color]
[code]
;
; This macro assumes that AL has high 4 bits set to zero
; and DI points to an ASCII buffer to dump the output
; Direction flag DF=1 (forward)
;

macro AL_DIGIT_TO_HEX
{
CMP AL, 10
SBB AL, 69h
DAS
STOSB
}

;
; This next macro assumes that AL is a byte to dump as ASCII
; and DI points to an ASCII buffer to dump the output
; Direction flag DF=1 (forward)
;

macro AL_TO_HEX
{
PUSH AX
SHR AL, 4
AL_DIGIT_TO_HEX

POP AX
AND AL, 0Fh
AL_DIGIT_TO_HEX
}

;
; This next macro assumes that AX is a word to dump as ASCII
; and DI points to an ASCII buffer to dump the output
; Direction flag DF=1 (forward)
;

macro AX_TO_HEX
{
PUSH AX
SHR AX, 8
AL_TO_HEX

POP AX
AL_TO_HEX
}

;
; Now all you have to do to convert to HEX is just
; load registers and call the macro
;

buffer db 'xxxx\$'

...

MOV AX, 0F59Bh
MOV DI, buffer
CLD
AX_TO_HEX

MOV DX, buffer ; Print it to console in DOS mode
MOV AH, 9h
INT 21h
[/code]
[color=Blue]
However, it is good only for hexadecimal dumping (fastest possible code). To do binary output - loops will be needed (unless the loop completely unrolled)
[/color]