diff --git a/.gitignore b/.gitignore index 7b13b47..ac53c9d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,15 @@ # # already existing elements were commented out -#/target -/tmp -/temp +**/target +/interpreter/target +/interpreter/tmp +/playground + +*.core +**/tmp +**/temp + +/testing/bin +/testing/artifacts +/testing/src/__pycache__ diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index a333194..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,48 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "trivilang" -version = "0.1.0" -dependencies = [ - "indoc", - "pretty_assertions", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock new file mode 100644 index 0000000..acb83f1 --- /dev/null +++ b/compiler/Cargo.lock @@ -0,0 +1,225 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "trivilang" +version = "0.1.0" +dependencies = [ + "clap", + "indoc", + "pretty_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/Cargo.toml b/compiler/Cargo.toml similarity index 66% rename from Cargo.toml rename to compiler/Cargo.toml index 88d9f7a..64804e5 100644 --- a/Cargo.toml +++ b/compiler/Cargo.toml @@ -8,8 +8,9 @@ name = "trivic" path = "src/main.rs" [dependencies] +clap = { version = "4.5.60", features = ["derive"] } indoc = "2.0" [dev-dependencies] -pretty_assertions = "1.4.1" \ No newline at end of file +pretty_assertions = "1.4.1" diff --git a/README.md b/compiler/README.md similarity index 100% rename from README.md rename to compiler/README.md diff --git a/compiler/fissc/Basic/.Makefile.swp b/compiler/fissc/Basic/.Makefile.swp new file mode 100755 index 0000000..c14fc39 Binary files /dev/null and b/compiler/fissc/Basic/.Makefile.swp differ diff --git a/compiler/fissc/Basic/Makefile b/compiler/fissc/Basic/Makefile new file mode 100755 index 0000000..a9f5973 --- /dev/null +++ b/compiler/fissc/Basic/Makefile @@ -0,0 +1,23 @@ +# Makefile script with commands to generate the benchmark assembly and binaries. +# Description of the targets: +# x86-rsa: produce lazart object files +# clean: clean generated files, including output binaries + +.PHONY: clean x86-rsa + +TARGET = crt-rsa + +FILES = src/countermeasure.c src/initialize.c src/main.c src/oracle.c src/crt-rsa.c +FILES_INLINE = src/countermeasure.c src/initialize.c src/main.c + +INC_DIR = -I ../share -Iinclude +BIN_DIR = bin + +CFLAGS = $(INC_DIR)-Wall -Wextra + +x86-rsa: + @mkdir -p $(BIN_DIR) + @clang -DMAKE_Rsa $(FILES) $(CFLAGS) -o $(BIN_DIR)/$(TARGET) + +clean: + @rm -rf $(BIN_DIR) diff --git a/compiler/fissc/Basic/bin/crt-rsa b/compiler/fissc/Basic/bin/crt-rsa new file mode 100755 index 0000000..672131b Binary files /dev/null and b/compiler/fissc/Basic/bin/crt-rsa differ diff --git a/compiler/fissc/Basic/include/commons.h b/compiler/fissc/Basic/include/commons.h new file mode 100755 index 0000000..b319980 --- /dev/null +++ b/compiler/fissc/Basic/include/commons.h @@ -0,0 +1,23 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +#ifndef H_COMMONS +#define H_COMMONS + + +#endif // H_COMMONS diff --git a/compiler/fissc/Basic/src/countermeasure.c b/compiler/fissc/Basic/src/countermeasure.c new file mode 100755 index 0000000..a8e5bce --- /dev/null +++ b/compiler/fissc/Basic/src/countermeasure.c @@ -0,0 +1,29 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +#include "interface.h" +#include "types.h" +#include + +extern UBYTE g_countermeasure; + +void countermeasure() +{ + g_countermeasure = 1; + exit(6); +} diff --git a/compiler/fissc/Basic/src/crt-rsa.c b/compiler/fissc/Basic/src/crt-rsa.c new file mode 100755 index 0000000..b650b53 --- /dev/null +++ b/compiler/fissc/Basic/src/crt-rsa.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + + +extern int g_M; +extern int g_N; +extern int g_e; +extern int g_p; +extern int g_q; +extern int g_dp; +extern int g_dq; +extern int g_iq; +extern int g_sign; + + +/* Returns: a + b. */ +int Add(int a, int b) { return a+b; } + +/* Returns: a - b. */ +int Sub(int a, int b) {return a-b; } + +/* Returns: a * b. */ +int Mul(int a, int b) { return a*b; } + + +/* Returns: a mod n. */ +#ifdef INLINE +__attribute__((always_inline)) inline int Mod(int a, int n) +#else +int Mod(int a, int n) +#endif +{ + int r = a%n; + while (r < 0) { r = Add(r, n); } + return r; +} + + +/* Returns: (a * b) mod n. */ +#ifdef INLINE +__attribute__((always_inline)) inline int MulMod(int a, int b, int n) +#else +int MulMod(int a, int b, int n) +#endif +{ + int r = 0; + while (b--) { + r = Add(r, a); + r = Mod(r, n); + } + return r; +} + +/* Right-to-left algorithm, returns: (a ^ b) mod n. */ +#ifdef INLINE +__attribute__((always_inline)) inline int PowerMod(int a, int b, int n) +#else +int PowerMod(int a, int b, int n) +#endif +{ + int r = 1; + a = Mod(a, n); + while (b > 0) { + if (b % 2 == 1) { + r = MulMod(r, a, n); + } + if (b >>= 1) { + a = MulMod(a, a, n); + } + } + return r; +} + + +#ifdef INLINE +__attribute__((always_inline)) inline int RsaSign() +#else +int RsaSign() +#endif +{ + int Cp, Cq, tmp; + Cp = PowerMod(g_M, g_dp, g_p); + Cq = PowerMod(g_M, g_dq, g_q); + tmp = Sub(Cp, Cq); + tmp = MulMod(tmp, g_iq, g_p); + tmp = Mul(tmp, g_q); + tmp = Add(tmp, Cq); + g_sign = tmp; + return g_sign; +} diff --git a/compiler/fissc/Basic/src/initialize.c b/compiler/fissc/Basic/src/initialize.c new file mode 100755 index 0000000..988dab9 --- /dev/null +++ b/compiler/fissc/Basic/src/initialize.c @@ -0,0 +1,45 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +#include "types.h" +#include "interface.h" + + +UBYTE g_countermeasure; +int g_M; +int g_N; +int g_e; +int g_p; +int g_q; +int g_dp; +int g_dq; +int g_iq; +int g_sign; + +void initialize() { + g_countermeasure = 0; + g_N = 11413; + g_e = 3533; + g_p = 101; + g_q = 113; + g_iq = 59; /* iq = (1/q) mod p */ + g_dp = 97; /* dp = d mod (p-1) */ + g_dq = 101; /* dq = d mod (q-1) */ + g_sign = -1; + g_M = 23; +} diff --git a/compiler/fissc/Basic/src/main.c b/compiler/fissc/Basic/src/main.c new file mode 100755 index 0000000..e6ceea6 --- /dev/null +++ b/compiler/fissc/Basic/src/main.c @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +/*$ + @name = CRT-RSA + @feature = CRT-RSA + @fault-model = data (BSet 0/1, symbolic) + @attack-scenario = oracle + @countermeasure = none + @maintainers = Etienne Boespflug, VERIMAG + @authors = Maxime Puys, SERTIF Consortium + @version 2.2 +*/ + +#include +#include "interface.h" +#include "types.h" +#include "lazart.h" + +#ifdef INLINE +#include "crt-rsa.c" +#include "oracle.c" +#endif + +extern UBYTE g_countermeasure; +extern int g_M; +extern int g_N; +extern int g_e; +extern int g_p; +extern int g_q; +extern int g_dp; +extern int g_dq; +extern int g_iq; +extern int g_sign; +int RsaSign(void); + +int main() { + initialize(); + RsaSign(); + LAZART_ORACLE(oracle()); + + printf("countermeasure = %i\nM = %i\nN = %i\ne = %i\np = %i\nq = %i\ndp = %i\ndq = %i\niq = %i\nsign = %i\n", g_countermeasure, g_M, g_N, g_e, g_p, g_q, g_dp, g_dq, g_iq, g_sign); + + return g_sign; +} diff --git a/compiler/fissc/Basic/src/oracle.c b/compiler/fissc/Basic/src/oracle.c new file mode 100755 index 0000000..5cb6c75 --- /dev/null +++ b/compiler/fissc/Basic/src/oracle.c @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +#include "interface.h" +#include "types.h" + +extern UBYTE g_countermeasure; +extern int g_M; +extern int g_N; +extern int g_e; +extern int g_p; +extern int g_q; +extern int g_dp; +extern int g_dq; +extern int g_iq; +extern int g_sign; + +int Add(int, int); +int Sub(int, int); +int Mul(int, int); +int Mod(int, int); +int MulMod(int, int, int); +int PowerMod(int, int, int); + +int RsaSign2() +{ + int Cp, Cq, tmp; + Cp = PowerMod(g_M, g_dp, g_p); + Cq = PowerMod(g_M, g_dq, g_q); + tmp = Sub(Cp, Cq); + tmp = MulMod(tmp, g_iq, g_p); + tmp = Mul(tmp, g_q); + tmp = Add(tmp, Cq); + g_sign = tmp; + return g_sign; +} + +BOOL oracle() + { + int sign = g_sign; + int S = RsaSign2(); + return + g_countermeasure != 1 + && S != sign + && (Mod(S,g_p) == Mod(sign, g_p) || Mod(S,g_q) == Mod(sign,g_q)); +} diff --git a/compiler/fissc/crt-rsa.trv b/compiler/fissc/crt-rsa.trv new file mode 100644 index 0000000..d44292c --- /dev/null +++ b/compiler/fissc/crt-rsa.trv @@ -0,0 +1,120 @@ +func main() -> Integer { + let g_countermeasure : Integer = 0; + let g_N : Integer = 11413; + let g_e : Integer = 3533; + let g_p : Integer = 101; + let g_q : Integer = 113; + let g_iq : Integer = 59; + let g_dp : Integer = 97; + let g_dq : Integer = 101; + let g_sign : Integer = -1; + let g_M : Integer = 23; + + let Cp : Integer = 0; + let Cq : Integer = 0; + let tmp : Integer = 0; + + let a : Integer = 0; + let b : Integer = 0; + let n : Integer = 0; + let r : Integer = 0; + + let mul_r : Integer = 0; + let mul_a : Integer = 0; + let mul_b : Integer = 0; + let mul_n : Integer = 0; + let mul_tmp : Integer = 0; + + let pow_a : Integer = 0; + let pow_tmp : Integer = 0; + let pow_b : Integer = 0; + let pow_n : Integer = 0; + + a = g_M; + b = g_dp; + n = g_p; + r = 1; + pow_b = 0; + while pow_b < b { + mul_a = r; + mul_b = a; + mul_n = n; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + r = mul_tmp; + pow_b = pow_b + 1; + } + Cp = r; + + a = g_M; + b = g_dq; + n = g_q; + r = 1; + pow_b = 0; + while pow_b < b { + mul_a = r; + mul_b = a; + mul_n = n; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + r = mul_tmp; + pow_b = pow_b + 1; + } + Cq = r; + + tmp = Cp - Cq; + + mul_a = tmp; + mul_b = g_iq; + mul_n = g_p; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + tmp = mul_tmp; + + tmp = tmp * g_q; + + tmp = tmp + Cq; + + g_sign = tmp; + + print("g_sign herro = ", 1337); + print("g_countermeasure: ", g_countermeasure); + + return g_sign; +} diff --git a/compiler/fissc/share/interface.h b/compiler/fissc/share/interface.h new file mode 100755 index 0000000..da13f28 --- /dev/null +++ b/compiler/fissc/share/interface.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +/*$ + @authors = SERTIF Consortium + @feature = N/A + @target = N/A + @countermeasure = N/A + @difficulties = N/A + @purpose = shared interface for examples +*/ + +#ifndef H_INTERFACE +#define H_INTERFACE + +#include "types.h" + +/* defines what happens when a fault has been detected + implementation defined +*/ +void countermeasure(void); + +/* initializes global variables at the beginning of an example + implementation-defined + example-defined +*/ +void initialize(void); + +/* determines whether an attack is successful + example-defined +*/ +BOOL oracle(void); + +#endif // H_INTERFACE diff --git a/compiler/fissc/share/lazart.h b/compiler/fissc/share/lazart.h new file mode 100755 index 0000000..cf68670 --- /dev/null +++ b/compiler/fissc/share/lazart.h @@ -0,0 +1,21 @@ +#ifndef LAZART_API_HPP +#define LAZART_API_HPP + +#ifdef LAZART + +#include + +/*#define LAZART_ORACLE(oracle) \ + if(oracle) { \ + klee_assume(oracle); \ + } else { \ + klee_assume(0 != 0 ); \ + }*/ + +#define LAZART_ORACLE(oracle) klee_assume(oracle) + +#else +#define LAZART_ORACLE(oracle) +#endif // LAZART + +#endif // LAZART_API_HPP diff --git a/compiler/fissc/share/types.h b/compiler/fissc/share/types.h new file mode 100755 index 0000000..27388a1 --- /dev/null +++ b/compiler/fissc/share/types.h @@ -0,0 +1,29 @@ +/**************************************************************************/ +/* */ +/* This file is part of FISSC. */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 3.0. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 3.0 */ +/* for more details (enclosed in the file LICENSE). */ +/* */ +/**************************************************************************/ + +#ifndef H_TYPES +#define H_TYPES +typedef signed char SBYTE; +typedef unsigned char UBYTE; +typedef unsigned char BOOL; +typedef unsigned long ULONG; + +#define BOOL_TRUE 0xAA +#define BOOL_FALSE 0x55 + +#endif // H_TYPES diff --git a/compiler/main.asm b/compiler/main.asm new file mode 100644 index 0000000..7613098 --- /dev/null +++ b/compiler/main.asm @@ -0,0 +1,455 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #11413 + str r0, [sp] + sub sp, sp, #4 + mov r0, #3533 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #113 + str r0, [sp] + sub sp, sp, #4 + mov r0, #59 + str r0, [sp] + sub sp, sp, #4 + mov r0, #97 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #-1 + str r0, [sp] + sub sp, sp, #4 + mov r0, #23 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #76] + str r0, [sp, #44] + ldr r0, [sp, #88] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_1: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_2: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_2 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_2 +end_while_2: +while_3: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_3 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_3 +end_while_3: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_4 + mov r0, #0 + str r0, [sp, #16] + b endif_4 +else_4: +endif_4: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_1 +end_while_1: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: + ldr r0, [sp, #36] + str r0, [sp, #60] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #72] + str r0, [sp, #44] + ldr r0, [sp, #84] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_5: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_5 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_6: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_6 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_7: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_7 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_7 +end_while_7: +while_8: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_8 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_8 +end_while_8: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_9 + mov r0, #0 + str r0, [sp, #16] + b endif_9 +else_9: +endif_9: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_6 +end_while_6: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_5 +end_while_5: + ldr r0, [sp, #36] + str r0, [sp, #56] + ldr r0, [sp, #60] + mov r1, r0 + ldr r0, [sp, #56] + sub r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #28] + ldr r0, [sp, #80] + str r0, [sp, #24] + ldr r0, [sp, #88] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_10: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_10 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_11: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_11 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_11 +end_while_11: +while_12: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_12 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_12 +end_while_12: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_13 + mov r0, #0 + str r0, [sp, #16] + b endif_13 +else_13: +endif_13: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_10 +end_while_10: + ldr r0, [sp, #16] + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #84] + mul r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #56] + add r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #68] + mov r0, #1 + ldr r1, =.Lstr0 + mov r2, #12 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_14 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_14 +print_int_loop_14: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_14 +print_int_done_14: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r7, #1 + svc #0 + +.size _start, .-_start + +.section .data +.Lstr0: + .ascii "gang_sign = " +newline: + .ascii "\n" +num_buf: + .space 16 diff --git a/compiler/out.asm b/compiler/out.asm new file mode 100644 index 0000000..f05f88c --- /dev/null +++ b/compiler/out.asm @@ -0,0 +1,498 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #11413 + str r0, [sp] + sub sp, sp, #4 + mov r0, #3533 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #113 + str r0, [sp] + sub sp, sp, #4 + mov r0, #59 + str r0, [sp] + sub sp, sp, #4 + mov r0, #97 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #-1 + str r0, [sp] + sub sp, sp, #4 + mov r0, #23 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #76] + str r0, [sp, #44] + ldr r0, [sp, #88] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_1: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_2: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_2 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_2 +end_while_2: +while_3: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_3 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_3 +end_while_3: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_4 + mov r0, #0 + str r0, [sp, #16] + b endif_4 +else_4: +endif_4: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_1 +end_while_1: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: + ldr r0, [sp, #36] + str r0, [sp, #60] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #72] + str r0, [sp, #44] + ldr r0, [sp, #84] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_5: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_5 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_6: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_6 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_7: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_7 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_7 +end_while_7: +while_8: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_8 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_8 +end_while_8: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_9 + mov r0, #0 + str r0, [sp, #16] + b endif_9 +else_9: +endif_9: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_6 +end_while_6: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_5 +end_while_5: + ldr r0, [sp, #36] + str r0, [sp, #56] + ldr r0, [sp, #60] + mov r1, r0 + ldr r0, [sp, #56] + sub r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #28] + ldr r0, [sp, #80] + str r0, [sp, #24] + ldr r0, [sp, #88] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_10: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_10 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_11: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_11 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_11 +end_while_11: +while_12: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_12 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_12 +end_while_12: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_13 + mov r0, #0 + str r0, [sp, #16] + b endif_13 +else_13: +endif_13: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_10 +end_while_10: + ldr r0, [sp, #16] + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #84] + mul r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #56] + add r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #68] + mov r0, #1 + ldr r1, =.Lstr0 + mov r2, #15 + mov r7, #4 + svc #0 + mov r0, #1337 + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_14 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_14 +print_int_loop_14: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_14 +print_int_done_14: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =.Lstr1 + mov r2, #18 + mov r7, #4 + svc #0 + ldr r0, [sp, #100] + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_15 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_15 +print_int_loop_15: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_15 +print_int_done_15: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r7, #1 + svc #0 + +.size _start, .-_start + +.section .data +.Lstr0: + .ascii "g_sign herro = " +.Lstr1: + .ascii "g_countermeasure: " +newline: + .ascii "\n" +num_buf: + .space 16 diff --git a/compiler/run_asm b/compiler/run_asm new file mode 100755 index 0000000..567c183 --- /dev/null +++ b/compiler/run_asm @@ -0,0 +1,28 @@ +#!/bin/bash + +# 1. Check if a filename was provided +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +INPUT_FILE=$1 +BASE_NAME="${INPUT_FILE%.*}" + +# 2. Assemble & Link +arm-none-eabi-as -mthumb -o "$BASE_NAME.o" "$INPUT_FILE" || exit 1 +arm-none-eabi-ld -o "$BASE_NAME" "$BASE_NAME.o" || exit 1 + +# 3. Run and capture the exit code +echo "--- Running $BASE_NAME ---" +qemu-arm "./$BASE_NAME" + +# Store the exit code of the PREVIOUS command (qemu-arm) +EXIT_STATUS=$? + +# 4. Cleanup +rm "$BASE_NAME.o" "$BASE_NAME" + +# 5. Exit with the captured status +echo "--- Finished with code: $EXIT_STATUS ---" +exit $EXIT_STATUS diff --git a/compiler/src/codegen/codegen.rs b/compiler/src/codegen/codegen.rs new file mode 100644 index 0000000..a3c801e --- /dev/null +++ b/compiler/src/codegen/codegen.rs @@ -0,0 +1,921 @@ +use crate::parser::{ parser::{ BinOp, Block, Expr, Function, Stmt }, AST }; +use core::panic; +use std::{ collections::HashMap, fs::File, io::Write }; + +#[derive(Debug)] +pub struct CodeGenerator { + pub file: File, + locals: HashMap, + stack_offset: i32, + label_count: usize, + hard: bool, + string_literals: Vec<(String, String)>, + need_int_print: bool, + step_counter: i32, +} + +impl CodeGenerator { + pub fn new(file: File) -> Self { + Self { + file, + locals: HashMap::new(), + stack_offset: 0, + label_count: 0, + hard: false, + string_literals: Vec::new(), + need_int_print: false, + step_counter: 0, + } + } + pub fn generate(&mut self, ast: AST, is_hard: bool) { + self.hard = is_hard; + self.gen_init(); + for func in ast { + self.emit(func); + } + if self.hard { + self.emit_countermeasure(); + } + self.emit_print_data(); + } + + fn gen_init(&mut self) { + self.write_line(".syntax unified", 0); + self.write_line(".thumb", 0); + self.write_line("", 0); + self.write_line(".section .text", 0); + self.write_line(".global _start", 0); + self.write_line(".type _start, %function", 0); + self.write_line("", 0); + } + + fn write_line(&mut self, string: &str, indents: usize) { + let indent_str = " ".repeat(indents); + + // writeln! automatically appends \n + let _ = writeln!(self.file, "{}{}", indent_str, string); + } + + fn emit(&mut self, func: Function) { + match func.name.as_str() { + "main" => self.emit_main(func), + _ => self.emit_func(func), + } + self.file.sync_all().unwrap(); + } + + fn emit_func(&mut self, func: Function) {} + + fn emit_main(&mut self, func: Function) { + self.write_line("_start:", 0); + + if self.hard { + self.write_line("mov r9, #0", 1); // step counter in register + self.write_line("mov r10, #1", 1); // step counter in register + } + self.emit_block(func.body, true); + if self.hard { + self.emit_countermeasure(); + } + self.write_line("\n.size _start, .-_start", 0); + } + fn emit_block(&mut self, block: Block, is_main: bool) { + let initial_offset = self.stack_offset; + let initial_locals = self.locals.clone(); + + let mut has_return = false; + for stmt in block.statements { + match stmt { + Stmt::Let(_, _, _) => self.emit_let(stmt), + Stmt::AssignStatement(_, _) => self.emit_assign(stmt), + Stmt::Return(_) => { + self.emit_return(stmt, is_main); + has_return = true; + } + Stmt::If { .. } => self.emit_if(stmt), + Stmt::While { .. } => self.emit_while(stmt), + Stmt::Print(exprs) => self.emit_print(exprs), + _ => panic!("Error found in expression in return"), + } + if self.hard { + self.emit_step_check(); + } + } + + let offset_diff = self.stack_offset - initial_offset; + if offset_diff > 0 && !has_return { + self.write_line(&format!("add sp, sp, #{}", offset_diff), 1); + } + + self.stack_offset = initial_offset; + self.locals = initial_locals; + } + fn emit_countermeasure(&mut self) { + self.write_line("countermeasure:", 0); + + // exit(77) + self.write_line("mov r0, #77", 1); + self.write_line("mov r7, #1", 1); + self.write_line("svc #0", 1); + } + + fn emit_step_check(&mut self) { + if !self.hard { + return; + } + + self.write_line("add r9, r9, #1", 1); + self.write_line("cmp r9, r10", 1); + self.write_line("bne countermeasure", 1); + self.write_line("add r10, r10, #1", 1); + } + + fn emit_print_data(&mut self) { + if self.string_literals.is_empty() && !self.need_int_print { + return; + } + self.write_line("", 0); + self.write_line(".section .data", 0); + + // clone string literals to avoid borrow conflicts while writing + let literals = self.string_literals.clone(); + for (label, contents) in literals { + self.write_line(&format!("{}:", label), 0); + self.write_line(&format!(".ascii \"{}\"", contents), 1); + } + if self.need_int_print { + self.write_line("newline:", 0); + self.write_line(".ascii \"\\n\"", 1); + self.write_line("num_buf:", 0); + self.write_line(".space 16", 1); + } + if self.hard { + self.write_line("step_counter:", 0); + self.write_line(".word 0", 1); + self.write_line("fault_msg:", 0); + self.write_line(".ascii \"Control flow violation detected\\n\"", 1); + } + } + fn emit_if(&mut self, if_stmt: Stmt) { + match if_stmt { + Stmt::If { condition, block, option } => { + let label_id = self.label_count; + self.label_count += 1; + + self.emit_expr(condition); + self.write_line("cmp r0, #0", 1); + self.write_line(&format!("beq else_{}", label_id), 1); + + // save step state + let saved = self.step_counter; + + // THEN branch + self.emit_step_check(); + self.emit_block(block, false); + let then_end = self.step_counter; + + self.write_line(&format!("b endif_{}", label_id), 1); + + // ELSE branch + self.write_line(&format!("else_{}:", label_id), 0); + + self.step_counter = saved; + + if let Some(else_block) = option { + self.emit_step_check(); + self.emit_block(else_block, false); + } + + let else_end = self.step_counter; + self.write_line(&format!("endif_{}:", label_id), 0); + self.step_counter = then_end.max(else_end); + } + _ => panic!("emit_if called with non-if statement"), + } + } + + fn emit_while(&mut self, while_stmt: Stmt) { + match while_stmt { + Stmt::While { expr, block } => { + let label_id = self.label_count; + self.label_count += 1; + + self.write_line(&format!("while_{}:", label_id), 0); + + self.emit_step_check(); + self.emit_expr(expr); + self.write_line("cmp r0, #0", 1); + self.write_line(&format!("beq end_while_{}", label_id), 1); + + self.emit_block(block, false); + self.emit_step_check(); + + self.write_line(&format!("b while_{}", label_id), 1); + self.write_line(&format!("end_while_{}:", label_id), 0); + } + _ => panic!("emit_while called with non-while statement"), + } + } + fn emit_print(&mut self, exprs: Vec) { + if exprs.is_empty() { + return; + } + + let last_index = exprs.len().saturating_sub(1); + for (idx, expr) in exprs.into_iter().enumerate() { + let is_last = idx == last_index; // newline only after the whole print + match expr { + Expr::StringLiteral(raw) => { + // Strip surrounding quotes if present + let inner = if raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"') { + raw[1..raw.len() - 1].to_string() + } else { + raw.clone() + }; + + let label = format!(".Lstr{}", self.string_literals.len()); + self.string_literals.push((label.clone(), inner.clone())); + + // write string to stdout (no newline) + self.write_line("mov r0, #1", 1); + self.write_line(&format!("ldr r1, ={}", label), 1); + self.write_line(&format!("mov r2, #{}", inner.len()), 1); + self.write_line("mov r7, #4", 1); + self.write_line("svc #0", 1); + } + other => { + // Print integer value; newline only after the last argument + self.need_int_print = true; + self.emit_expr(other); + + let id = self.label_count; + self.label_count += 1; + + // r0 holds value to print + self.write_line("mov r4, r0", 1); + self.write_line("ldr r1, =num_buf", 1); + self.write_line("add r1, r1, #16", 1); + self.write_line("mov r2, #0", 1); + + // handle zero specially + self.write_line("cmp r4, #0", 1); + self.write_line(&format!("bne print_int_loop_{}", id), 1); + self.write_line("mov r3, #48", 1); + self.write_line("sub r1, r1, #1", 1); + self.write_line("strb r3, [r1]", 1); + self.write_line("mov r2, #1", 1); + self.write_line(&format!("b print_int_done_{}", id), 1); + + // conversion loop + self.write_line(&format!("print_int_loop_{}:", id), 0); + self.write_line("mov r0, r4", 1); + self.write_line("mov r3, #10", 1); + self.write_line("sdiv r5, r0, r3", 1); // r5 = value / 10 + self.write_line("mul r6, r5, r3", 1); // r6 = (value/10)*10 + self.write_line("sub r7, r0, r6", 1); // r7 = value % 10 + self.write_line("add r7, r7, #48", 1); // to ASCII + self.write_line("sub r1, r1, #1", 1); + self.write_line("strb r7, [r1]", 1); + self.write_line("add r2, r2, #1", 1); + self.write_line("mov r4, r5", 1); // value = value / 10 + self.write_line("cmp r4, #0", 1); + self.write_line(&format!("bne print_int_loop_{}", id), 1); + + self.write_line(&format!("print_int_done_{}:", id), 0); + // write digits + self.write_line("mov r0, #1", 1); + self.write_line("mov r1, r1", 1); + self.write_line("mov r2, r2", 1); + self.write_line("mov r7, #4", 1); + self.write_line("svc #0", 1); + + // newline only once, after the last printed value + if is_last { + self.write_line("mov r0, #1", 1); + self.write_line("ldr r1, =newline", 1); + self.write_line("mov r2, #1", 1); + self.write_line("mov r7, #4", 1); + self.write_line("svc #0", 1); + } + } + } + } + } + + fn emit_return(&mut self, return_stmt: Stmt, is_main: bool) { + match return_stmt { + Stmt::Return(expr) => { + self.emit_expr(expr); + self.write_line("mov r7, #1", 1); + self.write_line("svc #0", 1); + } + _ => panic!("return poorly formed"), + } + } + fn emit_assign(&mut self, assign_stmt: Stmt) { + match assign_stmt { + Stmt::AssignStatement(name, expr) => { + self.emit_expr(expr); + let offset = self.locals.get(&name).expect("Undefined variable"); + if self.stack_offset - offset == 0 { + self.write_line(&format!("str r0, [sp]"), 1); + } else { + self.write_line(&format!("str r0, [sp, #{}]", self.stack_offset - offset), 1); + } + if self.hard { + self.emit_step_check(); + } + } + _ => panic!("Not a valid assignment"), + } + } + fn emit_let(&mut self, let_stmt: Stmt) { + match let_stmt { + Stmt::Let(name, type_name, expr) => { + self.stack_offset += 4; + self.write_line("sub sp, sp, #4", 1); + self.emit_expr(expr); + self.write_line("str r0, [sp]", 1); + self.locals.insert(name, self.stack_offset); + if self.hard { + self.emit_step_check(); + } + } + _ => panic!("Not a let statement format sorry "), + } + } + fn emit_expr(&mut self, expr: Expr) { + match expr { + Expr::IntegerLiteral(val) => self.write_line(&format!("mov r0, #{}", val), 1), + Expr::BooleanLiteral(val) => { + if val { + self.write_line(&format!("mov r0, #{}", 1), 1) + } else { + self.write_line(&format!("mov r0, #{}", 0), 1) + } + } + Expr::Identifier(name) => { + let offset = self.locals.get(&name).expect("Undefined variable"); + self.write_line(&format!("ldr r0, [sp, #{}]", self.stack_offset - offset), 1); + } + Expr::BinaryOp(_, _, _) => { + self.emit_bin_op(expr); + } + + _ => panic!("Expression not valid sorry"), + } + } + fn emit_bin_op(&mut self, bin_op_expr: Expr) { + match bin_op_expr { + Expr::BinaryOp(left, op, right) => { + self.emit_expr(*left); + self.write_line("mov r1, r0", 1); // store left + self.emit_expr(*right); + + match op { + BinOp::Add => self.write_line("add r0, r1, r0", 1), + BinOp::Sub => self.write_line("sub r0, r1, r0", 1), + BinOp::Mul => self.write_line("mul r0, r1, r0", 1), + BinOp::Div => self.write_line("sdiv r0, r1, r0", 1), + + BinOp::Equals => { + self.write_line("cmp r1, r0", 1); + self.write_line("mov r0, #0", 1); + self.write_line("it eq", 1); + self.write_line("moveq r0, #1", 1); + } + + BinOp::NotEquals => { + self.write_line("cmp r1, r0", 1); + self.write_line("mov r0, #0", 1); + self.write_line("it ne", 1); + self.write_line("movne r0, #1", 1); + } + + BinOp::GreaterThan => { + self.write_line("cmp r1, r0", 1); + self.write_line("mov r0, #0", 1); + self.write_line("it gt", 1); + self.write_line("movgt r0, #1", 1); + } + + BinOp::LessThan => { + self.write_line("cmp r1, r0", 1); + self.write_line("mov r0, #0", 1); + self.write_line("it lt", 1); + self.write_line("movlt r0, #1", 1); + } + } + } + _ => panic!("not a binary operation if ive ever seen one"), + } + } +} + +#[cfg(test)] +mod tests { + use crate::lexer::{ lexer::Lexer, token::Token }; + use crate::parser::parser::{ Parser, AST }; + use crate::CodeGenerator; + use std::fs::File; + use std::io::{ Read, Seek, SeekFrom }; + use std::sync::Once; + + static INIT: Once = Once::new(); + + fn initialize() { + INIT.call_once(|| { + let mut path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + path.push("temp/tests"); + std::fs::create_dir_all(path).unwrap(); + }); + } + + #[test] + fn can_generate_init() { + initialize(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_init.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_init.asm"); + + let ast = AST::new(); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + "## + } + ).to_string(); + assert_eq!(expected, buf); + } + + #[test] + fn can_generate_return() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_main_return.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_return.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_return.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + mov r0, #69 + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + + #[test] + fn can_generate_let() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_main_let.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_let.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_let.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + sub sp, sp, #4 + mov r0, #27 + str r0, [sp] + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + #[test] + fn can_generate_if_else_and_assign() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_if_else.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_if_else.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_if_else.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_0 + mov r0, #11 + str r0, [sp] + b endif_0 + else_0: + mov r0, #12 + str r0, [sp] + endif_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + + #[test] + fn can_generate_while() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_while_simple.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_while.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_while.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] + while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #12 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_0 + end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + + #[test] + fn can_generate_two_whiles() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_while_two_loops.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_two_whiles.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_two_whiles.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 + end_while_0: + while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #4 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 + end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #0] + add r0, r1, r0 + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + + #[test] + fn can_generate_nested_while() { + initialize(); + let source = std::fs + ::read_to_string("test_codes/test_while_nested.trv") + .expect("Failed to read file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open("temp/tests/test_can_generate_nested_while.asm") + .expect("Failed to create file: /temp/tests/test_can_generate_nested_while.asm"); + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + codegen.file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + codegen.file.read_to_string(&mut buf).unwrap(); + let expected = ( + indoc::indoc! { + r##" + .syntax unified + .thumb + + .section .text + .global _start + .type _start, %function + + _start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #2 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 + end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + add sp, sp, #4 + b while_0 + end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + + .size _start, .-_start + "## + } + ).to_string(); + assert_eq!(expected, buf) + } + + #[test] + fn compiles_all_correct() { + initialize(); + + let test_codes_dir = std::path::Path::new("test_codes"); + let trv_files: Vec<_> = std::fs + ::read_dir(test_codes_dir) + .unwrap() + .filter_map(|entry| { + let path = entry.unwrap().path(); + if path.extension().map_or(false, |ext| ext == "trv") { + Some(path) + } else { + None + } + }) + .collect(); + + for trv_path in trv_files { + let source = std::fs::read_to_string(&trv_path).expect("Failed to read trv file"); + let mut lexer: Lexer = Lexer::new(source); + let tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(tokens); + let ast: AST = parser.parse_program(); + + let file_stem = trv_path.file_stem().unwrap().to_str().unwrap(); + let output_asm_path = format!("temp/tests/{}.asm", file_stem); + let output_file = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&output_asm_path) + .expect(&format!("Failed to create file: {}", output_asm_path)); + + let mut codegen = CodeGenerator::new(output_file); + codegen.generate(ast, false); + + let output_path = trv_path.with_extension("trv.output"); + let expected_exit_code = std::fs + ::read_to_string(&output_path) + .expect("Failed to read output file") + .trim() + .parse::() + .expect("Failed to parse exit code"); + + let result = std::process::Command::new("./run_asm").arg(&output_asm_path).output(); + + let actual_exit_code = match result { + Ok(output) => output.status.code().unwrap_or(-1), + Err(_) => -1, + }; + + std::fs::remove_file(&output_asm_path).ok(); + + assert_eq!( + expected_exit_code, + actual_exit_code, + "File {}: expected exit code {}, got {}", + file_stem, + expected_exit_code, + actual_exit_code + ); + } + } +} diff --git a/compiler/src/codegen/mod.rs b/compiler/src/codegen/mod.rs new file mode 100644 index 0000000..9ea76c7 --- /dev/null +++ b/compiler/src/codegen/mod.rs @@ -0,0 +1,2 @@ +pub mod codegen; +pub use codegen::CodeGenerator; \ No newline at end of file diff --git a/src/lexer/lexer.rs b/compiler/src/lexer/lexer.rs similarity index 92% rename from src/lexer/lexer.rs rename to compiler/src/lexer/lexer.rs index a10dfc0..2b95fda 100644 --- a/src/lexer/lexer.rs +++ b/compiler/src/lexer/lexer.rs @@ -37,6 +37,9 @@ impl Lexer { '=' => { tokens.push(self.assign_or_equals()); } + '!' => { + tokens.push(self.not_or_notequals()); + } ':' => { tokens.push(self.simple_token(ch.to_string(), TokenType::Colon)); } @@ -115,11 +118,26 @@ impl Lexer { let original_col = self.column; self.advance(); if self.current_char().unwrap() == '=' { - Token::new("=".to_string(), TokenType::Equals, self.line, original_col) + let line = self.line.clone(); + self.advance(); + Token::new("==".to_string(), TokenType::Equals, line, original_col) + } else { + Token::new("=".to_string(), TokenType::Assign, self.line, original_col) + } + } + + fn not_or_notequals(&mut self) -> Token { + let original_col = self.column; + self.advance(); + if self.current_char().unwrap() == '=' { + let line = self.line.clone(); + self.advance(); + Token::new("!=".to_string(), TokenType::NotEquals, line, original_col) } else { - Token::new("==".to_string(), TokenType::Assign, self.line, original_col) + Token::new("!".to_string(), TokenType::Not, self.line, original_col) } } + fn minus_or_arrow(&mut self) -> Token { let original_col = self.column; self.advance(); @@ -200,7 +218,7 @@ impl Lexer { self.advance(); while let Some(ch) = self.current_char() { match ch { - 'A'..='Z' | 'a'..='z' | '_' => { + 'A'..='Z' | 'a'..='z' | '_' | '0'..='9' => { name.push(ch); self.advance(); } diff --git a/src/lexer/mod.rs b/compiler/src/lexer/mod.rs similarity index 100% rename from src/lexer/mod.rs rename to compiler/src/lexer/mod.rs diff --git a/src/lexer/token.rs b/compiler/src/lexer/token.rs similarity index 97% rename from src/lexer/token.rs rename to compiler/src/lexer/token.rs index b8697d9..d629a79 100644 --- a/src/lexer/token.rs +++ b/compiler/src/lexer/token.rs @@ -33,6 +33,7 @@ pub enum TokenType { GreaterThan, // > LessThan, // < Equals, // == + NotEquals, // != Plus, // + Minus, // - Multiply, // * @@ -102,6 +103,7 @@ impl fmt::Display for TokenType { TokenType::GreaterThan => write!(f, ">"), TokenType::LessThan => write!(f, "<"), TokenType::Equals => write!(f, "=="), + TokenType::NotEquals => write!(f, "!="), TokenType::Plus => write!(f, "+"), TokenType::Minus => write!(f, "-"), TokenType::Multiply => write!(f, "*"), diff --git a/compiler/src/main.rs b/compiler/src/main.rs new file mode 100644 index 0000000..432415c --- /dev/null +++ b/compiler/src/main.rs @@ -0,0 +1,57 @@ +mod codegen; +mod parser; +mod lexer; +mod semantic; + +use clap::Parser as Psr; +use std::fmt::format; +use std::fs; +use std::fs::File; +use std::string; +use lexer::token::Token; +use lexer::lexer::Lexer; // adjust if needed +use parser::parser::AST; +use parser::parser::Parser; +use codegen::codegen::CodeGenerator; + + +/// The triviC compiler +#[derive(Psr, Debug)] +#[command(name = "triviC", version, about = "Lexes and parses triviC source files", long_about = None)] +struct Args { + /// The source file to compile + filename: String, + + #[arg(short, long, default_value = "out.asm")] + output: String, + + #[arg(long)] + hard: bool +} + +fn main() { + // This entirely replaces the manual env::args() collection, + // the length check, and the manual error/exit logic. + let args = Args::parse(); + + let source = fs::read_to_string(&args.filename) + .expect("Failed to read file"); + + let output = File::options() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&args.output) + .expect(&format!("wrongdog output path {}", args.output)); + + let mut lexer: Lexer = Lexer::new(source); + let _tokens: Vec = lexer.tokenize(); + let mut parser: Parser = Parser::new(_tokens); + let _ast: AST = parser.parse_program(); + let mut codegen: CodeGenerator = CodeGenerator::new(output); + codegen.generate(_ast, args.hard); + + + println!("Lexing and parsing completed successfully."); +} diff --git a/src/parser/mod.rs b/compiler/src/parser/mod.rs similarity index 100% rename from src/parser/mod.rs rename to compiler/src/parser/mod.rs diff --git a/src/parser/parser.rs b/compiler/src/parser/parser.rs similarity index 84% rename from src/parser/parser.rs rename to compiler/src/parser/parser.rs index 708ba99..e93e171 100644 --- a/src/parser/parser.rs +++ b/compiler/src/parser/parser.rs @@ -1,4 +1,6 @@ -use crate::{lexer::token::{Token, TokenType}, semantic::symbol_table::SymbolTable}; +use crate::{ + lexer::token::{Token, TokenType}, +}; #[derive(Debug, Clone, PartialEq)] pub enum Stmt { Let(String, Type, Expr), @@ -13,7 +15,7 @@ pub enum Stmt { expr: Expr, block: Block, }, - Print(Expr), + Print(Vec), Return(Expr), } @@ -47,7 +49,7 @@ pub enum Expr { Identifier(String), BinaryOp(Box, BinOp, Box), UnaryOp(UnOp, Box), - Call(Vec), //I do not understand what this one is, but the expert recommended it + Call(Vec), } #[derive(Debug, Clone, PartialEq)] @@ -55,6 +57,7 @@ pub enum BinOp { Add, Sub, Mul, + Div, Equals, NotEquals, GreaterThan, @@ -64,6 +67,7 @@ pub enum BinOp { #[derive(Debug, Clone, PartialEq)] pub enum UnOp { Not, + Neg, } pub type AST = Vec; pub struct Parser { @@ -167,6 +171,8 @@ impl Parser { statements.push(self.parse_if()); } else if self.match_token(TokenType::While) { statements.push(self.parse_while()); + } else if self.match_token(TokenType::Print) { + statements.push(self.parse_print()); } else if self.match_token(TokenType::Identifier) && self.peek(TokenType::Assign) { statements.push(self.parse_assignment()); } else if self.match_token(TokenType::Return) { @@ -178,6 +184,25 @@ impl Parser { } Block { statements } } + fn parse_print(&mut self) -> Stmt { + // 'print' already matched by caller + self.consume(); // consume 'print' + let _ = self.expect(TokenType::LeftParen); + + let mut args: Vec = Vec::new(); + // allow empty argument list: print(); + if !self.match_token(TokenType::RightParen) { + args.push(self.parse_expression()); + while self.match_token(TokenType::Comma) { + self.consume(); // consume ',' + args.push(self.parse_expression()); + } + } + + let _ = self.expect(TokenType::RightParen); + let _ = self.expect(TokenType::Semicolon); + Stmt::Print(args) + } fn parse_return(&mut self) -> Stmt { self.consume(); let expr = self.parse_expression(); @@ -234,7 +259,9 @@ impl Parser { TokenType::Integer => Type::Integer, TokenType::Boolean => Type::Boolean, _ => panic!( - "Expected type, got something else at {}:{}", + "Expected type, got {:?} ({}) at {}:{}", + self.current().token_type, + self.current().value, self.current().line, self.current().column ), @@ -251,7 +278,9 @@ impl Parser { TokenType::Plus => BinOp::Add, TokenType::Minus => BinOp::Sub, TokenType::Multiply => BinOp::Mul, + TokenType::Division => BinOp::Div, TokenType::Equals => BinOp::Equals, + TokenType::NotEquals => BinOp::NotEquals, TokenType::GreaterThan => BinOp::GreaterThan, TokenType::LessThan => BinOp::LessThan, _ => panic!("Not a binary operator"), @@ -270,6 +299,7 @@ impl Parser { TokenType::GreaterThan, TokenType::LessThan, TokenType::Equals, + TokenType::NotEquals, ]) { let op_token = self.consume(); let op = self.token_to_binop(&op_token.token_type); @@ -296,7 +326,7 @@ impl Parser { } } TokenType::BooleanLiteral => { - if self.match_token(TokenType::Equals) { + if self.match_any(&[TokenType::Equals, TokenType::NotEquals]) { let op_token = self.consume(); let op = self.token_to_binop(&op_token.token_type); let right = self.parse_expression(); @@ -305,17 +335,9 @@ impl Parser { op, Box::new(right), ) - } else if self.match_token(TokenType::Not) { - let _ = self.expect(TokenType::Equals); - let op = BinOp::NotEquals; - let right = self.parse_expression(); - Expr::BinaryOp( - Box::new(Expr::BooleanLiteral(tok.value.parse::().unwrap())), - op, - Box::new(right), - ) } else { - Expr::BooleanLiteral(tok.value.parse::().unwrap()) + let val = tok.value.clone(); + Expr::BooleanLiteral(tok.value.to_lowercase().parse::().unwrap()) } } TokenType::StringLiteral => { @@ -345,6 +367,37 @@ impl Parser { let exprs = self.parse_expression(); Expr::UnaryOp(UnOp::Not, Box::new(exprs)) } + TokenType::Minus => match self.current().token_type { + TokenType::IntegerLiteral => { + let cur = self.consume(); + Expr::IntegerLiteral(-cur.value.parse::().unwrap()) + } + _ => { + let expr = self.parse_expression(); + Expr::UnaryOp(UnOp::Neg, Box::new(expr)) + } + }, + TokenType::LeftParen => { + let expr = self.parse_expression(); + let _ = self.expect(TokenType::RightParen); + if self.match_any(&[ + TokenType::Minus, + TokenType::Plus, + TokenType::Multiply, + TokenType::Division, + TokenType::GreaterThan, + TokenType::LessThan, + TokenType::Equals, + TokenType::NotEquals, + ]) { + let op_token = self.consume(); + let op = self.token_to_binop(&op_token.token_type); + let right = self.parse_expression(); + Expr::BinaryOp(Box::new(expr), op, Box::new(right)) + } else { + expr + } + } _ => panic!("Unexpected token {:?} in expression", tok.token_type), } } @@ -392,18 +445,16 @@ impl Parser { #[cfg(test)] mod tests { use crate::parser::parser::Parser; + use crate::parser::parser::Stmt; use crate::{ - lexer::{ - lexer::Lexer, - }, + lexer::lexer::Lexer, parser::parser::{BinOp, Block, Expr, Function, Type, AST}, }; - use crate::parser::parser::Stmt; #[test] fn test_parser_parses_correct_ast() { use std::fs; - let source = fs::read_to_string("test_codes/simple.trv").expect("Failed to read file"); + let source = fs::read_to_string("test_codes/test_correct_ast.trv").expect("Failed to read file"); let mut lexer = Lexer::new(source); let tokens = lexer.tokenize(); let mut parser = Parser::new(tokens); @@ -438,7 +489,7 @@ mod tests { block: Block { statements: vec![Stmt::AssignStatement( "num".to_string(), - Expr::IntegerLiteral(11), + Expr::IntegerLiteral(12), )], }, option: Some(Block { diff --git a/src/semantic/mod.rs b/compiler/src/semantic/mod.rs similarity index 100% rename from src/semantic/mod.rs rename to compiler/src/semantic/mod.rs diff --git a/src/semantic/symbol_table.rs b/compiler/src/semantic/symbol_table.rs similarity index 100% rename from src/semantic/symbol_table.rs rename to compiler/src/semantic/symbol_table.rs diff --git a/compiler/test_codes/simple.trv b/compiler/test_codes/simple.trv new file mode 100644 index 0000000..0205385 --- /dev/null +++ b/compiler/test_codes/simple.trv @@ -0,0 +1,22 @@ +func main() -> Boolean { + let num : Integer = 0; + let test : Boolean = True; + + while num < 10 do { + let etellerandet : Integer = 1; + num = num + etellerandet; + } + + if num > 10 { + num = 12; + let dingdong : Integer = 0; + } else { + num = 11; + } + + if test != True { + return test; + } else { + return num; + } +} diff --git a/compiler/test_codes/simple.trv.output b/compiler/test_codes/simple.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/compiler/test_codes/simple.trv.output @@ -0,0 +1 @@ +12 diff --git a/compiler/test_codes/test_correct_ast.trv b/compiler/test_codes/test_correct_ast.trv new file mode 100644 index 0000000..62abea2 --- /dev/null +++ b/compiler/test_codes/test_correct_ast.trv @@ -0,0 +1,13 @@ +func main() -> Integer { + let num : Integer = 0; + + while num < 10 do { + num = 11; + } + if num > 10 { + num = 12; + } else { + num = 11; + } + return num; +} diff --git a/compiler/test_codes/test_correct_ast.trv.output b/compiler/test_codes/test_correct_ast.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/compiler/test_codes/test_correct_ast.trv.output @@ -0,0 +1 @@ +12 diff --git a/test_codes/test_func_if_else.trv b/compiler/test_codes/test_if_else.trv similarity index 100% rename from test_codes/test_func_if_else.trv rename to compiler/test_codes/test_if_else.trv diff --git a/compiler/test_codes/test_if_else.trv.output b/compiler/test_codes/test_if_else.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/compiler/test_codes/test_if_else.trv.output @@ -0,0 +1 @@ +12 diff --git a/test_codes/test_main_let.trv b/compiler/test_codes/test_main_let.trv similarity index 100% rename from test_codes/test_main_let.trv rename to compiler/test_codes/test_main_let.trv diff --git a/compiler/test_codes/test_main_let.trv.output b/compiler/test_codes/test_main_let.trv.output new file mode 100644 index 0000000..f64f5d8 --- /dev/null +++ b/compiler/test_codes/test_main_let.trv.output @@ -0,0 +1 @@ +27 diff --git a/test_codes/test_main_return.trv b/compiler/test_codes/test_main_return.trv similarity index 100% rename from test_codes/test_main_return.trv rename to compiler/test_codes/test_main_return.trv diff --git a/compiler/test_codes/test_main_return.trv.output b/compiler/test_codes/test_main_return.trv.output new file mode 100644 index 0000000..b5489e5 --- /dev/null +++ b/compiler/test_codes/test_main_return.trv.output @@ -0,0 +1 @@ +69 diff --git a/compiler/test_codes/test_while.trv b/compiler/test_codes/test_while.trv new file mode 100644 index 0000000..db54eef --- /dev/null +++ b/compiler/test_codes/test_while.trv @@ -0,0 +1,7 @@ +func main() -> Integer { + let num : Integer = 9; + while num < 12 { + num = num + 1; + } + return num; +} diff --git a/compiler/test_codes/test_while.trv.output b/compiler/test_codes/test_while.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/compiler/test_codes/test_while.trv.output @@ -0,0 +1 @@ +12 diff --git a/compiler/test_codes/test_while_nested.trv b/compiler/test_codes/test_while_nested.trv new file mode 100644 index 0000000..7102f7f --- /dev/null +++ b/compiler/test_codes/test_while_nested.trv @@ -0,0 +1,11 @@ +func main() -> Integer { + let a : Integer = 0; + while a < 3 { + let b : Integer = 0; + while b < 2 { + b = b + 1; + } + a = a + 1; + } + return a; +} diff --git a/compiler/test_codes/test_while_nested.trv.output b/compiler/test_codes/test_while_nested.trv.output new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/compiler/test_codes/test_while_nested.trv.output @@ -0,0 +1 @@ +3 diff --git a/compiler/test_codes/test_while_simple.trv b/compiler/test_codes/test_while_simple.trv new file mode 100644 index 0000000..db54eef --- /dev/null +++ b/compiler/test_codes/test_while_simple.trv @@ -0,0 +1,7 @@ +func main() -> Integer { + let num : Integer = 9; + while num < 12 { + num = num + 1; + } + return num; +} diff --git a/compiler/test_codes/test_while_simple.trv.output b/compiler/test_codes/test_while_simple.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/compiler/test_codes/test_while_simple.trv.output @@ -0,0 +1 @@ +12 diff --git a/compiler/test_codes/test_while_two_loops.trv b/compiler/test_codes/test_while_two_loops.trv new file mode 100644 index 0000000..629c22c --- /dev/null +++ b/compiler/test_codes/test_while_two_loops.trv @@ -0,0 +1,11 @@ +func main() -> Integer { + let a : Integer = 0; + let b : Integer = 0; + while a < 3 { + a = a + 1; + } + while b < 4 { + b = b + 1; + } + return a+b; +} diff --git a/compiler/test_codes/test_while_two_loops.trv.output b/compiler/test_codes/test_while_two_loops.trv.output new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/compiler/test_codes/test_while_two_loops.trv.output @@ -0,0 +1 @@ +7 diff --git a/compiler/test_codes_compiled/simple.asm b/compiler/test_codes_compiled/simple.asm new file mode 100644 index 0000000..6070b1c --- /dev/null +++ b/compiler/test_codes_compiled/simple.asm @@ -0,0 +1,46 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + mov r0, #11 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_1 + mov r0, #11 + str r0, [sp] + b endif_1 +else_1: + mov r0, #11 + str r0, [sp] +endif_1: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/compiler/test_codes_compiled/test_main_let.asm b/compiler/test_codes_compiled/test_main_let.asm new file mode 100644 index 0000000..890e947 --- /dev/null +++ b/compiler/test_codes_compiled/test_main_let.asm @@ -0,0 +1,16 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #27 + str r0, [sp] + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/main.asm b/compiler/test_codes_compiled/test_main_return.asm similarity index 100% rename from main.asm rename to compiler/test_codes_compiled/test_main_return.asm diff --git a/compiler/test_codes_compiled/test_while.asm b/compiler/test_codes_compiled/test_while.asm new file mode 100644 index 0000000..af133c7 --- /dev/null +++ b/compiler/test_codes_compiled/test_while.asm @@ -0,0 +1,33 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #12 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/compiler/test_codes_compiled/test_while_nested.asm b/compiler/test_codes_compiled/test_while_nested.asm new file mode 100644 index 0000000..e8ede92 --- /dev/null +++ b/compiler/test_codes_compiled/test_while_nested.asm @@ -0,0 +1,54 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #2 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 +end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + add sp, sp, #4 + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/compiler/test_codes_compiled/test_while_simple.asm b/compiler/test_codes_compiled/test_while_simple.asm new file mode 100644 index 0000000..af133c7 --- /dev/null +++ b/compiler/test_codes_compiled/test_while_simple.asm @@ -0,0 +1,33 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #12 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/compiler/test_codes_compiled/test_while_two_loops.asm b/compiler/test_codes_compiled/test_while_two_loops.asm new file mode 100644 index 0000000..18d6895 --- /dev/null +++ b/compiler/test_codes_compiled/test_while_two_loops.asm @@ -0,0 +1,56 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: +while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #4 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 +end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #0] + add r0, r1, r0 + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/Cargo.lock b/interpreter/Cargo.lock new file mode 100644 index 0000000..2dd1dd9 --- /dev/null +++ b/interpreter/Cargo.lock @@ -0,0 +1,209 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thumb2_interpreter" +version = "0.1.0" +dependencies = [ + "clap", + "pretty_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml new file mode 100644 index 0000000..02732e6 --- /dev/null +++ b/interpreter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "thumb2_interpreter" +version = "0.1.0" +edition = "2024" + +[dev-dependencies] +pretty_assertions = "1.4" + +[dependencies] +clap = { version = "4.5.60", features = ["derive"] } diff --git a/interpreter/main.asm b/interpreter/main.asm new file mode 100644 index 0000000..7613098 --- /dev/null +++ b/interpreter/main.asm @@ -0,0 +1,455 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #11413 + str r0, [sp] + sub sp, sp, #4 + mov r0, #3533 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #113 + str r0, [sp] + sub sp, sp, #4 + mov r0, #59 + str r0, [sp] + sub sp, sp, #4 + mov r0, #97 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #-1 + str r0, [sp] + sub sp, sp, #4 + mov r0, #23 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #76] + str r0, [sp, #44] + ldr r0, [sp, #88] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_1: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_2: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_2 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_2 +end_while_2: +while_3: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_3 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_3 +end_while_3: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_4 + mov r0, #0 + str r0, [sp, #16] + b endif_4 +else_4: +endif_4: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_1 +end_while_1: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: + ldr r0, [sp, #36] + str r0, [sp, #60] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #72] + str r0, [sp, #44] + ldr r0, [sp, #84] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_5: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_5 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_6: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_6 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_7: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_7 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_7 +end_while_7: +while_8: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_8 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_8 +end_while_8: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_9 + mov r0, #0 + str r0, [sp, #16] + b endif_9 +else_9: +endif_9: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_6 +end_while_6: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_5 +end_while_5: + ldr r0, [sp, #36] + str r0, [sp, #56] + ldr r0, [sp, #60] + mov r1, r0 + ldr r0, [sp, #56] + sub r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #28] + ldr r0, [sp, #80] + str r0, [sp, #24] + ldr r0, [sp, #88] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_10: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_10 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_11: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_11 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_11 +end_while_11: +while_12: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_12 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_12 +end_while_12: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_13 + mov r0, #0 + str r0, [sp, #16] + b endif_13 +else_13: +endif_13: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_10 +end_while_10: + ldr r0, [sp, #16] + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #84] + mul r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #56] + add r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #68] + mov r0, #1 + ldr r1, =.Lstr0 + mov r2, #12 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_14 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_14 +print_int_loop_14: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_14 +print_int_done_14: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r7, #1 + svc #0 + +.size _start, .-_start + +.section .data +.Lstr0: + .ascii "gang_sign = " +newline: + .ascii "\n" +num_buf: + .space 16 diff --git a/interpreter/out.asm b/interpreter/out.asm new file mode 100644 index 0000000..f05f88c --- /dev/null +++ b/interpreter/out.asm @@ -0,0 +1,498 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #11413 + str r0, [sp] + sub sp, sp, #4 + mov r0, #3533 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #113 + str r0, [sp] + sub sp, sp, #4 + mov r0, #59 + str r0, [sp] + sub sp, sp, #4 + mov r0, #97 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #-1 + str r0, [sp] + sub sp, sp, #4 + mov r0, #23 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #76] + str r0, [sp, #44] + ldr r0, [sp, #88] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_1: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_2: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_2 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_2 +end_while_2: +while_3: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_3 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_3 +end_while_3: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_4 + mov r0, #0 + str r0, [sp, #16] + b endif_4 +else_4: +endif_4: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_1 +end_while_1: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: + ldr r0, [sp, #36] + str r0, [sp, #60] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #72] + str r0, [sp, #44] + ldr r0, [sp, #84] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_5: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_5 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_6: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_6 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_7: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_7 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_7 +end_while_7: +while_8: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_8 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_8 +end_while_8: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_9 + mov r0, #0 + str r0, [sp, #16] + b endif_9 +else_9: +endif_9: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_6 +end_while_6: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_5 +end_while_5: + ldr r0, [sp, #36] + str r0, [sp, #56] + ldr r0, [sp, #60] + mov r1, r0 + ldr r0, [sp, #56] + sub r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #28] + ldr r0, [sp, #80] + str r0, [sp, #24] + ldr r0, [sp, #88] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_10: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_10 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_11: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_11 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_11 +end_while_11: +while_12: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_12 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_12 +end_while_12: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_13 + mov r0, #0 + str r0, [sp, #16] + b endif_13 +else_13: +endif_13: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_10 +end_while_10: + ldr r0, [sp, #16] + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #84] + mul r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #56] + add r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #68] + mov r0, #1 + ldr r1, =.Lstr0 + mov r2, #15 + mov r7, #4 + svc #0 + mov r0, #1337 + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_14 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_14 +print_int_loop_14: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_14 +print_int_done_14: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =.Lstr1 + mov r2, #18 + mov r7, #4 + svc #0 + ldr r0, [sp, #100] + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_15 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_15 +print_int_loop_15: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_15 +print_int_done_15: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r7, #1 + svc #0 + +.size _start, .-_start + +.section .data +.Lstr0: + .ascii "g_sign herro = " +.Lstr1: + .ascii "g_countermeasure: " +newline: + .ascii "\n" +num_buf: + .space 16 diff --git a/interpreter/run b/interpreter/run new file mode 100755 index 0000000..36857f5 --- /dev/null +++ b/interpreter/run @@ -0,0 +1,49 @@ +#!/bin/bash + +set -euo pipefail + +# Always run from the interpreter crate directory, even if invoked from repo root. +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +rewrite_path_arg() { + local a="$1" + # If called from repo root like: ./interpreter/run interpreter/out.asm + if [[ "$a" == interpreter/* ]]; then + local stripped="${a#interpreter/}" + if [[ -f "$stripped" ]]; then + printf '%s' "$stripped" + return 0 + fi + fi + # If passed an absolute/relative path that doesn't exist from here, + # but the basename does (e.g., repo-root paths), fall back to basename. + if [[ ! -f "$a" ]]; then + local base + base="$(basename -- "$a")" + if [[ -f "$base" ]]; then + printf '%s' "$base" + return 0 + fi + fi + printf '%s' "$a" +} + +if [ "$1" = "--debug" ]; then + shift + if [[ $# -ge 1 ]]; then + first="$(rewrite_path_arg "$1")" + shift + cargo run -- "$first" "$@" + else + cargo run -- + fi +else + if [[ $# -ge 1 ]]; then + first="$(rewrite_path_arg "$1")" + shift + RUSTFLAGS="-A warnings" cargo run --release -- "$first" "$@" + else + RUSTFLAGS="-A warnings" cargo run --release -- + fi +fi diff --git a/interpreter/src/interpreter.rs b/interpreter/src/interpreter.rs new file mode 100644 index 0000000..8e9954b --- /dev/null +++ b/interpreter/src/interpreter.rs @@ -0,0 +1,1321 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::BufRead; +use std::process::exit; +use std::usize; + +use crate::memory::EmulatorMemory; + +const NUM_REGISTERS: usize = 32; + +#[derive(Clone, Debug, PartialEq, Default)] +pub struct ItState { + pub is_active: bool, + pub base_cond: String, + pub mask: Vec, + pub current_instr: usize, +} + +#[derive(Clone, Debug, PartialEq, Default)] +pub struct Cpsr { + pub n: bool, + pub z: bool, + pub c: bool, + pub v: bool, + pub it_state: ItState, +} +impl Cpsr { + pub fn evaluate_condition(&self, cond: &str) -> bool { + match cond.to_uppercase().as_str() { + "EQ" => self.z, + "NE" => !self.z, + "HS" | "CS" => self.c, + "LO" | "CC" => !self.c, + "MI" => self.n, + "PL" => !self.n, + "VS" => self.v, + "VC" => !self.v, + "HI" => self.c && !self.z, + "LS" => !self.c || self.z, + "GE" => self.n == self.v, + "LT" => self.n != self.v, + "GT" => !self.z && self.n == self.v, + "LE" => self.z || self.n != self.v, + "AL" => true, + _ => true, // Default to true if unknown + } + } + pub fn should_execute(&mut self) -> bool { + if !self.it_state.is_active { + return true; + } + + let step_idx = self.it_state.current_instr; + let is_then = self.it_state.mask[step_idx] == 't'; + let condition_met = self.evaluate_condition(&self.it_state.base_cond); + + let execute = if is_then { condition_met } else { !condition_met }; + + // Advance or Reset + self.it_state.current_instr += 1; + if self.it_state.current_instr >= self.it_state.mask.len() { + self.it_state.is_active = false; + self.it_state.mask.clear(); + } + + execute + } +} + +pub enum InjectionTarget { + Register { + register: usize, + bit: u32, + }, + Memory { + address: u32, + bit: u32, + }, + ProgramCounter { + bit: u32, + }, + Cpsr { + register: usize, + bit: u32, + }, + None, +} + +pub struct InjectionSpec { + pub target: InjectionTarget, + pub trigger_pc: i32, +} + +pub struct Interpreter { + memory: EmulatorMemory, + registers: [i32; NUM_REGISTERS], + pc: u32, + eof_pc: u32, + branch_map: HashMap, + data_map: HashMap, + cpsr: Cpsr, + file: Vec, + debug: bool, + start_time: std::time::Instant, + max_time: std::time::Duration, + fault_spec: InjectionSpec, +} + +impl Interpreter { + pub fn new() -> Self { + Self { + memory: EmulatorMemory::new(), + registers: [0; NUM_REGISTERS], + pc: 0, + branch_map: HashMap::new(), + data_map: HashMap::new(), + eof_pc: 0, + cpsr: Cpsr::default(), + file: Vec::new(), + debug: false, + start_time: std::time::Instant::now(), + max_time: std::time::Duration::from_millis(1000), + fault_spec: InjectionSpec { + target: InjectionTarget::None, + trigger_pc: -1, + }, + } + } + + pub fn inject(&mut self, injection_point: String) { + // FORMAT: X:Y:Z + // X = PC + // Y = Register + // Z = Bit to flip + let mut register: usize = usize::MAX; + let mut parts = injection_point.split(':'); + + // Get the first three parts + let fault_type = parts.next().ok_or("Missing PC value").unwrap(); + let pc = parts.next().ok_or("Missing PC value").unwrap().parse::().unwrap(); + if fault_type == "reg" { + register = parts + .next() + .ok_or("Missing Register value") + .unwrap() + .parse::() + .unwrap(); + } + let bit = parts.next().ok_or("Missing Bit value").unwrap().parse::().unwrap(); + + // CRITICAL: Check if there is a 5th part + if parts.next().is_some() { + panic!("Correct format for injection point is \"Type:X:Y:Z\""); + } + if register == usize::MAX && fault_type == "reg" { + panic!("Register is usize::MAX"); + } + let injection_target = if fault_type == "reg" { + InjectionTarget::Register { register, bit } + } else { + InjectionTarget::ProgramCounter { bit } + }; + self.fault_spec.trigger_pc = pc; + self.fault_spec.target = injection_target; + if fault_type == "reg" { + println!( + "Setup Injection Specification:\n Type: reg\n Trigger PC: {pc}\n Register: {register}\n Bit: {bit}" + ); + } else { + println!( + "Setup Injection Specification:\n Type: PC\n Trigger PC: {pc}\n Bit: {bit}" + ); + } + } + + pub fn set_debug(&mut self, debug: bool) { + self.debug = debug; + } + + pub fn set_max_time(&mut self, nanoseconds: u128) { + self.max_time = std::time::Duration::from_nanos_u128(nanoseconds); + } + + pub fn set_branch_map(&mut self, map: HashMap) { + self.branch_map = map; + } + + pub fn get_reg(&self, index: usize) -> i32 { + self.registers[index] + } + + pub fn set_reg(&mut self, index: usize, value: i32) { + self.registers[index] = value; + } + + pub fn get_pc(&self) -> u32 { + self.pc + } + + pub fn set_pc(&mut self, pc: u32) { + self.pc = pc; + } + + pub fn set_eof_pc(&mut self, pc: u32) { + self.eof_pc = pc; + } + pub fn read_file(&mut self, file_path: &String) -> bool { + let file = File::open(file_path).expect("Could not open file: {file_path}"); + let lines: Vec = std::io::BufReader + ::new(file) + .lines() + .map(|line| line.expect("Could not read line from file")) + .collect(); + self.file = lines; + true + } + + pub fn print_registers(&self) { + if self.debug { + for (i, &value) in self.registers.iter().enumerate() { + println!("r{}: {}", i, value); + } + } + } + + pub fn get_registers(&self) -> &[i32; NUM_REGISTERS] { + &self.registers + } + + pub fn print_memory(&self) { + if !self.debug { + return; + } + println!("╔══════════════════════════════════════════════════════════════════╗"); + println!("║ MEMORY STATE ║"); + println!("╠══════════════════════════════════════════════════════════════════╣"); + + println!("║ REGISTERS: ║"); + println!("╟──────────────────────────────────────────────────────────────────╢"); + for (i, &value) in self.registers.iter().enumerate() { + if i % 4 == 0 { + print!("║ "); + } + if i < 10 { + print!("r{} : {:<10} ", i, value); + } else { + print!("r{}: {:<10} ", i, value); + } + if i % 4 == 3 { + println!(" ║"); + } + } + + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ CPSR FLAGS: ║"); + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ Z (Zero): {:<5} ║", self.cpsr.z); + println!("║ N (Negative): {:<5} ║", self.cpsr.n); + println!("║ C (Carry): {:<5} ║", self.cpsr.c); + println!("║ V (Overflow): {:<5} ║", self.cpsr.v); + + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ PROGRAM COUNTER: ║"); + println!( + "║ PC: {:>10} (0x{:08x}) ║", + self.pc, + self.pc + ); + + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ STACK POINTER: ║"); + println!( + "║ SP: {:>10} (0x{:08x}) ║", + self.memory.get_sp(), + self.memory.get_sp() + ); + + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ HEAP: ║"); + println!( + "║ Heap allocated: {:>6} bytes ║", + self.memory.get_heap_alloc_index() + ); + println!( + "║ Heap size: {:>6} bytes ║", + self.memory.get_heap_size() + ); + + let heap_data = self.memory.get_heap(); + if self.memory.get_heap_alloc_index() > 0 { + println!("║ Heap contents (first 256 bytes): ║"); + let display_len = std::cmp::min(256, self.memory.get_heap_alloc_index()); + for row in (0..display_len).step_by(16) { + let end = std::cmp::min(row + 16, display_len); + print!("║ {:04x}: ", row); + for i in row..end { + print!("{:02x} ", heap_data[i]); + } + for _ in end..row + 16 { + print!(" "); + } + print!(" |"); + for i in row..end { + let byte = heap_data[i]; + if (32..127).contains(&byte) { + print!("{}", byte as char); + } else { + print!("."); + } + } + for _ in end..row + 16 { + print!(" "); + } + println!("|║"); + } + } + + println!("╟──────────────────────────────────────────────────────────────────╢"); + println!("║ STACK: ║"); + println!( + "║ Stack size: {:>6} bytes ║", + self.memory.get_stack().len() + ); + println!( + "║ SP: {:>10} (0x{:08x}) ║", + self.memory.get_sp(), + self.memory.get_sp() + ); + + let stack_data = self.memory.get_stack(); + let sp = self.memory.get_sp(); + let display_start = sp.saturating_sub(64); + let display_end = std::cmp::min(sp + 64, stack_data.len()); + if display_end > 0 { + println!("║ Stack contents (from sp-64 to sp+64): ║"); + for row in (display_start..display_end).step_by(16) { + let end = std::cmp::min(row + 16, display_end); + print!("║ {:04x}: ", row); + for i in row..end { + print!("{:02x} ", stack_data[i]); + } + for _ in end..row + 16 { + print!(" "); + } + print!(" |"); + for i in row..end { + let byte = stack_data[i]; + if (32..127).contains(&byte) { + print!("{}", byte as char); + } else { + print!("."); + } + } + for _ in end..row + 16 { + print!(" "); + } + println!("|║"); + } + } + + println!("╚══════════════════════════════════════════════════════════════════╝"); + } + + pub fn shout_file(self) { + println!("------------------- SHOUT FILE START -------------------"); + for (idx, line_content) in self.file.iter().enumerate() { + println!("Line {idx}, Content: {line_content}"); + } + println!("-------------------- SHOUT FILE END --------------------"); + } + + fn get_start(&mut self) -> Vec { + let mut result = Vec::new(); + let mut branch_map: HashMap = HashMap::new(); + let mut found_start = false; + + for line in &self.file { + match line.starts_with("_start:") { + true => { + found_start = true; + let label = line.trim_start().to_string(); + result.push(label.clone()); + branch_map.insert( + label[0..label.len() - 1].to_string(), + (result.len() - 1) as u32 + ); + } + false => { + if found_start { + if line.starts_with(".size _start") { + result.pop(); + break; + } else if line.starts_with(".section .data") || line.starts_with(".data") { + break; + } else { + let label = line.trim_start().to_string(); + result.push(line.trim_start().to_string()); + if line.contains(":") { + branch_map.insert( + label[0..label.len() - 1].to_string(), + (result.len() - 1) as u32 + ); + } + } + } + } + } + } + + self.set_eof_pc(result.len() as u32); + self.set_branch_map(branch_map); + result + } + + fn parse_data_section(&mut self) { + let mut in_data_section = false; + let mut current_addr = 0; + let mut last_label: Option = None; + + for line in &self.file { + let line = line.trim(); + + if line.starts_with(".section .data") || line == ".data" { + in_data_section = true; + continue; + } + + if line.starts_with(".text") || line.starts_with(".section .text") { + in_data_section = false; + continue; + } + + if in_data_section { + // A label definition line is of the form `label:` (colon at end). + // Don't treat directives like `.ascii "a:b"` as a label just because + // the string literal contains ':'. + if line.ends_with(':') { + let label = line.trim_end_matches(':').to_string(); + self.data_map.insert(label.clone(), current_addr); + last_label = Some(label); + } else if line.contains(".ascii") { + if let Some(_) = line.find("\"") { + let start = line.find("\"").unwrap() + 1; + let end = start + line[start..].find("\"").unwrap(); + let string_data = &line[start..end]; + + // The assembler interprets escapes inside ".ascii" strings (e.g., "\n"). + // We store the resulting bytes into the emulated data/heap region. + let mut chars = string_data.chars().peekable(); + while let Some(ch) = chars.next() { + if ch == '\\' { + let esc = chars.next().unwrap_or('\\'); + let byte = match esc { + 'n' => b'\n', + 'r' => b'\r', + 't' => b'\t', + '0' => b'\0', + '\\' => b'\\', + '"' => b'"', + // Unknown escape: keep as-is (backslash + char) + other => { + self.memory.heap[current_addr] = b'\\'; + current_addr += 1; + other as u8 + } + }; + self.memory.heap[current_addr] = byte; + current_addr += 1; + } else { + self.memory.heap[current_addr] = ch as u8; + current_addr += 1; + } + } + } + } else if line.contains(".space") { + if let Some(size_str) = line.split(".space").nth(1) { + let size: usize = size_str.trim().parse().unwrap_or(0); + for _ in 0..size { + self.memory.heap[current_addr] = 0; + current_addr += 1; + } + } + } + } + } + } + + fn exec_mov(&mut self, content: String) { + if self.debug { + println!("Executing mov instruction: {}", content); + } + let parts: Vec<&str> = content.split_whitespace().collect(); + let dest = parts[1].replace(",", ""); + let src = parts[2].replace(",", ""); + + let dest_idx: usize = dest[1..].parse().expect("Failed to parse register index"); + + if let Some(value_str) = src.strip_prefix('#') { + let value: i32 = value_str.parse().expect("Failed to parse immediate value"); + self.set_reg(dest_idx, value); + if self.debug { + println!("Mock: mov r{}, #{} (stored in register)", dest_idx, value); + } + } else { + let src_idx: usize = src[1..].parse().expect("Failed to parse register index"); + let value = self.get_reg(src_idx); + self.set_reg(dest_idx, value); + if self.debug { + println!("Mock: mov r{}, r{} (register to register)", dest_idx, src_idx); + } + } + } + + fn exec_svc(&mut self, content: String) -> Option { + if self.debug { + println!("Executing svc instruction: {}", content); + } + + let parts: Vec<&str> = content.split_whitespace().collect(); + if let Some(svc_num) = parts.get(1) { + let svc_num = svc_num.replace("#", ""); + if svc_num == "0" { + let syscall_num = self.get_reg(7); + if syscall_num == 1 { + let exit_code = self.get_reg(0); + if self.debug { + println!("Exit syscall (r7=1), returning exit code: {}", exit_code); + } + return Some(exit_code); + } else if syscall_num == 4 { + let fd = self.get_reg(0); + let addr = self.get_reg(1) as usize; + let len = self.get_reg(2) as usize; + + if fd == 1 { + let data = self.memory.read_heap(addr, len); + print!("{}", String::from_utf8_lossy(&data)); + use std::io::Write; + std::io::stdout().flush().ok(); + } + } + } + } + None + } + + fn exec_sub(&mut self, content: String) { + let parts: Vec<&str> = content.split_whitespace().collect(); + let dest = parts[1].replace(",", ""); + let src = parts[2].replace(",", ""); + let val_or_reg = parts[3].replace(",", ""); + let sub_value: i32; + if let Some(value_str) = val_or_reg.strip_prefix('#') { + let value_str = value_str.replace("#", ""); + sub_value = value_str.parse().expect("literal"); + } else { + let value_str = val_or_reg.replace("r", ""); + let this_reg: usize = value_str.parse().expect("Failed to parse register index"); + sub_value = self.get_reg(this_reg); + } + + let src_val: i32; + if let Some(src_str) = src.strip_prefix('r') { + let src_register = src_str.replace("r", "").parse().expect("Unable to parse register"); + src_val = self.get_reg(src_register); + } else { + src_val = self.memory.get_sp() as i32; + } + + if let Some(dst_str) = dest.strip_prefix('r') { + let dst_register = dst_str.replace("r", "").parse().expect("Unable to parse register"); + let result = src_val - sub_value; + self.set_reg(dst_register, result); + } else if src == "sp" { + self.memory.set_sp((src_val - sub_value) as usize); + } + } + + fn exec_str(&mut self, content: String) { + if self.debug { + println!("Executing str instruction: {}", content); + } + let parts: Vec<&str> = content.split_whitespace().collect(); + let dest_reg = parts[1].replace(",", ""); + let dest_idx: usize = dest_reg[1..].parse().expect("Failed to parse register index"); + let value = self.get_reg(dest_idx); + + let addr_part = content + .split_once('[') + .and_then(|(_, rest)| rest.split_once(']')) + .map(|(inside, _)| inside) + .unwrap_or(""); + + let mut base_addr: usize = 0; + let mut offset: i32 = 0; + let mut base_reg_name = String::new(); + + for part in addr_part.split(',') { + let part = part.trim(); + if part == "sp" || part.starts_with('r') { + base_reg_name = part.to_string(); + } else if let Some(imm) = part.strip_prefix('#') { + offset = imm.parse().expect("Failed to parse offset"); + } + } + + if base_reg_name == "sp" { + base_addr = self.memory.get_sp(); + } else if let Some(reg_num) = base_reg_name.strip_prefix('r') { + let reg_idx: usize = reg_num.parse().expect("Failed to parse register index"); + base_addr = self.get_reg(reg_idx) as usize; + } + + let effective_addr = ((base_addr as i32) + offset) as usize; + self.memory.write_stack32_at(effective_addr, value as u32); + + if self.debug { + println!("Stored value {} at address {}", value, effective_addr); + let stack_data = self.memory.get_stack(); + let sp = self.memory.get_sp(); + println!( + " SP: {:>10}, stack[SP]: {:02x} {:02x} {:02x} {:02x} = {}", + sp, + stack_data[sp], + stack_data[sp + 1], + stack_data[sp + 2], + stack_data[sp + 3], + u32::from_le_bytes([ + stack_data[sp], + stack_data[sp + 1], + stack_data[sp + 2], + stack_data[sp + 3], + ]) + ); + } + } + + fn exec_ldr(&mut self, content: String) { + if self.debug { + println!("Executing ldr instruction: {}", content); + } + + // Handle "ldr rN, =label" form (load address of label) + if content.contains("=.") || content.contains("=num_buf") || content.contains("=newline") { + let parts: Vec<&str> = content.split_whitespace().collect(); + let dest_reg = parts[1].replace(",", ""); + let dest_idx: usize = dest_reg[1..] + .parse() + .expect("Failed to parse destination register index"); + + // Try to find the label + let mut label_found = false; + + if let Some(label_start) = content.find("=.") { + let label = &content[label_start + 1..]; + let label = label.trim().trim_end_matches('\n'); + if let Some(&addr) = self.data_map.get(label) { + self.set_reg(dest_idx, addr as i32); + label_found = true; + if self.debug { + println!("Loaded label address {} for {} into r{}", addr, label, dest_idx); + } + } + } + + if !label_found && content.contains("=num_buf") { + if let Some(&addr) = self.data_map.get("num_buf") { + self.set_reg(dest_idx, addr as i32); + label_found = true; + if self.debug { + println!("Loaded label address {} for num_buf into r{}", addr, dest_idx); + } + } + } + + if !label_found && content.contains("=newline") { + if let Some(&addr) = self.data_map.get("newline") { + self.set_reg(dest_idx, addr as i32); + if self.debug { + println!("Loaded label address {} for newline into r{}", addr, dest_idx); + } + } + } + + return; + } + + // 1. Parse the parts (e.g., "ldr", "r0,", "[sp, #0]") + let parts: Vec<&str> = content.split_whitespace().collect(); + + // 2. Identify the destination register (e.g., "r0") + let dest_reg = parts[1].replace(",", ""); + let dest_idx: usize = dest_reg[1..] + .parse() + .expect("Failed to parse destination register index"); + + // 3. Extract the address contents between '[' and ']' + // Handles forms like: [sp], [sp,#4], [sp, #4] + let addr_part = content + .split_once('[') + .and_then(|(_, rest)| rest.split_once(']')) + .map(|(inside, _)| inside) + .unwrap_or(""); + + let mut base_addr: usize = 0; + let mut offset: i32 = 0; + let mut base_reg_name = String::new(); + + // 4. Parse the base register and optional immediate offset + for part in addr_part.split(',') { + let part = part.trim(); + if part == "sp" || part.starts_with('r') { + base_reg_name = part.to_string(); + } else if let Some(imm) = part.strip_prefix('#') { + offset = imm.parse().expect("Failed to parse offset"); + } + } + + // 5. Calculate the actual base address from the register + if base_reg_name == "sp" { + base_addr = self.memory.get_sp(); + } else if let Some(reg_num) = base_reg_name.strip_prefix('r') { + let reg_idx: usize = reg_num.parse().expect("Failed to parse base register index"); + base_addr = self.get_reg(reg_idx) as usize; + } + + // 6. Calculate the effective address + let effective_addr = ((base_addr as i32) + offset) as usize; + + // 7. Load the value from memory and update the register + let value = self.memory.read_stack32_at(effective_addr); + self.set_reg(dest_idx, value as i32); + + if self.debug { + println!("Loaded value {} from address {} into r{}", value, effective_addr, dest_idx); + } + } + + fn exec_cmp(&mut self, content: String) { + if self.debug { + println!("Executing cmp instruction: {}", content); + } + let pc = self.pc; + + let parts: Vec<&str> = content + .split(|c: char| (c == ',' || c.is_whitespace())) + .filter(|s| !s.is_empty()) + .collect(); + + if parts.len() < 3 { + return; + } + + let rn_idx: usize = parts[1][1..].parse().expect("Failed to Rn index"); + let val_n = self.get_reg(rn_idx); + + let val_op2 = if let Some(imm_str) = parts[2].strip_prefix('#') { + imm_str.parse::().expect("Failed to parse immediate") + } else { + let rm_idx: usize = parts[2][1..].parse().expect("Failed to parse Rm index"); + self.get_reg(rm_idx) + }; + let result = val_n.wrapping_sub(val_op2); + + self.cpsr.z = result == 0; + self.cpsr.n = result < 0; + self.cpsr.c = (val_n as u32) >= (val_op2 as u32); + + let (_, overflow) = val_n.overflowing_sub(val_op2); + self.cpsr.v = overflow; + + if self.debug { + println!( + "CMP Result: {:#x} - {:#x} = {:#x} | Flags: N:{} Z:{} C:{} V:{}", + val_n, + val_op2, + result, + self.cpsr.n, + self.cpsr.z, + self.cpsr.c, + self.cpsr.v + ); + } + } + + fn exec_itx(&mut self, content: String) { + let parts: Vec<&str> = content.split_whitespace().collect(); + let mnemonic = parts[0].to_lowercase(); // e.g., "itete" + let cond = parts[1].to_uppercase(); // e.g., "GT" + + self.cpsr.it_state.is_active = true; + self.cpsr.it_state.base_cond = cond; + self.cpsr.it_state.current_instr = 0; + + // Convert "itete" into ['T', 'E', 'T', 'E'] + self.cpsr.it_state.mask = mnemonic.chars().skip(1).collect(); + } + + //NOTE: Maybe we could just use one single exec_b, and have it check if the instruction is beq + //or ble etc. and use the cpsr register to do stuff instead of a million branch function for + //each conditional version + fn exec_b(&mut self, content: String) { + // trim instruction to branch or branch + condition + let parts: Vec<&str> = content.split_whitespace().collect(); + if parts[0].len() > 1 { + let cond = &parts[0][1..]; + let is_cond = self.cpsr.evaluate_condition(cond); + if is_cond { + let branch_pc = self.branch_map.get(parts[1]).unwrap().clone(); + self.set_pc(branch_pc); + } else { + self.set_pc(self.get_pc() + 1); + return; + } + } else { + let branch_pc = self.branch_map.get(parts[1]).unwrap().clone(); + self.set_pc(branch_pc); + } + } + + fn exec_add(&mut self, content: String) { + if self.debug { + println!("Executing add instruction: {}", content); + } + let parts: Vec<&str> = content.split_whitespace().collect(); + + let dest = parts[1].replace(",", ""); + let op1 = parts[2].replace(",", ""); + let op2 = parts[3].replace(",", ""); + + let dest_idx: usize = if dest == "sp" { 13 } else { dest[1..].parse().unwrap() }; + + let src_val: i32 = if op1 == "sp" { + self.memory.get_sp() as i32 + } else { + let src_idx: usize = op1[1..].parse().unwrap(); + self.get_reg(src_idx) + }; + + let value = if let Some(imm) = op2.strip_prefix('#') { + imm.parse::().unwrap() + } else { + let reg_idx: usize = if op2 == "sp" { 13 } else { op2[1..].parse().unwrap() }; + self.get_reg(reg_idx) + }; + + let result = src_val.wrapping_add(value); + + if dest == "sp" { + self.memory.set_sp(result as usize); + } else { + self.set_reg(dest_idx, result); + } + } + + fn exec_mul(&mut self, content: String) { + let parts: Vec<&str> = content.split_whitespace().collect(); + + let dest = parts[1].replace(",", ""); + let op1 = parts[2].replace(",", ""); + let op2 = parts[3].replace(",", ""); + + let dest_idx: usize = dest[1..].parse().unwrap(); + + let src_val: i32 = { + let src_idx: usize = op1[1..].parse().unwrap(); + self.get_reg(src_idx) + }; + + let value: i32 = { + let reg_idx: usize = op2[1..].parse().unwrap(); + self.get_reg(reg_idx) + }; + + let result = src_val.wrapping_mul(value); + self.set_reg(dest_idx, result); + } + + fn exec_sdiv(&mut self, content: String) { + if self.debug { + println!("Executing sdiv instruction: {}", content); + } + let parts: Vec<&str> = content.split_whitespace().collect(); + + let dest = parts[1].replace(",", ""); + let op1 = parts[2].replace(",", ""); + let op2 = parts[3].replace(",", ""); + + let dest_idx: usize = dest[1..].parse().unwrap(); + + let src_val: i32 = { + let src_idx: usize = op1[1..].parse().unwrap(); + self.get_reg(src_idx) + }; + + let value: i32 = { + let reg_idx: usize = op2[1..].parse().unwrap(); + self.get_reg(reg_idx) + }; + + let result = src_val / value; + self.set_reg(dest_idx, result); + } + + fn exec_strb(&mut self, content: String) { + if self.debug { + println!("Executing strb instruction: {}", content); + } + let parts: Vec<&str> = content.split_whitespace().collect(); + let src_reg = parts[1].replace(",", ""); + let src_idx: usize = src_reg[1..].parse().expect("Failed to parse register index"); + let value = self.get_reg(src_idx) as u8; + + let addr_part = content + .split_once('[') + .and_then(|(_, rest)| rest.split_once(']')) + .map(|(inside, _)| inside) + .unwrap_or(""); + + let mut base_addr: usize = 0; + let mut offset: i32 = 0; + let mut base_reg_name = String::new(); + + for part in addr_part.split(',') { + let part = part.trim(); + if part == "sp" || part.starts_with('r') { + base_reg_name = part.to_string(); + } else if let Some(imm) = part.strip_prefix('#') { + offset = imm.parse().expect("Failed to parse offset"); + } + } + + if base_reg_name == "sp" { + base_addr = self.memory.get_sp(); + } else if let Some(reg_num) = base_reg_name.strip_prefix('r') { + let reg_idx: usize = reg_num.parse().expect("Failed to parse register index"); + base_addr = self.get_reg(reg_idx) as usize; + } + + let effective_addr = ((base_addr as i32) + offset) as usize; + + if effective_addr < self.memory.heap.len() { + self.memory.heap[effective_addr] = value; + } else if effective_addr < self.memory.stack.len() { + self.memory.stack[effective_addr] = value; + } + } + + fn flip_bit(&self, flipee: i32, bit: u32) -> i32 { + let mask = 1 << bit; + flipee ^ mask + } + fn flip_bit_u32(&self, flipee: u32, bit: u32) -> u32 { + let mask = 1 << bit; + flipee ^ mask + } + fn trigger_register_fault(&mut self, register: usize, bit: u32) { + let old_val = self.registers[register]; + self.registers[register] = self.flip_bit(old_val, bit); + println!("REGISTER FAULT TRIGGERED!"); + } + + pub fn execute(&mut self) -> u32 { + self.parse_data_section(); + let start_block = self.get_start(); + if self.debug { + eprintln!("DEBUG: start_block = {:?}", start_block); + } + while self.pc < self.eof_pc { + let instruction = start_block.get(self.pc as usize).unwrap(); + + if (self.pc as i32) == self.fault_spec.trigger_pc { + match self.fault_spec.target { + InjectionTarget::Register { register, bit } => { + self.trigger_register_fault(register, bit); + } + InjectionTarget::ProgramCounter { bit } => { + self.pc = self.flip_bit_u32(self.pc, bit); + continue; + } + _ => panic!("Injection type not implemented yet."), + } + } + + if self.debug { + eprintln!("DEBUG: PC={}, instruction={}", self.pc, instruction); + } + if self.start_time.elapsed().as_nanos() > self.max_time.as_nanos() { + println!("Detected Infinite Loop"); + exit(88); + } + if !self.cpsr.should_execute() { + if self.debug { + println!(" -> Condition not met, skipping."); + } + self.set_pc(self.pc + 1); + continue; // Skip the match logic entirely + } + match instruction { + f if f.starts_with("mov") => self.exec_mov(instruction.clone()), + f if f.starts_with("svc") => { + if let Some(exit_code) = self.exec_svc(instruction.clone()) { + return (exit_code as u32) & 0xff; + } + } + f if f.starts_with("sub") => self.exec_sub(instruction.clone()), + // Important: check "strb" before "str" since "strb".starts_with("str") is true. + f if f.starts_with("strb") => self.exec_strb(instruction.clone()), + f if f.starts_with("str") => self.exec_str(instruction.clone()), + f if f.starts_with("ldr") => self.exec_ldr(instruction.clone()), + f if f.starts_with("cmp") => self.exec_cmp(instruction.clone()), + f if f.starts_with("it") => self.exec_itx(instruction.clone()), + f if f.starts_with("b") => { + self.exec_b(instruction.clone()); + continue; + } + f if f.starts_with("add") => self.exec_add(instruction.clone()), + f if f.starts_with("mul") => self.exec_mul(instruction.clone()), + f if f.starts_with("sdiv") => self.exec_sdiv(instruction.clone()), + f if f.contains(":") => {} + invalid => panic!("Invalid instruction: {invalid}"), + } + self.set_pc(self.pc + 1); + } + 0 + } +} + +impl Default for Interpreter { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn create_interpreter() -> Interpreter { + Interpreter::new() + } + use std::fs; + use std::path::Path; + use std::process::Command; + + fn run_qemu(asm_path: &str) -> i32 { + let path = Path::new(asm_path); + let stem = path.file_stem().unwrap().to_str().unwrap(); + + let obj = format!("/tmp/{}.o", stem); + let bin = format!("/tmp/{}", stem); + + // Assemble + let status = Command::new("arm-none-eabi-as") + .args(["-mthumb", "-o", &obj, asm_path]) + .status() + .expect("Failed to run assembler"); + + assert!(status.success(), "Assembler failed for {}", asm_path); + + // Link + let status = Command::new("arm-none-eabi-ld") + .args(["-o", &bin, &obj]) + .status() + .expect("Failed to run linker"); + + assert!(status.success(), "Linker failed for {}", asm_path); + + // Run in qemu + let status = Command::new("qemu-arm").arg(&bin).status().expect("Failed to run qemu"); + + status.code().unwrap_or(-1) + } + + #[test] + fn test_qemu_vs_interpreter() { + let test_dir = Path::new("test_codes_compiled"); + + for entry in fs::read_dir(test_dir).expect("Failed to read test directory") { + let entry = entry.unwrap(); + let path = entry.path(); + + if path.extension().and_then(|s| s.to_str()) != Some("asm") { + continue; + } + + let file_path = path.to_str().unwrap(); + + // Run qemu + let qemu_exit = run_qemu(file_path); + // Run interpreter + let mut interp = Interpreter::new(); + interp.read_file(&file_path.to_string()); + let interp_exit = interp.execute(); + + assert_eq!(qemu_exit as u32, interp_exit, "Mismatch for {}", file_path); + } + } + #[test] + fn test_mov_immediate() { + let mut interp = create_interpreter(); + interp.exec_mov("mov r0, #42".to_string()); + assert_eq!(interp.get_reg(0), 42); + } + + #[test] + fn test_mov_register_to_register() { + let mut interp = create_interpreter(); + interp.set_reg(1, 99); + interp.exec_mov("mov r0, r1".to_string()); + assert_eq!(interp.get_reg(0), 99); + } + + #[test] + fn test_mov_zero() { + let mut interp = create_interpreter(); + interp.set_reg(0, 42); + interp.exec_mov("mov r0, #0".to_string()); + assert_eq!(interp.get_reg(0), 0); + } + + #[test] + fn test_sub_sp() { + let mut interp = create_interpreter(); + let initial_sp = interp.memory.get_sp(); + interp.exec_sub("sub sp, sp, #4".to_string()); + assert_eq!(interp.memory.get_sp(), initial_sp - 4); + } + + #[test] + fn test_sub_with_register() { + let mut interp = create_interpreter(); + interp.set_reg(1, 8); + let initial_sp = interp.memory.get_sp(); + interp.exec_sub("sub sp, sp, r1".to_string()); + assert_eq!(interp.memory.get_sp(), initial_sp - 8); + } + + #[test] + fn test_str_store_to_stack() { + let mut interp = create_interpreter(); + interp.set_reg(0, 42); + let initial_sp = interp.memory.get_sp(); + interp.memory.set_sp(initial_sp - 4); + + interp.exec_str("str r0, [sp]".to_string()); + + let value = interp.memory.read_stack32(0); + assert_eq!(value, 42); + } + + #[test] + fn test_ldr_load_from_stack() { + let mut interp = create_interpreter(); + let initial_sp = interp.memory.get_sp(); + interp.memory.set_sp(initial_sp - 4); + interp.memory.write_stack32(0, 123); + + interp.exec_ldr("ldr r0, [sp]".to_string()); + + assert_eq!(interp.get_reg(0), 123); + } + + #[test] + fn test_svc_exit_with_code() { + let mut interp = create_interpreter(); + interp.set_reg(7, 1); + interp.set_reg(0, 42); + + let result = interp.exec_svc("svc #0".to_string()); + + assert_eq!(result, Some(42)); + } + + #[test] + fn test_svc_ignores_other_syscalls() { + let mut interp = create_interpreter(); + interp.set_reg(7, 2); + + let result = interp.exec_svc("svc #0".to_string()); + + assert_eq!(result, None); + } + + #[test] + fn test_get_start_single_function() { + let mut interp = create_interpreter(); + interp.file = vec![ + "_start:".to_string(), + " mov r0, #1".to_string(), + " svc #0".to_string() + ]; + + let start_block = interp.get_start(); + let test_label = "_start:".to_string(); + assert!(start_block.contains(&test_label)); + assert_eq!(start_block.len(), 3); + } + + #[test] + fn test_execute_simple_program() { + let mut interp = create_interpreter(); + interp.file = vec![ + "_start:".to_string(), + " mov r0, #10".to_string(), + " mov r7, #1".to_string(), + " svc #0".to_string() + ]; + + let exit_code = interp.execute(); + + assert_eq!(exit_code, 10); + } + + #[test] + fn test_execute_with_stack_operations() { + let mut interp = create_interpreter(); + interp.file = vec![ + "_start:".to_string(), + " sub sp, sp, #4".to_string(), + " mov r0, #42".to_string(), + " str r0, [sp]".to_string(), + " ldr r1, [sp]".to_string(), + " mov r0, r1".to_string(), + " mov r7, #1".to_string(), + " svc #0".to_string() + ]; + + let exit_code = interp.execute(); + + assert_eq!(exit_code, 42); + } + + #[test] + fn test_cmp_logic() { + let mut interp = create_interpreter(); + + // 1. Test Equality (Z flag) + interp.set_reg(1, 100); + interp.exec_cmp("cmp r1, #100".to_string()); + assert!(interp.cpsr.z, "Z should be true when values are equal"); + assert!(!interp.cpsr.n, "N should be false when values are equal"); + assert!(interp.cpsr.c, "C should be true (no borrow) when equal"); + + // 2. Test Less Than (N flag) + interp.set_reg(1, 50); + interp.exec_cmp("cmp r1, #100".to_string()); + assert!(!interp.cpsr.z, "Z should be false when not equal"); + assert!(interp.cpsr.n, "N should be true because 50 - 100 is negative"); + assert!(!interp.cpsr.c, "C should be false because 50 < 100 (borrow occurred)"); + + // 3. Test Greater Than (Positive result) + interp.set_reg(1, 200); + interp.exec_cmp("cmp r1, #100".to_string()); + assert!(!interp.cpsr.z); + assert!(!interp.cpsr.n); + assert!(interp.cpsr.c, "C should be true because 200 >= 100"); + + // 4. Test Signed Overflow (V flag) + // Large positive minus a large negative results in a value + // too big for 32-bit signed integer (wraps around) + interp.set_reg(1, 0x7fffffff); // Max Positive i32 + interp.set_reg(2, -1); // -1 in two's complement + // Math: 0x7FFFFFFF - (-1) = 0x80000000 (which is -2147483648 in signed) + interp.exec_cmp("cmp r1, r2".to_string()); + assert!(interp.cpsr.v, "V should be true due to signed overflow"); + assert!(interp.cpsr.n, "N should be true because result wrapped to 0x80000000"); + } + #[test] + fn test_it_block_execution_logic() { + let mut interp = create_interpreter(); + + // 1. Setup the IT block: ITE GT (3 instructions total) + // First 'T' = GT, second 'E' = LE (Not GT), third 'E' = LE + interp.exec_itx("iteee gt".to_string()); + + assert!(interp.cpsr.it_state.is_active); + assert_eq!(interp.cpsr.it_state.base_cond, "GT"); + assert_eq!(interp.cpsr.it_state.mask, vec!['t', 'e', 'e', 'e']); + + // 2. Scenario A: Condition is TRUE (R1 > R0) + interp.set_reg(1, 100); + interp.set_reg(0, 50); + interp.exec_cmp("cmp r1, r0".to_string()); // Sets flags for GT + + // We expect: Step 0 (T) -> Execute, Step 1 (E) -> Skip + assert!(interp.cpsr.evaluate_condition("GT"), "GT should be true"); + + // Test Instruction 1 (T) + let should_run_1 = interp.cpsr.should_execute(); + assert!(should_run_1, "Instruction 1 (T) should run when GT is true"); + assert_eq!(interp.cpsr.it_state.current_instr, 1); + + // Test Instruction 2 (E) + let should_run_2 = interp.cpsr.should_execute(); + assert!(!should_run_2, "Instruction 2 (E) should NOT run when GT is true"); + assert_eq!(interp.cpsr.it_state.current_instr, 2); + + // 3. Reset and Scenario B: Condition is FALSE (R1 < R0) + interp.exec_itx("ite gt".to_string()); // 2 instructions + interp.set_reg(1, 10); + interp.set_reg(0, 50); + interp.exec_cmp("cmp r1, r0".to_string()); // Sets flags for LT (Not GT) + + assert!(!interp.cpsr.evaluate_condition("GT"), "GT should be false"); + + // Test Instruction 1 (T) + let should_run_1_f = interp.cpsr.should_execute(); + assert!(!should_run_1_f, "Instruction 1 (T) should NOT run when GT is false"); + + // Test Instruction 2 (E) + let should_run_2_f = interp.cpsr.should_execute(); + assert!(should_run_2_f, "Instruction 2 (E) SHOULD run when GT is false (Else case)"); + + // Verify block auto-deactivates + assert!( + !interp.cpsr.it_state.is_active, + "IT block should be inactive after last instruction" + ); + } +} diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs new file mode 100644 index 0000000..5ae9f96 --- /dev/null +++ b/interpreter/src/lib.rs @@ -0,0 +1,81 @@ +pub mod interpreter; +pub mod memory; + +#[cfg(test)] +mod tests { + use super::interpreter::Interpreter; + use std::path::PathBuf; + + fn get_test_file_path(filename: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("test_files") + .join(filename) + } + + #[test] + fn test_interpret_test_let() { + let mut interp = Interpreter::new(); + interp.read_file( + &mut get_test_file_path("test_let.asm") + .to_string_lossy() + .to_string(), + ); + let exit_code = interp.execute(); + assert_eq!(exit_code, 27, "test_let.asm should exit with 27"); + } + + #[test] + fn test_interpret_test_stack() { + let mut interp = Interpreter::new(); + interp.read_file( + &mut get_test_file_path("test_stack.asm") + .to_string_lossy() + .to_string(), + ); + let exit_code = interp.execute(); + assert_eq!(exit_code, 42, "test_stack.asm should exit with 42"); + } + + #[test] + fn test_interpret_test_branching() { + let mut interp = Interpreter::new(); + interp.read_file( + &mut get_test_file_path("test_branching.asm") + .to_string_lossy() + .to_string(), + ); + let exit_code = interp.execute(); + assert_eq!(exit_code, 12, "test_branching.asm should exit with 12"); + } + #[test] + fn test_ite_condition_true() { + let mut interp = Interpreter::new(); + interp.read_file(&get_test_file_path("test_ite_true.asm").to_string_lossy().to_string()); + let exit_code = interp.execute(); + assert_eq!(exit_code, 1, "ITE True: T should have run, result 1"); + } + + #[test] + fn test_ite_condition_false() { + let mut interp = Interpreter::new(); + interp.read_file(&get_test_file_path("test_ite_false.asm").to_string_lossy().to_string()); + let exit_code = interp.execute(); + assert_eq!(exit_code, 2, "ITE False: E should have run, result 2"); + } + + #[test] + fn test_ittete_condition_true() { + let mut interp = Interpreter::new(); + interp.read_file(&get_test_file_path("test_ittete_true.asm").to_string_lossy().to_string()); + let exit_code = interp.execute(); + assert_eq!(exit_code, 211, "ITTETE True: Only T instructions should sum"); + } + + #[test] + fn test_ittete_condition_false() { + let mut interp = Interpreter::new(); + interp.read_file(&get_test_file_path("test_ittete_false.asm").to_string_lossy().to_string()); + let exit_code = interp.execute(); + assert_eq!(exit_code, 103, "ITTETE False: Only E instructions should sum"); + } +} diff --git a/interpreter/src/main.rs b/interpreter/src/main.rs new file mode 100644 index 0000000..2c2369d --- /dev/null +++ b/interpreter/src/main.rs @@ -0,0 +1,40 @@ +use std::process::exit; + +use clap::Parser; +use thumb2_interpreter::interpreter::Interpreter; + +/// A Thumb2 interpreter +#[derive(Parser, Debug, Clone, PartialEq, Eq)] +#[command(version, about, long_about = None)] +struct Args { + file_path: String, + + #[arg(short, long)] + debug: bool, + + #[arg(long)] + max_time: Option, + + #[arg(short, long)] + injection_point: Option +} + +fn main() { + let args = Args::parse(); + + let mut interpreter = Interpreter::default(); + + interpreter.set_debug(args.debug); + + if let Some(time) = args.max_time { + interpreter.set_max_time(time); + } + + interpreter.read_file(&args.file_path); + if let Some(injection_point) = args.injection_point { + interpreter.inject(injection_point); + } + + let return_code = interpreter.execute(); + exit(return_code as i32) +} diff --git a/interpreter/src/memory.rs b/interpreter/src/memory.rs new file mode 100644 index 0000000..51bf7c5 --- /dev/null +++ b/interpreter/src/memory.rs @@ -0,0 +1,178 @@ +const STACK_SIZE: usize = 1024 * 1024; +const HEAP_SIZE: usize = 1024 * 1024; + +pub struct EmulatorMemory { + pub stack: Vec, + pub heap: Vec, + heap_alloc_index: usize, + stack_pointer: usize, +} + +impl EmulatorMemory { + pub fn new() -> Self { + Self { + stack: vec![0; STACK_SIZE], + heap: vec![0; HEAP_SIZE], + heap_alloc_index: 0, + stack_pointer: STACK_SIZE, + } + } + + pub fn push32(&mut self, value: u32) { + let bytes = value.to_le_bytes(); + self.stack[self.stack_pointer..self.stack_pointer + 4].copy_from_slice(&bytes); + } + + pub fn pop32(&mut self) -> u32 { + let bytes = [ + self.stack[self.stack_pointer], + self.stack[self.stack_pointer + 1], + self.stack[self.stack_pointer + 2], + self.stack[self.stack_pointer + 3], + ]; + u32::from_le_bytes(bytes) + } + + pub fn push16(&mut self, value: u16) { + let bytes = value.to_le_bytes(); + self.stack[self.stack_pointer..self.stack_pointer + 2].copy_from_slice(&bytes); + } + + pub fn pop16(&mut self) -> u16 { + let bytes = [ + self.stack[self.stack_pointer], + self.stack[self.stack_pointer + 1], + ]; + u16::from_le_bytes(bytes) + } + + pub fn alloc(&mut self, size: usize) -> Result { + if self.heap_alloc_index + size > HEAP_SIZE { + return Err("Out of heap memory".to_string()); + } + let addr = self.heap_alloc_index; + self.heap_alloc_index += size; + Ok(addr) + } + + pub fn read32(&self, addr: usize) -> u32 { + let bytes = [ + self.heap[addr], + self.heap[addr + 1], + self.heap[addr + 2], + self.heap[addr + 3], + ]; + u32::from_le_bytes(bytes) + } + + pub fn write32(&mut self, addr: usize, value: u32) { + self.heap[addr..addr + 4].copy_from_slice(&value.to_le_bytes()); + } + + pub fn write_heap(&mut self, addr: usize, value: u8) { + self.heap[addr] = value; + } + + pub fn read_heap(&self, addr: usize, len: usize) -> Vec { + self.heap[addr..addr + len].to_vec() + } + + pub fn write_stack32(&mut self, offset: usize, value: u32) { + let (addr, overflow) = self.stack_pointer.overflowing_add(offset); + if overflow || addr + 4 > self.stack.len() { + eprintln!( + "Warning: Stack write out of bounds at offset {} (sp={}, addr={}, stack_len={})", + offset, + self.stack_pointer, + addr, + self.stack.len() + ); + return; + } + self.stack[addr..addr + 4].copy_from_slice(&value.to_le_bytes()); + } + + pub fn write_stack32_at(&mut self, addr: usize, value: u32) { + if addr + 4 > self.stack.len() { + eprintln!( + "Warning: Stack write out of bounds at addr {} (stack_len={})", + addr, + self.stack.len() + ); + return; + } + self.stack[addr..addr + 4].copy_from_slice(&value.to_le_bytes()); + } + + pub fn read_stack32_at(&self, addr: usize) -> u32 { + if addr + 4 > self.stack.len() { + eprintln!( + "Warning: Stack read out of bounds at addr {} (stack_len={})", + addr, + self.stack.len() + ); + return 0; + } + u32::from_le_bytes([ + self.stack[addr], + self.stack[addr + 1], + self.stack[addr + 2], + self.stack[addr + 3], + ]) + } + + pub fn read_stack32(&self, offset: usize) -> u32 { + let (addr, overflow) = self.stack_pointer.overflowing_add(offset); + if overflow || addr + 4 > self.stack.len() { + eprintln!( + "Warning: Stack read out of bounds at offset {} (sp={}, addr={}, stack_len={})", + offset, + self.stack_pointer, + addr, + self.stack.len() + ); + return 0; + } + + if addr + 4 > self.stack.len() { + panic!("Stack out of bounds read at offset {}", offset); + } + + u32::from_le_bytes([ + self.stack[addr], + self.stack[addr + 1], + self.stack[addr + 2], + self.stack[addr + 3], + ]) + } + + pub fn get_sp(&self) -> usize { + self.stack_pointer + } + + pub fn set_sp(&mut self, sp: usize) { + self.stack_pointer = sp; + } + + pub fn get_heap_alloc_index(&self) -> usize { + self.heap_alloc_index + } + + pub fn get_heap_size(&self) -> usize { + self.heap.len() + } + + pub fn get_heap(&self) -> &Vec { + &self.heap + } + + pub fn get_stack(&self) -> &Vec { + &self.stack + } +} + +impl Default for EmulatorMemory { + fn default() -> Self { + Self::new() + } +} diff --git a/interpreter/test.asm b/interpreter/test.asm new file mode 100644 index 0000000..7613098 --- /dev/null +++ b/interpreter/test.asm @@ -0,0 +1,455 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #11413 + str r0, [sp] + sub sp, sp, #4 + mov r0, #3533 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #113 + str r0, [sp] + sub sp, sp, #4 + mov r0, #59 + str r0, [sp] + sub sp, sp, #4 + mov r0, #97 + str r0, [sp] + sub sp, sp, #4 + mov r0, #101 + str r0, [sp] + sub sp, sp, #4 + mov r0, #-1 + str r0, [sp] + sub sp, sp, #4 + mov r0, #23 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #76] + str r0, [sp, #44] + ldr r0, [sp, #88] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_1: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_2: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_2 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_2 +end_while_2: +while_3: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_3 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_3 +end_while_3: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_4 + mov r0, #0 + str r0, [sp, #16] + b endif_4 +else_4: +endif_4: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_1 +end_while_1: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: + ldr r0, [sp, #36] + str r0, [sp, #60] + ldr r0, [sp, #64] + str r0, [sp, #48] + ldr r0, [sp, #72] + str r0, [sp, #44] + ldr r0, [sp, #84] + str r0, [sp, #40] + mov r0, #1 + str r0, [sp, #36] + mov r0, #0 + str r0, [sp, #4] +while_5: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #44] + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_5 + ldr r0, [sp, #36] + str r0, [sp, #28] + ldr r0, [sp, #48] + str r0, [sp, #24] + ldr r0, [sp, #40] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_6: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_6 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_7: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_7 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_7 +end_while_7: +while_8: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_8 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_8 +end_while_8: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_9 + mov r0, #0 + str r0, [sp, #16] + b endif_9 +else_9: +endif_9: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_6 +end_while_6: + ldr r0, [sp, #16] + str r0, [sp, #36] + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_5 +end_while_5: + ldr r0, [sp, #36] + str r0, [sp, #56] + ldr r0, [sp, #60] + mov r1, r0 + ldr r0, [sp, #56] + sub r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #28] + ldr r0, [sp, #80] + str r0, [sp, #24] + ldr r0, [sp, #88] + str r0, [sp, #20] + mov r0, #0 + str r0, [sp, #16] +while_10: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_10 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #28] + add r0, r1, r0 + str r0, [sp, #16] +while_11: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq end_while_11 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + sub r0, r1, r0 + str r0, [sp, #16] + b while_11 +end_while_11: +while_12: + ldr r0, [sp, #16] + mov r1, r0 + mov r0, #0 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_12 + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + add r0, r1, r0 + str r0, [sp, #16] + b while_12 +end_while_12: + ldr r0, [sp, #16] + mov r1, r0 + ldr r0, [sp, #20] + cmp r1, r0 + mov r0, #0 + it eq + moveq r0, #1 + cmp r0, #0 + beq else_13 + mov r0, #0 + str r0, [sp, #16] + b endif_13 +else_13: +endif_13: + ldr r0, [sp, #24] + mov r1, r0 + mov r0, #1 + sub r0, r1, r0 + str r0, [sp, #24] + b while_10 +end_while_10: + ldr r0, [sp, #16] + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #84] + mul r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + mov r1, r0 + ldr r0, [sp, #56] + add r0, r1, r0 + str r0, [sp, #52] + ldr r0, [sp, #52] + str r0, [sp, #68] + mov r0, #1 + ldr r1, =.Lstr0 + mov r2, #12 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r4, r0 + ldr r1, =num_buf + add r1, r1, #16 + mov r2, #0 + cmp r4, #0 + bne print_int_loop_14 + mov r3, #48 + sub r1, r1, #1 + strb r3, [r1] + mov r2, #1 + b print_int_done_14 +print_int_loop_14: + mov r0, r4 + mov r3, #10 + sdiv r5, r0, r3 + mul r6, r5, r3 + sub r7, r0, r6 + add r7, r7, #48 + sub r1, r1, #1 + strb r7, [r1] + add r2, r2, #1 + mov r4, r5 + cmp r4, #0 + bne print_int_loop_14 +print_int_done_14: + mov r0, #1 + mov r1, r1 + mov r2, r2 + mov r7, #4 + svc #0 + mov r0, #1 + ldr r1, =newline + mov r2, #1 + mov r7, #4 + svc #0 + ldr r0, [sp, #68] + mov r7, #1 + svc #0 + +.size _start, .-_start + +.section .data +.Lstr0: + .ascii "gang_sign = " +newline: + .ascii "\n" +num_buf: + .space 16 diff --git a/test_codes/simple.trv b/interpreter/test_codes/simple.trv similarity index 91% rename from test_codes/simple.trv rename to interpreter/test_codes/simple.trv index ecb9a22..62abea2 100644 --- a/test_codes/simple.trv +++ b/interpreter/test_codes/simple.trv @@ -5,7 +5,7 @@ func main() -> Integer { num = 11; } if num > 10 { - num = 11; + num = 12; } else { num = 11; } diff --git a/interpreter/test_codes/simple.trv.output b/interpreter/test_codes/simple.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/interpreter/test_codes/simple.trv.output @@ -0,0 +1 @@ +12 diff --git a/interpreter/test_codes/test_if_else.trv b/interpreter/test_codes/test_if_else.trv new file mode 100644 index 0000000..3b984ea --- /dev/null +++ b/interpreter/test_codes/test_if_else.trv @@ -0,0 +1,9 @@ +func main() -> Integer { + let num : Integer = 9; + if num > 10 { + num = 11; + } else { + num = 12; + } + return num; +} diff --git a/interpreter/test_codes/test_if_else.trv.output b/interpreter/test_codes/test_if_else.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/interpreter/test_codes/test_if_else.trv.output @@ -0,0 +1 @@ +12 diff --git a/interpreter/test_codes/test_main_let.trv b/interpreter/test_codes/test_main_let.trv new file mode 100644 index 0000000..fdc57cb --- /dev/null +++ b/interpreter/test_codes/test_main_let.trv @@ -0,0 +1,4 @@ +func main() -> Integer { + let num : Integer = 27; + return num; +} diff --git a/interpreter/test_codes/test_main_let.trv.output b/interpreter/test_codes/test_main_let.trv.output new file mode 100644 index 0000000..f64f5d8 --- /dev/null +++ b/interpreter/test_codes/test_main_let.trv.output @@ -0,0 +1 @@ +27 diff --git a/playground/test.trv b/interpreter/test_codes/test_main_return.trv similarity index 64% rename from playground/test.trv rename to interpreter/test_codes/test_main_return.trv index 5da0ba4..c66bbb3 100644 --- a/playground/test.trv +++ b/interpreter/test_codes/test_main_return.trv @@ -1,3 +1,3 @@ func main() -> Integer { - return 420; + return 69; } diff --git a/interpreter/test_codes/test_main_return.trv.output b/interpreter/test_codes/test_main_return.trv.output new file mode 100644 index 0000000..b5489e5 --- /dev/null +++ b/interpreter/test_codes/test_main_return.trv.output @@ -0,0 +1 @@ +69 diff --git a/interpreter/test_codes/test_while.trv b/interpreter/test_codes/test_while.trv new file mode 100644 index 0000000..db54eef --- /dev/null +++ b/interpreter/test_codes/test_while.trv @@ -0,0 +1,7 @@ +func main() -> Integer { + let num : Integer = 9; + while num < 12 { + num = num + 1; + } + return num; +} diff --git a/interpreter/test_codes/test_while.trv.output b/interpreter/test_codes/test_while.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/interpreter/test_codes/test_while.trv.output @@ -0,0 +1 @@ +12 diff --git a/interpreter/test_codes/test_while_nested.trv b/interpreter/test_codes/test_while_nested.trv new file mode 100644 index 0000000..7102f7f --- /dev/null +++ b/interpreter/test_codes/test_while_nested.trv @@ -0,0 +1,11 @@ +func main() -> Integer { + let a : Integer = 0; + while a < 3 { + let b : Integer = 0; + while b < 2 { + b = b + 1; + } + a = a + 1; + } + return a; +} diff --git a/interpreter/test_codes/test_while_nested.trv.output b/interpreter/test_codes/test_while_nested.trv.output new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/interpreter/test_codes/test_while_nested.trv.output @@ -0,0 +1 @@ +3 diff --git a/interpreter/test_codes/test_while_simple.trv b/interpreter/test_codes/test_while_simple.trv new file mode 100644 index 0000000..db54eef --- /dev/null +++ b/interpreter/test_codes/test_while_simple.trv @@ -0,0 +1,7 @@ +func main() -> Integer { + let num : Integer = 9; + while num < 12 { + num = num + 1; + } + return num; +} diff --git a/interpreter/test_codes/test_while_simple.trv.output b/interpreter/test_codes/test_while_simple.trv.output new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/interpreter/test_codes/test_while_simple.trv.output @@ -0,0 +1 @@ +12 diff --git a/interpreter/test_codes/test_while_two_loops.trv b/interpreter/test_codes/test_while_two_loops.trv new file mode 100644 index 0000000..629c22c --- /dev/null +++ b/interpreter/test_codes/test_while_two_loops.trv @@ -0,0 +1,11 @@ +func main() -> Integer { + let a : Integer = 0; + let b : Integer = 0; + while a < 3 { + a = a + 1; + } + while b < 4 { + b = b + 1; + } + return a+b; +} diff --git a/interpreter/test_codes/test_while_two_loops.trv.output b/interpreter/test_codes/test_while_two_loops.trv.output new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/interpreter/test_codes/test_while_two_loops.trv.output @@ -0,0 +1 @@ +7 diff --git a/interpreter/test_codes_compiled/simple.asm b/interpreter/test_codes_compiled/simple.asm new file mode 100644 index 0000000..6070b1c --- /dev/null +++ b/interpreter/test_codes_compiled/simple.asm @@ -0,0 +1,46 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + mov r0, #11 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_1 + mov r0, #11 + str r0, [sp] + b endif_1 +else_1: + mov r0, #11 + str r0, [sp] +endif_1: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_codes_compiled/test_main_let.asm b/interpreter/test_codes_compiled/test_main_let.asm new file mode 100644 index 0000000..890e947 --- /dev/null +++ b/interpreter/test_codes_compiled/test_main_let.asm @@ -0,0 +1,16 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #27 + str r0, [sp] + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/playground/main.asm b/interpreter/test_codes_compiled/test_main_return.asm similarity index 89% rename from playground/main.asm rename to interpreter/test_codes_compiled/test_main_return.asm index b4edc48..01167be 100644 --- a/playground/main.asm +++ b/interpreter/test_codes_compiled/test_main_return.asm @@ -6,7 +6,7 @@ .type _start, %function _start: - mov r0, #67 + mov r0, #69 mov r7, #1 svc #0 diff --git a/interpreter/test_codes_compiled/test_while.asm b/interpreter/test_codes_compiled/test_while.asm new file mode 100644 index 0000000..af133c7 --- /dev/null +++ b/interpreter/test_codes_compiled/test_while.asm @@ -0,0 +1,33 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #12 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_codes_compiled/test_while_nested.asm b/interpreter/test_codes_compiled/test_while_nested.asm new file mode 100644 index 0000000..e8ede92 --- /dev/null +++ b/interpreter/test_codes_compiled/test_while_nested.asm @@ -0,0 +1,54 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #2 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 +end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + add sp, sp, #4 + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_codes_compiled/test_while_simple.asm b/interpreter/test_codes_compiled/test_while_simple.asm new file mode 100644 index 0000000..af133c7 --- /dev/null +++ b/interpreter/test_codes_compiled/test_while_simple.asm @@ -0,0 +1,33 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] +while_0: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #12 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_0 +end_while_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_codes_compiled/test_while_two_loops.asm b/interpreter/test_codes_compiled/test_while_two_loops.asm new file mode 100644 index 0000000..18d6895 --- /dev/null +++ b/interpreter/test_codes_compiled/test_while_two_loops.asm @@ -0,0 +1,56 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #3 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp, #4] + b while_0 +end_while_0: +while_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #4 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_1 + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + add r0, r1, r0 + str r0, [sp] + b while_1 +end_while_1: + ldr r0, [sp, #4] + mov r1, r0 + ldr r0, [sp, #0] + add r0, r1, r0 + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_branching.asm b/interpreter/test_files/test_branching.asm new file mode 100644 index 0000000..16ec6ff --- /dev/null +++ b/interpreter/test_files/test_branching.asm @@ -0,0 +1,32 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #9 + str r0, [sp] + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_0 + mov r0, #11 + str r0, [sp, #0] + b endif_0 +else_0: + mov r0, #12 + str r0, [sp, #0] +endif_0: + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_ite_false.asm b/interpreter/test_files/test_ite_false.asm new file mode 100644 index 0000000..24b253b --- /dev/null +++ b/interpreter/test_files/test_ite_false.asm @@ -0,0 +1,18 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r1, #5 + cmp r1, #10 @ 5 < 10, so GT is False + mov r0, #0 + ite gt + movgt r0, #1 @ Should Skip + movle r0, #2 @ Should Run + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_ite_true.asm b/interpreter/test_files/test_ite_true.asm new file mode 100644 index 0000000..e243405 --- /dev/null +++ b/interpreter/test_files/test_ite_true.asm @@ -0,0 +1,18 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r1, #10 + cmp r1, #5 @ 10 > 5, so GT is True + mov r0, #0 + ite gt + movgt r0, #1 @ Should Run + movle r0, #2 @ Should Skip + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_ittete_false.asm b/interpreter/test_files/test_ittete_false.asm new file mode 100644 index 0000000..1c4525f --- /dev/null +++ b/interpreter/test_files/test_ittete_false.asm @@ -0,0 +1,21 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r1, #5 + cmp r1, #10 @ GT is False + mov r0, #0 + ittete gt + addgt r0, r0, #1 @ T: Run (+1) + addgt r0, r0, #10 @ T: Run (+10) + addle r0, r0, #100 @ E: Skip + addgt r0, r0, #200 @ T: Run (+1000) + addle r0, r0, #3 @ E: Skip + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_ittete_true.asm b/interpreter/test_files/test_ittete_true.asm new file mode 100644 index 0000000..2b783cb --- /dev/null +++ b/interpreter/test_files/test_ittete_true.asm @@ -0,0 +1,21 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r1, #10 + cmp r1, #5 @ GT is True + mov r0, #0 + ittete gt + addgt r0, r0, #1 @ T: Run (+1) + addgt r0, r0, #10 @ T: Run (+10) + addle r0, r0, #100 @ E: Skip + addgt r0, r0, #200 @ T: Run (+1000) + addle r0, r0, #3 @ E: Skip + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_let.asm b/interpreter/test_files/test_let.asm new file mode 100644 index 0000000..4d4f96c --- /dev/null +++ b/interpreter/test_files/test_let.asm @@ -0,0 +1,14 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r4, #27 + mov r0, r4 + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/interpreter/test_files/test_stack.asm b/interpreter/test_files/test_stack.asm new file mode 100644 index 0000000..5a2bca2 --- /dev/null +++ b/interpreter/test_files/test_stack.asm @@ -0,0 +1,17 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #42 + str r0, [sp] + ldr r1, [sp] + mov r0, r1 + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/playground/run_asm b/playground/run_asm deleted file mode 100755 index 4ab6b1e..0000000 --- a/playground/run_asm +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -ASM_FILE="${1:-main.asm}" - -if [ ! -f "$ASM_FILE" ]; then - echo "Error: File '$ASM_FILE' does not exist" >&2 - exit 1 -fi - -BASE_NAME="${ASM_FILE%.asm}" - -arm-none-eabi-as -mthumb -o "${BASE_NAME}.o" "$ASM_FILE" - -arm-none-eabi-ld -o "$BASE_NAME" "${BASE_NAME}.o" - -qemu-arm ./"$BASE_NAME" -echo $? - -rm "${BASE_NAME}.o" "$BASE_NAME" diff --git a/program.trv b/program.trv deleted file mode 100644 index fd640f1..0000000 --- a/program.trv +++ /dev/null @@ -1,31 +0,0 @@ -# Test file for small_lang compiler - -func is_greater_than_44(params : Integer) -> Integer { - let x : Integer = params; - if x > 44 then { - 1 - } - else { - 0 - } -} - -func otherFunction() -> Boolean { - let number : Integer = 20; - let is_greater : Integer = is_greater_than_44(number); - if is_greater == 0 then { - print("Didnt work the first time!"); - } - while is_greater == 0 do { - number = number + 1; - is_greater = is_greater_than_44(number); - } - True -} - -func main() -> Boolean { - let result : Boolean = otherFunction(); - print(result); - print("Finished"); - result -} diff --git a/run_asm b/run_asm deleted file mode 100755 index 1ae9685..0000000 --- a/run_asm +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# make assemble -arm-none-eabi-as -mthumb -o main.o main.asm - -# link -arm-none-eabi-ld -o main main.o - -# run -qemu-arm ./main -echo $? - -rm main.o main diff --git a/src/codegen/codegen.rs b/src/codegen/codegen.rs deleted file mode 100644 index 036d50f..0000000 --- a/src/codegen/codegen.rs +++ /dev/null @@ -1,410 +0,0 @@ -use crate::parser::{ AST, parser::{ BinOp, Block, Expr, Function, Stmt } }; -use core::panic; -use std::{ collections::HashMap, fs::File, io::Write }; - -#[derive(Debug)] -pub struct CodeGenerator { - pub file: File, - locals: HashMap, - stack_offset: i32, - label_count: usize, -} - -impl CodeGenerator { - pub fn new(file: File) -> Self { - Self { - file, - locals: HashMap::new(), - stack_offset: 0, - label_count: 0, - } - } - pub fn generate(&mut self, ast: AST) { - self.gen_init(); - for func in ast { - self.emit(func); - } - } - - fn gen_init(&mut self) { - self.write_line(".syntax unified", 0); - self.write_line(".thumb", 0); - self.write_line("", 0); - self.write_line(".section .text", 0); - self.write_line(".global _start", 0); - self.write_line(".type _start, %function", 0); - self.write_line("", 0); - } - - fn write_line(&mut self, string: &str, indents: usize) { - // Create indentation (e.g., 4 spaces per indent level) - let indent_str = " ".repeat(indents); - - // writeln! automatically appends \n - let _ = writeln!(self.file, "{}{}", indent_str, string); - } - - fn emit(&mut self, func: Function) { - match func.name.as_str() { - "main" => self.emit_main(func), - _ => self.emit_func(func), - } - self.file.sync_all().unwrap(); - } - - fn emit_func(&mut self, func: Function) {} - - fn emit_main(&mut self, func: Function) { - self.write_line("_start:", 0); - self.emit_block(func.body, true); - self.write_line("\n.size _start, .-_start", 0); - } - fn emit_block(&mut self, block: Block, is_main: bool) { - for stmt in block.statements { - match stmt { - Stmt::Let(_, _, _) => self.emit_let(stmt), - Stmt::AssignStatement(_, _) => self.emit_assign(stmt), - Stmt::Return(_) => self.emit_return(stmt, is_main), - Stmt::If { .. } => self.emit_if(stmt), - _ => panic!("Error found in expression in return"), - } - } - } - fn emit_if(&mut self, if_stmt: Stmt) { - match if_stmt { - Stmt::If { condition, block, option } => { - let label_id = self.label_count; - self.label_count += 1; - - self.emit_expr(condition); - self.write_line("cmp r0, #0", 1); - self.write_line(&format!("beq else_{}", label_id), 1); - - // then block - self.emit_block(block, false); - self.write_line(&format!("b endif_{}", label_id), 1); - - // else block - self.write_line(&format!("else_{}:", label_id), 0); - if let Some(else_block) = option { - self.emit_block(else_block, false); - } - - self.write_line(&format!("endif_{}:", label_id), 0); - } - _ => panic!("emit_if called with non-if statement"), - } - } - - fn emit_return(&mut self, return_stmt: Stmt, is_main: bool) { - match return_stmt { - Stmt::Return(expr) => { - match expr { - | crate::parser::parser::Expr::IntegerLiteral(_) - | crate::parser::parser::Expr::Identifier(_) => { - self.emit_expr(expr); - } - _ => panic!("Unsupported expression type in return"), - } - if is_main { - self.write_line("mov r7, #1", 1); - self.write_line("svc #0", 1); - } else { - self.write_line("bx lr", 1); - } - } - _ => panic!("return poorly formed"), - } - } - fn emit_assign(&mut self, assign_stmt: Stmt) { - match assign_stmt { - Stmt::AssignStatement(name, expr) => { - self.emit_expr(expr); - let offset = self.locals.get(&name).expect("Undefined variable"); - if self.stack_offset - offset == 0{ - self.write_line(&format!("str r0, [sp]"), 1); - }else{ - self.write_line(&format!("str r0, [sp, #{}]", self.stack_offset - offset), 1); - } - } - _ => panic!("Not a valid assignment"), - } - } - fn emit_let(&mut self, let_stmt: Stmt) { - match let_stmt { - Stmt::Let(name, type_name, expr) => { - self.stack_offset += 4; - self.write_line("sub sp, sp, #4", 1); - self.emit_expr(expr); - self.write_line("str r0, [sp]", 1); - self.locals.insert(name, self.stack_offset); - } - _ => panic!("Not a let statement format sorry "), - } - } - fn emit_expr(&mut self, expr: Expr) { - match expr { - Expr::IntegerLiteral(val) => { self.write_line(&format!("mov r0, #{}", val), 1) } - Expr::BooleanLiteral(val) => { self.write_line(&format!("mov r0, #{}", val), 1) } - Expr::Identifier(name) => { - let offset = self.locals.get(&name).expect("Undefined variable"); - self.write_line(&format!("ldr r0, [sp, #{}]", self.stack_offset - offset), 1); - } - Expr::BinaryOp(_, _, _) => { - self.emit_bin_op(expr); - } - - _ => panic!("Expression not valid sorry"), - } - } - fn emit_bin_op(&mut self, bin_op_expr: Expr) { - match bin_op_expr { - Expr::BinaryOp(left, op, right) => { - self.emit_expr(*left); - self.write_line("mov r1, r0", 1); // store left - self.emit_expr(*right); - - match op { - BinOp::Add => self.write_line("add r0, r1, r0", 1), - BinOp::Sub => self.write_line("sub r0, r1, r0", 1), - BinOp::Mul => self.write_line("mul r0, r1, r0", 1), - - BinOp::Equals => { - self.write_line("cmp r1, r0", 1); - self.write_line("mov r0, #0", 1); - self.write_line("it eq", 1); - self.write_line("moveq r0, #1", 1); - } - - BinOp::NotEquals => { - self.write_line("cmp r1, r0", 1); - self.write_line("mov r0, #0", 1); - self.write_line("it ne", 1); - self.write_line("movne r0, #1", 1); - } - - BinOp::GreaterThan => { - self.write_line("cmp r1, r0", 1); - self.write_line("mov r0, #0", 1); - self.write_line("it gt", 1); - self.write_line("movgt r0, #1", 1); - } - - BinOp::LessThan => { - self.write_line("cmp r1, r0", 1); - self.write_line("mov r0, #0", 1); - self.write_line("it lt", 1); - self.write_line("movlt r0, #1", 1); - } - } - } - _ => panic!("not a binary operation if ive ever seen one"), - } - } -} - -#[cfg(test)] -mod tests { - use crate::lexer::{ lexer::Lexer, token::Token }; - use crate::parser::parser::{ Parser, AST }; - use crate::CodeGenerator; - use std::fs::File; - use std::io::{ Read, Seek, SeekFrom }; - use std::sync::Once; - - static INIT: Once = Once::new(); - - fn initialize() { - INIT.call_once(|| { - let mut path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - path.push("temp/tests"); - std::fs::create_dir_all(path).unwrap(); - }); - } - - #[test] - fn can_generate_init() { - initialize(); - let output_file = File::options() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open("temp/tests/test_can_generate_init.asm") - .expect("Failed to create file: /temp/tests/test_can_generate_init.asm"); - - let ast = AST::new(); - let mut codegen = CodeGenerator::new(output_file); - codegen.generate(ast); - codegen.file.seek(SeekFrom::Start(0)).unwrap(); - let mut buf = String::new(); - codegen.file.read_to_string(&mut buf).unwrap(); - let expected = ( - indoc::indoc! { - r##" - .syntax unified - .thumb - - .section .text - .global _start - .type _start, %function - - "## - } - ).to_string(); - assert_eq!(expected, buf); - } - - #[test] - fn can_generate_return() { - initialize(); - let source = std::fs - ::read_to_string("test_codes/test_main_return.trv") - .expect("Failed to read file"); - let mut lexer: Lexer = Lexer::new(source); - let tokens: Vec = lexer.tokenize(); - let mut parser: Parser = Parser::new(tokens); - let ast: AST = parser.parse_program(); - let output_file = File::options() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open("temp/tests/test_can_generate_return.asm") - .expect("Failed to create file: /temp/tests/test_can_generate_return.asm"); - let mut codegen = CodeGenerator::new(output_file); - codegen.generate(ast); - codegen.file.seek(SeekFrom::Start(0)).unwrap(); - let mut buf = String::new(); - codegen.file.read_to_string(&mut buf).unwrap(); - let expected = ( - indoc::indoc! { - r##" - .syntax unified - .thumb - - .section .text - .global _start - .type _start, %function - - _start: - mov r0, #69 - mov r7, #1 - svc #0 - - .size _start, .-_start - "## - } - ).to_string(); - assert_eq!(expected, buf) - } - - #[test] - fn can_generate_let() { - initialize(); - let source = std::fs - ::read_to_string("test_codes/test_main_let.trv") - .expect("Failed to read file"); - let mut lexer: Lexer = Lexer::new(source); - let tokens: Vec = lexer.tokenize(); - let mut parser: Parser = Parser::new(tokens); - let ast: AST = parser.parse_program(); - let output_file = File::options() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open("temp/tests/test_can_generate_let.asm") - .expect("Failed to create file: /temp/tests/test_can_generate_let.asm"); - let mut codegen = CodeGenerator::new(output_file); - codegen.generate(ast); - codegen.file.seek(SeekFrom::Start(0)).unwrap(); - let mut buf = String::new(); - codegen.file.read_to_string(&mut buf).unwrap(); - let expected = ( - indoc::indoc! { - r##" - .syntax unified - .thumb - - .section .text - .global _start - .type _start, %function - - _start: - sub sp, sp, #4 - mov r0, #27 - str r0, [sp] - ldr r0, [sp, #0] - mov r7, #1 - svc #0 - - .size _start, .-_start - "## - } - ).to_string(); - assert_eq!(expected, buf) - } - #[test] - fn can_generate_if_else_and_assign() { - initialize(); - let source = std::fs - ::read_to_string("test_codes/test_func_if_else.trv") - .expect("Failed to read file"); - let mut lexer: Lexer = Lexer::new(source); - let tokens: Vec = lexer.tokenize(); - let mut parser: Parser = Parser::new(tokens); - let ast: AST = parser.parse_program(); - let output_file = File::options() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open("temp/tests/test_can_generate_if_else.asm") - .expect("Failed to create file: /temp/tests/test_can_generate_let.asm"); - let mut codegen = CodeGenerator::new(output_file); - codegen.generate(ast); - codegen.file.seek(SeekFrom::Start(0)).unwrap(); - let mut buf = String::new(); - codegen.file.read_to_string(&mut buf).unwrap(); - let expected = ( - indoc::indoc! { - r##" - .syntax unified - .thumb - - .section .text - .global _start - .type _start, %function - - _start: - sub sp, sp, #4 - mov r0, #9 - str r0, [sp] - ldr r0, [sp, #0] - mov r1, r0 - mov r0, #10 - cmp r1, r0 - mov r0, #0 - it gt - movgt r0, #1 - cmp r0, #0 - beq else_0 - mov r0, #11 - str r0, [sp] - b endif_0 - else_0: - mov r0, #12 - str r0, [sp] - endif_0: - ldr r0, [sp, #0] - mov r7, #1 - svc #0 - - .size _start, .-_start - "## - } - ).to_string(); - assert_eq!(expected, buf) - } -} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs deleted file mode 100644 index 24ccbdd..0000000 --- a/src/codegen/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod codegen; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 28495e7..0000000 --- a/src/main.rs +++ /dev/null @@ -1,38 +0,0 @@ -mod codegen; -mod lexer; -mod parser; -mod semantic; - -use lexer::lexer::Lexer; // adjust if needed -use lexer::token::Token; -use parser::parser::AST; -use parser::parser::Parser; -use semantic::symbol_table::SymbolTable; -use std::env; -use std::fs; -use std::fs::File; - -use crate::codegen::codegen::CodeGenerator; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - eprintln!("Usage: triviC "); - std::process::exit(1); - } - - let filename = &args[1]; - - let source = fs::read_to_string(filename).expect("Failed to read file"); - - let mut lexer: Lexer = Lexer::new(source); - let tokens: Vec = lexer.tokenize(); - let mut symbol_table = SymbolTable::new(); - let mut parser: Parser = Parser::new(tokens); - let ast: AST = parser.parse_program(); - println!("Lexing and parsing completed successfully."); - let output_file = File::create("main.asm").expect("failed to create main.asm"); - let mut codegen = CodeGenerator::new(output_file); - codegen.generate(ast); -} diff --git a/test b/test deleted file mode 100755 index cf410c7..0000000 Binary files a/test and /dev/null differ diff --git a/test.c b/test.c deleted file mode 100644 index cc1d4f3..0000000 --- a/test.c +++ /dev/null @@ -1,4 +0,0 @@ -int _start() { - int num = 27; - return num; -} diff --git a/test.s b/test.s deleted file mode 100644 index c216a29..0000000 --- a/test.s +++ /dev/null @@ -1,40 +0,0 @@ - .cpu cortex-a9 - .arch armv7-a - .fpu softvfp - .arch_extension mp - .arch_extension sec - .eabi_attribute 20, 1 - .eabi_attribute 21, 1 - .eabi_attribute 23, 3 - .eabi_attribute 24, 1 - .eabi_attribute 25, 1 - .eabi_attribute 26, 1 - .eabi_attribute 30, 6 - .eabi_attribute 34, 1 - .eabi_attribute 18, 4 - .file "test.c" - .text - .align 1 - .global _start - .syntax unified - .thumb - .thumb_func - .type _start, %function -_start: - @ args = 0, pretend = 0, frame = 8 - @ frame_needed = 1, uses_anonymous_args = 0 - @ link register save eliminated. - push {r7} - sub sp, sp, #12 - add r7, sp, #0 - movs r3, #27 - str r3, [r7, #4] - ldr r3, [r7, #4] - mov r0, r3 - adds r7, r7, #12 - mov sp, r7 - @ sp needed - pop {r7} - bx lr - .size _start, .-_start - .ident "GCC: (Arch Repository) 14.2.0" diff --git a/test_codes/test_func_let.trv b/test_codes/test_func_let.trv deleted file mode 100644 index cdf3d17..0000000 --- a/test_codes/test_func_let.trv +++ /dev/null @@ -1,10 +0,0 @@ -func x() -> Integer { - let v : Integer = 100; - return v; -} - -func main() -> Integer { - let func_res = x(); - let result = 5; - return 29; -} diff --git a/testing/1-20260313_104334/compiled_test_files/simple.asm b/testing/1-20260313_104334/compiled_test_files/simple.asm new file mode 100644 index 0000000..7116286 --- /dev/null +++ b/testing/1-20260313_104334/compiled_test_files/simple.asm @@ -0,0 +1,76 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + sub sp, sp, #4 + mov r0, #1 + str r0, [sp] +while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + sub sp, sp, #4 + mov r0, #1 + str r0, [sp] + ldr r0, [sp, #8] + mov r1, r0 + ldr r0, [sp, #0] + add r0, r1, r0 + str r0, [sp, #8] + add sp, sp, #4 + b while_0 +end_while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_1 + mov r0, #12 + str r0, [sp, #4] + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + add sp, sp, #4 + b endif_1 +else_1: + mov r0, #11 + str r0, [sp, #4] +endif_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + cmp r1, r0 + mov r0, #0 + it ne + movne r0, #1 + cmp r0, #0 + beq else_2 + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + b endif_2 +else_2: + ldr r0, [sp, #4] + mov r7, #1 + svc #0 +endif_2: + add sp, sp, #8 + +.size _start, .-_start diff --git a/testing/1-20260313_104334/compiled_test_files/simple.hard.asm b/testing/1-20260313_104334/compiled_test_files/simple.hard.asm new file mode 100644 index 0000000..b540827 --- /dev/null +++ b/testing/1-20260313_104334/compiled_test_files/simple.hard.asm @@ -0,0 +1,134 @@ +.syntax unified +.thumb + +.section .text +.global _start +.type _start, %function + +_start: + mov r9, #0 + mov r10, #1 + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + sub sp, sp, #4 + mov r0, #1 + str r0, [sp] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 +while_0: + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it lt + movlt r0, #1 + cmp r0, #0 + beq end_while_0 + sub sp, sp, #4 + mov r0, #1 + str r0, [sp] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + ldr r0, [sp, #8] + mov r1, r0 + ldr r0, [sp, #0] + add r0, r1, r0 + str r0, [sp, #8] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + add sp, sp, #4 + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + b while_0 +end_while_0: + ldr r0, [sp, #4] + mov r1, r0 + mov r0, #10 + cmp r1, r0 + mov r0, #0 + it gt + movgt r0, #1 + cmp r0, #0 + beq else_1 + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + mov r0, #12 + str r0, [sp, #4] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + sub sp, sp, #4 + mov r0, #0 + str r0, [sp] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + add sp, sp, #4 + b endif_1 +else_1: + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + mov r0, #11 + str r0, [sp, #4] + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 +endif_1: + ldr r0, [sp, #0] + mov r1, r0 + mov r0, #1 + cmp r1, r0 + mov r0, #0 + it ne + movne r0, #1 + cmp r0, #0 + beq else_2 + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + ldr r0, [sp, #0] + mov r7, #1 + svc #0 + b endif_2 +else_2: + add r9, r9, #1 + cmp r9, r10 + bne countermeasure + add r10, r10, #1 + ldr r0, [sp, #4] + mov r7, #1 + svc #0 +endif_2: + add sp, sp, #8 +countermeasure: + mov r0, #77 + mov r7, #1 + svc #0 + +.size _start, .-_start diff --git a/testing/1-20260313_104334/tests/simple.trv b/testing/1-20260313_104334/tests/simple.trv new file mode 100644 index 0000000..0205385 --- /dev/null +++ b/testing/1-20260313_104334/tests/simple.trv @@ -0,0 +1,22 @@ +func main() -> Boolean { + let num : Integer = 0; + let test : Boolean = True; + + while num < 10 do { + let etellerandet : Integer = 1; + num = num + etellerandet; + } + + if num > 10 { + num = 12; + let dingdong : Integer = 0; + } else { + num = 11; + } + + if test != True { + return test; + } else { + return num; + } +} diff --git a/testing/1-20260313_104334/tests/simple.trv.output b/testing/1-20260313_104334/tests/simple.trv.output new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/testing/1-20260313_104334/tests/simple.trv.output @@ -0,0 +1 @@ +11 diff --git a/testing/config.toml b/testing/config.toml new file mode 100644 index 0000000..77f1099 --- /dev/null +++ b/testing/config.toml @@ -0,0 +1,3 @@ +tests_folder = "tests" + + diff --git a/testing/fissc/crt-rsa.trv b/testing/fissc/crt-rsa.trv new file mode 100644 index 0000000..b1ce082 --- /dev/null +++ b/testing/fissc/crt-rsa.trv @@ -0,0 +1,120 @@ +func main() -> Integer { + let g_countermeasure : Integer = 0; + let g_N : Integer = 11413; + let g_e : Integer = 3533; + let g_p : Integer = 101; + let g_q : Integer = 113; + let g_iq : Integer = 59; + let g_dp : Integer = 97; + let g_dq : Integer = 101; + let g_sign : Integer = -1; + let g_M : Integer = 23; + + let Cp : Integer = 0; + let Cq : Integer = 0; + let tmp : Integer = 0; + + let a : Integer = 0; + let b : Integer = 0; + let n : Integer = 0; + let r : Integer = 0; + + let mul_r : Integer = 0; + let mul_a : Integer = 0; + let mul_b : Integer = 0; + let mul_n : Integer = 0; + let mul_tmp : Integer = 0; + + let pow_a : Integer = 0; + let pow_tmp : Integer = 0; + let pow_b : Integer = 0; + let pow_n : Integer = 0; + + a = g_M; + b = g_dp; + n = g_p; + r = 1; + pow_b = 0; + while pow_b < b { + mul_a = r; + mul_b = a; + mul_n = n; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + r = mul_tmp; + pow_b = pow_b + 1; + } + Cp = r; + + a = g_M; + b = g_dq; + n = g_q; + r = 1; + pow_b = 0; + while pow_b < b { + mul_a = r; + mul_b = a; + mul_n = n; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + r = mul_tmp; + pow_b = pow_b + 1; + } + Cq = r; + + tmp = Cp - Cq; + + mul_a = tmp; + mul_b = g_iq; + mul_n = g_p; + mul_tmp = 0; + while mul_b > 0 { + mul_tmp = mul_tmp + mul_a; + while mul_tmp > mul_n { + mul_tmp = mul_tmp - mul_n; + } + while mul_tmp < 0 { + mul_tmp = mul_tmp + mul_n; + } + if mul_tmp == mul_n { + mul_tmp = 0; + } + mul_b = mul_b - 1; + } + tmp = mul_tmp; + + tmp = tmp * g_q; + + tmp = tmp + Cq; + + g_sign = tmp; + + print("g_sign = ", g_sign); + print("g_countermeasure: ", g_countermeasure); + + return g_sign; +} diff --git a/testing/requirements.txt b/testing/requirements.txt new file mode 100644 index 0000000..bd79a65 --- /dev/null +++ b/testing/requirements.txt @@ -0,0 +1 @@ +toml diff --git a/testing/setup b/testing/setup new file mode 100755 index 0000000..f712e75 --- /dev/null +++ b/testing/setup @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BIN_DIR="$SCRIPT_DIR/bin" + +mkdir -p "$BIN_DIR" + +echo "Building compiler..." +cd "$SCRIPT_DIR/../compiler" +cargo build --release +cp target/release/trivic "$BIN_DIR/compiler" + +echo "Building interpreter..." +cd "$SCRIPT_DIR/../interpreter" +cargo build --release +cp target/release/thumb2_interpreter "$BIN_DIR/interpreter" + +echo "Done! Binaries in $BIN_DIR" diff --git a/testing/src/testrunner.py b/testing/src/testrunner.py new file mode 100644 index 0000000..44fd7e5 --- /dev/null +++ b/testing/src/testrunner.py @@ -0,0 +1,155 @@ +import os +import sys +import subprocess +import shutil +import toml +from datetime import datetime + +class TestRunner: + def __init__(self, config_path=None): + if config_path is None: + config_path = os.path.join(os.path.dirname(__file__), "..", "config.toml") + + with open(config_path, 'r') as f: + config = toml.load(f) + + self.base_dir = os.path.join(os.path.dirname(__file__), "..") + self.tests_folder = os.path.join(self.base_dir, config.get('tests_folder', 'tests')) + self.artifacts_folder = os.path.join(self.base_dir, "artifacts") + self.tests = self._discover_tests() + + def _discover_tests(self): + tests = [] + if not os.path.exists(self.tests_folder): + return tests + + files = os.listdir(self.tests_folder) + trv_files = sorted([f for f in files if f.endswith('.trv')]) + + for trv_file in trv_files: + test_name = trv_file[:-4] + output_file = trv_file + ".output" + + if output_file in files: + trv_path = os.path.join(self.tests_folder, trv_file) + output_path = os.path.join(self.tests_folder, output_file) + + with open(output_path, 'r') as f: + expected_output = f.read().strip() + + tests.append({ + 'name': test_name, + 'path': trv_path, + 'expected_output': expected_output + }) + + return tests + + def check_bin(self): + from pathlib import Path + + script_dir = Path(__file__).resolve().parent.parent + setup_path = script_dir / "setup" + + bin_dir = script_dir / "bin" + + if not bin_dir.is_dir(): + raise FileNotFoundError( + f"Missing: compiler, interpreter. Fix by running: {setup_path}" + ) + + missing = [] + + if not (bin_dir / "compiler").is_file(): + missing.append("compiler") + + if not (bin_dir / "interpreter").is_file(): + missing.append("interpreter") + + if missing: + raise FileNotFoundError( + f"Missing: {', '.join(missing)}. Fix by running: {setup_path}" + ) + + def compile_test(self, test, hard, compiled_folder): + trv_path = test['path'] + test_name = test['name'] + + if hard: + asm_path = os.path.join(compiled_folder, f"{test_name}.hard.asm") + else: + asm_path = os.path.join(compiled_folder, f"{test_name}.asm") + + compiler_dir = os.path.join(os.path.dirname(__file__), "..", "bin") + + cmd = [os.path.join(compiler_dir, "compiler"), trv_path, "-o", asm_path] + if hard: + cmd.append("--hard") + + result = subprocess.run(cmd, cwd=compiler_dir, capture_output=True, text=True) + return result.returncode == 0, asm_path, result.stdout, result.stderr + + def list_tests(self): + return self.tests + + def _get_next_run_number(self): + if not os.path.exists(self.artifacts_folder): + return 1 + + max_num = 0 + for item in os.listdir(self.artifacts_folder): + base_name = item[:-4] if item.endswith('.zip') else item + if os.path.isdir(os.path.join(self.artifacts_folder, item)) or item.endswith('.zip'): + parts = base_name.split('-', 1) + if len(parts) == 2 and parts[0].isdigit(): + max_num = max(max_num, int(parts[0])) + return max_num + 1 + + def setup(self): + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + run_num = self._get_next_run_number() + self.run_folder_name = f"{run_num}-{timestamp}" + self.run_folder = os.path.join(self.artifacts_folder, self.run_folder_name) + self.run_tests_folder = os.path.join(self.run_folder, "tests") + self.compiled_folder = os.path.join(self.run_folder, "compiled_test_files") + + os.makedirs(self.run_folder, exist_ok=True) + os.makedirs(self.run_tests_folder, exist_ok=True) + os.makedirs(self.compiled_folder, exist_ok=True) + + for f in os.listdir(self.tests_folder): + if f.endswith('.trv') or f.endswith('.trv.output'): + shutil.copy(os.path.join(self.tests_folder, f), self.run_tests_folder) + + return self.run_folder + + def compile(self): + results = [] + for test in self.tests: + test_result = {'name': test['name'], 'normal': None, 'hard': None} + + for hard in [False, True]: + hard_str = "hard" if hard else "normal" + success, asm_path, stdout, stderr = self.compile_test(test, hard, self.compiled_folder) + test_result[hard_str] = {'success': success, 'asm_path': asm_path, 'stdout': stdout, 'stderr': stderr} + + results.append(test_result) + + return results + + def run_tests(self): + + + zip_base = os.path.join(self.artifacts_folder, self.run_folder_name) + shutil.make_archive(zip_base, 'zip', self.artifacts_folder, self.run_folder_name) + shutil.rmtree(self.run_folder) + + print(f"\nArtifacts saved to: {zip_base}.zip") + + +if __name__ == "__main__": + runner = TestRunner() + + print(f"Running test runner") + print(f"Found {len(runner.tests)} tests") + runner.run_tests() diff --git a/testing/test b/testing/test new file mode 100755 index 0000000..65aae71 --- /dev/null +++ b/testing/test @@ -0,0 +1,33 @@ +#!/bin/python3 + +import argparse + +from src.testrunner import TestRunner + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--config', type=str, default=None, help='Path to config.toml') + parser.add_argument('--skip-run', action='store_true', help='Only setup and compile, skip running tests') + args = parser.parse_args() + + runner = TestRunner(config_path=args.config) + # check that the compiler and interpreter is present: + runner.check_bin() + + + print(f"Setting up...") + folder = runner.setup() + print(f"Created folder: {folder}") + + print(f"\nCompiling tests...") + results = runner.compile() + print(f"Compiled {len(results)} tests") + + if not args.skip_run: + print(f"\nRunning tests...") + runner.run_tests() + else: + print(f"\nSkipping test run (--skip-run)") + +if __name__ == "__main__": + main() diff --git a/testing/tests/simple.trv b/testing/tests/simple.trv new file mode 100644 index 0000000..0205385 --- /dev/null +++ b/testing/tests/simple.trv @@ -0,0 +1,22 @@ +func main() -> Boolean { + let num : Integer = 0; + let test : Boolean = True; + + while num < 10 do { + let etellerandet : Integer = 1; + num = num + etellerandet; + } + + if num > 10 { + num = 12; + let dingdong : Integer = 0; + } else { + num = 11; + } + + if test != True { + return test; + } else { + return num; + } +} diff --git a/testing/tests/simple.trv.output b/testing/tests/simple.trv.output new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/testing/tests/simple.trv.output @@ -0,0 +1 @@ +11