Четвърта задача
- Краен срок:
- 15.01.2014 17:30
- Точки:
- 6
Срокът за предаване на решения е отминал
Inline assembler
Езиците от по-ниско ниво позволяват вграждането на Assembler. Например в Pascal можете да имате следния фрагмент:
begin
width := 320; {код на Pascal}
height := 200; {още код на Pascal}
asm
mov ax,13h ; x86 assembler
int 10h ; x86 assembler
end;
...
end;
Тъй като Ruby е език от много високо ниво самата идея да добавим подобна функционалност е в разрез с идеите, стоящи зад него. Затова пък ще можем да упражним създаване на DSL-и. И така:
Ще симулираме много малко подмножество на
x86 архитектурата.
Цялата памет, с която разполагаме, ще бъде 4 регистъра, в които ще могат да се
пазят целочислени стойности. Регистрите се казват ax
, bx
, cx
и dx
.
Преди изпълнението на програмата всеки от тях има стойност 0
. Освен
регистрите, разпогаме със следните инструкции:
-
mov
destination_register, sourcemov
има два аргумента. source е число или име на регистър, от който да бъде взета числовата стойност, а destination_register е името на регистъра, в който ще бъде записана числовата стойност. Примери:-
mov ax, 2
- записва в регистъраax
стойността2
, нещо катоax = 2
. -
mov bx, dx
- записва в регистъраbx
стойността, съдържаща се в регистъраdx
, нещо катоbx = dx
.
-
-
inc
destination_register, valueinc
прибавя value към стойността, съдържаща се в destination_register и запазва получената стойност в destination_register. value може да бъде число, име на регистър, от който да бъде взета числовата стойност, а може и да не се подава като аргумент, в който случай се използва стойността по-подразбиране –1
. Примери:-
inc ax, 3
- увеличава стойността, записана вax
с 3, нещо катоax += 3
. -
inc bx, dx
- увеличава стойността, записана вbx
със стойността, записана вdx
, нещо катоbx += dx
. -
inc cx
- увеличава стойността, записана вcx
с единица, нещо катоcx += 1
.
-
-
dec
destination_register, valuedec
изважда value от стойността, съдържаща се в destination_register и запазва получената стойност в destination_register. value може да бъде число, име на регистър, от който да бъде взета числовата стойност, а може и да не се подава като аргумент, в който случай се използва стойността по-подразбиране –1
. Примери:-
dec ax, 3
- намалява стойността, записана вax
с 3, нещо катоax -= 3
. -
dec bx, dx
- намалява стойността, записана вbx
със стойността, записана вdx
, нещо катоbx -= dx
. -
dec cx
- намалява стойността, записана вcx
с единица, нещо катоcx -= 1
.
-
-
cmp
register, valuecmp
сравнява стойността, съдържаща се в register с value, където value може да бъде число, или име на регистър, от който да бъде взета числовата стойност. Резултатът от сравнението трябва да бъде запомнен някъде, вие избирате къде, за да може да бъде използван по-нататък. Резултатът се определя по следния начин:- нула, ако register == value
- отрицателно число, ако register < value
- положително число, ако register > value
Прилича ви на
<=>
, нали? Пример:mov ax, 3 mov bx, 2 cmp ax, 3 ; тук резултатът е 0 cmp ax, 10 ; тук резултатът е отрицателен cmp ax, bx ; тук резултатът е положителен
-
label
label_namelabel
служи, за да постави маркер в програмата, към който да можем да се върнем по-късно с някакъв вид jump. -
jmp
whereБезусловен преход към инструкция. where може да бъде име на етикет, зададен с
label
, или число – пореден номер на инструкция. Номерацията на инструкциите е от0
, катоlabel
не се брои за инструкция. Примери:mov ax, 3 ; инструкция 0 inc ax ; инструкция 1 dex ax ; инструкция 2 jmp 1 ; след този jmp изпълнението преминава на инструкция 1 mov ax, 3 ; инструкция 0 mov bx, 3 ; инструкция 1 label jump_here inc ax ; инструкция 2 dex ax ; инструкция 3 jmp jump_here ; след този jmp изпълнението преминава на инструкция 2
-
je
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
е нула. Ако резултатът от последнияcmp
не е нула, просто се преминава на следващата инструкция. Пример:mov ax, 2 cmp ax, 2 je finish mov bx, 3 ; тази инструкция никога не се изпълнява label finish mov ax, 2 cmp ax, 3 je finish mov bx, 3 ; тази инструкция ще се изпълни, понеже 2 не е равно на 3 label finish
-
jne
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
не е нула. Ако резултатът от последнияcmp
е нула, просто се преминава на следващата инструкция. -
jl
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
е отрицателен. Ако резултатът от последнияcmp
е нула, или е положителен, просто се преминава на следващата инструкция. -
jle
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
е отрицателен или е нула. Ако резултатът от последнияcmp
е положителен, просто се преминава на следващата инструкция. -
jg
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
е положителен. Ако резултатът от последнияcmp
е нула, или е отрицателен, просто се преминава на следващата инструкция. -
jge
whereПреход към инструкция, както при
jmp
, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmp
е положителен или е нула. Ако резултатът от последнияcmp
е отрицателен, просто се преминава на следващата инструкция.
Създайте модул Asm
, имащ метод Asm.asm
. Този метод трябва да приема блок с
последователност от инструкции от описаните по-горе и да връща масив, съдържащ
стойностите на регистрите ax
, bx
, cx
и dx
в този ред.
Пример за завършена програма, която намира най-големия общ делител на две числа (a.k.a. „Алгоритъм на Евклид“):
Asm.asm do
mov ax, 40
mov bx, 32
label cycle
cmp ax, bx
je finish
jl asmaller
dec ax, bx
jmp cycle
label asmaller
dec bx, ax
jmp cycle
label finish
end # => [8, 8, 0, 0]
Ограничения
Тази задача има следните ограничения:
- Най-много 90 символа на ред
- Най-много 8 реда на метод
- Най-много 3 нива на влагане
Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.
Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop
редовно, докато пишете кода. Ако смятате, че rubocop
греши по някакъв начин,
пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като
private gist. Ако пуснете кода си публично (например във форумите), ще смятаме
това за преписване.