I am having a problem understanding why my cmp statements are not working correctly.
When I run this, I enter 0 in first and it goes to storeValue. I enter 0 in for the second value and it goes to the searchArray like it is supposed to.
I have breakpoints on my cmp and jump statements and a watch on AL so I don't understand why it's storing the first 0 when it should prompt for the search value at that point.
Thanks for looking.
.DATA
prompt1 BYTE "Enter a value to put in array or 0 to search array.", 0
prompt2 BYTE "Enter a value to search array for.",0
intArray DWORD ?
numElem DWORD 0
SearchVal DWORD ?
resultNope BYTE "Not in array.",0
.CODE
_MainProc PROC
lea ebx, intArray ;get the address of array.
start: input prompt1, intArray, 50 ;read in integer
atod intArray ;convert to int
mov al, [ebx] ;move int to register
cmp al, 0 ;if integer is positive - store it!
jg storeValue ;JUMP!
cmp al, 0 ;if 0 - time to search array!
je searchArray ;JUMP!
storeValue: add numElem, 1 ;Adds 1 to num of elements in array.
mov [ebx], al ;moves number into array.
add ebx, 1 ;increment to next array address.
jmp start ;get next number for array. JUMP!
searchArray:input prompt2, searchVal, 50 ;What are we searching array for?
atod searchVal ;convert to int
lea ebx, intArray ;get address of array.
mov ecx, 1 ;set loop counter to 1.
You have forgotten to show how input and atod work. Looking into my crystal ball, I guess that input expects a buffer to store the user input as text, and the argument 50 is presumably its size. Notice that you don't have such a buffer and you don't even have 50 bytes space. I also think that since atod apparently only takes 1 argument, which is the text buffer to convert, it presumably returns the value in eax. This is also reinforced by the fact that your storeValue writes from al which would make no sense otherwise.
Long story short:
allocate a buffer of the proper size for the text entered
pass this array to atod
do not clobber al after the call to atod
(Applies to the search part too.)
Related
I currently have a array of 25 numbers. I want to create a string of 5 numbers and then output them. Then get 5 more numbers and output the string and so on.... So basically a 5x5 grid of the array numbers. My issue is that I am not sure how to get a number out of the array, then put it on the string. Loop 5x and then output.
x86 Assembly. Using WORDs
Here is my code:
VARIABLES
inputGridArray WORD MAXNBRS DUP (?)
grid_Rows WORD ?
grid_Cols WORD ?
gridSize WORD ?
temp WORD ?
empty WORD ?
prompt BYTE 0, 0
string BYTE 40 DUP (0)
MACRO
printArray MACRO array_name, size_array
LOCAL PRINT_ARRAY, PRINT_ROW, NEXT_ROW, END_PRINT_LOOP
lea ebx, array_name
mov cx, 0
mov temp, 0
PRINT_ARRAY:
cmp cx, grid_Cols
je END_PRINT_LOOP
PRINT_ROW:
mov ax, temp
cmp ax, grid_Rows
je NEXT_ROW
;I need to construct a string maybe, to be able to put the array into a grid shape
outputW WORD PTR [ebx]
add ebx, 2
inc temp
jmp PRINT_ROW
NEXT_ROW:
inc cx
mov temp, 0
jmp PRINT_ARRAY
END_PRINT_LOOP:
ENDM
I'm new to Assembly, I need help with an assignment in Assembly Language Irvine32. I want to know where I'm going wrong. I believe my code is 80% right but there's something I'm not seeing or recognizing. Here's the program details.
"Write an assembly language program that has an array of words. The program loads the last element of the array into an appropriately sized register and prints it. (Do not hardcode the index of the last element.)"
INCLUDE Irvine32.inc
.data
val1 word 1,2,3,4,5,6
val2 = ($-val1)/2
.code
main PROC
mov ax, 0
mov ax, val1[val2]
Call WriteDec
Call DumpRegs
exit
main ENDP
END main
First of all, your code has a bug: val1[val2] indexes with the element count in words, not the length in bytes (unless MASM syntax is even more magical than I expect). And it reads from one past the end of the array, since the first element is at val1[0].
To find the end, you either need to know the length (explicit length, like a buffer passed to memcpy(3)), or search it for a sentinel element (implicit length, like a C string passed to strcpy(3)).
Having a function that accepts an explicit length as a parameter seems fine to me. It's obviously much more efficient than a loop scanning for a sentinel element, and the array shown doesn't include one. (See Jose's answer for a suggestion to use '$' (i.e. 36) as a sentinel value. -1 or 0 might be more sensible sentinels/terminators.)
Obviously knowing the length is much better, since there's no need for a loop scanning the whole array.
I'd only call it hard-coding if you wrote val2 = 6, or worse val2 dw 6, rather than having it calculated at assemble-time from the array. If you want to write a function that could work with non-compile-time-constant arrays, you can have it accept the length as a value in memory, instead of an immediate that will be embedded into its load instruction.
e.g.
Length as a parameter in memory
.data
array word 1,2,3,4,5,6
array_len word ($-array)/2 ; some assemblers have syntactic sugar to calc this for you, like a SIZE operator or something.
.code
main PROC ; inputs: array and array_len in static storage
; output: ax = last element of array
; clobbers: si
; mov ax, 0 ; This is useless, the next mov overwrites it.
mov si, [array_len] ; do we need to save/restore si with push/pop in this ABI?
add si,si ; multiply by 2: length in words -> length in bytes
mov ax, [array + si - 2] ; note that the -2 folds into array at assemble time, so it's just a disp16 + index addressing mode
Call WriteDec
Call DumpRegs
exit
main ENDP
END main
You could also write a function to take pointer and length args on the stack or in registers, and have main pass those args.
You could save the add (or shl) by accepting a length in bytes, or a start and one-past-the-end pointer (like C++ STL range functions that take .begin() and .end() iterators). If you have the end pointer, you don't need the start pointer at all, except to return an error if they're equal (size = 0).
Or if you were not stuck with obsolete 16bit code, you could use a scaled index in the addressing mode, like [array + esi * 2]. You include Irvine32.inc...
I think your solution to reach the last element is the most efficient (($-val1)/2), but #zx485 is right and your teacher might believe you are cheating, so, among other solutions, you can reach the last element with a loop and the pointer SI :
INCLUDE Irvine32.inc
.data
val1 word 1,2,3,4,5,6
val2 = ($-val1)/2
.code
main PROC
; mov ax, 0
; mov ax, val1[val2]
mov cx, val2-1 ;COUNTER FOR LOOP (LENGTH-1).
mov si, offset val1 ;SI POINTS TO FIRST WORD IN ARRAY.
repeat:
add si, 2 ;POINT TO NEXT WORD IN ARRAY.
loop repeat ;CX--, IF CX > 0 REPEAT.
mov ax, [ si ] ;LAST WORD!
Call WriteDec
Call DumpRegs
exit
main ENDP
END main
One shorter way would be to get rid of the loop and jump straight to the last element by using the SI pointer (and changing val2 just a little) :
INCLUDE Irvine32.inc
.data
val1 dw 1,2,3,4,5,6
val2 = ($-val1)-2 ;NOW WE GET LENGTH - 2 BYTES.
.code
main PROC
; mov ax, 0
; mov ax, val1[val2]
mov si, offset val1 ;SI POINTS TO FIRST WORD IN ARRAY.
add si, val2 ;SI POINTS TO THE LAST WORD.
mov ax, [ si ] ;LAST WORD!
Call WriteDec
Call DumpRegs
exit
main ENDP
END main
And "Yes", you can join those two lines :
mov si, offset val1 ;SI POINTS TO FIRST WORD IN ARRAY.
add si, val2 ;SI POINTS TO THE LAST WORD.
into one, I separated them to comment each other :
mov si, offset val1 + val2
If you cannot use val2 = ($-val1)/2, one option would be to choose some terminating character for the array, for example, '$', and loop until it's found:
INCLUDE Irvine32.inc
.data
val1 word 1,2,3,4,5,6,'$' ;ARRAY WITH TERMINATING CHARACTER.
;val2 = ($-val1)/2
.code
main PROC
;mov ax, 0
;mov ax, val1[val2]
mov si, offset val1 ;SI POINTS TO VAL1.
mov ax, '$' ;TERMINATING CHARACTER.
repeat:
cmp [ si ], ax
je dollar_found ;IF [ SI ] == '$'
add si, 2 ;NEXT WORD IN ARRAY.
jmp repeat
dollar_found:
sub si, 2 ;PREVIOUS WORD.
mov ax, [ si ] ;FINAL WORD!
Call WriteDec
Call DumpRegs
exit
main ENDP
END main
I am having an issue with this code snippet of a problem. My expected output is (after the intro):
Please enter an unsigned number: (user input)
.
.
.
Repeat ten times
The output that I am getting is :
Please enter an unsigned number: 10
10 <--- this is displaying instead of "Please enter an unsigned number: "
Any idea why this problem might be happening?
;getString should display a prompt, then get the user’s keyboard input into a memory location
getString MACRO buffer
push edx ;Save edx register
mov edx, OFFSET buffer
call WriteString
call ReadString
pop edx
ENDM
;displayString should the string stored in a specified memory location.
displayString MACRO buffer
push edx
mov edx, OFFSET buffer
call WriteString
call crlf
ENDM
.data
intro_1 BYTE "PROGRAMMING ASSIGNMENT 5: Designing low-level I/O procedures",0
intro_2 BYTE "Written by: Eric Walters",0
intro_3 BYTE "Please provide 10 unsigned decimal integers.",0
intro_4 BYTE "Each number needs to be small enough to fit inside a 32 bit register.",0
intro_5 BYTE "After you have finished inputting the raw numbers I will display a list",0
intro_6 BYTE "of the integers, their sum, and their average value.",0
data_1 BYTE "Please enter an unsigned number: ",0
array DWORD 10 DUP(?)
.code
main PROC
;Introduction
displayString intro_1
displayString intro_2
displayString intro_3
displayString intro_4
displayString intro_5
displayString intro_6
push OFFSET array
call getString1
;readVal
exit
main ENDP
getString1 PROC
push ebp
mov ebp, esp
mov ecx, 9
mov esi, [ebp + 8]
getString2:
getString data_1
mov [esi], eax
add esi, 4
call crlf
dec ecx
jmp getString2
ret
getString1 ENDP
END main
I suspect the problem is in some parameter being inadvertently passed into read string pointing at the address where data_1 is stored. For example, if edx is pointing at data_1 when you mov the OFFSET into it, and the WriteString is looking at edx for the address of the data to write, ReadString is looking at edx for the address to store the line in once it is read. So the ReadString is overwriting your sentence in data_1 with the number you entered and an end of line marker. Then the next time the WriteString PROC is called, it is reading in that number.
To fix this, create a .data? field called input_data or whatever you like, and point edx at it after the WriteString call but before the ReadString call.
Just an aside, but I also think you need to change your line after "dec ecx" to read "jnz getString2", not "jmp getString2". Otherwise I cannot see you ever escaping the loop no matter what value ecx reaches.
EDIT: Wait, I see, you have an array set up for that already. I think you just need to point to it before calling ReadString.
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 trying to make a very simple program that will list out values in a BYTE named array, then reverse the digits. This is the problem I was given:
Write a program to do the following:
Use the BYTE directive to define the list of the 9 digits of your Student's ID number and name it array. Write instructions to reverse the order of those digits in array.
So far this is what I have:
.DATA
array BYTE 9h, 6h, 4h, 5h, 2h, 8h, 7h, 4h, 2h
.CODE
start:
mov esi, 0
mov edi, 0
; ?????
call DumpRegs
call WriteInt
exit
END start
I have used a BYTE for nine digits named array. I do not know how to start the reversal process. Is this done by LOOP? I understand the simple loop, however I am at a complete loss on this one. Any help or answer you can give is very much appreciated. Thank you in advance for helping me learn this material.
Here is something i put together, hope it helps.
.DATA
array BYTE 9h, 6h, 4h, 5h, 2h, 8h, 7h, 4h, 2h
reversearray BYTE 9 DUP (0) ; assign storage for array that will contain reverse
.CODE
mov ecx, SIZEOF array ; length of array is 9, so ecx contains 9
lea esi, array ; address of the start of array
lea edi, reversearray ; address of the start of reversearray
decarray:
movzx eax, byte ptr [esi+ecx-1] ; byte from esi (array start) + ecx counter - 1
mov byte ptr [edi], al ; store byte we have in al, into reverse array (edi)
inc edi ; add 1 to reverse array location for next bytes storage when we loop again
loop decarray ; if ecx is 0 we end, otherwise loop again and ecx will now be ecx-1
; do other stuff here, print our arrays etc