#!/bin/ruby # Written by: Long =begin Each line of a CAL16R assembly language program may contain the following: o zero or one label, followed by o zero or one instruction, followed by o zero or one comment. o Each label is a letter followed by a series of "symbol characters", that is, letters, digits, or the underscore ('_') character, followed by a colon (":"). Each instruction is terminated by a ';'. Whitespace may but need not follow the ':' in a label or the ';' terminating an instruction. Each comment starts with the pound-sign character ('#') and continues to the end of the line. The rules above define all legal lines. Each instruction consists of an instruction name (opcode) followed by operands; the number and format of the operands depend on the instruction. There is a special "pseudo instruction" .data that permits the declaration of static variables. The allowable instructions are the following: and d a b or d a b xnor d a b add d a b addi d a signedint4 rotr d a unsignedint4 lw d signedint4(a) sw d signedint4(a) jalr d signedint4(a) bneg a labelsymbol bz a labelsymbol lhi a labelsymbol llo a labelsymbol lhi a unsignedint16 llo a unsignedint16 disp a unsignedint8 j labelsymbol .data signedint Operands are preceded by whitespace and/or commas and separated by whitespace and/or commas. (except for parentheses in the case of lw, sw, and jalr) the lhi and llo pseudoinstructions both translate to an li instruction. lhi takes the top 8 bits of the 2nd operand and puts it in the specified register, while llo puts the lower 8 bits. Each a, b, d in the instructions above has the format $n, where n is one of {0, 1, ..., 15}. Signed and unsigned immediates are self-explanatory. A labelsymbol is a label without the terminating colon. =end # Stop looking.... now. symTable = {} instList = [] outList = [] opDict = {"and"=>"0","or"=>"1","xor"=>"2","add"=>"3","addi"=>"4","rotr"=>"5", "sw"=>"6","lw"=>"7","bneg"=>"a","bz"=>"b","jalr"=>"c"} addr = 0 if ARGV[0].nil? raise "blank input" end File.open(ARGV[0]) do |f| while(line = f.gets) do if line.include?("#") line = line[0...line.index('#')].strip next if line == "" end if line.include?(":") symTable[line[/^[^:]+/]] = addr line = line[line.index(":")+1..-1] end next if !line.include?(";") instList << line[/^[^;]*/].strip.split(/[\s,()]+/) addr += 1 end end lineNum = 0 instList.each do |inst| case inst[0] when "and", "or", "xor", "add" outList << opDict[inst[0].downcase] + inst[2][1..-1].to_i.to_s(base=16) + inst[1][1..-1].to_i.to_s(base=16) + inst[3][1..-1].to_i.to_s(base=16) when "addi", "rotr" outList << opDict[inst[0].downcase] + inst[2][1..-1].to_i.to_s(base=16) + inst[1][1..-1].to_i.to_s(base=16) + (inst[3].to_i & 15).to_s(base=16) when "lw", "sw", "jalr" outList << opDict[inst[0].downcase] + inst[3][1..-1].to_i.to_s(base=16) + inst[1][1..-1].to_i.to_s(base=16) + (inst[2].to_i & 15).to_s(base=16) when "bneg", "bz" outList << ((opDict[inst[0].downcase]+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | ((symTable[inst[2]]-lineNum) & 0xFF)).to_s(base=16) when "lhi" if symTable.has_key?(inst[2]) outList << (("8"+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | ((symTable[inst[2]] & 0xFF00) >> 8)).to_s(base=16) else outList << (("8"+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | ((inst[2].to_i & 0xFF00) >> 8)).to_s(base=16) end when "llo" if symTable.has_key?(inst[2]) outList << (("8"+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | (symTable[inst[2]] & 0xFF)).to_s(base=16) else outList << (("8"+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | (inst[2].to_i & 0xFF)).to_s(base=16) end when "j" if symTable.has_key?(inst[1]) outList << (0xF000 | (symTable[inst[1]] & 0x0FFF)).to_s(base=16) else outList << "ffff" end when ".data" outList << sprintf("%04x",inst[1].to_i).gsub(".","f") when "disp" outList << (("9"+inst[1][1..-1].to_i.to_s(base=16) +"00").to_i(base=16) | (inst[2].to_i & 0xFF)).to_s(base=16) end lineNum += 1 end print "v2.0 raw\n" outList.each do |i| print i + "\n" end