The Korra processor is a simple 32-bit CISC processor with RISC features
- https://en.wikipedia.org/wiki/Intel_8086
- https://en.wikipedia.org/wiki/ARM_architecture
- https://en.wikipedia.org/wiki/RISC-V
- https://en.wikipedia.org/wiki/Motorola_68000
- https://en.wikipedia.org/wiki/X86_instruction_listings
- http://unixwiz.net/techtips/x86-jumps.html
- 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
- 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
Every instruction has an opcode and two bits that describe its mode, the jump instructions are differently encoded:
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 |
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 | - | |
| # | 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)] = destimm: word [imm rotl rotate] = dest |
- |
| 7 | sh |
Store half (16-bit) to memory | reg: half [src + sx(disp)] = destimm: half [imm rotl rotate] = dest |
- |
| 8 | sb |
Store byte (8-bit) to memory | reg: byte [src + sx(disp)] = destimm: 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) + carryimm: 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) + carryimm: 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)) & 31imm: dest <<= (imm rotl rotate) & 31 |
z, s |
| 21 | shr |
Logical shift right | reg: dest >>= (src + sx(disp)) & 31imm: dest >>= (imm rotl rotate) & 31 |
z, s |
| 22 | sar |
Arithmetic shift right | reg: dest >>>= (src + sx(disp)) & 31imm: 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
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], rpsub sp, 4 |
pop reg |
Pop register | pop rp |
add sp, 4mov 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 |
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:
retmemset:
; 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
retstrcpy:
; 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:
retstrcat:
; 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