Консультация № 183961
04.09.2011, 23:45
73.74 руб.
0 1 1
Уважаемые эксперты! Пожалуйста, ответьте на вопрос:

Нужно реализовать программы на Ассемблере (как вставка в Делфи):
1. Реализуйте с помощью логических симметричное отображение младшего байта слова на старший байт с потерей исходных значений.
2. Представить обыкновенную дробь как запись с полями «Числитель» и «Знаменатель» и реализовать арифметические операции с дробями.

Помогите, пожалуйста. Желательно хотя бы с минимальным пояснением кода.
Спасибо.

Обсуждение

давно
Посетитель
7438
7205
05.09.2011, 23:28
общий
это ответ
Здравствуйте, Посетитель - 380267!
1) Доведенное до логического конца ответ эксперта Кривенко Евгения Владимировича в вопросе№ 183949
Код:
{симметричное отображение младшего байта слова на старший байт}
{параметр - двойное слово в EAX, но используется только младшее слово}
{результат - в EAX (младший бит байта al стал старшим битом байта ah, и т.д.)}
function simm(x:integer): integer; assembler;
asm
push ecx {сохраним регистр}
mov ecx,8 {число бит в байте}
@bit_loop: {цикл по битам}
ror al,1 {младший бит в старший}
shl ax,1 {старший бит младшего байта в младший бит старшего байта}
shr al,1 {вернем следующий бит на место}
loop @bit_loop {по всем битам байта}
pop ecx
end;


Вызов:
Код:
var
i: integer;

begin
i:=$1275;
i:=simm(i);
end;

В результате получим i = 0ae00h

2) Дроби
[code h=207]{дробь - запись из двух полей: числитель (x) и знаменатель(y)}
type
fraction = record
x, y: integer;
end;

{нужно для Lazarus-а, для Delphi, скорее всего, нет}
{$ASMMODE intel}

{функция вычисления наибольшего общего делителя}
function GCD(A, B: integer): integer; assembler;
asm
push ecx {сохраним регистр ECX}
mov ecx, edx {второе число в регистре EDX , при делении будет портиться, пусть будет в ECX}
test eax, eax {возьмем модуль обоих чисел}
jns @test_neg_B
neg eax
@test_neg_B:
test ecx, ecx
jns @GCD_continue
neg ecx
@GCD_continue: {цикл расчета НОД-а}
test eax, eax {пока одно из чисел не станет равным 0}
jz @GCD_ret
jcxz @GCD_ret
cmp eax, ecx {будем искать остаток от деления большего на меньшее}
jb @GCD_A_lt_B {A < B}
{A >= B}
xor edx, edx {числа положительные, можно смело обнулять}
div ecx {A / B}
mov eax, edx {EAX = A = A mod B, ECX = B}
jmp @GCD_continue
@GCD_A_lt_B: {A < B}
xchg eax, ecx {EAX = B, ECX = A}
xor edx, edx
div ecx {B / A}
mov eax, edx {EAX = B mod A}
xchg eax, ecx {ECX = B mod A, EAX = A}
jmp @GCD_continue
@GCD_ret:
add eax, ecx {EAX = A + B, или то, или другое, т.к. второе будет равно 0}
pop ecx
end;

{процедура деления дроби на НОД}
{EAX - НОД, EBX - знаменатель, EDI - числитель, ECX - адрес дроби-результата}

procedure Cut_f(); assembler;
asm
XCHG EAX, EBX {EAX = знаменатель, EBX = НОД}
CDQ {знаковое расширение до DX:AX}
IDIV EBX {EAX = знаменатель / НОД}
XCHG EAX, EDI {EAX = числитель, EDI = знаменатель / НОД}
CDQ {знаковое расширение до DX:AX}
IDIV EBX {EAX = числитель / НОД}
{пусть отрицательное число будет только в числителе}
TEST EDI, EDI {знак знаменателя}
jns @SET_NUMS
NEG EAX {умножим на -1}
NEG EDI
@SET_NUMS: {сохраним результат}
MOV [ECX].fraction.x, EAX
MOV [ECX].fraction.y, EDI
end;

{процедура складывания двух дробей}
{параметры - адреса складаемых и результата}
{правило вычисления для x1/y1 + x2/y2}
{(первая строка - числитель, вторая - знаменатель):}
{(x1*y2+x2*y1) div GCD(x1*y2+x2*y1, y1*y2)}
{(y1*y2) div GCD(x1*y2+x2*y1, y1*y2)}
procedure Add_f(var fr1, fr2, res:fraction);assembler;
asm
PUSH EBX {сохраним регистры}
PUSH ESI
PUSH EDI

MOV ESI, fr1 {адрес первой дроби}
MOV EDI, fr2 {второй}

MOV EAX, [ESI].fraction.y
IMUL [EDI].fraction.y
MOV EBX, EAX {EBX = y1*y2}

MOV EAX, [ESI].fraction.x
IMUL [EDI].fraction.y
XCHG EAX, EDI {EDI = x1*y2, EAX = адресу второй дроби}
MOV EAX, [EAX].fraction.x
MUL [ESI].fraction.y
ADD EDI, EAX {EDI = x1*y2 + x2*y1}

MOV EAX, EDI {числитель}
MOV EDX, EBX {знаменатель}
call GCD {EAX = НОД}

call Cut_f {делим на НОД и сохраняем}

POP EDI
POP ESI
POP EBX
end;

{процедура вычитания двух дробей}
{параметры - адреса уменьшаемого, вычитаемого и результата}
{правило вычисления для x1/y1 - x2/y2}
{(первая строка - числитель, вторая - знаменатель):}
{(x1*y2-x2*y1) div GCD(x1*y2-x2*y1, y1*y2)}
{(y1*y2) div GCD(x1*y2-x2*y1, y1*y2)}
procedure Sub_f(var fr1, fr2, res:fraction);assembler;
asm
PUSH EBX
PUSH ESI
PUSH EDI

MOV ESI, fr1
MOV EDI, fr2

MOV EAX, [ESI].fraction.y
IMUL [EDI].fraction.y
MOV EBX, EAX

MOV EAX, [ESI].fraction.x
IMUL [EDI].fraction.y
XCHG EAX, EDI
MOV EAX, [EAX].fraction.x
IMUL [ESI].fraction.y
SUB EDI, EAX {аналогично сумме, отличие только здесь!}

MOV EAX, EDI
MOV EDX, EBX
call GCD

call Cut_f

POP EDI
POP ESI
POP EBX
end;


{процедура умножения двух дробей}
{параметры - адреса множителей и результата}
{правило вычисления для x1/y1 * x2/y2}
{(первая строка - числитель, вторая - знаменатель):}
{(x1*x2) div GCD(x1*x2, y1*y2)}
{(y1*y2) div GCD(x1*x2, y1*y2)}
procedure Mul_f(var fr1, fr2, res:fraction);assembler;
asm
PUSH EBX
PUSH ESI
PUSH EDI

MOV ESI, fr1
MOV EDI, fr2

MOV EAX, [ESI].fraction.y
IMUL [EDI].fraction.y
MOV EBX, EAX {EBX = y1*y2}
MOV EAX, [ESI].fraction.x
IMUL [EDI].fraction.x
MOV EDI, EAX {EAX = EDI = x1*x2}

MOV EDX, EBX {второй множитель, первый уже в EAX}
call GCD {НОД}

call Cut_f {делим на НОД и сохраняем}

POP EDI
POP ESI
POP EBX
end;

{процедура деления двух дробей}
{параметры - адреса делимого, делителя и результата}
{правило вычисления для x1/y1 * x2/y2}
{(первая строка - числитель, вторая - знаменатель):}
{(x1*y2) div GCD(x1*y2, y1*x2)}
{(y1*x2) div GCD(x1*y2, y1*x2)}
procedure Div_f(var fr1, fr2, res:fraction);assembler;
asm
PUSH EBX
PUSH ESI
PUSH EDI

MOV ESI, fr1
MOV EDI, fr2

MOV EAX, [ESI].fraction.y
IMUL [EDI].fraction.x
MOV EBX, EAX {EBX = y1*x2}
MOV EAX, [ESI].fraction.x
IMUL [EDI].fraction.y
MOV EDI, EAX {EDI = x1*y2}

MOV EDX, EBX {делитель, делимое уже в EAX}

call GCD {НОД}

call Cut_f {делим на НОД и сохраняем}

POP EDI
POP ESI
POP EBX
end;[/code]

Вызываем так:
Код:
Var
f1, f2, f3: fraction;

begin
f1.x:=3;
f1.y:=4;
f2.x:=-9;
f2.y:=16;

Div_f(f1, f2, f3);
Mul_f(f1, f2, f3);
Add_f(f1, f2, f3);
Sub_f(f1, f2, f3);
end;
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа