This is actually my second question about this particular problem today, but the other one was answered pretty quickly.
In essence, I am trying to take in a string of letters (no numbers or symbols) and then compare each inputted letter to an array of .asciz values that represent the NATO Military Phonetic Alphabet (Alpha, Bravo, Charlie, etc.) and output the representative Military equivalent to the letter.
This is where I am stuck. I'm fairly new to Assembly and this is a homework assignment, so help is much needed and appreciated. My professor is not great at offering resources to learn this stuff and it's difficult to find good resources for exact problems online.
Any help would be much appreciated. Specifically on how to compare each letter input to the array. I've already successfully stored the input in a variable.
Below is a C# representation of what I am attempting to do.
class MilAlpha
{
static void Main(string[] args)
{
string input;
string[] miliAlpha = { "Alpha", "Beta", "Charlie", "Delta", "Echo", "Foxtrot", "Golf",
"Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November",
"Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform",
"Victor", "Whiskey", "X-Ray", "Yankee", "Zulu" };
Console.WriteLine("Enter a string of text: ");
input = Console.ReadLine();
for (int i = 0; i < input.Length; i++) {
for (int j = 0; j < miliAlpha.Length; j++) {
if (input[i] == ' ')
Console.WriteLine("\n")
string temp = miliAlpha[j].ToLower();
if (input[i] == temp[0])
Console.WriteLine("\n" + miliAlpha[j] + "\n");
}
}
Console.ReadKey();
}
}
EDIT:
So I believe this should do what I am trying to do, but it doesn't seem to work as intended. It compares the correct things in the debugger, but when it goes to print the respective portion of the array, it simply doesn't print anything.
.section .data
MAlpha:
.asciz "Alpha \n"
.equ ElementLen, .-MAlpha
.asciz "Bravo \n"
.asciz "Charlie \n"
.asciz "Delta \n"
.asciz "Echo \n"
.asciz "Foxtrot \n"
.asciz "Golf \n"
.asciz "Hotel \n"
.asciz "India \n"
.asciz "Juliet \n"
.asciz "Kilo \n"
.asciz "Lima \n"
.asciz "Mike \n"
.asciz "November \n"
.asciz "Oscar \n"
.asciz "Papa \n"
.asciz "Quebec \n"
.asciz "Romeo \n"
.asciz "Sierra \n"
.asciz "Tango \n"
.asciz "Uniform \n"
.asciz "Victor \n"
.asciz "Whiskey \n"
.asciz "X-Ray \n"
.asciz "Yankee \n"
.asciz "Zulu \n"
.asciz " \n"
.equ MAlphaLen, .-MAlpha
Input:
.fill 80
.equ InputLen, .-Input
InputMSG:
.ascii "Please enter a word: "
.equ InputMSGLen, .-InputMSG
BlankLine:
.ascii "\n"
.equ BlankLineLen, .-BlankLine
Converting:
.ascii "\nConverting to NATO Alphabet...\n\n"
.equ ConvertingLen, .-Converting
.section .bss
.section .text
.globl _start
GetInput:
movl $4, %eax
movl $1, %ebx
movl $InputMSG, %ecx
movl $InputMSGLen, %edx
int $0x80
movl $3, %eax
movl $0, %ebx
movl $Input, %ecx
movl $InputLen, %edx
int $0x80
ret
PrintInput:
movl $4, %eax
movl $1, %ebx
movl $BlankLine, %ecx
movl $BlankLineLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $Input, %ecx
movl $InputLen, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $Converting, %ecx
movl $ConvertingLen, %edx
int $0x80
ret
Convert:
# Get first letter of input string
# Compare letter to first letter of each array entry
# When match is found, print Array entry to screen
# Repeat until end of input string
movl $Input, %eax
movl $MAlpha, %edi
call Loop
ret
Loop:
movb (%eax), %al
cmp $0x0A, %al
je Finished
call CompareAlpha
jmp Loop
CompareAlpha:
movb (%edi), %bl
cmpb %bl, %al
je PrintWord
addl $ElementLen, %edi
jmp CompareAlpha
PrintWord:
movl $4, %eax
movl $1, %ebx
movl (%edi), %eax
movl $ElementLen, %edx
int $0x80
Finished:
call ExitProg
_start:
call GetInput
call PrintInput
call Convert
call ExitProg
PrintMAlpha:
movl $4, %eax
movl $1, %ebx
movl $MAlpha, %ecx
movl $MAlphaLen, %edx
int $0x80
ExitProg:
movl $1, %eax
movl $0, %ebx
int $0x80
Here's a few bugs to get you started:
In Loop, you are keeping the pointer to the input string in %eax, but you load the character into %al which is the low byte of %eax, thus trashing its value. Pick another register for one of them.
You never increment your pointer in Loop, so it will loop forever (if it doesn't crash first due to one of your other bugs).
CompareAlpha doesn't reset %edi on successive calls. So if the first character is 'H', %edi will be left pointing to "Hotel" after the call. If the next character is E, CompareAlpha will search forward for it starting from "Hotel". Of course it won't find it, so it runs off the end of the array and crashes.
PrintWord loads four bytes of the string into %eax (overwriting the system call number), whereas it should be loading the address of the string into %ecx. Replace movl (%edi), %eax by movl %edi, %ecx (note it is now a register-to-register move and not a load from memory).
PrintWord clobbers registers %eax, %ebx, %ecx, %edx, some of which its caller is expecting to remain unchanged. Either push and pop those registers, or rewrite CompareAlpha to do so before calling it.
PrintWord is missing a ret at the end, so it falls through into ExitProg.
After fixing these, I was able to successfully convert the string "HELLO".
These were all findable by single-stepping the code in the gdb debugger (si command) and watching the contents of registers (display $eax) and what they point to (display/s $edi, etc). I suggest practicing this.
Note that a more efficient design, instead of linear search through the array of codewords, would be to simply index into it. Take your character and subtract the ASCII code of 'A' (0x41), multiply by $ElementLen, and add to $MAlpha. Now you have a pointer to the desired codeword without looping. If you use an auxiliary array of pointers as in your other post, this is even easier as each pointer has length 4, so you can use the SIB addressing mode and do movl MAlpha(,%eax,4), %edi; make sure the high 24 bits of %eax are zeroed. This also avoids the need for padding all the code words with spaces (though then you'll need to write your own strlen to compute the length, or have a separate array of lengths, or write out one byte at a time until you see the 0 at the end).
Also, as a general tip, it would be wise to document each of your subroutines: what exactly does it do, in which registers does it expect its inputs and leave its outputs, and which registers does it clobber? You may want to try to have some commonality between these, perhaps even creating your own standard calling conventions.
Related
I'm trying to write a program that takes a string input of two words, then reverses the order of the words and prints them. Right now I'm struggling with a few problems because the teacher didn't teach us how to write x86 assembly very well.
Currently I'm trying to figure out how to:
Find the start of the array again, as I believe the current program messes with the stack, and printing ♣ at 8(%esp,%ebx) when %ebx = 0
How to store %ebx when the index hits the space, preferably into the register of %ecx, though I do not know how to prevent _printf from messing with %ecx in the current set up
Lastly, whenever I input two words where the first word is smaller than 3 letters it doesn't output the 2nd word.
Right now, this is the output of my program (when the input is "Hello World")
Enter a string:
Hello World (input)
Your string is:
Hello World
Index of Space is = 6
World♣ <--- Supposed to be World Hello, in this case just testing to make sure I can find the 1st element of %eax
The registers I am currently using are %eax for the string array, and %ebx for the index of %eax. I would like to use %ecx, or any other register to store %ebx value when %ebx hits the space character.
Here is my x86 Assembly code so far:
LC0:
.ascii "Enter a string: \0"
LC1:
.ascii "%[^\n]\0"
LC2:
.ascii "Your string is:\12\0"
LC3:
.ascii "\12Index of Space is = %d\12\0"
LC4:
.ascii "%c\0" # print out the character
LC6:
.ascii "Yeehaw!\0" # test message
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $112, %esp
movl $LC0, (%esp)
call _puts # similar to _printf function
leal 8(%esp), %eax
movl %eax, 4(%esp)
movl $LC1, (%esp)
call _scanf # get string
movl $LC2, (%esp)
call _printf # printout the output text of LC2
###### A[i] ###### ebx is the index
movl $0, %ebx #index of array i=0
movl $0, %ecx # Where I want to store space index
jmp .L2
.L1: #TEXT PORTION
movsbl %al, %eax # eax =FFFFFFXX XX means for each character print out the input character
incl %ebx # next index of array
movl %eax, 4(%esp) # print out from the first element of the array
movl $LC4, (%esp)
call _printf
.L2: #TEXT PORTION
movzbl 8(%esp,%ebx), %eax # the value of esp+8+0 strating address of the array to eax
cmpb $0x20, %al # compare eax to see if it is a space
jne .EndOfLine
movl %ebx, 8(%esp) # Save space index
.EndOfLine:
testb %al, %al # if eax is not equal to zero jump to L3, if is zero menas end of the string
jne .L1
movl 8(%esp), %ebx # Take space index back
incl %ebx
#States space index
movl %ebx, 4(%esp) # print out from the first element of the array
movl $LC3, (%esp)
call _printf
jmp .T2
.T1: #TEXT PORTION
movsbl %al, %eax # eax =FFFFFFXX XX means for each character print out the input character
incl %ebx # next index of array
movl %eax, 4(%esp) # print out from the first element of the array
movl $LC4, (%esp)
call _printf
.T2: #TEXT PORTION
movzbl 8(%esp,%ebx), %eax # the value of esp+8+0 strating address of the array to eax
testb %al, %al # if eax is not equal to zero jump to L3, if is zero menas end of the string
jne .T1
.Test:
#Test to find initial array value, currently prints ♣ at 8(%esp,$0)
movl $0, %ebx
movzbl 8(%esp,%ebx), %eax
movsbl %al, %eax
movl %eax, 4(%esp)
movl $LC4, (%esp)
call _printf
.Done:
movl $0, %eax
addl $112, %esp
leave
ret
Any help would be appreciated, and if explain why I need to do certain things since my teacher wasn't the best at explaining topics like the stack and such.
UPDATE: 8(%esp,%ebx) when ebx is 0 properly locates the initial letter, and using $116(%esp) as a local variable to store %ebx.
Hi Guys I got some annoying problem ,so I try to write a code just to reverse small string sequential
I Already got this :
.section .data
string:
.ascii "AAAAAABBBBBB"
length:
.quad . -string #Dot = 'here'
.section .text
.globl _start #Make entry point visible to linker
_start:
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl length,%edi #counter
shrl $1,%edi #half of string
movl $0,%ecx #start from index one
movl length,%edx #start from end
reverse:
movl string(,%ecx,1),%eax
movl string(,%edx,1),%ebx
movl %eax,string(,%edx,1)
movl %ebx,string(,%ecx,1)
inc %ecx
dec %edx
dec %edi
loop reverse #looping
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl $0, %ebx #Make program return syscall exit status
movl $1, %eax #1=exit
int $0x80 #Call System Again
And it's not working correctly , cuz in gbd i get wrong values in registers after making
movl string(,%ecx,1),%eax
or the next steps I think there should be in %eax value for A letter but its doesn't any ideas ?
Working at 64arch but emulating in as --32 so its problem with my addressing i guess
You should be processing bytes not longs, so use movb with 8 bit registers (al and bl, for example). Also, the LOOP instruction uses ECX automatically, you probably meant JNZ there to repeat until EDI reaches zero.
So guys I got problem . I'm trying just to change one letter in this easy string "Hello World"
so I'm trying assign value to memory cell like this
.section .text
string:
.ascii "Hello, world!"
length:
.quad . -string #Dot = 'here'
.globl _start #Make entry point visible to linker
_start:
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl $98,4(%ecx) #Critical moment why cannot I do this?
movl $98,string(,%ebx,1) #cannot do this too ;(
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80
movl $0, %ebx #Make program return syscall exit status
movl $1, %eax #1=exit
int $0x80 #Call System Again
and additional info I'm working at x64(linux) and assembling it in emulation mode x32 by linux gas so should be all right , TY for replies
You have put your string into the .text section, which is read-only. You should put it into the .data section (but don't forget to switch back to .text for the program code).
I wrote this to print argv[0] in x86:
.section .data
newline: .int 0xa, 0
.section .text
.globl _start
_start:
sub %al, %al
movl 4(%esp), %edi /* Pointer to argv[0]. */
sub %ecx, %ecx /* Set %ecx to 0.*/
not %ecx /* Set %ecx to -1.*/
repne scasb /* Search for %al over and over.*/
not %ecx /* Set %ecx to |%ecx| - 1.*/
dec %ecx
movl %ecx, %edx /* Move the strlen of argv[0] into %edx.*/
movl $4, %eax
movl $1, %ebx
movl 4(%esp), %ecx
int $0x80
movl $newline, %ecx
movl $1, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
When I run this file ("print"), the output is this:
[08:27 assembly]$ ./print test
./print[08:30 assembly]$
When I ran this through gdb, the actual string length held in edx is 27, and the string it's checking is "/home/robert/assembly/print", not "./print". So I changed the %esp offsets to 8, to check argv[1]. With the same command as before, the output is this:
test
[08:33 assembly]$
Why does checking argv[0] cause the strange output, when argv[1] does as expected?
I think gdb is "helping" you by adding the full path to argv[0]. After printing, %eax holds the number of characters printed, so you'll want to reload %eax for sys_write again to print the $newline (%ebx should still be okay) - by luck, "test" is the right length. Lord knows what system call you're getting with that longer string!
I'd say you're doing good! (might be a good idea to check argc to make sure argv[1] is there before you try to print it).
I wanna create a substring (ministring) of 3 asciz chars out of my original (thestring). The thing ain't printing when being run so I don't know what the hell I'm I doing. Why it ain't printing? Am I creating the ministring correctly?
.section .data
thestring: .asciz "111010101"
ministring: .asciz ""
formatd: .asciz "%d"
formats: .asciz "%s"
formatc: .asciz "%c"
.section .text
.globl _start
_start:
xorl %ecx, %ecx
ciclo:movb thestring(%ecx,1), %al
movzbl %al, %eax
movl %eax, ministring(%ecx,1)
incl %ecx
cmpl $3, %ecx
jl ciclo
movl thestring, %eax
pushl %eax
pushl $formats
call printf
addl $4, %esp
movl $1, %eax
movl $0, %ebx
int $0x80
You haven't reserved enough memory space to contain the null-terminated ministring which you're creating ... therefore, when you write to this memory, you're overwriting the value of formatd and formats (and so you're eventually passing something other than "%s" to printf).
Instead of your definition of the ministring memory location, try using the following :
ministring: .asciz " "
Also, instead of this:
movl %eax, ministring(%ecx,1)
I don't understand why you aren't using this instead:
movb %al, ministring(%ecx,1)
Also, if you want to print the ministring, then instead of this:
movl thestring, %eax
Do this:
movl ministring, %eax
Also instead of this:
addl $4, %esp
Why not this:
addl $8, %esp
ALso I suggest that you use a debugger to:
Step through the code
Watch the values contained in registers and in memory as you step through
Know the location of any segmentation fault