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?
: 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.
: : 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
: [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) ;
; ; 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]
Comments
: 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.
: 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
Best Regards,
Richard
The way I see it... Well, it's all pretty blurry
: : 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
: 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.
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
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]
: 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???
: 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
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]