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

soft-float
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. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "argh"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219"
dependencies = [
"argh_derive",
"argh_shared",
]
[[package]]
name = "argh_derive"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a"
dependencies = [
"argh_shared",
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "argh_shared"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531"
dependencies = [
"serde",
]
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color-eyre"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]]
name = "eyre"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]] [[package]]
name = "hbbytecode" name = "hbbytecode"
version = "0.1.0" version = "0.1.0"
@ -21,13 +146,85 @@ name = "hbxrt"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hbvm", "hbvm",
"nix",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "indenter"
version = "1.0.67" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "nix"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "object"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -41,6 +238,41 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "serde"
version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -52,12 +284,80 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tracing"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
dependencies = [
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]] [[package]]
name = "with_builtin_macros" name = "with_builtin_macros"
version = "0.0.3" version = "0.0.3"
@ -75,5 +375,14 @@ checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.109",
]
[[package]]
name = "xtask"
version = "0.1.0"
dependencies = [
"argh",
"color-eyre",
"once_cell",
] ]

View File

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

BIN
examples/addition.hbf Normal file

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; 0x00, UN, N, "Cause an unreachable code trap" ;
0x01, TX, N, "Termiante execution" ;
0, UN, N, "Cause an unreachable code trap" ; 0x02, NOP, N, "Do nothing" ;
1, TX, N, "Termiante execution" ; 0x03, ADD8, RRR, "Addition (8b)" ;
2, NOP, N, "Do nothing" ; 0x04, ADD16, RRR, "Addition (16b)" ;
0x05, ADD32, RRR, "Addition (32b)" ;
3, ADD, RRR, "Addition" ; 0x06, ADD64, RRR, "Addition (64b)" ;
4, SUB, RRR, "Subtraction" ; 0x07, SUB8, RRR, "Subtraction (8b)" ;
5, MUL, RRR, "Multiplication" ; 0x08, SUB16, RRR, "Subtraction (16b)" ;
6, AND, RRR, "Bitand" ; 0x09, SUB32, RRR, "Subtraction (32b)" ;
7, OR, RRR, "Bitor" ; 0x0A, SUB64, RRR, "Subtraction (64b)" ;
8, XOR, RRR, "Bitxor" ; 0x0B, MUL8, RRR, "Multiplication (8b)" ;
9, SL, RRR, "Unsigned left bitshift" ; 0x0C, MUL16, RRR, "Multiplication (16b)" ;
10, SR, RRR, "Unsigned right bitshift" ; 0x0D, MUL32, RRR, "Multiplication (32b)" ;
11, SRS, RRR, "Signed right bitshift" ; 0x0E, MUL64, RRR, "Multiplication (64b)" ;
12, CMP, RRR, "Signed comparsion" ; 0x0F, AND, RRR, "Bitand" ;
13, CMPU, RRR, "Unsigned comparsion" ; 0x10, OR, RRR, "Bitor" ;
14, DIR, RRRR, "Merged divide-remainder" ; 0x11, XOR, RRR, "Bitxor" ;
15, NEG, RR, "Bit negation" ; 0x12, SLU8, RRR, "Unsigned left bitshift (8b)" ;
16, NOT, RR, "Logical negation" ; 0x13, SLU16, RRR, "Unsigned left bitshift (16b)" ;
17, ADDI, RRD, "Addition with immediate" ; 0x14, SLU32, RRR, "Unsigned left bitshift (32b)" ;
18, MULI, RRD, "Multiplication with immediate" ; 0x15, SLU64, RRR, "Unsigned left bitshift (64b)" ;
19, ANDI, RRD, "Bitand with immediate" ; 0x16, SRU8, RRR, "Unsigned right bitshift (8b)" ;
20, ORI, RRD, "Bitor with immediate" ; 0x17, SRU16, RRR, "Unsigned right bitshift (16b)" ;
21, XORI, RRD, "Bitxor with immediate" ; 0x18, SRU32, RRR, "Unsigned right bitshift (32b)" ;
22, SLI, RRW, "Unsigned left bitshift with immedidate"; 0x19, SRU64, RRR, "Unsigned right bitshift (64b)" ;
23, SRI, RRW, "Unsigned right bitshift with immediate"; 0x1A, SRS8, RRR, "Signed right bitshift (8b)" ;
24, SRSI, RRW, "Signed right bitshift with immediate" ; 0x1B, SRS16, RRR, "Signed right bitshift (16b)" ;
25, CMPI, RRD, "Signed compare with immediate" ; 0x1C, SRS32, RRR, "Signed right bitshift (32b)" ;
26, CMPUI, RRD, "Unsigned compare with immediate" ; 0x1D, SRS64, RRR, "Signed right bitshift (64b)" ;
0x1E, CMP, RRR, "Signed comparsion" ;
27, CP, RR, "Copy register" ; 0x1F, CMPU, RRR, "Unsigned comparsion" ;
28, SWA, RR, "Swap registers" ; 0x20, DIRU8, RRRR, "Merged divide-remainder (unsigned 8b)" ;
29, LI, RD, "Load immediate" ; 0x21, DIRU16, RRRR, "Merged divide-remainder (unsigned 16b)" ;
30, LRA, RRO, "Load relative address" ; 0x22, DIRU32, RRRR, "Merged divide-remainder (unsigned 32b)" ;
31, LD, RRAH, "Load from absolute address" ; 0x23, DIRU64, RRRR, "Merged divide-remainder (unsigned 64b)" ;
32, ST, RRAH, "Store to absolute address" ; 0x24, DIRS8, RRRR, "Merged divide-remainder (signed 8b)" ;
33, LDR, RROH, "Load from relative address" ; 0x25, DIRS16, RRRR, "Merged divide-remainder (signed 16b)" ;
34, STR, RROH, "Store to relative address" ; 0x26, DIRS32, RRRR, "Merged divide-remainder (signed 32b)" ;
35, BMC, RRH, "Copy block of memory" ; 0x27, DIRS64, RRRR, "Merged divide-remainder (signed 64b)" ;
36, BRC, RRB, "Copy register block" ; 0x28, NEG, RR, "Bit negation" ;
0x29, NOT, RR, "Logical negation" ;
37, JMP, O, "Relative jump" ; 0x2A, SXT8, RR, "Sign extend 8b to 64b" ;
38, JAL, RRA, "Linking absolute jump" ; 0x2B, SXT16, RR, "Sign extend 16b to 64b" ;
39, JALR, RRO, "Linking relative jump" ; 0x2C, SXT32, RR, "Sign extend 32b to 64b" ;
40, JEQ, RRP, "Branch on equal" ; 0x2D, ADDI8, RRB, "Addition with immediate (8b)" ;
41, JNE, RRP, "Branch on nonequal" ; 0x2E, ADDI16, RRH, "Addition with immediate (16b)" ;
42, JLT, RRP, "Branch on lesser-than (signed)" ; 0x2F, ADDI32, RRW, "Addition with immediate (32b)" ;
43, JGT, RRP, "Branch on greater-than (signed)" ; 0x30, ADDI64, RRD, "Addition with immediate (64b)" ;
44, JLTU, RRP, "Branch on lesser-than (unsigned)" ; 0x31, MULI8, RRW, "Multiplication with immediate (8b)" ;
45, JGTU, RRP, "Branch on greater-than (unsigned)" ; 0x32, MULI16, RRH, "Multiplication with immediate (16b)" ;
46, ECA, N, "Environment call trap" ; 0x33, MULI32, RRW, "Multiplication with immediate (32b)" ;
47, EBP, N, "Environment breakpoint" ; 0x34, MULI64, RRD, "Multiplication with immediate (64b)" ;
0x35, ANDI, RRD, "Bitand with immediate" ;
48, ADDF, RRR, "Floating addition" ; 0x36, ORI, RRD, "Bitor with immediate" ;
49, SUBF, RRR, "Floating subtraction" ; 0x37, XORI, RRD, "Bitxor with immediate" ;
50, MULF, RRR, "Floating multiply" ; 0x38, SLUI8, RRW, "Unsigned left bitshift with immedidate (8b)" ;
51, DIRF, RRRR, "Merged floating divide-remainder" ; 0x39, SLUI16, RRW, "Unsigned left bitshift with immedidate (16b)";
52, FMAF, RRRR, "Fused floating multiply-add" ; 0x3A, SLUI32, RRW, "Unsigned left bitshift with immedidate (32b)";
53, NEGF, RR, "Floating sign negation" ; 0x3B, SLUI64, RRW, "Unsigned left bitshift with immedidate (64b)";
54, ITF, RR, "Int to float" ; 0x3C, SRUI8, RRW, "Unsigned right bitshift with immediate (8b)" ;
55, FTI, RR, "Float to int" ; 0x3D, SRUI16, RRW, "Unsigned right bitshift with immediate (16b)";
0x3E, SRUI32, RRW, "Unsigned right bitshift with immediate (32b)";
56, ADDFI, RRD, "Floating addition with immediate" ; 0x3F, SRUI64, RRW, "Unsigned right bitshift with immediate (64b)";
57, MULFI, RRD, "Floating multiplication with immediate"; 0x40, SRSI8, RRW, "Signed right bitshift with immediate" ;
0x41, SRSI16, RRW, "Signed right bitshift with immediate" ;
58, LRA16 , RRP, "Load relative immediate (16 bit)" ; 0x42, SRSI32, RRW, "Signed right bitshift with immediate" ;
59, LDR16 , RRPH, "Load from relative address (16 bit)" ; 0x43, SRSI64, RRW, "Signed right bitshift with immediate" ;
60, STR16 , RRPH, "Store to relative address (16 bit)" ; 0x44, CMPI, RRD, "Signed compare with immediate" ;
61, JMPR16, P, "Relative jump (16 bit)" ; 0x45, CMPUI, RRD, "Unsigned compare with immediate" ;
0x46, CP, RR, "Copy register" ;
0x47, SWA, RR, "Swap registers" ;
0x48, LI8, RB, "Load immediate (8b)" ;
0x49, LI16, RH, "Load immediate (16b)" ;
0x4A, LI32, RW, "Load immediate (32b)" ;
0x4B, LI64, RD, "Load immediate (64b)" ;
0x4C, LRA, RRO, "Load relative address" ;
0x4D, LD, RRAH, "Load from absolute address" ;
0x4E, ST, RRAH, "Store to absolute address" ;
0x4F, LDR, RROH, "Load from relative address" ;
0x50, STR, RROH, "Store to relative address" ;
0x51, BMC, RRH, "Copy block of memory" ;
0x52, BRC, RRB, "Copy register block" ;
0x53, JMP, O, "Relative jump" ;
0x54, JAL, RRA, "Linking absolute jump" ;
0x55, JALR, RRO, "Linking relative jump" ;
0x56, JEQ, RRP, "Branch on equal" ;
0x57, JNE, RRP, "Branch on nonequal" ;
0x58, JLT, RRP, "Branch on lesser-than (signed)" ;
0x59, JGT, RRP, "Branch on greater-than (signed)" ;
0x5A, JLTU, RRP, "Branch on lesser-than (unsigned)" ;
0x5B, JGTU, RRP, "Branch on greater-than (unsigned)" ;
0x5C, ECA, N, "Environment call trap" ;
0x5D, EBP, N, "Environment breakpoint" ;
0x5E, FADD32, RRR, "Floating point addition (32b)" ;
0x5F, FADD64, RRR, "Floating point addition (64b)" ;
0x60, FSUB32, RRR, "Floating point subtraction (32b)" ;
0x61, FSUB64, RRR, "Floating point subtraction (64b)" ;
0x62, FMUL32, RRR, "Floating point multiply (32b)" ;
0x63, FMUL64, RRR, "Floating point multiply (64b)" ;
0x64, FDIV32, RRR, "Floating point division (32b)" ;
0x65, FDIV64, RRR, "Floating point division (64b)" ;
0x66, FMA32, RRR, "Float fused multiply-add (32b)" ;
0x67, FMA64, RRR, "Float fused multiply-add (64b)" ;
0x68, FINV32, RR, "Float reciprocal (32b)" ;
0x69, FINV64, RR, "Float reciprocal (64b)" ;
0x6A, FCMPLT32, RRR, "Flaot compare less than (32b)" ;
0x6B, FCMPLT64, RRR, "Flaot compare less than (64b)" ;
0x6C, FCMPGT32, RRR, "Flaot compare greater than (32b)" ;
0x6D, FCMPGT64, RRR, "Flaot compare greater than (64b)" ;
0x6E, ITF32, RR, "Int to 32 bit float" ;
0x6F, ITF64, RR, "Int to 64 bit float" ;
0x70, FTI32, RRB, "Float 32 to int" ;
0x71, FTI64, RRB, "Float 64 to int" ;
0x72, FC32T64, RR, "Float 64 to Float 32" ;
0x73, FC64T32, RRB, "Float 32 to Float 64" ;
0x74, LRA16, RRP, "Load relative immediate (16 bit)" ;
0x75, LDR16, RRPH, "Load from relative address (16 bit)" ;
0x76, STR16, RRPH, "Store to relative address (16 bit)" ;
0x77, JMP16, P, "Relative jump (16 bit)" ;

View File

@ -1,5 +1,7 @@
#![no_std] #![no_std]
use core::convert::TryFrom;
type OpR = u8; type OpR = u8;
type OpA = u64; type OpA = u64;
@ -17,6 +19,7 @@ pub unsafe trait BytecodeItem {}
macro_rules! define_items { macro_rules! define_items {
($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => { ($($name:ident ($($item:ident),* $(,)?)),* $(,)?) => {
$( $(
#[derive(Clone, Copy, Debug)]
#[repr(packed)] #[repr(packed)]
pub struct $name($(pub $item),*); pub struct $name($(pub $item),*);
unsafe impl BytecodeItem for $name {} unsafe impl BytecodeItem for $name {}
@ -31,6 +34,9 @@ define_items! {
OpsRRB (OpR, OpR, OpB ), OpsRRB (OpR, OpR, OpB ),
OpsRRH (OpR, OpR, OpH ), OpsRRH (OpR, OpR, OpH ),
OpsRRW (OpR, OpR, OpW ), OpsRRW (OpR, OpR, OpW ),
OpsRB (OpR, OpB ),
OpsRH (OpR, OpH ),
OpsRW (OpR, OpW ),
OpsRD (OpR, OpD ), OpsRD (OpR, OpD ),
OpsRRD (OpR, OpR, OpD ), OpsRRD (OpR, OpR, OpD ),
OpsRRAH (OpR, OpR, OpA, OpH), OpsRRAH (OpR, OpR, OpA, OpH),
@ -89,4 +95,24 @@ macro_rules! gen_opcodes {
}; };
} }
/// Rounding mode
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum RoundingMode {
NearestEven = 0,
Truncate = 1,
Up = 2,
Down = 3,
}
impl TryFrom<u8> for RoundingMode {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
(value >= 3)
.then(|| unsafe { core::mem::transmute(value) })
.ok_or(())
}
}
invoke_with_def!(gen_opcodes); invoke_with_def!(gen_opcodes);

66
hbvm/src/float/aarch64.rs Normal file
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] #![no_std]
#![cfg_attr(feature = "nightly", feature(fn_align))] #![cfg_attr(feature = "nightly", feature(fn_align))]
#![warn(missing_docs)]
use mem::{Address, Memory}; use mem::{Address, Memory};
@ -23,6 +22,7 @@ pub mod mem;
pub mod value; pub mod value;
mod bmc; mod bmc;
mod float;
mod utils; mod utils;
mod vmrun; mod vmrun;
@ -92,6 +92,12 @@ pub enum VmRunError {
/// Reached unreachable code /// Reached unreachable code
Unreachable, Unreachable,
/// Invalid operand
InvalidOperand,
/// Unimplemented feature
Unimplemented,
} }
/// Virtual machine halt ok /// Virtual machine halt ok

View File

@ -6,7 +6,7 @@ pub(crate) mod addr;
pub use addr::Address; pub use addr::Address;
use {crate::utils::impl_display, hbbytecode::BytecodeItem}; use crate::utils::impl_display;
/// Load-store memory access /// Load-store memory access
pub trait Memory { pub trait Memory {
@ -36,13 +36,7 @@ pub trait Memory {
/// ///
/// # Safety /// # Safety
/// - Data read have to be valid /// - Data read have to be valid
unsafe fn prog_read<T: BytecodeItem>(&mut self, addr: Address) -> Option<T>; unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T;
/// Read from program memory to exectue
///
/// # Safety
/// - You have to be really sure that these bytes are there, understand?
unsafe fn prog_read_unchecked<T: BytecodeItem>(&mut self, addr: Address) -> T;
} }
/// Unhandled load access trap /// Unhandled load access trap

View File

@ -78,31 +78,19 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
} }
#[inline(always)] #[inline(always)]
unsafe fn prog_read<T>(&mut self, addr: Address) -> Option<T> { unsafe fn prog_read<T>(&mut self, addr: Address) -> T {
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() { if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
return self.icache.fetch::<T>(addr, self.root_pt); return self
.icache
.fetch::<T>(addr, self.root_pt)
.unwrap_or_else(|| unsafe { core::mem::zeroed() });
} }
let addr = addr.truncate_usize(); let addr = addr.truncate_usize();
self.program self.program
.get(addr..addr + size_of::<T>()) .get(addr..addr + size_of::<T>())
.map(|x| x.as_ptr().cast::<T>().read()) .map(|x| x.as_ptr().cast::<T>().read())
} .unwrap_or_else(|| unsafe { core::mem::zeroed() })
#[inline(always)]
unsafe fn prog_read_unchecked<T>(&mut self, addr: Address) -> T {
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
return self
.icache
.fetch::<T>(addr, self.root_pt)
.unwrap_or_else(|| core::mem::zeroed());
}
self.program
.as_ptr()
.add(addr.truncate_usize())
.cast::<T>()
.read()
} }
} }

View File

@ -46,8 +46,8 @@ pub(crate) mod internal {
pub(crate) use impl_display_match_fragment; pub(crate) use impl_display_match_fragment;
} }
macro_rules! static_assert_eq(($l:expr, $r:expr $(,)?) => { macro_rules! static_assert(($expr:expr $(,)?) => {
const _: [(); ($l != $r) as usize] = []; const _: [(); !$expr as usize] = [];
}); });
pub(crate) use {impl_display, static_assert_eq}; pub(crate) use {impl_display, static_assert};

View File

@ -1,35 +1,32 @@
//! HoleyBytes register value definition //! HoleyBytes register value definition
/// Define [`Value`] union use crate::utils::static_assert;
/// Define [`Value`] »union« (it's fake)
/// ///
/// # Safety /// # Safety
/// Union variants have to be sound to byte-reinterpretate /// Its variants have to be sound to byte-reinterpretate
/// between each other. Otherwise the behaviour is undefined. /// between each other. Otherwise the behaviour is undefined.
macro_rules! value_def { macro_rules! value_def {
($($ty:ident),* $(,)?) => { ($($ty:ident),* $(,)?) => {
/// HBVM register value /// HBVM register value
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[repr(packed)] #[repr(transparent)]
pub union Value { pub struct Value(pub u64);
$(
#[doc = concat!(stringify!($ty), " type")]
pub $ty: $ty
),*
}
$( $(
impl From<$ty> for Value { impl From<$ty> for Value {
#[inline] #[inline]
fn from(value: $ty) -> Self { fn from(value: $ty) -> Self {
Self { $ty: value } let mut new = core::mem::MaybeUninit::<u64>::zeroed();
unsafe {
new.as_mut_ptr().cast::<$ty>().write(value);
Self(new.assume_init())
}
} }
} }
crate::utils::static_assert_eq!( static_assert!(core::mem::size_of::<$ty>() <= core::mem::size_of::<Value>());
core::mem::size_of::<$ty>(),
core::mem::size_of::<Value>(),
);
impl private::Sealed for $ty {} impl private::Sealed for $ty {}
unsafe impl ValueVariant for $ty {} unsafe impl ValueVariant for $ty {}
@ -41,23 +38,7 @@ impl Value {
/// Byte reinterpret value to target variant /// Byte reinterpret value to target variant
#[inline] #[inline]
pub fn cast<V: ValueVariant>(self) -> V { pub fn cast<V: ValueVariant>(self) -> V {
/// Evil. unsafe { core::mem::transmute_copy(&self.0) }
///
/// Transmute cannot be performed with generic type
/// as size is unknown, so union is used.
///
/// # Safety
/// If [`ValueVariant`] implemented correctly, it's fine :)
///
/// :ferrisClueless:
union Transmute<Variant: ValueVariant> {
/// Self
src: Value,
/// Target variant
variant: Variant,
}
unsafe { Transmute { src: self }.variant }
} }
} }
@ -69,8 +50,8 @@ mod private {
pub trait Sealed {} pub trait Sealed {}
} }
value_def!(u64, i64, f64); value_def!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
crate::utils::static_assert_eq!(core::mem::size_of::<Value>(), 8); static_assert!(core::mem::size_of::<Value>() == 8);
impl core::fmt::Debug for Value { impl core::fmt::Debug for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@ -78,3 +59,28 @@ impl core::fmt::Debug for Value {
write!(f, "{:x}", self.cast::<u64>()) write!(f, "{:x}", self.cast::<u64>())
} }
} }
pub(crate) trait CheckedDivRem {
fn checked_div(self, other: Self) -> Option<Self>
where
Self: Sized;
fn checked_rem(self, other: Self) -> Option<Self>
where
Self: Sized;
}
macro_rules! impl_checked_div_rem {
($($ty:ty),* $(,)?) => {
$(impl CheckedDivRem for $ty {
#[inline(always)]
fn checked_div(self, another: Self) -> Option<Self>
{ self.checked_div(another) }
#[inline(always)]
fn checked_rem(self, another: Self) -> Option<Self>
{ self.checked_rem(another) }
})*
};
}
impl_checked_div_rem!(u8, u16, u32, u64, i8, i16, i32, i64);

View File

@ -2,7 +2,7 @@
//! //!
//! Have fun //! Have fun
use hbbytecode::OpsN; use hbbytecode::RoundingMode;
use { use {
super::{ super::{
@ -11,19 +11,20 @@ use {
value::{Value, ValueVariant}, value::{Value, ValueVariant},
Vm, VmRunError, VmRunOk, Vm, VmRunError, VmRunOk,
}, },
crate::mem::Address, crate::{mem::Address, value::CheckedDivRem},
core::{cmp::Ordering, ops}, core::{cmp::Ordering, ops},
hbbytecode::{ hbbytecode::{
BytecodeItem, OpsO, OpsP, OpsRD, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO, OpsRROH, OpsN, OpsO, OpsP, OpsRB, OpsRD, OpsRH, OpsRR, OpsRRAH, OpsRRB, OpsRRD, OpsRRH, OpsRRO,
OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, OpsRROH, OpsRRP, OpsRRPH, OpsRRR, OpsRRRR, OpsRRW, OpsRW,
}, },
}; };
macro_rules! handler { macro_rules! handler {
($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{ ($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{
let $ty($($ident),*) = $self.decode::<$ty>(); let $ty($($ident),*) = $self.decode::<$ty>();
#[allow(clippy::no_effect)] $expr; #[allow(clippy::no_effect)] let e = $expr;
$self.bump_pc::<$ty>(); $self.bump_pc::<$ty>();
e
}}; }};
} }
@ -58,11 +59,7 @@ where
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU // - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
// sorry 8 bit fans, HBVM won't run on your Speccy :( // sorry 8 bit fans, HBVM won't run on your Speccy :(
unsafe { unsafe {
match self match self.memory.prog_read::<u8>(self.pc as _) {
.memory
.prog_read::<u8>(self.pc as _)
.ok_or(VmRunError::ProgramFetchLoadEx(self.pc as _))?
{
UN => { UN => {
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Err(VmRunError::Unreachable); return Err(VmRunError::Unreachable);
@ -72,15 +69,32 @@ where
return Ok(VmRunOk::End); return Ok(VmRunOk::End);
} }
NOP => handler!(self, |OpsN()| ()), NOP => handler!(self, |OpsN()| ()),
ADD => self.binary_op(u64::wrapping_add), ADD8 => self.binary_op(u8::wrapping_add),
SUB => self.binary_op(u64::wrapping_sub), ADD16 => self.binary_op(u16::wrapping_add),
MUL => self.binary_op(u64::wrapping_mul), ADD32 => self.binary_op(u32::wrapping_add),
ADD64 => self.binary_op(u64::wrapping_add),
SUB8 => self.binary_op(u8::wrapping_sub),
SUB16 => self.binary_op(u16::wrapping_sub),
SUB32 => self.binary_op(u32::wrapping_sub),
SUB64 => self.binary_op(u64::wrapping_sub),
MUL8 => self.binary_op(u8::wrapping_mul),
MUL16 => self.binary_op(u16::wrapping_mul),
MUL32 => self.binary_op(u32::wrapping_mul),
MUL64 => self.binary_op(u64::wrapping_mul),
AND => self.binary_op::<u64>(ops::BitAnd::bitand), AND => self.binary_op::<u64>(ops::BitAnd::bitand),
OR => self.binary_op::<u64>(ops::BitOr::bitor), OR => self.binary_op::<u64>(ops::BitOr::bitor),
XOR => self.binary_op::<u64>(ops::BitXor::bitxor), XOR => self.binary_op::<u64>(ops::BitXor::bitxor),
SL => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)), SLU8 => self.binary_op(|l, r| u8::wrapping_shl(l, r as u32)),
SR => self.binary_op(|l, r| u64::wrapping_shr(l, r as u32)), SLU16 => self.binary_op(|l, r| u16::wrapping_shl(l, r as u32)),
SRS => self.binary_op(|l: u64, r| i64::wrapping_shl(l as i64, r as u32) as u64), SLU32 => self.binary_op(u32::wrapping_shl),
SLU64 => self.binary_op(|l, r| u64::wrapping_shl(l, r as u32)),
SRU8 => self.binary_op(|l, r| u8::wrapping_shr(l, r as u32)),
SRU16 => self.binary_op(|l, r| u16::wrapping_shr(l, r as u32)),
SRU32 => self.binary_op(u32::wrapping_shr),
SRS8 => self.binary_op(|l: i8, r| i8::wrapping_shl(l, r as u32)),
SRS16 => self.binary_op(|l: i16, r| i16::wrapping_shl(l, r as u32)),
SRS32 => self.binary_op(|l: i32, r| i32::wrapping_shl(l, r as u32)),
SRS64 => self.binary_op(|l: i64, r| i64::wrapping_shl(l, r as u32)),
CMP => handler!(self, |OpsRRR(tg, a0, a1)| { CMP => handler!(self, |OpsRRR(tg, a0, a1)| {
// Compare a0 <=> a1 // Compare a0 <=> a1
// < → 0 // < → 0
@ -107,6 +121,14 @@ where
+ 1, + 1,
); );
}), }),
DIRU8 => self.dir::<u8>(),
DIRU16 => self.dir::<u16>(),
DIRU32 => self.dir::<u32>(),
DIRU64 => self.dir::<u64>(),
DIRS8 => self.dir::<i8>(),
DIRS16 => self.dir::<i16>(),
DIRS32 => self.dir::<i32>(),
DIRS64 => self.dir::<i64>(),
NEG => handler!(self, |OpsRR(tg, a0)| { NEG => handler!(self, |OpsRR(tg, a0)| {
// Bit negation // Bit negation
self.write_reg(tg, !self.read_reg(a0).cast::<u64>()) self.write_reg(tg, !self.read_reg(a0).cast::<u64>())
@ -115,21 +137,38 @@ where
// Logical negation // Logical negation
self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0)); self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0));
}), }),
DIR => handler!(self, |OpsRRRR(dt, rt, a0, a1)| { SXT8 => handler!(self, |OpsRR(tg, a0)| {
// Fused Division-Remainder self.write_reg(tg, self.read_reg(a0).cast::<i8>() as i64)
let a0 = self.read_reg(a0).cast::<u64>();
let a1 = self.read_reg(a1).cast::<u64>();
self.write_reg(dt, a0.checked_div(a1).unwrap_or(u64::MAX));
self.write_reg(rt, a0.checked_rem(a1).unwrap_or(u64::MAX));
}), }),
ADDI => self.binary_op_imm(u64::wrapping_add), SXT16 => handler!(self, |OpsRR(tg, a0)| {
MULI => self.binary_op_imm(u64::wrapping_sub), self.write_reg(tg, self.read_reg(a0).cast::<i16>() as i64)
}),
SXT32 => handler!(self, |OpsRR(tg, a0)| {
self.write_reg(tg, self.read_reg(a0).cast::<i32>() as i64)
}),
ADDI8 => self.binary_op_imm(u8::wrapping_add),
ADDI16 => self.binary_op_imm(u16::wrapping_add),
ADDI32 => self.binary_op_imm(u32::wrapping_add),
ADDI64 => self.binary_op_imm(u64::wrapping_add),
MULI8 => self.binary_op_imm(u8::wrapping_sub),
MULI16 => self.binary_op_imm(u16::wrapping_sub),
MULI32 => self.binary_op_imm(u32::wrapping_sub),
MULI64 => self.binary_op_imm(u64::wrapping_sub),
ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand), ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand),
ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor), ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor),
XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor), XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor),
SLI => self.binary_op_ims(u64::wrapping_shl), SLUI8 => self.binary_op_ims::<u8>(ops::Shl::shl),
SRI => self.binary_op_ims(u64::wrapping_shr), SLUI16 => self.binary_op_ims::<u16>(ops::Shl::shl),
SRSI => self.binary_op_ims(i64::wrapping_shr), SLUI32 => self.binary_op_ims::<u32>(ops::Shl::shl),
SLUI64 => self.binary_op_ims::<u64>(ops::Shl::shl),
SRUI8 => self.binary_op_ims::<u8>(ops::Shr::shr),
SRUI16 => self.binary_op_ims::<u16>(ops::Shr::shr),
SRUI32 => self.binary_op_ims::<u32>(ops::Shr::shr),
SRUI64 => self.binary_op_ims::<u64>(ops::Shr::shr),
SRSI8 => self.binary_op_ims::<i8>(ops::Shr::shr),
SRSI16 => self.binary_op_ims::<i16>(ops::Shr::shr),
SRSI32 => self.binary_op_ims::<i32>(ops::Shr::shr),
SRSI64 => self.binary_op_ims::<i64>(ops::Shr::shr),
CMPI => handler!(self, |OpsRRD(tg, a0, imm)| { CMPI => handler!(self, |OpsRRD(tg, a0, imm)| {
self.write_reg( self.write_reg(
tg, tg,
@ -158,9 +197,10 @@ where
} }
} }
}), }),
LI => handler!(self, |OpsRD(tg, imm)| { LI8 => handler!(self, |OpsRB(tg, imm)| self.write_reg(tg, imm)),
self.write_reg(tg, imm); LI16 => handler!(self, |OpsRH(tg, imm)| self.write_reg(tg, imm)),
}), LI32 => handler!(self, |OpsRW(tg, imm)| self.write_reg(tg, imm)),
LI64 => handler!(self, |OpsRD(tg, imm)| self.write_reg(tg, imm)),
LRA => handler!(self, |OpsRRO(tg, reg, imm)| { LRA => handler!(self, |OpsRRO(tg, reg, imm)| {
self.write_reg( self.write_reg(
tg, tg,
@ -272,34 +312,65 @@ where
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Ok(VmRunOk::Breakpoint); return Ok(VmRunOk::Breakpoint);
} }
ADDF => self.binary_op::<f64>(ops::Add::add), FADD32 => self.binary_op::<f32>(ops::Add::add),
SUBF => self.binary_op::<f64>(ops::Sub::sub), FADD64 => self.binary_op::<f64>(ops::Add::add),
MULF => self.binary_op::<f64>(ops::Mul::mul), FSUB32 => self.binary_op::<f32>(ops::Sub::sub),
DIRF => handler!(self, |OpsRRRR(dt, rt, a0, a1)| { FSUB64 => self.binary_op::<f64>(ops::Sub::sub),
let a0 = self.read_reg(a0).cast::<f64>(); FMUL32 => self.binary_op::<f32>(ops::Mul::mul),
let a1 = self.read_reg(a1).cast::<f64>(); FMUL64 => self.binary_op::<f64>(ops::Mul::mul),
self.write_reg(dt, a0 / a1); FDIV32 => self.binary_op::<f32>(ops::Div::div),
self.write_reg(rt, a0 % a1); FDIV64 => self.binary_op::<f64>(ops::Div::div),
FMA32 => self.fma::<f32>(),
FMA64 => self.fma::<f64>(),
FINV32 => handler!(self, |OpsRR(tg, reg)| {
self.write_reg(tg, 1. / self.read_reg(reg).cast::<f32>())
}), }),
FMAF => handler!(self, |OpsRRRR(dt, a0, a1, a2)| { FINV64 => handler!(self, |OpsRR(tg, reg)| {
self.write_reg(tg, 1. / self.read_reg(reg).cast::<f64>())
}),
FCMPLT32 => self.fcmp::<f32>(Ordering::Less),
FCMPLT64 => self.fcmp::<f64>(Ordering::Less),
FCMPGT32 => self.fcmp::<f32>(Ordering::Greater),
FCMPGT64 => self.fcmp::<f64>(Ordering::Greater),
ITF32 => handler!(self, |OpsRR(tg, reg)| {
self.write_reg(tg, self.read_reg(reg).cast::<i64>() as f32);
}),
ITF64 => handler!(self, |OpsRR(tg, reg)| {
self.write_reg(tg, self.read_reg(reg).cast::<i64>() as f64);
}),
FTI32 => handler!(self, |OpsRRB(tg, reg, mode)| {
self.write_reg( self.write_reg(
dt, tg,
self.read_reg(a0).cast::<f64>() * self.read_reg(a1).cast::<f64>() crate::float::f32toint(
+ self.read_reg(a2).cast::<f64>(), self.read_reg(reg).cast::<f32>(),
RoundingMode::try_from(mode)
.map_err(|()| VmRunError::InvalidOperand)?,
),
); );
}), }),
NEGF => handler!(self, |OpsRR(dt, a0)| { FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| {
self.write_reg(dt, -self.read_reg(a0).cast::<f64>()); self.write_reg(
tg,
crate::float::f64toint(
self.read_reg(reg).cast::<f64>(),
RoundingMode::try_from(mode)
.map_err(|()| VmRunError::InvalidOperand)?,
),
);
}), }),
ITF => handler!(self, |OpsRR(dt, a0)| { FC32T64 => handler!(self, |OpsRR(tg, reg)| {
self.write_reg(dt, self.read_reg(a0).cast::<i64>() as f64); self.write_reg(tg, self.read_reg(reg).cast::<f32>() as f64);
}),
FC64T32 => handler!(self, |OpsRRB(tg, reg, mode)| {
self.write_reg(
tg,
crate::float::conv64to32(
self.read_reg(reg).cast(),
RoundingMode::try_from(mode)
.map_err(|()| VmRunError::InvalidOperand)?,
),
)
}), }),
FTI => {
let OpsRR(dt, a0) = self.decode();
self.write_reg(dt, self.read_reg(a0).cast::<f64>() as i64);
}
ADDFI => self.binary_op_imm::<f64>(ops::Add::add),
MULFI => self.binary_op_imm::<f64>(ops::Mul::mul),
LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| { LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| {
self.write_reg( self.write_reg(
tg, tg,
@ -312,7 +383,7 @@ where
STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| { STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| {
self.store(dst, base, u64::from(off).wrapping_add(self.pc.get()), count)?; self.store(dst, base, u64::from(off).wrapping_add(self.pc.get()), count)?;
}), }),
JMPR16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)), JMP16 => handler!(self, |OpsP(off)| self.pc = self.pc.wrapping_add(off)),
op => return Err(VmRunError::InvalidOpcode(op)), op => return Err(VmRunError::InvalidOpcode(op)),
} }
} }
@ -328,14 +399,14 @@ where
/// Bump instruction pointer /// Bump instruction pointer
#[inline(always)] #[inline(always)]
fn bump_pc<T: BytecodeItem>(&mut self) { fn bump_pc<T: Copy>(&mut self) {
self.pc = self.pc.wrapping_add(core::mem::size_of::<T>() + 1); self.pc = self.pc.wrapping_add(core::mem::size_of::<T>() + 1);
} }
/// Decode instruction operands /// Decode instruction operands
#[inline(always)] #[inline(always)]
unsafe fn decode<T: BytecodeItem>(&mut self) -> T { unsafe fn decode<T: Copy>(&mut self) -> T {
self.memory.prog_read_unchecked::<T>(self.pc + 1_u64) self.memory.prog_read::<T>(self.pc + 1_u64)
} }
/// Load /// Load
@ -395,12 +466,11 @@ where
/// Perform binary operation over register and immediate /// Perform binary operation over register and immediate
#[inline(always)] #[inline(always)]
unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) { unsafe fn binary_op_imm<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
let OpsRRD(tg, reg, imm) = self.decode(); let OpsRR(tg, reg) = self.decode();
self.write_reg( let imm: T = self.decode();
tg, self.write_reg(tg, op(self.read_reg(reg).cast::<T>(), imm));
op(self.read_reg(reg).cast::<T>(), Value::from(imm).cast::<T>()),
);
self.bump_pc::<OpsRRD>(); self.bump_pc::<OpsRRD>();
self.bump_pc::<T>();
} }
/// Perform binary operation over register and shift immediate /// Perform binary operation over register and shift immediate
@ -411,6 +481,51 @@ where
self.bump_pc::<OpsRRW>(); self.bump_pc::<OpsRRW>();
} }
/// Fused division-remainder
#[inline(always)]
unsafe fn dir<T: ValueVariant + CheckedDivRem>(&mut self) {
handler!(self, |OpsRRRR(td, tr, a0, a1)| {
let a0 = self.read_reg(a0).cast::<T>();
let a1 = self.read_reg(a1).cast::<T>();
if let Some(div) = a0.checked_div(a1) {
self.write_reg(td, div);
} else {
self.write_reg(td, -1_i64);
}
if let Some(rem) = a0.checked_rem(a1) {
self.write_reg(tr, rem);
} else {
self.write_reg(tr, a0);
}
});
}
/// Fused multiply-add
#[inline(always)]
unsafe fn fma<T>(&mut self)
where
T: ValueVariant + core::ops::Mul<Output = T> + core::ops::Add<Output = T>,
{
handler!(self, |OpsRRRR(tg, a0, a1, a2)| {
let a0 = self.read_reg(a0).cast::<T>();
let a1 = self.read_reg(a1).cast::<T>();
let a2 = self.read_reg(a2).cast::<T>();
self.write_reg(tg, a0 * a1 + a2)
});
}
/// Float comparsion
#[inline(always)]
unsafe fn fcmp<T: PartialOrd + ValueVariant>(&mut self, nan: Ordering) {
handler!(self, |OpsRRR(tg, a0, a1)| {
let a0 = self.read_reg(a0).cast::<T>();
let a1 = self.read_reg(a1).cast::<T>();
self.write_reg(tg, (a0.partial_cmp(&a1).unwrap_or(nan) as i8 + 1) as u8)
});
}
/// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected /// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected
#[inline(always)] #[inline(always)]
unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) { unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) {

View File

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

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>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut prog = vec![]; eprintln!("== HB×RT (Holey Bytes Linux Runtime) v0.1 ==");
stdin().read_to_end(&mut prog)?; eprintln!("[W] Currently supporting only flat images");
let Some(image_path) = args().nth(1) else {
eprintln!("[E] Missing image path");
exit(1);
};
// Load program
eprintln!("[I] Loading image from \"{image_path}\"");
let file = File::open(image_path)?;
let ptr = unsafe {
mmap(
None,
NonZeroUsize::new(file.metadata()?.len() as usize).ok_or("File is empty")?,
ProtFlags::PROT_READ,
MapFlags::MAP_PRIVATE,
Some(&file),
0,
)?
};
eprintln!("[I] Image loaded at {ptr:p}");
// Execute program
let mut vm = unsafe { Vm::<_, 0>::new(mem::HostMemory, Address::new(ptr as u64)) };
let stat = loop {
match vm.run() {
Ok(VmRunOk::Breakpoint) => eprintln!(
"[I] Hit breakpoint\nIP: {}\n== Registers ==\n{:?}",
vm.pc, vm.registers
),
Ok(VmRunOk::Timer) => (),
Ok(VmRunOk::Ecall) => unsafe {
std::arch::asm!(
"syscall",
inlateout("rax") vm.registers[1].0,
in("rdi") vm.registers[2].0,
in("rsi") vm.registers[3].0,
in("rdx") vm.registers[4].0,
in("r10") vm.registers[5].0,
in("r8") vm.registers[6].0,
in("r9") vm.registers[7].0,
)
},
Ok(VmRunOk::End) => break Ok(()),
Err(e) => break Err(e),
}
};
eprintln!("\n== Registers ==\n{:?}", vm.registers);
if let Err(e) = stat {
eprintln!("\n[E] Runtime error: {e:?}");
exit(2);
}
eprintln!("WARNING! Bytecode valider has not been yet implemented and running program can lead to undefiend behaviour.");
Ok(()) Ok(())
} }

31
hbxrt/src/mem.rs Normal file
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 {}