POCO用コンパイラ(笑)できたよー

昨日の計算機構成の課題1

演習6-1
0 番地,1 番地,2 番地にそれぞれP,Q,R が格納されている。(Q-P) OR (Q+R) の演算を行い、3 番地に格納するプログラムをPOCO で実行せよ。

機械語変換をミスって2と合わせて1時間もかかってしまったのでその反省を踏まえ、こういうのはコンパイラがやるべきという解決策を実行します。機械語変換でミスったんだからアセンブラ作ればよくね?


input:

( ( [ 1 ] - [ 0 ] ) | ( [ 1 ] + [ 2 ] ) )

output:

LDUI r0, #1
LD r1, (r0)
LDUI r0, #0
LD r2, (r0)
SUB r1, r2
LDUI r0, #1
LD r2, (r0)
LDUI r0, #2
LD r3, (r0)
ADD r2, r3
OR r1, r2
Result: r1

  • LDUI: LoaD Unsigned Immediate
  • LD: LoaD
  • ADD SUB OR
    • 左と右のレジスタを何かして結果を左に入れる


文法

  • 数値
    • そのまま数字。POCOは16bitだけどとりあえず即値に書ける-128..+127。
  • [数値]
    • そのメモリアドレスにある値。メモリ空間は16bitあるけどとりあえず(ryの0..255。
  • (式 演算子 式)
    • とりあえず+ - & |の4つが使用可能。


問題点

  • トークンの入力にscanf"%s"を使っているので全てのトークンをスペース等で区切る必要がある。
  • 普通の数式はめんどいので解析しやすいよう"(" left_expr operator right_expr ")"というようにカッコをただ1回必須。
  • レジスタはr0がメモリ操作用に予約、残り7つ。足りなくなると死ぬ。メモリを使いだしたりとか賢いことはしない。
  • 要するにレキサがゴミ。パーザもゴミ。
  • 我ながらひどいやっつけである。
  • まあでも機械語を吐いて横にコメントで今吐いてるアセンブラを出せば使える…かもしれない。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

#define TOKEN_MAX 256
#define TOKEN_MAX_S "255"
#define MAX8 127
#define MIN8 (-128)
#define MAXU8 255
#define MINU8 0

int reg_table[8] = {1, 0, 0, 0, 0, 0, 0, 0};

int expr();
int alloc_reg();
void free_reg(int reg);
int parse_int(const char *str, int min, int max);
void next_token(char *buf);
void on_error(const char *msg);

int expr() {
	char text[TOKEN_MAX];
	next_token(text);

	if (isdigit(text[0]) || text[0] == '-') {
		int reg, num;
		reg = alloc_reg();
		num = parse_int(text, MIN8, MAX8);
		printf("LDI r%d, #%d\n", reg, num);
		return reg;
	} else if (text[0] == '[') {
		int reg, addr;
		char straddr[TOKEN_MAX], close[TOKEN_MAX];

		next_token(straddr);
		next_token(close);
		if (strcmp(close, "]") != 0) {
			on_error("Parse error.");
		}

		reg = alloc_reg();
		addr = parse_int(straddr, MINU8, MAXU8);
		printf("LDUI r0, #%d\n", addr);
		printf("LD r%d, (r0)\n", reg);
		return reg;
	} else if (strcmp(text, "(") == 0) {
		int reg1, reg2;
		char op[TOKEN_MAX], close[TOKEN_MAX];

		reg1 = expr();
		next_token(op);
		reg2 = expr();
		next_token(close);
		if (strcmp(close, ")") != 0) {
			on_error("Parse error.");
		}
		if (strcmp(op, "+") == 0) {
			printf("ADD r%d, r%d\n", reg1, reg2);
		} else if (strcmp(op, "-") == 0) {
			printf("SUB r%d, r%d\n", reg1, reg2);
		} else if (strcmp(op, "&") == 0) {
			printf("AND r%d, r%d\n", reg1, reg2);
		} else if (strcmp(op, "|") == 0) {
			printf("OR r%d, r%d\n", reg1, reg2);
		} else {
			on_error("Parse error.");
		}

		free_reg(reg2);
		return reg1;
	} else {
		on_error("Parse error.");
		return -1;
	}
}

int alloc_reg() {
	int i;
	for (i=0; i<sizeof(reg_table)/sizeof(reg_table[0]); i++) {
		if (!reg_table[i]) {
			reg_table[i] = 1;
			return i;
		}
	}
	on_error("Register full.");
	return -1;
}

void free_reg(int reg) {
	assert(reg_table[reg]);
	reg_table[reg] = 0;
}

int parse_int(const char *str, int min, int max) {
	char *endptr;
	long num = strtol(str, &endptr, 10);
	if (*endptr != '\0') {
		on_error("Number format error.");
	} else if (num > max || num < min) {
		on_error("Number out of range.");
	}
	return (int)num;
}

void next_token(char *buf) {
	if (scanf("%" TOKEN_MAX_S "s", buf) != 1) {
		on_error("Parse error.");
	}
}

void on_error(const char *msg) {
	fputs(msg, stderr);
	exit(1);
}

int main() {
	int reg;
	reg = expr();
	assert(reg >= 0 && reg <= 7);
	printf("Result: r%d\n", reg);
	return 0;
}