forked from AbleOS/holey-bytes
New float instructions, Linux runtime added, some other stuff I forgor
This commit is contained in:
parent
a23148b04d
commit
2123e860b6
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.
|
||||
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]]
|
||||
name = "hbbytecode"
|
||||
version = "0.1.0"
|
||||
|
@ -21,13 +146,85 @@ name = "hbxrt"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hbvm",
|
||||
"nix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.67"
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
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 = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -41,6 +238,41 @@ dependencies = [
|
|||
"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]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -52,12 +284,80 @@ dependencies = [
|
|||
"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]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "with_builtin_macros"
|
||||
version = "0.0.3"
|
||||
|
@ -75,5 +375,14 @@ checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"color-eyre",
|
||||
"once_cell",
|
||||
]
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[workspace]
|
||||
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;
|
||||
|
||||
0, UN, N, "Cause an unreachable code trap" ;
|
||||
1, TX, N, "Termiante execution" ;
|
||||
2, NOP, N, "Do nothing" ;
|
||||
|
||||
3, ADD, RRR, "Addition" ;
|
||||
4, SUB, RRR, "Subtraction" ;
|
||||
5, MUL, RRR, "Multiplication" ;
|
||||
6, AND, RRR, "Bitand" ;
|
||||
7, OR, RRR, "Bitor" ;
|
||||
8, XOR, RRR, "Bitxor" ;
|
||||
9, SL, RRR, "Unsigned left bitshift" ;
|
||||
10, SR, RRR, "Unsigned right bitshift" ;
|
||||
11, SRS, RRR, "Signed right bitshift" ;
|
||||
12, CMP, RRR, "Signed comparsion" ;
|
||||
13, CMPU, RRR, "Unsigned comparsion" ;
|
||||
14, DIR, RRRR, "Merged divide-remainder" ;
|
||||
15, NEG, RR, "Bit negation" ;
|
||||
16, NOT, RR, "Logical negation" ;
|
||||
17, ADDI, RRD, "Addition with immediate" ;
|
||||
18, MULI, RRD, "Multiplication with immediate" ;
|
||||
19, ANDI, RRD, "Bitand with immediate" ;
|
||||
20, ORI, RRD, "Bitor with immediate" ;
|
||||
21, XORI, RRD, "Bitxor with immediate" ;
|
||||
22, SLI, RRW, "Unsigned left bitshift with immedidate";
|
||||
23, SRI, RRW, "Unsigned right bitshift with immediate";
|
||||
24, SRSI, RRW, "Signed right bitshift with immediate" ;
|
||||
25, CMPI, RRD, "Signed compare with immediate" ;
|
||||
26, CMPUI, RRD, "Unsigned compare with immediate" ;
|
||||
|
||||
27, CP, RR, "Copy register" ;
|
||||
28, SWA, RR, "Swap registers" ;
|
||||
29, LI, RD, "Load immediate" ;
|
||||
30, LRA, RRO, "Load relative address" ;
|
||||
31, LD, RRAH, "Load from absolute address" ;
|
||||
32, ST, RRAH, "Store to absolute address" ;
|
||||
33, LDR, RROH, "Load from relative address" ;
|
||||
34, STR, RROH, "Store to relative address" ;
|
||||
35, BMC, RRH, "Copy block of memory" ;
|
||||
36, BRC, RRB, "Copy register block" ;
|
||||
|
||||
37, JMP, O, "Relative jump" ;
|
||||
38, JAL, RRA, "Linking absolute jump" ;
|
||||
39, JALR, RRO, "Linking relative jump" ;
|
||||
40, JEQ, RRP, "Branch on equal" ;
|
||||
41, JNE, RRP, "Branch on nonequal" ;
|
||||
42, JLT, RRP, "Branch on lesser-than (signed)" ;
|
||||
43, JGT, RRP, "Branch on greater-than (signed)" ;
|
||||
44, JLTU, RRP, "Branch on lesser-than (unsigned)" ;
|
||||
45, JGTU, RRP, "Branch on greater-than (unsigned)" ;
|
||||
46, ECA, N, "Environment call trap" ;
|
||||
47, EBP, N, "Environment breakpoint" ;
|
||||
|
||||
48, ADDF, RRR, "Floating addition" ;
|
||||
49, SUBF, RRR, "Floating subtraction" ;
|
||||
50, MULF, RRR, "Floating multiply" ;
|
||||
51, DIRF, RRRR, "Merged floating divide-remainder" ;
|
||||
52, FMAF, RRRR, "Fused floating multiply-add" ;
|
||||
53, NEGF, RR, "Floating sign negation" ;
|
||||
54, ITF, RR, "Int to float" ;
|
||||
55, FTI, RR, "Float to int" ;
|
||||
|
||||
56, ADDFI, RRD, "Floating addition with immediate" ;
|
||||
57, MULFI, RRD, "Floating multiplication with immediate";
|
||||
|
||||
58, LRA16 , RRP, "Load relative immediate (16 bit)" ;
|
||||
59, LDR16 , RRPH, "Load from relative address (16 bit)" ;
|
||||
60, STR16 , RRPH, "Store to relative address (16 bit)" ;
|
||||
61, JMPR16, P, "Relative jump (16 bit)" ;
|
||||
0x00, UN, N, "Cause an unreachable code trap" ;
|
||||
0x01, TX, N, "Termiante execution" ;
|
||||
0x02, NOP, N, "Do nothing" ;
|
||||
0x03, ADD8, RRR, "Addition (8b)" ;
|
||||
0x04, ADD16, RRR, "Addition (16b)" ;
|
||||
0x05, ADD32, RRR, "Addition (32b)" ;
|
||||
0x06, ADD64, RRR, "Addition (64b)" ;
|
||||
0x07, SUB8, RRR, "Subtraction (8b)" ;
|
||||
0x08, SUB16, RRR, "Subtraction (16b)" ;
|
||||
0x09, SUB32, RRR, "Subtraction (32b)" ;
|
||||
0x0A, SUB64, RRR, "Subtraction (64b)" ;
|
||||
0x0B, MUL8, RRR, "Multiplication (8b)" ;
|
||||
0x0C, MUL16, RRR, "Multiplication (16b)" ;
|
||||
0x0D, MUL32, RRR, "Multiplication (32b)" ;
|
||||
0x0E, MUL64, RRR, "Multiplication (64b)" ;
|
||||
0x0F, AND, RRR, "Bitand" ;
|
||||
0x10, OR, RRR, "Bitor" ;
|
||||
0x11, XOR, RRR, "Bitxor" ;
|
||||
0x12, SLU8, RRR, "Unsigned left bitshift (8b)" ;
|
||||
0x13, SLU16, RRR, "Unsigned left bitshift (16b)" ;
|
||||
0x14, SLU32, RRR, "Unsigned left bitshift (32b)" ;
|
||||
0x15, SLU64, RRR, "Unsigned left bitshift (64b)" ;
|
||||
0x16, SRU8, RRR, "Unsigned right bitshift (8b)" ;
|
||||
0x17, SRU16, RRR, "Unsigned right bitshift (16b)" ;
|
||||
0x18, SRU32, RRR, "Unsigned right bitshift (32b)" ;
|
||||
0x19, SRU64, RRR, "Unsigned right bitshift (64b)" ;
|
||||
0x1A, SRS8, RRR, "Signed right bitshift (8b)" ;
|
||||
0x1B, SRS16, RRR, "Signed right bitshift (16b)" ;
|
||||
0x1C, SRS32, RRR, "Signed right bitshift (32b)" ;
|
||||
0x1D, SRS64, RRR, "Signed right bitshift (64b)" ;
|
||||
0x1E, CMP, RRR, "Signed comparsion" ;
|
||||
0x1F, CMPU, RRR, "Unsigned comparsion" ;
|
||||
0x20, DIRU8, RRRR, "Merged divide-remainder (unsigned 8b)" ;
|
||||
0x21, DIRU16, RRRR, "Merged divide-remainder (unsigned 16b)" ;
|
||||
0x22, DIRU32, RRRR, "Merged divide-remainder (unsigned 32b)" ;
|
||||
0x23, DIRU64, RRRR, "Merged divide-remainder (unsigned 64b)" ;
|
||||
0x24, DIRS8, RRRR, "Merged divide-remainder (signed 8b)" ;
|
||||
0x25, DIRS16, RRRR, "Merged divide-remainder (signed 16b)" ;
|
||||
0x26, DIRS32, RRRR, "Merged divide-remainder (signed 32b)" ;
|
||||
0x27, DIRS64, RRRR, "Merged divide-remainder (signed 64b)" ;
|
||||
0x28, NEG, RR, "Bit negation" ;
|
||||
0x29, NOT, RR, "Logical negation" ;
|
||||
0x2A, SXT8, RR, "Sign extend 8b to 64b" ;
|
||||
0x2B, SXT16, RR, "Sign extend 16b to 64b" ;
|
||||
0x2C, SXT32, RR, "Sign extend 32b to 64b" ;
|
||||
0x2D, ADDI8, RRB, "Addition with immediate (8b)" ;
|
||||
0x2E, ADDI16, RRH, "Addition with immediate (16b)" ;
|
||||
0x2F, ADDI32, RRW, "Addition with immediate (32b)" ;
|
||||
0x30, ADDI64, RRD, "Addition with immediate (64b)" ;
|
||||
0x31, MULI8, RRW, "Multiplication with immediate (8b)" ;
|
||||
0x32, MULI16, RRH, "Multiplication with immediate (16b)" ;
|
||||
0x33, MULI32, RRW, "Multiplication with immediate (32b)" ;
|
||||
0x34, MULI64, RRD, "Multiplication with immediate (64b)" ;
|
||||
0x35, ANDI, RRD, "Bitand with immediate" ;
|
||||
0x36, ORI, RRD, "Bitor with immediate" ;
|
||||
0x37, XORI, RRD, "Bitxor with immediate" ;
|
||||
0x38, SLUI8, RRW, "Unsigned left bitshift with immedidate (8b)" ;
|
||||
0x39, SLUI16, RRW, "Unsigned left bitshift with immedidate (16b)";
|
||||
0x3A, SLUI32, RRW, "Unsigned left bitshift with immedidate (32b)";
|
||||
0x3B, SLUI64, RRW, "Unsigned left bitshift with immedidate (64b)";
|
||||
0x3C, SRUI8, RRW, "Unsigned right bitshift with immediate (8b)" ;
|
||||
0x3D, SRUI16, RRW, "Unsigned right bitshift with immediate (16b)";
|
||||
0x3E, SRUI32, RRW, "Unsigned right bitshift with immediate (32b)";
|
||||
0x3F, SRUI64, RRW, "Unsigned right bitshift with immediate (64b)";
|
||||
0x40, SRSI8, RRW, "Signed right bitshift with immediate" ;
|
||||
0x41, SRSI16, RRW, "Signed right bitshift with immediate" ;
|
||||
0x42, SRSI32, RRW, "Signed right bitshift with immediate" ;
|
||||
0x43, SRSI64, RRW, "Signed right bitshift with immediate" ;
|
||||
0x44, CMPI, RRD, "Signed compare with immediate" ;
|
||||
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]
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
type OpR = u8;
|
||||
|
||||
type OpA = u64;
|
||||
|
@ -17,6 +19,7 @@ pub unsafe trait BytecodeItem {}
|
|||
macro_rules! define_items {
|
||||
($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => {
|
||||
$(
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(packed)]
|
||||
pub struct $name($(pub $item),*);
|
||||
unsafe impl BytecodeItem for $name {}
|
||||
|
@ -31,6 +34,9 @@ define_items! {
|
|||
OpsRRB (OpR, OpR, OpB ),
|
||||
OpsRRH (OpR, OpR, OpH ),
|
||||
OpsRRW (OpR, OpR, OpW ),
|
||||
OpsRB (OpR, OpB ),
|
||||
OpsRH (OpR, OpH ),
|
||||
OpsRW (OpR, OpW ),
|
||||
OpsRD (OpR, OpD ),
|
||||
OpsRRD (OpR, OpR, OpD ),
|
||||
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);
|
||||
|
|
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]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use mem::{Address, Memory};
|
||||
|
||||
|
@ -23,6 +22,7 @@ pub mod mem;
|
|||
pub mod value;
|
||||
|
||||
mod bmc;
|
||||
mod float;
|
||||
mod utils;
|
||||
mod vmrun;
|
||||
|
||||
|
@ -92,6 +92,12 @@ pub enum VmRunError {
|
|||
|
||||
/// Reached unreachable code
|
||||
Unreachable,
|
||||
|
||||
/// Invalid operand
|
||||
InvalidOperand,
|
||||
|
||||
/// Unimplemented feature
|
||||
Unimplemented,
|
||||
}
|
||||
|
||||
/// Virtual machine halt ok
|
||||
|
|
|
@ -6,7 +6,7 @@ pub(crate) mod addr;
|
|||
|
||||
pub use addr::Address;
|
||||
|
||||
use {crate::utils::impl_display, hbbytecode::BytecodeItem};
|
||||
use crate::utils::impl_display;
|
||||
|
||||
/// Load-store memory access
|
||||
pub trait Memory {
|
||||
|
@ -36,13 +36,7 @@ pub trait Memory {
|
|||
///
|
||||
/// # Safety
|
||||
/// - Data read have to be valid
|
||||
unsafe fn prog_read<T: BytecodeItem>(&mut self, addr: Address) -> Option<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;
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T;
|
||||
}
|
||||
|
||||
/// Unhandled load access trap
|
||||
|
|
|
@ -78,31 +78,19 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
|||
}
|
||||
|
||||
#[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() {
|
||||
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();
|
||||
self.program
|
||||
.get(addr..addr + size_of::<T>())
|
||||
.map(|x| x.as_ptr().cast::<T>().read())
|
||||
}
|
||||
|
||||
#[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()
|
||||
.unwrap_or_else(|| unsafe { core::mem::zeroed() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ pub(crate) mod internal {
|
|||
pub(crate) use impl_display_match_fragment;
|
||||
}
|
||||
|
||||
macro_rules! static_assert_eq(($l:expr, $r:expr $(,)?) => {
|
||||
const _: [(); ($l != $r) as usize] = [];
|
||||
macro_rules! static_assert(($expr:expr $(,)?) => {
|
||||
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
|
||||
|
||||
/// Define [`Value`] union
|
||||
use crate::utils::static_assert;
|
||||
|
||||
/// Define [`Value`] »union« (it's fake)
|
||||
///
|
||||
/// # 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.
|
||||
macro_rules! value_def {
|
||||
($($ty:ident),* $(,)?) => {
|
||||
/// HBVM register value
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
pub union Value {
|
||||
$(
|
||||
#[doc = concat!(stringify!($ty), " type")]
|
||||
pub $ty: $ty
|
||||
),*
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Value(pub u64);
|
||||
|
||||
$(
|
||||
impl From<$ty> for Value {
|
||||
#[inline]
|
||||
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!(
|
||||
core::mem::size_of::<$ty>(),
|
||||
core::mem::size_of::<Value>(),
|
||||
);
|
||||
static_assert!(core::mem::size_of::<$ty>() <= core::mem::size_of::<Value>());
|
||||
|
||||
impl private::Sealed for $ty {}
|
||||
unsafe impl ValueVariant for $ty {}
|
||||
|
@ -41,23 +38,7 @@ impl Value {
|
|||
/// Byte reinterpret value to target variant
|
||||
#[inline]
|
||||
pub fn cast<V: ValueVariant>(self) -> V {
|
||||
/// Evil.
|
||||
///
|
||||
/// 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 }
|
||||
unsafe { core::mem::transmute_copy(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,8 +50,8 @@ mod private {
|
|||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
value_def!(u64, i64, f64);
|
||||
crate::utils::static_assert_eq!(core::mem::size_of::<Value>(), 8);
|
||||
value_def!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
|
||||
static_assert!(core::mem::size_of::<Value>() == 8);
|
||||
|
||||
impl core::fmt::Debug for Value {
|
||||
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>())
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
use hbbytecode::OpsN;
|
||||
use hbbytecode::RoundingMode;
|
||||
|
||||
use {
|
||||
super::{
|
||||
|
@ -11,19 +11,20 @@ use {
|
|||
value::{Value, ValueVariant},
|
||||
Vm, VmRunError, VmRunOk,
|
||||
},
|
||||
crate::mem::Address,
|
||||
crate::{mem::Address, value::CheckedDivRem},
|
||||
core::{cmp::Ordering, ops},
|
||||
hbbytecode::{
|
||||
BytecodeItem, OpsO, OpsP, OpsRD, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO, OpsRROH,
|
||||
OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW,
|
||||
OpsN, OpsO, OpsP, OpsRB, OpsRD, OpsRH, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO,
|
||||
OpsRROH, OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, OpsRW,
|
||||
},
|
||||
};
|
||||
|
||||
macro_rules! handler {
|
||||
($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{
|
||||
let $ty($($ident),*) = $self.decode::<$ty>();
|
||||
#[allow(clippy::no_effect)] $expr;
|
||||
#[allow(clippy::no_effect)] let e = $expr;
|
||||
$self.bump_pc::<$ty>();
|
||||
e
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -58,11 +59,7 @@ where
|
|||
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
|
||||
// sorry 8 bit fans, HBVM won't run on your Speccy :(
|
||||
unsafe {
|
||||
match self
|
||||
.memory
|
||||
.prog_read::<u8>(self.pc as _)
|
||||
.ok_or(VmRunError::ProgramFetchLoadEx(self.pc as _))?
|
||||
{
|
||||
match self.memory.prog_read::<u8>(self.pc as _) {
|
||||
UN => {
|
||||
self.bump_pc::<OpsN>();
|
||||
return Err(VmRunError::Unreachable);
|
||||
|
@ -72,15 +69,32 @@ where
|
|||
return Ok(VmRunOk::End);
|
||||
}
|
||||
NOP => handler!(self, |OpsN()| ()),
|
||||
ADD => self.binary_op(u64::wrapping_add),
|
||||
SUB => self.binary_op(u64::wrapping_sub),
|
||||
MUL => self.binary_op(u64::wrapping_mul),
|
||||
ADD8 => self.binary_op(u8::wrapping_add),
|
||||
ADD16 => self.binary_op(u16::wrapping_add),
|
||||
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),
|
||||
OR => self.binary_op::<u64>(ops::BitOr::bitor),
|
||||
XOR => self.binary_op::<u64>(ops::BitXor::bitxor),
|
||||
SL => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)),
|
||||
SR => self.binary_op(|l, r| u64::wrapping_shr(l, r as u32)),
|
||||
SRS => self.binary_op(|l: u64, r| i64::wrapping_shl(l as i64, r as u32) as u64),
|
||||
SLU8 => self.binary_op(|l, r| u8::wrapping_shl(l, r as u32)),
|
||||
SLU16 => self.binary_op(|l, r| u16::wrapping_shl(l, r as u32)),
|
||||
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)| {
|
||||
// Compare a0 <=> a1
|
||||
// < → 0
|
||||
|
@ -107,6 +121,14 @@ where
|
|||
+ 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)| {
|
||||
// Bit negation
|
||||
self.write_reg(tg, !self.read_reg(a0).cast::<u64>())
|
||||
|
@ -115,21 +137,38 @@ where
|
|||
// Logical negation
|
||||
self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0));
|
||||
}),
|
||||
DIR => handler!(self, |OpsRRRR(dt, rt, a0, a1)| {
|
||||
// Fused Division-Remainder
|
||||
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));
|
||||
SXT8 => handler!(self, |OpsRR(tg, a0)| {
|
||||
self.write_reg(tg, self.read_reg(a0).cast::<i8>() as i64)
|
||||
}),
|
||||
ADDI => self.binary_op_imm(u64::wrapping_add),
|
||||
MULI => self.binary_op_imm(u64::wrapping_sub),
|
||||
SXT16 => handler!(self, |OpsRR(tg, a0)| {
|
||||
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),
|
||||
ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor),
|
||||
XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor),
|
||||
SLI => self.binary_op_ims(u64::wrapping_shl),
|
||||
SRI => self.binary_op_ims(u64::wrapping_shr),
|
||||
SRSI => self.binary_op_ims(i64::wrapping_shr),
|
||||
SLUI8 => self.binary_op_ims::<u8>(ops::Shl::shl),
|
||||
SLUI16 => self.binary_op_ims::<u16>(ops::Shl::shl),
|
||||
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)| {
|
||||
self.write_reg(
|
||||
tg,
|
||||
|
@ -158,9 +197,10 @@ where
|
|||
}
|
||||
}
|
||||
}),
|
||||
LI => handler!(self, |OpsRD(tg, imm)| {
|
||||
self.write_reg(tg, imm);
|
||||
}),
|
||||
LI8 => handler!(self, |OpsRB(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)| {
|
||||
self.write_reg(
|
||||
tg,
|
||||
|
@ -272,34 +312,65 @@ where
|
|||
self.bump_pc::<OpsN>();
|
||||
return Ok(VmRunOk::Breakpoint);
|
||||
}
|
||||
ADDF => self.binary_op::<f64>(ops::Add::add),
|
||||
SUBF => self.binary_op::<f64>(ops::Sub::sub),
|
||||
MULF => self.binary_op::<f64>(ops::Mul::mul),
|
||||
DIRF => handler!(self, |OpsRRRR(dt, rt, a0, a1)| {
|
||||
let a0 = self.read_reg(a0).cast::<f64>();
|
||||
let a1 = self.read_reg(a1).cast::<f64>();
|
||||
self.write_reg(dt, a0 / a1);
|
||||
self.write_reg(rt, a0 % a1);
|
||||
FADD32 => self.binary_op::<f32>(ops::Add::add),
|
||||
FADD64 => self.binary_op::<f64>(ops::Add::add),
|
||||
FSUB32 => self.binary_op::<f32>(ops::Sub::sub),
|
||||
FSUB64 => self.binary_op::<f64>(ops::Sub::sub),
|
||||
FMUL32 => self.binary_op::<f32>(ops::Mul::mul),
|
||||
FMUL64 => self.binary_op::<f64>(ops::Mul::mul),
|
||||
FDIV32 => self.binary_op::<f32>(ops::Div::div),
|
||||
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(
|
||||
dt,
|
||||
self.read_reg(a0).cast::<f64>() * self.read_reg(a1).cast::<f64>()
|
||||
+ self.read_reg(a2).cast::<f64>(),
|
||||
tg,
|
||||
crate::float::f32toint(
|
||||
self.read_reg(reg).cast::<f32>(),
|
||||
RoundingMode::try_from(mode)
|
||||
.map_err(|()| VmRunError::InvalidOperand)?,
|
||||
),
|
||||
);
|
||||
}),
|
||||
NEGF => handler!(self, |OpsRR(dt, a0)| {
|
||||
self.write_reg(dt, -self.read_reg(a0).cast::<f64>());
|
||||
FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| {
|
||||
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)| {
|
||||
self.write_reg(dt, self.read_reg(a0).cast::<i64>() as f64);
|
||||
FC32T64 => handler!(self, |OpsRR(tg, reg)| {
|
||||
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)| {
|
||||
self.write_reg(
|
||||
tg,
|
||||
|
@ -312,7 +383,7 @@ where
|
|||
STR16 => handler!(self, |OpsRRPH(dst, base, off, 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)),
|
||||
}
|
||||
}
|
||||
|
@ -328,14 +399,14 @@ where
|
|||
|
||||
/// Bump instruction pointer
|
||||
#[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);
|
||||
}
|
||||
|
||||
/// Decode instruction operands
|
||||
#[inline(always)]
|
||||
unsafe fn decode<T: BytecodeItem>(&mut self) -> T {
|
||||
self.memory.prog_read_unchecked::<T>(self.pc + 1_u64)
|
||||
unsafe fn decode<T: Copy>(&mut self) -> T {
|
||||
self.memory.prog_read::<T>(self.pc + 1_u64)
|
||||
}
|
||||
|
||||
/// Load
|
||||
|
@ -395,12 +466,11 @@ where
|
|||
/// Perform binary operation over register and immediate
|
||||
#[inline(always)]
|
||||
unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
|
||||
let OpsRRD(tg, reg, imm) = self.decode();
|
||||
self.write_reg(
|
||||
tg,
|
||||
op(self.read_reg(reg).cast::<T>(), Value::from(imm).cast::<T>()),
|
||||
);
|
||||
let OpsRR(tg, reg) = self.decode();
|
||||
let imm: T = self.decode();
|
||||
self.write_reg(tg, op(self.read_reg(reg).cast::<T>(), imm));
|
||||
self.bump_pc::<OpsRRD>();
|
||||
self.bump_pc::<T>();
|
||||
}
|
||||
|
||||
/// Perform binary operation over register and shift immediate
|
||||
|
@ -411,6 +481,51 @@ where
|
|||
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
|
||||
#[inline(always)]
|
||||
unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
name = "hbxrt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "hbxrt"
|
||||
|
||||
[dependencies]
|
||||
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>> {
|
||||
let mut prog = vec![];
|
||||
stdin().read_to_end(&mut prog)?;
|
||||
eprintln!("== HB×RT (Holey Bytes Linux Runtime) v0.1 ==");
|
||||
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(())
|
||||
}
|
||||
|
|
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