Dupa cateva saptamani de pauza, inginerii echipelor de Formula 1 s-au intors la treaba. Fiind foarte multumiti de munca voastra anterioara, ei vor sa ii ajutati si acum.
Tema este formata din 4 exercitii independente si un exercitiu bonus. Fiecare task consta în implementarea unei sau mai multor functii in limbaj de asamblare. Implementarea se realizeaza in fisierele puse la dispozitie pentru fiecare exercitiu.
Inginerul sef de la Ferrari trebuie sa le transmita celorlalti coechipieri mesaje criptate pentru a afla date despre masina. El vrea să folosească simple cipher pentru a transmite mesajele.
Acest algoritm de criptare presupune shiftarea la dreapta în cadrul alfabetului a fiecărui caracter de un anumit număr de ori. De exemplu, textul ANABANANA se transformă în BOBCBOBOB când pasul este 1. Astfel, o criptare cu pasul 26 nu modifică litera, întrucât alfabetul englez are 26 de caractere.
Pentru acest task va trebui să implementați în fișierul simple.asm funcția simple(), care criptează un string folosind metoda descrisă mai sus.
Antetul funcției este:
void simple(int n, char* plain, char* enc_string, int step)
Semnificația argumentelor este:
Intre timp, mecanicii de la RedBull incearca sa eficientizeze sistemul pe care il folosesc prin sortarea proceselor care ruleaza pe masina lor.
Prin intermediul acestui task se doreste aprofundarea lucrului cu structuri.
Se da structura simplificata a unui proces:
struct proc{ short pid; char prio; short time; };
Pentru aceasta parte a task-ului aveti de implementat functia sort_procs() in fisierul sort-procs.asm care va simula sortarea tuturor proceselor active in momentul curent.
Pentru a intelege mai bine cum functioneaza un proces, vom explica mai jos ce inseamna fiecare field al structurii proc:
Pentru a sorta procesele, stabilim urmatoarele reguli:
Sortarea se va face in place, adica vectorul procs prezentat mai jos va trebui, in urma apelului functiei, sa fie sortat. Antetul functiei este:
void sort_procs(struct proc *procs, int len);
Semnificatia argumentelor este:
- procs adresa de inceput a vectorului de procese - len numarul de procese aflate in sistem
In continuarea exercitiului 1, acum trebuie sa implementati functia run_procs() in fisierul run_procs.asm care va calcula intr-un mod simplificat timpul mediu de rulare pentru fiecare prioritate, adica va trebui sa calculati suma cuantelor de timp pentru o prioritate si apoi sa o impartiti la numarul de procese care au acea prioritate.
Pentru acest task va trebui sa declarati o structura avg,
struct avg{ short quo; short remain; };
unde:
Va trebui sa puneti valorile obtinute in vectorul avg_out prezentat mai jos, pe prima pozitie aflandu-se rezultatul pentru prioritatea 1, iar pe ultima rezultatul pentru prioritatea 5. Antetul functiei este:
void run_procs(struct proc* procs, int len, struct avg *avg_out);
Semnificatia argumentelor este:
In timp ce inginerii de la Ferrari folosec simple_cipher, echipa Mercedes se intoarce in timp si alege sa foloseasca Enigma fiind convinsi ca rivalii lor de la Ferrari nu se vor descurca sa o decripteze.
Enigma machine este o masina de criptare complexa folosita de germani in al 2-lea Razboi Mondial pentru a encoda mesaje. Aceasta este alcatuita din mai multe componente:
Exemplu : Pentru Rotor1 avem A B C D E F G H I J K L M N O P Q R S T U V W X Y Z E K M F L G D Q V Z N T O W Y H X U S P A I B R C J, cu notch pe Q.
Vrem sa construim logica din spatele masinii enigma pentru a putea encripta si decripta mesaje.
Configuratia masinii este stocata intr-o matrice config[10][26] astfel :
Pentru rezolvarea acestui task aveti de implementat functi rotate_x_positions in fisierul enigma.asm. Antetul functiei este uramtorul:
void rotate_x_positions(int x, int rotor, char config[10][26], int forward)
unde:
Se va modifica matricea config conform parametrului rotor.
Exemplu: Daca primele 2 linii din matricea config contin configuratia primului rotor, de exemplu A B C D E F G H I J K L M N O P Q R S T U V W X Y Z E K M F L G D Q V Z N T O W Y H X U S P A I B R C J si aplicam functia astfel: rotate_x_positions(3, 0, config, 0), atunci se modifica aceste 2 linii in D E F G H I J K L M N O P Q R S T U V W X Y Z A B C F L G D Q V Z N T O W Y H X U S P A I B R C J E K M
Pentru rezolvarea acestui task aveti de implementat functia enigma in fisierul enigma.asm. Antetul functiei este uramtorul:
void enigma(char *plain, char key[3], char notches[3], char config[10][26], char *enc)
unde:
Atentie : inainte de citirea caracterului din plain, trebuie rotit al 3-lea rotor cu 1 pozitie (shiftare la stanga) si incrementata pozitia initiala a acestuia (alfabetul este circular deci daca incrementam Z acesta devine A). Daca pozitia curenta INAINTE DE ROTIRE a rotorului este egala cu notch-ul acestuia, atunci vom roti si rotorul din stanga acestuia cu 1 pozitie (acest lucru se va realiza pentru toti rotorii mai putin primul).
Exemplu 1 : key = “QWE”, notches = “AAE” ⇒ key = “QXF”
Exemplu 2 : key = “QWE”, notches = “AWE” ⇒ key = “RXF”
Pentru ca se plictisesc in garaj, mecanicii McLaren au decis ca tura asta sa nu mai joace UNO, ci sa joace dame.
Dorim sa simulam jocul de checkers (dame). Avem o matrice de 8×8 ce reprezinta suprafata de joc. Dandu-se pozitie unei dame pe suprafata de joc, dorim sa calculam noi pozitii pe care poate ajunge aceasta.
Pentru aceast task trebuie implementata functia checkers din fișierul checkers.asm. Antetul functiei este uramtorul:
void checkers(int x, int y, char table[8][8])
unde:
Nu vor exista coliziuni intre piesele de joc. Presupunem ca pe tabla de joc se afla o singura piesa.
Piesele nu pot sa iasa din suprafata de joc.
Dorim sa optimizam reprezentarea suprafetei de joc de la Task 4 astfel incat sa ocupam mai putina memorie si calculele pozitiilor sa se faca mai rapid.
O posibila optimizare este reprezentata de notiunea de Bitboard. Bitboard reprezinta o structura de date binara in care fiecare bit reprezinta prezenta sau absenta unei piese pe o anumita pozitie a tablei de joc. De obicei, in C, pentru a reprezenta un bitboard, se foloseste o variabila de tip unsigned long long.
unsigned long lon este un tip de date ce contine 64 biti (8 octeti). O table de joc de dimensiunea 8×8 poate fi reprezentata intuitiv cu o singura variabila de acest tip, grupand, la nivel logic, cate 8 biti pentru fiecare linie din suprafata. Din pacate insa, noi nu avem acces la registri pe 64 biti, astfel incat trebuie folositi 2 registri pentru reprezentarea suprafetei de joc.
Pentru aceast task trebuie implementata functia bonus din fișierul bonus.asm. Antetul functiei este uramtorul:
void bonus(int x, int y, int board[2])
unde:
În schelet este inclus şi checker-ul, împreună cu testele folosite de acesta. Pentru a executa toate testele, se poate executa direct scriptul `checker.sh` din rădăcina temei:
python3 local_checker.py --all
Pentru a avea acces la output-ul functiilor, trebuie rulat checker-ul in cadrul folderului task-ului dorit:
make checher ./checker
Pentru a testa task-uri individual, folosiți:
python3 local_checker.py -t <număr_task>
Pentru a scrie rezolvarea unui task, intrați în directorul asociat task-ului respectiv și scrieți cod în fișierele în limbaj de asamblare indicate în enunț. NU modificați alte fișiere C, script-uri etc!
Pentru matricea alocată static: 1 2 3 4 5 6 7 8 9 Avem de fapt în memorie un șir continuu de forma: 1 2 3 4 5 6 7 8 9
Temele vor trebui încărcate pe platforma Moodle, in cadrul assingment-ului Tema 2 și vor fi testate automat.
python3 local_checker.py --zip
pentru a crea arhiva.
Punctajul final acordat pe o temă este compus din:
Scheletul şi checker-ul sunt disponibile pe repository-ul de IOCLA de pe GitLab.