summaryrefslogtreecommitdiff
path: root/tools/spasm
diff options
context:
space:
mode:
authorXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
committerXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
commit7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch)
treec28d0748652ad4b4222309e46e6cfc82c0906220 /tools/spasm
parenta2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff)
downloadpsxsdk-7c24e9a9b02b04dcaf9507acb94091ea70a2c02d.tar.gz
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'tools/spasm')
-rw-r--r--tools/spasm/Makefile17
-rw-r--r--tools/spasm/codegen.c144
-rw-r--r--tools/spasm/codegen.h33
-rw-r--r--tools/spasm/error.c58
-rw-r--r--tools/spasm/error.h10
-rw-r--r--tools/spasm/eval.c130
-rw-r--r--tools/spasm/eval.h6
-rwxr-xr-xtools/spasm/fcaseopen.c137
-rwxr-xr-xtools/spasm/fcaseopen.h40
-rw-r--r--tools/spasm/opcode.c2214
-rw-r--r--tools/spasm/opcode.h108
-rw-r--r--tools/spasm/parser.c478
-rw-r--r--tools/spasm/parser.h29
-rw-r--r--tools/spasm/readme.txt120
-rw-r--r--tools/spasm/spasm.c167
-rw-r--r--tools/spasm/spasm.h20
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