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); <[ () ]> } }