Консультация № 183962
04.09.2011, 23:47
80.31 руб.
0 7 2
Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос:

Помогите пожалуйста реализовать программы на Ассемблере (как вставка в Делфи)
3. Реализуйте подпрограмму, которая возвращает символы имеющиеся в данной строке с указанием числа вхождений каждого символа.
4. Дано натуральное число n. Вычислить: (1-1/2!)(1-1/3!)(1-1/4!)…(1-1/n!)

Спасибо.

Обсуждение

давно
Старший Модератор
31795
6196
06.09.2011, 17:57
общий
это ответ
Здравствуйте, Посетитель - 380267!

Код заданий в приложении:
№1
[code h=150]program Project2;

{$APPTYPE CONSOLE}

uses
SysUtils;
type
tMass=array[0..255]of byte;
var
a:ShortString;
b:tMass;
c:integer;
procedure A001(a1:ShortString;var b1:tMass);assembler;
asm
{edx указатель на массив b1}
{eax указатель на строку a1}
{сбрасываем значение счетчиков в массиве}
mov ecx,256{количество элементов в массиве}
push edx{запоминаем указатель на массив В1}
@@001:
mov byte ptr[edx],0{сбрасываем значение счетчика}
inc edx{следующее значение}
loop @@001{пока СХ больше нуля}
pop edx{востанавливаем значение указателя на массив В1}
{выполняем задание}
xor ecx,ecx{команда не нужна, т.к. после цикла там будет ноль, но всетаки}
xor ebx,ebx{сбрасываем значение регистра ЕВХ}
mov cl,[eax]{загружаем количество символов}
@@002:
inc eax{следующее значение}
mov bl,[eax]{считываем текущий символ}
inc byte ptr[ebx+edx]{увеличиваем значение счетчика соответсвующего коду символа}
loop @@002{пока не кончится введенная строка}
end;
begin
{вводим строку}
write('enter string:');
readln(a);
{выполняем задание}
a001(a,b);
{выводим результат}
for c:=0 to 255 do if b[c]>0 then write(chr(c):6,b[c]:4);
readln;
end.[/code]
Как я не старался, мой компилятор не хочет передавать параметры в процедуру через стек. Использована строка длиной до 256-ти, 8-ми битных символов.

№2
[code h=150]program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils;
var n:integer;
a:double;
begin
{ввод целого числа с контролем}
repeat
write('enter N:');
readln(n);
until(n>1)and(n<1000);
{сбрасываем результат}
a:=1;
asm
mov ecx,n{загружаем введенное целое}
FINIT{инициализируем сопроцессор}
{считаем факториал}
@@000:
push ecx{запоминаем счетчик}
mov eax,1{сбрасываем начальное значение факториала}
@@001:
xor edx,edx{сбрасываем старший операнд}
mul ecx{умножаем на счетчик}
loop @@001{пока СХ больше нуля}
mov n,eax{записываем факториал}
pop ecx{востанавливаем счетчик}
dec ecx{уменьшаем счетчик}
jz @@002{ноль выходим}
{считаем формулу}
FLD1{заружаем в стек первую единицу}
FLD1{загружаем в стек единицу дроби}
FILD n{загружаем в стек факториал}
FDIV{получаем значение дроби}
FSUB{получаем значение текущей итерации}
FMUL a{умножаем с предыдущими значениями}
FSTP a{запоминаем рузультат}
jmp @@000{следующая итерация}
@@002:
end;
write('result:',a);
readln;
end.
[/code]
Обращаю Ваше внимание, что регистры сопроцессора называются стеком. Не путайте со стеком программы.

Всё проверялось в delphi 6.
Удачи!
5
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
06.09.2011, 17:59
общий
Забыл добавить, что формула считается справо на лево.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
06.09.2011, 20:46
общий
06.09.2011, 20:46
Адресаты:
Как я не старался, мой компилятор не хочет передавать параметры в процедуру через стек

procedure A001(a1:ShortString;var b1:tMass);stdcall;assembler;
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Старший Модератор
31795
6196
06.09.2011, 20:57
общий
Адресаты:
Спасибо!


Старею.
Завтра переделаю под стек.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
06.09.2011, 21:40
общий
это ответ
Здравствуйте, Посетитель - 380267!
3) Предлагаю свою версию программы.
Работает со строками типа String.
В результате формируется строка String, состоящая из разных символов, и массив из Integer с количествами символов.
Массив из 256 integer необходимо передать параметром, он используется для подсчета, а также в нем возвращаются счетчики символов.

[code h=207]program q183962;

{$APPTYPE CONSOLE}
{$ASMMODE intel}

uses
SysUtils;

{процедура формирует String из разных символов и массив из количеств символов}
procedure CharsCount(Src:String; var Tgt:String; var Cnt:array of Integer); stdcall; Assembler;
asm
sub esp, 256 {выделим буфер под разные символы в стеке}
mov edx, esp {адрес буфера разных символов}
push edi {сохраним регистры}
push esi
push ebx
mov esi, Src {адрес исходной строки}
mov edi, Cnt {адрес счетчиков}
mov ecx, 256 {предполагаем, что там 256 dword-ов}
xor eax, eax {0}
rep stosd {обнуляем}
mov edi, Cnt {адрес счетчиков}
mov ecx, [esi-4] {длина исходного string-а}
jcxz @FormString {обойдем пустую строку}
@CalcCharsLoop: {цикл по символам строки}
lodsb {очередной символ}
inc dword ptr [edi+eax*4] {считаем в массиве счетчиков}
loop @CalcCharsLoop {по всем символам}
@FormString: {формируем результат}
xor ebx, ebx {индекс для 256 значений счетчиков}
xor eax, eax {счетчик разных значений}
mov ecx, 256 {счетчик в буфере}
@FormStringLoop: {цикл формирования результата}
mov esi, [edi+ebx*4] {читаем счетчик}
test esi, esi {проверяем на 0}
jz @FormStringNext {если 0, то обходим}
mov [edx+eax], bl {для ненулевого сохранякм индекс, как код символа}
mov [edi+eax*4], esi {и количество, сохраняем с начала массива счетчиков}
inc eax {считаем разные значения}
@FormStringNext: {на следующий счетчик}
inc ebx {индекс следующего счетчика}
loop @FormStringLoop {по всем}
{сформируем string из массива разных символов}
mov ecx, eax {количество}
mov eax, Tgt {адрес string}
{edx = адресу массива символов!}
call SetString {формируем string}
pop ebx {восстанавливаем регистры}
pop esi
pop edi
add esp, 256 {убираем из стека буфер}
end;

Var
i, N: integer;
a: string;
Tgt: string;
Cnt: array[0..255] of integer;
work: string;
num: string;
begin;
{вводим строку}
write('enter string:');
readln(a);

{выполняем задание}
CharsCount(a, Tgt, Cnt);

{сформируем результат}
N:=Length(Tgt); {длина строки}
{строка разных символов}
work:='Target = ' + Tgt + char(10)+'Counts = ';
for i:=0 to N-1 do {и счетчики}
begin
Str(Cnt[i],num);
work:=work+num+',';
end;
SetLength(work,Length(work)-1); {уберем последнюю запятую}
{выводим результат}
write(work);
readln;
end.

[/code]

4) Предлагаю свое решение и этой задачи тоже.
Расчет факториала ведется, начиная с 2 и с учетом предыдущего значения.
Чем достигается значительное убыстрение выполнения.
Каждый член произведения используется в виде (i! - 1) / i!

[code h=207]program q183962b;

{$APPTYPE CONSOLE}
{$ASMMODE intel}

uses
SysUtils;

{процедура вычисляет требуемое выражение}
{результат - в стеке сопроцессора}
function CalcValue(n: Integer):double; Assembler;
asm
sub esp, 4 {здесь будет число 2...n}
mov dword ptr [esp], 2 {начинаем с 2}
finit {инициализация сопроцессора}
fld1 {1, для накопления окончательного выражения}
fld st {1, для накопления факториала}
@mainLoop: {цикл расчета}
cmp [esp], eax {проверим, дошли ли до конца, EAX = n}
ja @finish {конец расчета}
fimul dword ptr [esp] {считаем факториал, умножая предыдущее значение на очередное}
fld st {посчитаем числитель, как i!-1}
fld1 {1}
fsubp st(1), st {числитель}
fdiv st, st(1) {получим (i!-1)/i!}
fmulp st(2), st {накапливаем произведения с потерей промежуточного частного}
inc dword ptr [esp] {на следующий член}
jmp @mainLoop {пока не дойдем до n}
@finish: {конец}
fstp st {уберем из стека сопроцессора n!, теперь st = посчитанной величине!}
add esp, 4 {уберем двойное слово из стека процессора}
end;

Var
n, pos: integer;
d: double;
a: string;

begin
{вводим число}
write('Enter number: ');
readln(a);
Val (a, n, pos); {преобразуем в число}
if pos<>0 then {проверка на корректность числа}
Writeln ('Error at position ',pos,' : ',a[pos])
else
begin
if (n>1) and (n<=1000) then {проверка на диапозон}
begin
{выполняем задание}
d := CalcValue(n); {считаем}
Writeln ('Value = ', d:10:8); {выводим}
end
else
Writeln ('Number must be from 2 to 1000');
end;
readln;
end.[/code]
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
07.09.2011, 11:49
общий
Здравствуйте.
Добавил решение и второй (4-й) задачи
Выбирайте, какая программа Вам по душе
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Старший Модератор
31795
6196
08.09.2011, 11:13
общий
08.09.2011, 11:14
Переделал код под стек, (спасибо Игорю Витальевичу, подсказал)
[code h=150]program Project2;

{$APPTYPE CONSOLE}

uses
SysUtils;
type
tMass=array[0..255]of byte;
var
a:ShortString;
b:tMass;
c:integer;
procedure A001(a1:ShortString;var b1:tMass)stdcall;assembler;
asm
mov edx,[ebp+4*3]{edx указатель на массив В1}
mov eax,[ebp+4*2]{eax указатель на строку a1}
{сбрасываем значение счетчиков в массиве}
mov ecx,256{количество элементов в массиве}
push edx{запоминаем указатель на массив В1}
@@001:
mov byte ptr[edx],0{сбрасываем значение счетчика}
inc edx{следующее значение}
loop @@001{пока СХ больше нуля}
pop edx{востанавливаем значение указателя на массив В1}
{выполняем задание}
xor ecx,ecx{команда не нужна, т.к. после цикла там будет ноль, но всетаки}
xor ebx,ebx{сбрасываем значение регистра ЕВХ}
mov cl,[eax]{загружаем количество символов}
@@002:
inc eax{следующее значение}
mov bl,[eax]{считываем текущий символ}
inc byte ptr[ebx+edx]{увеличиваем значение счетчика соответсвующего коду символа}
loop @@002{пока не кончится введенная строка}
end;
begin
{вводим строку}
write('enter string:');
readln(a);
{выполняем задание}
a001(a,b);
{выводим результат}
for c:=0 to 255 do if b[c]>0 then write(chr(c):6,b[c]:4);
readln;
end.[/code]
Добавилось две строки:
mov edx,[ebp+4*3]
mov eax,[ebp+4*2]
При передаче параметром с помощью стека, используется указатель базы ЕВР, но в отличии от ЕВХ, этот работает со стеком. В командах есть цифры:
4- размер операнда в стеке;
3- второй передаваемый параметр;
2- первый передаваемый параметр;
такая последовательность из-за модели памяти stdcall, дальше для ознакомления:
1- сохраненное значение ЕВР;
0- адрес возврата из подпрограммы.
Вроде всё удачи!
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Форма ответа