x86 Irvine32: Assembly language - Get last array [duplicate] - arrays

This question already has answers here:
x86 assembly: Irvine32 - Get last element of an array
(2 answers)
Closed 6 years ago.
I'm trying to figure out this program. Just want to know if I'm on point with the program. How do I correct this program? Any help is appreciated. Thank you.
"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 ; This is hardcoding
.code
main PROC
mov ax, 0
mov ax, val1[val2]
Call WriteDec
Call DumpRegs
exit
main ENDP
END main

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 ; This is hardcoding
.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

Related

How to write a loop that does something different in the last iteration?

The output for the given program should be 1--2--3--4--5--6--7--8.
However, mine keeps giving me 1--2--3--4--5--6--7-8--.I need to get rid of "--" after 8, but I couldn't figure out how to do it. Could someone help me, please?
INCLUDE Irvine32.inc
.data
arrayb byte 1,2,3,4,5,6,7,8
space byte "--",0
.code
main PROC
mov eax,0
mov edx,offset space
mov esi,offset arrayb
mov ecx, 8
printb:
mov al, [esi]
call writedec
call writestring
inc esi
mov eax, 8
loop printb
exit
main ENDP
end main
Your code (and loop specifically) currently does:
<init state>
counter = 8
printb:
output number
output "--"
loop to printb
<exit>
If you run through it in your head, it should be obvious why "--" is printed after last number.
There are many ways how to adjust that code, in real world code doing formatting I often either use some kind of join function which takes list and separator, and produces the formatted string, or if doing it manually, I would probably hardcode output of "1" ahead of loop, initialize the state to start loop as if starting from "2", and output "--" as first thing in the loop, i.e.:
<init state>
output number
advance state as if it was printed inside loop
counter = 8-1 ; one value is already out
printb:
output "--" ; the loop body starts with "--" output
output number
loop to printb
<exit>
I.e. in your code (with some modifications "improving" some things I didn't like :
...
mov edx,offset space
mov esi,offset arrayb
movzx eax, BYTE PTR [esi]
inc esi
call writedec
mov ecx, 8-1
printb:
call writestring
movzx eax, BYTE PTR [esi]
inc esi
call writedec
loop printb
exit
...
edit
The Peter Cordes idea from comments to make last item special case would maybe lead to better code in generic cases, where the amount of items is variable (with "8" fixed you know you can display first element and then 7 more will be looped).
Let's imagine such function in assembly, taking arguments in registers:
; esi = byte array to output
; ecx = amount of bytes
; will modify eax, ecx, edx, esi
OutputByteArray PROC
jecxz NoItemsToOutput ; if ecx=0
dec ecx
jz OutputLastItem ; if ecx=1, display only last item
; 1 < count, so "0 < ecx" here (ecx = count-1) => do the loop
mov edx,offset space
printb: ; output number and "--" "count-1" times
movzx eax, BYTE PTR [esi]
inc esi
call writedec
call writestring
loop printb
OutputLastItem:
movzx eax, BYTE PTR [esi]
call writedec
NoItemsToOutput:
ret
ENDP

Loops in Assembly Language

Suppose A={1,2,3,4} and B={2,3,4,5} be two arrays. How can we initialize them by using loops? And how can we add corresponding elements of these arrays and store them into 3rd array by using loops? My assembler is masm615.
include irvine32.inc
.data
word ayyay1 5 dup(?)
word ayyay2 5 dup(?)
.code
main proc
top:
----------
----------
loop top
call dumpregs
exit
main endp
end main
Untested but generally guiding:
include irvine32.inc
.data
word ayyay1 5 dup(?)
word ayyay2 5 dup(?)
.code
main proc
lea edi, [ayyay1]
lea esi, [ayyay2]
mov ax, 1
mov bx, 2
mov cx, 4
top:
mov word [edi],ax
mov word [esi],bx
inc ax
inc bx
add edi,2 ; add to point to next word location
add esi,2 ; add to point to next word location
loop top
call dumpregs
exit
main endp
end main

x86 assembly: Irvine32 - Get last element of an array

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

How do I loop through a string and store it into an array in NASM?

(total newbie to NASM struggling to learn)
I've put the 1st command line argument into register eax. It can be a string between 1-20 lowercase characters.
I now want to loop through this string, copying one character at a time into a byte array A in the program's memory and storing the length of the string N in memory too. At this point in the program I've checked that the string is a legal input and is fine length-wise and case-wise.
This is a rough structure (?) that doesn't seem to be working:
section .bss ; uninitialized data
N resd 1 ; length of string
A resb 1 ; byte array A
section .text
asm_main:
// legal input checking code
mov edx, 0
loop2:
mov al, [eax+edx]
mov [A+edx],al
inc edx
cmp al, 0
jz done_loop2
jmp loop2
done_loop2:
mov [N], edx
call print_int
mov eax, A
call print_string
// code for jumps to errors and end of main
(I'm only printing the size and string to check if the loops works correctly)
I'm getting unexpected outputs: for eg
input "hello" gives me
-6193920
hxETmcxbt=Se6o=Eaco/oa
Any help would be super appreciated, thank you! :)
I'll give you an example:
global asm_main
SECTION .data
global x
mystring: db "stackoverflow",0
mystringl: equ $-mystring
array: TIMES mystringl dd 0
SECTION .text
asm_main:
enter 0,0
pusha
mov ecx, 0 ; counter
looper:
cmp byte[mystring+ecx], 0x0 ; check for end of string
je exit
mov eax, 0 ; empty eax
mov al, byte[mystring+ecx] ; move string position into al
mov byte[array+ecx], al ; write into memory
push eax
mov eax, 0
mov al, byte[array+ecx]
call print_char
call print_nl
pop eax
add ecx, 1
jmp looper
exit:
If you need any help, feel free to message me.

Assembly Array and loop

I've a problem in assembly language that I want to make loop for sum element of an array. Suppose an array contains 10,20,30,40,50,60,70,80,90,100 I have to sum all elements of the array by loop... How can I do this?
I'm trying this:
.MODEL SMALL
.STACK 100H
.DATA
W DW 10,20,30,40,50,60,70,80,90,100
.CODE
MAIN PROC
MOV AX, #data
MOV DS, AX
XOR AX, AX
XOR BX, BX
MOV CX, 10
ADDNOS:
ADD AX, W [BX]
ADD BX, 2
LOOP ADDNOS
;this for display
MOV DX, AX
MOV AH,2
INT 21H
MOV AH, 4CH
INT 21H
MAIN ENDP
END MAIN
but something wrong in display that print from ascii (&).
EDIT: Updated answer since the code in the question has been changed:
INT 21h / AH=2 prints a single character (note that the integer 1 and the character '1' are different values).
The sum of the elements in your array is 550, which requires 3 characters to print. The way to solve that is to write a routine that converts the value 550 to the string "550", and then use INT 21h / AH=9 to print that string. How you'd go about doing that has been asked several times before on StackOverflow; see e.g. this question and the answers to it.
This is my answer for the original question
For future questions, note that "but something wrong" is a terrible problem description. You should explain precisely in what way the code isn't behaving the way you intended.
That said, there are a number of problems with your code:
Here you're initializing CX to the first value in x. Actually, since the elements in x are bytes (because you used DB) and CX is a word (two bytes) you'll get CX = 301h (which is 769 in decimal):
MOV CX, x
Here you're simply moving the first element of x into BX over and over, instead of doing an addition. And again, x contains bytes while BX is a word register.
top: MOV BX, [x]
The loop instruction decrements CX by 1 and jumps to the given label if CX != 0. By incrementing CX before the loop you're creating an infinite loop. Also, the CMP is useless (and I'm not sure why you're comparing against 7 since x only has 5 elements):
INC CX
CMP CX, 7
loop top
This will only work for values in the range 0-9. If the sum is >=10 it will require multiple characters. See e.g. this answer for an example of how to convert a multi-digit number to a string that can be printed. Also, you're writing a word-sized register to a byte variable:
ADD BX, '0'
MOV [sum], BX
Here I'm a bit lost at what you're trying to do. If you wanted to write a single character to STDOUT you should use INT 21h / AH = 2 / DL = character. Note that MOV AX,4 sets AH=0 and AL=4. Also, you should end your program with INT 21h / AX = 4C00h:
display_:
MOV DX,1
MOV CX, sum
MOV BX, 1
MOV AX,4
INT 21h
MOV AX, 1
INT 21h
I suspect that there is an error in the code following the top label.
You do MOV BX, [x] but I think there you should sum the item pointed by CX with what currently is in BX (that seems to store the sum). So substitute the move instruction with:
ADD BX, [CX]

Resources