forked from koniifer/ableos
New float instructions, Linux runtime added, some other stuff I forgor
This commit is contained in:
parent
889aefe87a
commit
0cb20d5727
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[alias]
|
||||||
|
xtask = "r -p xtask --"
|
317
Cargo.lock
generated
317
Cargo.lock
generated
|
@ -2,6 +2,131 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argh"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219"
|
||||||
|
dependencies = [
|
||||||
|
"argh_derive",
|
||||||
|
"argh_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argh_derive"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a"
|
||||||
|
dependencies = [
|
||||||
|
"argh_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.38",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argh_shared"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color-eyre"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"color-spantrace",
|
||||||
|
"eyre",
|
||||||
|
"indenter",
|
||||||
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
|
"tracing-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color-spantrace"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eyre"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
|
||||||
|
dependencies = [
|
||||||
|
"indenter",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hbbytecode"
|
name = "hbbytecode"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -21,13 +146,85 @@ name = "hbxrt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hbvm",
|
"hbvm",
|
||||||
|
"nix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "indenter"
|
||||||
version = "1.0.67"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.32.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owo-colors"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -41,6 +238,41 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.38",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -52,12 +284,80 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-error"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
|
||||||
|
dependencies = [
|
||||||
|
"sharded-slab",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "with_builtin_macros"
|
name = "with_builtin_macros"
|
||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
|
@ -75,5 +375,14 @@ checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"argh",
|
||||||
|
"color-eyre",
|
||||||
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["hbbytecode", "hbvm", "hbxrt"]
|
members = ["hbbytecode", "hbvm", "hbxrt", "xtask"]
|
||||||
|
|
BIN
examples/addition.hbf
Normal file
BIN
examples/addition.hbf
Normal file
Binary file not shown.
BIN
examples/linux-hello.hbf
Normal file
BIN
examples/linux-hello.hbf
Normal file
Binary file not shown.
|
@ -1,70 +1,120 @@
|
||||||
// OPCODE, MNEMONIC, TYPE, DOC;
|
0x00, UN, N, "Cause an unreachable code trap" ;
|
||||||
|
0x01, TX, N, "Termiante execution" ;
|
||||||
0, UN, N, "Cause an unreachable code trap" ;
|
0x02, NOP, N, "Do nothing" ;
|
||||||
1, TX, N, "Termiante execution" ;
|
0x03, ADD8, RRR, "Addition (8b)" ;
|
||||||
2, NOP, N, "Do nothing" ;
|
0x04, ADD16, RRR, "Addition (16b)" ;
|
||||||
|
0x05, ADD32, RRR, "Addition (32b)" ;
|
||||||
3, ADD, RRR, "Addition" ;
|
0x06, ADD64, RRR, "Addition (64b)" ;
|
||||||
4, SUB, RRR, "Subtraction" ;
|
0x07, SUB8, RRR, "Subtraction (8b)" ;
|
||||||
5, MUL, RRR, "Multiplication" ;
|
0x08, SUB16, RRR, "Subtraction (16b)" ;
|
||||||
6, AND, RRR, "Bitand" ;
|
0x09, SUB32, RRR, "Subtraction (32b)" ;
|
||||||
7, OR, RRR, "Bitor" ;
|
0x0A, SUB64, RRR, "Subtraction (64b)" ;
|
||||||
8, XOR, RRR, "Bitxor" ;
|
0x0B, MUL8, RRR, "Multiplication (8b)" ;
|
||||||
9, SL, RRR, "Unsigned left bitshift" ;
|
0x0C, MUL16, RRR, "Multiplication (16b)" ;
|
||||||
10, SR, RRR, "Unsigned right bitshift" ;
|
0x0D, MUL32, RRR, "Multiplication (32b)" ;
|
||||||
11, SRS, RRR, "Signed right bitshift" ;
|
0x0E, MUL64, RRR, "Multiplication (64b)" ;
|
||||||
12, CMP, RRR, "Signed comparsion" ;
|
0x0F, AND, RRR, "Bitand" ;
|
||||||
13, CMPU, RRR, "Unsigned comparsion" ;
|
0x10, OR, RRR, "Bitor" ;
|
||||||
14, DIR, RRRR, "Merged divide-remainder" ;
|
0x11, XOR, RRR, "Bitxor" ;
|
||||||
15, NEG, RR, "Bit negation" ;
|
0x12, SLU8, RRR, "Unsigned left bitshift (8b)" ;
|
||||||
16, NOT, RR, "Logical negation" ;
|
0x13, SLU16, RRR, "Unsigned left bitshift (16b)" ;
|
||||||
17, ADDI, RRD, "Addition with immediate" ;
|
0x14, SLU32, RRR, "Unsigned left bitshift (32b)" ;
|
||||||
18, MULI, RRD, "Multiplication with immediate" ;
|
0x15, SLU64, RRR, "Unsigned left bitshift (64b)" ;
|
||||||
19, ANDI, RRD, "Bitand with immediate" ;
|
0x16, SRU8, RRR, "Unsigned right bitshift (8b)" ;
|
||||||
20, ORI, RRD, "Bitor with immediate" ;
|
0x17, SRU16, RRR, "Unsigned right bitshift (16b)" ;
|
||||||
21, XORI, RRD, "Bitxor with immediate" ;
|
0x18, SRU32, RRR, "Unsigned right bitshift (32b)" ;
|
||||||
22, SLI, RRW, "Unsigned left bitshift with immedidate";
|
0x19, SRU64, RRR, "Unsigned right bitshift (64b)" ;
|
||||||
23, SRI, RRW, "Unsigned right bitshift with immediate";
|
0x1A, SRS8, RRR, "Signed right bitshift (8b)" ;
|
||||||
24, SRSI, RRW, "Signed right bitshift with immediate" ;
|
0x1B, SRS16, RRR, "Signed right bitshift (16b)" ;
|
||||||
25, CMPI, RRD, "Signed compare with immediate" ;
|
0x1C, SRS32, RRR, "Signed right bitshift (32b)" ;
|
||||||
26, CMPUI, RRD, "Unsigned compare with immediate" ;
|
0x1D, SRS64, RRR, "Signed right bitshift (64b)" ;
|
||||||
|
0x1E, CMP, RRR, "Signed comparsion" ;
|
||||||
27, CP, RR, "Copy register" ;
|
0x1F, CMPU, RRR, "Unsigned comparsion" ;
|
||||||
28, SWA, RR, "Swap registers" ;
|
0x20, DIRU8, RRRR, "Merged divide-remainder (unsigned 8b)" ;
|
||||||
29, LI, RD, "Load immediate" ;
|
0x21, DIRU16, RRRR, "Merged divide-remainder (unsigned 16b)" ;
|
||||||
30, LRA, RRO, "Load relative address" ;
|
0x22, DIRU32, RRRR, "Merged divide-remainder (unsigned 32b)" ;
|
||||||
31, LD, RRAH, "Load from absolute address" ;
|
0x23, DIRU64, RRRR, "Merged divide-remainder (unsigned 64b)" ;
|
||||||
32, ST, RRAH, "Store to absolute address" ;
|
0x24, DIRS8, RRRR, "Merged divide-remainder (signed 8b)" ;
|
||||||
33, LDR, RROH, "Load from relative address" ;
|
0x25, DIRS16, RRRR, "Merged divide-remainder (signed 16b)" ;
|
||||||
34, STR, RROH, "Store to relative address" ;
|
0x26, DIRS32, RRRR, "Merged divide-remainder (signed 32b)" ;
|
||||||
35, BMC, RRH, "Copy block of memory" ;
|
0x27, DIRS64, RRRR, "Merged divide-remainder (signed 64b)" ;
|
||||||
36, BRC, RRB, "Copy register block" ;
|
0x28, NEG, RR, "Bit negation" ;
|
||||||
|
0x29, NOT, RR, "Logical negation" ;
|
||||||
37, JMP, O, "Relative jump" ;
|
0x2A, SXT8, RR, "Sign extend 8b to 64b" ;
|
||||||
38, JAL, RRA, "Linking absolute jump" ;
|
0x2B, SXT16, RR, "Sign extend 16b to 64b" ;
|
||||||
39, JALR, RRO, "Linking relative jump" ;
|
0x2C, SXT32, RR, "Sign extend 32b to 64b" ;
|
||||||
40, JEQ, RRP, "Branch on equal" ;
|
0x2D, ADDI8, RRB, "Addition with immediate (8b)" ;
|
||||||
41, JNE, RRP, "Branch on nonequal" ;
|
0x2E, ADDI16, RRH, "Addition with immediate (16b)" ;
|
||||||
42, JLT, RRP, "Branch on lesser-than (signed)" ;
|
0x2F, ADDI32, RRW, "Addition with immediate (32b)" ;
|
||||||
43, JGT, RRP, "Branch on greater-than (signed)" ;
|
0x30, ADDI64, RRD, "Addition with immediate (64b)" ;
|
||||||
44, JLTU, RRP, "Branch on lesser-than (unsigned)" ;
|
0x31, MULI8, RRW, "Multiplication with immediate (8b)" ;
|
||||||
45, JGTU, RRP, "Branch on greater-than (unsigned)" ;
|
0x32, MULI16, RRH, "Multiplication with immediate (16b)" ;
|
||||||
46, ECA, N, "Environment call trap" ;
|
0x33, MULI32, RRW, "Multiplication with immediate (32b)" ;
|
||||||
47, EBP, N, "Environment breakpoint" ;
|
0x34, MULI64, RRD, "Multiplication with immediate (64b)" ;
|
||||||
|
0x35, ANDI, RRD, "Bitand with immediate" ;
|
||||||
48, ADDF, RRR, "Floating addition" ;
|
0x36, ORI, RRD, "Bitor with immediate" ;
|
||||||
49, SUBF, RRR, "Floating subtraction" ;
|
0x37, XORI, RRD, "Bitxor with immediate" ;
|
||||||
50, MULF, RRR, "Floating multiply" ;
|
0x38, SLUI8, RRW, "Unsigned left bitshift with immedidate (8b)" ;
|
||||||
51, DIRF, RRRR, "Merged floating divide-remainder" ;
|
0x39, SLUI16, RRW, "Unsigned left bitshift with immedidate (16b)";
|
||||||
52, FMAF, RRRR, "Fused floating multiply-add" ;
|
0x3A, SLUI32, RRW, "Unsigned left bitshift with immedidate (32b)";
|
||||||
53, NEGF, RR, "Floating sign negation" ;
|
0x3B, SLUI64, RRW, "Unsigned left bitshift with immedidate (64b)";
|
||||||
54, ITF, RR, "Int to float" ;
|
0x3C, SRUI8, RRW, "Unsigned right bitshift with immediate (8b)" ;
|
||||||
55, FTI, RR, "Float to int" ;
|
0x3D, SRUI16, RRW, "Unsigned right bitshift with immediate (16b)";
|
||||||
|
0x3E, SRUI32, RRW, "Unsigned right bitshift with immediate (32b)";
|
||||||
56, ADDFI, RRD, "Floating addition with immediate" ;
|
0x3F, SRUI64, RRW, "Unsigned right bitshift with immediate (64b)";
|
||||||
57, MULFI, RRD, "Floating multiplication with immediate";
|
0x40, SRSI8, RRW, "Signed right bitshift with immediate" ;
|
||||||
|
0x41, SRSI16, RRW, "Signed right bitshift with immediate" ;
|
||||||
58, LRA16 , RRP, "Load relative immediate (16 bit)" ;
|
0x42, SRSI32, RRW, "Signed right bitshift with immediate" ;
|
||||||
59, LDR16 , RRPH, "Load from relative address (16 bit)" ;
|
0x43, SRSI64, RRW, "Signed right bitshift with immediate" ;
|
||||||
60, STR16 , RRPH, "Store to relative address (16 bit)" ;
|
0x44, CMPI, RRD, "Signed compare with immediate" ;
|
||||||
61, JMPR16, P, "Relative jump (16 bit)" ;
|
0x45, CMPUI, RRD, "Unsigned compare with immediate" ;
|
||||||
|
0x46, CP, RR, "Copy register" ;
|
||||||
|
0x47, SWA, RR, "Swap registers" ;
|
||||||
|
0x48, LI8, RB, "Load immediate (8b)" ;
|
||||||
|
0x49, LI16, RH, "Load immediate (16b)" ;
|
||||||
|
0x4A, LI32, RW, "Load immediate (32b)" ;
|
||||||
|
0x4B, LI64, RD, "Load immediate (64b)" ;
|
||||||
|
0x4C, LRA, RRO, "Load relative address" ;
|
||||||
|
0x4D, LD, RRAH, "Load from absolute address" ;
|
||||||
|
0x4E, ST, RRAH, "Store to absolute address" ;
|
||||||
|
0x4F, LDR, RROH, "Load from relative address" ;
|
||||||
|
0x50, STR, RROH, "Store to relative address" ;
|
||||||
|
0x51, BMC, RRH, "Copy block of memory" ;
|
||||||
|
0x52, BRC, RRB, "Copy register block" ;
|
||||||
|
0x53, JMP, O, "Relative jump" ;
|
||||||
|
0x54, JAL, RRA, "Linking absolute jump" ;
|
||||||
|
0x55, JALR, RRO, "Linking relative jump" ;
|
||||||
|
0x56, JEQ, RRP, "Branch on equal" ;
|
||||||
|
0x57, JNE, RRP, "Branch on nonequal" ;
|
||||||
|
0x58, JLT, RRP, "Branch on lesser-than (signed)" ;
|
||||||
|
0x59, JGT, RRP, "Branch on greater-than (signed)" ;
|
||||||
|
0x5A, JLTU, RRP, "Branch on lesser-than (unsigned)" ;
|
||||||
|
0x5B, JGTU, RRP, "Branch on greater-than (unsigned)" ;
|
||||||
|
0x5C, ECA, N, "Environment call trap" ;
|
||||||
|
0x5D, EBP, N, "Environment breakpoint" ;
|
||||||
|
0x5E, FADD32, RRR, "Floating point addition (32b)" ;
|
||||||
|
0x5F, FADD64, RRR, "Floating point addition (64b)" ;
|
||||||
|
0x60, FSUB32, RRR, "Floating point subtraction (32b)" ;
|
||||||
|
0x61, FSUB64, RRR, "Floating point subtraction (64b)" ;
|
||||||
|
0x62, FMUL32, RRR, "Floating point multiply (32b)" ;
|
||||||
|
0x63, FMUL64, RRR, "Floating point multiply (64b)" ;
|
||||||
|
0x64, FDIV32, RRR, "Floating point division (32b)" ;
|
||||||
|
0x65, FDIV64, RRR, "Floating point division (64b)" ;
|
||||||
|
0x66, FMA32, RRR, "Float fused multiply-add (32b)" ;
|
||||||
|
0x67, FMA64, RRR, "Float fused multiply-add (64b)" ;
|
||||||
|
0x68, FINV32, RR, "Float reciprocal (32b)" ;
|
||||||
|
0x69, FINV64, RR, "Float reciprocal (64b)" ;
|
||||||
|
0x6A, FCMPLT32, RRR, "Flaot compare less than (32b)" ;
|
||||||
|
0x6B, FCMPLT64, RRR, "Flaot compare less than (64b)" ;
|
||||||
|
0x6C, FCMPGT32, RRR, "Flaot compare greater than (32b)" ;
|
||||||
|
0x6D, FCMPGT64, RRR, "Flaot compare greater than (64b)" ;
|
||||||
|
0x6E, ITF32, RR, "Int to 32 bit float" ;
|
||||||
|
0x6F, ITF64, RR, "Int to 64 bit float" ;
|
||||||
|
0x70, FTI32, RRB, "Float 32 to int" ;
|
||||||
|
0x71, FTI64, RRB, "Float 64 to int" ;
|
||||||
|
0x72, FC32T64, RR, "Float 64 to Float 32" ;
|
||||||
|
0x73, FC64T32, RRB, "Float 32 to Float 64" ;
|
||||||
|
0x74, LRA16, RRP, "Load relative immediate (16 bit)" ;
|
||||||
|
0x75, LDR16, RRPH, "Load from relative address (16 bit)" ;
|
||||||
|
0x76, STR16, RRPH, "Store to relative address (16 bit)" ;
|
||||||
|
0x77, JMP16, P, "Relative jump (16 bit)" ;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
type OpR = u8;
|
type OpR = u8;
|
||||||
|
|
||||||
type OpA = u64;
|
type OpA = u64;
|
||||||
|
@ -17,6 +19,7 @@ pub unsafe trait BytecodeItem {}
|
||||||
macro_rules! define_items {
|
macro_rules! define_items {
|
||||||
($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => {
|
($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => {
|
||||||
$(
|
$(
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct $name($(pub $item),*);
|
pub struct $name($(pub $item),*);
|
||||||
unsafe impl BytecodeItem for $name {}
|
unsafe impl BytecodeItem for $name {}
|
||||||
|
@ -31,6 +34,9 @@ define_items! {
|
||||||
OpsRRB (OpR, OpR, OpB ),
|
OpsRRB (OpR, OpR, OpB ),
|
||||||
OpsRRH (OpR, OpR, OpH ),
|
OpsRRH (OpR, OpR, OpH ),
|
||||||
OpsRRW (OpR, OpR, OpW ),
|
OpsRRW (OpR, OpR, OpW ),
|
||||||
|
OpsRB (OpR, OpB ),
|
||||||
|
OpsRH (OpR, OpH ),
|
||||||
|
OpsRW (OpR, OpW ),
|
||||||
OpsRD (OpR, OpD ),
|
OpsRD (OpR, OpD ),
|
||||||
OpsRRD (OpR, OpR, OpD ),
|
OpsRRD (OpR, OpR, OpD ),
|
||||||
OpsRRAH (OpR, OpR, OpA, OpH),
|
OpsRRAH (OpR, OpR, OpA, OpH),
|
||||||
|
@ -89,4 +95,24 @@ macro_rules! gen_opcodes {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rounding mode
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum RoundingMode {
|
||||||
|
NearestEven = 0,
|
||||||
|
Truncate = 1,
|
||||||
|
Up = 2,
|
||||||
|
Down = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for RoundingMode {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
(value >= 3)
|
||||||
|
.then(|| unsafe { core::mem::transmute(value) })
|
||||||
|
.ok_or(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
invoke_with_def!(gen_opcodes);
|
invoke_with_def!(gen_opcodes);
|
||||||
|
|
66
hbvm/src/float/aarch64.rs
Normal file
66
hbvm/src/float/aarch64.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use {core::arch::asm, hbbytecode::RoundingMode};
|
||||||
|
|
||||||
|
macro_rules! fnsdef {
|
||||||
|
{$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $name:ident[$inreg:ident -> $outreg:ident]($from:ident -> $to:ident): $ins:literal;
|
||||||
|
)*} => {$(
|
||||||
|
$(#[$attr])*
|
||||||
|
$vis fn $name(val: $from, mode: RoundingMode) -> $to {
|
||||||
|
let result: $to;
|
||||||
|
unsafe {
|
||||||
|
set_rounding_mode(mode);
|
||||||
|
asm!(
|
||||||
|
$ins,
|
||||||
|
out($outreg) result,
|
||||||
|
in($inreg) val,
|
||||||
|
);
|
||||||
|
default_rounding_mode();
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
fnsdef! {
|
||||||
|
/// Convert [`f64`] to [`f32`] with chosen rounding mode
|
||||||
|
pub fn conv64to32[vreg -> vreg](f64 -> f32): "fcvt {:s}, {:d}";
|
||||||
|
|
||||||
|
/// Convert [`f32`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f32toint[vreg -> reg](f32 -> i64): "fcvtzs {}, {:s}";
|
||||||
|
|
||||||
|
/// Convert [`f64`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f64toint[vreg -> reg](f64 -> i64): "fcvtzs {}, {:d}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set rounding mode
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Do not call if rounding mode isn't [`RoundingMode::NearestEven`]
|
||||||
|
/// - Do not perform any Rust FP operations until reset using
|
||||||
|
/// [`default_rounding_mode`], you have to rely on inline assembly
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn set_rounding_mode(mode: RoundingMode) {
|
||||||
|
if mode == RoundingMode::NearestEven {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fpcr: u64;
|
||||||
|
asm!("mrs {}, fpcr", out(reg) fpcr);
|
||||||
|
|
||||||
|
let fpcr = fpcr & !(0b11 << 22)
|
||||||
|
| (match mode {
|
||||||
|
RoundingMode::NearestEven => 0b00,
|
||||||
|
RoundingMode::Truncate => 0b11,
|
||||||
|
RoundingMode::Up => 0b01,
|
||||||
|
RoundingMode::Down => 0b10,
|
||||||
|
}) << 22;
|
||||||
|
|
||||||
|
asm!("msr fpcr, {}", in(reg) fpcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn default_rounding_mode() {
|
||||||
|
// I hope so much it gets optimised
|
||||||
|
set_rounding_mode(RoundingMode::NearestEven);
|
||||||
|
}
|
17
hbvm/src/float/mod.rs
Normal file
17
hbvm/src/float/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
macro_rules! arch_specific {
|
||||||
|
{
|
||||||
|
$({$($cfg:tt)*} : $mod:ident;)*
|
||||||
|
} => {$(
|
||||||
|
#[cfg($($cfg)*)]
|
||||||
|
mod $mod;
|
||||||
|
|
||||||
|
#[cfg($($cfg)*)]
|
||||||
|
pub use $mod::*;
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_specific! {
|
||||||
|
{target_arch = "x86_64" }: x86_64;
|
||||||
|
{target_arch = "riscv64"}: riscv64;
|
||||||
|
{target_arch = "aarch64"}: aarch64;
|
||||||
|
}
|
59
hbvm/src/float/riscv64.rs
Normal file
59
hbvm/src/float/riscv64.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use {core::arch::asm, hbbytecode::RoundingMode};
|
||||||
|
|
||||||
|
macro_rules! roundm_op_litmode_internal {
|
||||||
|
($ins:literal, $in:expr, $out:expr => $outy:ident, $mode:literal $(,)?) => {
|
||||||
|
asm!(
|
||||||
|
concat!($ins, " {}, {}, ", $mode),
|
||||||
|
out($outy) $out,
|
||||||
|
in(freg) $in,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! gen_roundm_op_litmode {
|
||||||
|
[$($ty:ident => $reg:ident),* $(,)?] => {
|
||||||
|
macro_rules! roundm_op_litmode {
|
||||||
|
$(
|
||||||
|
($ins:literal, $in:expr, $out:expr => $ty, $mode:literal) => {
|
||||||
|
roundm_op_litmode_internal!($ins, $in, $out => $reg, $mode)
|
||||||
|
};
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_roundm_op_litmode![
|
||||||
|
f32 => freg,
|
||||||
|
f64 => freg,
|
||||||
|
i64 => reg,
|
||||||
|
];
|
||||||
|
|
||||||
|
macro_rules! fnsdef {
|
||||||
|
{$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $name:ident($from:ident -> $to:ident): $ins:literal;
|
||||||
|
)*} => {$(
|
||||||
|
$(#[$attr])*
|
||||||
|
$vis fn $name(val: $from, mode: RoundingMode) -> $to {
|
||||||
|
let result: $to;
|
||||||
|
unsafe {
|
||||||
|
match mode {
|
||||||
|
RoundingMode::NearestEven => roundm_op_litmode!($ins, val, result => $to, "rne"),
|
||||||
|
RoundingMode::Truncate => roundm_op_litmode!($ins, val, result => $to, "rtz"),
|
||||||
|
RoundingMode::Up => roundm_op_litmode!($ins, val, result => $to, "rup"),
|
||||||
|
RoundingMode::Down => roundm_op_litmode!($ins, val, result => $to, "rdn"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
fnsdef! {
|
||||||
|
/// Convert [`f64`] to [`f32`] with chosen rounding mode
|
||||||
|
pub fn conv64to32(f64 -> f32): "fcvt.s.d";
|
||||||
|
/// Convert [`f32`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f32toint(f32 -> i64): "fcvt.l.s";
|
||||||
|
/// Convert [`f64`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f64toint(f64 -> i64): "fcvt.l.d";
|
||||||
|
}
|
71
hbvm/src/float/x86_64.rs
Normal file
71
hbvm/src/float/x86_64.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use {
|
||||||
|
core::arch::{asm, x86_64 as arin},
|
||||||
|
hbbytecode::RoundingMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! gen_op {
|
||||||
|
[$($ty:ident => $reg:ident),* $(,)?] => {
|
||||||
|
macro_rules! op {
|
||||||
|
$(
|
||||||
|
($ins:literal, $in:expr, $out:expr => $ty) => {
|
||||||
|
asm!(concat!($ins, " {}, {}"), out($reg) $out, in(xmm_reg) $in)
|
||||||
|
};
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_op![
|
||||||
|
f32 => xmm_reg,
|
||||||
|
f64 => xmm_reg,
|
||||||
|
i64 => reg,
|
||||||
|
];
|
||||||
|
|
||||||
|
macro_rules! fnsdef {
|
||||||
|
{$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$vis:vis fn $name:ident($from:ident -> $to:ident): $ins:literal;
|
||||||
|
)*} => {$(
|
||||||
|
$(#[$attr])*
|
||||||
|
$vis fn $name(val: $from, mode: RoundingMode) -> $to {
|
||||||
|
let result: $to;
|
||||||
|
unsafe {
|
||||||
|
set_rounding_mode(mode);
|
||||||
|
op!($ins, val, result => $to);
|
||||||
|
default_rounding_mode();
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
fnsdef! {
|
||||||
|
/// Convert [`f64`] to [`f32`] with chosen rounding mode
|
||||||
|
pub fn conv64to32(f64 -> f32): "cvtsd2ss";
|
||||||
|
/// Convert [`f32`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f32toint(f32 -> i64): "cvttss2si";
|
||||||
|
/// Convert [`f64`] to [`i64`] with chosen rounding mode
|
||||||
|
pub fn f64toint(f64 -> i64): "cvttsd2si";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set rounding mode
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Do not call if rounding mode isn't [`RoundingMode::NearestEven`]
|
||||||
|
/// - Do not perform any Rust FP operations until reset using
|
||||||
|
/// [`default_rounding_mode`], you have to rely on inline assembly
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn set_rounding_mode(mode: RoundingMode) {
|
||||||
|
arin::_MM_SET_ROUNDING_MODE(match mode {
|
||||||
|
RoundingMode::NearestEven => return,
|
||||||
|
RoundingMode::Truncate => arin::_MM_ROUND_TOWARD_ZERO,
|
||||||
|
RoundingMode::Up => arin::_MM_ROUND_UP,
|
||||||
|
RoundingMode::Down => arin::_MM_ROUND_DOWN,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_rounding_mode() {
|
||||||
|
// SAFETY: This is said to be the default mode, do not trust me.
|
||||||
|
unsafe { arin::_MM_SET_ROUNDING_MODE(arin::_MM_ROUND_NEAREST) };
|
||||||
|
}
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
use mem::{Address, Memory};
|
use mem::{Address, Memory};
|
||||||
|
|
||||||
|
@ -23,6 +22,7 @@ pub mod mem;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
mod bmc;
|
mod bmc;
|
||||||
|
mod float;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod vmrun;
|
mod vmrun;
|
||||||
|
|
||||||
|
@ -92,6 +92,12 @@ pub enum VmRunError {
|
||||||
|
|
||||||
/// Reached unreachable code
|
/// Reached unreachable code
|
||||||
Unreachable,
|
Unreachable,
|
||||||
|
|
||||||
|
/// Invalid operand
|
||||||
|
InvalidOperand,
|
||||||
|
|
||||||
|
/// Unimplemented feature
|
||||||
|
Unimplemented,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Virtual machine halt ok
|
/// Virtual machine halt ok
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub(crate) mod addr;
|
||||||
|
|
||||||
pub use addr::Address;
|
pub use addr::Address;
|
||||||
|
|
||||||
use {crate::utils::impl_display, hbbytecode::BytecodeItem};
|
use crate::utils::impl_display;
|
||||||
|
|
||||||
/// Load-store memory access
|
/// Load-store memory access
|
||||||
pub trait Memory {
|
pub trait Memory {
|
||||||
|
@ -36,13 +36,7 @@ pub trait Memory {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - Data read have to be valid
|
/// - Data read have to be valid
|
||||||
unsafe fn prog_read<T: BytecodeItem>(&mut self, addr: Address) -> Option<T>;
|
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T;
|
||||||
|
|
||||||
/// Read from program memory to exectue
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// - You have to be really sure that these bytes are there, understand?
|
|
||||||
unsafe fn prog_read_unchecked<T: BytecodeItem>(&mut self, addr: Address) -> T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unhandled load access trap
|
/// Unhandled load access trap
|
||||||
|
|
|
@ -78,31 +78,19 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn prog_read<T>(&mut self, addr: Address) -> Option<T> {
|
unsafe fn prog_read<T>(&mut self, addr: Address) -> T {
|
||||||
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
|
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
|
||||||
return self.icache.fetch::<T>(addr, self.root_pt);
|
return self
|
||||||
|
.icache
|
||||||
|
.fetch::<T>(addr, self.root_pt)
|
||||||
|
.unwrap_or_else(|| unsafe { core::mem::zeroed() });
|
||||||
}
|
}
|
||||||
|
|
||||||
let addr = addr.truncate_usize();
|
let addr = addr.truncate_usize();
|
||||||
self.program
|
self.program
|
||||||
.get(addr..addr + size_of::<T>())
|
.get(addr..addr + size_of::<T>())
|
||||||
.map(|x| x.as_ptr().cast::<T>().read())
|
.map(|x| x.as_ptr().cast::<T>().read())
|
||||||
}
|
.unwrap_or_else(|| unsafe { core::mem::zeroed() })
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn prog_read_unchecked<T>(&mut self, addr: Address) -> T {
|
|
||||||
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
|
|
||||||
return self
|
|
||||||
.icache
|
|
||||||
.fetch::<T>(addr, self.root_pt)
|
|
||||||
.unwrap_or_else(|| core::mem::zeroed());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.program
|
|
||||||
.as_ptr()
|
|
||||||
.add(addr.truncate_usize())
|
|
||||||
.cast::<T>()
|
|
||||||
.read()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@ pub(crate) mod internal {
|
||||||
pub(crate) use impl_display_match_fragment;
|
pub(crate) use impl_display_match_fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! static_assert_eq(($l:expr, $r:expr $(,)?) => {
|
macro_rules! static_assert(($expr:expr $(,)?) => {
|
||||||
const _: [(); ($l != $r) as usize] = [];
|
const _: [(); !$expr as usize] = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
pub(crate) use {impl_display, static_assert_eq};
|
pub(crate) use {impl_display, static_assert};
|
||||||
|
|
|
@ -1,35 +1,32 @@
|
||||||
//! HoleyBytes register value definition
|
//! HoleyBytes register value definition
|
||||||
|
|
||||||
/// Define [`Value`] union
|
use crate::utils::static_assert;
|
||||||
|
|
||||||
|
/// Define [`Value`] »union« (it's fake)
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Union variants have to be sound to byte-reinterpretate
|
/// Its variants have to be sound to byte-reinterpretate
|
||||||
/// between each other. Otherwise the behaviour is undefined.
|
/// between each other. Otherwise the behaviour is undefined.
|
||||||
macro_rules! value_def {
|
macro_rules! value_def {
|
||||||
($($ty:ident),* $(,)?) => {
|
($($ty:ident),* $(,)?) => {
|
||||||
/// HBVM register value
|
/// HBVM register value
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(packed)]
|
#[repr(transparent)]
|
||||||
pub union Value {
|
pub struct Value(pub u64);
|
||||||
$(
|
|
||||||
#[doc = concat!(stringify!($ty), " type")]
|
|
||||||
pub $ty: $ty
|
|
||||||
),*
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(
|
$(
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: $ty) -> Self {
|
fn from(value: $ty) -> Self {
|
||||||
Self { $ty: value }
|
let mut new = core::mem::MaybeUninit::<u64>::zeroed();
|
||||||
|
unsafe {
|
||||||
|
new.as_mut_ptr().cast::<$ty>().write(value);
|
||||||
|
Self(new.assume_init())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::utils::static_assert_eq!(
|
static_assert!(core::mem::size_of::<$ty>() <= core::mem::size_of::<Value>());
|
||||||
core::mem::size_of::<$ty>(),
|
|
||||||
core::mem::size_of::<Value>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
impl private::Sealed for $ty {}
|
impl private::Sealed for $ty {}
|
||||||
unsafe impl ValueVariant for $ty {}
|
unsafe impl ValueVariant for $ty {}
|
||||||
|
@ -41,23 +38,7 @@ impl Value {
|
||||||
/// Byte reinterpret value to target variant
|
/// Byte reinterpret value to target variant
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cast<V: ValueVariant>(self) -> V {
|
pub fn cast<V: ValueVariant>(self) -> V {
|
||||||
/// Evil.
|
unsafe { core::mem::transmute_copy(&self.0) }
|
||||||
///
|
|
||||||
/// Transmute cannot be performed with generic type
|
|
||||||
/// as size is unknown, so union is used.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// If [`ValueVariant`] implemented correctly, it's fine :)
|
|
||||||
///
|
|
||||||
/// :ferrisClueless:
|
|
||||||
union Transmute<Variant: ValueVariant> {
|
|
||||||
/// Self
|
|
||||||
src: Value,
|
|
||||||
/// Target variant
|
|
||||||
variant: Variant,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { Transmute { src: self }.variant }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +50,8 @@ mod private {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
}
|
}
|
||||||
|
|
||||||
value_def!(u64, i64, f64);
|
value_def!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
|
||||||
crate::utils::static_assert_eq!(core::mem::size_of::<Value>(), 8);
|
static_assert!(core::mem::size_of::<Value>() == 8);
|
||||||
|
|
||||||
impl core::fmt::Debug for Value {
|
impl core::fmt::Debug for Value {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
@ -78,3 +59,28 @@ impl core::fmt::Debug for Value {
|
||||||
write!(f, "{:x}", self.cast::<u64>())
|
write!(f, "{:x}", self.cast::<u64>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait CheckedDivRem {
|
||||||
|
fn checked_div(self, other: Self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
fn checked_rem(self, other: Self) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_checked_div_rem {
|
||||||
|
($($ty:ty),* $(,)?) => {
|
||||||
|
$(impl CheckedDivRem for $ty {
|
||||||
|
#[inline(always)]
|
||||||
|
fn checked_div(self, another: Self) -> Option<Self>
|
||||||
|
{ self.checked_div(another) }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn checked_rem(self, another: Self) -> Option<Self>
|
||||||
|
{ self.checked_rem(another) }
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_checked_div_rem!(u8, u16, u32, u64, i8, i16, i32, i64);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Have fun
|
//! Have fun
|
||||||
|
|
||||||
use hbbytecode::OpsN;
|
use hbbytecode::RoundingMode;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::{
|
super::{
|
||||||
|
@ -11,19 +11,20 @@ use {
|
||||||
value::{Value, ValueVariant},
|
value::{Value, ValueVariant},
|
||||||
Vm, VmRunError, VmRunOk,
|
Vm, VmRunError, VmRunOk,
|
||||||
},
|
},
|
||||||
crate::mem::Address,
|
crate::{mem::Address, value::CheckedDivRem},
|
||||||
core::{cmp::Ordering, ops},
|
core::{cmp::Ordering, ops},
|
||||||
hbbytecode::{
|
hbbytecode::{
|
||||||
BytecodeItem, OpsO, OpsP, OpsRD, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO, OpsRROH,
|
OpsN, OpsO, OpsP, OpsRB, OpsRD, OpsRH, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO,
|
||||||
OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW,
|
OpsRROH, OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, OpsRW,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! handler {
|
macro_rules! handler {
|
||||||
($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{
|
($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{
|
||||||
let $ty($($ident),*) = $self.decode::<$ty>();
|
let $ty($($ident),*) = $self.decode::<$ty>();
|
||||||
#[allow(clippy::no_effect)] $expr;
|
#[allow(clippy::no_effect)] let e = $expr;
|
||||||
$self.bump_pc::<$ty>();
|
$self.bump_pc::<$ty>();
|
||||||
|
e
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +59,7 @@ where
|
||||||
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
|
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
|
||||||
// sorry 8 bit fans, HBVM won't run on your Speccy :(
|
// sorry 8 bit fans, HBVM won't run on your Speccy :(
|
||||||
unsafe {
|
unsafe {
|
||||||
match self
|
match self.memory.prog_read::<u8>(self.pc as _) {
|
||||||
.memory
|
|
||||||
.prog_read::<u8>(self.pc as _)
|
|
||||||
.ok_or(VmRunError::ProgramFetchLoadEx(self.pc as _))?
|
|
||||||
{
|
|
||||||
UN => {
|
UN => {
|
||||||
self.bump_pc::<OpsN>();
|
self.bump_pc::<OpsN>();
|
||||||
return Err(VmRunError::Unreachable);
|
return Err(VmRunError::Unreachable);
|
||||||
|
@ -72,15 +69,32 @@ where
|
||||||
return Ok(VmRunOk::End);
|
return Ok(VmRunOk::End);
|
||||||
}
|
}
|
||||||
NOP => handler!(self, |OpsN()| ()),
|
NOP => handler!(self, |OpsN()| ()),
|
||||||
ADD => self.binary_op(u64::wrapping_add),
|
ADD8 => self.binary_op(u8::wrapping_add),
|
||||||
SUB => self.binary_op(u64::wrapping_sub),
|
ADD16 => self.binary_op(u16::wrapping_add),
|
||||||
MUL => self.binary_op(u64::wrapping_mul),
|
ADD32 => self.binary_op(u32::wrapping_add),
|
||||||
|
ADD64 => self.binary_op(u64::wrapping_add),
|
||||||
|
SUB8 => self.binary_op(u8::wrapping_sub),
|
||||||
|
SUB16 => self.binary_op(u16::wrapping_sub),
|
||||||
|
SUB32 => self.binary_op(u32::wrapping_sub),
|
||||||
|
SUB64 => self.binary_op(u64::wrapping_sub),
|
||||||
|
MUL8 => self.binary_op(u8::wrapping_mul),
|
||||||
|
MUL16 => self.binary_op(u16::wrapping_mul),
|
||||||
|
MUL32 => self.binary_op(u32::wrapping_mul),
|
||||||
|
MUL64 => self.binary_op(u64::wrapping_mul),
|
||||||
AND => self.binary_op::<u64>(ops::BitAnd::bitand),
|
AND => self.binary_op::<u64>(ops::BitAnd::bitand),
|
||||||
OR => self.binary_op::<u64>(ops::BitOr::bitor),
|
OR => self.binary_op::<u64>(ops::BitOr::bitor),
|
||||||
XOR => self.binary_op::<u64>(ops::BitXor::bitxor),
|
XOR => self.binary_op::<u64>(ops::BitXor::bitxor),
|
||||||
SL => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)),
|
SLU8 => self.binary_op(|l, r| u8::wrapping_shl(l, r as u32)),
|
||||||
SR => self.binary_op(|l, r| u64::wrapping_shr(l, r as u32)),
|
SLU16 => self.binary_op(|l, r| u16::wrapping_shl(l, r as u32)),
|
||||||
SRS => self.binary_op(|l: u64, r| i64::wrapping_shl(l as i64, r as u32) as u64),
|
SLU32 => self.binary_op(u32::wrapping_shl),
|
||||||
|
SLU64 => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)),
|
||||||
|
SRU8 => self.binary_op(|l, r| u8::wrapping_shr(l, r as u32)),
|
||||||
|
SRU16 => self.binary_op(|l, r| u16::wrapping_shr(l, r as u32)),
|
||||||
|
SRU32 => self.binary_op(u32::wrapping_shr),
|
||||||
|
SRS8 => self.binary_op(|l: i8, r| i8::wrapping_shl(l, r as u32)),
|
||||||
|
SRS16 => self.binary_op(|l: i16, r| i16::wrapping_shl(l, r as u32)),
|
||||||
|
SRS32 => self.binary_op(|l: i32, r| i32::wrapping_shl(l, r as u32)),
|
||||||
|
SRS64 => self.binary_op(|l: i64, r| i64::wrapping_shl(l, r as u32)),
|
||||||
CMP => handler!(self, |OpsRRR(tg, a0, a1)| {
|
CMP => handler!(self, |OpsRRR(tg, a0, a1)| {
|
||||||
// Compare a0 <=> a1
|
// Compare a0 <=> a1
|
||||||
// < → 0
|
// < → 0
|
||||||
|
@ -107,6 +121,14 @@ where
|
||||||
+ 1,
|
+ 1,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
DIRU8 => self.dir::<u8>(),
|
||||||
|
DIRU16 => self.dir::<u16>(),
|
||||||
|
DIRU32 => self.dir::<u32>(),
|
||||||
|
DIRU64 => self.dir::<u64>(),
|
||||||
|
DIRS8 => self.dir::<i8>(),
|
||||||
|
DIRS16 => self.dir::<i16>(),
|
||||||
|
DIRS32 => self.dir::<i32>(),
|
||||||
|
DIRS64 => self.dir::<i64>(),
|
||||||
NEG => handler!(self, |OpsRR(tg, a0)| {
|
NEG => handler!(self, |OpsRR(tg, a0)| {
|
||||||
// Bit negation
|
// Bit negation
|
||||||
self.write_reg(tg, !self.read_reg(a0).cast::<u64>())
|
self.write_reg(tg, !self.read_reg(a0).cast::<u64>())
|
||||||
|
@ -115,21 +137,38 @@ where
|
||||||
// Logical negation
|
// Logical negation
|
||||||
self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0));
|
self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0));
|
||||||
}),
|
}),
|
||||||
DIR => handler!(self, |OpsRRRR(dt, rt, a0, a1)| {
|
SXT8 => handler!(self, |OpsRR(tg, a0)| {
|
||||||
// Fused Division-Remainder
|
self.write_reg(tg, self.read_reg(a0).cast::<i8>() as i64)
|
||||||
let a0 = self.read_reg(a0).cast::<u64>();
|
|
||||||
let a1 = self.read_reg(a1).cast::<u64>();
|
|
||||||
self.write_reg(dt, a0.checked_div(a1).unwrap_or(u64::MAX));
|
|
||||||
self.write_reg(rt, a0.checked_rem(a1).unwrap_or(u64::MAX));
|
|
||||||
}),
|
}),
|
||||||
ADDI => self.binary_op_imm(u64::wrapping_add),
|
SXT16 => handler!(self, |OpsRR(tg, a0)| {
|
||||||
MULI => self.binary_op_imm(u64::wrapping_sub),
|
self.write_reg(tg, self.read_reg(a0).cast::<i16>() as i64)
|
||||||
|
}),
|
||||||
|
SXT32 => handler!(self, |OpsRR(tg, a0)| {
|
||||||
|
self.write_reg(tg, self.read_reg(a0).cast::<i32>() as i64)
|
||||||
|
}),
|
||||||
|
ADDI8 => self.binary_op_imm(u8::wrapping_add),
|
||||||
|
ADDI16 => self.binary_op_imm(u16::wrapping_add),
|
||||||
|
ADDI32 => self.binary_op_imm(u32::wrapping_add),
|
||||||
|
ADDI64 => self.binary_op_imm(u64::wrapping_add),
|
||||||
|
MULI8 => self.binary_op_imm(u8::wrapping_sub),
|
||||||
|
MULI16 => self.binary_op_imm(u16::wrapping_sub),
|
||||||
|
MULI32 => self.binary_op_imm(u32::wrapping_sub),
|
||||||
|
MULI64 => self.binary_op_imm(u64::wrapping_sub),
|
||||||
ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand),
|
ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand),
|
||||||
ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor),
|
ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor),
|
||||||
XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor),
|
XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor),
|
||||||
SLI => self.binary_op_ims(u64::wrapping_shl),
|
SLUI8 => self.binary_op_ims::<u8>(ops::Shl::shl),
|
||||||
SRI => self.binary_op_ims(u64::wrapping_shr),
|
SLUI16 => self.binary_op_ims::<u16>(ops::Shl::shl),
|
||||||
SRSI => self.binary_op_ims(i64::wrapping_shr),
|
SLUI32 => self.binary_op_ims::<u32>(ops::Shl::shl),
|
||||||
|
SLUI64 => self.binary_op_ims::<u64>(ops::Shl::shl),
|
||||||
|
SRUI8 => self.binary_op_ims::<u8>(ops::Shr::shr),
|
||||||
|
SRUI16 => self.binary_op_ims::<u16>(ops::Shr::shr),
|
||||||
|
SRUI32 => self.binary_op_ims::<u32>(ops::Shr::shr),
|
||||||
|
SRUI64 => self.binary_op_ims::<u64>(ops::Shr::shr),
|
||||||
|
SRSI8 => self.binary_op_ims::<i8>(ops::Shr::shr),
|
||||||
|
SRSI16 => self.binary_op_ims::<i16>(ops::Shr::shr),
|
||||||
|
SRSI32 => self.binary_op_ims::<i32>(ops::Shr::shr),
|
||||||
|
SRSI64 => self.binary_op_ims::<i64>(ops::Shr::shr),
|
||||||
CMPI => handler!(self, |OpsRRD(tg, a0, imm)| {
|
CMPI => handler!(self, |OpsRRD(tg, a0, imm)| {
|
||||||
self.write_reg(
|
self.write_reg(
|
||||||
tg,
|
tg,
|
||||||
|
@ -158,9 +197,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
LI => handler!(self, |OpsRD(tg, imm)| {
|
LI8 => handler!(self, |OpsRB(tg, imm)| self.write_reg(tg, imm)),
|
||||||
self.write_reg(tg, imm);
|
LI16 => handler!(self, |OpsRH(tg, imm)| self.write_reg(tg, imm)),
|
||||||
}),
|
LI32 => handler!(self, |OpsRW(tg, imm)| self.write_reg(tg, imm)),
|
||||||
|
LI64 => handler!(self, |OpsRD(tg, imm)| self.write_reg(tg, imm)),
|
||||||
LRA => handler!(self, |OpsRRO(tg, reg, imm)| {
|
LRA => handler!(self, |OpsRRO(tg, reg, imm)| {
|
||||||
self.write_reg(
|
self.write_reg(
|
||||||
tg,
|
tg,
|
||||||
|
@ -272,34 +312,65 @@ where
|
||||||
self.bump_pc::<OpsN>();
|
self.bump_pc::<OpsN>();
|
||||||
return Ok(VmRunOk::Breakpoint);
|
return Ok(VmRunOk::Breakpoint);
|
||||||
}
|
}
|
||||||
ADDF => self.binary_op::<f64>(ops::Add::add),
|
FADD32 => self.binary_op::<f32>(ops::Add::add),
|
||||||
SUBF => self.binary_op::<f64>(ops::Sub::sub),
|
FADD64 => self.binary_op::<f64>(ops::Add::add),
|
||||||
MULF => self.binary_op::<f64>(ops::Mul::mul),
|
FSUB32 => self.binary_op::<f32>(ops::Sub::sub),
|
||||||
DIRF => handler!(self, |OpsRRRR(dt, rt, a0, a1)| {
|
FSUB64 => self.binary_op::<f64>(ops::Sub::sub),
|
||||||
let a0 = self.read_reg(a0).cast::<f64>();
|
FMUL32 => self.binary_op::<f32>(ops::Mul::mul),
|
||||||
let a1 = self.read_reg(a1).cast::<f64>();
|
FMUL64 => self.binary_op::<f64>(ops::Mul::mul),
|
||||||
self.write_reg(dt, a0 / a1);
|
FDIV32 => self.binary_op::<f32>(ops::Div::div),
|
||||||
self.write_reg(rt, a0 % a1);
|
FDIV64 => self.binary_op::<f64>(ops::Div::div),
|
||||||
|
FMA32 => self.fma::<f32>(),
|
||||||
|
FMA64 => self.fma::<f64>(),
|
||||||
|
FINV32 => handler!(self, |OpsRR(tg, reg)| {
|
||||||
|
self.write_reg(tg, 1. / self.read_reg(reg).cast::<f32>())
|
||||||
}),
|
}),
|
||||||
FMAF => handler!(self, |OpsRRRR(dt, a0, a1, a2)| {
|
FINV64 => handler!(self, |OpsRR(tg, reg)| {
|
||||||
|
self.write_reg(tg, 1. / self.read_reg(reg).cast::<f64>())
|
||||||
|
}),
|
||||||
|
FCMPLT32 => self.fcmp::<f32>(Ordering::Less),
|
||||||
|
FCMPLT64 => self.fcmp::<f64>(Ordering::Less),
|
||||||
|
FCMPGT32 => self.fcmp::<f32>(Ordering::Greater),
|
||||||
|
FCMPGT64 => self.fcmp::<f64>(Ordering::Greater),
|
||||||
|
ITF32 => handler!(self, |OpsRR(tg, reg)| {
|
||||||
|
self.write_reg(tg, self.read_reg(reg).cast::<i64>() as f32);
|
||||||
|
}),
|
||||||
|
ITF64 => handler!(self, |OpsRR(tg, reg)| {
|
||||||
|
self.write_reg(tg, self.read_reg(reg).cast::<i64>() as f64);
|
||||||
|
}),
|
||||||
|
FTI32 => handler!(self, |OpsRRB(tg, reg, mode)| {
|
||||||
self.write_reg(
|
self.write_reg(
|
||||||
dt,
|
tg,
|
||||||
self.read_reg(a0).cast::<f64>() * self.read_reg(a1).cast::<f64>()
|
crate::float::f32toint(
|
||||||
+ self.read_reg(a2).cast::<f64>(),
|
self.read_reg(reg).cast::<f32>(),
|
||||||
|
RoundingMode::try_from(mode)
|
||||||
|
.map_err(|()| VmRunError::InvalidOperand)?,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
NEGF => handler!(self, |OpsRR(dt, a0)| {
|
FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| {
|
||||||
self.write_reg(dt, -self.read_reg(a0).cast::<f64>());
|
self.write_reg(
|
||||||
|
tg,
|
||||||
|
crate::float::f64toint(
|
||||||
|
self.read_reg(reg).cast::<f64>(),
|
||||||
|
RoundingMode::try_from(mode)
|
||||||
|
.map_err(|()| VmRunError::InvalidOperand)?,
|
||||||
|
),
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
ITF => handler!(self, |OpsRR(dt, a0)| {
|
FC32T64 => handler!(self, |OpsRR(tg, reg)| {
|
||||||
self.write_reg(dt, self.read_reg(a0).cast::<i64>() as f64);
|
self.write_reg(tg, self.read_reg(reg).cast::<f32>() as f64);
|
||||||
|
}),
|
||||||
|
FC64T32 => handler!(self, |OpsRRB(tg, reg, mode)| {
|
||||||
|
self.write_reg(
|
||||||
|
tg,
|
||||||
|
crate::float::conv64to32(
|
||||||
|
self.read_reg(reg).cast(),
|
||||||
|
RoundingMode::try_from(mode)
|
||||||
|
.map_err(|()| VmRunError::InvalidOperand)?,
|
||||||
|
),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
FTI => {
|
|
||||||
let OpsRR(dt, a0) = self.decode();
|
|
||||||
self.write_reg(dt, self.read_reg(a0).cast::<f64>() as i64);
|
|
||||||
}
|
|
||||||
ADDFI => self.binary_op_imm::<f64>(ops::Add::add),
|
|
||||||
MULFI => self.binary_op_imm::<f64>(ops::Mul::mul),
|
|
||||||
LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| {
|
LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| {
|
||||||
self.write_reg(
|
self.write_reg(
|
||||||
tg,
|
tg,
|
||||||
|
@ -312,7 +383,7 @@ where
|
||||||
STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| {
|
STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| {
|
||||||
self.store(dst, base, u64::from(off).wrapping_add(self.pc.get()), count)?;
|
self.store(dst, base, u64::from(off).wrapping_add(self.pc.get()), count)?;
|
||||||
}),
|
}),
|
||||||
JMPR16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)),
|
JMP16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)),
|
||||||
op => return Err(VmRunError::InvalidOpcode(op)),
|
op => return Err(VmRunError::InvalidOpcode(op)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,14 +399,14 @@ where
|
||||||
|
|
||||||
/// Bump instruction pointer
|
/// Bump instruction pointer
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn bump_pc<T: BytecodeItem>(&mut self) {
|
fn bump_pc<T: Copy>(&mut self) {
|
||||||
self.pc = self.pc.wrapping_add(core::mem::size_of::<T>() + 1);
|
self.pc = self.pc.wrapping_add(core::mem::size_of::<T>() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode instruction operands
|
/// Decode instruction operands
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn decode<T: BytecodeItem>(&mut self) -> T {
|
unsafe fn decode<T: Copy>(&mut self) -> T {
|
||||||
self.memory.prog_read_unchecked::<T>(self.pc + 1_u64)
|
self.memory.prog_read::<T>(self.pc + 1_u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load
|
/// Load
|
||||||
|
@ -395,12 +466,11 @@ where
|
||||||
/// Perform binary operation over register and immediate
|
/// Perform binary operation over register and immediate
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
|
unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
|
||||||
let OpsRRD(tg, reg, imm) = self.decode();
|
let OpsRR(tg, reg) = self.decode();
|
||||||
self.write_reg(
|
let imm: T = self.decode();
|
||||||
tg,
|
self.write_reg(tg, op(self.read_reg(reg).cast::<T>(), imm));
|
||||||
op(self.read_reg(reg).cast::<T>(), Value::from(imm).cast::<T>()),
|
|
||||||
);
|
|
||||||
self.bump_pc::<OpsRRD>();
|
self.bump_pc::<OpsRRD>();
|
||||||
|
self.bump_pc::<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform binary operation over register and shift immediate
|
/// Perform binary operation over register and shift immediate
|
||||||
|
@ -411,6 +481,51 @@ where
|
||||||
self.bump_pc::<OpsRRW>();
|
self.bump_pc::<OpsRRW>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fused division-remainder
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn dir<T: ValueVariant + CheckedDivRem>(&mut self) {
|
||||||
|
handler!(self, |OpsRRRR(td, tr, a0, a1)| {
|
||||||
|
let a0 = self.read_reg(a0).cast::<T>();
|
||||||
|
let a1 = self.read_reg(a1).cast::<T>();
|
||||||
|
|
||||||
|
if let Some(div) = a0.checked_div(a1) {
|
||||||
|
self.write_reg(td, div);
|
||||||
|
} else {
|
||||||
|
self.write_reg(td, -1_i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rem) = a0.checked_rem(a1) {
|
||||||
|
self.write_reg(tr, rem);
|
||||||
|
} else {
|
||||||
|
self.write_reg(tr, a0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fused multiply-add
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn fma<T>(&mut self)
|
||||||
|
where
|
||||||
|
T: ValueVariant + core::ops::Mul<Output = T> + core::ops::Add<Output = T>,
|
||||||
|
{
|
||||||
|
handler!(self, |OpsRRRR(tg, a0, a1, a2)| {
|
||||||
|
let a0 = self.read_reg(a0).cast::<T>();
|
||||||
|
let a1 = self.read_reg(a1).cast::<T>();
|
||||||
|
let a2 = self.read_reg(a2).cast::<T>();
|
||||||
|
self.write_reg(tg, a0 * a1 + a2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Float comparsion
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn fcmp<T: PartialOrd + ValueVariant>(&mut self, nan: Ordering) {
|
||||||
|
handler!(self, |OpsRRR(tg, a0, a1)| {
|
||||||
|
let a0 = self.read_reg(a0).cast::<T>();
|
||||||
|
let a1 = self.read_reg(a1).cast::<T>();
|
||||||
|
self.write_reg(tg, (a0.partial_cmp(&a1).unwrap_or(nan) as i8 + 1) as u8)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected
|
/// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) {
|
unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hbxrt"
|
name = "hbxrt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
default-run = "hbxrt"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hbvm.path = "../hbvm"
|
hbvm.path = "../hbvm"
|
||||||
|
nix = { version = "0.27", features = ["mman"] }
|
||||||
|
|
|
@ -1,11 +1,69 @@
|
||||||
use std::io::{stdin, Read};
|
//! Holey Bytes Experimental Runtime
|
||||||
|
|
||||||
/// Holey Bytes Experimental Runtime
|
mod mem;
|
||||||
|
|
||||||
|
use {
|
||||||
|
hbvm::{mem::Address, Vm, VmRunOk},
|
||||||
|
nix::sys::mman::{mmap, MapFlags, ProtFlags},
|
||||||
|
std::{env::args, fs::File, num::NonZeroUsize, process::exit},
|
||||||
|
};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut prog = vec![];
|
eprintln!("== HB×RT (Holey Bytes Linux Runtime) v0.1 ==");
|
||||||
stdin().read_to_end(&mut prog)?;
|
eprintln!("[W] Currently supporting only flat images");
|
||||||
|
|
||||||
|
let Some(image_path) = args().nth(1) else {
|
||||||
|
eprintln!("[E] Missing image path");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load program
|
||||||
|
eprintln!("[I] Loading image from \"{image_path}\"");
|
||||||
|
let file = File::open(image_path)?;
|
||||||
|
let ptr = unsafe {
|
||||||
|
mmap(
|
||||||
|
None,
|
||||||
|
NonZeroUsize::new(file.metadata()?.len() as usize).ok_or("File is empty")?,
|
||||||
|
ProtFlags::PROT_READ,
|
||||||
|
MapFlags::MAP_PRIVATE,
|
||||||
|
Some(&file),
|
||||||
|
0,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("[I] Image loaded at {ptr:p}");
|
||||||
|
|
||||||
|
// Execute program
|
||||||
|
let mut vm = unsafe { Vm::<_, 0>::new(mem::HostMemory, Address::new(ptr as u64)) };
|
||||||
|
let stat = loop {
|
||||||
|
match vm.run() {
|
||||||
|
Ok(VmRunOk::Breakpoint) => eprintln!(
|
||||||
|
"[I] Hit breakpoint\nIP: {}\n== Registers ==\n{:?}",
|
||||||
|
vm.pc, vm.registers
|
||||||
|
),
|
||||||
|
Ok(VmRunOk::Timer) => (),
|
||||||
|
Ok(VmRunOk::Ecall) => unsafe {
|
||||||
|
std::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
inlateout("rax") vm.registers[1].0,
|
||||||
|
in("rdi") vm.registers[2].0,
|
||||||
|
in("rsi") vm.registers[3].0,
|
||||||
|
in("rdx") vm.registers[4].0,
|
||||||
|
in("r10") vm.registers[5].0,
|
||||||
|
in("r8") vm.registers[6].0,
|
||||||
|
in("r9") vm.registers[7].0,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Ok(VmRunOk::End) => break Ok(()),
|
||||||
|
Err(e) => break Err(e),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("\n== Registers ==\n{:?}", vm.registers);
|
||||||
|
if let Err(e) = stat {
|
||||||
|
eprintln!("\n[E] Runtime error: {e:?}");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
eprintln!("WARNING! Bytecode valider has not been yet implemented and running program can lead to undefiend behaviour.");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
31
hbxrt/src/mem.rs
Normal file
31
hbxrt/src/mem.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use hbvm::mem::{Address, LoadError, Memory, StoreError};
|
||||||
|
|
||||||
|
pub struct HostMemory;
|
||||||
|
impl Memory for HostMemory {
|
||||||
|
#[inline]
|
||||||
|
unsafe fn load(
|
||||||
|
&mut self,
|
||||||
|
addr: Address,
|
||||||
|
target: *mut u8,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), LoadError> {
|
||||||
|
unsafe { core::ptr::copy(addr.get() as *const u8, target, count) }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn store(
|
||||||
|
&mut self,
|
||||||
|
addr: Address,
|
||||||
|
source: *const u8,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), StoreError> {
|
||||||
|
unsafe { core::ptr::copy(source, addr.get() as *mut u8, count) }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
|
||||||
|
core::ptr::read(addr.get() as *const T)
|
||||||
|
}
|
||||||
|
}
|
9
xtask/Cargo.toml
Normal file
9
xtask/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "xtask"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
argh = "0.1"
|
||||||
|
color-eyre = "0.6"
|
||||||
|
once_cell = "1.18"
|
93
xtask/src/fmt.rs
Normal file
93
xtask/src/fmt.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use {
|
||||||
|
crate::{utils::IterExt, ROOT},
|
||||||
|
argh::FromArgs,
|
||||||
|
color_eyre::{eyre::eyre, Result},
|
||||||
|
std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader, BufWriter, Seek, Write},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Format `instructions.in`
|
||||||
|
#[derive(Debug, FromArgs, PartialEq)]
|
||||||
|
#[argh(subcommand, name = "fmt")]
|
||||||
|
pub struct Command {
|
||||||
|
/// renumber instructions in their definition order
|
||||||
|
#[argh(switch, short = 'r')]
|
||||||
|
renumber: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn command(args: Command) -> Result<()> {
|
||||||
|
let mut file = File::options()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(ROOT.join("hbbytecode/instructions.in"))?;
|
||||||
|
|
||||||
|
// Extract records
|
||||||
|
let reader = BufReader::new(&file);
|
||||||
|
let mut recs = vec![];
|
||||||
|
let mut lens = [0_usize; 4];
|
||||||
|
|
||||||
|
for rec in reader.split(b';').filter_map(|r| {
|
||||||
|
r.map(|ln| {
|
||||||
|
let s = String::from_utf8_lossy(&ln);
|
||||||
|
let s = s.trim_matches('\n');
|
||||||
|
if s.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.split(',')
|
||||||
|
.map(|s| Box::<str>::from(s.trim()))
|
||||||
|
.collect_array::<4>()
|
||||||
|
.map(Ok::<_, ()>)
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}) {
|
||||||
|
let rec = rec?.map_err(|_| eyre!("Invalid record format"))?;
|
||||||
|
for (current, next) in lens.iter_mut().zip(rec.iter()) {
|
||||||
|
*current = (*current).max(next.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
recs.push(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear file!
|
||||||
|
file.set_len(0)?;
|
||||||
|
file.seek(std::io::SeekFrom::Start(0))?;
|
||||||
|
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
|
||||||
|
let ord_opco_len = digit_count(recs.len()) as usize;
|
||||||
|
for (n, rec) in recs.iter().enumerate() {
|
||||||
|
// Write opcode number
|
||||||
|
if args.renumber {
|
||||||
|
let n = format!("{n:#04X}");
|
||||||
|
write!(writer, "{n}, {}", padding(ord_opco_len, &n))?;
|
||||||
|
} else {
|
||||||
|
write!(writer, "{}, {}", rec[0], padding(lens[0], &rec[0]))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write other fields
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
"{}, {}{},{} {}{};",
|
||||||
|
rec[1],
|
||||||
|
padding(lens[1], &rec[1]),
|
||||||
|
rec[2],
|
||||||
|
padding(lens[2], &rec[2]),
|
||||||
|
rec[3],
|
||||||
|
padding(lens[3], &rec[3]),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn padding(req: usize, s: &str) -> Box<str> {
|
||||||
|
" ".repeat(req.saturating_sub(s.len())).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn digit_count(n: usize) -> u32 {
|
||||||
|
n.checked_ilog10().unwrap_or(0) + 1
|
||||||
|
}
|
25
xtask/src/main.rs
Normal file
25
xtask/src/main.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
mod fmt;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use {argh::FromArgs, color_eyre::Result, once_cell::sync::Lazy, std::path::Path};
|
||||||
|
|
||||||
|
static ROOT: Lazy<&Path> = Lazy::new(|| Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap());
|
||||||
|
|
||||||
|
/// xTask for Holey Bytes project
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
struct Command {
|
||||||
|
#[argh(subcommand)]
|
||||||
|
subcom: Subcommands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
#[argh(subcommand)]
|
||||||
|
enum Subcommands {
|
||||||
|
Format(fmt::Command),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
match argh::from_env::<Command>().subcom {
|
||||||
|
Subcommands::Format(com) => fmt::command(com),
|
||||||
|
}
|
||||||
|
}
|
19
xtask/src/utils.rs
Normal file
19
xtask/src/utils.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub trait IterExt: Iterator {
|
||||||
|
fn collect_array<const N: usize>(&mut self) -> Option<[Self::Item; N]>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let mut array: [MaybeUninit<Self::Item>; N] =
|
||||||
|
unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
|
for item in &mut array {
|
||||||
|
item.write(self.next()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(array.map(|item| unsafe { item.assume_init() }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Iterator> IterExt for T {}
|
Loading…
Reference in a new issue