Перед 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.
Дата 12.3.2012, 15:37 (ссылка) | (нет голосов) Загрузка … |
|
Новичок
Профиль
Группа: Участник
Сообщений: 48
Регистрация: 20.2.2012
Репутация: нет
Всего: нет
Стоит задача в том, чтобы найти сумму элементов заданного массива, и вывести результат на экран. Подскажите, где ошибка в выводе значения, а то сколько времени сижу, не получается, хотя, вроде как, все правильно…
Компилятор – TASM
Код |
|
Это сообщение отредактировал(а) Relrin – 12.3.2012, 15:41
Многие считают, что 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. Т. е. приставка турбо всё же есть))