New float instructions, Linux runtime added, some other stuff I forgor

trunk
Erin 2023-10-18 12:14:24 +02:00
parent 889aefe87a
commit 0cb20d5727
24 changed files with 1154 additions and 208 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[alias]
xtask = "r -p xtask --"

317
Cargo.lock generated
View File

@ -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",
]

View File

@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = ["hbbytecode", "hbvm", "hbxrt"]
members = ["hbbytecode", "hbvm", "hbxrt", "xtask"]

BIN
examples/addition.hbf Normal file

Binary file not shown.

BIN
examples/linux-hello.hbf Normal file

Binary file not shown.

View File

@ -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)" ;

View File

@ -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
View 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
View 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
View 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
View 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) };
}

View File

@ -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

View File

@ -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

View File

@ -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() })
}
}

View File

@ -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};

View File

@ -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);

View File

@ -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) {

View File

@ -1,7 +1,9 @@
[package]
name = "hbxrt"
version = "0.1.0"
edition = "2021"
name = "hbxrt"
version = "0.1.0"
edition = "2021"
default-run = "hbxrt"
[dependencies]
hbvm.path = "../hbvm"
nix = { version = "0.27", features = ["mman"] }

View File

@ -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
View 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
View 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
View 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
View 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
View 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 {}