POCOアセンブラ

アセンブラみたいに文法がものすごく簡単で、しかし手書きだと細かいところがめんどくさくて死ねるものに対してパーザジェネレータはものすごく便利だということがわかった。木構造とか全然してないし、javaccコードの中にガリガリ書いていける。
授業のページ
http://www.am.ics.keio.ac.jp/parthenon/
を見たら次回の資料がアップされていたので新命令BMI・JMPにも対応。
実際これはかなり使えると思う。現状「人間」が「アセンブラ」を書くのが一番効率的だろうからね(この文、何のことか分かってない人にはかなり怪しいな…)。改めて言っておくけど、これは計算機構成という授業の演習を早く終わらせて、北斗をやる時間を増やすための作業です。しかし次回からVerilogでPoco自体を改造する課題になるみたいだから本当に役に立つか…?というかこのVerilogコード結構わけわからんから(北斗の時間が)危ないかもしれない。予習必須。
途中から単純作業になったけど2時間半ほどかかってしまった。コピペも多かったのでバグが怖い。一応次回のサンプルを通した限りでは間違いはなかったけど…。


追記:そんなに北斗がやりたいのかという質問を受けましたが、確かに北斗北斗と書きすぎていました。実際のところ、北斗と麻雀と非想天則の3択だと思われます。


次回のサンプルプログラム、「第七回授業資料」の下のimem.datからアセンブラ部分を取り出して今回作ったアセンブラに通した結果がこちら。
input

LDIU r0, #2
LD r1,(r0)
LDIU r0, #3
LD r2,(r0)
LDIU r3, #0
ADD r3, r1
ADDI r2, #-1
BNZ r2, #-3
LDIU r0, #0
ST r3,(r0)
BEZ r2, #-1

output

01001_000_00000010		// LDIU r0, #2
00000_001_000_01001		// LD r1, (r0)
01001_000_00000011		// LDIU r0, #3
00000_010_000_01001		// LD r2, (r0)
01001_011_00000000		// LDIU r3, #0
00000_011_001_00110		// ADD r3, r1
01100_010_11111111		// ADDI r2, #-1
10001_010_11111101		// BEZ r2, #-3
01001_000_00000000		// LDIU r0, #0
00000_011_000_01000		// ST r3, (r0)
10000_010_11111111		// BEZ r2, #-1

元データ

01001_000_00000010          // LDIU r0, #2
00000_001_000_01001         // LD r1,(r0)
01001_000_00000011          // LDIU r0, #3
00000_010_000_01001         // LD r2,(r0)
01001_011_00000000          // LDIU r3, #0
00000_011_001_00110         // ADD r3, r1
01100_010_11111111          // ADDI r2, #-1
10001_010_11111101          // BNZ r2, #-3
01001_000_00000000          // LDIU r0, #0
00000_011_000_01000         // ST r3,(r0)
10000_010_11111111          // BEZ r2, #-1


javacc

options {
	STATIC = false;
}

PARSER_BEGIN(Pocoa)

package pocoa;

public class Pocoa {

	private static String toBinU(int num, int bit) {
		assert bit > 0;
		if (num < 0 || num > ((1 << bit) - 1))
			throw new Error(bit + " bit out of range: " + num);
		StringBuilder buf = new StringBuilder(bit);
		for( int i=bit-1; i>=0; i--) {
			int pattern = 1 << i;
			boolean b = (num & pattern) != 0;
			buf.append(b ? '1' : '0');
		}
		assert buf.length() == bit;
		return buf.toString();
	}

	private static String toBin(int num, int bit) {
		assert bit > 0;
		if (num < -(1 << (bit - 1)) || num > ((1 << (bit - 1)) - 1))
			throw new Error(bit + " bit out of range: " + num);
		StringBuilder buf = new StringBuilder(bit);
		for(int i=bit-1; i>=0; i--) {
			int pattern = 1 << i;
			boolean b = (num & pattern) != 0;
			buf.append(b ? '1' : '0');
		}
		assert buf.length() == bit;
		return buf.toString();
	}

	private static void printType1(
		String iname, String r1, String r2, String funcCode,
		boolean isAddr)
	{
		String br1 = (r1 != null) ?
			toBinU(Integer.parseInt(r1.substring(1)), 3) : "000";
		String br2 = (r2 != null) ?
			toBinU(Integer.parseInt(r2.substring(1)), 3) : "000";
		assert br1.length() == 3;
		assert br2.length() == 3;
		assert funcCode.length() == 5;
		System.out.printf("00000_%s_%s_%s", br1, br2, funcCode);
		System.out.print("\t\t// ");
		System.out.print(iname);
		if (r1 != null)
			System.out.printf(" %s", r1);
		if (r2 != null) {
			if (isAddr) {
				System.out.printf(", (%s)", r2);
			} else {
				System.out.printf(", %s", r2);
			}
		}
		System.out.println();
	}

	private static void printType2(
		String iname, String opecode, String rd, String x, 
		boolean signed)
	{
		String brd = toBinU(Integer.parseInt(rd.substring(1)), 3);
		int ix = Integer.parseInt(x);
		String bx = signed ? toBin(ix, 8) : toBinU(ix, 8);
		assert opecode.length() == 5;
		System.out.printf("%s_%s_%s", opecode, brd, bx);
		System.out.print("\t\t// ");
		System.out.printf("%s %s, #%s%n", iname, rd, x);
	}

	private static void printType3(String iname, String opecode, String x) {
		String bx = toBin(Integer.parseInt(x), 11);
		assert opecode.length() == 5;
		System.out.printf("%s_%s", opecode, bx);
		System.out.print("\t\t// ");
		System.out.printf("%s #%s%n", iname, x);
	}

	public static void main(String[] args) throws Exception {
		Pocoa p = new Pocoa(System.in);
		p.program();
	}

}

PARSER_END(Pocoa)

SKIP: {
	" "
|	"\t"
|	"\r"
|	"\n"
}

SPECIAL_TOKEN: {
	< COMMENT: "//" (~["\r", "\n"])* ("\r" | "\n" | "\r\n") >
}

TOKEN: {
	< NUMBER: ("-")? (["0"-"9"])+ >
|	< REG   : "r" ["0"-"7"] >
|	< SHARP : "#" >
|	< COMMA : "," >
|	< LPAREN: "(" >
|	< RPAREN: ")" >

|	< NOP  : "NOP" >
|	< MV   : "MV" >
|	< AND  : "AND" >
|	< OR   : "OR" >
|	< SL   : "SL" >
|	< SR   : "SR" >
|	< ADD  : "ADD" >
|	< SUB  : "SUB" >
|	< ST   : "ST" >
|	< LD   : "LD" >
|	< LDI  : "LDI" >
|	< LDIU : "LDIU" >
|	< ADDI : "ADDI" >
|	< ADDIU: "ADDIU" >
|	< LDHI : "LDHI" >
|	< BEZ  : "BEZ" >
|	< BNZ  : "BNZ" >
|	< BMI  : "BMI" >
|	< JMP  : "JMP" >
}

void program():
{}
{
	(instruction())*
}

void instruction():
{Token rd, rs, ra, x;}
{
	<NOP>
	{
		printType1("NOP", null, null, "00000", false);
	}
|	<MV> rd = <REG> <COMMA> rs = <REG>
	{
		printType1("MV", rd.image, rs.image, "00001", false);
	}
|	<AND> rd = <REG> <COMMA> rs = <REG>
	{
		printType1("AND", rd.image, rs.image, "00010", false);
	}
|	<OR> rd = <REG> <COMMA> rs = <REG>
	{
		printType1("OR", rd.image, rs.image, "00011", false);
	}
|	<SL> rd = <REG>
	{
		printType1("SL", rd.image, null, "00100", false);
	}
|	<SR> rd = <REG>
	{
		printType1("SR", rd.image, null, "00101", false);
	}
|	<ADD> rd = <REG> <COMMA> rs = <REG>
	{
		printType1("ADD", rd.image, rs.image, "00110", false);
	}
|	<SUB> rd = <REG> <COMMA> rs = <REG>
	{
		printType1("SUB", rd.image, rs.image, "00111", false);
	}
|	<ST> rs = <REG> <COMMA> <LPAREN> ra = <REG> <RPAREN>
	{
		printType1("ST", rs.image, ra.image, "01000", true);
	}
|	<LD> rd = <REG> <COMMA> <LPAREN> ra = <REG> <RPAREN>
	{
		printType1("LD", rd.image, ra.image, "01001", true);
	}

|	<LDI> rd = <REG> <COMMA> <SHARP> x = <NUMBER>
	{
		printType2("LDI", "01000", rd.image, x.image, true);
	}
|	<LDIU> rd = <REG> <COMMA> <SHARP> x = <NUMBER>
	{
		printType2("LDIU", "01001", rd.image, x.image, false);
	}
|	<ADDI> rd = <REG> <COMMA> <SHARP> x = <NUMBER>
	{
		printType2("ADDI", "01100", rd.image, x.image, true);
	}
|	<ADDIU> rd = <REG> <COMMA> <SHARP> x = <NUMBER>
	{
		printType2("ADDIU", "01101", rd.image, x.image, false);
	}
|	<LDHI> rd = <REG> <COMMA> <SHARP> x = <NUMBER>
	{
		printType2("ADDIU", "01010", rd.image, x.image, false);
	}
|	<BEZ> rd = <REG> <COMMA> (<SHARP>)? x = <NUMBER>
	{
		printType2("BEZ", "10000", rd.image, x.image, true);
	}
|	<BNZ> rd = <REG> <COMMA> (<SHARP>)? x = <NUMBER>
	{
		printType2("BEZ", "10001", rd.image, x.image, true);
	}
|	<BMI> rd = <REG> <COMMA> (<SHARP>)? x = <NUMBER>
	{
		printType2("BEZ", "10011", rd.image, x.image, true);
	}

|	<JMP> (<SHARP>)? x = <NUMBER>
	{
		printType3("JMP", "10101", x.image);
	}

}