All pastes #1055476 Raw Edit

Something

public text v1 · immutable
#1055476 ·published 2008-06-25 03:17 UTC
rendered paste body
using System;
using System.IO;
using Nemerle.Collections;
using Nemerle.Compiler;

namespace Grace.Assembler.Macros {
	public enum Opcode {
		| Nop = 0x00
		
		| Add  = 0x01
		| Sub  = 0x02
		| Mul  = 0x03
		| Div  = 0x04
		| Mod  = 0x05
		| Xor  = 0x06
		| Or   = 0x07
		| And  = 0x08
		| Not  = 0x09
		| Shl  = 0x0A
		| Shr  = 0x0B
		| AShr = 0x0C
		| Neg  = 0x0D
		
		| Call   = 0x20
		| Return = 0x21
		
		| Jmp = 0x30
		| Jc  = 0x31
		| Jnc = 0x32
		
		| Load  = 0x40
		| Store = 0x41
		| Move  = 0x42
		| Push  = 0x43
		| Pop   = 0x44
	}
	
	public variant Operand {
		| Register {
			Num : int
		}
		| BigReg {
			Num : int
		}
		| Imm {
			Num : int
		}
		| RelImm {
			Num : int
		}
	}
	
	public variant Instruction {
		| Branch {
			Type : Opcode;
			Target : string;
		}
		| Final {
			Opcode : Opcode;
			OpA : Operand;
			OpB : Operand;
			OpC : Operand;
		}
	}
	
	macro grace(file, body)
	syntax("grace", "(", file, ")", body) {
		def lines = match(body) {
			| <[ { .. $lines } ]> =>
				lines
			| _ => null
		}
		
		mutable Instructions = [];
		def Labels : Hashtable[string, int] = Hashtable();
		mutable pos = 0;
		
		def ParseOp(op) {
			def op = op.ToString();
			if(op.StartsWith("r")) {
				Operand.Register(Convert.ToInt32(op.Substring(1)))
			} else if(op.StartsWith("br")) {
				Operand.BigReg(Convert.ToInt32(op.Substring(2)))
			} else
				Operand.Imm(Convert.ToInt32(op))
		}
		
		def ParseLine(line) {
			| <[ $left = $right ]> =>
				def left = ParseOp(left);
				match(right) {
					| <[ $a +   $b ]> with op = Opcode.Add
					| <[ $a -   $b ]> with op = Opcode.Sub
					| <[ $a *   $b ]> with op = Opcode.Mul
					| <[ $a /   $b ]> with op = Opcode.Div
					| <[ $a %   $b ]> with op = Opcode.Mod
					| <[ $a ^   $b ]> with op = Opcode.Xor
					| <[ $a %|  $b ]> with op = Opcode.Or
					| <[ $a &   $b ]> with op = Opcode.And
					| <[ $a <<  $b ]> with op = Opcode.Shl
					| <[ $a >>  $b ]> with op = Opcode.Shr
					| <[ $a >>> $b ]> with op = Opcode.AShr =>
						Instructions = Instruction.Final(
							op,
							left,
							ParseOp(a),
							ParseOp(b)
						) :: Instructions
					
					| <[ ~ $a ]> with op = Opcode.Not
					| <[ - $a ]> with op = Opcode.Neg =>
						Instructions = Instruction.Final(
							op,
							left,
							ParseOp(a),
							null
						) :: Instructions
					
					| right =>
						Instructions = Instruction.Final(
							Opcode.Move,
							left,
							ParseOp(right),
							null
						) :: Instructions
				}
				pos += 8
			
			| <[ label($label) ]> =>
				Labels[label.ToString()] = pos
			
			| <[ $j($label) ]> =>
				Instructions = Instruction.Branch(
					match(j) {
						| <[ call ]> => Opcode.Call
						| <[ jmp ]> => Opcode.Jmp
						| _ => Opcode.Nop
					},
					label.ToString()
				) :: Instructions;
				pos += 8
			
			| <[ return ]> =>
				Instructions = Instruction.Final(
					Opcode.Return, 
					null, 
					null, 
					null
				) :: Instructions;
				pos += 8
			
			| _ => ()
		}
		
		def CountOps(a, b, c) {
			match((a, b, c)) {
				| (null, null, null) => 0
				| (_, null, null) => 1
				| (_, _, null) => 2
				| (_, _, _) => 3
			}
		}
		
		def EncodeForm(op : Operand) {
			| null | Register => 0b00
			| BigReg => 0b01
			| Imm => 0b10
			| RelImm => 0b11
		}
		
		def EncodeOp(op : Operand, buf, pos) {
			match(op) {
				| null with num = 0
				| Register(num)
				| BigReg(num)
				| Imm(num)
				| RelImm(num) =>
					buf[pos] = ((num >> 8) & 0xFF) :> byte;
					buf[pos+1] = (num & 0xFF) :> byte
			}
		}
		
		foreach(line in lines) {
			ParseLine(line)
		}
		
		pos = 0;
		def code = array(Instructions.Length * 8);
		foreach(inst in Instructions.Reverse()) {
			def inst = (
				match(inst) {
					| Branch(btype, target) =>
						Instruction.Final(
							btype,
							Operand.RelImm(Labels[target] - pos),
							null,
							null
						)
					
					| inst => inst
				} :> Instruction.Final
			);
			
			def (opcd, a, b, c) = inst;
			
			code[pos+0] = (
				(CountOps(a, b, c) << 6) | 
				(EncodeForm(a) << 4) | 
				(EncodeForm(b) << 2) | 
				EncodeForm(c)
			) :> byte;
			code[pos+1] = opcd :> byte;
			EncodeOp(a, code, pos+2);
			EncodeOp(b, code, pos+4);
			EncodeOp(c, code, pos+6);
			
			pos += 8
		}
		
		File.OpenWrite(
			match(file) {
				| <[ $(file : string) ]> =>
					file
				| _ => null
			}
		).Write(code, 0, code.Length);
		
		<[ () ]>
	}
}