Files
retrobsd/src/cmd/smallc/codemips.c
2014-04-09 14:27:18 +01:00

825 lines
16 KiB
C

#include <stdio.h>
#include "defs.h"
#include "data.h"
/*
* Some predefinitions:
*
* INTSIZE is the size of an integer in the target machine
* BYTEOFF is the offset of an byte within an integer on the
* target machine. (ie: 8080,pdp11 = 0, 6809 = 1,
* 360 = 3)
* This compiler assumes that an integer is the SAME length as
* a pointer - in fact, the compiler uses INTSIZE for both.
*/
#define BYTEOFF 0
/*
* Print all assembler info before any code is generated.
*/
header()
{
output_string ("#\tSmall C for MIPS32\n");
output_string ("#\tRetroBSD Project\n");
output_string ("#\n");
output_string ("\t.set\tnoreorder\n");
//output_line ("global\tTlneg");
//output_line ("global\tTcase");
//output_line ("global\tTeq");
//output_line ("global\tTne");
//output_line ("global\tTlt");
//output_line ("global\tTle");
//output_line ("global\tTgt");
//output_line ("global\tTge");
//output_line ("global\tTult");
//output_line ("global\tTule");
//output_line ("global\tTugt");
//output_line ("global\tTuge");
//output_line ("global\tTbool");
//output_line ("global\tTmult");
//output_line ("global\tTdiv");
//output_line ("global\tTmod");
}
newline()
{
output_byte ('\n');
}
galign(t)
int t;
{
int sign;
if (t < 0) {
sign = 1;
t = -t;
} else
sign = 0;
t = (t + INTSIZE - 1) & ~(INTSIZE - 1);
t = sign? -t: t;
return (t);
}
/*
* Return size of an integer.
*/
intsize()
{
return INTSIZE;
}
/*
* Return offset of ls byte within word.
* (ie: 8080 & pdp11 is 0, 6809 is 1, 360 is 3)
*/
byteoff()
{
return BYTEOFF;
}
/*
* Output internal generated label prefix.
*/
void output_label_prefix()
{
output_string (".L");
}
/*
* Output a label definition terminator.
*/
void output_label_terminator()
{
output_string (":");
}
/*
* Begin a comment line for the assembler.
*/
void gen_comment() {
output_byte ('#');
}
/*
* Print any assembler stuff needed after all code.
*/
trailer()
{
}
/*
* Function prologue.
*/
fentry(int argtop)
{
int i;
/* Save register arguments to stack. */
for (i=0; i<argtop; i+=4)
fprintf(output, "\tsw\t$a%d, %d($sp)\n", i>>2, i);
/* Allocate an empty call frame for 4 args.
* Plus additional 4 bytes to save RA. */
output_line("addiu\t$sp, -20");
output_line("sw\t$ra, 16($sp)");
}
/*
* Text (code) segment.
*/
code_segment_gtext()
{
output_line (".text");
}
/*
* Data segment.
*/
data_segment_gdata()
{
output_line (".data");
}
char *inclib() {
#ifdef cpm
return("B:");
#endif
#ifdef unix
#ifdef INCDIR
return(INCDIR);
#else
return "";
#endif
#endif
}
/*
* Output the variable symbol at scptr as an extrn or a public.
*/
void ppubext (symbol_t *scptr)
{
if( scptr->storage == STATIC )
return;
output_string ("\t.globl\t");
output_string (scptr);
newline();
}
/*
* Output the function symbol at scptr as an extrn or a public
*/
void fpubext (symbol_t *scptr)
{
ppubext (scptr);
}
/*
* Output a decimal number to the assembler file.
*/
void output_number(int num)
{
fprintf(output, "%d", num);
}
/*
* Fetch a static memory cell into the primary register.
*/
void gen_get_memory(symbol_t *sym)
{
output_string ("\tla\t$t0, ");
output_string (sym->name);
newline();
if ((sym->identity != POINTER) & (sym->type & CCHAR)) {
if(sym->type & UNSIGNED ) {
output_line ("lbu\t$v0, 0($t0)");
} else {
output_line ("lb\t$v0, 0($t0)");
}
} else {
output_line ("lw\t$v0, 0($t0)");
}
}
/*
* Fetch the address of the specified symbol into the primary register.
*/
int gen_get_location(symbol_t *sym)
{
if( sym->storage == LSTATIC) {
output_string ("\tla $v0, ");
print_label(sym->offset);
newline();
} else {
output_string("\taddiu\t$v0, $sp, ");
output_number (sym->offset - stkp);
newline();
}
}
/*
* Store the primary register into the specified static memory cell.
*/
void gen_put_memory(symbol_t *sym)
{
output_string ("\tla\t$t0, ");
output_string (sym->name);
newline();
if ((sym->identity != POINTER) & (sym->type & CCHAR)) {
output_line ("sb\t$v0, 0($t0)");
} else {
output_line ("sw\t$v0, 0($t0)");
}
}
/*
* Store the specified object type in the primary register
* at the address on the top of the stack.
*/
void gen_put_indirect(char typeobj)
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
if (typeobj & CCHAR)
output_line ("sb\t$v0, 0($t1)");
else
output_line ("sw\t$v0, 0($t1)");
stkp = stkp + INTSIZE;
}
/*
* Fetch the specified object type indirect through the primary
* register into the primary register.
*/
void gen_get_indirect(char typeobj, int reg)
{
if (typeobj & CCHAR) {
if( typeobj & UNSIGNED ) {
output_line ("lbu\t$v0, 0($v0)");
} else {
output_line ("lb\t$v0, 0($v0)");
}
} else {
output_line ("lw\t$v0, 0($v0)");
}
}
/*
* Swap the primary and secondary registers.
*/
gen_swap()
{
output_line ("move\t$at, $v0\n\tmove\t$v0, $v1\n\tmove\t$v1, $at");
}
/*
* Print partial instruction to get an immediate value into
* the primary register.
*/
gen_immediate_a()
{
output_string ("\tla\t$v0, ");
}
gen_immediate_c()
{
output_string ("\tli\t$v0, ");
}
/*
* Push the primary register onto the stack.
*/
gen_push()
{
output_line ("addiu\t$sp, -4");
output_line ("sw\t$v0, 16($sp)");
stkp = stkp - INTSIZE;
}
/*
* Pop the top of the stack into the secondary register.
*/
gen_pop()
{
output_line ("lw\t$v1, 16($sp)");
output_line ("addiu\t$sp, 4");
stkp = stkp + INTSIZE;
}
/*
* Swap the primary register and the top of the stack.
*/
gen_swap_stack()
{
output_line ("move\t$t1, $v0");
output_line ("lw\t$v0, 16($sp)");
output_line ("sw\t$t1, 16($sp)");
}
/*
* Call the specified subroutine name.
*/
gen_call (char * sname)
{
output_string ("\tjal\t");
if (*sname == '^') {
output_string ("__sc_");
sname++;
}
output_string (sname);
newline();
output_line ("nop"); /* fill delay slot */
}
/*
* Return from subroutine.
*/
gen_ret()
{
output_line("lw\t$ra, 16($sp)");
output_line("jr\t$ra");
output_line("addiu\t$sp, 20");
}
/*
* Perform subroutine call to value on top of stack.
*/
callstk()
{
output_line ("lw\t$t1, 16($sp)");
output_line("jr\t$t1");
output_line ("addiu\t$sp, 4");
stkp = stkp + INTSIZE;
}
/*
* Jump to specified internal label number.
*/
gen_jump (int label)
{
output_string ("\tj\t");
print_label (label);
newline();
output_line ("nop");
}
/*
* Test the primary register and jump if false to label.
*/
gen_test_jump (int label, int ft)
{
if (ft)
output_string("\tbne\t$v0, $zero, ");
else
output_string("\tbeq\t$v0, $zero, ");
print_label (label);
newline();
output_line("nop"); // fill delay slot
}
/*
* Print pseudo-op to define a byte.
*/
gen_def_byte()
{
output_string ("\t.byte\t");
}
/*
* Print pseudo-op to define storage.
*/
gen_def_storage()
{
output_string ("\t.space\t");
}
/*
* Print pseudo-op to define a word.
*/
gen_def_word()
{
output_string ("\t.word\t");
}
/*
* Generate alignment to a word boundary.
*/
gen_align_word()
{
output_string ("\t.align\t2\n");
}
/*
* Modify the stack pointer to the new value indicated.
*/
gen_modify_stack (int newstkp)
{
int k;
k = newstkp - stkp;
if (k % INTSIZE)
error("Bad stack alignment (compiler error)");
if (k == 0)
return (newstkp);
output_string ("\taddiu\t$sp, ");
output_number (k);
newline();
return (newstkp);
}
/*
* Multiply the primary register by INTSIZE.
*/
gen_multiply_by_two()
{
output_line ("sll\t$v0, 2");
}
/*
* Divide the primary register by INTSIZE.
*/
gen_divide_by_two()
{
output_line ("sra\t$v0, 2");
}
/*
* Case jump instruction.
*/
gen_jump_case()
{
gen_call("^case");
}
/*
* Add the primary and secondary registers.
* If lval2 is int pointer and lval is int, scale lval.
*/
gen_add (int *lval, int *lval2)
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
if (dbltest (lval2, lval)) {
output_line("sll\t$t1, 2");
}
output_line ("add\t$v0, $t1");
stkp = stkp + INTSIZE;
}
/*
* Subtract the primary register from the secondary. // *** from TOS
*/
gen_sub()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("sub\t$v0, $t1, $v0");
stkp = stkp + INTSIZE;
}
/*
* Multiply the primary and secondary registers.
* (result in primary)
*/
gen_mult()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("mult\t$v0, $t1");
output_line ("mflo\t$v0");
//gcall ("^mult");
stkp = stkp + INTSIZE;
}
/*
* Divide the secondary register by the primary.
* (quotient in primary, remainder in secondary)
*/
gen_div()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("div\t$t1, $v0");
output_line ("mflo\t$v0");
output_line ("mfhi\t$t1");
//gcall ("^div");
stkp = stkp + INTSIZE;
}
gen_udiv()
{
output_line ("#FIXME genudiv");
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("divu\t$t1, $v0");
output_line ("mflo\t$v0");
output_line ("mfhi\t$t1");
//gcall ("^div");
stkp = stkp + INTSIZE;
}
/*
* Compute the remainder (mod) of the secondary register
* divided by the primary register.
* (remainder in primary, quotient in secondary)
*/
gen_mod()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("div\t$t1, $v0");
output_line ("mflo\t$t1");
output_line ("mfhi\t$v0");
//gcall ("^mod");
stkp = stkp + INTSIZE;
}
gen_umod()
{
output_line ("#FIXME genumod");
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("divu\t$t1, $v0");
output_line ("mflo\t$t1");
output_line ("mfhi\t$v0");
//gcall ("^mod");
stkp = stkp + INTSIZE;
}
/*
* Inclusive 'or' the primary and secondary registers.
*/
gen_or()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("or\t$v0, $t1");
//output_line ("or.l\t(%sp)+,%d0");
stkp = stkp + INTSIZE;
}
/*
* Exclusive 'or' the primary and secondary registers.
*/
gen_xor()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("xor\t$v0, $t1");
//output_line ("mov.l\t(%sp)+,%d1");
//output_line ("eor.l\t%d1,%d0");
stkp = stkp + INTSIZE;
}
/*
* 'And' the primary and secondary registers.
*/
gen_and()
{
//output_line ("and.l\t(%sp)+,%d0");
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("and\t$v0, $t1");
stkp = stkp + INTSIZE;
}
/*
* Arithmetic shift right the secondary register the number of
* times in the primary register.
* (results in primary register)
*/
gen_arithm_shift_right()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("srav\t$v0, $t1, $v0");
stkp = stkp + INTSIZE;
}
/*
* Arithmetic shift left the secondary register the number of
* times in the primary register.
* (results in primary register)
*/
gen_arithm_shift_left()
{
output_line ("lw\t$t1, 16($sp)");
output_line ("addiu\t$sp, 4");
output_line ("sllv\t$v0, $t1, $v0");
stkp = stkp + INTSIZE;
}
/*
* Two's complement of primary register.
*/
gen_twos_complement()
{
output_line ("sub\t$v0, $zero, $v0");
}
/*
* Logical complement of primary register.
*/
gen_logical_negation()
{
//gcall ("^lneg");
output_line ("sltu\t$t1, $v0, $zero");
output_line ("sltu\t$t2, $zero, $v0");
output_line ("or\t$v0, $t1, $t2");
output_line ("xori\t$v0, 1");
}
/*
* One's complement of primary register.
*/
gen_complement()
{
output_line ("addiu\t$t1, $zero, -1");
output_line ("xor\t$v0, $t1");
}
/*
* Convert primary register into logical value.
*/
gen_convert_primary_reg_value_to_bool()
{
output_line ("sltu\t$t1, $v0, $zero");
output_line ("sltu\t$t2, $zero, $v0");
output_line ("or\t$v0, $t1, $t2");
//gcall ("^bool");
}
/*
* Increment the primary register by 1 if char, INTSIZE if int.
*/
gen_increment_primary_reg (lvalue_t *lval)
{
if (lval->ptr_type & CINT)
output_line("addiu\t$v0, 4");
else
output_line("addiu\t$v0, 1");
}
/*
* Decrement the primary register by one if char, INTSIZE if int.
*/
gen_decrement_primary_reg (lvalue_t *lval)
{
if (lval->ptr_type & CINT)
output_line("addiu\t$v0, -4");
else
output_line("addiu\t$v0, -1");
}
/*
* Following are the conditional operators.
* They compare the secondary register against the primary register
* and put a literl 1 in the primary if the condition is true,
* otherwise they clear the primary register.
*/
///// BEEP BEEP actually, compare tos
/*
* equal
*/
gen_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("sltu\t$t2, $v0, $t1");
output_line("sltu\t$v0, $t1, $v0");
output_line("or\t$v0, $t2");
output_line("xori\t$v0, 1");
output_line("addiu\t$sp, 4");
//gcall ("^eq");
stkp = stkp + INTSIZE;
}
/*
* not equal
*/
gen_not_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("sltu\t$t2, $v0, $t1");
output_line("sltu\t$v0, $t1, $v0");
output_line("or\t$v0, $t2");
output_line("addiu\t$sp, 4");
//gcall ("^ne");
stkp = stkp + INTSIZE;
}
/*
* less than (signed) - TOS < primary
*/
gen_less_than()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("slt\t$v0, $t1, $v0");
//gcall ("^lt");
stkp = stkp + INTSIZE;
}
/*
* less than or equal (signed) TOS <= primary
*/
gen_less_or_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("slt\t$v0, $v0, $t1"); // primary < tos
output_line("xori\t$v0, 1"); // primary >= tos
//gcall ("^le");
stkp = stkp + INTSIZE;
}
/*
* greater than (signed) TOS > primary
*/
gen_greater_than()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("slt\t$v0, $v0, $t1"); //pimary < TOS
//output_line("xori\t$v0, 1");
//gcall ("^gt");
stkp = stkp + INTSIZE;
}
/*
* greater than or equal (signed) TOS >= primary
*/
gen_greater_or_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("slt\t$v0, $t1, $v0"); //tos < primary
output_line("xori\t$v0, 1"); //tos >= primary
//gcall ("^ge");
stkp = stkp + INTSIZE;
}
/*
* less than (unsigned)
*/
gen_unsigned_less_than()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("sltu\t$v0, $t1, $v0");
//gcall ("^ult");
stkp = stkp + INTSIZE;
}
/*
* less than or equal (unsigned)
*/
gen_unsigned_less_or_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("sltu\t$v0, $v0, $t1"); // primary < tos
output_line("xori\t$v0, 1"); // primary >= tos
//gcall ("^ule");
stkp = stkp + INTSIZE;
}
/*
* greater than (unsigned)
*/
gen_usigned_greater_than()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("sltu\t$v0, $v0, $t1"); //pimary < TOS
//gcall ("^ugt");
stkp = stkp + INTSIZE;
}
/*
* greater than or equal (unsigned)
*/
gen_unsigned_greater_or_equal()
{
output_line("lw\t$t1, 16($sp)");
output_line("addiu\t$sp, 4");
output_line("sltu\t$v0, $t1, $v0"); //tos < primary
output_line("xori\t$v0, 1"); //tos >= primary
//gcall ("^uge");
stkp = stkp + INTSIZE;
}
/*
* Put first 4 arguments to registers a0-a3.
*/
gnargs (nargs)
int nargs;
{
int i;
if (nargs > 4) {
error("Too many arguments in a function call (max 4 args supported)");
nargs = 4;
}
for (i=0; i<nargs; i++)
fprintf(output, "\tlw\t$a%d, %d($sp)\n", i, (nargs-1 - i) * 4 + 16);
}