388 lines
9.6 KiB
C
388 lines
9.6 KiB
C
|
/* Assembler code fragment for LC-2K */
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#define MAXLINELENGTH 1000
|
||
|
|
||
|
int readAndParse(FILE *, char *, char *, char *, char *, char *);
|
||
|
int isNumber(char *, int *);
|
||
|
|
||
|
typedef struct{
|
||
|
char *word;
|
||
|
unsigned int hash;
|
||
|
int line_number;
|
||
|
} Label;
|
||
|
|
||
|
typedef struct{
|
||
|
char *word_space;
|
||
|
char *word_cursor;
|
||
|
Label *labels;
|
||
|
int count;
|
||
|
int max;
|
||
|
} Table;
|
||
|
|
||
|
char *
|
||
|
copy_advance(char *dest, char *src){
|
||
|
while (*dest++ = *src++);
|
||
|
return(dest);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
get_hash(char *string){
|
||
|
unsigned int x, c;
|
||
|
x = 5236;
|
||
|
while (c = *string++){
|
||
|
x += c + (x << 5) + (x << 1);
|
||
|
}
|
||
|
return(x);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
add_label(Table *table, char *string, int line_number){
|
||
|
int r;
|
||
|
Label l, *ls;
|
||
|
int max;
|
||
|
unsigned int index;
|
||
|
|
||
|
r = 0;
|
||
|
max = table->max;
|
||
|
ls = table->labels;
|
||
|
|
||
|
l.word = table->word_cursor;
|
||
|
table->word_cursor = copy_advance(table->word_cursor, string);
|
||
|
l.hash = get_hash(string);
|
||
|
l.line_number = line_number;
|
||
|
index = l.hash % max;
|
||
|
|
||
|
while (ls[index].word){
|
||
|
if (ls[index].hash == l.hash){
|
||
|
if (strcmp(ls[index].word, l.word) == 0){
|
||
|
r = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
index = (index + 1) % max;
|
||
|
}
|
||
|
|
||
|
if (r == 0) ls[index] = l;
|
||
|
++table->count;
|
||
|
|
||
|
return(r);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
find_label(Table *table, char *string, int *line_number){
|
||
|
int r;
|
||
|
Label l, *ls;
|
||
|
int max;
|
||
|
unsigned int hash,index;
|
||
|
|
||
|
r = 0;
|
||
|
max = table->max;
|
||
|
ls = table->labels;
|
||
|
hash = get_hash(string);
|
||
|
index = hash % max;
|
||
|
|
||
|
while (ls[index].word){
|
||
|
if (ls[index].hash == hash){
|
||
|
if (strcmp(ls[index].word, string) == 0){
|
||
|
r = 1;
|
||
|
*line_number = ls[index].line_number;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
index = (index + 1) % max;
|
||
|
}
|
||
|
|
||
|
return(r);
|
||
|
}
|
||
|
|
||
|
#define OP_FILL -1
|
||
|
#define OP_ADD 0
|
||
|
#define OP_NAND 1
|
||
|
#define OP_LW 2
|
||
|
#define OP_SW 3
|
||
|
#define OP_BEQ 4
|
||
|
#define OP_JALR 5
|
||
|
#define OP_HALT 6
|
||
|
#define OP_NOOP 7
|
||
|
|
||
|
#define MIN16S -32768
|
||
|
#define MAX16S 32767
|
||
|
|
||
|
int
|
||
|
encode_insanity(FILE *outFilePtr, int o, int a, int b, int c){
|
||
|
int ins, r;
|
||
|
|
||
|
r = 0;
|
||
|
if (o == OP_FILL){
|
||
|
r = (b != 0 || c != 0);
|
||
|
}
|
||
|
else{
|
||
|
r = (a < 0 || a > 7 || b < 0 || b > 7);
|
||
|
if (!r){
|
||
|
switch (o){
|
||
|
case OP_ADD: case OP_NAND:
|
||
|
r = (c < 0 || c > 7); break;
|
||
|
|
||
|
case OP_LW: case OP_SW:
|
||
|
case OP_BEQ: case OP_JALR:
|
||
|
r = (c < MIN16S || c > MAX16S); break;
|
||
|
|
||
|
case OP_HALT: case OP_NOOP:
|
||
|
r = (a != 0 || b != 0 || c != 0); break;
|
||
|
|
||
|
default: r = 1; break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!r){
|
||
|
if (o == OP_FILL){
|
||
|
ins = a;
|
||
|
}
|
||
|
else{
|
||
|
ins = 0;
|
||
|
ins |= (o << 22);
|
||
|
ins |= (a << 19);
|
||
|
ins |= (b << 16);
|
||
|
ins |= 0xFFFF & (c);
|
||
|
}
|
||
|
fprintf(outFilePtr, "%d\n", ins);
|
||
|
}
|
||
|
|
||
|
return(r);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main(int argc, char *argv[])
|
||
|
{
|
||
|
Table table;
|
||
|
char *inFileString, *outFileString;
|
||
|
FILE *inFilePtr, *outFilePtr;
|
||
|
char label[MAXLINELENGTH], opcode[MAXLINELENGTH], arg0[MAXLINELENGTH],
|
||
|
arg1[MAXLINELENGTH], arg2[MAXLINELENGTH];
|
||
|
int max_instructions;
|
||
|
int line_number;
|
||
|
int i;
|
||
|
int o,a,b,c;
|
||
|
|
||
|
if (argc != 3) {
|
||
|
printf("error: usage: %s <assembly-code-file> <machine-code-file>\n",
|
||
|
argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
inFileString = argv[1];
|
||
|
outFileString = argv[2];
|
||
|
|
||
|
inFilePtr = fopen(inFileString, "r");
|
||
|
if (inFilePtr == NULL) {
|
||
|
printf("error in opening %s\n", inFileString);
|
||
|
exit(1);
|
||
|
}
|
||
|
outFilePtr = fopen(outFileString, "w");
|
||
|
if (outFilePtr == NULL) {
|
||
|
printf("error in opening %s\n", outFileString);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
fseek(inFilePtr, 0, SEEK_END);
|
||
|
max_instructions = ftell(inFilePtr);
|
||
|
fseek(inFilePtr, 0, SEEK_SET);
|
||
|
max_instructions >>= 2;
|
||
|
|
||
|
table.max = max_instructions * 3 / 2;
|
||
|
table.labels = (Label*)malloc(sizeof(Label)*table.max);
|
||
|
table.count = 0;
|
||
|
table.word_space = table.word_cursor = (char*)malloc((max_instructions << 2) + max_instructions);
|
||
|
memset(table.labels, 0, sizeof(Label)*table.max);
|
||
|
|
||
|
line_number = 0;
|
||
|
while (readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2)){
|
||
|
if (*label != 0){
|
||
|
if (add_label(&table, label, line_number)){
|
||
|
printf("error two labels of same name: %s\n", label);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
++line_number;
|
||
|
}
|
||
|
|
||
|
rewind(inFilePtr);
|
||
|
|
||
|
line_number = 0;
|
||
|
while (readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2)){
|
||
|
if (strcmp(opcode, "add") == 0){
|
||
|
o = 0;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
sscanf(arg2, "%d", &c);
|
||
|
}
|
||
|
else if (strcmp(opcode, "nand") == 0){
|
||
|
o = 1;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
sscanf(arg2, "%d", &c);
|
||
|
}
|
||
|
else if (strcmp(opcode, "lw") == 0){
|
||
|
o = 2;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
if (!isNumber(arg2, &c)){
|
||
|
if (!find_label(&table, arg2, &c)){
|
||
|
printf("non-existant label: %s\n", arg2);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (strcmp(opcode, "sw") == 0){
|
||
|
o = 3;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
if (!isNumber(arg2, &c)){
|
||
|
if (!find_label(&table, arg2, &c)){
|
||
|
printf("non-existant label: %s\n", arg2);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (strcmp(opcode, "beq") == 0){
|
||
|
o = 4;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
if (!isNumber(arg2, &c)){
|
||
|
if (!find_label(&table, arg2, &c)){
|
||
|
printf("non-existant label: %s\n", arg2);
|
||
|
exit(1);
|
||
|
}
|
||
|
c = c - (line_number + 1);
|
||
|
}
|
||
|
}
|
||
|
else if (strcmp(opcode, "jalr") == 0){
|
||
|
o = 5;
|
||
|
sscanf(arg0, "%d", &a);
|
||
|
sscanf(arg1, "%d", &b);
|
||
|
c = 0;
|
||
|
}
|
||
|
else if (strcmp(opcode, "halt") == 0){
|
||
|
o = 6;
|
||
|
a = b = c = 0;
|
||
|
}
|
||
|
else if (strcmp(opcode, "noop") == 0){
|
||
|
o = 7;
|
||
|
a = b = c = 0;
|
||
|
}
|
||
|
else if (strcmp(opcode, ".fill") == 0){
|
||
|
o = -1;
|
||
|
if (!isNumber(arg0, &a)){
|
||
|
if (!find_label(&table, arg0, &a)){
|
||
|
printf("non-existant label: %s\n", arg0);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
b = c = 0;
|
||
|
}
|
||
|
else{
|
||
|
printf("not real instruction found: %s\n", opcode);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (encode_insanity(outFilePtr, o, a, b, c)){
|
||
|
printf("encoding error on line %d\n", line_number + 1);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
++line_number;
|
||
|
}
|
||
|
|
||
|
free(table.word_space);
|
||
|
free(table.labels);
|
||
|
fclose(inFilePtr);
|
||
|
fclose(outFilePtr);
|
||
|
exit(0);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read and parse a line of the assembly-language file. Fields are returned
|
||
|
* in label, opcode, arg0, arg1, arg2 (these strings must have memory already
|
||
|
* allocated to them).
|
||
|
*
|
||
|
* Return values:
|
||
|
* 0 if reached end of file
|
||
|
* 1 if all went well
|
||
|
*
|
||
|
* exit(1) if line is too long.
|
||
|
*/
|
||
|
int
|
||
|
readAndParse(FILE *inFilePtr, char *label, char *opcode, char *arg0,
|
||
|
char *arg1, char *arg2)
|
||
|
{
|
||
|
char line[MAXLINELENGTH + 1];
|
||
|
char *ptr = line;
|
||
|
int l;
|
||
|
|
||
|
/* delete prior values */
|
||
|
label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0';
|
||
|
|
||
|
/* read the line from the assembly-language file */
|
||
|
if (fgets(line, MAXLINELENGTH, inFilePtr) == NULL) {
|
||
|
/* reached end of file */
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
if (feof(inFilePtr)){
|
||
|
l = strlen(line);
|
||
|
if (line[l-1] != '\n'){
|
||
|
line[l] = '\n';
|
||
|
line[l+1] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
char line[MAXLINELENGTH];
|
||
|
char *ptr = line;
|
||
|
|
||
|
/* delete prior values */
|
||
|
label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0';
|
||
|
|
||
|
/* read the line from the assembly-language file */
|
||
|
if (fgets(line, MAXLINELENGTH, inFilePtr) == NULL) {
|
||
|
/* reached end of file */
|
||
|
return(0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* check for line too long (by looking for a \n) */
|
||
|
if (strchr(line, '\n') == NULL) {
|
||
|
/* line too long */
|
||
|
printf("error: line too long\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* is there a label? */
|
||
|
ptr = line;
|
||
|
if (sscanf(ptr, "%[^\t\n\r ]", label)) {
|
||
|
/* successfully read label; advance pointer over the label */
|
||
|
ptr += strlen(label);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Parse the rest of the line. Would be nice to have real regular
|
||
|
* expressions, but scanf will suffice.
|
||
|
*/
|
||
|
sscanf(ptr, "%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]",
|
||
|
opcode, arg0, arg1, arg2);
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
isNumber(char *string, int *i)
|
||
|
{
|
||
|
return( (sscanf(string, "%d", i)) == 1);
|
||
|
}
|