From 44ccfd9b37ca86db344b0b4cb66a02847dca610e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Drwi=C4=99ga?= Date: Tue, 14 Jan 2025 18:48:39 +0100 Subject: [PATCH] [GP 0.5.4] New instructions (#32) --- .github/workflows/build.yml | 41 ++-- README.md | 3 +- asconfig.json | 6 + assembly/arguments.ts | 57 +++--- assembly/instructions-exe.ts | 285 ++++++++++++++------------- assembly/instructions.ts | 283 +++++++++++++------------- assembly/instructions/bit.test.ts | 201 +++++++++++++++++++ assembly/instructions/bit.ts | 62 ++++++ assembly/instructions/branch.test.ts | 30 +++ assembly/instructions/logic.test.ts | 73 +++++++ assembly/instructions/logic.ts | 156 ++------------- assembly/instructions/math.test.ts | 100 ++++++++++ assembly/instructions/math.ts | 36 +++- assembly/instructions/rot.test.ts | 92 +++++++++ assembly/instructions/rot.ts | 69 +++++++ assembly/instructions/shift.ts | 140 +++++++++++++ assembly/instructions/store.ts | 2 +- assembly/math.ts | 0 assembly/registers.ts | 4 + assembly/test-run.ts | 50 +++++ assembly/test.ts | 35 ++++ bin/test.js | 5 + package.json | 4 +- 23 files changed, 1285 insertions(+), 449 deletions(-) create mode 100644 assembly/instructions/bit.test.ts create mode 100644 assembly/instructions/bit.ts create mode 100644 assembly/instructions/branch.test.ts create mode 100644 assembly/instructions/logic.test.ts create mode 100644 assembly/instructions/math.test.ts create mode 100644 assembly/instructions/rot.test.ts create mode 100644 assembly/instructions/rot.ts create mode 100644 assembly/instructions/shift.ts create mode 100644 assembly/math.ts create mode 100644 assembly/test-run.ts create mode 100644 assembly/test.ts create mode 100644 bin/test.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 77f0546..f2ceabf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,3 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - name: Node.js CI on: @@ -9,31 +6,41 @@ on: pull_request: branches: [ "main" ] +env: + NODE_VERSION: 22.x + jobs: - build: + jamtestvectors: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [22.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - - run: npm run qa - - run: npm run build --if-present - - run: npm test --if-present - + - run: npm run build - name: Checkout JAM test vectors uses: actions/checkout@v4 with: repository: FluffyLabs/jamtestvectors path: "./jamtestvectors" - ref: 0746da541814a6e1bb5da0ece4b1d249a10e5a13 # TODO Temporary 64-bit + ref: 747c2525145d2ed720c7168e3afa8582f66d00c2 # GP0.5.4 - run: npm start ./jamtestvectors/pvm/programs/*.json + + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - run: npm ci + - run: npm run qa + - run: npm run build + - run: npm test --if-present + + diff --git a/README.md b/README.md index e5b3043..599e242 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ Assembly Script implementation of the JAM PVM (32bit). - [x] Memory - [x] [JAM tests](https://github.com/w3f/jamtestvectors/pull/3) compatibility -- [ ] 64-bit & new instructions ([GrayPaper v0.5.0](https://graypaper.fluffylabs.dev)) +- [x] 64-bit & new instructions ([GrayPaper v0.5.0](https://graypaper.fluffylabs.dev)) +- [x] GP 0.5.4 compatibility (ZBB extensions) ### Why? diff --git a/asconfig.json b/asconfig.json index b0711e8..4743f34 100644 --- a/asconfig.json +++ b/asconfig.json @@ -14,6 +14,12 @@ "shrinkLevel": 0, "converge": false, "noAssert": false + }, + "test": { + "outFile": "build/test.wasm", + "textFile": "build/test.wat", + "sourceMap": true, + "debug": true } }, "options": { diff --git a/assembly/arguments.ts b/assembly/arguments.ts index 898162c..a472955 100644 --- a/assembly/arguments.ts +++ b/assembly/arguments.ts @@ -21,21 +21,34 @@ export const REQUIRED_BYTES = [0, 0, 1, 0, 1, 9, 1, 1, 1, 1, 1, 2, 2]; // @unmanaged export class Args { + static from(a: u32, b: u32 = 0, c: u32 = 0, d: u32 = 0): Args { + const x = new Args(); + x.a = a; + x.b = b; + x.c = c; + x.d = d; + return x; + } + + /** + * TwoReg: `omega_A` + * TwoRegOneOff: `omega_B` + * ThreeReg: `omega_B` + */ a: u32 = 0; + /** + * TwoReg: `omega'_D` + * TwoRegOneOff: `omega'_A` + * ThreeReg: `omega_A` + */ b: u32 = 0; + /** + * ThreeReg: `omega'_D` + */ c: u32 = 0; d: u32 = 0; } -function asArgs(a: u32, b: u32, c: u32, d: u32): Args { - const x = new Args(); - x.a = a; - x.b = b; - x.c = c; - x.d = d; - return x; -} - type ArgsDecoder = (data: Uint8Array, immLimit: u32) => Args; function twoImm(data: Uint8Array, lim: u32): Args { @@ -43,34 +56,34 @@ function twoImm(data: Uint8Array, lim: u32): Args { const split = Math.min(4, n.low) + 1; const first = decodeI32(data, 1, split); const second = decodeI32(data, split, lim); - return asArgs(first, second, 0, 0); + return Args.from(first, second, 0, 0); } export const DECODERS: ArgsDecoder[] = [ // DECODERS[Arguments.Zero] = (_d, _l) => { - return asArgs(0, 0, 0, 0); + return Args.from(0, 0, 0, 0); }, // DECODERS[Arguments.OneImm] = (data, lim) => { - return asArgs(decodeI32(data, 0, lim), 0, 0, 0); + return Args.from(decodeI32(data, 0, lim), 0, 0, 0); }, // DECODERS[Arguments.TwoImm] = (data, lim) => twoImm(data, lim), // DECODERS[Arguments.OneOff] = (data, lim) => { - return asArgs(decodeI32(data, 0, lim), 0, 0, 0); + return Args.from(decodeI32(data, 0, lim), 0, 0, 0); }, // DECODERS[Arguments.OneRegOneImm] = (data, lim) => { - return asArgs(nibbles(data[0]).low, decodeI32(data, 1, lim), 0, 0); + return Args.from(nibbles(data[0]).low, decodeI32(data, 1, lim), 0, 0); }, // DECODERS[Arguments.OneRegOneExtImm] = (data, _lim) => { const a = nibbles(data[0]).low; const b = decodeU32(data.subarray(1)); const c = decodeU32(data.subarray(5)); - return asArgs(a, b, c, 0); + return Args.from(a, b, c, 0); }, //DECODERS[Arguments.OneRegTwoImm] = (data, lim) => { @@ -78,7 +91,7 @@ export const DECODERS: ArgsDecoder[] = [ const split = Math.min(4, n.hig) + 1; const immA = decodeI32(data, 1, split); const immB = decodeI32(data, split, lim); - return asArgs(n.low, immA, immB, 0); + return Args.from(n.low, immA, immB, 0); }, // DECODERS[Arguments.OneRegOneImmOneOff] = (data, lim) => { @@ -86,34 +99,34 @@ export const DECODERS: ArgsDecoder[] = [ const split = Math.min(4, n.hig) + 1; const immA = decodeI32(data, 1, split); const offs = decodeI32(data, split, lim); - return asArgs(n.low, immA, offs, 0); + return Args.from(n.low, immA, offs, 0); }, // DECODERS[Arguments.TwoReg] = (data, _lim) => { const n = nibbles(data[0]); - return asArgs(n.hig, n.low, 0, 0); + return Args.from(n.hig, n.low, 0, 0); }, // DECODERS[Arguments.TwoRegOneImm] = (data, lim) => { const n = nibbles(data[0]); - return asArgs(n.hig, n.low, decodeI32(data, 1, lim), 0); + return Args.from(n.hig, n.low, decodeI32(data, 1, lim), 0); }, // DECODERS[Arguments.TwoRegOneOff] = (data, lim) => { const n = nibbles(data[0]); - return asArgs(n.hig, n.low, decodeI32(data, 1, lim), 0); + return Args.from(n.hig, n.low, decodeI32(data, 1, lim), 0); }, // DECODERS[Arguments.TwoRegTwoImm] = (data, lim) => { const n = nibbles(data[0]); const result = twoImm(data.subarray(1), lim > 1 ? lim - 1 : 0); - return asArgs(n.hig, n.low, result.a, result.b); + return Args.from(n.hig, n.low, result.a, result.b); }, // DECODERS[Arguments.ThreeReg] = (data, _lim) => { const a = nibbles(data[0]); const b = nibbles(data[1]); - return asArgs(a.hig, a.low, b.low, 0); + return Args.from(a.hig, a.low, b.low, 0); }, ]; diff --git a/assembly/instructions-exe.ts b/assembly/instructions-exe.ts index 6b869cf..8893aa1 100644 --- a/assembly/instructions-exe.ts +++ b/assembly/instructions-exe.ts @@ -1,19 +1,21 @@ +import * as bit from "./instructions/bit"; import * as branch from "./instructions/branch"; import * as jump from "./instructions/jump"; import * as load from "./instructions/load"; import * as logic from "./instructions/logic"; import * as math from "./instructions/math"; import * as mov from "./instructions/mov"; +import * as rot from "./instructions/rot"; import * as set from "./instructions/set"; +import * as shift from "./instructions/shift"; import * as store from "./instructions/store"; import { INVALID, ecalli, fallthrough, sbrk, trap } from "./instructions/misc"; import { InstructionRun } from "./instructions/outcome"; export const RUN: InstructionRun[] = [ - // 0 - trap, - fallthrough, + /* 000 */ trap, + /* 001 */ fallthrough, INVALID, INVALID, INVALID, @@ -23,8 +25,7 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 10 - ecalli, + /* 010 */ ecalli, INVALID, INVALID, INVALID, @@ -35,8 +36,7 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 20 - load.load_imm_64, + /* 020 */ load.load_imm_64, INVALID, INVALID, INVALID, @@ -47,11 +47,10 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 30 - store.store_imm_u8, - store.store_imm_u16, - store.store_imm_u32, - store.store_imm_u64, + /* 030 */ store.store_imm_u8, + /* 031 */ store.store_imm_u16, + /* 032 */ store.store_imm_u32, + /* 033 */ store.store_imm_u64, INVALID, INVALID, INVALID, @@ -59,8 +58,7 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 40 - jump.jump, + /* 040 */ jump.jump, INVALID, INVALID, INVALID, @@ -71,22 +69,20 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 50 - jump.jump_ind, - load.load_imm, - load.load_u8, - load.load_i8, - load.load_u16, - load.load_i16, - load.load_u32, - load.load_i32, - load.load_u64, - store.store_u8, + /* 050 */ jump.jump_ind, + /* 051 */ load.load_imm, + /* 052 */ load.load_u8, + /* 053 */ load.load_i8, + /* 054 */ load.load_u16, + /* 055 */ load.load_i16, + /* 056 */ load.load_u32, + /* 057 */ load.load_i32, + /* 058 */ load.load_u64, + /* 059 */ store.store_u8, - // 60 - store.store_u16, - store.store_u32, - store.store_u64, + /* 060 */ store.store_u16, + /* 061 */ store.store_u32, + /* 062 */ store.store_u64, INVALID, INVALID, INVALID, @@ -95,11 +91,10 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 70 - store.store_imm_ind_u8, - store.store_imm_ind_u16, - store.store_imm_ind_u32, - store.store_imm_ind_u64, + /* 070 */ store.store_imm_ind_u8, + /* 071 */ store.store_imm_ind_u16, + /* 072 */ store.store_imm_ind_u32, + /* 073 */ store.store_imm_ind_u64, INVALID, INVALID, INVALID, @@ -107,20 +102,18 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 80 - jump.load_imm_jump, - branch.branch_eq_imm, - branch.branch_ne_imm, - branch.branch_lt_u_imm, - branch.branch_le_u_imm, - branch.branch_ge_u_imm, - branch.branch_gt_u_imm, - branch.branch_lt_s_imm, - branch.branch_le_s_imm, - branch.branch_ge_s_imm, + /* 080 */ jump.load_imm_jump, + /* 081 */ branch.branch_eq_imm, + /* 082 */ branch.branch_ne_imm, + /* 083 */ branch.branch_lt_u_imm, + /* 084 */ branch.branch_le_u_imm, + /* 085 */ branch.branch_ge_u_imm, + /* 086 */ branch.branch_gt_u_imm, + /* 087 */ branch.branch_lt_s_imm, + /* 088 */ branch.branch_le_s_imm, + /* 089 */ branch.branch_ge_s_imm, - // 90 - branch.branch_gt_s_imm, + /* 090 */ branch.branch_gt_s_imm, INVALID, INVALID, INVALID, @@ -131,9 +124,19 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 100 - mov.move_reg, - sbrk, + /* 100 */ mov.move_reg, + /* 101 */ sbrk, + /* 102 */ bit.count_set_bits_64, + /* 103 */ bit.count_set_bits_32, + /* 104 */ bit.leading_zero_bits_64, + /* 105 */ bit.leading_zero_bits_32, + /* 106 */ bit.trailing_zero_bits_64, + /* 107 */ bit.trailing_zero_bits_32, + /* 108 */ bit.sign_extend_8, + /* 109 */ bit.sign_extend_16, + + /* 110 */ bit.zero_extend_16, + /* 111 */ bit.reverse_bytes, INVALID, INVALID, INVALID, @@ -143,68 +146,73 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 110 - store.store_ind_u8, - store.store_ind_u16, - store.store_ind_u32, - store.store_ind_u64, - load.load_ind_u8, - load.load_ind_i8, - load.load_ind_u16, - load.load_ind_i16, - load.load_ind_u32, - load.load_ind_i32, + /* 120 */ store.store_ind_u8, + /* 121 */ store.store_ind_u16, + /* 122 */ store.store_ind_u32, + /* 123 */ store.store_ind_u64, + /* 124 */ load.load_ind_u8, + /* 125 */ load.load_ind_i8, + /* 126 */ load.load_ind_u16, + /* 127 */ load.load_ind_i16, + /* 128 */ load.load_ind_u32, + /* 129 */ load.load_ind_i32, + + /* 130 */ load.load_ind_u64, + /* 131 */ math.add_imm_32, + /* 132 */ logic.and_imm, + /* 133 */ logic.xor_imm, + /* 134 */ logic.or_imm, + /* 135 */ math.mul_imm_32, + /* 136 */ set.set_lt_u_imm, + /* 137 */ set.set_lt_s_imm, + /* 138 */ shift.shlo_l_imm_32, + /* 139 */ shift.shlo_r_imm_32, - // 120 - load.load_ind_u64, - math.add_imm_32, - logic.and_imm, - logic.xor_imm, - logic.or_imm, - math.mul_imm_32, - set.set_lt_u_imm, - set.set_lt_s_imm, - logic.shlo_l_imm_32, - logic.shlo_r_imm_32, + /* 140 */ shift.shar_r_imm_32, + /* 141 */ math.neg_add_imm_32, + /* 142 */ set.set_gt_u_imm, + /* 143 */ set.set_gt_s_imm, + /* 144 */ shift.shlo_l_imm_alt_32, + /* 145 */ shift.shlo_r_imm_alt_32, + /* 146 */ shift.shar_r_imm_alt_32, + /* 147 */ mov.cmov_iz_imm, + /* 148 */ mov.cmov_nz_imm, + /* 149 */ math.add_imm, - // 130 - logic.shar_r_imm_32, - math.neg_add_imm_32, - set.set_gt_u_imm, - set.set_gt_s_imm, - logic.shlo_l_imm_alt_32, - logic.shlo_r_imm_alt_32, - logic.shar_r_imm_alt_32, - mov.cmov_iz_imm, - mov.cmov_nz_imm, - math.add_imm, + /* 150 */ math.mul_imm, + /* 151 */ shift.shlo_l_imm, + /* 152 */ shift.shlo_r_imm, + /* 153 */ shift.shar_r_imm, + /* 154 */ math.neg_add_imm, + /* 155 */ shift.shlo_l_imm_alt, + /* 156 */ shift.shlo_r_imm_alt, + /* 157 */ shift.shar_r_imm_alt, + /* 158 */ rot.rot_r_64_imm, + /* 159 */ rot.rot_r_64_imm_alt, - // 140 - math.mul_imm, - logic.shlo_l_imm, - logic.shlo_r_imm, - logic.shar_r_imm, - math.neg_add_imm, - logic.shlo_l_imm_alt, - logic.shlo_r_imm_alt, - logic.shar_r_imm_alt, + /* 160 */ rot.rot_r_32_imm, + /* 161 */ rot.rot_r_32_imm_alt, + INVALID, + INVALID, + INVALID, + INVALID, + INVALID, + INVALID, INVALID, INVALID, - // 150 - branch.branch_eq, - branch.branch_ne, - branch.branch_lt_u, - branch.branch_lt_s, - branch.branch_ge_u, - branch.branch_ge_s, + /* 170 */ branch.branch_eq, + /* 171 */ branch.branch_ne, + /* 172 */ branch.branch_lt_u, + /* 173 */ branch.branch_lt_s, + /* 174 */ branch.branch_ge_u, + /* 175 */ branch.branch_ge_s, INVALID, INVALID, INVALID, INVALID, - // 160 - jump.load_imm_jump_ind, + /* 180 */ jump.load_imm_jump_ind, INVALID, INVALID, INVALID, @@ -215,39 +223,48 @@ export const RUN: InstructionRun[] = [ INVALID, INVALID, - // 170 - math.add_32, - math.sub_32, - math.mul_32, - math.div_u_32, - math.div_s_32, - math.rem_u_32, - math.rem_s_32, - logic.shlo_l_32, - logic.shlo_r_32, - logic.shar_r_32, + /* 190 */ math.add_32, + /* 191 */ math.sub_32, + /* 192 */ math.mul_32, + /* 193 */ math.div_u_32, + /* 194 */ math.div_s_32, + /* 195 */ math.rem_u_32, + /* 196 */ math.rem_s_32, + /* 197 */ shift.shlo_l_32, + /* 198 */ shift.shlo_r_32, + /* 199 */ shift.shar_r_32, + + /* 200 */ math.add_64, + /* 201 */ math.sub, + /* 202 */ math.mul, + /* 203 */ math.div_u, + /* 204 */ math.div_s, + /* 205 */ math.rem_u, + /* 206 */ math.rem_s, + /* 207 */ shift.shlo_l, + /* 208 */ shift.shlo_r, + /* 209 */ shift.shar_r, - // 180 - math.add_64, - math.sub, - math.mul, - math.div_u, - math.div_s, - math.rem_u, - math.rem_s, - logic.shlo_l, - logic.shlo_r, - logic.shar_r, + /* 210 */ logic.and, + /* 211 */ logic.xor, + /* 212 */ logic.or, + /* 213 */ math.mul_upper_s_s, + /* 214 */ math.mul_upper_u_u, + /* 215 */ math.mul_upper_s_u, + /* 216 */ set.set_lt_u, + /* 217 */ set.set_lt_s, + /* 218 */ mov.cmov_iz, + /* 219 */ mov.cmov_nz, - // 190 - logic.and, - logic.xor, - logic.or, - math.mul_upper_s_s, - math.mul_upper_u_u, - math.mul_upper_s_u, - set.set_lt_u, - set.set_lt_s, - mov.cmov_iz, - mov.cmov_nz, + /* 220 */ rot.rot_l_64, + /* 221 */ rot.rot_l_32, + /* 222 */ rot.rot_r_64, + /* 223 */ rot.rot_r_32, + /* 224 */ logic.and_inv, + /* 225 */ logic.or_inv, + /* 226 */ logic.xnor, + /* 227 */ math.max, + /* 228 */ math.max_u, + /* 229 */ math.min, + /* 230 */ math.min_u, ]; diff --git a/assembly/instructions.ts b/assembly/instructions.ts index fd4e110..a5d38f7 100644 --- a/assembly/instructions.ts +++ b/assembly/instructions.ts @@ -23,9 +23,8 @@ export const MISSING_INSTRUCTION = instruction("INVALID", Arguments.Zero, 1, fal export const SBRK = instruction("SBRK", Arguments.TwoReg, 1); export const INSTRUCTIONS: Instruction[] = [ - // 0 - instruction("TRAP", Arguments.Zero, 1, true), - instruction("FALLTHROUGH", Arguments.Zero, 1, true), + /* 000 */ instruction("TRAP", Arguments.Zero, 1, true), + /* 001 */ instruction("FALLTHROUGH", Arguments.Zero, 1, true), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -35,8 +34,7 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 10 - instruction("ECALLI", Arguments.OneImm, 1), + /* 010 */ instruction("ECALLI", Arguments.OneImm, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -47,8 +45,7 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 20 - instruction("LOAD_IMM_64", Arguments.OneRegOneExtImm, 1), + /* 020 */ instruction("LOAD_IMM_64", Arguments.OneRegOneExtImm, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -59,11 +56,10 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 30 - instruction("STORE_IMM_U8", Arguments.TwoImm, 1), - instruction("STORE_IMM_U16", Arguments.TwoImm, 1), - instruction("STORE_IMM_U32", Arguments.TwoImm, 1), - instruction("STORE_IMM_U64", Arguments.TwoImm, 1), + /* 030 */ instruction("STORE_IMM_U8", Arguments.TwoImm, 1), + /* 031 */ instruction("STORE_IMM_U16", Arguments.TwoImm, 1), + /* 032 */ instruction("STORE_IMM_U32", Arguments.TwoImm, 1), + /* 033 */ instruction("STORE_IMM_U64", Arguments.TwoImm, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -71,8 +67,7 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 40 - instruction("JUMP", Arguments.OneOff, 1, true), + /* 040 */ instruction("JUMP", Arguments.OneOff, 1, true), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -83,22 +78,20 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 50 - instruction("JUMP_IND", Arguments.OneRegOneImm, 1, true), - instruction("LOAD_IMM", Arguments.OneRegOneImm, 1), - instruction("LOAD_U8", Arguments.OneRegOneImm, 1), - instruction("LOAD_I8", Arguments.OneRegOneImm, 1), - instruction("LOAD_U16", Arguments.OneRegOneImm, 1), - instruction("LOAD_I16", Arguments.OneRegOneImm, 1), - instruction("LOAD_U32", Arguments.OneRegOneImm, 1), - instruction("LOAD_I32", Arguments.OneRegOneImm, 1), - instruction("LOAD_U64", Arguments.OneRegOneImm, 1), - instruction("STORE_U8", Arguments.OneRegOneImm, 1), + /* 050 */ instruction("JUMP_IND", Arguments.OneRegOneImm, 1, true), + /* 051 */ instruction("LOAD_IMM", Arguments.OneRegOneImm, 1), + /* 052 */ instruction("LOAD_U8", Arguments.OneRegOneImm, 1), + /* 053 */ instruction("LOAD_I8", Arguments.OneRegOneImm, 1), + /* 054 */ instruction("LOAD_U16", Arguments.OneRegOneImm, 1), + /* 055 */ instruction("LOAD_I16", Arguments.OneRegOneImm, 1), + /* 056 */ instruction("LOAD_U32", Arguments.OneRegOneImm, 1), + /* 057 */ instruction("LOAD_I32", Arguments.OneRegOneImm, 1), + /* 058 */ instruction("LOAD_U64", Arguments.OneRegOneImm, 1), + /* 059 */ instruction("STORE_U8", Arguments.OneRegOneImm, 1), - // 60 - instruction("STORE_U16", Arguments.OneRegOneImm, 1), - instruction("STORE_U32", Arguments.OneRegOneImm, 1), - instruction("STORE_U64", Arguments.OneRegOneImm, 1), + /* 060 */ instruction("STORE_U16", Arguments.OneRegOneImm, 1), + /* 061 */ instruction("STORE_U32", Arguments.OneRegOneImm, 1), + /* 062 */ instruction("STORE_U64", Arguments.OneRegOneImm, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -107,11 +100,10 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 70 - instruction("STORE_IMM_IND_U8", Arguments.OneRegTwoImm, 1), - instruction("STORE_IMM_IND_U16", Arguments.OneRegTwoImm, 1), - instruction("STORE_IMM_IND_U32", Arguments.OneRegTwoImm, 1), - instruction("STORE_IMM_IND_U64", Arguments.OneRegTwoImm, 1), + /* 070 */ instruction("STORE_IMM_IND_U8", Arguments.OneRegTwoImm, 1), + /* 071 */ instruction("STORE_IMM_IND_U16", Arguments.OneRegTwoImm, 1), + /* 072 */ instruction("STORE_IMM_IND_U32", Arguments.OneRegTwoImm, 1), + /* 073 */ instruction("STORE_IMM_IND_U64", Arguments.OneRegTwoImm, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -119,20 +111,18 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 80 - instruction("LOAD_IMM_JUMP", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_EQ_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_NE_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_LT_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_LE_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_GE_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_GT_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_LT_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_LE_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), - instruction("BRANCH_GE_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 080 */ instruction("LOAD_IMM_JUMP", Arguments.OneRegOneImmOneOff, 1, true), + /* 081 */ instruction("BRANCH_EQ_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 082 */ instruction("BRANCH_NE_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 083 */ instruction("BRANCH_LT_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 084 */ instruction("BRANCH_LE_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 085 */ instruction("BRANCH_GE_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 086 */ instruction("BRANCH_GT_U_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 087 */ instruction("BRANCH_LT_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 088 */ instruction("BRANCH_LE_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 089 */ instruction("BRANCH_GE_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), - // 90 - instruction("BRANCH_GT_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), + /* 090 */ instruction("BRANCH_GT_S_IMM", Arguments.OneRegOneImmOneOff, 1, true), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -143,9 +133,19 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 100 - instruction("MOVE_REG", Arguments.TwoReg, 1), - SBRK, + /* 100 */ instruction("MOVE_REG", Arguments.TwoReg, 1), + /* 101 */ SBRK, + /* 102 */ instruction("COUNT_SET_BITS_64", Arguments.TwoReg, 1), + /* 103 */ instruction("COUNT_SET_BITS_32", Arguments.TwoReg, 1), + /* 104 */ instruction("LEADING_ZERO_BITS_64", Arguments.TwoReg, 1), + /* 105 */ instruction("LEADING_ZERO_BITS_32", Arguments.TwoReg, 1), + /* 106 */ instruction("TRAILING_ZERO_BITS_64", Arguments.TwoReg, 1), + /* 107 */ instruction("TRAILING_ZERO_BITS_32", Arguments.TwoReg, 1), + /* 108 */ instruction("SIGN_EXTEND_8", Arguments.TwoReg, 1), + /* 109 */ instruction("SIGN_EXTEND_16", Arguments.TwoReg, 1), + + /* 110 */ instruction("ZERO_EXTEND_16", Arguments.TwoReg, 1), + /* 111 */ instruction("REVERSE_BYTES", Arguments.TwoReg, 1), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -155,68 +155,73 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 110 - instruction("STORE_IND_U8", Arguments.TwoRegOneImm, 1), - instruction("STORE_IND_U16", Arguments.TwoRegOneImm, 1), - instruction("STORE_IND_U32", Arguments.TwoRegOneImm, 1), - instruction("STORE_IND_U64", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_U8", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_I8", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_U16", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_I16", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_U32", Arguments.TwoRegOneImm, 1), - instruction("LOAD_IND_I32", Arguments.TwoRegOneImm, 1), + /* 120 */ instruction("STORE_IND_U8", Arguments.TwoRegOneImm, 1), + /* 121 */ instruction("STORE_IND_U16", Arguments.TwoRegOneImm, 1), + /* 122 */ instruction("STORE_IND_U32", Arguments.TwoRegOneImm, 1), + /* 123 */ instruction("STORE_IND_U64", Arguments.TwoRegOneImm, 1), + /* 124 */ instruction("LOAD_IND_U8", Arguments.TwoRegOneImm, 1), + /* 125 */ instruction("LOAD_IND_I8", Arguments.TwoRegOneImm, 1), + /* 126 */ instruction("LOAD_IND_U16", Arguments.TwoRegOneImm, 1), + /* 127 */ instruction("LOAD_IND_I16", Arguments.TwoRegOneImm, 1), + /* 128 */ instruction("LOAD_IND_U32", Arguments.TwoRegOneImm, 1), + /* 129 */ instruction("LOAD_IND_I32", Arguments.TwoRegOneImm, 1), + + /* 130 */ instruction("LOAD_IND_U64", Arguments.TwoRegOneImm, 1), + /* 131 */ instruction("ADD_IMM_32", Arguments.TwoRegOneImm, 1), + /* 132 */ instruction("AND_IMM", Arguments.TwoRegOneImm, 1), + /* 133 */ instruction("XOR_IMM", Arguments.TwoRegOneImm, 1), + /* 134 */ instruction("OR_IMM", Arguments.TwoRegOneImm, 1), + /* 135 */ instruction("MUL_IMM_32", Arguments.TwoRegOneImm, 1), + /* 136 */ instruction("SET_LT_U_IMM", Arguments.TwoRegOneImm, 1), + /* 137 */ instruction("SET_LT_S_IMM", Arguments.TwoRegOneImm, 1), + /* 138 */ instruction("SHLO_L_IMM_32", Arguments.TwoRegOneImm, 1), + /* 139 */ instruction("SHLO_R_IMM_32", Arguments.TwoRegOneImm, 1), - // 120 - instruction("LOAD_IND_U64", Arguments.TwoRegOneImm, 1), - instruction("ADD_IMM_32", Arguments.TwoRegOneImm, 1), - instruction("AND_IMM", Arguments.TwoRegOneImm, 1), - instruction("XOR_IMM", Arguments.TwoRegOneImm, 1), - instruction("OR_IMM", Arguments.TwoRegOneImm, 1), - instruction("MUL_IMM_32", Arguments.TwoRegOneImm, 1), - instruction("SET_LT_U_IMM", Arguments.TwoRegOneImm, 1), - instruction("SET_LT_S_IMM", Arguments.TwoRegOneImm, 1), - instruction("SHLO_L_IMM_32", Arguments.TwoRegOneImm, 1), - instruction("SHLO_R_IMM_32", Arguments.TwoRegOneImm, 1), + /* 140 */ instruction("SHAR_R_IMM_32", Arguments.TwoRegOneImm, 1), + /* 141 */ instruction("NEG_ADD_IMM_32", Arguments.TwoRegOneImm, 1), + /* 142 */ instruction("SET_GT_U_IMM", Arguments.TwoRegOneImm, 1), + /* 143 */ instruction("SET_GT_S_IMM", Arguments.TwoRegOneImm, 1), + /* 144 */ instruction("SHLO_L_IMM_ALT_32", Arguments.TwoRegOneImm, 1), + /* 145 */ instruction("SHLO_R_IMM_ALT_32", Arguments.TwoRegOneImm, 1), + /* 146 */ instruction("SHAR_R_IMM_ALT_32", Arguments.TwoRegOneImm, 1), + /* 147 */ instruction("CMOV_IZ_IMM", Arguments.TwoRegOneImm, 1), + /* 148 */ instruction("CMOV_NZ_IMM", Arguments.TwoRegOneImm, 1), + /* 149 */ instruction("ADD_IMM_64", Arguments.TwoRegOneImm, 1), - // 130 - instruction("SHAR_R_IMM_32", Arguments.TwoRegOneImm, 1), - instruction("NEG_ADD_IMM_32", Arguments.TwoRegOneImm, 1), - instruction("SET_GT_U_IMM", Arguments.TwoRegOneImm, 1), - instruction("SET_GT_S_IMM", Arguments.TwoRegOneImm, 1), - instruction("SHLO_L_IMM_ALT_32", Arguments.TwoRegOneImm, 1), - instruction("SHLO_R_IMM_ALT_32", Arguments.TwoRegOneImm, 1), - instruction("SHAR_R_IMM_ALT_32", Arguments.TwoRegOneImm, 1), - instruction("CMOV_IZ_IMM", Arguments.TwoRegOneImm, 1), - instruction("CMOV_NZ_IMM", Arguments.TwoRegOneImm, 1), - instruction("ADD_IMM_64", Arguments.TwoRegOneImm, 1), + /* 150 */ instruction("MUL_IMM_64", Arguments.TwoRegOneImm, 1), + /* 151 */ instruction("SHLO_L_IMM_64", Arguments.TwoRegOneImm, 1), + /* 152 */ instruction("SHLO_R_IMM_64", Arguments.TwoRegOneImm, 1), + /* 153 */ instruction("SHAR_R_IMM_64", Arguments.TwoRegOneImm, 1), + /* 154 */ instruction("NEG_ADD_IMM_64", Arguments.TwoRegOneImm, 1), + /* 155 */ instruction("SHLO_L_IMM_ALT_64", Arguments.TwoRegOneImm, 1), + /* 156 */ instruction("SHLO_R_IMM_ALT_64", Arguments.TwoRegOneImm, 1), + /* 157 */ instruction("SHAR_R_IMM_ALT_64", Arguments.TwoRegOneImm, 1), + /* 158 */ instruction("ROT_R_64_IMM", Arguments.TwoRegOneImm, 1), + /* 159 */ instruction("ROT_R_64_IMM_ALT", Arguments.TwoRegOneImm, 1), - // 140 - instruction("MUL_IMM_64", Arguments.TwoRegOneImm, 1), - instruction("SHLO_L_IMM_64", Arguments.TwoRegOneImm, 1), - instruction("SHLO_R_IMM_64", Arguments.TwoRegOneImm, 1), - instruction("SHAR_R_IMM_64", Arguments.TwoRegOneImm, 1), - instruction("NEG_ADD_IMM_64", Arguments.TwoRegOneImm, 1), - instruction("SHLO_L_IMM_ALT_64", Arguments.TwoRegOneImm, 1), - instruction("SHLO_R_IMM_ALT_64", Arguments.TwoRegOneImm, 1), - instruction("SHAR_R_IMM_ALT_64", Arguments.TwoRegOneImm, 1), + /* 160 */ instruction("ROT_R_32_IMM", Arguments.TwoRegOneImm, 1), + /* 161 */ instruction("ROT_R_32_IMM_ALT", Arguments.TwoRegOneImm, 1), + MISSING_INSTRUCTION, + MISSING_INSTRUCTION, + MISSING_INSTRUCTION, + MISSING_INSTRUCTION, + MISSING_INSTRUCTION, + MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 150 - instruction("BRANCH_EQ", Arguments.TwoRegOneOff, 1, true), - instruction("BRANCH_NE", Arguments.TwoRegOneOff, 1, true), - instruction("BRANCH_LT_U", Arguments.TwoRegOneOff, 1, true), - instruction("BRANCH_LT_S", Arguments.TwoRegOneOff, 1, true), - instruction("BRANCH_GE_U", Arguments.TwoRegOneOff, 1, true), - instruction("BRANCH_GE_S", Arguments.TwoRegOneOff, 1, true), + /* 170 */ instruction("BRANCH_EQ", Arguments.TwoRegOneOff, 1, true), + /* 171 */ instruction("BRANCH_NE", Arguments.TwoRegOneOff, 1, true), + /* 172 */ instruction("BRANCH_LT_U", Arguments.TwoRegOneOff, 1, true), + /* 173 */ instruction("BRANCH_LT_S", Arguments.TwoRegOneOff, 1, true), + /* 174 */ instruction("BRANCH_GE_U", Arguments.TwoRegOneOff, 1, true), + /* 175 */ instruction("BRANCH_GE_S", Arguments.TwoRegOneOff, 1, true), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 160 - instruction("LOAD_IMM_JUMP_IND", Arguments.TwoRegTwoImm, 1, true), + /* 180 */ instruction("LOAD_IMM_JUMP_IND", Arguments.TwoRegTwoImm, 1, true), MISSING_INSTRUCTION, MISSING_INSTRUCTION, MISSING_INSTRUCTION, @@ -227,39 +232,49 @@ export const INSTRUCTIONS: Instruction[] = [ MISSING_INSTRUCTION, MISSING_INSTRUCTION, - // 170 - instruction("ADD_32", Arguments.ThreeReg, 1), - instruction("SUB_32", Arguments.ThreeReg, 1), - instruction("MUL_32", Arguments.ThreeReg, 1), - instruction("DIV_U_32", Arguments.ThreeReg, 1), - instruction("DIV_S_32", Arguments.ThreeReg, 1), - instruction("REM_U_32", Arguments.ThreeReg, 1), - instruction("REM_S_32", Arguments.ThreeReg, 1), - instruction("SHLO_L_32", Arguments.ThreeReg, 1), - instruction("SHLO_R_32", Arguments.ThreeReg, 1), - instruction("SHAR_R_32", Arguments.ThreeReg, 1), + /* 190 */ instruction("ADD_32", Arguments.ThreeReg, 1), + /* 191 */ instruction("SUB_32", Arguments.ThreeReg, 1), + /* 192 */ instruction("MUL_32", Arguments.ThreeReg, 1), + /* 193 */ instruction("DIV_U_32", Arguments.ThreeReg, 1), + /* 194 */ instruction("DIV_S_32", Arguments.ThreeReg, 1), + /* 195 */ instruction("REM_U_32", Arguments.ThreeReg, 1), + /* 196 */ instruction("REM_S_32", Arguments.ThreeReg, 1), + /* 197 */ instruction("SHLO_L_32", Arguments.ThreeReg, 1), + /* 198 */ instruction("SHLO_R_32", Arguments.ThreeReg, 1), + /* 199 */ instruction("SHAR_R_32", Arguments.ThreeReg, 1), + + /* 200 */ instruction("ADD_64", Arguments.ThreeReg, 1), + /* 201 */ instruction("SUB_64", Arguments.ThreeReg, 1), + /* 202 */ instruction("MUL_64", Arguments.ThreeReg, 1), + /* 203 */ instruction("DIV_U_64", Arguments.ThreeReg, 1), + /* 204 */ instruction("DIV_S_64", Arguments.ThreeReg, 1), + /* 205 */ instruction("REM_U_64", Arguments.ThreeReg, 1), + /* 206 */ instruction("REM_S_64", Arguments.ThreeReg, 1), + /* 207 */ instruction("SHLO_L_64", Arguments.ThreeReg, 1), + /* 208 */ instruction("SHLO_R_64", Arguments.ThreeReg, 1), + /* 209 */ instruction("SHAR_R_64", Arguments.ThreeReg, 1), + + /* 210 */ instruction("AND", Arguments.ThreeReg, 1), + /* 211 */ instruction("XOR", Arguments.ThreeReg, 1), + /* 212 */ instruction("OR", Arguments.ThreeReg, 1), + /* 213 */ instruction("MUL_UPPER_S_S", Arguments.ThreeReg, 1), + /* 214 */ instruction("MUL_UPPER_U_U", Arguments.ThreeReg, 1), + /* 215 */ instruction("MUL_UPPER_S_U", Arguments.ThreeReg, 1), + /* 216 */ instruction("SET_LT_U", Arguments.ThreeReg, 1), + /* 217 */ instruction("SET_LT_S", Arguments.ThreeReg, 1), + /* 218 */ instruction("CMOV_IZ", Arguments.ThreeReg, 1), + /* 219 */ instruction("CMOV_NZ", Arguments.ThreeReg, 1), - // 180 - instruction("ADD_64", Arguments.ThreeReg, 1), - instruction("SUB_64", Arguments.ThreeReg, 1), - instruction("MUL_64", Arguments.ThreeReg, 1), - instruction("DIV_U_64", Arguments.ThreeReg, 1), - instruction("DIV_S_64", Arguments.ThreeReg, 1), - instruction("REM_U_64", Arguments.ThreeReg, 1), - instruction("REM_S_64", Arguments.ThreeReg, 1), - instruction("SHLO_L_64", Arguments.ThreeReg, 1), - instruction("SHLO_R_64", Arguments.ThreeReg, 1), - instruction("SHAR_R_64", Arguments.ThreeReg, 1), + /* 220 */ instruction("ROT_L_64", Arguments.ThreeReg, 1), + /* 221 */ instruction("ROT_L_32", Arguments.ThreeReg, 1), + /* 222 */ instruction("ROT_R_64", Arguments.ThreeReg, 1), + /* 223 */ instruction("ROT_R_32", Arguments.ThreeReg, 1), + /* 224 */ instruction("AND_INV", Arguments.ThreeReg, 1), + /* 225 */ instruction("OR_INV", Arguments.ThreeReg, 1), + /* 226 */ instruction("XNOR", Arguments.ThreeReg, 1), + /* 227 */ instruction("MAX", Arguments.ThreeReg, 1), + /* 228 */ instruction("MAX_U", Arguments.ThreeReg, 1), + /* 229 */ instruction("MIN", Arguments.ThreeReg, 1), - // 190 - instruction("AND", Arguments.ThreeReg, 1), - instruction("XOR", Arguments.ThreeReg, 1), - instruction("OR", Arguments.ThreeReg, 1), - instruction("MUL_UPPER_S_S", Arguments.ThreeReg, 1), - instruction("MUL_UPPER_U_U", Arguments.ThreeReg, 1), - instruction("MUL_UPPER_S_U", Arguments.ThreeReg, 1), - instruction("SET_LT_U", Arguments.ThreeReg, 1), - instruction("SET_LT_S", Arguments.ThreeReg, 1), - instruction("CMOV_IZ", Arguments.ThreeReg, 1), - instruction("CMOV_NZ", Arguments.ThreeReg, 1), + /* 230 */ instruction("MIN_U", Arguments.ThreeReg, 1), ]; diff --git a/assembly/instructions/bit.test.ts b/assembly/instructions/bit.test.ts new file mode 100644 index 0000000..f497aaf --- /dev/null +++ b/assembly/instructions/bit.test.ts @@ -0,0 +1,201 @@ +import { Args } from "../arguments"; +import { MemoryBuilder } from "../memory"; +import { newRegisters } from "../registers"; +import { Assert, Test, test } from "../test"; +import { + count_set_bits_32, + count_set_bits_64, + leading_zero_bits_32, + leading_zero_bits_64, + reverse_bytes, + sign_extend_8, + sign_extend_16, + trailing_zero_bits_32, + trailing_zero_bits_64, + zero_extend_16, +} from "./bit"; +import { Outcome } from "./outcome"; +import { reg } from "./utils"; + +export const TESTS: Test[] = [ + test("count_set_bits_64", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xffff_0000_1111; + const memo = new MemoryBuilder().build(0); + + // when + const res = count_set_bits_64(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 2 * 8 + 4); + return assert; + }), + test("count_set_bits_32", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xffff_0000_1111; + const memo = new MemoryBuilder().build(0); + + // when + const res = count_set_bits_32(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 4); + return assert; + }), + test("leading_zero_bits_64", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xfff0_0000_0111; + const memo = new MemoryBuilder().build(0); + + // when + const res = leading_zero_bits_64(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 16); + return assert; + }), + test("leading_zero_bits_32", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xffff_0000_1111; + const memo = new MemoryBuilder().build(0); + + // when + const res = leading_zero_bits_32(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 19); + return assert; + }), + test("trailing_zero_bits_64", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xfff0_0000_0000; + const memo = new MemoryBuilder().build(0); + + // when + const res = trailing_zero_bits_64(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 36); + return assert; + }), + test("trailing_zero_bits_32", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xfff0_0000_0000; + const memo = new MemoryBuilder().build(0); + + // when + const res = trailing_zero_bits_32(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 32); + return assert; + }), + test("sign_extend_8", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xdead_beef; + const memo = new MemoryBuilder().build(0); + + // when + const res = sign_extend_8(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 0xffff_ffff_ffff_ffef); + return assert; + }), + test("sign_extend_16", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xdead_beef; + const memo = new MemoryBuilder().build(0); + + // when + const res = sign_extend_16(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 0xffff_ffff_ffff_beef); + return assert; + }), + test("zero_extend_16", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xdead_beef; + const memo = new MemoryBuilder().build(0); + + // when + const res = zero_extend_16(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 0x0000_beef); + return assert; + }), + test("reverse_bytes", () => { + // when + const args = new Args(); + args.a = 0x1; + args.b = 0xf; + const regs = newRegisters(); + regs[reg(args.a)] = 0xfff0_dead_beef; + const memo = new MemoryBuilder().build(0); + + // when + const res = reverse_bytes(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(res.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(0xf)], 0xefbe_adde_f0ff_0000); + return assert; + }), +]; diff --git a/assembly/instructions/bit.ts b/assembly/instructions/bit.ts new file mode 100644 index 0000000..5137d70 --- /dev/null +++ b/assembly/instructions/bit.ts @@ -0,0 +1,62 @@ +import { InstructionRun, ok } from "./outcome"; +import { reg, u8SignExtend, u16SignExtend } from "./utils"; + +// COUNT_SET_BITS_64 +export const count_set_bits_64: InstructionRun = (args, regs) => { + regs[reg(args.b)] = popcnt(regs[reg(args.a)]); + return ok(); +}; + +// COUNT_SET_BITS_32 +export const count_set_bits_32: InstructionRun = (args, regs) => { + regs[reg(args.b)] = popcnt(u32(regs[reg(args.a)])); + return ok(); +}; + +// LEADING_ZERO_BITS_64 +export const leading_zero_bits_64: InstructionRun = (args, regs) => { + regs[reg(args.b)] = clz(regs[reg(args.a)]); + return ok(); +}; + +// LEADING_ZERO_BITS_32 +export const leading_zero_bits_32: InstructionRun = (args, regs) => { + regs[reg(args.b)] = clz(u32(regs[reg(args.a)])); + return ok(); +}; + +// TRAILING_ZERO_BITS_64 +export const trailing_zero_bits_64: InstructionRun = (args, regs) => { + regs[reg(args.b)] = ctz(regs[reg(args.a)]); + return ok(); +}; + +// TRAILING_ZERO_BITS_32 +export const trailing_zero_bits_32: InstructionRun = (args, regs) => { + regs[reg(args.b)] = ctz(u32(regs[reg(args.a)])); + return ok(); +}; + +// SIGN_EXTEND_8 +export const sign_extend_8: InstructionRun = (args, regs) => { + regs[reg(args.b)] = u8SignExtend(u8(regs[reg(args.a)])); + return ok(); +}; + +// SIGN_EXTEND_16 +export const sign_extend_16: InstructionRun = (args, regs) => { + regs[reg(args.b)] = u16SignExtend(u16(regs[reg(args.a)])); + return ok(); +}; + +// ZERO_EXTEND_16 +export const zero_extend_16: InstructionRun = (args, regs) => { + regs[reg(args.b)] = u64(u16(regs[reg(args.a)])); + return ok(); +}; + +// REVERSE_BYTES +export const reverse_bytes: InstructionRun = (args, regs) => { + regs[reg(args.b)] = bswap(regs[reg(args.a)]); + return ok(); +}; diff --git a/assembly/instructions/branch.test.ts b/assembly/instructions/branch.test.ts new file mode 100644 index 0000000..6f80a63 --- /dev/null +++ b/assembly/instructions/branch.test.ts @@ -0,0 +1,30 @@ +import { Args } from "../arguments"; +import { MemoryBuilder } from "../memory"; +import { newRegisters } from "../registers"; +import { Assert, Test, test } from "../test"; +import { branch_eq_imm } from "./branch"; +import { Outcome } from "./outcome"; +import { reg } from "./utils"; + +export const TESTS: Test[] = [ + test("branch_eq_imm", () => { + // when + const args = new Args(); + args.a = 0; + args.b = 0xfe; + args.c = 0xdeadbeef; + const regs = newRegisters(); + regs[reg(args.a)] = 0xfe; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = branch_eq_imm(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.StaticJump, "outcome"); + assert.isEqual(ret.staticJump, 0xdeadbeef, "staticJump"); + return assert; + }), +]; diff --git a/assembly/instructions/logic.test.ts b/assembly/instructions/logic.test.ts new file mode 100644 index 0000000..1852e7e --- /dev/null +++ b/assembly/instructions/logic.test.ts @@ -0,0 +1,73 @@ +import { Args } from "../arguments"; +import { MemoryBuilder } from "../memory"; +import { newRegisters } from "../registers"; +import { Assert, Test, test } from "../test"; +import { and_inv, or_inv, xnor } from "./logic"; +import { Outcome } from "./outcome"; +import { reg } from "./utils"; + +export const TESTS: Test[] = [ + test("and_inv", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x3; + const regs = newRegisters(); + regs[reg(args.a)] = 0x0000_0000_000f; + regs[reg(args.b)] = 0xf000_0000_0001; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = and_inv(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0x0000_f000_0000_0000); + return assert; + }), + test("or_inv", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x3; + const regs = newRegisters(); + regs[reg(args.a)] = 0x0000_0000_000f; + regs[reg(args.b)] = 0xf000_0000_0001; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = or_inv(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0xffff_ffff_ffff_fff1); + return assert; + }), + test("xnor", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x3; + const regs = newRegisters(); + regs[reg(args.a)] = 0x0000_0000_000f; + regs[reg(args.b)] = 0xf000_0000_0000; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = xnor(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0xffff_0fff_ffff_fff0); + return assert; + }), +]; diff --git a/assembly/instructions/logic.ts b/assembly/instructions/logic.ts index 59d009c..af286c9 100644 --- a/assembly/instructions/logic.ts +++ b/assembly/instructions/logic.ts @@ -1,9 +1,6 @@ import { InstructionRun, ok } from "./outcome"; import { reg, u32SignExtend } from "./utils"; -const MAX_SHIFT_64 = 64; -const MAX_SHIFT_32 = 32; - // AND_IMM export const and_imm: InstructionRun = (args, registers) => { registers[reg(args.b)] = registers[reg(args.a)] & u32SignExtend(args.c); @@ -22,155 +19,38 @@ export const or_imm: InstructionRun = (args, registers) => { return ok(); }; -// SHLO_L_IMM_32 -export const shlo_l_imm_32: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_32); - const value = u32(registers[reg(args.a)]); - registers[reg(args.b)] = u32SignExtend(value << shift); - return ok(); -}; - -// SHLO_R_IMM_32 -export const shlo_r_imm_32: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_32); - const value = u32(registers[reg(args.a)]); - registers[reg(args.b)] = u32SignExtend(value >>> shift); - return ok(); -}; - -// SHAR_R_IMM_32 -export const shar_r_imm_32: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_32); - const value = u32SignExtend(u32(registers[reg(args.a)])); - registers[reg(args.b)] = value >> shift; - return ok(); -}; - -// SHLO_L_IMM_ALT_32 -export const shlo_l_imm_alt_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - registers[reg(args.b)] = u32SignExtend(args.c << shift); - return ok(); -}; - -// SHLO_R_IMM_ALT_32 -export const shlo_r_imm_alt_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - registers[reg(args.b)] = u32SignExtend(args.c >>> shift); - return ok(); -}; - -// SHAR_R_IMM_ALT_32 -export const shar_r_imm_alt_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - const imm = u32SignExtend(args.c); - registers[reg(args.b)] = u32SignExtend(u32(imm >> shift)); - return ok(); -}; - -// SHLO_L_IMM -export const shlo_l_imm: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_64); - registers[reg(args.b)] = registers[reg(args.a)] << shift; - return ok(); -}; - -// SHLO_R_IMM -export const shlo_r_imm: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_64); - registers[reg(args.b)] = registers[reg(args.a)] >>> shift; - return ok(); -}; - -// SHAR_R_IMM -export const shar_r_imm: InstructionRun = (args, registers) => { - const shift = u32(args.c % MAX_SHIFT_64); - const value = i64(registers[reg(args.a)]); - registers[reg(args.b)] = value >> shift; - return ok(); -}; - -// SHLO_L_IMM_ALT -export const shlo_l_imm_alt: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - registers[reg(args.b)] = u32SignExtend(args.c) << shift; - return ok(); -}; - -// SHLO_R_IMM_ALT -export const shlo_r_imm_alt: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - registers[reg(args.b)] = u32SignExtend(args.c) >>> shift; - return ok(); -}; - -// SHAR_R_IMM_ALT -export const shar_r_imm_alt: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - const value = u32SignExtend(args.c); - registers[reg(args.b)] = u32SignExtend(u32(value >> shift)); - return ok(); -}; - -// SHLO_L_32 -export const shlo_l_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - const value = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(value << shift); - return ok(); -}; - -// SHLO_R_32 -export const shlo_r_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - const value = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(value >>> shift); - return ok(); -}; - -// SHAR_R_32 -export const shar_r_32: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); - const regValue = u32SignExtend(u32(registers[reg(args.b)])); - registers[reg(args.c)] = u32SignExtend(u32(regValue >> shift)); - return ok(); -}; - -// SHLO_L -export const shlo_l: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - registers[reg(args.c)] = registers[reg(args.b)] << shift; +// AND +export const and: InstructionRun = (args, registers) => { + registers[reg(args.c)] = registers[reg(args.b)] & registers[reg(args.a)]; return ok(); }; -// SHLO_R -export const shlo_r: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - registers[reg(args.c)] = registers[reg(args.b)] >>> shift; +// XOR +export const xor: InstructionRun = (args, registers) => { + registers[reg(args.c)] = registers[reg(args.b)] ^ registers[reg(args.a)]; return ok(); }; -// SHAR_R -export const shar_r: InstructionRun = (args, registers) => { - const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); - registers[reg(args.c)] = i64(registers[reg(args.b)]) >> shift; +// OR +export const or: InstructionRun = (args, registers) => { + registers[reg(args.c)] = registers[reg(args.b)] | registers[reg(args.a)]; return ok(); }; -// AND -export const and: InstructionRun = (args, registers) => { - registers[reg(args.c)] = registers[reg(args.a)] & registers[reg(args.b)]; +// AND_INV +export const and_inv: InstructionRun = (args, registers) => { + registers[reg(args.c)] = registers[reg(args.b)] & ~registers[reg(args.a)]; return ok(); }; -// XOR -export const xor: InstructionRun = (args, registers) => { - registers[reg(args.c)] = registers[reg(args.a)] ^ registers[reg(args.b)]; +// OR_INV +export const or_inv: InstructionRun = (args, registers) => { + registers[reg(args.c)] = registers[reg(args.b)] | ~registers[reg(args.a)]; return ok(); }; -// OR -export const or: InstructionRun = (args, registers) => { - registers[reg(args.c)] = registers[reg(args.a)] | registers[reg(args.b)]; +// XNOR +export const xnor: InstructionRun = (args, registers) => { + registers[reg(args.c)] = ~(registers[reg(args.b)] ^ registers[reg(args.a)]); return ok(); }; diff --git a/assembly/instructions/math.test.ts b/assembly/instructions/math.test.ts new file mode 100644 index 0000000..d6980f4 --- /dev/null +++ b/assembly/instructions/math.test.ts @@ -0,0 +1,100 @@ +import { Args } from "../arguments"; +import { MemoryBuilder } from "../memory"; +import { newRegisters } from "../registers"; +import { Assert, Test, test } from "../test"; +import * as math from "./math"; +import { Outcome } from "./outcome"; +import { reg } from "./utils"; + +export const TESTS: Test[] = [ + test("max", () => { + // when + const args = Args.from(0x0, 0x1, 0x3); + const regs = newRegisters(); + regs[reg(args.a)] = -(2 ** 63); + regs[reg(args.b)] = 2; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = math.max(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 2); + return assert; + }), + test("max_u", () => { + // when + const args = Args.from(0x0, 0x1, 0x3); + const regs = newRegisters(); + regs[reg(args.a)] = -(2 ** 63); + regs[reg(args.b)] = 2; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = math.max_u(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], -(2 ** 63)); + return assert; + }), + test("min", () => { + // when + const args = Args.from(0x0, 0x1, 0x3); + const regs = newRegisters(); + regs[reg(args.a)] = -(2 ** 63); + regs[reg(args.b)] = 2; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = math.min(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], -(2 ** 63)); + return assert; + }), + test("min_u", () => { + // when + const args = Args.from(0x0, 0x1, 0x3); + const regs = newRegisters(); + regs[reg(args.a)] = -(2 ** 63); + regs[reg(args.b)] = 2; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = math.min_u(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 2); + return assert; + }), + test("add_32", () => { + // when + const args = Args.from(0x0, 0x1, 0x3); + const regs = newRegisters(); + regs[reg(args.a)] = 2 ** 64 - 1; + regs[reg(args.b)] = 2 ** 64 - 1; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = math.add_32(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0xffff_ffff_ffff_fffe); + return assert; + }), +]; diff --git a/assembly/instructions/math.ts b/assembly/instructions/math.ts index 1aa5059..5882a46 100644 --- a/assembly/instructions/math.ts +++ b/assembly/instructions/math.ts @@ -44,7 +44,9 @@ export const neg_add_imm: InstructionRun = (args, registers) => { // ADD_32 export const add_32: InstructionRun = (args, registers) => { - registers[reg(args.c)] = u32SignExtend(u32(registers[reg(args.a)]) + u32(registers[reg(args.b)])); + const a = u32(registers[reg(args.a)]); + const b = u32(registers[reg(args.b)]); + registers[reg(args.c)] = u32SignExtend(a + b); return ok(); }; @@ -195,3 +197,35 @@ export const mul_upper_s_u: InstructionRun = (args, registers) => { registers[reg(args.c)] = mulUpperSignedUnsigned(i64(registers[reg(args.a)]), registers[reg(args.b)]); return ok(); }; + +// MAX +export const max: InstructionRun = (args, registers) => { + const a = i64(registers[reg(args.a)]); + const b = i64(registers[reg(args.b)]); + registers[reg(args.c)] = a < b ? b : a; + return ok(); +}; + +// MAX_U +export const max_u: InstructionRun = (args, registers) => { + const a = registers[reg(args.a)]; + const b = registers[reg(args.b)]; + registers[reg(args.c)] = a < b ? b : a; + return ok(); +}; + +// MIN +export const min: InstructionRun = (args, registers) => { + const a = i64(registers[reg(args.a)]); + const b = i64(registers[reg(args.b)]); + registers[reg(args.c)] = a > b ? b : a; + return ok(); +}; + +// MIN_U +export const min_u: InstructionRun = (args, registers) => { + const a = registers[reg(args.a)]; + const b = registers[reg(args.b)]; + registers[reg(args.c)] = a > b ? b : a; + return ok(); +}; diff --git a/assembly/instructions/rot.test.ts b/assembly/instructions/rot.test.ts new file mode 100644 index 0000000..893f727 --- /dev/null +++ b/assembly/instructions/rot.test.ts @@ -0,0 +1,92 @@ +import { Args } from "../arguments"; +import { MemoryBuilder } from "../memory"; +import { newRegisters } from "../registers"; +import { Assert, Test, test } from "../test"; +import { Outcome } from "./outcome"; +import { rot_l_32, rot_l_64, rot_r_64_imm, rot_r_64_imm_alt } from "./rot"; +import { reg } from "./utils"; + +export const TESTS: Test[] = [ + test("rot_r_64_imm", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x8; + const regs = newRegisters(); + regs[reg(args.a)] = 0xdead_beef; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = rot_r_64_imm(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.b)], 0xef00_0000_00de_adbe); + return assert; + }), + test("rot_r_64_imm_alt", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0xdead_beef; + const regs = newRegisters(); + regs[reg(args.a)] = 0x8; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = rot_r_64_imm_alt(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.b)], 0xef00_0000_00de_adbe); + return assert; + }), + test("rot_l_64", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x8; + const regs = newRegisters(); + regs[reg(args.a)] = 0x8; + regs[reg(args.b)] = 0xdead_beef; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = rot_l_64(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0x0000_00de_adbe_ef00); + return assert; + }), + test("rot_l_32", () => { + // when + const args = new Args(); + args.a = 0x0; + args.b = 0x1; + args.c = 0x8; + const regs = newRegisters(); + regs[reg(args.a)] = 0x8; + regs[reg(args.b)] = 0xdead_beef; + + const memo = new MemoryBuilder().build(0); + + // when + const ret = rot_l_32(args, regs, memo); + + // then + const assert = new Assert(); + assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); + assert.isEqual(regs[reg(args.c)], 0xffff_ffff_adbe_efde); + return assert; + }), +]; diff --git a/assembly/instructions/rot.ts b/assembly/instructions/rot.ts new file mode 100644 index 0000000..c70c03b --- /dev/null +++ b/assembly/instructions/rot.ts @@ -0,0 +1,69 @@ +import { InstructionRun, ok } from "./outcome"; +import { reg, u32SignExtend } from "./utils"; + +// ROT_R_64_IMM +export const rot_r_64_imm: InstructionRun = (args, regs) => { + regs[reg(args.b)] = math.rot_r(regs[reg(args.a)], args.c); + return ok(); +}; + +// ROT_R_64_IMM_ALT +export const rot_r_64_imm_alt: InstructionRun = (args, regs) => { + regs[reg(args.b)] = math.rot_r(args.c, regs[reg(args.a)]); + return ok(); +}; + +// ROT_R_32_IMM +export const rot_r_32_imm: InstructionRun = (args, regs) => { + regs[reg(args.b)] = u32SignExtend(math.rot_r_32(u32(regs[reg(args.a)]), u32(args.c))); + return ok(); +}; + +// ROT_R_32_IMM_ALT +export const rot_r_32_imm_alt: InstructionRun = (args, regs) => { + regs[reg(args.b)] = u32SignExtend(math.rot_r_32(u32(args.c), u32(regs[reg(args.a)]))); + return ok(); +}; + +// ROT_L_64 +export const rot_l_64: InstructionRun = (args, regs) => { + regs[reg(args.c)] = math.rot_l(regs[reg(args.b)], regs[reg(args.a)]); + return ok(); +}; + +// ROT_L_32 +export const rot_l_32: InstructionRun = (args, regs) => { + regs[reg(args.c)] = u32SignExtend(math.rot_l_32(u32(regs[reg(args.b)]), u32(regs[reg(args.a)]))); + return ok(); +}; + +// ROT_R_64 +export const rot_r_64: InstructionRun = (args, regs) => { + regs[reg(args.c)] = math.rot_r(regs[reg(args.b)], regs[reg(args.a)]); + return ok(); +}; + +// ROT_R_32 +export const rot_r_32: InstructionRun = (args, regs) => { + regs[reg(args.c)] = u32SignExtend(math.rot_r_32(u32(regs[reg(args.b)]), u32(regs[reg(args.a)]))); + return ok(); +}; + +export namespace math { + // @inline + export function rot_r(v: u64, shift: u64): u64 { + return rotr(v, shift); + } + // @inline + export function rot_r_32(v: u32, shift: u32): u32 { + return rotr(v, shift); + } + // @inline + export function rot_l(v: u64, shift: u64): u64 { + return rotl(v, shift); + } + // @inline + export function rot_l_32(v: u32, shift: u32): u32 { + return rotl(v, shift); + } +} diff --git a/assembly/instructions/shift.ts b/assembly/instructions/shift.ts new file mode 100644 index 0000000..881608b --- /dev/null +++ b/assembly/instructions/shift.ts @@ -0,0 +1,140 @@ +import { InstructionRun, ok } from "./outcome"; +import { reg, u32SignExtend } from "./utils"; + +const MAX_SHIFT_64 = 64; +const MAX_SHIFT_32 = 32; + +// SHLO_L_IMM_32 +export const shlo_l_imm_32: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_32); + const value = u32(registers[reg(args.a)]); + registers[reg(args.b)] = u32SignExtend(value << shift); + return ok(); +}; + +// SHLO_R_IMM_32 +export const shlo_r_imm_32: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_32); + const value = u32(registers[reg(args.a)]); + registers[reg(args.b)] = u32SignExtend(value >>> shift); + return ok(); +}; + +// SHAR_R_IMM_32 +export const shar_r_imm_32: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_32); + const value = u32SignExtend(u32(registers[reg(args.a)])); + registers[reg(args.b)] = value >> shift; + return ok(); +}; + +// SHLO_L_IMM_ALT_32 +export const shlo_l_imm_alt_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + registers[reg(args.b)] = u32SignExtend(args.c << shift); + return ok(); +}; + +// SHLO_R_IMM_ALT_32 +export const shlo_r_imm_alt_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + registers[reg(args.b)] = u32SignExtend(args.c >>> shift); + return ok(); +}; + +// SHAR_R_IMM_ALT_32 +export const shar_r_imm_alt_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + const imm = u32SignExtend(args.c); + registers[reg(args.b)] = u32SignExtend(u32(imm >> shift)); + return ok(); +}; + +// SHLO_L_IMM +export const shlo_l_imm: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_64); + registers[reg(args.b)] = registers[reg(args.a)] << shift; + return ok(); +}; + +// SHLO_R_IMM +export const shlo_r_imm: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_64); + registers[reg(args.b)] = registers[reg(args.a)] >>> shift; + return ok(); +}; + +// SHAR_R_IMM +export const shar_r_imm: InstructionRun = (args, registers) => { + const shift = u32(args.c % MAX_SHIFT_64); + const value = i64(registers[reg(args.a)]); + registers[reg(args.b)] = value >> shift; + return ok(); +}; + +// SHLO_L_IMM_ALT +export const shlo_l_imm_alt: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + registers[reg(args.b)] = u32SignExtend(args.c) << shift; + return ok(); +}; + +// SHLO_R_IMM_ALT +export const shlo_r_imm_alt: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + registers[reg(args.b)] = u32SignExtend(args.c) >>> shift; + return ok(); +}; + +// SHAR_R_IMM_ALT +export const shar_r_imm_alt: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + const value = u32SignExtend(args.c); + registers[reg(args.b)] = u32SignExtend(u32(value >> shift)); + return ok(); +}; + +// SHLO_L_32 +export const shlo_l_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + const value = u32(registers[reg(args.b)]); + registers[reg(args.c)] = u32SignExtend(value << shift); + return ok(); +}; + +// SHLO_R_32 +export const shlo_r_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + const value = u32(registers[reg(args.b)]); + registers[reg(args.c)] = u32SignExtend(value >>> shift); + return ok(); +}; + +// SHAR_R_32 +export const shar_r_32: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_32); + const regValue = u32SignExtend(u32(registers[reg(args.b)])); + registers[reg(args.c)] = u32SignExtend(u32(regValue >> shift)); + return ok(); +}; + +// SHLO_L +export const shlo_l: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + registers[reg(args.c)] = registers[reg(args.b)] << shift; + return ok(); +}; + +// SHLO_R +export const shlo_r: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + registers[reg(args.c)] = registers[reg(args.b)] >>> shift; + return ok(); +}; + +// SHAR_R +export const shar_r: InstructionRun = (args, registers) => { + const shift = u32(registers[reg(args.a)] % MAX_SHIFT_64); + registers[reg(args.c)] = i64(registers[reg(args.b)]) >> shift; + return ok(); +}; diff --git a/assembly/instructions/store.ts b/assembly/instructions/store.ts index c1f084d..5ade58b 100644 --- a/assembly/instructions/store.ts +++ b/assembly/instructions/store.ts @@ -25,7 +25,7 @@ export const store_imm_u32: InstructionRun = (args, _registers, memory) => { // STORE_IMM_U64 export const store_imm_u64: InstructionRun = (args, _registers, memory) => { const address = args.a; - const pageFault = memory.setU64(address, u64(args.b)); + const pageFault = memory.setU64(address, u32SignExtend(args.b)); return okOrFault(pageFault); }; diff --git a/assembly/math.ts b/assembly/math.ts new file mode 100644 index 0000000..e69de29 diff --git a/assembly/registers.ts b/assembly/registers.ts index 78142b5..2c33e22 100644 --- a/assembly/registers.ts +++ b/assembly/registers.ts @@ -4,3 +4,7 @@ export const NO_OF_REGISTERS = 13; export const REG_SIZE_BYTES = 8; export type Registers = StaticArray; + +export function newRegisters(): Registers { + return new StaticArray(NO_OF_REGISTERS); +} diff --git a/assembly/test-run.ts b/assembly/test-run.ts new file mode 100644 index 0000000..095e3e7 --- /dev/null +++ b/assembly/test-run.ts @@ -0,0 +1,50 @@ +import * as bit from "./instructions/bit.test"; +import * as branch from "./instructions/branch.test"; +import * as logic from "./instructions/logic.test"; +import * as math from "./instructions/math.test"; +import * as rot from "./instructions/rot.test"; +import { Test } from "./test"; + +export function runAllTests(): void { + let a: u64 = 0; + a += run(bit.TESTS, "bit.ts"); + a += run(branch.TESTS, "branch.ts"); + a += run(math.TESTS, "math.ts"); + a += run(logic.TESTS, "logic.ts"); + a += run(rot.TESTS, "rot.ts"); + + const okay = u32(a >> 32); + const total = u32(a); + + printSummary("\n\nTotal", okay, total); + if (okay !== total) { + throw new Error("Some tests failed."); + } +} + +function run(tests: Test[], file: string): u64 { + let ok = 0; + console.log(`> ${file}`); + for (let i = 0; i < tests.length; i++) { + console.log(` >>> ${tests[i].name}`); + const res = tests[i].ptr(); + if (res.isOkay) { + console.log(` <<< ${tests[i].name} ✅`); + ok += 1; + } else { + for (let i = 0; i < res.errors.length; i++) { + console.log(` ${res.errors[i]}`); + } + console.log(` <<< ${tests[i].name} 🔴`); + } + } + + printSummary(`< ${file}`, ok, tests.length); + + return (u64(ok) << 32) + tests.length; +} + +function printSummary(msg: string, okay: u32, total: u32): void { + const ico = okay === total ? "✅" : "🔴"; + console.log(`${msg} ${okay} / ${total} ${ico}`); +} diff --git a/assembly/test.ts b/assembly/test.ts new file mode 100644 index 0000000..bca7390 --- /dev/null +++ b/assembly/test.ts @@ -0,0 +1,35 @@ +export class Test { + constructor( + public name: string, + public ptr: () => Assert, + ) {} +} + +export class Assert { + public isOkay: boolean = true; + public errors: string[] = []; + + static todo(): Assert { + const r = new Assert(); + r.fail("Not implemented yet!"); + return r; + } + + fail(msg: string): void { + this.isOkay = false; + this.errors.push(msg); + } + + isEqual(actual: T, expected: T, msg: string = ""): void { + if (actual !== expected) { + this.isOkay = false; + const actualDisplay = isInteger(actual) ? `${actual} (0x${actual.toString(16)})` : `${actual}`; + const expectDisplay = isInteger(expected) ? `${expected} (0x${expected.toString(16)})` : `${expected}`; + this.errors.push(`Got: ${actualDisplay}, expected: ${expectDisplay} @ ${msg}`); + } + } +} + +export function test(name: string, ptr: () => Assert): Test { + return new Test(name, ptr); +} diff --git a/bin/test.js b/bin/test.js new file mode 100644 index 0000000..2c6a098 --- /dev/null +++ b/bin/test.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import { runAllTests } from "../build/test.js"; + +runAllTests(); diff --git a/package.json b/package.json index 2e46ccf..e49549a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "asbuild": "npm run asbuild:debug && npm run asbuild:release", "asbuild:debug": "asc assembly/index.ts --target debug", "asbuild:release": "asc assembly/index.ts --target release", + "asbuild:test": "asc assembly/test-run.ts --target test", "cp-build": "rm -rf ./web/build; cp -r ./build ./web/", "build": "npm run asbuild && npm run cp-build", "update-version": "node ./web/bump-version.js $GITHUB_SHA", @@ -17,7 +18,8 @@ "preweb": "npm run build", "web": "npx live-server ./web", "start": "node ./bin/index.js", - "fuzz": "npx jazzer --sync ./bin/fuzz" + "fuzz": "npx jazzer --sync ./bin/fuzz", + "test": "npm run asbuild:test && node ./bin/test.js" }, "keywords": [], "author": "Fluffy Labs",