Verified Commit 0f6837a8 authored by Roman Lacko's avatar Roman Lacko
Browse files

Add hw03 template

parent 05aa548b
Loading
Loading
Loading
Loading

hw03/CMakeLists.txt

0 → 100644
+71 −0
Original line number Diff line number Diff line
cmake_minimum_required(VERSION 3.18)

# Project configuration, sources and targets
project(hw03)
set(SOURCES "cpu.c" )
set(SOURCES_LIB ${SOURCES})
set(EXECUTABLE cpu)
set(EXECUTABLE_COMPILER compiler)

# Executable
add_executable(${EXECUTABLE} ${SOURCES} main.c)
add_executable(${EXECUTABLE_COMPILER} compiler.c)

# CUT Test files
file(GLOB TESTS_CUT_GLOB "tests-cut/*.c")

# CUT Test utility files
file(GLOB TESTS_CUT_LIBS_GLOB "tests-cut/libs/*.c")

foreach (file_path ${TESTS_GLOB})
    message("Found test: " ${file_path})
endforeach ()
set(TEST_SOURCES
    ${TESTS_CUT_LIBS_GLOB}
    ${TESTS_CUT_GLOB}
    ${SOURCES_LIB}
)

# CUT test target
set(EXECUTABLE_TESTS cpu-tests-cut)

# CUT tests executable
add_definitions(-DCUT)
add_executable(${EXECUTABLE_TESTS} ${TEST_SOURCES})

# CLI Test Files
file(GLOB TESTS_CLI_SH "tests-cli/*.sh")
file(GLOB TESTS_CLI_DATA "tests-cli/data/*")
file(COPY ${TESTS_CLI_SH} "tests-cli/cpu-tests-cli" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${TESTS_CLI_DATA} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data)

# For compiler
target_compile_definitions(compiler PUBLIC -D_POSIX_C_SOURCE=200809L)

# For bonus
# to enable the first bonus (cmp, jmp, ...) uncomment this line:
#target_compile_definitions(cpu PUBLIC -DBONUS_JMP)

# to enable the second bonus (call, ret) uncomment this line:
#target_compile_definitions(cpu PUBLIC -DBONUS_CALL)

# -------------------------------------------------------------------
# DO NOT EDIT LINES BELOW lest you break it

# Configure compiler warnings
if (CMAKE_C_COMPILER_ID MATCHES Clang OR ${CMAKE_C_COMPILER_ID} STREQUAL GNU)
    # using regular Clang, AppleClang or GCC
    # Strongly suggested: neable -Werror
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -pedantic")
elseif (${CMAKE_C_COMPILER_ID} STREQUAL MSVC)
    # using Visual Studio C++
    target_compile_definitions(${EXECUTABLE} PRIVATE _CRT_SECURE_NO_DEPRECATE)
    target_compile_definitions(${EXECUTABLE_TESTS} PRIVATE _CRT_SECURE_NO_DEPRECATE)
    set(CMAKE_CXX_FLAGS "/permissive- /W4 /EHsc")
endif ()

if (MINGW)
    target_compile_definitions(${EXECUTABLE} PRIVATE __USE_MINGW_ANSI_STDIO=1)
    target_compile_definitions(${EXECUTABLE_TESTS} PRIVATE _CRT_SECURE_NO_DEPRECATE)
endif ()
+32 −0
Original line number Diff line number Diff line
; digit sum with stack

in c

movr d 10

digit:
	push c
	pop a
	div d
	mul d
	swap a c
	sub c
	push a
	swap a c
	div d
	swap a c
	inc b
	loop digit

movr a 0
push b
pop c

sum:
	pop b
	add b
	dec c
	loop sum

out a
halt
+16 −0
Original line number Diff line number Diff line
; 10 even numbers

movr c 10
movr a 2
movr b 2
movr d 32

start:
	out a
	put d
	add b
	dec c
	loop start
	movr b 10
	put b
	halt
+20 −0
Original line number Diff line number Diff line
; Fibonacci

movr c 10
movr a 1
movr b 1
movr d 32
out a
put d

start:
	out a
	put d
	push a
	add b
	pop b
	dec c
	loop start
	movr b 10
	put b
	halt

hw03/assignment.ad

0 → 100644
+486 −0
Original line number Diff line number Diff line
---
title: "HW03: Emulátor 32bitového CPU"
layout: "homework"
list-of-files: ["cpu.h", "cpu.c"]
solution-path: /home/kontr/pb071/hw03/cpu
deadline-early: 2024-04-10 24:00
deadline-final: 2024-04-17 24:00
publish: ~
authors:
 - Desana Daxnerová
editors:
 - xbiersk1
 - xhrbace1
---

== Představení úkolu

Představme si jednoduchý procesor, který zpracovává aritmetické operace.
Náš procesor má k dispozici paměť s instrukcemi, které zpracovává,
a http://cs.wikipedia.org/wiki/Z%C3%A1sobn%C3%ADk_(datov%C3%A1_struktura)[zásobník]
(_stack_), který se nachází na konci této paměti. Navíc může ukládat mezivýsledky
do malé a rychlé paměti – registrů.

Emulátor procesoru postupně čte jednotlivé instrukce z paměti a vykonává je.
Naším cílem bude napsat program, který načte soubor s instrukcemi a vytvoří
takovýto emulátor, který je vykoná.

Odevzdávat budete pouze soubory `cpu.h` a `cpu.c`. V kostře naleznete
již naimplementovaný `main.c`, který otevře vstupní soubor zadaný pomocí
argumentu programu a volá vámi dodané funkce. Předpokládaný vstupní soubor
s instrukcemi je binární, proto máte v kostře i zdrojový kód programu `compiler.c`,
který můžete použít k převodu textových instrukcí (assembleru) do binární podoby.

== Zadání

Naimplementujte funkce z kostry úkolu, které emulují jednoduchý procesor.
Přehled jednotlivých funkcí a jejich popis najdete v kapitole <<requirements>>.

Seznam požadovaných instrukcí a detaily ke vstupním souboru najdete
v kapitole <<instructions>>.

[[requirements]]
== Požadavky

.`NULL` parametr
[WARNING]
====
Pokud není explicitně uvedeno jinak, všechny funkce přebírající argument
ukazatelem předpokládají, že tyto ukazatele nejsou `NULL`.
**Tuto skutečnost ověřujte makrem `assert`!**
====

* Číselné atributy ukládejte do proměnných typu `int32_t`, který najdete
  v hlavičkovém souboru `stdint.h`.
* Nevyužité atributy všech struktur udržujte na vhodně zvolené hodnotě,
  tj. čísle 0 nebo ukazateli `NULL`.
* Pro dvojici hlavičkového (`.h`) a implementačního (`.c`) souboru platí,
  že pomocí `.h` zveřejňujete funkce poskytované v `.c`. Jinými slovy, hlavičkový
  soubor udává veřejné rozhraní implementačního souboru. V `.c` samozřejmě můžete
  (a měli byste) mít jiné pomocné funkce, ty ale už do `.h` nedoplňujte
  a označte je klíčovým slovem `static` (viz příklad níže).
* Dodržujte požadované rozhraní hlavičkového souboru. Smí obsahovat jen struktury
  a funkce specifikované v zadání. **Některé testy se kompilují s námi dodanými
  hlavičkovými soubory.** Pokud byste tedy upravili hlavičky funkcí nebo přidali
  nějaké vlastní deklarace, kód se vůbec nemusí zkompilovat!


[source,c]
----
// Somewhere in a `.c` file...
static int helper_function(void);
...
static int helper_function(void)
{
    return 42;
}
----

=== Typ `struct cpu`

Reprezentuje 32bitový procesor. V rámci tohoto úkolu bude implementován
jako tzv. *opaque struct*, což znamená, že ve veřejném rozhraní (hlavičkovém
souboru) se nachází pouze deklarace struktury. Její atributy jsou uvedeny až
v implementačním souboru, a tak k nim nelze zvenčí přistupovat bez použití
speciálních funkcí.

Náš procesor by měl obsahovat následující části:

 * celočíselné registry `A`, `B`, `C`, `D` sloužící k ukládání mezivýpočtů,
 * registr `status` typu `enum cpu_status` popisující stav procesoru
   (viz sekce <<status_codes>>),
 * registr obsahující aktuální počet uložených hodnot v zásobníku,
 * registr obsahující index instrukce, která má být vykonaná v následujícím
   kroku výpočtu,
 * ukazatel na paměť emulovaného programu,
 * ukazatele do paměti emulovaného programu, které vymezují prostor,
   ve kterém se v paměti nachází zásobník.

=== Struktura paměti

[NOTE]
====
Abychom odlišili kontext paměti programu a paměti emulátoru, budeme _adresou_
nazývat hodnotu proměnné typu ukazatel v jazyce C a _indexem_ (který je číslo,
ne ukazatel) budeme nazývat adresu buněk v paměti emulovaného programu. Jedna
buňka emulované paměti má velikost jako `int32_t` v reálné paměti.
====

Instrukce programu se nachází na začátku paměti. Zásobník se nachází na konci
a plnit se bude směrem od konce paměti k začátku. Paměť mezi instrukcemi
a zásobníkem (pokud nějaká existuje) musí být vynulovaná.

_Indexem instrukce_ rozumíme počet buněk typu `int32_t`, které se nachází před ní.
Například, pokud program tvoří dvě instrukce a první má jeden operand,
pak rozložení indexů bude:

 * index 0: první instrukce

 * index 1: operand první instrukce

 * index 2: druhá instrukce

=== Vizualizace paměti

image::memory_layout.svg[]

=== Funkce na práci s procesorem

Úkol spočívá převážně v implementaci následujících funkcí. Jejich hlavičky se
musí nacházet v `cpu.h`. Abyste strukturu hlavičkových souborů lépe chytili
do ruky, necháváme jejich doplnění na vás.

[source,c]
----
int32_t* cpu_create_memory(FILE *program, size_t stack_capacity, int32_t **stack_bottom);
----

Funkce přečte binární program pro procesor ze souboru `program` (například pomocí
funkce `fgetc(3)` z knihovny `stdio.h` (http://www.cplusplus.com/reference/cstdio/fgetc/[dokumentace]))
a uloží ho do bloku paměti *P*, kterou naalokuje. Soubor se do paměti zkopíruje až po `EOF`.

*P* musí za instrukcemi obsahovat dostatek volného místa pro zásobník
velikosti `stack_capacity` (počet `int32_t` buněk, ne bajtů). Na adresu, na kterou
ukazuje `stack_bottom` (tj. do `pass:[*]stack_bottom`) funkce uloží adresu posledního
prvku (typu `int32_t`) v *P*, se kterým je ješte možné pracovat. Vrátí ukazatel
na začátek *P*, nebo `NULL` při chybě. Za chybu se také považuje případ,
kdy počet bajtů ve vstupním souboru není násobkem velikosti typu `int32_t`.

Velikost vstupu nemusí být dopředu známa, proto je třeba paměť dle potřeby zvětšovat.
Aby nebyla alokace paměti zbytečně neefektivní, musí tato funkce alokovat paměť
po blocích velikosti 4 KiB.

CAUTION: Soubor můžete přečíst jen jednou. **Nepředpokládejte**, že
`program` reprezentuje regulární soubor, tj. funkce `ftell(3)`
a `fseek(3)` nemusí na `program` dávat smysl.

[source,c]
----
struct cpu *cpu_create(int32_t *memory, int32_t *stack_bottom, size_t stack_capacity);
----

Funkce inicializuje strukturu `cpu`. Parametr `memory` ukazuje na paměť
emulovaného programu, který přečetla funkce `cpu_create_memory()`.

Atribut `stack_bottom` je parametr, který se získá voláním
`cpu_create_memory(..., &stack_bottom)`. Je v pořádku, pokud není mezi instrukcemi
a zásobníkem volné místo.

Funkce vrací ukazatel na inicializovanou strukturu `cpu`, který lze předat
jako parametr do následujících funkcí.

[source,c]
----
int32_t cpu_get_register(struct cpu *cpu, enum cpu_register reg);
----

Vrátí hodnotu registru (`A` až `D`).
Validitu parametru `reg` ověřte pomocí makra `assert`.

[source,c]
----
void cpu_set_register(struct cpu *cpu, enum cpu_register reg, int32_t value);
----

Nastaví registr (`A` až `D`) na hodnotu `value`.
Validitu parametru `reg` ověřte pomocí makra `assert`.

[source,c]
----
enum cpu_status cpu_get_status(struct cpu *cpu);
----

Vrátí stavový kód procesoru.

[source,c]
----
int32_t cpu_get_stack_size(struct cpu *cpu);
----

Vrátí aktuální velikost zásobníku.

[source,c]
----
void cpu_destroy(struct cpu *cpu);
----

Uvolní zdroje procesoru a nastaví ukazatele ve struktuře na `NULL`. Také vynuluje
všechny registry.

[source,c]
----
void cpu_reset(struct cpu *cpu);
----

Vynuluje všechny registry (včetně `status`) a vyprázdní a vynuluje zásobník.
Nedealokuje žádnou paměť.

NOTE: Pro `cpu_step()` a `cpu_run()` platí, že dokud je stavový kód procesoru
jiný, než `CPU_OK`, vrátí 0 a nedělají nic jiného.

[source,c]
----
int cpu_step(struct cpu *cpu);
----

Vykoná jednu instrukci procesoru. Pokud je úspěšná, vrátí nenulový kód,
jinak vrátí 0.

[source,c]
----
long long cpu_run(struct cpu *cpu, size_t steps);
----

Vykoná `steps` instrukcí. Vrátí -K, pokud se procesor dostal vykonáním K kroků
do chybového stavu, jinak vrátí skutečný počet vykonaných instrukcí.
Ten může být menší než `steps`, pokud došlo k vykonání instrukce `halt`.

Například pro nasledující program:

----
movr C 42 ; Make loop jump.
loop -112 ; Jump to an invalid address.
----

Funkce `cpu_run` nejdřív úspěšně vykoná `movr`, potom úspěšně vykoná
`loop`, následně se při vykonávání nasledující (neexistující, ale to
nevadí) "instrukce" stane chyba kvůli záporné hodnotě
`instruction_pointer` – dohromady 3 kroky. Funkce tedy vrátí -3.

[[instructions]]
== Instrukce

Instrukce jsou v binárním souboru, stejně jako v paměti programu, reprezentované
jako 32bitové čísla. Můžou mít (32bitové) parametry typu `REG` (číslo
registu 0 (`A`) až 3 (`D`)), `INDEX` (index intrukce) a `NUM` (celé číslo).
Endianita instrukcí a operandů je _little-endian_.

[[status_codes]]
=== Stavové kódy

Instrukce můžou v jistých případech nastavovat stavový kód. V kostře se nachází
`enum cpu_status` s následujícími hodnotami (v pořadí):

* `CPU_OK`,
* `CPU_HALTED`,
* `CPU_ILLEGAL_INSTRUCTION`,
* `CPU_ILLEGAL_OPERAND`,
* `CPU_INVALID_ADDRESS`,
* `CPU_INVALID_STACK_OPERATION`,
* `CPU_DIV_BY_ZERO`,
* `CPU_IO_ERROR`.

Kromě `CPU_OK` a `CPU_HALTED` jsou všechny ostatní stavy _chybové_.
Pro vykonání všech instrukcí platí:

 * Pokud je stavový kód emulátoru jiný než `CPU_OK`, instrukce se nevykoná.
 * Pokud je kód instrukce neznámý, nastaví se kód `CPU_ILLEGAL_INSTRUCTION`.
 * Pokud je registr instrukce neznámý, nastaví se kód `CPU_ILLEGAL_OPERAND`.
 * Pokud je index instrukce v registru mimo paměť emulovaného programu,
   nebo ukazuje do zásobníku, nastaví se kód `CPU_INVALID_ADDRESS`.
 * Pokud během vykonávání instrukce nastane chyba, index instrukce v registru
   se nemění.

Počáteční status procesoru je ve funkci `cpu_create` inicializován na `CPU_OK`.

=== Seznam instrukcí

Číselné hodnoty instrukcí jsou definované pořadím v tomto seznamu.

[start=0]
. `nop`: Nedělá nic.
. `halt`: Zastaví vykonávání programu a nastaví stav procesoru na `CPU_HALTED`.
  Funkce `cpu_step()` po jejím vykonání vrátí 0.
. `add REG`: Připočítá k registru `A` hodnotu registru `REG`.
. `sub REG`: Odečte z registru `A` hodnotu registru `REG`.
. `mul REG`: Vynásobí registr `A` hodnotou registru `REG`.
. `div REG`: Vydělí registr `A` hodnotou registru `REG`. Pokud je jeho hodnota 0,
  instrukci nevykoná a nastaví stavový kód na `CPU_DIV_BY_ZERO`.
. `inc REG`: Inkrementuje registr `REG`.
. `dec REG`: Dekrementuje registr `REG`.
. `loop INDEX`: Pokud je registr `C` nenulový, skočí na instrukci s indexem `INDEX`,
   jinak neudělá níc.
. `movr REG NUM`: Uloží do registru `REG` číslo `NUM`.
. `load REG NUM`: Uloží do registru `REG` číslo ze zásobníku, které se v něm
  nachází na indexu `D` + `NUM` od konce. Tedy pokud jsou registr `D` i `NUM`
  rovny nule, uloží se do registru hodnota na vrcholu zásobníku (tj. poslední
  vložená). Aktuální velikost zásobníku zůstává nezměněná. V případě,
  že je `D` + `NUM` index mimo zaplněnou část zásobníku, operace se nevykoná
  a nastaví se stavový kód `CPU_INVALID_STACK_OPERATION`.
. `store REG NUM`: Funguje podobně jako `load`, ale hodnotu na zásobník ukládá,
  tedy hodnotu z registru `REG` vloží na index `D` + `NUM` od konce. Aktuální
  velikost zásobníku zůstává nezměněná. V případě neplatného indexu se nastaví
  stavový kód `CPU_INVALID_STACK_OPERATION` stejně jako u `load`.
. `in REG`: Přečte ze vstupu 32bitové číslo a uloží ho do registru `REG`.
  Pokud byty na vstupu nereprezentují číslo, instrukce nic neudělá a nastaví
  stav procesoru na `CPU_IO_ERROR`. Pokud na vstupu už žádná čísla nejsou (`EOF`),
  nastaví registr `C` na 0 a do `REG` uloží hodnotu -1 (a to i v případě,
  že `REG` je `C`).
. `get REG`: Přečte ze vstupu jeden znak (byte) a uloží jej do registru `REG`.
  V případě, že na vstupu už žádné byty nejsou (`EOF`), chová se instrukce jako `in`.
. `out REG`: Vypíše hodnotu registru `REG` jako číslo na standartní výstup.
  Výstup této instrukce je pouze sekvence znaků `'0'` – `'9'`.
. `put REG`: Pokud je hodnota registru `REG` v rozsahu 0 – 255, vypíše tuto
  hodnotu jako právě jeden znak na standartní výstup. Jinak neudělá nic a nastaví
  stavový kód na `CPU_ILLEGAL_OPERAND`.
. `swap REG REG`: Vymění hodnoty registrů.
. `push REG`: Přidá hodnotu registru `REG` na zásobník, pokud není plný.
  Jinak neudělá nic a nastaví stavový kód `CPU_INVALID_STACK_OPERATION`.
  Instrukce upravuje aktuální velikost zásobníku.
. `pop REG`: Pokud je na zásobníku alespoň jeden prvek, odebere jej a jeho
  hodnotu uloží do registru `REG`. Jinak neudělá nic a nastaví stavový kód
  `CPU_INVALID_STACK_OPERATION`. Instrukce upravuje aktuální velikost zásobníku.

TIP: Instrukce `in` a `get` v případě `EOF` vynulují registr `C`, aby při čtení
     vstupu v cyklu (instrukcí `loop`) způsobilo `EOF` ukončení cyklu.

=== Assembler

Programy pro procesor můžete napsat v textové podobě – assembleru, a ten si
následně zkompilovat pomocí kompilátoru v souboru `compiler.c` z kostry
nebo pomocí referenční implementace.

[WARNING]
====
`compiler.c` obsahuje POSIXovou funkci `getline(3)`, díky které nejde zkompilovat
na Windows. Na tomto operačním systému je třeba provést kompilaci ve WSL nebo na Aise.
====

Assembler může obsahovat kromě instrukcí i deklaraci návěstí (alfanumerický ASCII
identifikátor začínající písmenem a končící dvojtečkou, např. `here:`).
Instrukce, které berou argument typu `INDEX`, můžou použít místo číselné
konstanty tyto návěstí.

Na každém řádku se může nacházet nejvíce jedna instrukce, která je od svých
operandů oddělená právě jednou mezerou. Před a za ní může být libovolný počet
mezer. Komentáře začínají znakem `;`.

Do kostry jsme vám do adresáře `assembler_inputs` přiložili pár
programů, kterými se můžete inspirovat při psaní vlastních.

=== Příklad

Assembler:

[source,asm]
----
; simple homework assembly excercise
  dec 1     ; Decrement register B, same as `dec B`.
  loop here ; Same as loop 6.
  push 0
here:
  halt
----

Binární soubor (hodnota v prvním sloupci – index se v souboru nenachází,
slouží jen pro názornější ukázku):


----
index  1  2  3  4    význam
0      07 00 00 00   ( 7) dec
1      01 00 00 00             (operand registr B)
2      08 00 00 00   ( 8) loop
3      06 00 00 00   ( 6)      (operand index 6)
4      11 00 00 00   (17) push
5      00 00 00 00   ( 0)      (operand registr A)
6      01 00 00 00   ( 1) halt
----

=== Rozhraní v příkazovém řádku

Tato část je implementovaná v kostře.

Program akceptuje dva až tři argumenty: první je `run` nebo `trace`, druhý,
nepovinný, je `stack_capacity`, poslední je cesta k souboru s instrukcemi.
`run` vykoná všechny instrukce a vypíše stav CPU, `trace` vypíše stav po každé
instrukci a počká na Enter před vykonáním další.

== Bonusové rozšíření

=== Instrukce skoků [5 bodů]

[NOTE]
====
Kód související s touto rozšířenou instrukční sadou obalte příkazy preprocesoru
(každý na samostatném řádku). Pro účely testování přidejte k přepínačům překladače
(pro CMake obsah proměnné `CMAKE_C_FLAGS`) přepínač `-DBONUS_JMP`.

[source,c]
----
#ifdef BONUS_JMP
    // Bonus code.
#endif // BONUS_JMP
----
====

Rozšiřte `struct cpu` o registr `result`, který bude obsahovat výsledek poslední
aritmetické operace (`add`, `sub`, `mul`, `div`, `inc`, `dec`) nebo operace `cmp`.

Rozšiřte všechny relevantní funkce o registr `result`. Tento registr bude
jako operand dostupný pod indexem **4**.

Instrukční sadu rozšiřte o následující instrukce:

[start=19]
. `cmp REG_X REG_Y`: Do registru `result` zapíše `REG_X` - `REG_Y`. Hodnoty
  registrů `REG_X` ani `REG_Y` se nezmění.
. `jmp INDEX`: Skočí na `INDEX`.
. `jz INDEX`: Skočí na `INDEX`, pokud se `result` rovná nule.
. `jnz INDEX`: Skočí na `INDEX`, pokud `result` není nula.
. `jgt INDEX`: Skočí na `INDEX`, pokud je `result` striktně větší než nula.

Do registru `result` zároveň uloží svůj výsledek všechny aritmetické operace
(např. `add` do `result` zkopíruje konečnou hodnotu `A`, `inc` do `result`
zkopíruje hodnotu operandu po jeho inkrementaci apod.). Ostatní instrukce
(např. `load`, `swap`, `in`) můžou tento registr pouze číst. Při pokusu
o zápis procesor nastaví stavový kód `CPU_ILLEGAL_OPERAND`.

=== Procedury [5 bodů]

[NOTE]
====
Tento bonus povolte makrem `BONUS_CALL`:

[source,c]
----
#ifdef BONUS_CALL
    // Bonus code.
#endif // BONUS_CALL
----
====

[start=24]
. `call INDEX`: Pokud je na zásobníku dost místa, uloží na něm index následující
instrukce a skočí na `INDEX`. Jinak nastaví stavový kód na `CPU_INVALID_STACK_OPERATION`.
. `ret`: Vybere z vrcholu zásobníku index instrukce (kterou, pokud byl program
korektně napsaný, tam vložila instrukce `call`) a skočí na ni. Pokud je zásobník
prázdný, nastaví stavový kód `CPU_INVALID_STACK_OPERATION`.

CAUTION: Pokud se rozhodnete implementovat jen druhý bonus, ujistěte se, že má
instrukce `call` ve vašem řešení stejný kód, jaký by měla, pokud by existovaly
i instrukce `cmp` a `jgt`!

== Poznámky

* Program kompilujte příkazem
+
----
gcc -o hw03 main.c cpu.c -std=c99 -Wall -Wextra -Werror -pedantic
----

* Vzorové řešení můžete spustit na aise:
+
----
/home/kontr/pb071/hw03/compiler
/home/kontr/pb071/hw03/cpu
/home/kontr/pb071/hw03/cpu-bonus
----

* POZOR! Vzorové řešení vyžaduje argument!
* Nezapomínejte na vhodnou dekompozici. Vyrobte si pomocné funkce, kde třeba.

TIP: Během psaní řešení vás to možná bude svádět k vytvoření obrovského `switch`.
     Ten nepotěší vaše opravující a práce s ním bude zbytečně pracná. Zkuste
     využít znalosti z přednášek a než začnete psát, zamyslete se nad správným
     návrhem vašeho řešení.
Loading