HoleyBytes ISA Specification
Bytecode format
- Holey Bytes program should start with following magic:
[0xAB, 0x1E, 0x0B]
- All numbers are encoded little-endian
- There is 256 registers, they are represented by a byte
- Immediate values are 64 bit
- Program is by spec required to be terminated with 12 zero bytes
Instruction encoding
- Instruction parameters are packed (no alignment)
- [opcode, …parameters…]
Instruction parameter types
- B = Byte
- D = Doubleword (64 bits)
- H = Halfword (16 bits)
Name |
Size |
BBBB |
32 bits |
BBB |
24 bits |
BBDH |
96 bits |
BBD |
80 bits |
BBW |
48 bits |
BB |
16 bits |
BD |
72 bits |
D |
64 bits |
N |
0 bits |
Instructions
#n
: register in parameter n
imm #n
: for immediate in parameter n
P ← V
: Set register P to value V
[x]
: Address x
No-op
Opcode |
Name |
Action |
0 |
UN |
Trigger unreachable code trap |
1 |
NOP |
Do nothing |
Integer binary ops.
Opcode |
Name |
Action |
2 |
ADD |
Wrapping addition |
3 |
SUB |
Wrapping subtraction |
4 |
MUL |
Wrapping multiplication |
5 |
AND |
Bitand |
6 |
OR |
Bitor |
7 |
XOR |
Bitxor |
8 |
SL |
Unsigned left bitshift |
9 |
SR |
Unsigned right bitshift |
10 |
SRS |
Signed right bitshift |
Comparsion
Opcode |
Name |
Action |
11 |
CMP |
Signed comparsion |
12 |
CMPU |
Unsigned comparsion |
Comparsion table
#1 op #2 |
Result |
< |
-1 |
= |
0 |
> |
1 |
Division-remainder
- Type BBBB
- In case of
#3
is zero, the resulting value is all-ones
#0 ← #2 ÷ #3
#1 ← #2 % #3
Opcode |
Name |
Action |
13 |
DIR |
Divide and remainder combinated |
Negations
Opcode |
Name |
Action |
14 |
NEG |
Bit negation |
15 |
NOT |
Logical negation |
Integer immediate binary ops.
- Type BBD
#0 ← #1 <op> imm #2
Opcode |
Name |
Action |
16 |
ADDI |
Wrapping addition |
17 |
MULI |
Wrapping subtraction |
18 |
ANDI |
Bitand |
19 |
ORI |
Bitor |
20 |
XORI |
Bitxor |
Bitshifts
- Type BBW
Opcode |
Name |
Action |
21 |
SLI |
Unsigned left bitshift |
22 |
SRI |
Unsigned right bitshift |
23 |
SRSI |
Signed right bitshift |
Comparsion
- Comparsion is the same as when RRR type
Opcode |
Name |
Action |
24 |
CMPI |
Signed comparsion |
25 |
CMPUI |
Unsigned comparsion |
Register value set / copy
Copy
Opcode |
Name |
Action |
26 |
CP |
Copy |
Swap
- Type BB
- Swap #0 and #1
- Zero register rules:
- Both: no-op
- One: Copy zero to the non-zero register
Opcode |
Name |
Action |
27 |
SWA |
Swap |
Load immediate
Opcode |
Name |
Action |
28 |
LI |
Load immediate |
Memory operations
- Type BBDH
- If loaded/store value exceeds one register size, continue accessing following registers
Load / Store
Opcode |
Name |
Action |
29 |
LD |
#0 ← [#1 + imm #3], copy imm #4 bytes |
30 |
ST |
[#1 + imm #3] ← #0, copy imm #4 bytes |
Block copy
- Block copy source and target can overlap
Memory copy
Opcode |
Name |
Action |
31 |
BMC |
[#1] ← [#0], copy imm #2 bytes |
Register copy
- Type BBB
- Copy a block a register to another location (again, overflowing to following registers)
Opcode |
Name |
Action |
32 |
BRC |
#1 ← #0, copy imm #2 registers |
Control flow
Unconditional jump
Opcode |
Name |
Action |
33 |
JAL |
Save PC past JAL to #0 and jump at #1 + imm #2 |
Conditional jumps
- Type BBD
- Jump at
imm #2
if #0 <op> #1
Opcode |
Name |
Comparsion |
34 |
JEQ |
= |
35 |
JNE |
≠ |
36 |
JLT |
< (signed) |
37 |
JGT |
> (signed) |
38 |
JLTU |
< (unsigned) |
39 |
JGTU |
> (unsigned) |
Environment call
Opcode |
Name |
Action |
40 |
ECALL |
Cause an trap to the host environment |
Floating point operations
Opcode |
Name |
Action |
41 |
ADDF |
Addition |
42 |
SUBF |
Subtraction |
43 |
MULF |
Multiplication |
Division-remainder
Opcode |
Name |
Action |
44 |
DIRF |
Same as for integer DIR |
Fused Multiply-Add
Opcode |
Name |
Action |
45 |
FMAF |
#0 ← (#1 * #2) + #3 |
Negation
- Type BB
Opcode |
Name |
Action |
46 |
NEGF |
#0 ← -#1 |
Conversion
- Type BB
- Signed
#0 ← #1 as _
Opcode |
Name |
Action |
47 |
ITF |
Int to Float |
48 |
FTI |
Float to Int |
Floating point immediate operations
- Type BBD
#0 ← #1 <op> imm #2
Opcode |
Name |
Action |
49 |
ADDFI |
Addition |
50 |
MULFI |
Multiplication |
Registers
- There is 255 registers + one zero register (with index 0)
- Reading from zero register yields zero
- Writing to zero register is a no-op
Memory
- Addresses are 64 bit
- Memory implementation is arbitrary
- In case of accessing invalid address:
- Program shall trap (LoadAccessEx, StoreAccessEx) with parameter of accessed address
- Value of register when trapped is undefined
Recommendations
- Leave address
0x0
as invalid
- If paging used:
- Leave first page invalid
- Pages should be at least 4 KiB
Program execution
- The way of program execution is implementation defined
- The execution is arbitrary, as long all effects are obervable
in the way as program was executed literally, in order.
Program validation
- Invalid program should cause runtime error:
- The form of error is arbitrary. Can be a trap or an interpreter-specified error
- It shall not be handleable from within the program
- Executing invalid opcode should trap
- Program can be validaded either before execution or when executing
Traps
Program should at least implement these traps:
- Environment call
- Invalid instruction exception
- Load address exception
- Store address exception
- Unreachable instruction
and executing environment should be able to get information about them,
like the opcode of invalid instruction or attempted address to load/store.
Details about these are left as an implementation detail.
Assembly
HoleyBytes assembly format is not defined, this is just a weak description
of hbasm
syntax.
- Opcode names correspond to specified opcode names, lowercase (
nop
)
- Parameters are separated by comma (
addi r0, r0, 1
)
- Instructions are separated by either line feed or semicolon
- Registers are represented by
r
followed by the number (r10
)
- Labels are defined by label name followed with colon (
loop:
)
- Labels are references simply by their name (
print
)
- Immediates are entered plainly. Negative numbers supported.