I am new to MASM coding. I found that it is really difficult to handle with registers, due to lack of knowledge in built-in functions.
I am trying to write a program to change all letters in input string to CAPITAL letters. Here is my code:
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
include msvcrt.inc
includelib msvcrt.lib
.data
InputMsg db "String Input: (At most 20 characters)", 10, 0
OutputMsg db "Your string is ", 0
StringFormat db "%s", 0
.data?
StringData db 20 dup(?)
.code
start:
invoke crt_printf, addr InputMsg
invoke crt_scanf, addr StringFormat, addr StringData, 20
;Change lowercase letter to uppercase
lea ecx, StringData
CounterLoop:
.if [ecx] >= 97
.if [ecx] <= 122
sub [ecx], 32
.endif
.endif
inc ecx
.if [ecx] != 0
jmp CounterLoop
.endif
invoke crt_printf, addr OutputMsg
invoke crt_printf, addr StringData
invoke ExitProcess, NULL
end start
I want to use ecx to store the effective address of StringData. However, A2070 error occured when I want to get the content of StringData.
Is [ecx] incorrect? How can I get the character in StringData using direct addressing? Thank you very much!
Related
Let's say we work on architecture x86_64 and let's say we have the following string, "123456". In ASCII characters, it becomes 31 32 33 34 35 36 00.
Now, which assembly instructions should I use to move the entire (even if fragmented) content of this string somewhere in a way that %rdi stores the address of that string (points to that)?
Because I am not simply able to move the hex representation of the string into a register, like one can do with unsigned values, how do I do it?
There are a couple of ways to do so.
If you want to move the entire string to another offset first, you would have to do so with a loop.
mov rbx, 0
loop:
mov al, [string+rbx]
mov [copyoffset+rbx], al
inc rbx
cmp al, 0x0
jne loop
... Insert other code here
Then you can use the Lea instruction described below to move it into rdi.
If you just want to load the address of the string and don't care about moving it you can just use lea
lea rdi, [stringoffset]
Edit: Changed rax to al so we only move one byte at a time
Using NASM, I need to change a character in a string at a given index and print the string in its new form. Here is a simplified version of my code:
;test_code.asm
global main
extern printf
output_str: db "----------"
index: dq 7
main:
push rbp
mov rdi, output_str
mov rax, index
mov byte[rdi + rax], 'x'
xor rax, rax
call printf
pop rbp
ret
I then compile using:
nasm -felf64 test_code.asm && gcc test_code.o -lm
and get a seg fault. Would someone please point out the flaw here? I can't seem to find it myself.
your string is in the .text section of the executable, which is read only by default. Either you allocate a buffer on the stack, copy the string and you modify it there, or you put the string in the .data section (which is read/write) using the section directive. In this last case, notice that the character replacement will be persistent, i.e. even later in the program the string will remain modified;
if you want to print that string with printf it has to be NUL-terminated. Add a ,0 to the end of the db line;
that mov rax, index is wrong - index is the address of the qword you wrote above, while you actually want to copy in rax the datum wrote there; you probably want mov rax, [index].
So, something like
;test_code.asm
global main
extern printf
section .data
output_str:
db "----------",0
section .text
index:
dq 7
main:
push rbp
mov rdi, output_str
mov rax, [index]
mov byte[rdi + rax], 'x'
xor rax, rax
call printf
pop rbp
ret
I'm writing a while loop in assembly to compile in the Linux terminal with nasm and gcc. The program compares x and y until y >= x and reports number of loops at the end. Here's the code:
segment .data
out1 db "It took ", 10, 0
out2 db "iterations to complete loop. That seems like a lot.", 10, 0
x db 10
y db 2
count db 0
segment .bss
segment .text
global main
extern printf
main:
mov eax, x
mov ebx, y
mov ecx, count
jmp lp ;jump to loop lp
lp:
cmp ebx, eax ;compare x and y
jge end ;jump to end if y >= x
inc eax ;add 1 to x
inc ebx ;add 2 to y
inc ebx
inc ecx ;add 1 to count
jp lp ;repeat loop
end:
push out1 ;print message part 1
call printf
push count ;print count
call printf
push out2 ;print message part 2
call printf
;mov edx, out1 ;
;call print_string ;
;
;mov edx, ecx ;these were other attempts to print
;call print_int ;using an included file
;
;mov edx, out2 ;
;call print_string ;
This is compiled and run in the terminal with:
nasm -f elf test.asm
gcc -o test test.o
./test
Terminal output comes out as:
It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)
I can't see anything wrong with the logic. I think it's syntactical but we've only just started learning assembly and I've tried all sorts of different syntax like brackets around variables and using ret at the end of a segment, but nothing seems to work. I've also searched for segmentation faults but I haven't found anything really helpful. Any help would be appreciated because I'm an absolute beginner.
The reason it crashes is probably that your main function doesn't have a ret instruction. Also be sure to set eax to 0 to signal success:
xor eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret
Additionally, global variables designate pointers, not values. mov eax, x sets eax to the address of x. You need to write back to it if you want anything to happen (or not use global variables).
Finally, you're calling printf with a single non-string argument:
push count ;print count
call printf
The first argument needs to be a format string, like "%i". Here, count is a pointer to a null byte, so you get nothing instead. Off my head, you should try this:
out3 db "%i ", 0
; snip
push ecx
push out3
call printf
I think your problem might just be that you are referencing the addresses of your constants and not their intrinsic value. One must think of a label in nasm as a pointer rather than a value. To access it you just need to use [label]:
segment .data
x dw 42
segment .text
global main
extern printf
main:
mov eax, x
push eax
call printf ; will print address of x (like doing cout<<&x in C++)
mov eax, [x]
push eax
call printf ; will print 42
sub esp, 8
xor eax, eax
ret
PS:I don't think anyone has mentioned it but volatile registers are modified very often when calling external code (C or C++ or other) since at compilation those functions you use are "translated" to assembly and then linked with your asm file. The PC is not a human so it is not distinguishing between what was written in high-level or low-level, the processor is just reading opcodes and operands stored in registers and memory, hence why an external function when using low-level language (call printf) is going to modify (or not! always depends on compiler and architecture) registers that you are also using.
To solve this there are various solutions:
You check what registers are not being modified by using gcc your_c_file.c -S and then in the file your_c_file.swill be the pre-prepared assembly code your compiler has produced from your C file. (It tends to be quite hard to figure out what is what and if you are going to use this method check out Name Mangling, to see how func names will be changed.)
Push all the registers you want to save to stack, and then after the call pop them back to their registers keeping in mind LIFO method.
Use the instructions PUSHA and POPAwhich push or pop all registers respectively.
This is the NASM manual chapter 3 which explains the basis of the language to use: http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html
Hope you managed to solve it.
I'm currently presented with implementing a program that takes an input of a grade value (ex. 75) and then outputs a letter grade corresponding to it. I've implemented the following requested scale via an array / table:
.data
table BYTE 89d, 'A'
BYTE 79d, 'B'
BYTE 69d, 'C'
BYTE 59d, 'D'
BYTE 0d, 'F'
NumCols = 2
NumRows = 5
user_ip BYTE ?
message1 BYTE "Enter a grade value: ", 0h
message2 BYTE "The Grade of ", 0h
message3 BYTE " Yields A Letter Grade of ", 0h
I'm using the following code to sort through this array / table and output the letter grade.
mov edx, OFFSET message1
call WriteString
call readDec
mov user_ip, al
mov esi, OFFSET user_ip
mov edi, OFFSET table
mov ecx, NumRows
L1:
CMPSB
jae L2
add edi, NumCols
Loop L1
L2:
mov edx, OFFSET message2
call WriteString
mov al, user_ip
call WriteDec
mov edx, OFFSET message3
call WriteString
mov edx, edi
call WriteString
call Crlf
With an input of 75, I'm being presented with: "The Grade of 75 Yields A Letter Grade of EC;D". The Program also temporarily stops working.
I'm confident it has something to do with pointers and data sizes. My ideal goal is to store the value of the letter grade in a variable, but I can't seem to find a way to do it given the data size needed to use pointers. Any ideas of how to do this?
You are calling WriteString but the values in your table are characters not strings. The difference in this case is that they are not zero terminated. Either use WriteChar if you have that, or put a zero in your table but then don't forget to adjust NumCols too.
Also note that CMPSB increments both pointers which means your comparisons will be wrong. You should probably just use the non-string CMP especially since the user_ip is already in register AL.
PS: Finally somebody who uses a table :)
I am using MASM assembly and I am trying to write a loop that processes the string str1 byte-by-byte, changing each lowercase letter into the corresponding capital letter using bit operations. If the letter is already capital, leave it alone. Nothing seems to happen to my string str1 when I execute my code and I'm having difficulty figuring out why, maybe I shouldn't be processing my array as such, but nonetheless, here's the code:
.386
.MODEL FLAT
str1 dword "aBcD", cr, Lf, 0
....
.code
_start:
output str1
**sub esi, esi ; sum = 0
lea ebx, str1
top: mov al, [ebx + esi] ; attempting to move each character value from
str1 into the register al for comparison and
possible conversion to uppercase
add esi, 5
cmp al, 0
je zero
sub al, 20h** ; convert lowercase to corresponding uppercase
loop top
zero: output zeromsg ; for TESTING of al purposes only
done: output str1value
output str1
Nothing changes , and on top of the conversion not taking place, the string it printing in reverse order. why? prints out as: "DcBa". Any inquiry would be appreciated! Thanks in advance.
You must load the character, process it, and store it back. You don't store it.
Something like:
mov [esi+ebx], al
is missing.
Why do you sub 0x20 from the char? And why do you add 5 to esi?
Update
Before you start coding, you should think about what the required steps are.
Load the character.
If the character is 0 the string is done.
If the character is uppercase, convert it
Store the character
Adavance to the next character and back to 1
That's it. Now when you look at your code example, you can easily see what is missing and where you go wrong.
May help you a bit
.writeLoop2
mov eax,[ebx] ;mov eax start of data block [ebx]
cmp al,&61 ;61hex is "a"
jb dontsub20 ;if its less don't bother because it's a CAPITAL letter
sub al,&20 ;else take off 20 hex
.dontsub20
call "osasci" ;print to screen command. Output the character in eax
inc ebx ;move ebx forward to next character
inc ecx ;ecx is the rolling count
cmp ecx,edx ;when ecx=edx we are at the end of the data block
jb writeLoop2 ;otherwise loop, there are more characters to print