A programming language that compiles to human-readable mips assembly.
Made with Typescript, Assembly
web demo: https://pfg.pw/masc
mips masc
generate mips from human code
example:
\\.text
\\j main
fn gcd(a: i32, b: i32) i32 {
if b == 0 {return a;}
return gcd(b, a%b);
}
\\main:
print_int(gcd(25, 15));
// ----- //
inline fn print_int(value: i32) void {
$v0 = 1;
$a0 = value;
syscall();
}
inline fn syscall() void {
\\syscall
!clear $call;
}
↓
.text # .text
j main # j main
# ====================
# jal call_gcd
# args:
# $a0: a - i32
# $a1: b - i32
# return:
# $v0: i32
# ====================
call_gcd: # fn gcd(a: i32, b: i32) i32{
# save used s registers to stack
subiu $sp, $sp, 4 # $sp = &$sp[-1]
sw $ra, 0($sp) # $sp[0] = $ra
#
move $t0 $a0 # a = $a0
move $t1 $a1 # b = $a1
# body
bnez $t1, if_end # if b == 0 {
move $v0 $t0 # . a
j deinit_gcd # return ^;
if_end: # }
move $a0 $t1 # . b
rem $a1, $t0 $t1 # | a % b
jal call_gcd # | gcd(^, ^^^^^)
move $v0, $v0 # return$v0 = ^^^^^^^^^^^^^
#
deinit_gcd: # cleanup:
# reload used s registers from stack
lw $ra, 0($sp) # $ra = $sp[0]
addiu $sp, $sp, 4 # $sp = &$sp[1]
jr $ra # }
main: # main:
li $a0 25 # . 25
li $a1 15 # | 15
jal call_gcd # | gcd(^^, ^^)
move $t0, $v0 # | ^^^^^^^^^^^
li $v0 1 # print_int(^^^^^^^^^^^)
move $a0 $t0 #
syscall #
syntax
inspired by zig.
statement:
-
var varname: TYPE = EXPRESSION;
- define variable -
variable | register = EXPRESSION;
- set variable. egmyvar = 3;
or$v0 = 5;
-
save EXPRESSION = EXPRESSION
- save into memory. egsave myptr.* = 25;
. you may wonder why this is not justmyptr.* = 25;
. it should be. -
inline? fn function_name(arg_name: TYPE, ...) { STATEMENT... }
- define ¿inline? function. make sure to jump over functions. functions will save any needed things to the stack (and unneeded because they will always save $ra even if they don't call any functions oops) -
loop { STATEMENT ... }
- loop forever. break/continue out of the loop. -
if EXPRESSION operator EXPRESSION { STATEMENT... }
- expression:<=
,<
,==
,!=
,>
,>=
. -
!clear $ra, $call, ...
- tell the register allocator that these variables cannot be used.$call
expands into $t0-7, $a0-4, $ra, $v0-1. this way, using a variable across a syscall makes it go into $s0-7. -
\\inline assembly...
- inline assembly currently has no option to use variables. make sure to !clear any used registers after inline assembly. eg:\syscall
-
return EXPRESSION?;
- return a value from a function -
break;
- break from a loop -
continue;
- continue in a loop
types:
-
u32
,i32
,u8
- unsigned and signed integer types -
[*]TYPE
- pointer to array (indexable, math supported) -
*TYPE
- pointer to one (not indexable, no math) -
void
- nothing. for use as a return value -
any
- any value (up to 32 bits)
expression:
variable_name
-
@TYPE:data_name
- get something from the .data section with name data_name and type TYPE. example:@i32:len
or@[*]i32:x[25]
. masc doesn't help with defining items in the .data section, do it yourself. -
$register_name
- use a register -
function_name(EXPRESSION, ...)
- call a function -
EXPRESSION + - * / ^ % EXPRESSION
- math. eg1 + 1
or5 * 3 + 6 % 8
.^
is binary xor. -
EXPRESSION[EXPRESSION]
- index array, egsomearray[5]
-
EXPRESSION.*
- get value of pointer, egsomepointer.*
-
&EXPRESSION
- address of expression, eg&somepointer[2]
-
undefined
- anything. egvar varname: u32 = undefined;
-
25
- any number, eg-8
or5325
. no binary literals, hex literals, or floating point numbers supported.
comments:
-
// asdfnjdksalk
- comment that will not be visible in the output code -
/* asdfnjdks */
- comment that will not be visible in the output code -
\\# comment that will be visible in the output code
(inline assembly "#") -
\\
- newline that will be visible in the output code
there's probably more that I'm missing. look at the examples.
notes
all variables are stored in a register. if you run out of registers, save some things to the stack manually yourself.
issues
- no parenthesis expression. you can't do
(1 + 1) * 2
- newlines have to be explicitly preserved
- inline function call comments are all on one line
- no strings, no way to make a print function
- no way to make a macro fn
- lots of missing integer types
- supporting larger types (eg doubles) would require a pretty big refactor probably
- too many registers has no position associated with the error
- register allocation bugs probably
- intermediate representation is using untyped strings and regex replace rather than actual objects