diff options
| author | Xavi Del Campo <xavi.dcr@tutanota.com> | 2020-01-31 10:32:23 +0100 |
|---|---|---|
| committer | Xavi Del Campo <xavi.dcr@tutanota.com> | 2020-01-31 10:32:23 +0100 |
| commit | 7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch) | |
| tree | c28d0748652ad4b4222309e46e6cfc82c0906220 /tools/spasm | |
| parent | a2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff) | |
| download | psxsdk-7c24e9a9b02b04dcaf9507acb94091ea70a2c02d.tar.gz | |
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'tools/spasm')
| -rw-r--r-- | tools/spasm/Makefile | 17 | ||||
| -rw-r--r-- | tools/spasm/codegen.c | 144 | ||||
| -rw-r--r-- | tools/spasm/codegen.h | 33 | ||||
| -rw-r--r-- | tools/spasm/error.c | 58 | ||||
| -rw-r--r-- | tools/spasm/error.h | 10 | ||||
| -rw-r--r-- | tools/spasm/eval.c | 130 | ||||
| -rw-r--r-- | tools/spasm/eval.h | 6 | ||||
| -rwxr-xr-x | tools/spasm/fcaseopen.c | 137 | ||||
| -rwxr-xr-x | tools/spasm/fcaseopen.h | 40 | ||||
| -rw-r--r-- | tools/spasm/opcode.c | 2214 | ||||
| -rw-r--r-- | tools/spasm/opcode.h | 108 | ||||
| -rw-r--r-- | tools/spasm/parser.c | 478 | ||||
| -rw-r--r-- | tools/spasm/parser.h | 29 | ||||
| -rw-r--r-- | tools/spasm/readme.txt | 120 | ||||
| -rw-r--r-- | tools/spasm/spasm.c | 167 | ||||
| -rw-r--r-- | tools/spasm/spasm.h | 20 |
16 files changed, 3711 insertions, 0 deletions
diff --git a/tools/spasm/Makefile b/tools/spasm/Makefile new file mode 100644 index 0000000..093b9b5 --- /dev/null +++ b/tools/spasm/Makefile @@ -0,0 +1,17 @@ +include ../../Makefile.cfg + +OUT = spasm$(EXE_SUFFIX) + +OBJS = $(patsubst %.c, %.o, $(wildcard *.c)) + +$(OUT): $(OBJS) + $(HOST_CC) $(HOST_CFLAGS) -o $(OUT) $(OBJS) + +%.o: %.c + $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $< + +install: + cp -rv $(OUT) $(TOOLCHAIN_PREFIX)/bin + +clean: + rm -f $(OBJS) $(OUT) diff --git a/tools/spasm/codegen.c b/tools/spasm/codegen.c new file mode 100644 index 0000000..fda456d --- /dev/null +++ b/tools/spasm/codegen.c @@ -0,0 +1,144 @@ +#include "spasm.h" + +char curIns[128]; +unsigned int curInsArg; +unsigned int curInsArgT; +unsigned int insArgv[64]; +unsigned int insArgc; +unsigned int insArgt[64]; +unsigned int copn; +int org_found; + +void (*INSFUNC)(void); + +volatile unsigned int curPc = 0; +int curPass = 0; +unsigned int startAddress = 0; +unsigned int numLabels; +unsigned int numLabelsAlloc; +int first_instruction; +asm_label *labels; +static int find_label_status = 1; + +void codegen_init(void) +{ + curPc = startAddress; + curPass = 0; + numLabels = 0; + numLabelsAlloc = 0; + labels = NULL; +} + +static asm_label *find_label_internal(char *name) +{ + int i; + + for(i = 0; i < numLabels; i++) + { + if(strcmp(name, labels[i].name) == 0) + return &labels[i]; + } + + return NULL; +} + +static void add_label_internal(char *name, unsigned int pc) +{ + // add labels only if current pass >= 1! + asm_label *l; + +/* if(curPass == ) + return; + + if(curPass >= 2) + return;*/ + + //printf("Name = %s\n", name); + + l = find_label_internal(name); + + if(l) + { + if(l->pc != pc) + { + //if(l->pass == curPass) + // assembler_error("Impossible to redefine label %s", name); + + //printf("Redefining, [%s] = %08X, pass %d\n", l->name, pc, curPass); + l->pc = pc; + } + + return; + } + + if(numLabels == numLabelsAlloc) + { + numLabelsAlloc += 128; + labels = realloc(labels, sizeof(asm_label) * numLabelsAlloc); + } + + strncpy(labels[numLabels].name, name, 127); + labels[numLabels].pass = curPass; + + labels[numLabels].pc = pc; + + numLabels++; + + //printf("label #%d, [%s] = %08X, pass = %d\n", numLabels, name, pc, curPass); + + /*while(*name) + { + printf("%x, \'%c\'\n", *name, *name); + name++; + }*/ +} + + + +void add_label(char *name, unsigned int pc) +{ + if(curPass == -1) + return; + + return add_label_internal(name, pc); +} + +void add_label_equ(char *name, unsigned int pc) +{ + return add_label_internal(name, pc); +} + +unsigned int find_label(char *name) +{ + //printf("find_label(%s)\n", name); + + asm_label *l = find_label_internal(name); + + if(l) + { + //find_label_status = 1; + return l->pc; + } + +// remember! if pass >= 1, abort if you can't find a label, because that means it was really +// impossible to find. + +// printf(">>DEBUG, PASS = %d << Couldn't find label %s$\n", curPass, name); + + find_label_status = 0; + + if(curPass == 1) + instruction_error("Cannot find label %s", name); + + return 0xFFFFFFFF; +} + +void find_label_reset() +{ + find_label_status = 1; +} + +int find_label_ok() +{ + return find_label_status; +} diff --git a/tools/spasm/codegen.h b/tools/spasm/codegen.h new file mode 100644 index 0000000..e9e0ab7 --- /dev/null +++ b/tools/spasm/codegen.h @@ -0,0 +1,33 @@ +#ifndef _SPASM_CODEGEN_H +#define _SPASM_CODEGEN_H + +typedef struct +{ + char name[128]; + unsigned int pc; + unsigned int pass; +}asm_label; + +extern asm_label *labels; + +extern volatile unsigned int curPc; +extern int curPass; +extern unsigned int numLabels; +extern unsigned int startAddress; +extern unsigned int copn; +extern int first_instruction; +extern int org_found; +extern void (*INSFUNC)(void); + +void codegen_init(void); +void add_label(char *label, unsigned int pc); +void add_label_equ(char *label, unsigned int pc); +unsigned int find_label(char *label); +void find_label_reset(); +int find_label_ok(); +int label_was_not_found_once(char *name); +void add_not_found_label(char *name); +//void resolve_labels(); + + +#endif diff --git a/tools/spasm/error.c b/tools/spasm/error.c new file mode 100644 index 0000000..019c3da --- /dev/null +++ b/tools/spasm/error.c @@ -0,0 +1,58 @@ +#include "spasm.h" + +static void show_line(void) +{ + printf("%s\n", curLine); + printf("^^^^^^^^^^^^^^\n"); +} + +void instruction_error(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + printf("Line %d: Error(%s) - ", line_number, curIns); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); + + exit(EXIT_FAILURE); +} + +void instruction_warning(char *format, ...) +{ + if(curPass <= 0) + return; + + va_list ap; + + va_start(ap, format); + + printf("Line %d: Warning (%s) - ", line_number, curIns); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); +} + +void assembler_error(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + printf("Line %d, assembler error: ", line_number); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); + + exit(EXIT_FAILURE); +} + +
\ No newline at end of file diff --git a/tools/spasm/error.h b/tools/spasm/error.h new file mode 100644 index 0000000..3951ffd --- /dev/null +++ b/tools/spasm/error.h @@ -0,0 +1,10 @@ +#ifndef _SPASM_ERROR_H +#define _SPASM_ERROR_H + +void instruction_error(char *format, ...); +void instruction_warning(char *format, ...); +void assembler_error(char *format, ...); +extern int yylineno; + +#endif + diff --git a/tools/spasm/eval.c b/tools/spasm/eval.c new file mode 100644 index 0000000..01f8f66 --- /dev/null +++ b/tools/spasm/eval.c @@ -0,0 +1,130 @@ +#include "spasm.h" + +/** + * Expressions in SPASM are implemented in a totally broken manner. + * + * The result of an expression is the value of its initial argument + * after executing the operation of the last operand with the last argument. + * + * For example dw $cafeba00+2+4 is not equal to dw $cafeba06 + * but to dw $cafeba04. + * Likewise, dw $2+$cafeba00+4 is not equal to dw $cafeba06 + * but to dw $6 ! + */ + +unsigned int spasm_eval(char *expr) +{ + char *cset = "+-><&|*!"; + char *csetp; + char *cp; + int ok; + int t = T_INTEGER; + char sbuf[128]; + + if(strcmp(expr, "*") == 0) // Return current return address + return curPc; + + if(strcasecmp(curIns, "incbin") == 0) + return 0; // If current instruction is incbin, do not evaluate. + + if(*expr == '"' || *expr == '\'') + return 0; // I won't even try to evaluate strings! + + int ispan = strcspn(expr, cset); + + csetp = cset; + + ok = 0; // ok will be 0 if we found no operator, 1 if we found it + + char op = '?'; + + cp = NULL; + + while(*csetp) + { + char *tcp = strrchr(expr, *csetp); + + if(tcp) // Found operator + { + if(tcp > cp) + cp = tcp; + + if(*cp == '>') + { + if(*(cp-1) != '>') + instruction_error("Bad operator"); + } + + if(*cp == '<') + { + if(*(cp-1) != '<') + instruction_error("Bad operator"); + } + + cp++; + ok = 1; + op = *csetp; + break; + } + + csetp++; + } + + unsigned int one = 0; + unsigned int two = 0; + + memcpy(sbuf, expr, ispan); + sbuf[ispan] = '\0'; + + if(strlen(sbuf) == 0) + strcpy(sbuf, "0"); + //instruction_error("Bad expression"); + + //printf("sbuf = %s\n", sbuf); + one = asm_atoi(sbuf); + + t = atoiT[insArgc]; + + if(ok) + { + strcpy(sbuf, cp); + + if(strlen(sbuf) == 0) + instruction_error("Bad expression"); + + two = asm_atoi(sbuf); + + if(t == T_INTEGER) + t = atoiT[insArgc]; + } + + atoiT[insArgc] = t; + + switch(op) + { + case '+': + return one + two; + break; + case '-': + return one - two; + break; + case '<': + return one << two; + break; + case '>': + return one >> two; + break; + case '&': + return one & two; + break; + case '|': + case '!': + return one | two; + break; + case '*': + return one * two; + break; + } + + return one; +} diff --git a/tools/spasm/eval.h b/tools/spasm/eval.h new file mode 100644 index 0000000..a568f7e --- /dev/null +++ b/tools/spasm/eval.h @@ -0,0 +1,6 @@ +#ifndef _SPASM_EVAL_H +#define _SPASM_EVAL_H + +unsigned int spasm_eval(char *expr); + +#endif diff --git a/tools/spasm/fcaseopen.c b/tools/spasm/fcaseopen.c new file mode 100755 index 0000000..20efd5b --- /dev/null +++ b/tools/spasm/fcaseopen.c @@ -0,0 +1,137 @@ +/* +Copyright (c) 2009 Keith Bauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "fcaseopen.h" + +#if !defined(_WIN32) +#include <stdlib.h> +#include <string.h> + +#include <dirent.h> +#include <errno.h> +#include <unistd.h> + +// r must have strlen(path) + 2 bytes +static int casepath(char const *path, char *r) +{ + size_t l = strlen(path); + char *p = alloca(l + 1); + strcpy(p, path); + size_t rl = 0; + + DIR *d; + if (p[0] == '/') + { + d = opendir("/"); + p = p + 1; + } + else + { + d = opendir("."); + r[0] = '.'; + r[1] = 0; + rl = 1; + } + + int last = 0; + char *c = strsep(&p, "/"); + while (c) + { + if (!d) + { + return 0; + } + + if (last) + { + closedir(d); + return 0; + } + + r[rl] = '/'; + rl += 1; + r[rl] = 0; + + struct dirent *e = readdir(d); + while (e) + { + if (strcasecmp(c, e->d_name) == 0) + { + strcpy(r + rl, e->d_name); + rl += strlen(e->d_name); + + closedir(d); + d = opendir(r); + + break; + } + + e = readdir(d); + } + + if (!e) + { + strcpy(r + rl, c); + rl += strlen(c); + last = 1; + } + + c = strsep(&p, "/"); + } + + if (d) closedir(d); + return 1; +} +#endif + +FILE *fcaseopen(char const *path, char const *mode) +{ + FILE *f = fopen(path, mode); +#if !defined(_WIN32) + if (!f) + { + char *r = alloca(strlen(path) + 2); + if (casepath(path, r)) + { + f = fopen(r, mode); + } + } +#endif + return f; +} + +void casechdir(char const *path) +{ +#if !defined(_WIN32) + char *r = alloca(strlen(path) + 2); + if (casepath(path, r)) + { + chdir(r); + } + else + { + errno = ENOENT; + } +#else + chdir(path); +#endif +} diff --git a/tools/spasm/fcaseopen.h b/tools/spasm/fcaseopen.h new file mode 100755 index 0000000..a6d024b --- /dev/null +++ b/tools/spasm/fcaseopen.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 2009 Keith Bauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef fcaseopen_h +#define fcaseopen_h + +#include <stdio.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +extern FILE *fcaseopen(char const *path, char const *mode); + +extern void casechdir(char const *path); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/tools/spasm/opcode.c b/tools/spasm/opcode.c new file mode 100644 index 0000000..a09c119 --- /dev/null +++ b/tools/spasm/opcode.c @@ -0,0 +1,2214 @@ +#include "spasm.h" + +struct +{ + char *name; + void (*func)(); +}instruction_table[] = +{ + {"add" ,INS_ADD}, + {"addi" ,INS_ADDI}, + {"addiu" ,INS_ADDIU}, + {"addu" ,INS_ADDU}, + {"and" ,INS_AND}, + {"andi" ,INS_ANDI}, + {"beq" ,INS_BEQ}, + {"bgez" ,INS_BGEZ}, + {"bgezal" ,INS_BGEZAL}, + {"bgtz" ,INS_BGTZ}, + {"blez" ,INS_BLEZ}, + {"bltz" ,INS_BLTZ}, + {"bltzal" ,INS_BLTZAL}, + {"bne" ,INS_BNE}, + {"break" ,INS_BREAK}, + {"cfc0" ,INS_CFC }, + {"cfc1" ,INS_CFC }, + {"cfc2" ,INS_CFC }, + {"cfc3" ,INS_CFC }, + {"cop0" ,INS_COP }, + {"cop1" ,INS_COP }, + {"cop2" ,INS_COP }, + {"cop3" ,INS_COP }, + {"ctc0" ,INS_CTC }, + {"ctc1" ,INS_CTC }, + {"ctc2" ,INS_CTC }, + {"ctc3" ,INS_CTC }, + {"div" ,INS_DIV}, + {"divu" ,INS_DIVU}, + {"j" ,INS_J}, + {"jal" ,INS_JAL}, + {"jalr" ,INS_JALR}, + {"jr" ,INS_JR}, + {"lb" ,INS_LB}, + {"lbu" ,INS_LBU}, + {"lh" ,INS_LH}, + {"lhu" ,INS_LHU}, + {"lui" ,INS_LUI}, + {"lw" ,INS_LW}, + {"lwc0" ,INS_LWC }, + {"lwc1" ,INS_LWC }, + {"lwc2" ,INS_LWC }, + {"lwc3" ,INS_LWC }, + {"lwl" ,INS_LWL}, + {"lwr" ,INS_LWR}, + {"mfc0" ,INS_MFC}, + {"mfc1" ,INS_MFC}, + {"mfc2" ,INS_MFC}, + {"mfc3" ,INS_MFC}, + {"mfhi" ,INS_MFHI}, + {"mflo" ,INS_MFLO}, + {"mtc0" ,INS_MTC }, + {"mtc1" ,INS_MTC }, + {"mtc2" ,INS_MTC }, + {"mtc3" ,INS_MTC }, + {"mthi" ,INS_MTHI}, + {"mtlo" ,INS_MTLO}, + {"mult" ,INS_MULT}, + {"multu" ,INS_MULTU}, + {"nor" ,INS_NOR}, + {"or" ,INS_OR}, + {"ori" ,INS_ORI}, + {"sb" ,INS_SB}, + {"sh" ,INS_SH}, + {"sll" ,INS_SLL}, + {"sllv" ,INS_SLLV}, + {"slt" ,INS_SLT}, + {"slti" ,INS_SLTI}, + {"sltiu" ,INS_SLTIU}, + {"sltu" ,INS_SLTU}, + {"sra" ,INS_SRA}, + {"srav" ,INS_SRAV}, + {"srl" ,INS_SRL}, + {"srlv" ,INS_SRLV}, + {"sub" ,INS_SUB}, + {"subu" ,INS_SUBU}, + {"sw" ,INS_SW}, + {"swc0" ,INS_SWC}, + {"swc1" ,INS_SWC}, + {"swc2" ,INS_SWC}, + {"swc3" ,INS_SWC}, + {"swl" ,INS_SWL}, + {"swr" ,INS_SWR}, + {"syscall" ,INS_SYSCALL}, + {"xor" ,INS_XOR}, + {"xori" ,INS_XORI}, + + {"b" ,INS_B}, + {"la" ,INS_LA}, + {"li" ,INS_LI}, + {"nop" ,INS_NOP}, + {"move" ,INS_MOVE}, + {"subi" ,INS_SUBI}, + {"subiu" ,INS_SUBIU}, + {"beqz" ,INS_BEQZ}, + {"bnez" ,INS_BNEZ}, + {"bal" ,INS_BAL}, + {"org" ,INS_ORG}, + {"include" ,INS_INCLUDE}, + {"incbin" ,INS_INCBIN}, + {"dcb" ,INS_DCB}, + {"db" ,INS_DB}, + {"dh" ,INS_DH}, + {"dw" ,INS_DW}, + {"align" ,INS_ALIGN}, + + {NULL}, +}; + +#define I_TYPE(op, rs, rt, imm) \ + ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \ + ((imm) & 0xFFFF) ) + +#define J_TYPE(op, target) \ + ( (((op) & 63) << 26) | ((target) & 0x3FFFFFF) ) + +#define R_TYPE(op, rs, rt, rd, shamt, funct) \ + ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \ + (((rd) & 31) << 11) | (((shamt) & 31) << 6) | \ + ((funct) & 63)) + +// copn = coprocessor number +// SET_DIS_CHECK() + +#define SET_DIS_CHECK() /* Argument type check. Placeholder, contains nothing for now. */ + +int set_delay_slot = 0; + +void OUTSEEK(unsigned int position) +{ + fseek(asmOut, position, SEEK_SET); +} + +void OUTBYTE(unsigned char b) +{ + if(curPass>0) + fputc(b, asmOut); + + curPc++; +} + +void OUTHALF(unsigned short h) +{ + if(curPass>0) + { + fputc(h&0xff, asmOut); + fputc(h>>8, asmOut); + } + + curPc += 2; +} + +void OUTWORD(unsigned int w) +{ + if(curPass>0) + { + fputc(w&0xff, asmOut); + fputc((w>>8)&0xff, asmOut); + fputc((w>>16)&0xff, asmOut); + fputc(w>>24, asmOut); + } + + curPc += 4; +} + +void OUTINS(unsigned int instruction) +{ + OUTWORD(instruction); + + if(set_delay_slot) + { + OUTWORD(0); + + set_delay_slot = 0; + } +} + + +void OUTSTRING(char *string) +{ + int stringt; + int esc=0; + + if(*string == '"') + stringt = 0; + else if(*string == '\'') + stringt = 1; + else + instruction_error("OUTSTRING <INTERNAL ERROR>. Not a string!"); + + string++; + + while(*string) + { + if(*string == '"') + { + if(stringt == 0 && !esc) + break; + + OUTBYTE('"'); + esc = 0; + } + else if(*string == '\'') + { + if(stringt == 1 && !esc) + break; + + OUTBYTE('\''); + esc = 0; + } + else if(*string == 'n') + { + if(esc) + OUTBYTE('\n'); + else + OUTBYTE('n'); + + esc = 0; + } + else if(*string == 't') + { + if(esc) + OUTBYTE('\t'); + else + OUTBYTE('t'); + + esc = 0; + } + else if(*string == 'r') + { + if(esc) + OUTBYTE('\r'); + else + OUTBYTE('r'); + + esc = 0; + } + else if(*string == '\\') + { + if(esc) + { + OUTBYTE('\\'); + esc = 0; + } + else + esc = 1; + } + else + { + if(esc) + instruction_error("Invalid escape sequence \\%c in string", *string); + + OUTBYTE(*string); + } + + string++; + } +} + +unsigned int OUTSIZE(void) +{ + int r; + int pos = ftell(asmOut); + fseek(asmOut, 0, SEEK_END); + r = ftell(asmOut); + fseek(asmOut, pos, SEEK_SET); + return r; +} + +unsigned short compute_branch(unsigned int imm) +{ + unsigned int off = imm - (curPc + 4); + //off >>= 2; + + if(curPass <= 0) + return 0; + + if(off >= 0x20000 && off < -0x20000) + instruction_error("Branch out of range. %04x", off); + + return (off>>2) & 0xFFFF; +} + +unsigned int compute_jump(unsigned int imm) +{ + if(curPass <= 0) + return 0; + + return (imm >> 2); +} + +struct +{ + int size; + int pos; + unsigned int *el; +}cannotPredict = {0, 0, NULL}; + +unsigned int compute_real_offset(unsigned int imm, unsigned int base, + unsigned int rt) +{ + int i, unpredictable=0; + unsigned short hipart; + unsigned short lopart; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(!unpredictable && ((imm <= 0xFFFF) || (imm >= 0xFFFF8000 && imm <= 0xFFFFFFFF))) + return I_TYPE(0, base, rt, imm); + +//compute_real_offset_output_wide: + // li at, offset + // add at, at, base + // lw rt, 0(at) + hipart = (imm >> 16); + lopart = imm & 0xFFFF; + int t=(*curIns == 'l') ? rt : 1; + + // lw at, $CAFEBABE(zero) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(at) + // lw at, $CAFEBABE(v0) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(v0) + // sw at, $CAFEBABE -> + + if(base) + { + /*OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16) + + if(lopart || unpredictable) + OUTINS( I_TYPE (13, t, t, lopart)); // ori $t, $t, offset & 0xffff + + OUTINS( R_TYPE (0, t, base, t, 0, 33) ); // add $t, $t, base + return I_TYPE(0, t, rt, 0);*/ + +// SPASM is seriously broken regarding this.. + return I_TYPE(0, base, rt, imm); + } + + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16) + + return I_TYPE(0, t, rt, lopart); // XX rt, lopart(rt) +} + +void INS_ADD(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADD rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ADDI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ADDI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 32) ); +} + +void INS_ADDI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range."); + + OUTINS( I_TYPE (8, rs, rt, imm) ); +} + +void INS_ADDIU(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDIU rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range."); + + OUTINS( I_TYPE (9, rs, rt, imm) ); +} + +void INS_ADDU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDU rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ADDIU(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ADDIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 33) ); +} + +void INS_AND(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // AND rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ANDI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ANDI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 36) ); +} + +void INS_ANDI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ANDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (12, rs, rt, imm) ); +} + +void INS_BEQ(void) +{ + unsigned int rt, rs, imm; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + imm = insArgv[2]; + + OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) ); +} + +void INS_BGEZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 1, compute_branch(imm)) ); +} + +void INS_BGEZAL(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 17, compute_branch(imm)) ); +} + +void INS_BGTZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(7, rs, 0, compute_branch(imm)) ); +} + +void INS_BLEZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(6, rs, 0, compute_branch(imm)) ); +} + +void INS_BLTZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 0, compute_branch(imm)) ); +} + +void INS_BLTZAL(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 16, compute_branch(imm)) ); +} + +void INS_BNE(void) +{ + unsigned int rt, rs, imm; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + imm = insArgv[2]; + + OUTINS( I_TYPE(5, rs, rt, compute_branch(imm)) ); +} + +void INS_BREAK(void) +{ + unsigned int imm = 0; + + if(insArgc > 1) + instruction_error("Too many arguments"); + + if(insArgc == 1) + imm = insArgv[0]; + + imm &= 0xFFFFF; + + OUTINS( (imm << 6) | 13 ); +} + +void INS_CFC(void) +{ + unsigned int rt, rd; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rd = insArgv[1]; + + OUTINS ( R_TYPE(16 | copn, 2, rt, rd, 0, 0) ); +} + +void INS_COP(void) +{ + unsigned int cofun; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + cofun = insArgv[0]; + + OUTINS( ( (16 | copn) << 26) | (1<<25) | (cofun & 0x1FFFFFF)); +} + +void INS_CTC(void) +{ + unsigned int rt, rd; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rd = insArgv[1]; + + OUTINS ( R_TYPE(16 | copn, 6, rt, rd, 0, 0) ); +} + +void INS_DIV(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS ( R_TYPE(0, rs, rt, 0, 0, 26)); +} + +void INS_DIVU(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS ( R_TYPE(0, rs, rt, 0, 0, 27)); +} + +void INS_J(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( J_TYPE(2, compute_jump(insArgv[0]))); +} + +void INS_JAL(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( J_TYPE(3, compute_jump(insArgv[0]))); +} + +void INS_JALR(void) +{ + unsigned int rd, rs; + + if(insArgc < 1) + instruction_error("Not enough arguments"); + if(insArgc > 2) + instruction_error("Too many arguments"); + + if(insArgc == 1) + { + rd = 31; // register ra + rs = insArgv[0]; + } + else + { + rd = insArgv[0]; + rs = insArgv[1]; + } + + OUTINS ( R_TYPE(0, rs, 0, rd, 0, 9)); +} + +void INS_JR(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( R_TYPE(0, insArgv[0], 0, 0, 0, 8)); +} + +void INS_LB(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(32, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LBU(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(36, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LH(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(33, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LHU(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(37, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LUI(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(I_TYPE(15, 0, insArgv[0], insArgv[1])); +} + +void INS_LW(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(35, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWC(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(48 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWL(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(34, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWR(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(38, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_MFC(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE (16 | copn, 0, insArgv[0], insArgv[1], 0, 0)); +} + +void INS_MFHI(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 16)); +} + +void INS_MFLO(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 18)); +} + +void INS_MTC(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE (16 | copn, 4, insArgv[0], insArgv[1], 0, 0)); +} + +void INS_MTHI(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 17)); +} + +void INS_MTLO(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 19)); +} + +void INS_MULT(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS(R_TYPE(0, rs, rt, 0, 0, 24)); +} + +void INS_MULTU(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS(R_TYPE(0, rs, rt, 0, 0, 25)); +} + +void INS_NOR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 39) ); +} + +void INS_OR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ORI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ORI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 37) ); +} + +void INS_ORI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ANDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (13, rs, rt, imm) ); +} + +void INS_SB(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(40, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SH(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(41, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SLL(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 0)); +} + +void INS_SLLV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 4) ); +} + +void INS_SLT(void) +{ + unsigned int rd, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + if(atoiT[2] == T_INTEGER) + return INS_SLTI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 42) ); +} + +void INS_SLTI(void) +{ + unsigned int imm, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + + if(imm > 0x7FFF && imm < 0xFFFF8000) + instruction_error("Immediate out of range."); + + OUTINS( I_TYPE (10, rs, rt, imm) ); +} + +void INS_SLTIU(void) +{ + unsigned int imm, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + + if(imm > 0x7FFF && imm < 0xFFFF8000) + instruction_error("Immediate out of range."); + + OUTINS( I_TYPE (11, rs, rt, imm) ); +} + +void INS_SLTU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + if(atoiT[2] == T_INTEGER) + return INS_SLTIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 43) ); +} + +void INS_SRA(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 3)); +} + +void INS_SRAV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 7) ); +} + +void INS_SRL(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 2)); +} + +void INS_SRLV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 6) ); +} + +void INS_SUB(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_SUBI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_SUBI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 34) ); +} + +void INS_SUBU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADD rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_SUBIU(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_SUBIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 35) ); +} + +void INS_SW(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(43, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWC(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(56 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWL(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(42, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWR(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(46, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SYSCALL(void) +{ + unsigned int imm = 0; + + if(insArgc > 1) + instruction_error("Too many arguments"); + + if(insArgc == 1) + imm = insArgv[0]; + + imm &= 0xFFFFF; + + OUTINS( (imm << 6) | 12 ); +} + +void INS_XOR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_XORI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_XORI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 38) ); +} + +void INS_XORI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (14, rs, rt, imm) ); +} + +// ***** PSEUDO INSTRUCTIONS ***** + +void INS_B(void) +{ + unsigned int imm; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + imm = insArgv[0]; + + OUTINS( I_TYPE(4, 0, 0, compute_branch(imm)) ); // <- beq zero, zero, imm +} + +/*void INS_LI(void) +{ + unsigned int rd, imm; + unsigned short lopart, hipart; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(atoiT[1] == T_INTEGER && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + else + { + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + OUTINS( I_TYPE (9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff + } +}*/ + +void INS_LI(void) +{ + int i; + unsigned int rd, imm; + unsigned short lopart, hipart; + int unpredictable=0; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + else if(!unpredictable && !lopart) + OUTINS(I_TYPE(15, 0, rd, hipart)); + else + { + // if(lopart >= 0x8000) + // hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + + // OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff + OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff + } + +} + +void INS_LA(void) +{ + int i; + unsigned int rd, imm; + unsigned short lopart, hipart; + int unpredictable=0; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + //else if(!unpredictable && !lopart) + // OUTINS(I_TYPE(15, 0, rd, hipart)); + else + { + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + + OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff +// OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff + } + +} + +/*void INS_LA(void) +{ + INS_LI(); // The LI and LA pseudo-instructions are the same thing in SPASM +}*/ + +void INS_NOP(void) +{ + OUTINS(0); +} + +void INS_MOVE(void) +{ + unsigned int rd, rs; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + rs = insArgv[1]; + + OUTINS( R_TYPE (0, rs, 0, rd, 0, 33) ); // addu $rd, $rs, $zero +} + +void INS_SUBI(void) +{ +// just like ADDI, but switches the sign of the immediate + + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = -insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = -insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (8, rs, rt, imm) ); +} + +void INS_SUBIU(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = -insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = -insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (9, rs, rt, imm) ); +} + +void INS_BEQZ(void) +{ + unsigned int rt, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(4, rt, 0, compute_branch(imm)) ); // <- beq zero, rt, imm +// OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) ); + +} + +void INS_BNEZ(void) +{ + unsigned int rt, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(5,rt, 0, compute_branch(imm)) ); // <- bne zero, rt, imm +} + +void INS_BAL(void) +{ + unsigned int imm; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + imm = insArgv[0]; + + OUTINS( I_TYPE(1, 0, 17, compute_branch(imm)) ); //bgezal $zero, imm +} + +void INS_ORG(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + //if(!first_instruction) + // instruction_error("ORG is not the first instruction"); + + curPc = insArgv[0]; + startAddress = insArgv[0]; + org_found = 1; +} + +void INS_INCBIN(void) +{ + FILE *f; + char *path, *cp; + int sz; + + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + path = rawArgv[0]; + + if(*path == '"') + { + path++; + if((cp = strrchr(path, '"'))) + *cp = '\0'; + } + else if(*path == '\'') + { + path++; + if((cp = strrchr(path, '\''))) + *cp = '\0'; + } + + //printf("DEBUG(INCBIN): including %s\n", path); + + f = spasm_fopen(path, "rb"); + + if(!f) + instruction_error("Could not open \"%s\"", path); + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + if(curPass <= 0) + curPc += sz; + else + { + for(;sz;sz--) + OUTBYTE(fgetc(f)); + } + + fclose(f); +} + +void INS_DCB(void) +{ + unsigned int num, value; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + num = insArgv[0]; + value = insArgv[1]; + + if(num & (1<<31)) + { + instruction_warning("Negative number of values, ignoring instruction"); + + return; + } + + if(curPass <= 0) + curPc += num; + else + { + for(;num;num--) + OUTBYTE(value); + } +} + +void INS_DB(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTBYTE(insArgv[i] & 0xFF); + } +} + +void INS_DH(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTHALF(insArgv[i] & 0xFFFF); + } +} + +void INS_DW(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTWORD(insArgv[i]); + } +} + +void INS_ALIGN(void) +{ + unsigned int unit; + unsigned int delta; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + unit = insArgv[0]; + + if(unit <= 0) + instruction_error("Alignment unit cannot be equal to zero or negative"); + + if((curPc % unit)) + { + delta = (unit - (curPc % unit)) % unit; + + if(curPass <= 0) + curPc += delta; + else + { + for(;delta;delta--) + OUTBYTE(0); + } + } +} + +void INS_INCLUDE(void) +{ + int i, l, o; + int sz; + int tsz; + FILE *f; + char *path; + char *cp; + char *newtext; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + if(curPass <= 0) + { + + path = rawArgv[0]; + + if(*path == '"') + { + path++; + if((cp = strrchr(path, '"'))) + *cp = '\0'; + } + else if(*path == '\'') + { + path++; + if((cp = strrchr(path, '\''))) + *cp = '\0'; + } + + f = spasm_fopen(path, "rb"); + + if(!f) + instruction_error("Could not open %s", path); + + //printf("DEBUG(INCLUDE): Including %s, line %d\n", path, line_number); + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + tsz = strlen(curText); + + newtext = malloc(sz + tsz + 1); + + for(i = 0, l = 1; l < line_number; i++) + { + if(curText[i] == '\n') + l++; + + newtext[i] = curText[i]; + } + + fread(&newtext[i], sizeof(char), sz, f); + + o = i+sz; + + if(newtext[i+sz-1] != '\n') + { + instruction_warning("No newline found at end of file %s", path); + newtext[o++] = '\n'; + } + + + for(; curText[i] != '\n'; i++); + + i++; + newtext[o] = '\0'; + + strcat(newtext, &curText[i]); + + free(curText); + curText = newtext; + } +} + +void INS_BLANK(void) +{ + +} + +void *get_instruction(char *name) +{ + int i; + + + for(i = 0; instruction_table[i].name; i++) + { + //printf("name = ^%s$, iname = ^%s$\n", name, instruction_table[i].name); + + if(strcasecmp(name, instruction_table[i].name) == 0) + { + // printf("di sranron\n"); + return instruction_table[i].func; + } + } + + return NULL; +} diff --git a/tools/spasm/opcode.h b/tools/spasm/opcode.h new file mode 100644 index 0000000..c2f6857 --- /dev/null +++ b/tools/spasm/opcode.h @@ -0,0 +1,108 @@ +#ifndef _SPASM_OPCODE_H +#define _SPASM_OPCODE_H + +// Helper functions + +void OUTSEEK(unsigned int position); +void OUTBYTE(unsigned char b); +void OUTHALF(unsigned short h); +void OUTWORD(unsigned int w); +void OUTINS(unsigned int instruction); +void OUTSTRING(char *string); +unsigned int OUTSIZE(void); +void *get_instruction(char *name); + +// Instructions + +void INS_ADD(void); +void INS_ADDI(void); +void INS_ADDIU(void); +void INS_ADDU(void); +void INS_AND(void); +void INS_ANDI(void); +void INS_BEQ(void); +void INS_BGEZ(void); +void INS_BGEZAL(void); +void INS_BGTZ(void); +void INS_BLEZ(void); +void INS_BLTZ(void); +void INS_BLTZAL(void); +void INS_BNE(void); +void INS_BREAK(void); +void INS_CFC(void); +void INS_COP(void); +void INS_CTC(void); +void INS_DIV(void); +void INS_DIVU(void); +void INS_J(void); +void INS_JAL(void); +void INS_JALR(void); +void INS_JR(void); +void INS_LB(void); +void INS_LBU(void); +void INS_LH(void); +void INS_LHU(void); +void INS_LUI(void); +void INS_LW(void); +void INS_LWC(void); +void INS_LWL(void); +void INS_LWR(void); +void INS_MFC(void); +void INS_MFHI(void); +void INS_MFLO(void); +void INS_MTC(void); +void INS_MTHI(void); +void INS_MTLO(void); +void INS_MULT(void); +void INS_MULTU(void); +void INS_NOR(void); +void INS_OR(void); +void INS_ORI(void); +void INS_SB(void); +void INS_SH(void); +void INS_SLL(void); +void INS_SLLV(void); +void INS_SLT(void); +void INS_SLTI(void); +void INS_SLTIU(void); +void INS_SLTU(void); +void INS_SRA(void); +void INS_SRAV(void); +void INS_SRL(void); +void INS_SRLV(void); +void INS_SUB(void); +void INS_SUBU(void); +void INS_SW(void); +void INS_SWC(void); +void INS_SWL(void); +void INS_SWR(void); +void INS_SYSCALL(void); +void INS_XOR(void); +void INS_XORI(void); + +// Pseudo instructions + +void INS_B(void); +void INS_LI(void); +void INS_LA(void); +void INS_NOP(void); +void INS_MOVE(void); +void INS_SUBI(void); +void INS_SUBIU(void); +void INS_BEQZ(void); +void INS_BNEZ(void); +void INS_BAL(void); +void INS_ORG(void); +void INS_INCBIN(void); +void INS_INCLUDE(void); +void INS_DCB(void); +void INS_DB(void); +void INS_DH(void); +void INS_DW(void); +void INS_ALIGN(void); + +// Fake instructions + +void INS_BLANK(void); + +#endif diff --git a/tools/spasm/parser.c b/tools/spasm/parser.c new file mode 100644 index 0000000..46fdccb --- /dev/null +++ b/tools/spasm/parser.c @@ -0,0 +1,478 @@ +#include "spasm.h" + +int atoiT[64]; + +static char *reg_names[] = +{ + "zero", + "at", + "v0", "v1", + "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", + "k0", "k1", + "gp", "sp", + "fp", + "ra", + NULL +}; + +static int regtoi(char *arg) +{ + int i; + + if(strcmp(arg, "s8") == 0) + arg = "fp"; + + for(i = 0; reg_names[i]; i++) + { + if(strcmp(arg, reg_names[i]) == 0) + return i; + } + + return -1; +} + +unsigned int asm_atoi(char *arg) +{ + unsigned int i; + + + if((i = regtoi(arg)) != -1) + { + atoiT[insArgc] = T_REGISTER; + return i; + } + else if(tolower((int)*arg) == 'r' &&( (strlen(arg) == 2 && isdigit((int)*(arg+1))) || + (strlen(arg) == 3 && isdigit((int)*(arg+1)) && isdigit((int)*(arg+2))))) + { + sscanf(arg+1, "%d", &i); + atoiT[insArgc] = T_REGISTER; + return i; + } + else if(*arg == '-' && *(arg+1) == '$' && isxdigit((unsigned int)*(arg+2)) ) + { + sscanf(arg+2, "%x", &i); + atoiT[insArgc] = T_INTEGER; + return -i; + } + else if(*arg == '$' ) + { + sscanf(arg+1, "%x", &i); + atoiT[insArgc] = T_INTEGER; + + return i; + } + else if(strcmp(arg, "*") == 0) + { + atoiT[insArgc] = T_INTEGER; + + return curPc; + } + else if(isalpha((unsigned int)*arg) || (*arg) == '_') + { + atoiT[insArgc] = T_LABEL; + + i = find_label(arg); + + return i; + } + + sscanf(arg, "%i", &i); + atoiT[insArgc] = T_INTEGER; + + return i; +} + +enum +{ + INITIAL, ARG_ENTER, COMMENT +}; + +char *spasm_parser(char *text, int pass) +{ + int i, j, l, m; + char linebuf[1024]; + char linebuf2[1024]; + char linebuf3[1024]; + char argbuf[1024]; + char *tok[256]; + int state = INITIAL; + int num_of_tok=0; + char *t; + curText = text; + unsigned int v; + +theBeginning: + i = 0; + curPass = pass; + org_found = 0; + first_instruction = 1; + line_number = 0; + text = curText; + + while(text[i]) + { + state = INITIAL; + + for(j = 0; text[i] && text[i] != '\n'; i++) + { + if(j < 1023 && text[i] != '\r') + linebuf[j++] = text[i]; + } + + line_number++; + rawArgc = insArgc = 0; + INSFUNC = NULL; + + if(text[i] == '\n') + i++; + + linebuf[j] = '\0'; + +//tokenize_line: + strcpy(linebuf2, linebuf); // Keep a second copy, we will need it later. + strcpy(linebuf3, linebuf); + curLine = linebuf3; + + char *a = linebuf; + char *s; + j = 0; + + num_of_tok = 0; + + for(m = 0; m < 256; m++) + tok[m]=NULL; + + while((s = strtok(a, " \t"))) + { + tok[num_of_tok++] = s; + a = NULL; + } + + tok[num_of_tok] = NULL; + + j = 0; + + + + while((s = tok[j])) + { + //printf("tok[%d] = %s\n", j, tok[j]); + + find_label_reset(); + + if(strlen(s) == 0) + { // A token with zero length is garbage, skip it + j++; + continue; + } + + //printf("s = ^%s\n", s); + + switch(state) + { + case INITIAL: // Initial case + + // Is this token a comment? + + if(*s == ';') + { + state = COMMENT; + break; + } + + // Is this token an instruction? + + if((INSFUNC = get_instruction(s))) + { + strncpy(curIns, s, 127); + s[127] = '\0'; + insArgc = 0; + state = ARG_ENTER; + argbuf[0] = '\0'; + + break; + } + + // Now we know it's a label + // There are two possible cases now + // - It's a label with a specified value: i.e. label EQU value + // - It's a label which has the current value of the program counter + + // First, we will check for EQU + + if((j + 2) < num_of_tok) + { // If there are not enough tokens in the line, don't bother checking for EQU. + if(strcasecmp(tok[j+1], "equ") == 0 || strcasecmp(tok[j+1], "=") == 0) + { // EQU found! Set label value to the one specified. + + // Set current instruction to EQU + strcpy(curIns, "equ"); + + // Remove quotes. Yes, in SPASM, values in EQU statements can have quotes + // even if they are numerical values! + + if( (t = strchr(tok[j+2], '"')) ) + { + *t = '\0'; + if( (t = strrchr(tok[j+2], '"')) ) + *t = '\0'; + } + + find_label_reset(); + + v = spasm_eval(tok[j+2]); + + if(strchr(tok[j+2], ';')) + state = COMMENT; + + if(!find_label_ok()) + assembler_error("Can't resolve expression for EQU statement"); + + if(find_label_ok()) + add_label_equ(tok[j], v); + else + add_label(tok[j], v); + + j+=2;// As we have processed the EQU, we need to jump two tokens ahead + break; + } + } + + // At this point, it is a label which has the current value of the program counter + + if((t = strrchr(tok[j], ':'))) + *t='\0'; // Remove trailing colon, if any. + + add_label(tok[j], curPc); + break; + + case ARG_ENTER: // Inside instruction + if(curPass == -1) + break; + + // Is this token a comment? + // If so, we do not have arguments anymore. + + + if(*s == ';') + { + state = COMMENT; + break; + } + + strcat(argbuf, s); + + int was_sp = 0; + int in_string = 0; + int stringt = 0; + char *argPlace = &linebuf2[ tok[j] - tok[0] ]; + + a = linebuf2; + + l = 0; + while(l < j && (argPlace = strtok(a, " \t"))) + { + a = NULL; + l++; + } + + while(*argPlace)argPlace++; + while(!(*argPlace))argPlace++; + + char arg[64]; + char *argp = arg; + int is_ok = 0; + int esc = 0; + + rawArgc = 0; + + // Emulate a bug in Hitmen's assembler + if(strcasecmp(curIns, "li") == 0) + { +argplace_li_remove_spaces_begin: + for(l = 0; argPlace[l]; l++) + { + if(argPlace[l] == ' ') + { + l++; + for(; argPlace[l]; l++) + argPlace[l-1] = argPlace[l]; + + argPlace[l-1] = '\0'; + + goto argplace_li_remove_spaces_begin; + } + } + } + + for(l = 0; argPlace[l] && rawArgc < 64; l++) + { + char c = argPlace[l]; + + if(in_string) + { + *(argp++) = c; + + if(!esc) + { + if(stringt == 0 && c == '"') + in_string = 0; + else if(stringt == 1 && c == '\'') + in_string = 0; + else if(c == '\\') + esc = 1; + } + else + esc = 0; + } + else + { + if(isalnum((unsigned int)c) || c == '_' || c == '$' || c == '.' || c == '*') + { + is_ok = 0; + + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + } + else if(c == '"') + { + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + in_string = 1; + stringt = 0; + esc = 0; + } + else if(c == '\'') + { + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + in_string = 1; + stringt = 1; + esc = 0; + } + else if(c == ' ' || c == '\t') + { + is_ok = 0; + was_sp = 1; + } + else if(c == '+' || c == '-' || c == '>' || c == '<' + || c == '(' || c == ')' || c == '&' || c == '|' || c == '!') + { + is_ok = 0; + *(argp++) = c; + } + else if(c == ',') + { + *argp = '\0'; + insArgt[rawArgc] = 0; + strcpy(rawArgv[rawArgc++], arg); + argp = arg; + was_sp = 0; + is_ok = 1; + } + else if(c == ';' || c == '/') + { +// '/' added in order to emulate a very buggy behavior of SPASM that is needed +// in order to assemble the imbNES 1.3.2 sources without modifications. +// yes, imbNES has some UNDENOTATED comments! +// +// pearls such as +// +// lw v0,$1074(v1) // this line would be at $DFAC where the jump goes from the patch +// +// and... +// +// lw v0,$DFFC(v0) load the address to jump back to that was set in _patch_card +// +// It makes no sense, but hey it works in the original SPASM! + + + goto noMoreArgs; + } + else + { + instruction_error("Invalid character!\n"); + break; + } + } + } + +noMoreArgs: + if(!is_ok) + { + *argp = '\0'; + + char *fb = strchr(arg, '('); + char *sb = strrchr(arg, ')'); + + + int pa=(*arg != '"' && *arg != '\'' && fb && sb && fb<sb); + + if(pa) + { + *fb = '\0'; + *sb = '\0'; + insArgt[rawArgc+1] = 1; + strcpy(rawArgv[rawArgc+1], fb+1); + } + + insArgt[rawArgc] = 0; + strcpy(rawArgv[rawArgc++], arg); + + if(pa)rawArgc++; + } + + insArgc = 0; + + find_label_reset(); + + for(l = 0; l < rawArgc; l++, insArgc++) + insArgv[l] = spasm_eval(rawArgv[l]); + + if(curPass == 1 && !find_label_ok()) + instruction_error("Can't resolve expression"); + + goto theNextLine; + break; + + case COMMENT: // Inside comment + + break; + } + + a = NULL; + j++; + } + +theNextLine: + if(INSFUNC) + { + if(curPass>=0)INSFUNC(); + + if(strcasecmp(curIns, "include") == 0 && curPass == 0) + goto theBeginning; + + first_instruction = 0; + } + } + + return curText; +} diff --git a/tools/spasm/parser.h b/tools/spasm/parser.h new file mode 100644 index 0000000..72ecb0d --- /dev/null +++ b/tools/spasm/parser.h @@ -0,0 +1,29 @@ +#ifndef _SPASM_PARSER_H +#define _SPASM_PARSER_H + +enum +{ + T_INTEGER = 1, T_REGISTER = 2, T_LABEL = 3 +}; + +extern char *fileBuffer; +extern unsigned int fileBufferSize; + +extern char *curText; +extern char *curLine; + +unsigned int asm_atoi(char *arg); +char *spasm_parser(char *text, int pass); + +extern char curIns[128]; +extern unsigned int curInsArg; +extern unsigned int curInsArgT; +extern unsigned int insArgv[64]; +extern unsigned int insArgt[64]; +extern unsigned int insArgc; +extern int rawArgc; +extern char rawArgv[64][128]; +extern FILE *asmOut; +extern int line_number; +extern int atoiT[64]; +#endif diff --git a/tools/spasm/readme.txt b/tools/spasm/readme.txt new file mode 100644 index 0000000..8baa290 --- /dev/null +++ b/tools/spasm/readme.txt @@ -0,0 +1,120 @@ +nv-SPASM 0.34.1 +(c) 2014-2015 Giuseppe Gatta, a.k.a. nextvolume <tails92@gmail.com> +------------------------- + +1. What is this? +2. Features +3. How to use +4. License + +1. What is this? +-------------------------- + +nv-SPASM is a stand-alone MIPS assembler that can output either a little-endian flat binary file +or Sony PlayStation PS-X EXE executable (the default). + +It is simple and lightweight, and it is ideal for people who just want to play around +with assembly without bothering with too many details which would arise with +more advanced tools, such as those provided by the GNU project. + +nv-SPASM is also meant to be compatible with the Hitmen group's SPASM assembler, +which was an early tool for PlayStation homebrew programmers who had no access +to more advanced software development kits, such as Net Yaroze or PsyQ. + +While to most a tool like SPASM isn't as useful as it was back in the day, due to the +availability of freely usable software development kits like the PSXSDK, as already +mentioned before there are still some use cases for which it's more feasible +to use nv-SPASM instead of more advanced tools. + +And, why not? nv-SPASM is easier to hack and to learn from than more advanced, +complex designs. + +You could use it as the assembler for your virtual machine, or even embed this assembler inside it. + +1. What is this not? +-------------------------- + +nv-SPASM isn't a complex and full-fledged macro assembler; in fact it does not +even support macros currently. + +You can't easily mix code written in a high-level language with assembly code +with nv-SPASM, as nv-SPASM is unable to output object files that can be linked +against other object files. + +nv-SPASM does not currently support complex expressions, just very simple ones. + +2. Features +-------------------------- + +- Complete MIPS R3000 instruction set support, with the following pseudo-instructions + implemented: + + - B - unconditional branch + - LA, LI - load label/integer inside a register (LA and LI are the same thing in nv-SPASM) + - NOP - No operation + - MOVE - Copy the contents of a register into another + - SUBI - Subtract immediate from register + - SUBIU - Subtract immediate unsigned from register + - BEQZ - Branch if register is Equal to Zero + - BNEZ - Branch if register is Not Equal to Zero + - BAL - unconditional branch-and-link + - ORG - move start address to the desired address + it relocates every instruction, and must be the first instruction. + - INCLUDE - include a source file + - INCBIN - include a binary file + - DCB - declare a block of bytes + - DB - declare bytes and/or strings + - DH - declare half-words and/or strings + - DW - declare words and/or strings + - ALIGN - align incoming code to the specified boundary + +- Compatibility with the Hitmen group's SPASM assembler and thus the possibility + of assembling software developed with it, barring bugs in nv-SPASM or the fact + that the original source code may depend on bugs in the Hitmen's SPASM assembler + in order to assemble correctly. + +- Very simple expression support, single operator, two operands. + For laymen, this means that while for instance 3+3 is rendered as 6, + 3+3+2 is rendered as 5, as if it were 3+2. + This is coherent with the original SPASM assembler. + Supported operators are +, -, >>, <<, & and | + +- Automatic generation of Sony PlayStation PS-X EXE executables. + +- Symbol names (labels) can be treated just like normal numerical values, + the numerical value being either the program counter value at the moment + the label was met by the assembler, or a value set with an EQU or = statement. + + +3. How to use +-------------------------- + +nv-SPASM is really simple to use, and does not need any special explanation. +Therefore, it will suffice to report its usage banner. + +nv-spASM version 0.34.1 (c) 2014-2015 nextvolume +Assembler for the Sony PlayStation + +Usage: ./spasm [options] infile outfile + +Options: -b create binary file instead of PS-X EXE + -N do not pad executable + -S be case sensitive on file paths + +4. License +-------------------------- + +It is permitted to do (almost) anything with the nv-SPASM code and documentation. +The only restriction is that all existing copyright notices must remain intact, and +modifed versions must ship with the corresponding modified source code. + +fcaseopen.c is (C) 2009 Keith Bauer. Read fcaseopen.c for the license (BSD-style) + +5. Useful information +-------------------------- + +Hitmen's official website <http://www.hitmen-console.org> +- The README file for Hitmen's original SPASM assembler is a good read, + as this assembler tries to closely follow it. +- The GreenTro source code is another good resource, as it was developed with SPASM, + and thus it can be considered an example to write nv-SPASM assembly source code. diff --git a/tools/spasm/spasm.c b/tools/spasm/spasm.c new file mode 100644 index 0000000..c94f5a7 --- /dev/null +++ b/tools/spasm/spasm.c @@ -0,0 +1,167 @@ +#include "spasm.h" +#include "fcaseopen.h" + +int rawArgc = 0; +char rawArgv[64][128]; +char *curText; +char *curLine; +FILE *asmOut; +int line_number; + +char *fileBuffer; +unsigned int fileBufferSize; + +int open_case_sensitive = 0; + +static void titlecard(void) +{ + printf("nv-spASM version 0.34.1 (c) 2014-2015 nextvolume\n"); +} + +static void usage(char *prog_name) +{ + titlecard(); + printf("Assembler for the Sony PlayStation\n"); + printf("\n"); + printf("Usage: %s [options] infile outfile\n", prog_name); + printf("\n"); + printf("Options: -b create binary file instead of PS-X EXE\n"); + printf(" -N do not pad executable\n"); + printf(" -S be case sensitive on file paths\n"); + printf("\n"); +} + +FILE *spasm_fopen(const char *path, const char *mode) +{ + if(open_case_sensitive) + return fopen(path, mode); + + return fcaseopen(path, mode); +} + +int main(int argc, char *argv[]) +{ + int sz; + int farg=1; + int bfile=0; + int no_newline=0; + int no_pad=0; + + if(argc < 3) + { + usage(argv[0]); + return EXIT_SUCCESS; + } + + while(argv[farg][0] == '-') + { + if(strcmp(argv[farg], "-b") == 0) + bfile = 1; // Generate binary file + else if(strcmp(argv[farg], "-N") == 0) + no_pad = 1; + else if(strcmp(argv[farg], "-S") == 0) + open_case_sensitive = 1; + else + { + usage(argv[0]); + return EXIT_FAILURE; + } + + farg++; + } + + FILE *f = spasm_fopen(argv[farg], "rb"); + + if(!f) + { + printf("Could not open file %s for reading! Exiting.\n", argv[farg]); + return EXIT_FAILURE; + } + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + fileBufferSize = sz + 2; // two newline bytes + two NUL bytes + fileBuffer = malloc(fileBufferSize); + fread(fileBuffer, sizeof(char), sz, f); + + if(fileBuffer[sz-1] != '\n') + no_newline = 1; + + fileBuffer[sz] = '\n'; + fileBuffer[sz+1] = '\0'; + + //fileBuffer = process_includes(fileBuffer); + + fclose(f); + + asmOut = fopen(argv[farg+1], "wb"); + + if(!asmOut) + { + printf("Could not open file %s for writing! Exiting.\n", argv[farg+1]); + return EXIT_FAILURE; + } + + startAddress = 0x80010000; + + titlecard(); + + if(no_newline) + printf("Warning: No newline found at end of file.\n"); + + + printf("\nPass 1\n"); + codegen_init(); + fileBuffer = spasm_parser(fileBuffer, -1); + fileBuffer = spasm_parser(fileBuffer, 0); + + /*if(!org_found) + { + printf("Warning: no ORG directive found... defaulting to $80010000\n"); + startAddress = 0x80010000; + }*/ + + curPc = startAddress; + + printf("Pass 2\n"); + printf("Writing output file %s ...\n", argv[farg+1]); + + if(!bfile) + OUTSEEK(0x800); + + fileBuffer = spasm_parser(fileBuffer, 1); + + if(!bfile) + { + OUTSEEK(0x0); + OUTSTRING("\"PS-X EXE\""); // PSX-EXE magic + OUTSEEK(0x10); + OUTWORD(startAddress); // Initial program counter + OUTSEEK(0x18); + OUTWORD(startAddress); // Text section start address + OUTSEEK(0x30); + OUTWORD(0x801FFFF0); // Initial stack pointer + + sz = OUTSIZE(); + + + if(!no_pad && (sz & 2047)) // Pad executable + { + sz |= 2047; + sz++; + OUTSEEK(sz-1); + OUTBYTE(0); + } + + OUTSEEK(0x1C); + OUTWORD(sz - 0x800); // Output size of text section + } + + printf("\nAssembly complete\n\n"); + + fclose(asmOut); + + return EXIT_SUCCESS; +} diff --git a/tools/spasm/spasm.h b/tools/spasm/spasm.h new file mode 100644 index 0000000..b1ea821 --- /dev/null +++ b/tools/spasm/spasm.h @@ -0,0 +1,20 @@ +#ifndef _SPASM_H +#define _SPASM_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include "error.h" +#include "codegen.h" +#include "parser.h" +#include "opcode.h" +#include "spasm.h" +#include "eval.h" + +FILE *spasm_fopen(const char *path, const char *mode); + +#endif |
