Четвърта задача
- Краен срок:
- 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. Освен
регистрите, разпогаме със следните инструкции:
-
movdestination_register, sourcemovима два аргумента. source е число или име на регистър, от който да бъде взета числовата стойност, а destination_register е името на регистъра, в който ще бъде записана числовата стойност. Примери:-
mov ax, 2- записва в регистъраaxстойността2, нещо катоax = 2. -
mov bx, dx- записва в регистъраbxстойността, съдържаща се в регистъраdx, нещо катоbx = dx.
-
-
incdestination_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.
-
-
decdestination_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.
-
-
cmpregister, 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 ; тук резултатът е положителен -
labellabel_namelabelслужи, за да постави маркер в програмата, към който да можем да се върнем по-късно с някакъв вид jump. -
jmpwhereБезусловен преход към инструкция. 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 -
jewhereПреход към инструкция, както при
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 -
jnewhereПреход към инструкция, както при
jmp, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmpне е нула. Ако резултатът от последнияcmpе нула, просто се преминава на следващата инструкция. -
jlwhereПреход към инструкция, както при
jmp, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmpе отрицателен. Ако резултатът от последнияcmpе нула, или е положителен, просто се преминава на следващата инструкция. -
jlewhereПреход към инструкция, както при
jmp, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmpе отрицателен или е нула. Ако резултатът от последнияcmpе положителен, просто се преминава на следващата инструкция. -
jgwhereПреход към инструкция, както при
jmp, но този път преходът трябва да се случи само ако резултатът от последно изпълненияcmpе положителен. Ако резултатът от последнияcmpе нула, или е отрицателен, просто се преминава на следващата инструкция. -
jgewhereПреход към инструкция, както при
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. Ако пуснете кода си публично (например във форумите), ще смятаме
това за преписване.
