/*
|
/*
|
* asm.c -- assembler
|
* asm.c -- assembler
|
*/
|
*/
|
|
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "console.h"
|
#include "console.h"
|
#include "error.h"
|
#include "error.h"
|
#include "instr.h"
|
#include "instr.h"
|
#include "asm.h"
|
#include "asm.h"
|
|
|
|
|
#define MAX_TOKENS 10
|
#define MAX_TOKENS 10
|
|
|
|
|
static char *msgs[] = {
|
static char *msgs[] = {
|
/* 0 */ "too many tokens on line",
|
/* 0 */ "too many tokens on line",
|
/* 1 */ "empty line",
|
/* 1 */ "empty line",
|
/* 2 */ "unknown instruction name",
|
/* 2 */ "unknown instruction name",
|
/* 3 */ "unknown instruction format",
|
/* 3 */ "unknown instruction format",
|
/* 4 */ "excess tokens on line",
|
/* 4 */ "excess tokens on line",
|
/* 5 */ "too few operands",
|
/* 5 */ "too few operands",
|
/* 6 */ "illegal register",
|
/* 6 */ "illegal register",
|
/* 7 */ "illegal immediate value",
|
/* 7 */ "illegal immediate value",
|
/* 8 */ "immediate value out of range",
|
/* 8 */ "immediate value out of range",
|
/* 9 */ "target is not aligned",
|
/* 9 */ "target is not aligned",
|
/* 10 */ "target cannot be reached"
|
/* 10 */ "target cannot be reached"
|
};
|
};
|
|
|
|
|
static Bool asmReg(char *token, int *reg) {
|
static Bool asmReg(char *token, int *reg) {
|
char *end;
|
char *end;
|
|
|
if (*token != '$') {
|
if (*token != '$') {
|
return false;
|
return false;
|
}
|
}
|
*reg = strtoul(token + 1, &end, 10);
|
*reg = strtoul(token + 1, &end, 10);
|
if (*end != '\0') {
|
if (*end != '\0') {
|
return false;
|
return false;
|
}
|
}
|
if (*reg < 0 || *reg >= 32) {
|
if (*reg < 0 || *reg >= 32) {
|
return false;
|
return false;
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
|
|
static Bool asmNum(char *token, unsigned int *val) {
|
static Bool asmNum(char *token, unsigned int *val) {
|
char *end;
|
char *end;
|
|
|
*val = strtoul(token, &end, 16);
|
*val = strtoul(token, &end, 16);
|
return *end == '\0';
|
return *end == '\0';
|
}
|
}
|
|
|
|
|
char *asmInstr(char *line, Word addr, Word *instrPtr) {
|
char *asmInstr(char *line, Word addr, Word *instrPtr) {
|
char *tokens[MAX_TOKENS];
|
char *tokens[MAX_TOKENS];
|
int n;
|
int n;
|
char *p;
|
char *p;
|
Instr *instr;
|
Instr *instr;
|
Word result;
|
Word result;
|
int r1, r2, r3;
|
int r1, r2, r3;
|
unsigned int uimm;
|
unsigned int uimm;
|
signed int simm;
|
signed int simm;
|
|
|
/* separate tokens */
|
/* separate tokens */
|
n = 0;
|
n = 0;
|
p = strtok(line, " \t\n,");
|
p = strtok(line, " \t\n,");
|
while (p != NULL) {
|
while (p != NULL) {
|
if (n == MAX_TOKENS) {
|
if (n == MAX_TOKENS) {
|
return msgs[0];
|
return msgs[0];
|
}
|
}
|
tokens[n++] = p;
|
tokens[n++] = p;
|
p = strtok(NULL, " \t\n,");
|
p = strtok(NULL, " \t\n,");
|
}
|
}
|
if (n == 0) {
|
if (n == 0) {
|
return msgs[1];
|
return msgs[1];
|
}
|
}
|
/* lookup mnemonic */
|
/* lookup mnemonic */
|
instr = lookupInstr(tokens[0]);
|
instr = lookupInstr(tokens[0]);
|
if (instr == NULL) {
|
if (instr == NULL) {
|
return msgs[2];
|
return msgs[2];
|
}
|
}
|
/* do processing according to format */
|
/* do processing according to format */
|
switch (instr->format) {
|
switch (instr->format) {
|
case FORMAT_N:
|
case FORMAT_N:
|
/* no operands (but may get a constant operand) */
|
/* no operands (but may get a constant operand) */
|
if (n > 2) {
|
if (n > 2) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 1) {
|
if (n < 1) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (n == 2) {
|
if (n == 2) {
|
if (!asmNum(tokens[1], &uimm)) {
|
if (!asmNum(tokens[1], &uimm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
} else {
|
} else {
|
uimm = 0;
|
uimm = 0;
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(uimm & MASK(26));
|
(uimm & MASK(26));
|
break;
|
break;
|
case FORMAT_RH:
|
case FORMAT_RH:
|
/* one register and a half operand */
|
/* one register and a half operand */
|
if (n > 3) {
|
if (n > 3) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 3) {
|
if (n < 3) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmNum(tokens[2], &uimm)) {
|
if (!asmNum(tokens[2], &uimm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if (uimm >= (unsigned) (1 << 16)) {
|
if (uimm >= (unsigned) (1 << 16)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(uimm & MASK(16));
|
(uimm & MASK(16));
|
break;
|
break;
|
case FORMAT_RHH:
|
case FORMAT_RHH:
|
/* one register and a half operand */
|
/* one register and a half operand */
|
/* ATTENTION: high-order 16 bits encoded */
|
/* ATTENTION: high-order 16 bits encoded */
|
if (n > 3) {
|
if (n > 3) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 3) {
|
if (n < 3) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmNum(tokens[2], &uimm)) {
|
if (!asmNum(tokens[2], &uimm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
uimm >>= 16;
|
uimm >>= 16;
|
if (uimm >= (unsigned) (1 << 16)) {
|
if (uimm >= (unsigned) (1 << 16)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(uimm & MASK(16));
|
(uimm & MASK(16));
|
break;
|
break;
|
case FORMAT_RRH:
|
case FORMAT_RRH:
|
/* two registers and a half operand */
|
/* two registers and a half operand */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmNum(tokens[3], &uimm)) {
|
if (!asmNum(tokens[3], &uimm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if (uimm >= (unsigned) (1 << 16)) {
|
if (uimm >= (unsigned) (1 << 16)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(uimm & MASK(16));
|
(uimm & MASK(16));
|
break;
|
break;
|
case FORMAT_RRS:
|
case FORMAT_RRS:
|
/* two registers and a signed half operand */
|
/* two registers and a signed half operand */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if (simm >= (signed) (1 << 15) ||
|
if (simm >= (signed) (1 << 15) ||
|
simm < - (signed) (1 << 15)) {
|
simm < - (signed) (1 << 15)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(simm & MASK(16));
|
(simm & MASK(16));
|
break;
|
break;
|
case FORMAT_RRR:
|
case FORMAT_RRR:
|
/* three register operands */
|
/* three register operands */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[3], &r3)) {
|
if (!asmReg(tokens[3], &r3)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r3 << 16) |
|
(r3 << 16) |
|
(r1 << 11);
|
(r1 << 11);
|
break;
|
break;
|
case FORMAT_RRX:
|
case FORMAT_RRX:
|
/* either FORMAT_RRR or FORMAT_RRH */
|
/* either FORMAT_RRR or FORMAT_RRH */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (*tokens[3] == '$') {
|
if (*tokens[3] == '$') {
|
/* FORMAT_RRR */
|
/* FORMAT_RRR */
|
if (!asmReg(tokens[3], &r3)) {
|
if (!asmReg(tokens[3], &r3)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r3 << 16) |
|
(r3 << 16) |
|
(r1 << 11);
|
(r1 << 11);
|
} else {
|
} else {
|
/* FORMAT_RRH */
|
/* FORMAT_RRH */
|
if (!asmNum(tokens[3], &uimm)) {
|
if (!asmNum(tokens[3], &uimm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if (uimm >= (unsigned) (1 << 16)) {
|
if (uimm >= (unsigned) (1 << 16)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = (((Word) instr->opcode + 1) << 26) |
|
result = (((Word) instr->opcode + 1) << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(uimm & MASK(16));
|
(uimm & MASK(16));
|
}
|
}
|
break;
|
break;
|
case FORMAT_RRY:
|
case FORMAT_RRY:
|
/* either FORMAT_RRR or FORMAT_RRS */
|
/* either FORMAT_RRR or FORMAT_RRS */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (*tokens[3] == '$') {
|
if (*tokens[3] == '$') {
|
/* FORMAT_RRR */
|
/* FORMAT_RRR */
|
if (!asmReg(tokens[3], &r3)) {
|
if (!asmReg(tokens[3], &r3)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r3 << 16) |
|
(r3 << 16) |
|
(r1 << 11);
|
(r1 << 11);
|
} else {
|
} else {
|
/* FORMAT_RRS */
|
/* FORMAT_RRS */
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if (simm >= (signed) (1 << 15) ||
|
if (simm >= (signed) (1 << 15) ||
|
simm < - (signed) (1 << 15)) {
|
simm < - (signed) (1 << 15)) {
|
return msgs[8];
|
return msgs[8];
|
}
|
}
|
result = (((Word) instr->opcode + 1) << 26) |
|
result = (((Word) instr->opcode + 1) << 26) |
|
(r2 << 21) |
|
(r2 << 21) |
|
(r1 << 16) |
|
(r1 << 16) |
|
(simm & MASK(16));
|
(simm & MASK(16));
|
}
|
}
|
break;
|
break;
|
case FORMAT_RRB:
|
case FORMAT_RRB:
|
/* two registers and a 16 bit signed offset operand */
|
/* two registers and a 16 bit signed offset operand */
|
if (n > 4) {
|
if (n > 4) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 4) {
|
if (n < 4) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmReg(tokens[2], &r2)) {
|
if (!asmReg(tokens[2], &r2)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
if (!asmNum(tokens[3], (unsigned int *) &simm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if ((simm & 0x00000003) != 0) {
|
if ((simm & 0x00000003) != 0) {
|
return msgs[9];
|
return msgs[9];
|
}
|
}
|
simm -= addr + 4;
|
simm -= addr + 4;
|
simm /= 4;
|
simm /= 4;
|
if (simm >= (signed) (1 << 15) ||
|
if (simm >= (signed) (1 << 15) ||
|
simm < - (signed) (1 << 15)) {
|
simm < - (signed) (1 << 15)) {
|
return msgs[10];
|
return msgs[10];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r1 << 21) |
|
(r1 << 21) |
|
(r2 << 16) |
|
(r2 << 16) |
|
(simm & MASK(16));
|
(simm & MASK(16));
|
break;
|
break;
|
case FORMAT_J:
|
case FORMAT_J:
|
/* no registers and a 26 bit signed offset operand */
|
/* no registers and a 26 bit signed offset operand */
|
if (n > 2) {
|
if (n > 2) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 2) {
|
if (n < 2) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmNum(tokens[1], (unsigned int *) &simm)) {
|
if (!asmNum(tokens[1], (unsigned int *) &simm)) {
|
return msgs[7];
|
return msgs[7];
|
}
|
}
|
if ((simm & 0x00000003) != 0) {
|
if ((simm & 0x00000003) != 0) {
|
return msgs[9];
|
return msgs[9];
|
}
|
}
|
simm -= addr + 4;
|
simm -= addr + 4;
|
simm /= 4;
|
simm /= 4;
|
if (simm >= (signed) (1 << 25) ||
|
if (simm >= (signed) (1 << 25) ||
|
simm < - (signed) (1 << 25)) {
|
simm < - (signed) (1 << 25)) {
|
return msgs[10];
|
return msgs[10];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(simm & MASK(26));
|
(simm & MASK(26));
|
break;
|
break;
|
case FORMAT_JR:
|
case FORMAT_JR:
|
/* one register operand */
|
/* one register operand */
|
if (n > 2) {
|
if (n > 2) {
|
return msgs[4];
|
return msgs[4];
|
}
|
}
|
if (n < 2) {
|
if (n < 2) {
|
return msgs[5];
|
return msgs[5];
|
}
|
}
|
if (!asmReg(tokens[1], &r1)) {
|
if (!asmReg(tokens[1], &r1)) {
|
return msgs[6];
|
return msgs[6];
|
}
|
}
|
result = ((Word) instr->opcode << 26) |
|
result = ((Word) instr->opcode << 26) |
|
(r1 << 21);
|
(r1 << 21);
|
break;
|
break;
|
default:
|
default:
|
return msgs[3];
|
return msgs[3];
|
}
|
}
|
/* line successfully assembled */
|
/* line successfully assembled */
|
*instrPtr = result;
|
*instrPtr = result;
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|