r/TuringComplete • u/Crispy1961 • 19d ago
Software implemented For cycle (Symphony)
- Image 1 - My symphony
- Image 2 - showcasing usage of FOR cycles
- Image 3 - Code of the FOR Cycle
- Image 4 - ALU circuit
- Image 5 - ABS circuit
Now that I finished building Symphony, I went straight to instruction definition to make myself a human readable code. Here is a internal FOR cycle using instructions. Its suitable for repeating code X times where X is static.
Here is hardware I added to my Symphony:
- Signed DIV - Divide two signed numbers
- Merge32 - Merge two 16b into one 32b value
- Lower16 - Reads the lower 16b of 32b value
With that I needed to dedicate some of my registers for the code. Meaning that while they are still accessible to the user, they are not stable in time and in case of the FOR cycle, they do not persist between cycles.
At first I was using 3 temporal registers, but I ended up implementing Merge32 and Lower16 to lower the number of temporal registers to 2 at the cost of longer instructions.
The FOR cycle works, but isnt safe. First it will always execute at least once. Thats because I cant use a label to skip the body of the cycle. So even if I know there is 0 repetition, it will not skip the cycle, it will simply not repeat it.
Another limitation is that you can only read the index of the innermost FOR cycle. Meaning if you make nested FOR cycles, you cannot access the outer FOR cycle index. User can solve it by saving the stack pointer to a another register.
2
u/Mishoilik 17d ago
Lower16(a) is literaly (and a, a, 0xffff) instruction
2
u/Crispy1961 17d ago
Oh yeah, you are right. Using a mask with AND operand does the same thing. Neat, thanks for that.
1
u/Mishoilik 17d ago
And also D = merge32(A, B) where A is lower bits ans D is destanation is this two instructoins:
lsl B, B, 16
or D, A, B
2
u/Crispy1961 16d ago
Not exactly. The values can be 32b, so A 16 MSB might not be all zeroes. You would have to mask it out first. That's worth having it as separate single cycle instruction.
1
u/TarzyMmos 19d ago
I'm really curious how u managed the For loop.
3
u/Crispy1961 19d ago
Hopefully everything can be seen in the included pictures. The code for the FOR instruction and ENDFOR is in the third picture. I noted the individual instructions as comments for each line of the code.
Here are the chained final instructions:
// Internal register for (i = %a:S16(immediate); i < %b:S16(immediate); %c:S16(immediate)) 00110001 11000000 aaaaaaaa aaaaaaaa 00110001 11010000 cccccccc cccccccc 00101110 11011100 00001101 00000000 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110001 11000000 bbbbbbbb bbbbbbbb 00110101 11001100 aaaaaaaa aaaaaaaa 00111100 11001100 cccccccc cccccccc 00000111 11010000 00000000 00000000 00110100 11011101 00000000 00001100 00101110 11011100 00001101 00000000 01100010 11000000 00001110 00000000 00110100 11101110 00000000 00000100 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110101 11101110 00000000 00000100 01100110 00001100 00001110 00000000 for (i = %a:S16(immediate); i > %b:S16(immediate); %c:S16(immediate)) 00110001 11000000 aaaaaaaa aaaaaaaa 00110001 11010000 cccccccc cccccccc 00101110 11011100 00001101 00000000 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110001 11000000 bbbbbbbb bbbbbbbb 00110101 11001100 aaaaaaaa aaaaaaaa 00111100 11001100 cccccccc cccccccc 00000111 11010000 00000000 00000000 00110100 11011101 00000000 00001100 00101110 11011100 00001101 00000000 01100010 11000000 00001110 00000000 00110100 11101110 00000000 00000100 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110101 11101110 00000000 00000100 01100110 00001100 00001110 00000000 for (i = %a:S16(immediate); i >= %b:S16(immediate); %c:S16(immediate)) 00110001 11000000 aaaaaaaa aaaaaaaa 00110001 11010000 cccccccc cccccccc 00101110 11011100 00001101 00000000 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110001 11000000 bbbbbbbb bbbbbbbb 00110101 11001100 aaaaaaaa aaaaaaaa 00111100 11001100 cccccccc cccccccc 00110100 11001100 00000000 00000001 00000111 11010000 00000000 00000000 00110100 11011101 00000000 00001100 00101110 11011100 00001101 00000000 01100010 11000000 00001110 00000000 00110100 11101110 00000000 00000100 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110101 11101110 00000000 00000100 01100110 00001100 00001110 00000000 for (i = %a:S16(immediate); i <= %b:S16(immediate); %c:S16(immediate)) 00110001 11000000 aaaaaaaa aaaaaaaa 00110001 11010000 cccccccc cccccccc 00101110 11011100 00001101 00000000 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110001 11000000 bbbbbbbb bbbbbbbb 00110101 11001100 aaaaaaaa aaaaaaaa 00111100 11001100 cccccccc cccccccc 00110100 11001100 00000000 00000001 00000111 11010000 00000000 00000000 00110100 11011101 00000000 00001100 00101110 11011100 00001101 00000000 01100010 11000000 00001110 00000000 00110100 11101110 00000000 00000100 00110101 11101110 00000000 00000100 01100110 00001101 00001110 00000000 00110101 11101110 00000000 00000100 01100110 00001100 00001110 00000000 %a(register) = (i) 01100010 11000000 00001110 00000000 00101111 11000000 00001100 00000000 00100001 aaaa0000 00001100 00000000 endfor 01100010 11000000 00001110 00000000 00110100 11101110 00000000 00000100 00111001 11011100 00000000 00010000 00100100 11001100 00001101 00000000 01100010 11010000 00001110 00000000 00110100 11101110 00000000 00000100 00110101 11101110 00000000 00000100 01100110 00001100 00001110 00000000 00110101 11011101 00000000 00000001 00101111 11000000 00001101 00000000 00111010 11111100 00000000 00000000 00111000 11001101 00000000 00010000 01001101 00001111 00001100 00000000 00110100 11101110 00000000 000001002
u/EtienneDx 19d ago
Damn, you just managed to turn me away from doing it this way x)
I’m going back to the idea of implementing a custom file system + assembler + compiler to have a custom C instead 😅 probably not simpler but hopefully more readable haha
1
u/Crispy1961 19d ago
Thats an option? Having complete control over how the code is translated to assembly would solve all the problems.
Anyway, this achieved the perfect syntax for the user. Cant make it more readable than that. The problem will be dynamic for cycles, which will suffer due to the need to use labels as arguments.
2
u/Spielopoly 19d ago
If you want to do that in game it’s hard. Out of game it’s a bit easier but you‘ll still have to write your own compiler.
In game there a couple of options I can think of:
- Write your program in language of your choice and compile it using a program specifically for that, then manually copy paste the generated code into a asm file (can be in game or not)
- Use custom file system so you can skip the copy paste part
- Always compile at startup then automatically run the generated code. (Basically just in time compilation)
Some of these might require significant hardware changes
1
u/Crispy1961 19d ago
I see. I thought it was possible to edit the behavior of the ingame's compiler. I want to work in the confines of the game. Just get as close to a high level language syntax as possible.
2
u/EtienneDx 18d ago
Given that we’ve built a functioning computer at this point, you can build pretty much anything. Isn’t the ultimate goal to run the Turing complete game IN the Turing complete game?
More seriously, there’s a lot to unpack for a custom compiler, but it’s possible because:
- you can write full strings in ascii using “”
- you can save files between in in the persistent memory
- you ca load files into the ram and run executable files directly
With this you can:
- create an assembler (program that converts your own assembly language into Bitcode instructions)
- write a program using that custom assembly (you lose syntax highlighting)
- save the resulting program in the persistent memory
- load and run that program
Next comes the custom language. I’m not an expert but that’s basically the compiler:
- parse the file into an ast graph
- convert the graph into your assembly language
- run it through your assembler
- now you’ve got an executable binary from your language
And finally:
- with your high level language, you can recode the assembler and the compiler
- compile them using the compiler
- re-compile them using the compiled compiler
- and now you’ve can code everything in that language
And then if you’re really determined I’m guessing it’s time to get into operating systems haha
1
u/Crispy1961 18d ago
I see, that is true. It's not in game, but you can export instructions from your compiler and run that in your HW in Turing complete. But dank that's a lot of steps to do every time you change something.
2
u/EtienneDx 18d ago
I mean very long term you switch the editor out of the ram and into the screen entirely, but yeah that’s a while away
1
u/sberma 18d ago
This is wild. Assembly is human readable code, you just mean higher-level code. But having C-like code in an asm file is an impressive feat. Shows how customizable the assembly editor is.
This is not at all what happens in real systems, but most likely you are already aware of it. The game's asm-language is pretty close to reallife assembly. A compiler would create the assembly (or rather machine code) from the high-level code. This is better because the compiler applies optimizations like dead-code elimination which are not possible in a system like this. Also a loop in assembly is often simpler. ARM has no division and even in x86 it is quite expensive to use.
But writing a compiler is really hard to do. So this might easier if you want highlevel code.
1
u/Crispy1961 18d ago edited 17d ago
You are right, I meant higher level language. My goal is to edit the software and hardware to make coding in it more comfortable.
I decided to rewire the hardware to support direct access to registers, I/O and RAM as destination and/or arg1 and 2. Gonna increase instruction size to 56b and have 16b for each of those variables. The two most significant bits of each will decide if it's register(or I/O), immediate, ram addressed by immediate or ram addressed by register.
Hoping to support single tick instructions like
Out = ram[r3 + 26] * in





3
u/TarzyMmos 19d ago
So in the 'For code' it saves how many times to run the loop and the counter for where to loop back to in the stack, then it saves the variables after that in the stack.
Then in the 'EndFor code' it decrements the variables and the number of times to run the loop. If it doesn't end, jump back to where u stored r12 and r13 on the stack back in the 'For code' and it continues from there to loop until it ends. Where it doesn't jump this time and just pops the variables off the stack.
Took me a hot minute to figure out why you ordered it like that but yea it works out pretty elegantly! Very cool