Cog/Frameworks/HighlyExperimental/HighlyExperimental/Core/r3000asm.c
2013-09-30 03:36:30 -07:00

441 lines
16 KiB
C

/////////////////////////////////////////////////////////////////////////////
//
// r3000asm - R3000 quick assembler (no symbols, no macro instructions)
//
/////////////////////////////////////////////////////////////////////////////
#ifndef EMU_COMPILE
#error "Hi I forgot to set EMU_COMPILE"
#endif
#include "r3000asm.h"
/////////////////////////////////////////////////////////////////////////////
enum {
TOKEN_NONE,
TOKEN_REG,
TOKEN_C0REG,
TOKEN_LPAREN,
TOKEN_RPAREN,
TOKEN_COMMA,
TOKEN_NUMBER,
TOKEN_UNKNOWN
};
/////////////////////////////////////////////////////////////////////////////
static int myisspace(unsigned char c) {
return (c == 9 || c == 10 || c == 13 || c == 32);
}
static int myisdigit(unsigned char c) {
return (c >= '0' && c <= '9');
}
static int myisalpha(unsigned char c) {
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
static int myisalnum(unsigned char c) {
return (myisalpha(c) || myisdigit(c));
}
/////////////////////////////////////////////////////////////////////////////
//
// Returns TRUE on match
//
static int alphacompare(const char *text, const char *pattern) {
for(; *pattern; pattern++, text++) {
if(!(*text)) return 0;
if(tolower(*text) != tolower(*pattern)) return 0;
}
// The key: The trailing text must be non-alnum
if(myisalnum(*text)) return 0;
return 1;
}
/////////////////////////////////////////////////////////////////////////////
static const char *scan(const char *text, uint32 *token, uint32 *subtoken) {
const char *s;
*token = TOKEN_UNKNOWN;
//
// Skip opening space
//
while(*text && myisspace(*text)) text++;
s = text;
//
// Token: None
//
if(!(*s)) {
*token = TOKEN_NONE;
return text;
}
//
// Token: Register
//
if(*s == '$') {
int regnum = 0;
s++;
if(myisdigit(*s)) {
regnum = *(s++) - '0';
if(myisdigit(*s)) {
regnum = (regnum * 10) + (*(s++) - '0');
}
if(regnum >= 32) return text;
} else {
char a = 0;
char b = 0;
if(*s) { a = tolower(*s); s++; }
if(*s) { b = tolower(*s); s++; }
if(a=='a'&&b=='t') { regnum = 1; }
else if(a=='v'&&b=='0') { regnum = 2; }
else if(a=='v'&&b=='1') { regnum = 3; }
else if(a=='a'&&b=='0') { regnum = 4; }
else if(a=='a'&&b=='1') { regnum = 5; }
else if(a=='a'&&b=='2') { regnum = 6; }
else if(a=='a'&&b=='3') { regnum = 7; }
else if(a=='t'&&b=='0') { regnum = 8; }
else if(a=='t'&&b=='1') { regnum = 9; }
else if(a=='t'&&b=='2') { regnum = 10; }
else if(a=='t'&&b=='3') { regnum = 11; }
else if(a=='t'&&b=='4') { regnum = 12; }
else if(a=='t'&&b=='5') { regnum = 13; }
else if(a=='t'&&b=='6') { regnum = 14; }
else if(a=='t'&&b=='7') { regnum = 15; }
else if(a=='s'&&b=='0') { regnum = 16; }
else if(a=='s'&&b=='1') { regnum = 17; }
else if(a=='s'&&b=='2') { regnum = 18; }
else if(a=='s'&&b=='3') { regnum = 19; }
else if(a=='s'&&b=='4') { regnum = 20; }
else if(a=='s'&&b=='5') { regnum = 21; }
else if(a=='s'&&b=='6') { regnum = 22; }
else if(a=='s'&&b=='7') { regnum = 23; }
else if(a=='t'&&b=='8') { regnum = 24; }
else if(a=='t'&&b=='9') { regnum = 25; }
else if(a=='k'&&b=='0') { regnum = 26; }
else if(a=='k'&&b=='1') { regnum = 27; }
else if(a=='g'&&b=='p') { regnum = 28; }
else if(a=='s'&&b=='p') { regnum = 29; }
else if(a=='f'&&b=='p') { regnum = 30; }
else if(a=='r'&&b=='a') { regnum = 31; }
else { return text; }
}
*token = TOKEN_REG;
*subtoken = regnum;
return s;
}
//
// Token: C0 register
//
if(tolower(s[0]) == 'C' && s[1] == '0' && s[2] == '_') {
int regnum = 0;
s += 3;
if(myisdigit(*s)) {
regnum = *(s++) - '0';
if(myisdigit(*s)) {
regnum = (regnum * 10) + (*(s++) - '0');
}
if(regnum >= 32) return text;
} else {
if(alphacompare(s, "status")) { regnum = 12; }
else if(alphacompare(s, "cause" )) { regnum = 13; }
else if(alphacompare(s, "epc" )) { regnum = 14; }
else { return text; }
}
*token = TOKEN_C0REG;
*subtoken = regnum;
return s;
}
//
// Token: Various punctuation
//
switch(*s) {
case ',': s++; *token = TOKEN_COMMA ; return s;
case '(': s++; *token = TOKEN_LPAREN; return s;
case ')': s++; *token = TOKEN_RPAREN; return s;
}
//
// Token: Number
//
if(myisdigit(*s) || *s == '-') {
int bNegative = 0;
int radix = 10;
int num = 0;
if(*s == '-') { bNegative = 1; s++; }
if(*s == '0') {
s++;
if(tolower(*s) == 'x') {
radix = 16;
s++;
} else {
radix = 8;
}
}
for(;;) {
int digit = 0;
char c = *s;
if(!c) break;
if(c >= '0' && c <= '9') { digit = c - '0'; }
else if(c >= 'a' && c <= 'f') { digit = 10 + c - 'a'; }
else if(c >= 'A' && c <= 'F') { digit = 10 + c - 'A'; }
else { break; }
if(digit >= radix) return text;
num *= radix;
num += digit;
s++;
}
if(bNegative) { num = -num; }
*token = TOKEN_NUMBER;
*subtoken = num;
return s;
}
//
// Token was unknown
//
return text;
}
/////////////////////////////////////////////////////////////////////////////
static void replaceinsfield(uint32 *ins, int nBitPosition, int nBits, uint32 dwValue) {
uint32 dwMask = (1 << nBits) - 1;
dwValue &= dwMask;
(*ins) &= ~(dwMask << nBitPosition);
(*ins) |= (dwValue << nBitPosition);
}
/////////////////////////////////////////////////////////////////////////////
static const char *assemblepattern(
const char *pattern,
uint32 pc,
const char *text,
uint32 *ins
) {
uint32 token;
uint32 subtoken;
for(;;) {
char p = *pattern++;
text = scan(text, &token, &subtoken);
switch(p) {
case 0:
if(token != TOKEN_NONE) return "Expected end-of-line";
return NULL;
case ',':
if(token != TOKEN_COMMA) return "Expected ','";
break;
case '(':
if(token != TOKEN_LPAREN) return "Expected '('";
break;
case ')':
if(token != TOKEN_RPAREN) return "Expected ')'";
break;
//
// S is 21
// T is 16
// D is 11
//
case 'S':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 21, 5, subtoken);
break;
case 'T':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 16, 5, subtoken);
break;
case 'D':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 11, 5, subtoken);
break;
//
// E = D and T
// F = D and S
// G = T and S
//
case 'E':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 11, 5, subtoken);
replaceinsfield(ins, 16, 5, subtoken);
break;
case 'F':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 11, 5, subtoken);
replaceinsfield(ins, 21, 5, subtoken);
break;
case 'G':
if(token != TOKEN_REG) return "Expected register name";
replaceinsfield(ins, 16, 5, subtoken);
replaceinsfield(ins, 21, 5, subtoken);
break;
case 'H':
if(token != TOKEN_NUMBER) return "Expected shift constant";
replaceinsfield(ins, 6, 5, subtoken);
break;
case 'I':
if(token != TOKEN_NUMBER) return "Expected number";
replaceinsfield(ins, 0, 16, subtoken);
break;
case 'C':
if(token != TOKEN_C0REG) return "Expected C0 register name";
replaceinsfield(ins, 11, 5, subtoken);
break;
case 'J':
if(token != TOKEN_NUMBER) return "Expected address";
replaceinsfield(ins, 0, 26, subtoken>>2);
break;
case 'B':
if(token != TOKEN_NUMBER) return "Expected address";
replaceinsfield(ins, 0, 16, (subtoken-(pc+4))>>2);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////
const char *assemblepatterns(
const char *patterns,
uint32 pc,
const char *text,
uint32 *ins,
uint32 insTemplate
) {
int i;
const char *complaint = NULL;
do {
char pattemp[20];
*ins = insTemplate;
for(i = 0;; i++) {
char c = *patterns;
if(!c) break;
patterns++;
if(c == '/') break;
pattemp[i] = c;
}
pattemp[i] = 0;
complaint = assemblepattern(pattemp, pc, text, ins);
if(!complaint) break;
} while(*patterns);
return complaint;
}
/////////////////////////////////////////////////////////////////////////////
static const char *assemble(uint32 pc, const char *text, uint32 *ins) {
//
// Skip opening space
//
while(*text && myisspace(*text)) text++;
//
// E = D and T
// F = D and S
// G = T and S
//
if(alphacompare(text, "nop" )) return assemblepatterns("" , pc, text + 3, ins, 0x00);
if(alphacompare(text, "sll" )) return assemblepatterns("E,H/D,T,H", pc, text + 3, ins, 0x00);
if(alphacompare(text, "srl" )) return assemblepatterns("E,H/D,T,H", pc, text + 3, ins, 0x02);
if(alphacompare(text, "sra" )) return assemblepatterns("E,H/D,T,H", pc, text + 3, ins, 0x03);
if(alphacompare(text, "sllv" )) return assemblepatterns("E,S/D,T,S", pc, text + 4, ins, 0x04);
if(alphacompare(text, "srlv" )) return assemblepatterns("E,S/D,T,S", pc, text + 4, ins, 0x06);
if(alphacompare(text, "srav" )) return assemblepatterns("E,S/D,T,S", pc, text + 4, ins, 0x07);
if(alphacompare(text, "jr" )) return assemblepatterns("S" , pc, text + 2, ins, 0x08);
if(alphacompare(text, "jalr" )) return assemblepatterns("S/D,S" , pc, text + 4, ins, 0x09|(0x1F<<11));
if(alphacompare(text, "syscall")) return assemblepatterns("" , pc, text + 7, ins, 0x0C);
if(alphacompare(text, "mfhi" )) return assemblepatterns("D" , pc, text + 4, ins, 0x10);
if(alphacompare(text, "mthi" )) return assemblepatterns("D" , pc, text + 4, ins, 0x11);
if(alphacompare(text, "mflo" )) return assemblepatterns("D" , pc, text + 4, ins, 0x12);
if(alphacompare(text, "mtlo" )) return assemblepatterns("D" , pc, text + 4, ins, 0x13);
if(alphacompare(text, "mult" )) return assemblepatterns("S,T" , pc, text + 4, ins, 0x18);
if(alphacompare(text, "multu" )) return assemblepatterns("S,T" , pc, text + 5, ins, 0x19);
if(alphacompare(text, "div" )) return assemblepatterns("S,T" , pc, text + 3, ins, 0x1A);
if(alphacompare(text, "divu" )) return assemblepatterns("S,T" , pc, text + 4, ins, 0x1B);
if(alphacompare(text, "add" )) return assemblepatterns("F,T/D,S,T", pc, text + 3, ins, 0x20);
if(alphacompare(text, "addu" )) return assemblepatterns("F,T/D,S,T", pc, text + 4, ins, 0x21);
if(alphacompare(text, "sub" )) return assemblepatterns("F,T/D,S,T", pc, text + 3, ins, 0x22);
if(alphacompare(text, "subu" )) return assemblepatterns("F,T/D,S,T", pc, text + 4, ins, 0x23);
if(alphacompare(text, "and" )) return assemblepatterns("F,T/D,S,T", pc, text + 3, ins, 0x24);
if(alphacompare(text, "or" )) return assemblepatterns("F,T/D,S,T", pc, text + 2, ins, 0x25);
if(alphacompare(text, "xor" )) return assemblepatterns("F,T/D,S,T", pc, text + 3, ins, 0x26);
if(alphacompare(text, "nor" )) return assemblepatterns("F,T/D,S,T", pc, text + 3, ins, 0x27);
if(alphacompare(text, "slt" )) return assemblepatterns("D,S,T" , pc, text + 3, ins, 0x2A);
if(alphacompare(text, "sltu" )) return assemblepatterns("D,S,T" , pc, text + 4, ins, 0x2B);
if(alphacompare(text, "bltz" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x01<<26)|(0x00<<16));
if(alphacompare(text, "bgez" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x01<<26)|(0x01<<16));
if(alphacompare(text, "bltzal" )) return assemblepatterns("S,B" , pc, text + 6, ins, (0x01<<26)|(0x10<<16));
if(alphacompare(text, "bgezal" )) return assemblepatterns("S,B" , pc, text + 6, ins, (0x01<<26)|(0x11<<16));
if(alphacompare(text, "j" )) return assemblepatterns("J" , pc, text + 1, ins, (0x02<<26));
if(alphacompare(text, "jal" )) return assemblepatterns("J" , pc, text + 3, ins, (0x03<<26));
if(alphacompare(text, "beq" )) return assemblepatterns("S,T,B" , pc, text + 3, ins, (0x04<<26));
if(alphacompare(text, "bne" )) return assemblepatterns("S,T,B" , pc, text + 3, ins, (0x05<<26));
if(alphacompare(text, "beqz" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x04<<26));
if(alphacompare(text, "bnez" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x05<<26));
if(alphacompare(text, "blez" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x06<<26));
if(alphacompare(text, "bgtz" )) return assemblepatterns("S,B" , pc, text + 4, ins, (0x07<<26));
if(alphacompare(text, "addi" )) return assemblepatterns("G,I/T,S,I", pc, text + 4, ins, (0x08<<26));
if(alphacompare(text, "addiu" )) return assemblepatterns("G,I/T,S,I", pc, text + 5, ins, (0x09<<26));
if(alphacompare(text, "slti" )) return assemblepatterns("T,S,I" , pc, text + 4, ins, (0x0A<<26));
if(alphacompare(text, "sltiu" )) return assemblepatterns("T,S,I" , pc, text + 5, ins, (0x0B<<26));
if(alphacompare(text, "andi" )) return assemblepatterns("G,I/T,S,I", pc, text + 4, ins, (0x0C<<26));
if(alphacompare(text, "ori" )) return assemblepatterns("G,I/T,S,I", pc, text + 3, ins, (0x0D<<26));
if(alphacompare(text, "xori" )) return assemblepatterns("G,I/T,S,I", pc, text + 4, ins, (0x0E<<26));
if(alphacompare(text, "lui" )) return assemblepatterns("T,I" , pc, text + 3, ins, (0x0F<<26));
if(alphacompare(text, "mfc0" )) return assemblepatterns("T,C" , pc, text + 4, ins, (0x10<<26)|(0x00<<21));
if(alphacompare(text, "mtc0" )) return assemblepatterns("T,C" , pc, text + 4, ins, (0x10<<26)|(0x04<<21));
if(alphacompare(text, "rfe" )) return assemblepatterns("" , pc, text + 3, ins, (0x10<<26)|(0x10<<21));
if(alphacompare(text, "lb" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x20<<26));
if(alphacompare(text, "lh" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x21<<26));
if(alphacompare(text, "lw" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x23<<26));
if(alphacompare(text, "lbu" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 3, ins, (uint32)(0x24<<26));
if(alphacompare(text, "lhu" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 3, ins, (uint32)(0x25<<26));
if(alphacompare(text, "sb" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x28<<26));
if(alphacompare(text, "sh" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x29<<26));
if(alphacompare(text, "sw" )) return assemblepatterns("T,(S)/T,I(S)", pc, text + 2, ins, (uint32)(0x2B<<26));
//
// Friendly instructions like not, move, neg, negu, li
// "li" always expands to a signed addiu - not an ori.
//
if(alphacompare(text, "move" )) return assemblepatterns("D,T" , pc, text + 4, ins, 0x21);
if(alphacompare(text, "neg" )) return assemblepatterns("E/D,T", pc, text + 3, ins, 0x22);
if(alphacompare(text, "negu" )) return assemblepatterns("E/D,T", pc, text + 4, ins, 0x23);
if(alphacompare(text, "not" )) return assemblepatterns("E/D,T", pc, text + 3, ins, 0x27);
if(alphacompare(text, "li" )) return assemblepatterns("T,I" , pc, text + 2, ins, (0x09<<26));
return "Unknown instuction name";
}
/////////////////////////////////////////////////////////////////////////////
//
// Returns negative on error (and fills the error string buffer)
// Must be 256 bytes in the error string buffer
//
sint32 EMU_CALL r3000asm(uint32 pc, const char *text, uint32 *ins, char *errorstring) {
const char *complaint = assemble(pc, text, ins);
if(complaint) {
int i;
for(i = 0; i < 255 && complaint[i]; i++) errorstring[i] = complaint[i];
errorstring[i] = 0;
return -1;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////