holey-bytes/spec.md

14 KiB
Raw Blame History

HoleyBytes ISA Specification

Bytecode format

  • Image format is not specified, though ELF is recommended
  • All numbers are encoded little-endian
  • There is 256 registers, they are represented by a byte
  • Immediate values are 8, 16, 32 or 64 bit

Instruction encoding

  • Instruction operands are packed (no alignment)
  • [opcode, operand 0, operand 1, …]

Instruction parameter types

  • R: Register (8 bits)
  • Relative program-counter offset immediates:
    • O: 32 bit (Si32)
    • P: 16 bit (Si16)
  • Immediates:
    • B: Byte, 8 bit (Xi8)
    • H: Half-word, 16 bit (Xi16)
    • W: Word, 32 bit (Xi32)
    • D: Double-word, 64 bit (Xi64)
  • A: Absolute address immediate, 64 bit (Ui64)

Types

  • Sin: Signed integer of size n bits (Si8, Si16, Si32, Si64)
  • Uin: Unsigned integer of size n bits (Ui8, Ui16, Ui32, Ui64)
  • Xin: Sign-agnostic integer of size n bits (Xi8, Xi16, Xi32, Xi64)
  • Fln: Floating point number of size n bits (Fl32, Fl64)

Behaviour

  • There is only one type of register, a general-purpose one. Used for both integers and floats.
  • Integer operations are wrapping, including signed numbers
    • Bitshifts are truncating
  • Two's complement
  • Floats as specified by IEEE 754
  • Execution model is implementation defined as long all observable effects are performed in correct order

Relative addressing

Relative addresses are computed from address of the first byte of offset in the code. Not from the beginning of current or following instruction.

Zero register

  • Register 0
  • Cannot be clobbered
    • Write is no-op
  • Load always yields 0

Rounding modes

Rounding mode Value
To nearest, ties to even 0b00
Towards 0 (truncate) 0b01
Towards +∞ (up) 0b10
Towards -∞ (down) 0b11
  • Remaining values in the byte traps with invalid operand exception

Memory

  • Memory implementation is implementation-defined
  • Zero address (0x0) is considered invalid

Traps

  • Environment call
  • Environment breakpoint

Program counter goes to the following instruction

Exceptions

  • Memory access fault
  • Invalid operand
  • Unknown opcode

Program counter stays on the currently executed instruction

Instructions

  • #n: register in parameter n
  • $n: for immediate in parameter n
  • #P ← V: Set register P to value V
  • [x]: Address x
  • XY: X bytes from location Y
  • pc: Program counter
  • <XYZ>: Placeholder
  • Type(X): Cast

Program execution control

  • Type N
Opcode Mnemonic Action
0x00 UN Throw unreachable code exception
0x01 TX Terminate execution (eg. on end of program)
0x02 NOP Do nothing

Binary register-register ops

  • Type RRR
  • Action: #0 ← #1 <OP> #2

Addition (+)

Opcode Mnemonic Type
0x03 ADD8 Xi8
0x04 ADD16 Xi16
0x05 ADD32 Xi32
0x06 ADD64 Xi64

Subtraction (-)

Opcode Mnemonic Type
0x07 SUB8 Xi8
0x08 SUB16 Xi16
0x09 SUB32 Xi32
0x0A SUB64 Xi64

Multiplication (*)

Opcode Mnemonic Type
0x0B MUL8 Xi8
0x0C MUL16 Xi16
0x0D MUL32 Xi32
0x0E MUL64 Xi64

Bitwise ops (type: Xi64)

Opcode Mnemonic Operation
0x0F AND Conjunction (&)
0x10 OR Disjunction (|)
0x11 XOR Non-equivalence (^)

Unsigned left bitshift (<<)

Opcode Mnemonic Type
0x12 SLU8 Ui8
0x13 SLU16 Ui16
0x14 SLU32 Ui32
0x15 SLU64 Ui64

Unsigned right bitshift (>>)

Opcode Mnemonic Type
0x16 SRU8 Ui8
0x17 SRU16 Ui16
0x18 SRU32 Ui32
0x19 SRU64 Ui64

Signed right bitshift (>>)

Opcode Mnemonic Type
0x1A SRS8 Si8
0x1B SRS16 Si16
0x1C SRS32 Si32
0x1D SRS64 Si64

Comparsion

  • Compares two numbers, saves result to register
  • Operation: #0 ← #1 <=> #2
Ordering Number
< -1
= 0
> 1
Opcode Mnemonic Type
0x1E CMPU Ui64
0x1F CMPS Si64

Merged divide-remainder

  • Type RRRR

  • Operation:

    • #0 ← #2 / #3
    • #1 ← #2 % #3
  • If dividing by zero:

    • #0 ← Ui64(-1)
    • #1 ← #2
Opcode Mnemonic Type
0x20 DIRU8 Ui8
0x21 DIRU16 Ui16
0x22 DIRU32 Ui32
0x23 DIRU64 Ui64
0x24 DIRS8 Si8
0x25 DIRS16 Si16
0x26 DIRS32 Si32
0x27 DIRS64 Si64

Unary register operations (type: Xi64)

  • Type: RR
  • Operation: #0 ← <OP> #1
Opcode Mnemonic Operation
0x28 NEG Bitwise complement (~)
0x29 NOT Logical negation (!)

Sign extensions

  • Operation: #0 ← Si64(#1)
Opcode Mnemonic Source type
0x2A SXT8 Si8
0x2B SXT16 Si16
0x2C SXT32 Si32

Binary register-immediate operations

  • Type: RR<IMM>
  • Operation: #0 ← #1 <OP> $2

Addition (+)

Opcode Mnemonic Type
0x2D ADDI8 Xi8
0x2E ADDI16 Xi16
0x2F ADDI32 Xi32
0x30 ADDI64 Xi64

Multiplication (*)

Opcode Mnemonic Type
0x31 MULI8 Xi8
0x32 MULI16 Xi16
0x33 MULI32 Xi32
0x34 MULI64 Xi64

Bitwise ops (type: Xi64)

Opcode Mnemonic Operation
0x35 ANDI Conjunction (&)
0x36 ORI Disjunction (|)
0x37 XORI Non-equivalence (^)

Register-immediate bitshifts

  • Type: RRB
  • Operation: #0 ← #1 <OP> $2

Unsigned left bitshift (<<)

Opcode Mnemonic Type
0x38 SLUI8 Ui8
0x39 SLUI16 Ui16
0x3A SLUI32 Ui32
0x3B SLUI64 Ui64

Unsigned right bitshift (>>)

Opcode Mnemonic Type
0x3C SRUI8 Ui8
0x3D SRUI16 Ui16
0x3E SRUI32 Ui32
0x3F SRUI64 Ui64

Signed right bitshift (>>)

Opcode Mnemonic Type
0x40 SRSI8 Si8
0x41 SRSI16 Si16
0x42 SRSI32 Si32
0x43 SRSI64 Si64

Comparsion

  • Compares two numbers, saves result to register
  • Operation: #0 ← #1 <=> $2
  • Comparsion table same for register-register one
Opcode Mnemonic Type
0x44 CMPUI Ui64
0x45 CMPSI Si64

Register copies

  • Type: RR
Opcode Mnemonic Operation
0x46 CP Copy register value (#0 ← #1)
0x47 SWA Swap register values (#0 ⇆ #1)

Load immediate

  • Load immediate value from code to register
  • Type: R<IMM>
  • Operation: #0 ← $1
Opcode Mnemonic Type
0x48 LI8 Xi8
0x49 LI16 Xi16
0x4A Li32 Xi32
0x4B Li64 Xi64

Load relative address

  • Compute value from program counter, register value and offset
  • Type: RRO
  • Operation: #0 ← pc + #1 + $2
Opcode Mnemonic
0x4C LRA

Memory access operations

  • Immediate $3 specifies size
  • If size is greater than register size, it overflows to adjecent register (eg. copying 16 bytes to register r1 copies first 8 bytes to it and the remaining to r2)

Absolute addressing

  • Type: RRAH
  • Computes address from base register and absolute offset
Opcode Mnemonic Operation
0x4D LD #0 ← $3[#1 + $2]
0x4E ST $3[#1 + $2] ← #0

Relative addressing

  • Type: RROH
  • Computes address from register and offset from program counter
Opcode Mnemonic Operation
0x4F LDR #0 ← $3[pc + #1 + $2]
0x50 STR $3[pc + #1 + $2] ← #0

Block memory copy

  • Type: RRH
  • Copies block of $3 bytes from memory location on address on #0 to #1
Opcode Mnemonic Operation
0x51 BMC $3[#1] ← $3[x0]

Block register copy

  • Type: RRB
  • Copy block of $3 registers starting with #0 to #1
  • Copying over the 256 registers causes an exception
Opcode Mnemonic Operation
0x52 BRC $3#1 ← $3#0

Relative jump

  • Type: O
Opcode Mnemonic Operation
0x53 JMP pc ← pc + $0

Linking jump

  • Operation:
    • Save address of following instruction to #0
      • #0 ← pc+<instruction size>
    • Jump to specified address
Opcode Mnemonic Instruction type Address
0x54 JAL RRO (size = 6 B) Relative, pc + #1 + $2
0x55 JALA RRA (size = 10 B) Absolute, #1 + $2

Conditional jump

  • Perform comparsion, if operation met, jump to relative address
  • Type: RRP
  • Operation: if #0 <CMP> #1 { pc ← pc + $2 }
Opcode Mnemonic Condition Type
0x56 JEQ Equals (=) Xi64
0x57 JNE Not-equals () Xi64
0x58 JLTU Less-than (<) Ui64
0x59 JGTU Greater-than (>) Ui64
0x5A JLTS Less-than (<) Si64
0x5B JGTS Greater-than (>) Si64

Environment traps

  • Traps to the environment
  • Type: N
Opcode Mnemonic Trap type
0x5C ECA Environment call
0x5D EBP Breakpoint

Floating point binary operations

  • Type: RRR
  • Operation: #0 ← #1 <OP> #2
Opcode Mnemonic Operation Type
0x5E FADD32 Addition (+) Fl32
0x5F FADD64 Addition (+) Fl64
0x60 FSUB32 Subtraction (-) Fl32
0x61 FSUB64 Subtraction (-) Fl64
0x62 FMUL32 Multiplication (*) Fl32
0x63 FMUL64 Multiplication (*) Fl64
0x64 FDIV32 Division (/) Fl32
0x65 FDIV64 Division (/) Fl64

Fused multiply-add

  • Type: RRRR
  • Operation: #0 ← (#1 * #2) + #3
Opcode Mnemonic Type
0x66 FMA32 Fl32
0x67 FMA64 Fl64

Comparsions

  • Type: RRR
  • Operation: #0 ← #1 <=> #2
  • Comparsion table same as for CMPx/CMPxI
  • NaN is less-than/greater-than depends on variant
Opcode Mnemonic Type NaN is
0x6A FCMPLT32 Fl32 <
0x6B FCMPLT64 Fl64 <
0x6C FCMPGT32 Fl32 >
0x6D FCMPGT64 Fl64 >

Int to float

  • Type: RR
  • Converts from Si64
  • Operation: #0 ← Fl<SIZE>(#1)
Opcode Mnemonic Type
0x6E ITF32 Fl32
0x6F ITF64 Fl64

Float to int

  • Type: RRB
  • Operation: #0 ← Si64(#1)
  • Immediate $2 specifies rounding mode
Opcode Mnemonic Type
0x70 FTI32 Fl32
0x71 FTI64 Fl64

Fl32 to Fl64

  • Type: RR
  • Operation: #0 ← Fl64(#1)
Opcode Mnemonic
0x72 FC32T64

Fl64 to Fl32

  • Type: RRB
  • Operation: #0 ← Fl32(#1)
  • Immediate $2 specified rounding mode
Opcode Mnemonic
0x73 FC64T32

16-bit relative address instruction variants

Opcode Mnemonic Type Variant of
0x74 LRA16 RRP LRA
0x75 LDR16 RRPH LDR
0x76 STR16 RRPH STR
0x77 JMP16 P JMP

psABI

C datatypes and alignment

  • One byte is 8 bits
C Type Description Byte sizes
char Character / byte 1
short Short integer 2
int Integer 4
long Long integer 8
long long Long long integer 8
float Single-precision float 4
double Double-precision float 8
long double Extended-precision float 8
  • Bikeshedding note: long double is now 8 bytes as the base ISA does not support f128. an extension for that should be made.

Call convention

  • Registers r1 r31 are caller saved
  • Registers r32 r255 are callee saved
Register Description Saver
r0 Hard-wired zero N/A
r1 - r2 Return values Caller
r2 - r11 Function parameters Caller
r12 - r30 General purpose Caller
r31 Return address Caller
r32 - r253 General purpose Callee
r254 Stack pointer Callee
r255 Thread pointer N/A
  • If return value is too big to fit r1, r2 is also used.
  • Values larger than two double-words are passed by reference