IOTA-C0
Paradigm(s) | Imperative |
---|---|
Designed by | User:Enoua5 |
Appeared in | 2018 |
Memory system | Cell based |
Computational class | Turing complete |
Major implementations | IOTA-C0 Suite |
File extension(s) | .c0 .ic0 |
Introduction
IOTA-C0 is a processor specification created by User:Enoua5 in 2018. It was meant to work entirely off of computational RAM, its only native instruction being to copy the value in one memory address into another memory address. There were a few decisions in the creation of this specification that would make the IOTA-C0 very impractical to build, though it does make the programming a little easier.
Processor Documentation
Memory
Basics
RAM: 65536 16-bit addresses, from 0x0000 to 0xFFFF
Registers: 2 16-bit registers for holding data read from RAM
Layout of RAM
Section name | Start address | End address | Read only? | Description | Default value |
---|---|---|---|---|---|
Literals | 0x0000 | 0x7FFF | YES |
Because the IOTA-C0 can only work with pointers, these memory addresses hold literals, so that programs can actually use literals. |
Their address in memory |
Computation RAM | 0x8000 | 0x807F | |||
Stack A | 0x8080 | 0x80BF | YES |
Values are filled from low end to high end. If all 64 are filled and a value is pushed, nothing happens If there are no values in the stack when a value is popped, returns 0x0000 and makes no change |
0 |
Stack B | 0x80C0 | 0x80FF | YES |
Values are filled from low end to high end. If all 64 are filled and a value is pushed, nothing happens If there are no values in the stack when a value is popped, returns 0x0000 and makes no change |
0 |
Standard RAM | 0x8100 | 0xFFFF | NO | Low end reserved for program; all rest is for the program's variables | 0 |
Clock Cycle
The following procedure is run every clock cycle
- Copy the value in memory address 0x8000 into register 0
- Copy the value in the memory address pointed to by the value in register 0 into register 0
- Increment the value in memory address 0x8000
- Copy the value in memory address 0x8000 into register 1
- Copy the value in the memory address pointed to by the value in register 1 into register 1
- Increment the value in memory address 0x8000
- Copy the value in the memory address pointed to by the value in register 1 into the memory address pointed to by the value in register 0
Or, in less verbose terms:
- R[0]=M[0x8000]
- R[0]=M[R[0]]
- M[0x8000]++
- R[1]=M[0x8000]
- R[1]=M[R[1]]
- M[0x8000]++
- M[R[0]]=M[R[1]]
Computational RAM
Computation RAM is how the IOTA-C0 performs all its computation. The values in this area (0x8000-0x807F) change between clock cycles based on nearby values, following the rules detailed in the table below.
A hex number is a literal.
A hex number in brackets is a value pointed to by the number. (a pointer)
A hex number in double brackets is a value pointed to by the number at the address pointed to by the number. (a pointer pointer)
Address | Read only? | Operation |
---|---|---|
0x8000 | NO | Instruction pointer Default value: 0x8100 |
0x8001 | NO | Value becomes value|0x8000 Default value: 0x807F |
0x8002 | NO | The value here gets written to [[0x8001]] |
0x8003 | NO | Normal slot |
0x8004 | NO | Value becomes value|0x8000 |
0x8005 | NO | Value becomes value|0x8000 |
0x8006 | YES | Value becomes [0x8004] if [0x8003] is not 0x0000; Value becomes [0x8005] id [0x8003] is 0x0000 |
0x8007 | NO | Normal slot |
0x8008 | NO | Normal slot |
0x8009 | NO | Normal slot |
0x800A | YES | Value becomes [0x8008] if [0x8007] is not 0x0000; Value becomes [0x8009] id [0x8007] is 0x0000 |
0x800B | NO | Normal slot |
0x800C | NO | Normal slot |
0x800D | YES | Value becomes [0x800B]&[0x800C] |
0x800E | NO | Normal slot |
0x800F | NO | Normal slot |
0x8010 | YES | Value becomes [0x800E]|[0x800F] |
0x8011 | NO | Normal slot |
0x8012 | NO | Normal slot |
0x8013 | YES | Value becomes [0x8011]XOR[0x8012] |
0x8014 | NO | Normal slot |
0x8015 | YES | Value becomes ~[0x8014] |
0x8016 | NO | Normal slot |
0x8017 | NO | Normal slot |
0x8018 | YES | Value becomes 0xFFFF if [0x8016]<[0x8017]; Value becomes 0x0000 otherwise |
0x8019 | NO | Normal slot |
0x801A | NO | Normal slot |
0x801B | YES | Value becomes 0xFFFF if [0x8019]<=[0x801A]; Value becomes 0x0000 otherwise |
0x801C | NO | Normal slot |
0x801D | NO | Normal slot |
0x801E | YES | Value becomes 0xFFFF if [0x801C]>[0x801D]; Value becomes 0x0000 otherwise |
0x801F | NO | Normal slot |
0x8020 | NO | Normal slot |
0x8021 | YES | Value becomes 0xFFFF if [0x801F]>=[0x8020]; Value becomes 0x0000 otherwise |
0x8022 | NO | Normal slot |
0x8023 | NO | Normal slot |
0x8024 | YES | Value becomes 0xFFFF if [0x8022]==[0x8023]; Value becomes 0x0000 otherwise |
0x8025 | NO | Normal slot |
0x8026 | NO | Normal slot |
0x8027 | YES | Value becomes 0xFFFF if [0x8025]!=[0x8026]; Value becomes 0x0000 otherwise |
0x8028 | NO | Normal slot |
0x8029 | NO | Normal slot |
0x802A | YES | Value becomes [0x8028]>>>[0x8029] |
0x802B | NO | Normal slot |
0x802C | NO | Normal slot |
0x802D | YES | Value becomes [0x802B]>>[0x802C] |
0x802E | NO | Normal slot |
0x802F | NO | Normal slot |
0x8030 | YES | Value becomes [0x802E]<<[0x802F] |
0x8031 | NO | Normal slot |
0x8032 | NO | Normal slot |
0x8033 | YES | Value becomes [0x8031]+[0x8032] |
0x8034 | NO | Normal slot |
0x8035 | NO | Normal slot |
0x8036 | YES | Value becomes [0x8034]-[0x8035] |
0x8037 | NO | Normal slot |
0x8038 | NO | Normal slot |
0x8039 | YES | Value becomes [0x8037]*[0x8038] |
0x803A | NO | Normal slot |
0x803B | NO | Normal slot |
0x803C | YES | Value becomes [0x803A]/[0x803B] Division by 0 results in 0 |
0x803D | NO | Normal slot |
0x803E | NO | Normal slot |
0x803F | YES | Value becomes [0x803D]%[0x803E] Division by 0 results in 0 |
0x8040 | NO | Normal slot |
0x8041 | YES | Value becomes [0x8040]+1 |
0x8042 | NO | Normal slot |
0x8043 | YES | Value becomes [0x8042]-1 |
0x8044 | NO | Normal slot |
0x8045 | YES | Value becomes [0x8044]*-1 |
0x8046 | NO | Value becomes value|0x8000 |
0x8047 | NO | Normal slot |
0x8048 | YES | If a non-zero value is attempted to be moved here, [0x8047] is pushed to stack A |
0x8049 | NO | Normal slot |
0x804A | YES | If a non-zero value is attempted to be moved here, [0x8049] becomes the value popped off stack A |
0x804B | NO | Normal slot |
0x804C | YES | If a non-zero value is attempted to be moved here, [0x804B]|0x8000 is pushed to stack B |
0x804D | NO | Normal slot |
0x804E | YES | If a non-zero value is attempted to be moved here, [0x804D] becomes the value popped off stack B |
0x804F | YES | Returns a random value when read |
0x8050 | YES | Normal slot |
0x8051 | YES | Value becomes the value in [[0x8050]] |
0x8052-0x807E | YES | Unassigned |
0x807F | YES | If a non-zero value is attempted to be moved here, all writable memory is set to 0 |
Assembly Documentation
Preprocessor
A preprocessot instruction begins with a .
character on its own line. The dot is followed by the command, then arguments seperated by spaces.
Commands
Command | Arguments | Description | Technical |
---|---|---|---|
.const | name value | Creates a constant that can be called by prepending : to the front of the name. |
|
.var | name | Declares a varible that can be called by prepending : to the front of the name. |
|
.var | name[number] | Declares an array that can be called by prepending : to the front of the name and appending an index in brackets. |
|
Preprocessor Example
If we start with the following code;
.var variable
.var array[3]
.const constant1 10
.const constant2 42
ADD :constant1 :constant2 :array[1]
ADD :array[1] :array :variable
First, vars will be compiled into consts to get this;
.const variable 0xFFFF
.const array[2] 0xFFFE
.const array[1] 0xFFFD
.const array[0] 0xFFFC
.const array 0x7FFC
.const constant1 10
.const constant2 42
ADD :constant1 :constant2 :array[1]
ADD :array[1] :array :variable
Then, the consts will be pasted into the code to get this;
ADD 10 42 0xFFFF
ADD 0xFFFE 0x7FFC 0xFFFF
Assembly
- One command per line
#
denotes a comment- Any amount of whitespace can go before or after a command
- One of more white space characters (excluding new line) can go between arguments
- Arguments can be any dec, hex, octal, or binary number from 0x000-0xFFFF
- dec is written as just a number (eg 42)
- hex is written as 0x and a number (eg 0x2A)
- octal is written as 0 and a number (eg 052)
- binary is written as a 0b and a number (eg 0b101010)
- * can be put before a number to to work as an address (eg *0x10 becomes 0x8010 when compiled)
In this table:
- An argument prepended with
:
will default to being prepended with*
, even if this is not put there in the program. This cannot be turned off. - An argument in brackets means that it is optional. A section of compilation in brackets means that it will only be present if the optional argument is present
Command | Arguments | Mnemonic | Description | Compiles into |
---|---|---|---|---|
NOP | None | No Operation | Does nothing |
0000 0000 |
MOV | :N M | Move | Copy M into address N |
NNNN MMMM |
JMP | N | Jump | Jump execution to address N |
8046 NNNN 8000 8046 |
QJP | :N | Quick Jump (Can't take literals) | Jump execution to the address in address N |
8000 NNNN |
SET | N M | Set | Set the value in memory address N to M |
8001 NNNN 8002 MMMM |
STA | N | Set Address | Like SET, but without changing the value currently being used |
8001 NNNN |
STV | N | Set Value | Like SET, but without changing the address currently being used |
8002 NNNN |
TNA | N M L [:K] | Ternary Address | If N is nonzero, set the value in memory address K to :M; else set it to :L |
8003 NNNN 8004 MMMM 8005 LLLL [KKKK 8006] |
TRN | N M L [:K] | Ternary | If N is nonzero, set the value in memory address K to M; else set it to L |
8007 NNNN 8008 MMMM 8009 LLLL [KKKK 800A] |
AND | N M [:L] | And | Write the result of the bitwise-and operation between N and M to memory address L |
800B NNNN 800C MMMM [LLLL 800D] |
ORR | N M [:L] | Or | Write the result of the bitwise-or operation between N and M to memory address L |
800E NNNN 800F MMMM [LLLL 8010] |
XOR | N M [:L] | Exclusive Or | Write the result of the bitwise-xor operation between N and M to memory address L |
8011 NNNN 8012 MMMM [LLLL 8013] |
NOT | N [:M] | Not | Write the result of the bitwise-not operation of N to memory address M |
8014 NNNN [MMMM 8015] |
LST | N M [:L] | Less Than | If M is less than M, set the value in memory address L to 0xFFFF; else set it to 0 |
8016 NNNN 8017 MMMM [LLLL 8018] |
LOE | N M [:L] | Less Than or Equal | If M is less than or equal to M, set the value in memory address L to 0xFFFF; else set it to 0 |
8019 NNNN 801A MMMM [LLLL 801B] |
GRT | N M [:L] | Greater Than | If M is greater than M, set the value in memory address L to 0xFFFF; else set it to 0 |
801C NNNN 801D MMMM [LLLL 801E] |
GOE | N M [:L] | Greater Than or Equal | If M is greater than or equal to M, set the value in memory address L to 0xFFFF; else set it to 0 |
801F NNNN 8020 MMMM [LLLL 8021] |
EQU | N M [:L] | Equal | If M is equal to M, set the value in memory address L to 0xFFFF; else set it to 0 |
8022 NNNN 8023 MMMM [LLLL 8024] |
NEQ | N M [:L] | Not Equal | If M is not equal to M, set the value in memory address L to 0xFFFF; else set it to 0 |
8025 NNNN 8026 MMMM [LLLL 8027] |
SHR | N M [:L] | Shift Right | Shifts N right by M bits and saves the value in memory address L |
8028 NNNN 8029 MMMM [LLLL 802A] |
ASR | N M [:L] | Arithmatic Shift Right | Shifts N right by M bits arithmatically and saves the value in memory address L |
802B NNNN 802C MMMM [LLLL 802D] |
SHL | N M [:L] | Shift Left | Shifts N left by M bits and saves the value in memory address L |
802E NNNN 802F MMMM [LLLL 8030] |
ADD | N M [:L] | Add | Add N and M, saving the result in memory address L |
8031 NNNN 8032 MMMM [LLLL 8033] |
SUB | N M [:L] | Subtract | Subtract M form N, saving the result in memory address L |
8034 NNNN 8035 MMMM [LLLL 8036] |
MUL | N M [:L] | Multiply | Multiply N by M, saving the result in memory address L |
8037 NNNN 8038 MMMM [LLLL 8039] |
DIV | N M [:L] | Divide | Divide N by M, saving the result in memory address L. Division by zero results in a 0. |
803A NNNN 803B MMMM [LLLL 803C] |
MOD | N M [:L] | Modulo | Divide N by M, saving the remainder of result in memory address L. Modulo by zero results in a 0. |
803D NNNN 803E MMMM [LLLL 803F] |
INC | N [:M] | Increment | Add 1 to N, saving the result in memory address M |
8040 NNNN [MMMM 8041] |
DEC | N [:M] | Decrement | Subtract 1 from N, saving the result in memory address M |
8042 NNNN [MMMM 8043] |
NEG | N [:M] | Negative | Multiply N by -1 (i.e. 0xFFFF), saving the result in memory address M |
8044 NNNN [MMMM 8045] |
ADR | N [:M] | Address | Use bitwise-or with N and 0x8000 (i.e. make a litteral into an pointer), saving the result in memory address M |
8046 NNNN [MMMM 8046] |
PSH | N | Push | Push the value N onto stack A |
8047 NNNN 8048 0001 |
POP | [:N] | Pop | Pop a value off of stack A and save it in memory address N |
804A 0001 [NNNN 8049] |
PSA | N | Push Address | Use bitwise-or with N and 0x8000 (i.e. make a litteral into an pointer), and push the result onto stack B (the call-stack) |
804B NNNN 804C 0001 |
POA | [:N] | Pop Address | Pop a value off of stack B (the call-stack), and the it in memory address N |
804E 0001 [NNNN 804D] |
RND | :N | Random | Get a random number and save it in memory address N |
NNNN 804F |
GET | :N [:M] | Get | Copy the value pointed to by the value in memory address N and copy it into memory address M |
8050 NNNN [MMMM 8051] |
LBL | :N | Label | Save the address of the next command in memory address N |
NNNN 8000 |
CAL | N | Call | Jump execution to memory address N, pushing the memory address of the next command onto stack B (the call-stack) |
8031 0008 8032 8000 804B 8033 804C 0001 8046 NNNN 8000 8046 |
RET | None | Return | Pop a pointer off stack B (the call-stack) and jump exectuation to it. |
804E 0001 8000 804D |
HLT | None | Halt | Set all writable RAM to 0, stopping execution. |
807F 0001 |
Postprocessor
A postprocessor instruction begins with a . character on its own line.
All postprocessor instructions and targets are ignored by the assembler.
Command | Arguments | Description | Technical |
---|---|---|---|
.lbl | name | Will create a label which can be jumped to by the JMP command. | Will replace all instances of &name with the address of the next instruction in memory |
Example Programs
Fill
Fills the memory with 0x0F0F
#uses only draft 1 SET 0x0200 0x0F0F ADR 0x0108 MOV 0x01FF *0x0046 INC *0x0001 0x0001 QJP 0x01FF
Fibonacci
Calculates the Fibonacci sequence
#uses only draft 2, which added more instructions #initial state MOV 0x200 1 MOV 0x201 1 #locations of the most recent numbers ADR 0x200 0x1FE ADR 0x201 0x1FF #location of the next number STA 0x202 #start the loop LBL 0x1FB #add the two numbers together GET 0x1FE 0x1FC GET 0x1FF 0x1FD ADD *0x1FC *0x1FD #write the sum to the referenced address STV *0x33 #increment the pointers INC *0x1FE 0x1FE INC *0x1FF 0x1FF #the address of DFA INC *0x1 0x1 #jump to the start of the loop QJP 0x1FB
Factorial
Calculates the factorials for the numbers 1 through 8
#uses draft 3, which added pre and post processor JMP &start_program .var results[8] .var n .var tot .lbl fact #init factorial MOV :tot 1 #start the body of the function .lbl fact_body #check if n is at or below 1 .var fact_reached_1 LOE :n 1 :fact_reached_1 #if it is, exit the function .var fact_jump_loc TNA :fact_reached_1 &fact_ret &fact_mult :fact_jump_loc QJP :fact_jump_loc #otherwise, tot=tot*(n--) .lbl fact_mult MUL :n :tot :tot DEC :n :n #recursion! CAL &fact_body #the exit .lbl fact_ret RET .lbl start_program #for i=1 .var i MOV :i 1 .var for_loop LBL :for_loop #i<=8 .var end_of_array LOE :i 8 :end_of_array .var for_jump TNA :end_of_array &continue_for &exit_for :for_jump QJP :for_jump .lbl continue_for #get i factorial into tot MOV :n :i CAL &fact #put it into results .var addr ADD :results :i :addr DEC :addr :addr ADR :addr :addr SET :addr :tot #i++ INC :i :i QJP :for_loop .lbl exit_for .lbl exit_program JMP &exit_program
The Future
I am considering creating another similar processor specification, the IOTA-C1. If I decide to go with it, the C1 would improve upon many of the C0's shortcomings.
- The C0's GET and SET commands require a memory look-up, which cannot be reasonably done from the C-RAM of a physical processor. Therefore, those commands would have to be replaced or removed entirely. I don't know what to replace them with though.
- The C0 has no interactive IO. The C1 would add a few bytes of C-RAM for the purpose of communicating with external devices.
- The HLT command was put in before the LBL command was added. It doesn't provide any reasonable purpose now, and should be removed for the C1.
- The C0 is very limited on memory. The C1 could (though this is more questionable) provide a way to bank-swap sections of RAM. It is possible that the literals section could be used for this purpose, but I'm not so sure of the idea of losing access to literals in order to save more data.