I m having a problem with my loop, the code contained in it is long and it gives me error "short jump out of range", so i want to know if there is a way to make the loop work by not reducing the amount of code in it ?
example1:
label:
my code
LOOP label
; It work fine but when I add more code in it
example2:
label:
my code
more code added
LOOP label
; It does not work and error appears "short jump out of range"
The LOOP instruction can't jump to a distance of more than 127 bytes. You'll need to change your code to use DEC ECX with JNZ instructions.
For example:
MOV ECX, 10
label:
;some codes
LOOP label
Become:
MOV ECX, 10
label:
;some codes
DEC ECX
JNZ label
Have you tried with near instruction?
Related
I am new to Assembly and is wondering how I could write a for loop that will iterate 15 times. In the for loop i need to have an if condition that tests wether or not an integer k is greater than 7 as well. How would this be written in code?
Usually this would be written as a comment, but it is a little bit too complex. The code in the other answer isn't as efficient as it could be:
MOV ECX, 15 ; number of iterations
.loop:
...
CMP EAX, 8 ; compare k to 7
DEC ECX ; partial-flag merge needed after this, slow on some CPUs
JA .loop ; check for loop exit condition
B: ; code after the loop
...
;; check CF here to see if k >= 8 or not, i.e. k > 7
EDIT: To have k in memory as 32-bit integer as before, the CMP instruction looks as the following:
CMP DWORD [k], 8
EDIT2: Save one conditional jump. The OP didn't mention to leave or to stay when k is greater than 7. The above code leaves the loop when it ran 15 times or k isn't greater than 7.
Note that this trick with combining the comparisons is only likely to be good on CPUs like Intel Sandybridge-family or Silvermont which don't have partial-flag stalls for reading CF after a dec. On Core2 / Nehalem, this will stall for ~7 cycles to merge flags, like you'd get in an adc BigInteger loop using dec. See Agner Fog's microarch PDF.
Unless you're sure it's good on all the CPUs your code will run on, use cmp / ja or jae separately from dec / jnz.
EDIT3: The OP asks for incrementing/decrementing an integer (here edx) when eax is greater/smaller than 7:
.loop:
...
CMP EAX, 7
DEC EDX
JB .end
INC EDX
JE .end
INC EDX
.end:
DEC ECX
JZ .loop
(probably there'll be someone optimizing this further)
I suggest learning how to use compiled code to teach you such things. Write the code you want in C, then compile it with flags to output the assembly. For example, in gcc this is done with the -S flag. You can then read the machine-generated assembly code to see how it was accomplished.
The general strategy for a for-loop could be something like this:
MOV ECX, 0 ; clear ECX
A:
CMP [k], 7 ; compare k to 7
JGT B ; jump to B if k is greater than 7
CMP ECX, 15 ; check for loop exit condition
JE C ; jump to C if we have iterated 15 times
INC ECX
JMP A
B: ; this happens if K is greater than 7
...
C: ; this happens if we reach the end of the loop
I ran into a problem in my program. Basically what i want to do check if there is a space after a dot in a string and if there isn't i add a space right after the dot. However i don't get how to go about this since my buffer is limited size, therefore if i add the space, the last letter of the buffer will be erased? or am i doing this wrong? thank you for the help in advance :)
For example: Hello.Hi = Hello. Hi
MOV cx, ax
MOV si, offset readBuf
MOV di, offset writeBuf
work:
MOV dl, [si]
CMP dl, '.'
JE dot
increase:
MOV [di], dl
INC si
INC di
LOOP work
dot:
CMP dl+1, ' '
JNE noSpace
JMP increase
noSpace:
There are a few problems with the code. The first one is this line:
CMP dl+1, ' '
This is adding 1 to the value in dl and comparing that to a space character which is not what you want. What you want is to compare the next character, so you'll have to load it into a register with MOV dl, [si] or similar.
The second problem is algorithm. It's often easiest to start with psuedo-code and then create the assembly language version from that. For example:
load a character
is there room left?
if not, exit
if so, save the char
does char == period?
if not, go to 1
is there room left?
if not, exit
if so, save space char
load a character
does char == space?
if so, go to 1
if not, go to 2
Note that "load a character" means both fetching the character and incrementing si, and "save a character" means both saving the character and incrementing di. Also note that steps 2, 3 and 4 are identical to steps 7, 8 and 9. This suggests a potential for a subroutine or macro so that you only have to write (and debug!) the code once and can use it multiple times.
I'm having a bit of trouble reading to and from arrays in assembly.
It's a fairly simple program (albeit at this point, far from finished). All I'm trying to do at this point is read a string of (what we're assuming is numbers), converting it to a decimal number, and printing it. Here's what I've got so far. As of now, it prints str1. After you enter a number and hit enter, it prints str1 again and freezes. Can anyone offer some insight as to what all I'm doing wrong?
INCLUDE Irvine32.inc
.data
buffersize equ 80
buffer DWORD buffersize DUP (0)
str1 BYTE "Enter numbers to be added together. Press (Q) to Quit.", 0dh, 0ah,0;
str2 BYTE "The numbers entered were: ", 0dh, 0ah, 0
str3 BYTE "The total of numbers entered is: ", 0dh, 0ah, 0
error BYTE "Invalid Entry. Please try again.", 0dh, 0ah,0
value DWORD 0
.code
main PROC
mov edx, OFFSET str1
call Writestring
Input:
call readstring
mov buffer[edi], eax
cmp buffer[edi], 0
JL NOTDIGIT
cmp buffer[edi], 9
JG NOTDIGIT
call cvtDec
mov edx, buffer[edi]
call WriteString
jmp endloop
Notdigit:
mov edx, OFFSET error
call writestring
exit
cvtDec:
mov eax, buffer[edi]
AND eax,0Fh
mov buffer[edi],edx
ret
endloop:
main ENDP
END MAIN
First off, Mr. Irvine created the function called WriteString, but you use 2 variations - writestring and Writestring; you do use the correct case of the function in one place. Get into the habit of using the correct names of functions now, and it will cut down on bugs later.
Second, you created a label called Notdigit but yet you use JL NOTDIGIT and JG NOTDIGIT in your code. Again, use the correct spelling. MASM should of given you an A2006 error "undefined symbol"
You also declared your entry point as main, but you close your code section with END MAIN instead of END main.
If you have MASM set up properly (by adding option casemap:none at the top of your source. Or just open irvine32.inc and uncomment the line that says OPTION CASEMAP:NONE)
Let's look at the ReadString procedure comment in irvine32.asm:
; Reads a string from the keyboard and places the characters
; in a buffer.
; Receives: EDX offset of the input buffer
; ECX = maximum characters to input (including terminal null)
; Returns: EAX = size of the input string.
; Comments: Stops when Enter key (0Dh,0Ah) is pressed. If the user
; types more characters than (ECX-1), the excess characters
; are ignored.
ReadString takes an address of the buffer to hold the inputed string in edx, you are using the address of your prompt str1, maybe you meant to use buffer? You also did not put the size of the buffer into ecx
Your using edi as an index into your buffer, what value does edi contain? Your trying to put the value of eax into it, what does eax contain??? Both edi and eax probably contain garbage; not what you want.
Look at this carefully:
cvtDec:
mov eax, buffer[edi]
AND eax,0Fh
mov buffer[edi],edx
Your putting a value (That you think is an ASCII value of a number) into eax then converting to a decimal value... ok... Next, you are putting whatever is in edx back into your buffer. Is that what you want?
Learning NASM Assembly, I am trying to make a program that reads two one-digit number inputs.
I have two variables declared in the .bss:
num1 resb 1
num2 resb 1
Then, I ask the user to write the numbers like this:
; Get number 1
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,1
int 0x80
; Get number 2
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,1
int 0x80
Since I am only interested in one-digit number inputs, I set EDX to 1. This way, whatever the user types, only the first character will be stored in my variable (right?).
The problem is that everything that follows after that first character will be used for the future reads. If you type 5 and then press ENTER, the ASCII code 53 will be stored in num1 just fine, but the line break you generated by pressing ENTER will carry on to the next read instruction, which will be stored in num2. Clearly that's not what I was intending. I want the user to type a number, press ENTER, type another number, and press ENTER.
I am not entirely sure how to work around this in the simplest way possible.
The dumbest idea was to put a "dummy" read instruction between num1 and num2, which will capture the line break (and do nothing with it). This is obviously not good.
Here's a very basic way of reading input until you get digits you want. It will skip anything but digits. This approach is fine if it provides the functionality you want. If you need different behavior depending upon other non-numeric input, then you need to specify that behavior. Then that behavior can be programmed as well.
; Get number 1
mov ECX,num1
call GetNumber
; Get number 2
mov ECX,num2
call GetNumber
...
GetNumber:
pusha ; save regs
get:
mov EAX,3 ; system call for reading a character
mov EBX,0 ; 0 is standard input
mov EDX,1 ; number of characters to read
int 0x80 ; ECX has the buffer, passed into GetNumber
cmp byte [ecx],0x30
jl get ; Retry if the byte read is < '0'
cmp byte [ecx],0x39
jg get ; Retry if the byte read is > '9'
; At this point, if you want to just return an actual number,
; you could subtract '0' (0x30) off of the value read
popa ; restore regs
ret
Meddling with stdin to disable I_CANON will work, but may be the "hard way". Using a two byte buffer and doing mov edx, 2 will work if the pesky user is well behaved - either clear the second byte, or just ignore it.
Sometimes the pesky user is not well behaved. Dealing with "garbage input" or other error conditions generally takes much more code than just "doing the work"! Either deal with it, or be satisfied with a program that "usually" works. The second option may be sufficient for beginners.
The pesky user might just hit "enter" without entering a number. In this case, we want to either re-prompt, or perhaps print "Sorry you didn't like my program" and exit. Or he/she might type more than one character before hitting "enter". This is potentially dangerous! If a malicious user types "1rm -rf .", you've just wiped out your entire system! Unix is powerful, and like any powerful tool can be dangerous in the hands of an unskilled user.
You might try something like (warning: untested code ahead!)...
section .bss
num1 resb 1
num2 resb 1
trashbin resb 1
section .text
re_prompt:
; prompt for your number
; ...
; get the number (character representing the number!)
mov ecx, num1
reread:
mov edx, 1
mov ebx, 0 ; 1 will work, but 0 is stdin
mov eax, 3 ; sys_read
int 0x80
cmp byte [ecx], 10 ; linefeed
jz got_it
mov ecx, trashbin
jmp reread
got_it:
cmp byte [num1], 10 ; user entered nothing?
jz re_prompt ; or do something intelligent
; okay, we have a character in num1
; may want to make sure it's a valid digit
; convert character to number now?
; carry on
You may need to fiddle with that to make it work. I probably shouldn't post untested code (you can embarrass yourself that way!). "Something like that" might be easier for you than fiddling with termios. The second link Michael gave you includes the code I use for that. I'm not very happy with it (sloppy!), but it "kinda works". Either way, have fun! :)
You will have to deal with canonical disabling, raw keyboard.
This is how linux manages entering console password for exampe without showing it.
The assembly to do this is nicely described here:
http://asm.sourceforge.net/articles/rawkb.html
I'm a newbie when it comes to FASM, and pretty new to ASM in general, but I'm trying to compare two strings stored in the "variables": user_input and exit_cmd:
At the moment, it assembles fine but when I enter anything in the prompt it crashes. Yes, my code is messy and the task I'm trying to accomplish may seem out of my reach for the level of ASM I know, but I've done it in other languages so I'm trying it out in ASM.
You can see I'm using the macro CompareStrings (source unknown; it's not mine) to set EAX to 1 if the strings match, but when I compare EAX to 1 using CMP and then JE to a label it refuses to work. Any help?
Here's the buggy code:
format PE console
;win32a.inc, win32w.inc, win64a.inc and win64w.inc
include 'win32w.inc'
entry main
; Define macros for things like printing strings ('print') and pausing (well, waiting for the user to press enter ('pause'))
macro print str {
push str
call [printf]
push 0
}
macro pause {
invoke system,'pause>nul'
}
macro getinput prompt {
print prompt
invoke gets,user_input
}
macro CompareStrings str1, str2
{
lea edi, [str2] ; edi -> address of string2
dec edi ; edi = edi - 1
.lab1: ; loop through all chars and compare each of them
inc edi ; ds:di --> next character in string2
lodsb ; al = next char from string1. loadsb increments si automatically
cmp [edi], al ; compare characters
jne .notfound ; jump out of the loop if they're unequal
cmp al, 0 ; chars are equal, but make sure we compared the entire string
jne .lab1 ; if not, continue the loop
mov eax, 1 ; else: strings are equal -> eax = 1
ret ; return; result: strings are equal
.notfound: ; chars are not equal
mov eax, 0 ; unequal -> eax = 0
ret ; return; result: strings are not equal
}
section '.text' code readable executable
main:
print header_msg
jmp input_loop
input_loop:
getinput cmd_prompt
print newline
CompareStrings user_input,cmd_exit
cmp eax, 1
je exit_cmd
jne input_loop
exit_cmd:
print header_msg
ret
section '.data' data readable writable
header_msg db "Basic Programming Language in ASM | Version 0.0.1",13,10,0
cmd_prompt db "> ",0
newline db "",13,10,0
user_input db "",0dh,0ah,0
chrarr db '%s',0dh,0ah,0
cmd_exit db 'exit',0
section '.idata' import data readable
library msvcrt,"msvcrt.dll"
import msvcrt,\
puts,"puts",\
getchar,"getchar",\
exit,"exit",\
printf,"printf",\
scanf,"scanf",\
system,"system",\
gets,"gets"
There are many problems in this program and it is not strange it crashes.
Problem 1
You didn't allocated enough room for the string you input. Use rb directive to allocate some big enough space to handle your input:
user_input rb 1024
Put it at the end of the data section in order to not take place in the result executable.
Problem 2
I don't know how the CPP library have to be used, but probably the calling convention is not STDCALL and the parameters are not exactly these. For example gets probably needs some kind of CPP self expandable string, not simple buffer address. Otherwise it is very easy to overflow the buffers and there is no way to check such a problem.
So, better use plain WinAPI - ReadFile and WriteFile to read and write to the console.
Problem 3
On the above background, it is nitpicking, but anyway:
push str
call [printf]
push 0
This push 0 looks weird here. What is is supposed to do?
Problem 4
Your style. Try to implement this program without using "{" and "}" characters. It will help you to understand assembly language better.