Как найти сумму элементов массива ассемблер

Перед loop (точнее, перед меткой начала) нужно поместить в cx количество шагов

Если в bx поместили адрес массива, то с помощью [bx] получают данные по текущему адресу. Отмечу, что регистр bx использован зря, можно обойтись dx. (иначе bx нужно сохранять и восстанавливать)

На каждом шаге bx нужно увеличивать на 2

Как инициализировать регистр для суммирования и проводить в нем суммирование на каждом шаге цикла – должно быть очевидной из обычной программистской логики.

Проверил в Delphi с edx вместо dx (поскольку 32-разрядная система)

asm
    mov dx, offset x
    xor ax, ax 
    mov cx, 10
@@1:
    add ax, [dx]    
    add dx, 2
    loop @@1

    mov sum, ax
end

Ещё вариант (не проверял)

asm
   lds si, x    {адрес массива}
   xor dx, dx 
   mov cx, 10
@@1:
   lodsw   {загрузить слово в ax из [si]}
   add dx, ax    
   loop @@1

   mov sum, dx
end

Все ссылки на статьи и ролики моего канала Old Programmer:
Программирование. Тематическое оглавление моего Zen-канала (Old Programmer). А вот это ссылки на все мои статьи по языку программирования ассемблер x86-64.

Решил привести еще один пример на использование команд для вещественных чисел. Или как обычно говорят команд сопроцессора.

Пример суммирования элементов массива на ассемблере x86-64

Теперь задача заключается в том, чтобы получить сумму массива элементов float на ассемблере. Как и в прошлом примере основная программа написана на C (main2100.c), а модель, где производится вычисление на ассемблере.

Несколько пояснений к программе asm2100.s. Передаваемый массив состоит из 32-битовых вещественных чисел (float), поэтому add $4, %rdi. Как я неоднократно говорил в статьях об ассемблере при вызове функции первый параметр передается через регистр rdi, второй – rsi. Таким образом в начале rdi указывает на первый элемент массива, rsi содержит количество элементов в массиве.

В бар заходит бесконечное количество математиков.
Первый просит литр пива. Второй просит пол-литра пива. Третий просит четверть литра пива.
Бармен кричит «Прекратите!» и наливает два литра пива.

Подписываемся на мой канал о программировании и программистах Old Programmer.

Фрагмент программы asm2100.s
Фрагмент программы asm2100.s
Relrin
Дата 12.3.2012, 15:37 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Новичок

Профиль
Группа: Участник
Сообщений: 48
Регистрация: 20.2.2012

Репутация: нет
Всего: нет

Стоит задача в том, чтобы найти сумму элементов заданного массива, и вывести результат на экран. Подскажите, где ошибка в выводе значения, а то сколько времени сижу, не получается, хотя, вроде как, все правильно…
Компилятор – TASM

Код

name prog
.model small    ;сегмент кода
.stack 100h        ;стек

;сегмент данных(раздел переменных)
.data
ar1 dw 3,4,2,1,5,4,2,7    ;8 элементов
ar2 dw 15,34,13,35,12,57,0;7 элементов
ar3 dw 1,4,15,3,7,5,8,68,5;9 элементов
mes db 'summa elementov massiva ravna ','$'

;сегмент кода
.code
start:
    ;ссылаем на сегмент данных
    mov ax, @data
    mov ds, ax
        ;ициализация переменных
        xor bx,bx    ;ax=0
        mov cx,8     ;cx=8
        xor si,si    ;si=0

                ;цикл, суммирующий элементы массива
cycle: 
        add bx,ar1[si]    ;ax=ax+ar[si]   
        inc si            ;si++
        loop cycle        ;повторяем цикл
        ;вывод строки
string:        
    mov ah,09h      ;команда на вывод
    mov dx,offset mes ;помещаем строку
    int 21h          ;вызываем прерывания

        xor cx,cx
    xor ax,ax
    xor dx,dx
    ;вывод числа
    mov     ax,bx   ;ax=bx    
    push    -1    ;Сохраним признак конца числа
    mov    cx,10    ;Делим на 10
repeat:   
    xor    dx,dx    ;Очистим регистр dx
    div    cx    ;Делим 
    push    dx    ;Сохраним цифру
    cmp    ax,0    ;Остался 0? (оптимальнее or ax,ax)
    jne    repeat    ;нет -> продолжим
    mov    ah,2h
digit:   
    pop    dx    ;Восстановим цифру
    cmp    dx,-1    ;Дошли до конца -> выход {оптимальнее: or dx,dx jl ex}
    je    exit
    add    dl,'0'    ;Преобразуем число в цифру
    int    21h    ;Выведем цифру на экран
    jmp    digit    ;И продолжим

         exit:
    mov ax, 4C00h 
    int 21h
end start

Это сообщение отредактировал(а) Relrin – 12.3.2012, 15:41

PM MAIL   Вверх

Многие считают, что Assembler – уже устаревший и нигде не используемый язык, однако в основном это молодые люди, которые не занимаются профессионально системным программированием. Разработка ПО, конечно, хорошо, но в отличие от высокоуровневых языков программирования, Ассемблер научит глубоко понимать работу компьютера, оптимизировать работку с аппаратными ресурсами, а также программировать любую технику, тем самым развиваясь в направлении машинного обучения. Для понимания этого древнего ЯП, для начала стоит попрактиковаться с простыми программами, которые лучше всего объясняют функционал Ассемблера.

IDE для Assembler

Первый вопрос: в какой среде разработки программировать на Ассемблере? Ответ однозначный – MASM32. Это стандартная программа, которую используют для данного ЯП. Скачать её можно на официальном сайте masm32.com в виде архива, который нужно будет распаковать и после запустить инсталлятор install.exe. Как альтернативу можно использовать FASM, однако для него код будет значительно отличаться.

Перед работой главное не забыть дописать в системную переменную PATH строчку:

С:masm32bin

Программа «Hello world» на ассемблере

Считается, что это базовая программа в программировании, которую начинающие при знакомстве с языком пишут в первую очередь. Возможно, такой подход не совсем верен, но так или иначе позволяет сразу же увидеть наглядный результат:

.386
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib
 
.data
msg_title db "Title", 0
msg_message db "Hello world", 0
 
.code
start:
invoke MessageBox, 0, addr msg_message, addr msg_title, MB_OK
invoke ExitProcess, 0
end start

Для начала запускаем редактор qeditor.exe в папке с установленной MASM32, и в нём пишем код программы. После сохраняем его в виде файла с расширением «.asm», и билдим программу с помощью пункта меню «Project» → «Build all». Если в коде нет ошибок, программа успешно скомпилируется, и на выходе мы получим готовый exe-файл, который покажет окно Windows с надписью «Hello world».

Сложение двух чисел на assembler

В этом случае мы смотрим, равна ли сумма чисел нулю, или же нет. Если да, то на экране появляется соответствующее сообщение об этом, и, если же нет – появляется иное уведомление.

.486
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm 
uselib masm32, comctl32, ws2_32

.data

.code
start:

mov eax, 123
mov ebx, -90 
add eax, ebx

test eax, eax

jz zero 
invoke MessageBox, 0, chr$("В eax не 0!"), chr$("Info"), 0
jmp lexit

zero:
invoke MessageBox, 0, chr$("В eax 0!"), chr$("Info"), 0

lexit:
invoke ExitProcess, 0

end start

Здесь мы используем так называемые метки и специальные команды с их использованием (jz, jmp, test). Разберём подробнее:

  • test – используется для логического сравнения переменных (операндов) в виде байтов, слов, или двойных слов. Для сравнения команда использует логическое умножение, и смотрит на биты: если они равны 1, то и бит результата будет равен 1, в противном случае – 0. Если мы получили 0, ставятся флаги совместно с ZF (zero flag), которые будут равны 1. Далее результаты анализируются на основе ZF.
  • jnz – в случае, если флаг ZF нигде не был поставлен, производится переход по данной метке. Зачастую эта команда применяется, если в программе есть операции сравнения, которые как-либо влияют на результат ZF. К таким как раз и относятся test и cmp.
  • jz – если флаг ZF всё же был установлен, выполняется переход по метке.
  • jmp – независимо от того, есть ZF, или же нет, производится переход по метке.

Программа суммы чисел на ассемблере

Примитивная программа, которая показывает процесс суммирования двух переменных:

.486
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm 
uselib masm32, comctl32, ws2_32

.data
msg_title db "Title", 0
A DB 1h
B DB 2h
buffer db 128 dup(?)
format db "%d",0

.code
start:

MOV AL, A
ADD AL, B

invoke wsprintf, addr buffer, addr format, eax
invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK

invoke ExitProcess, 0

end start

В Ассемблере для того, чтобы вычислить сумму, потребуется провести немало действий, потому как язык программирования работает напрямую с системной памятью. Здесь мы по большей частью манипулируем ресурсами, и самостоятельно указываем, сколько выделить под переменную, в каком виде воспринимать числа, и куда их девать.

Получение значения из командной строки на ассемблере

Одно из важных основных действий в программировании – это получить данные из консоли для их дальнейшей обработки. В данном случае мы их получаем из командной строки и выводим в окне Windows:

.486
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm 
uselib masm32, comctl32, ws2_32

.data

.code
start:

call GetCommandLine ; результат будет помещен в eax
 
push 0
push chr$("Command Line")
push eax ; текст для вывода берем из eax
push 0
call MessageBox

push 0
call ExitProcess

end start

Также можно воспользоваться альтернативным методом:

.486
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm 
uselib masm32, comctl32, ws2_32

.data

.code
start:

call GetCommandLine ; результат будет помещен в eax
 
invoke GetCommandLine
invoke MessageBox, 0, eax, chr$("Command Line"), 0
invoke ExitProcess, 0

push 0
call ExitProcess

end start

Здесь используется invoke – специальный макрос, с помощью которого упрощается код программы. Во время компиляции макрос-команды преобразовываются в команды Ассемблера. Так или иначе, мы пользуемся стеком – примитивным способом хранения данных, но в тоже время очень удобным. По соглашению stdcall, во всех WinAPI-функциях переменные передаются через стек, только в обратном порядке, и помещаются в соответствующий регистр eax.

Циклы в ассемблере

Вариант использования:

.data

msg_title db "Title", 0
A DB 1h
buffer db 128 dup(?)
format db "%d",0

.code
start:

mov AL, A
.REPEAT
inc AL

.UNTIL AL==7

invoke wsprintf, addr buffer, addr format, AL
invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK
 
invoke ExitProcess, 0

end start
.data

msg_title db "Title", 0
buffer db 128 dup(?)
format db "%d",0

.code
start:

mov eax, 1
mov edx, 1

.WHILE edx==1
inc eax
.IF eax==7
.BREAK
.ENDIF
.ENDW

invoke wsprintf, addr buffer, addr format, eax
invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK
 
invoke ExitProcess, 0

Для создания цикла используется команда repeat. Далее с помощью inc увеличивается значение переменной на 1, независимо от того, находится она в оперативной памяти, или же в самом процессоре. Для того, чтобы прервать работу цикла, используется директива «.BREAK». Она может как останавливать цикл, так и продолжать его действие после «паузы». Также можно прервать выполнение кода программы и проверить условие repeat и while с помощью директивы «.CONTINUE».

Сумма элементов массива на assembler

Здесь мы суммируем значения переменных в массиве, используя цикл «for»:

.486
.model flat, stdcall
option casemap: none
 
include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc
 
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm 
uselib masm32, comctl32, ws2_32

.data

msg_title db "Title", 0
A DB 1h
x dd 0,1,2,3,4,5,6,7,8,9,10,11
n dd 12

buffer db 128 dup(?)
format db "%d",0

.code
start:
mov eax, 0
mov ecx, n
mov ebx, 0
L: add eax, x[ebx]
add ebx, type x
dec ecx
cmp ecx, 0
jne L

invoke wsprintf, addr buffer, addr format, eax
invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK
 
invoke ExitProcess, 0

end start

Команда dec, как и inc, меняет значение операнда на единицу, только в противоположную сторону, на -1. А вот cmp сравнивает переменные методом вычитания: отнимает одно значение из второго, и, в зависимости от результата ставит соответствующие флаги.

С помощью команды jne выполняется переход по метке, основываясь на результате сравнения переменных. Если он отрицательный – происходит переход, а если операнды не равняются друг другу, переход не осуществляется.

Ассемблер интересен своим представлением переменных, что позволяет делать с ними что угодно. Специалист, который разобрался во всех тонкостях данного языка программирования, владеет действительно ценными знаниями, которые имеют множество путей использования. Одна задачка может решаться самыми разными способами, поэтому путь будет тернист, но не менее увлекательным.


Post Views:
58 094

Математически, чтобы найти сумму элементов массива, каждый из которых умножен на одно и то же число, достаточно сложить все элементы и полученную сумму умножить на это число:
x1*c+x2*c+x3*c+…+xn*c=(x1+x2+x3+…+xn)*c
Но с учётом, что это учебное задание, для DOS программа может выглядеть примерно так:

data segment
;array db 4 dup (4 dup (?)); незаполненный массив
; или заполненный
array db 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
B db 5; переменная В
M db 4; кол-во строк
N db 4; кол-во столбцов
C dw ?; переменная для результата
data ends

code segment
assume cs:code, ds:data
N63718405:
mov ax,data
mov ds,ax
;ввод размера массива, его элементов и значения переменной В
;здесь не приводится
xor bx,bx; обнуление смещения начала строки
mov C,bx; начальное значение суммы С: =0
xor ch,ch; обнуление старшей половины счетчика строк
mov cl,M; загрузка в счётчик кол-ва строк
RowLoop:
xor si,si; обнуление смещения элемента от начала строки
push cx; сохранение счётчика строк в стеке
mov cl,N; загрузка в счётчик кол-ва столбцов
ColLoop:
mov al,array[bx][si]; загрузка в аккумулятор текущего элемента
mul byte ptr B; умножение его на B
add C,ax; прибавление произведения к сумме С: =C+a[ i, j ]*5
inc si; переход к след. элементу в строке
loop ColLoop; цикл по элементам строки
xor ah,ah; обнуление старшей части длины строки
mov al,N; загрузка кол-ва столбцов (= длине строки)
add bx,ax; переход к след. строке
pop cx; восстановление счётчика строк из стека
loop RowLoop; цикл по строкам
;здесь вывод результата куда-нибудь
mov ah,4Ch
int 21h
code ends
end N63718405

P.S. Так как строки массива идут непосредственно друг за другом, можно его обрабатывать как одномерный, тогда из программы уберётся внутренний вложенный цикл, а результат не изменится.
P.P.S. TASM – это Turbo Assembler. Т. е. приставка турбо всё же есть))

Добавить комментарий