Skip to content

Instantly share code, notes, and snippets.

@bplaat
Last active August 17, 2025 06:32
Show Gist options
  • Save bplaat/b240ca1b712f7e17fbfc1c337cd09919 to your computer and use it in GitHub Desktop.
Save bplaat/b240ca1b712f7e17fbfc1c337cd09919 to your computer and use it in GitHub Desktop.
The Korra 32-bit Processor

The Korra 32-bit Processor

The Korra processor is a simple 32-bit CISC processor with RISC features

Pages that inspired the Korra processor

Features

  • A new simple 32-bit CISC design with RISC features like the Neva processor
  • 32-bit internal address bus
  • 8-bit external data bus
  • Small, simple, orthogonal, load / store instruction set
  • Variable instruction length (2 bytes or 4 bytes) for smaller program size
  • All external I/O must be memory based
  • Room to be expanded with more instructions

Insperations

  • Many registers for an efficient calling convention: ARM, RISC-V
  • Memory access only in special load / store instructions: ARM, RISC-V
  • Immediate encoding with rotate for larger immediate encoding space: ARM
  • Most instruction names, pseudo instructions and assembler style: x86
  • Flags and jump condition combinations: x86

Instruction encoding

Every instruction has an opcode and two bits that describe its mode, the jump instructions are differently encoded:

Korra instruction encoding

Registers

The Korra processor has way more registers than the Neva processor, this ensures that code can better be optimized. All registers have names and are used by the Korra ABI in a specific way:

# Names Meaning (calling convention)
Temporary registers:
0 t0 Temporary variable 1 / Variable arguments count
1 t1 Temporary variable 2
2 t2 Temporary variable 3
3 t3 Temporary variable 4
Saved registers:
4 s0 Saved variable 1
5 s1 Saved variable 2
6 s2 Saved variable 3
7 s3 Saved variable 4
Argument registers:
8 a0 Function argument 1 / return value 1
9 a1 Function argument 2 / return value 2
10 a2 Function argument 3
11 a3 Function argument 4
Special registers:
12 bp Stack base pointer
13 sp Stack pointer
14 rp Return pointer
15 flags Flags (specific bits described below)
Inaccessible registers:
- ip Instruction pointer

Flag register bits

The Korra processor has general flags and processor state flags, all flags are stored in the flags register:

# Name Meaning
General flags:
0 Carry Is set when a carry overflow occurs
1 Zero Is set when the result is zero
2 Sign Is set when the highest sign bit is set
3 Overflow Is set when a overflow occurs
4/7 Reserved -
Processor state flags:
8 Halt When set halts the processor
9/31 Reserved -

Opcodes

# Name Meaning Operation Flags
Move, load and store instructions (9):
0 mov Move reg: dest = src + sx(disp)
imm: dest = imm rotl rotate
-
1 lw Load word (32-bit) reg: dest = word [src + sx(disp)]
imm: dest = word [imm rotl rotate]
-
2 lh Load half (16-bit) reg: dest = half [src + sx(disp)]
imm: dest = half [imm rotl rotate]
-
3 lhsx Load half (16-bit) sign extended reg: dest = sx(byte [src + sx(disp)])
imm: dest = sx(byte [imm rotl rotate])
-
4 lb Load byte (8-bit) reg: dest = byte [src + sx(disp)]
imm: dest = byte [imm rotl rotate]
-
5 lbsx Load byte (8-bit) sign extended reg: dest = sx(byte [src + sx(disp)])
imm: dest = sx(byte [imm rotl rotate])
-
6 sw Store word (32-bit) to memory reg: word [src + sx(disp)] = dest
imm: word [imm rotl rotate] = dest
-
7 sh Store half (16-bit) to memory reg: half [src + sx(disp)] = dest
imm: half [imm rotl rotate] = dest
-
8 sb Store byte (8-bit) to memory reg: byte [src + sx(disp)] = dest
imm: byte [imm rotl rotate] = dest
-
Arithmetic instructions (6):
9 add Add reg: dest += src + sx(disp)
imm: dest += imm rotl rotate
c, z, s, o
10 adc Add with carry reg: dest += src + sx(disp) + carry
imm: dest += (imm rotl rotate) + carry
c, z, s, o
11 sub Subtract reg: dest -= src + sx(disp)
imm: dest -= imm rotl rotate
c, z, s, o
12 sbb Subtract with borrow reg: dest -= src + sx(disp) + carry
imm: dest -= (imm rotl rotate) + carry
c, z, s, o
13 reg neg Negate reg: dest = -(src + sx(disp)) c, z, s, o
13 imm* adr Address for an relative ip offset imm: dest = ip + sx(disp) c, z, s, o
14 cmp Arithmetic compare (sub) reg: dest - (src + sx(disp)) (only set flags)
imm: dest - (imm rotl rotate) (only set flags)
c, z, s, o
Bitwise instructions (8):
15 and Logical and reg: dest &= src + sx(disp)
imm: dest &= imimm rotl rotatem
z, s
16 or Logical or reg: dest |= src + sx(disp)
imm: dest |= imm rotl rotate
z, s
17 xor Logical xor reg: dest ^= src + sx(disp)
imm: dest ^= imm rotl rotate
z, s
18 not Logical not reg: dest = ~(src + sx(disp))
imm: dest = ~(imm rotl rotate)
z, s
19 test Bitwise compare (and) reg:dest & (src + sx(disp)) (only set flags)
imm: dest & (imm rotl rotate) (only set flags)
z, s
20 shl Logical shift left reg: dest <<= (src + sx(disp)) & 31
imm: dest <<= (imm rotl rotate) & 31
z, s
21 shr Logical shift right reg: dest >>= (src + sx(disp)) & 31
imm: dest >>= (imm rotl rotate) & 31
z, s
22 sar Arithmetic shift right reg: dest >>>= (src + sx(disp)) & 31
imm: dest >>>= (imm rotl rotate) & 31
z, s
Jump instructions (1):
23 0* jmp Jump reg: ip = src + sx(disp)
imm: ip += sx(disp)
-
23 1* call Call reg: rp = ip, ip = src + sx(disp)
imm: rp = ip, ip += sx(disp)
-
23 2* jc Jump carry reg: if (carry) ip += src + sx(disp)
imm: if (carry) ip += sx(disp)
-
23 3* jnc Jump not carry reg: if (!carry) ip = src + sx(disp)
imm: if (!carry) ip += sx(disp)
-
23 4* jz Jump zero reg: if (zero) ip = src + sx(disp)
imm: if (zero) ip += sx(disp)
-
23 5* jnz Jump not zero reg: if (!zero) ip = src + sx(disp)
imm: if (!zero) ip += sx(disp)
-
23 6* js Jump sign reg: if (sign) ip = src + sx(disp)
imm: if (sign) ip += sx(disp)
-
23 7* jns Jump not sign reg: if (!sign) ip = src + sx(disp)
imm: if (!sign) ip += sx(disp)
-
23 8* jo Jump overflow reg: if (overflow) ip = src + sx(disp)
imm: if (overflow) ip += sx(disp)
-
23 9* jno Jump not overflow reg: if (!overflow) ip = src + sx(disp)
imm: if (!overflow) ip += sx(disp)
-
23 10* ja Jump above reg: if (!carry && !zero) ip = src + sx(disp)
imm: if (!carry && !zero) ip += sx(disp)
-
23 11* jna Jump not above reg: if (carry || zero) ip = src + sx(disp)
imm: if (carry || zero) ip += sx(disp)
-
23 12* jl Jump less reg: if (sign != overflow) ip = src + sx(disp)
imm: if (sign != overflow) ip += sx(disp)
-
23 13* jnl Jump not less reg: if (sign == overflow) ip = src + sx(disp)
imm: if (sign == overflow)) ip += sx(disp)
-
23 14* jg Jump greater reg: if (zero && (sign == overflow)) ip = src + sx(disp)
imm: if (zero && (sign == overflow)) ip += sx(disp)
-
23 15* jng Jump not greater reg: if (!zero || (sign != overflow)) ip = src + sx(disp)
imm: if (!zero || (sign != overflow)) ip += sx(disp)
-
Reserved instructions (40):
24/63 Reserved - - -

* The adr and jmp instructions have a different encoding see diagram above, the jmp instruction encodes its 4-bit condition inside the destination register part

Pseudo instructions

There are also some pseudo instructions which the assembler translates to other instructions, most pseudo instructions are used to make the Korra assembler look more like x86 assembler:

Name Meaning Example Translation
Generic helpers:
nop No operation nop mov t0, t0
inc reg Increment register inc t0 add t0, 1
dec reg Decrement register dec t0 sub t0, 1
hlt Set halt flag hlt or flags, 1 << 8
ret Return from function ret jmp rp
Move load store helpers:
mov reg, -imm Move negative immediate mov t0, -200 not t0, 199
mov reg, word [?] Move load word mov t0, word [0xbeef] lw t0, 0xbeef
mov reg, half [?] Move load half mov t0, half [0xbeef] lh t0, 0xbeef
movsx reg, half [?] Move load half sign extended movsx t0, half [0xbeef] lhsx t0, 0xbeef
mov reg, byte [?] Move load byte mov t0, byte [0xbeef] lb t0, 0xbeef
movsx reg, byte [?] Move load byte sign extended movsx t0, byte [0xbeef] lbsx t0, 0xbeef
mov word [?], reg Move store word mov word [0xbeef], t0 sw t0, 0xbeef
mov half [?], reg Move store half mov half [0xbeef], t0 sh t0, 0xbeef
mov byte [?], reg Move store byte mov byte [0xbeef], t0 sb t0, 0xbeef
Stack helpers:
push reg Push register push rp mov word [sp], rp
sub sp, 4
pop reg Pop register pop rp add sp, 4
mov rp, word [sp]
Other jump conditions:
jb Jump below jb label jc label
jnae Jump not above or equal jnae label jc label
jnb Jump not below jb label jnc label
jae Jump above or equal jae label jnc label
je Jump equal je label jz label
jne Jump not equal jne label jnz label
jnbe Jump not below or equal jnbe label ja label
jbe Jump below or equal jbe label jna label
jnge Jump not greater or equal jnge label jl label
jge Jump greater or equal jge label jnl label
jnle Jump not less or equal jnle label jg label
jle Jump less or equal jle label jng label

Assembly examples

Here are some assembly examples:

; memcpy, memset, memcmp
memcpy:
    ; a0 = dest
    ; a1 = src
    ; a2 = size
    mov t0, a0
    add t0, a2
.repeat:
    cmp a0, t0
    je .done
    mov t1, byte [a1]
    mov byte [a0], t1
    inc a0
    inc a1
    jmp .repeat
.done:
    ret
memset:
    ; a0 = dest
    ; a1 = value
    ; a2 = size
    mov t0, a0
    add t0, a2
.repeat:
    cmp a0, t0
    je .done
    mov byte [a0], a1
    inc a0
    jmp .repeat
.done:
    ret
; strlen, strcpy, strcat, strcmp
strlen:
    ; a0 = str
    mov t0, a0
.repeat:
    mov t1, byte [a0]
    cmp t1, 0
    je .done
    inc a0
    jmp .repeat
.done:
    sub a0, t0
    ret
strcpy:
    ; a0 = dest
    ; a1 = src
.repeat:
    mov t0, byte [a1]
    cmp t0, 0
    je .done
    mov byte [a0], t0
    inc a0
    inc a1
    jmp .repeat
.done:
    ret
strcat:
    ; a0 = dest
    ; a1 = src
    push rp
.repeat:
    mov t0, byte [a0]
    cmp t0, 0
    je .done
    inc a0
    jmp .repeat
.done:
    call strcpy
    pop rp
    ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment