SICO
SICO is a Single Instruction COmputer. Originally named Unileq, it is an unsigned version of Subleq. Instead of jumping if A-B<=0
, SICO jumps if A-B
generates a borrow or zero.
Overview
A basic SICO instruction looks like this when implemented in Python:
A = mem[IP+0] B = mem[IP+1] C = mem[IP+2] IP += 3 if mem[A] <= mem[B]: IP = C mem[A] -= mem[B]
Interaction with the host environment is done by writing to special addresses. For instance: if A=-1
, the program will terminate. If A=-2
, then mem[B]
will be printed to the console.
Assembly
The assembly language is fairly simple:
Name | Function |
---|---|
label:
|
Defines a label |
.sublabel:
|
Defines a sublabel |
?
|
The current address |
#
|
Single line comment |
#| ... |#
|
Block comment |
Functions
Functions can easily be implemented with no need for assembler macros like in other OISC's. We can call a function by executing a SICO instruction that places the current IP
in address 0
, and then jumps to the function. For instance:
0 ? uint.add A B C
This can be broken down into two components:
0 ? uint.add # calling instruction
A B C # parameters for the function
The function uint.add
will use the value in address 0 to determine where it was called from. It will then load A
, B
, and C
, perform [A]=[B]+[C]
, and then jump back to the address after C
to resume program flow.
For completeness, this is what the uint.add
function is in SICO assembly:
uint.add: # Call : 0 ? uint.add ret a b # # Effect: [ret] = [a] + [b] # # Time : 30 # # Use [0] to get the calling address. .tmp .tmp ?+1 .tmp .arg2 ?+1 0 .tmp ?+1 0 .off ?+1 .arg2 0 ?+1 .arg3 0 ?+1 .arg4 0 ?+1 .arg5 0 ?+1 # Get [ret] .ret0 .arg2:2 ?+1 .ret1 .ret0 ?+1 .ret2 .ret0 ?+1 .tmp .tmp ?+1 .tmp .ret1 ?+1 .ret0 .ret0 ?+1 .ret0 .tmp ?+1 # Get [a] .tmp .tmp ?+1 .tmp .arg3:3 ?+1 .a0 .a0 ?+1 .a0 .tmp ?+1 # Get [b] .tmp .tmp ?+1 .tmp .arg4:4 ?+1 .b0 .b0 ?+1 .b0 .tmp ?+1 # Set [ret] .tmp .tmp ?+1 .tmp .a0:0 ?+1 .tmp .b0:0 ?+1 .ret0:0 .ret1:0 ?+1 .ret2:0 .tmp ?+1 # Return 0 0 .arg5:5 # Variables .off:2 .tmp:0
Performance
Using the function calling notation above, several libraries have been created for SICO (here) which implement most common programming functions.
Time complexities for some of the functions are given below. Time is measured in number of instructions needed when using 64-bit underlying integers.
Function | Time |
---|---|
uint.write
|
2303 |
uint.read
|
1456 |
uint.cmp
|
29 |
uint.min
|
34 |
uint.max
|
34 |
uint.set
|
24 |
uint.neg
|
25 |
uint.add
|
30 |
uint.sub
|
31 |
uint.mul
|
885 |
uint.div
|
613 |
uint.gcd
|
1330 |
uint.shl
|
168 |
uint.shr
|
543 |
uint.not
|
26 |
uint.and
|
485 |
uint.or
|
486 |
uint.xor
|
487 |
Comparisons vs Subleq
The two major differences between SICO and Subleq are the signedness of the values and the order of the operands.
For the values A B C
, Subleq would perform mem[B] -= mem[A]
, while SICO will perform mem[A] -= mem[B]
.
SICO also guarantees 3 conditions in which an instruction will jump:
- If
mem[A] = mem[B]
- If
mem[A] = 0
- If
mem[B] = -1
While Subleq only guarantees a jump if mem[A] = mem[B]
. These extra conditions have helped SICO when optimizing mathematical functions.
See also
External resources
SICO - An online interpreter for SICO.