mips32-compiler
v1.0.4
Published
A package with a method to compile C code into mips32 asm code
Downloads
3
Readme
Mips compiler
Description
This repository constitutes a Pseudo C language compiler to a valid mips32 code with 32 general purpose registers. The generated mips32 code according to this Cheat sheet with several differences. | Instruction | Operation | |-----------------|-------------------| | J address | pc = address | | NOOP | None |
The input string undergoes several transformations before being compiled into Mips32 instructions.
.
First, the C code is passed through the AST(Abstract syntax tree) compiler which splits the code into tokens, then it is assembled into an intermediary ASM instruction set which resembles x86 instruction set with some differences, and at the end is, again translated into mips32 code.
Playground
The compiler can be tested Here
Predefined methods.
There are several predefined methods which can help in order to use this library.
int setElement(int buffer,int pos,int element);
This will set an element at a pointers position (pos << 2). Its equivalent of using buffer[pos] = element. *(buffer + pos << 2) = element;
int getElement(int buffer,int pos);
This will get an element at a pointers position (pos << 2). Its equivalent of using element = buffer[pos]. element = *(buffer + pos << 2);
int printNumber(int element);
Will put the 32 bits number on the stdout pointer position.
int printChar(int element);
Will put a char at the current stdout pointer offset (this is used to print individual ASCII characters).
Examples
Hello world
For this one, we should print all characters one after another.
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
void main() {
printChar(72);printChar(101);
printChar(108);printChar(108);
printChar(111);printChar(32);
printChar(87);printChar(111);
printChar(114);printChar(108);
printChar(100);
}
`, {
stdout: 1024 * 512,
stackPointer: 1024 * 1024,
memorySize: 1024 * 1024 * 4
})
mipsCompiler.compile();
mipsCompiler.run(); // This runs the mips32 instructions in order
console.log(mipsCompiler.stdoutBuffer()); // This will print "Hello World"
Sum of 2 numbers.
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
void main() {
int a = 5;
int b = 10;
printNumber(a + b);
}
`, {
stdout: 1024 * 512, // The pointer from where "mipsCompiler.stdoutBuffer()" will transform the data into a string.
stackPointer: 1024 * 1024, // The stack pointer for when the program will start to run.
memorySize: 1024 * 1024 * 4 // The memory of the process that runs the program in bytes.
})
mipsCompiler.compile();
mipsCompiler.run(); // This runs the mips32 instructions in order
console.log(mipsCompiler.stdoutBuffer()); // This will print 15
Recursive fibbonaci.
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
int fibboRecursive(int n) {
if ( n < 2 ) {
return 1;
}
return fibboRecursive ( n - 1 ) + fibboRecursive ( n - 2 );
}
void main() {
printNumber(fibboRecursive(8));
}
`, {
stdout: 1024 * 512,
stackPointer: 1024 * 1024,
memorySize: 1024 * 1024 * 4
})
mipsCompiler.compile();
mipsCompiler.run(); // This runs the mips32 instructions in order
console.log(mipsCompiler.stdoutBuffer()); // This will print 34
Print first 10 digits
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
void main() {
for(int i = 0; i < 10; i = i + 1) {
printNumber(i);
printChar(32);
}
}
`, {
stdout: 1024 * 512,
stackPointer: 1024 * 1024,
memorySize: 1024 * 1024 * 4
})
mipsCompiler.compile();
mipsCompiler.run();
console.log(mipsCompiler.stdoutBuffer()); // This will print 0 1 2 3 4 5 6 7 8 9
Arrays
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
void main() {
int array = 1024;
for(int i = 0; i < 10; i = i + 1) {
*(array + i * 4) = i;
}
for(int i = 0; i < 10; i = i + 1) {
printNumber(*(array + i * 4));
printChar(32);
}
}
`, {
stdout: 1024 * 512,
stackPointer: 1024 * 1024,
memorySize: 1024 * 1024 * 4
})
mipsCompiler.compile();
mipsCompiler.run();
console.log(mipsCompiler.stdoutBuffer()); // This will print 0 1 2 3 4 5 6 7 8 9
Instructions
In order to print the mips32 instructions code we can use the method.
mipsCompiler.mips32Code().toString(true)
Example
const { Mips32Compiler } = require('mips32-compiler');
let mipsCompiler = new Mips32Compiler(`
void main() {
int a = 5+2*3;
}
`, {
stdout: 1024 * 512,
stackPointer: 1024 * 1024,
memorySize: 1024 * 1024 * 4
});
mipsCompiler.compile();
console.log(mipsCompiler.mips32Code().toString(true));
Will show this (without the comments).
// header part
0: OR $31 $31 $31 // zero register.
1: ADDI $22 $31 0 // split the 1024 * 1024 number into two 16 bits number in order to store it into a register.
2: ADDI $30 $31 16
3: SLL $30 $30 16
4: OR $30 $30 $22
5: ADDI $22 $31 0
6: ADDI $29 $31 8
7: SLL $29 $29 16
8: OR $29 $29 $22
// Header part
9: J 10
10: NOOP // Main method
11: ADDI $0 $31 2
12: ADDI $1 $31 3
13: MULT $0 $1
14: ADDI $2 $26 0 // store LO part register in $2
15: SW $2 0($30) // Adds the result to the stack
16: ADDI $30 $30 4 // Increase the stack pointer with 4
17: ADDI $0 $31 5 // Store 5 into 0 register.
18: ADDI $28 $31 4
19: SUB $28 $30 $28 // Set stack pointer for variable 'a' in register 28
20: LW $1 0($28) // Loads the (5*3) result into $1 register from [stack_pointer - 4] position.
21: ADD $2 $0 $1 // adds the result and stores it into $2 register.
22: ADDI $28 $31 4
23: SUB $30 $30 $28 // pops the stack from the main method.
There is also a config for some of the registers which hold stuff such as stack pointer, high, low (for mult and div) and other such stuff
{
zeroReg: 31, // The register which holds value 0.
stackPointerRegister: 30, // The register which holds the stack pointer
stddoutRegister: 29, // Deprecated register.
freeRegister: 28, // General purpose register used for operations substitutions.
hi: 27,
lo: 26,
testRegister: 25,
rsp: 24, // The register which holds the return value after a function call.
ret: 23, // The register which holds the address where the pc should go after the method yields.
bitSplitterRegister: 22, // A register used for splitting immediates which are higher then 16 bits.
}
What it can do
- Compilation messages in case of errors (although quite limited in what they offer).
- Methods usage.
- Expressions definitions.
- Initializations.
- Assignations.
- Code blocks.
- General pointers access. (Full access to the entire memory).
- Loops.
Limitations and problems.
- The compiler processes only 4 bytes signed integers.
- There are no definitions for stack pointers (int a[100]), a[1]=.... but there is general access to the memory of the program
through pointers.
int a = 5000; *a = 150;
- No direct negative numbers definitions.
// is not allowed. int c = -5;
// is allowed. int c = 0 - 5;
- Even if the asm is mips32, in order to be translated to valid binaries, the amount of generated instructions should be less then 64kb (j jumps support 24 bits address jump but BEQ $a $b label supports only 16 bits offset jump and the compiler does not split the a 32 bits label into 16 bits numbers).
- Assignations are not threated as expressions so things like
a = b = c;
is not considered as valid code. - No comments.
- No instructions assignations such as
a++, a += 2, etc...
; - Might suffer some weird compilation error problems due to bugs.
Notes
The compiler might not be complete and lacks things that C language already have, however it might be a good way to be used for simple MIPS32.