initial commit
This commit is contained in:
commit
5ad6baa30d
35 changed files with 2877 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
tarpaulin-report.html
|
||||
456
Cargo.lock
generated
Normal file
456
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "echse"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "echse_cute"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"echse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[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.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "krabbeltier"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"echse",
|
||||
"shellwords",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "shellwords"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e515aa4699a88148ed5ef96413ceef0048ce95b43fbc955a33bde0a70fcae6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[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.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[workspace]
|
||||
members = ["echse_cute", "krabbeltier", "echse"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
shellwords = { version = "1.1.0" }
|
||||
ariadne = { version = "0.5.0", features = ["auto-color"] }
|
||||
48
README.md
Normal file
48
README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# echse
|
||||
|
||||
The cutest assembly-like language!
|
||||
|
||||
This is for learning or teaching purposes only - and is probably not useful for anything else.
|
||||
|
||||
## From code to running
|
||||
|
||||
1. Code with macros (.es)
|
||||
2. Preprocessor applies macros and resolves jump labels (.ei)
|
||||
3. Transform into binary representation (.eo)
|
||||
4. Run the binary
|
||||
|
||||
Currently, you have to write and execute a .ei file.
|
||||
|
||||
## Ideas and ToDos
|
||||
|
||||
- a cute manual for the provided functionality
|
||||
- preprocessor that supports jumping to labels and unrolled loops
|
||||
- separate machine configuration and program in files
|
||||
- register custom instructions before parsing
|
||||
- optional stack with pus, pop, ret
|
||||
- small games built upon this:
|
||||
- battle multiple programs against each other
|
||||
- rustlings-like exercises to learn the instructions
|
||||
- put multiple echsen into an environment, interact with environment
|
||||
|
||||
## Limitations
|
||||
|
||||
List of constraints that I want to keep:
|
||||
|
||||
- general-purpose registers
|
||||
- are always interpreted as native signed integers
|
||||
- are numbered r0...rn, without gaps
|
||||
- all base instruction
|
||||
- have names with 3 letters
|
||||
- all test instructions start with i (after prefixes) and always set r0
|
||||
- all conditional jumps act based upon r0
|
||||
- when interpreting numbers as booleans: 0 is false, everything else is true. The name of instructions should reflect that.
|
||||
- all extension instructions
|
||||
- have names longer than 3 letters
|
||||
- have a extension prefix
|
||||
- the executed instructions do not know anything about the preprocessor and all formatting and jump marks are lost
|
||||
- there should be no hard-coded calling convention
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under GPL v2 or later, unless stated otherwise in the file.
|
||||
13
all_instructions.ei
Normal file
13
all_instructions.ei
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
add 1 2 r0
|
||||
add r0 r1 r0
|
||||
mod r0 10 r1
|
||||
div 100 r1 r1
|
||||
nop
|
||||
put 42 r1
|
||||
put r1 r0
|
||||
sub 10 5 r1
|
||||
sub r1 r1 r1
|
||||
teq r1 r1
|
||||
tlt r0 r1
|
||||
tze r1
|
||||
jze r0 r1
|
||||
14
echse/Cargo.toml
Normal file
14
echse/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "echse"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.9.3"
|
||||
ariadne = {version = "0.5.0", optional = true}
|
||||
|
||||
[features]
|
||||
default = ["error-report"]
|
||||
error-report = ["dep:ariadne"]
|
||||
135
echse/src/instructions/add_instruction.rs
Normal file
135
echse/src/instructions/add_instruction.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use crate::instructions::{
|
||||
try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterIndex, RegisterOrValue};
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Addition {
|
||||
/// first input
|
||||
pub a: RegisterOrValue,
|
||||
/// second input
|
||||
pub b: RegisterOrValue,
|
||||
/// result register
|
||||
pub r: RegisterIndex,
|
||||
}
|
||||
|
||||
impl Instruction for Addition {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let value = self.a.read(machine) + self.b.read(machine);
|
||||
self.r.write(machine, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Addition {
|
||||
const INSTRUCTION_NAME: &'static str = "add";
|
||||
}
|
||||
|
||||
impl Display for Addition {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"{} {} {} {}",
|
||||
Self::INSTRUCTION_NAME,
|
||||
self.a,
|
||||
self.b,
|
||||
self.r
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Addition {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
let (a, b, r) = try_load_binary_op(params)?;
|
||||
Ok(Rc::new(Self { a, b, r }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Addition, Instruction, ParametersError, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let r1 = RegisterIndex::new(1);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.with_registers(2)
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0, 0]);
|
||||
Instruction::execute(
|
||||
&Addition {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(23),
|
||||
r: r0,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![65, 0]);
|
||||
Instruction::execute(
|
||||
&Addition {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-23),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![65, 42]);
|
||||
Instruction::execute(
|
||||
&Addition {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(r1),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![65, 1379]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
Addition::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 3
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
Addition::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Value(1337),
|
||||
]),
|
||||
Err(ParametersError::UnexpectedType { index: 2 })
|
||||
));
|
||||
assert!(matches!(
|
||||
Addition::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Register(RegisterIndex::R0),
|
||||
]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"add 1337 r0 r1",
|
||||
format!(
|
||||
"{}",
|
||||
Addition {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
r: RegisterIndex(1),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
47
echse/src/instructions/collection.rs
Normal file
47
echse/src/instructions/collection.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use crate::instructions::*;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Index;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Instructions(Vec<Rc<dyn Instruction>>);
|
||||
|
||||
impl Instructions {
|
||||
pub fn empty() -> Self {
|
||||
Self(vec![])
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Rc<dyn Instruction>> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Rc<dyn Instruction>>> for Instructions {
|
||||
fn from(instructions: Vec<Rc<dyn Instruction>>) -> Self {
|
||||
Self(instructions)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Instructions {
|
||||
type Output = Rc<dyn Instruction>;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Instructions {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (index, instr) in self.iter().enumerate() {
|
||||
f.write_fmt(format_args!("{index:03}: {instr}\n"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
135
echse/src/instructions/div_instruction.rs
Normal file
135
echse/src/instructions/div_instruction.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use crate::instructions::{
|
||||
try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterIndex, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Division {
|
||||
/// first input
|
||||
pub a: RegisterOrValue,
|
||||
/// second input
|
||||
pub b: RegisterOrValue,
|
||||
/// result register
|
||||
pub r: RegisterIndex,
|
||||
}
|
||||
|
||||
impl Instruction for Division {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let value = self.a.read(machine) / self.b.read(machine);
|
||||
self.r.write(machine, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Division {
|
||||
const INSTRUCTION_NAME: &'static str = "div";
|
||||
}
|
||||
|
||||
impl Display for Division {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"{} {} {} {}",
|
||||
Self::INSTRUCTION_NAME,
|
||||
self.a,
|
||||
self.b,
|
||||
self.r
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Division {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
let (a, b, r) = try_load_binary_op(params)?;
|
||||
Ok(Rc::new(Self { a, b, r }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Division, Instruction, ParametersError, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let r1 = RegisterIndex::new(1);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.with_registers(2)
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0, 0]);
|
||||
Instruction::execute(
|
||||
&Division {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(10),
|
||||
r: r0,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![4, 0]);
|
||||
Instruction::execute(
|
||||
&Division {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-1),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![4, -4]);
|
||||
Instruction::execute(
|
||||
&Division {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(r1),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![4, -334]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
Division::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 3
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
Division::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Value(1337),
|
||||
]),
|
||||
Err(ParametersError::UnexpectedType { index: 2 })
|
||||
));
|
||||
assert!(matches!(
|
||||
Division::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Register(RegisterIndex::R0),
|
||||
]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"div 1337 r0 r1",
|
||||
format!(
|
||||
"{}",
|
||||
Division {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
r: RegisterIndex(1),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
72
echse/src/instructions/jmp_instruction.rs
Normal file
72
echse/src/instructions/jmp_instruction.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Jump {
|
||||
/// first in
|
||||
pub to: RegisterOrValue,
|
||||
}
|
||||
|
||||
impl Instruction for Jump {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
machine.ip = self.to.read(machine) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Jump {
|
||||
const INSTRUCTION_NAME: &'static str = "jmp";
|
||||
}
|
||||
|
||||
impl Display for Jump {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{} {}", Self::INSTRUCTION_NAME, self.to)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Jump {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 1)?;
|
||||
Ok(Rc::new(Jump { to: params[0] }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, Jump};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn jump() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.minimal_registers()
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0]);
|
||||
assert_eq!(machine.ip, 0);
|
||||
Instruction::execute(
|
||||
&Jump {
|
||||
to: RegisterOrValue::Value(42),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![0]);
|
||||
assert_eq!(machine.ip, 42);
|
||||
machine.registers[0].write(19);
|
||||
assert_eq!(machine.registers.read_all(), vec![19]);
|
||||
Instruction::execute(
|
||||
&Jump {
|
||||
to: RegisterOrValue::Register(r0),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19]);
|
||||
assert_eq!(machine.ip, 19);
|
||||
}
|
||||
}
|
||||
80
echse/src/instructions/jze_instruction.rs
Normal file
80
echse/src/instructions/jze_instruction.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JumpIfZero {
|
||||
pub a: RegisterOrValue,
|
||||
pub to: RegisterOrValue,
|
||||
}
|
||||
|
||||
impl Instruction for JumpIfZero {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
if self.a.read(machine) == 0 {
|
||||
machine.ip = self.to.read(machine) as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for JumpIfZero {
|
||||
const INSTRUCTION_NAME: &'static str = "jze";
|
||||
}
|
||||
|
||||
impl Display for JumpIfZero {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{} {} {}", Self::INSTRUCTION_NAME, self.a, self.to)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for JumpIfZero {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 2)?;
|
||||
Ok(Rc::new(JumpIfZero {
|
||||
a: params[0],
|
||||
to: params[1],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, JumpIfZero};
|
||||
use crate::machine::RegisterOrValue::Value;
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.minimal_registers()
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0]);
|
||||
assert_eq!(machine.ip, 0);
|
||||
Instruction::execute(
|
||||
&JumpIfZero {
|
||||
a: Value(0),
|
||||
to: Value(42),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![0]);
|
||||
assert_eq!(machine.ip, 42);
|
||||
machine.registers[0].write(19);
|
||||
assert_eq!(machine.registers.read_all(), vec![19]);
|
||||
Instruction::execute(
|
||||
&JumpIfZero {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
to: RegisterOrValue::Register(r0),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19]);
|
||||
assert_eq!(machine.ip, 42);
|
||||
}
|
||||
}
|
||||
29
echse/src/instructions/mod.rs
Normal file
29
echse/src/instructions/mod.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
mod add_instruction;
|
||||
mod collection;
|
||||
mod div_instruction;
|
||||
mod jmp_instruction;
|
||||
mod jze_instruction;
|
||||
mod mod_instruction;
|
||||
mod nop_instruction;
|
||||
mod put_instruction;
|
||||
mod sub_instruction;
|
||||
mod teq_instruction;
|
||||
mod tlt_instruction;
|
||||
mod traits;
|
||||
mod try_load;
|
||||
mod tze_instruction;
|
||||
|
||||
pub use add_instruction::*;
|
||||
pub use collection::*;
|
||||
pub use div_instruction::*;
|
||||
pub use jmp_instruction::*;
|
||||
pub use jze_instruction::*;
|
||||
pub use mod_instruction::*;
|
||||
pub use nop_instruction::*;
|
||||
pub use put_instruction::*;
|
||||
pub use sub_instruction::*;
|
||||
pub use teq_instruction::*;
|
||||
pub use tlt_instruction::*;
|
||||
pub use traits::*;
|
||||
pub use try_load::*;
|
||||
pub use tze_instruction::*;
|
||||
135
echse/src/instructions/mod_instruction.rs
Normal file
135
echse/src/instructions/mod_instruction.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use crate::instructions::{
|
||||
try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterIndex, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Modulus {
|
||||
/// first input
|
||||
pub a: RegisterOrValue,
|
||||
/// second input
|
||||
pub b: RegisterOrValue,
|
||||
/// result register
|
||||
pub r: RegisterIndex,
|
||||
}
|
||||
|
||||
impl Instruction for Modulus {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let value = self.a.read(machine) % self.b.read(machine);
|
||||
self.r.write(machine, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Modulus {
|
||||
const INSTRUCTION_NAME: &'static str = "mod";
|
||||
}
|
||||
|
||||
impl Display for Modulus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"{} {} {} {}",
|
||||
Self::INSTRUCTION_NAME,
|
||||
self.a,
|
||||
self.b,
|
||||
self.r
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Modulus {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
let (a, b, r) = try_load_binary_op(params)?;
|
||||
Ok(Rc::new(Self { a, b, r }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, Modulus, ParametersError, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let r1 = RegisterIndex::new(1);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.with_registers(2)
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0, 0]);
|
||||
Instruction::execute(
|
||||
&Modulus {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(23),
|
||||
r: r0,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 0]);
|
||||
Instruction::execute(
|
||||
&Modulus {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-23),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 19]);
|
||||
Instruction::execute(
|
||||
&Modulus {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(r1),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
Modulus::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 3
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
Modulus::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Value(1337),
|
||||
]),
|
||||
Err(ParametersError::UnexpectedType { index: 2 })
|
||||
));
|
||||
assert!(matches!(
|
||||
Modulus::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Register(RegisterIndex::R0),
|
||||
]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"mod 1337 r0 r1",
|
||||
format!(
|
||||
"{}",
|
||||
Modulus {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
r: RegisterIndex(1),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
32
echse/src/instructions/nop_instruction.rs
Normal file
32
echse/src/instructions/nop_instruction.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoOperation {}
|
||||
|
||||
impl Instruction for NoOperation {
|
||||
fn execute(&self, _: &mut Machine) {
|
||||
// intentionally left empty as this is the no operation instruction
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for NoOperation {
|
||||
const INSTRUCTION_NAME: &'static str = "nop";
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for NoOperation {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 0)?;
|
||||
Ok(Rc::new(NoOperation {}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NoOperation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.write_str(Self::INSTRUCTION_NAME)
|
||||
}
|
||||
}
|
||||
79
echse/src/instructions/put_instruction.rs
Normal file
79
echse/src/instructions/put_instruction.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterIndex, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Put {
|
||||
/// input
|
||||
pub a: RegisterOrValue,
|
||||
/// result register
|
||||
pub r: RegisterIndex,
|
||||
}
|
||||
|
||||
impl Instruction for Put {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let value = self.a.read(machine);
|
||||
self.r.write(machine, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Put {
|
||||
const INSTRUCTION_NAME: &'static str = "put";
|
||||
}
|
||||
|
||||
impl Display for Put {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{} {} {}", Self::INSTRUCTION_NAME, self.a, self.r)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Put {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 2)?;
|
||||
|
||||
let r = match params[1] {
|
||||
RegisterOrValue::Register(r) => r,
|
||||
RegisterOrValue::Value(_) => return Err(ParametersError::UnexpectedType { index: 2 }),
|
||||
};
|
||||
|
||||
Ok(Rc::new(Self { a: params[0], r }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, Put};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let r1 = RegisterIndex::new(1);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.with_registers(2)
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0, 0]);
|
||||
Instruction::execute(
|
||||
&Put {
|
||||
a: RegisterOrValue::Value(42),
|
||||
r: r0,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![42, 0]);
|
||||
Instruction::execute(
|
||||
&Put {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![42, 42]);
|
||||
}
|
||||
}
|
||||
135
echse/src/instructions/sub_instruction.rs
Normal file
135
echse/src/instructions/sub_instruction.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use crate::instructions::{
|
||||
try_load_binary_op, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterIndex, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Subtraction {
|
||||
/// first input
|
||||
pub a: RegisterOrValue,
|
||||
/// second input
|
||||
pub b: RegisterOrValue,
|
||||
/// result register
|
||||
pub r: RegisterIndex,
|
||||
}
|
||||
|
||||
impl Instruction for Subtraction {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let value = self.a.read(machine) - self.b.read(machine);
|
||||
self.r.write(machine, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for Subtraction {
|
||||
const INSTRUCTION_NAME: &'static str = "sub";
|
||||
}
|
||||
|
||||
impl Display for Subtraction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"{} {} {} {}",
|
||||
Self::INSTRUCTION_NAME,
|
||||
self.a,
|
||||
self.b,
|
||||
self.r
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for Subtraction {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
let (a, b, r) = try_load_binary_op(params)?;
|
||||
Ok(Rc::new(Self { a, b, r }))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, ParametersError, Subtraction, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let r1 = RegisterIndex::new(1);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.with_registers(2)
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers.read_all(), vec![0, 0]);
|
||||
Instruction::execute(
|
||||
&Subtraction {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(23),
|
||||
r: r0,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 0]);
|
||||
Instruction::execute(
|
||||
&Subtraction {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-23),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 42]);
|
||||
Instruction::execute(
|
||||
&Subtraction {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(r1),
|
||||
r: r1,
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers.read_all(), vec![19, 1295]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
Subtraction::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 3
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
Subtraction::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Value(1337),
|
||||
]),
|
||||
Err(ParametersError::UnexpectedType { index: 2 })
|
||||
));
|
||||
assert!(matches!(
|
||||
Subtraction::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Value(23),
|
||||
RegisterOrValue::Register(RegisterIndex::R0),
|
||||
]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"sub 1337 r0 r1",
|
||||
format!(
|
||||
"{}",
|
||||
Subtraction {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
r: RegisterIndex(1),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
107
echse/src/instructions/teq_instruction.rs
Normal file
107
echse/src/instructions/teq_instruction.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestEqual {
|
||||
pub a: RegisterOrValue,
|
||||
pub b: RegisterOrValue,
|
||||
}
|
||||
|
||||
impl Instruction for TestEqual {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let result = self.a.read(machine) == self.b.read(machine);
|
||||
machine.registers[0].write(if result { 1 } else { 0 });
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for TestEqual {
|
||||
const INSTRUCTION_NAME: &'static str = "teq";
|
||||
}
|
||||
|
||||
impl Display for TestEqual {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{} {} {}", Self::INSTRUCTION_NAME, self.a, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for TestEqual {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 2)?;
|
||||
Ok(Rc::new(Self {
|
||||
a: params[0],
|
||||
b: params[1],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, ParametersError, TestEqual, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.minimal_registers()
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers[0].read(), 0);
|
||||
Instruction::execute(
|
||||
&TestEqual {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(23),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers[0].read(), 0);
|
||||
|
||||
machine.registers[0].write(-23);
|
||||
Instruction::execute(
|
||||
&TestEqual {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-23),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers[0].read(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
TestEqual::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 2
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
TestEqual::try_load(&[
|
||||
RegisterOrValue::Value(42),
|
||||
RegisterOrValue::Register(RegisterIndex::R0),
|
||||
]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"teq 1337 r0",
|
||||
format!(
|
||||
"{}",
|
||||
TestEqual {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
104
echse/src/instructions/tlt_instruction.rs
Normal file
104
echse/src/instructions/tlt_instruction.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
use crate::instructions::{
|
||||
check_param_count, Instruction, InstructionName, ParametersError, TryLoadInstruction,
|
||||
};
|
||||
use crate::machine::{Machine, RegisterOrValue};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestLessThan {
|
||||
pub a: RegisterOrValue,
|
||||
pub b: RegisterOrValue,
|
||||
}
|
||||
|
||||
impl Instruction for TestLessThan {
|
||||
fn execute(&self, machine: &mut Machine) {
|
||||
let result = self.a.read(machine) < self.b.read(machine);
|
||||
machine.registers[0].write(if result { 1 } else { 0 });
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionName for TestLessThan {
|
||||
const INSTRUCTION_NAME: &'static str = "tlt";
|
||||
}
|
||||
|
||||
impl Display for TestLessThan {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{} {} {}", Self::INSTRUCTION_NAME, self.a, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryLoadInstruction for TestLessThan {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError> {
|
||||
check_param_count(params, 2)?;
|
||||
Ok(Rc::new(Self {
|
||||
a: params[0],
|
||||
b: params[1],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instructions::{Instruction, ParametersError, TestLessThan, TryLoadInstruction};
|
||||
use crate::machine::{MachineBuilder, RegisterIndex, RegisterOrValue};
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let r0 = RegisterIndex::new(0);
|
||||
let mut machine = MachineBuilder::new()
|
||||
.minimal_registers()
|
||||
.without_instructions()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(machine.registers[0].read(), 0);
|
||||
Instruction::execute(
|
||||
&TestLessThan {
|
||||
a: RegisterOrValue::Value(42),
|
||||
b: RegisterOrValue::Value(23),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers[0].read(), 0);
|
||||
|
||||
machine.registers[0].write(-24);
|
||||
Instruction::execute(
|
||||
&TestLessThan {
|
||||
a: RegisterOrValue::Register(r0),
|
||||
b: RegisterOrValue::Value(-23),
|
||||
},
|
||||
&mut machine,
|
||||
);
|
||||
assert_eq!(machine.registers[0].read(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
assert!(matches!(
|
||||
TestLessThan::try_load(&[]),
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
found: 0,
|
||||
expected: 2
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
TestLessThan::try_load(&[RegisterOrValue::Value(42), RegisterOrValue::Value(23),]),
|
||||
Ok(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print() {
|
||||
assert_eq!(
|
||||
"tlt 1337 r0",
|
||||
format!(
|
||||
"{}",
|
||||
TestLessThan {
|
||||
a: RegisterOrValue::Value(1337),
|
||||
b: RegisterOrValue::Register(RegisterIndex::R0),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
11
echse/src/instructions/traits.rs
Normal file
11
echse/src/instructions/traits.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use crate::machine::Machine;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub trait Instruction: Debug + Display {
|
||||
// TODO: this needs to be able to return errors, e.g. for division by zero
|
||||
fn execute(&self, machine: &mut Machine);
|
||||
}
|
||||
|
||||
pub trait InstructionName {
|
||||
const INSTRUCTION_NAME: &'static str;
|
||||
}
|
||||
39
echse/src/instructions/try_load.rs
Normal file
39
echse/src/instructions/try_load.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use crate::machine::{RegisterIndex, RegisterOrValue};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) trait TryLoadInstruction {
|
||||
fn try_load(params: &[RegisterOrValue]) -> Result<Rc<Self>, ParametersError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParametersError {
|
||||
UnexpectedCount { expected: usize, found: usize },
|
||||
UnexpectedType { index: usize },
|
||||
}
|
||||
|
||||
pub(crate) fn check_param_count(
|
||||
params: &[RegisterOrValue],
|
||||
expected: usize,
|
||||
) -> Result<(), ParametersError> {
|
||||
if params.len() != expected {
|
||||
Err(ParametersError::UnexpectedCount {
|
||||
expected,
|
||||
found: params.len(),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_load_binary_op(
|
||||
params: &[RegisterOrValue],
|
||||
) -> Result<(RegisterOrValue, RegisterOrValue, RegisterIndex), ParametersError> {
|
||||
check_param_count(params, 3)?;
|
||||
|
||||
let r = match params[2] {
|
||||
RegisterOrValue::Register(r) => r,
|
||||
RegisterOrValue::Value(_) => return Err(ParametersError::UnexpectedType { index: 2 }),
|
||||
};
|
||||
|
||||
Ok((params[0], params[1], r))
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue