Write basic assembler.
This commit is contained in:
parent
6bceee5e68
commit
115dce6b8c
228
assembler.cr
Normal file
228
assembler.cr
Normal file
|
@ -0,0 +1,228 @@
|
|||
require "option_parser"
|
||||
|
||||
input = ""
|
||||
output = ""
|
||||
unit = "ns"
|
||||
cycle = 20
|
||||
start = 20
|
||||
|
||||
prog_pin = "{sim:/cpu/prog}"
|
||||
inst_pin = "{sim:/cpu/pinst}"
|
||||
addr_pin = "{sim:/cpu/paddr}"
|
||||
|
||||
OptionParser.parse! do |parser|
|
||||
parser.banner = "Usage: assembler [arguments]"
|
||||
parser.on("-f NAME", "--from=NAME", "Select the input file.") do |file|
|
||||
input = file
|
||||
end
|
||||
parser.on("-t NAME", "--to=NAME", "Select the output file.") do |file|
|
||||
output = file
|
||||
end
|
||||
parser.on("-u UNIT", "--unit=UNIT", "select the time units.") do |u|
|
||||
unit = u
|
||||
end
|
||||
parser.on("-c CYCLE", "--cycle=CYCLE", "select the clock cycle speed") do |c|
|
||||
cycle = c.to_i? || 20
|
||||
end
|
||||
parser.on("-b BEGIN", "--begin=BEGIN", "select the beginning of the programming mode") do |b|
|
||||
start = b.to_i? || 20
|
||||
end
|
||||
parser.on("-p PPIN", "--prog-pin=PPIN", "select the programming enable pin") do |pp|
|
||||
prog_pin = pp
|
||||
end
|
||||
parser.on("-a APIN", "--addr-pin=APIN", "select address pin") do |ap|
|
||||
addr_pin = ap
|
||||
end
|
||||
parser.on("-i IPIN", "--inst-pin=IPIN", "select instruction pin") do |ip|
|
||||
inst_pin = ip
|
||||
end
|
||||
end
|
||||
|
||||
enum InstType
|
||||
RInst,
|
||||
IInst,
|
||||
JInst,
|
||||
SInst,
|
||||
DInst
|
||||
end
|
||||
|
||||
# w c j s o p
|
||||
# 0 0 0 0 0 0
|
||||
|
||||
INSTS = {
|
||||
"add" => { "100010", InstType::RInst },
|
||||
"sub" => { "100110", InstType::RInst },
|
||||
"and" => { "100000", InstType::RInst },
|
||||
"or" => { "100001", InstType::RInst },
|
||||
"slt" => { "100011", InstType::RInst },
|
||||
"addc" => { "110010", InstType::IInst },
|
||||
"subc" => { "110110", InstType::IInst },
|
||||
"andc" => { "110000", InstType::IInst },
|
||||
"orc" => { "110001", InstType::IInst },
|
||||
"sltc" => { "110011", InstType::IInst },
|
||||
"jmp" => { "001100", InstType::JInst },
|
||||
"jez" => { "001000", InstType::JInst },
|
||||
"jnz" => { "001001", InstType::JInst },
|
||||
"disp" => { "000000", InstType::DInst},
|
||||
"read" => { "100011", InstType::SInst},
|
||||
}
|
||||
|
||||
def decode_register(reg)
|
||||
if reg.size != 2
|
||||
puts "Invalid register operand size."
|
||||
return nil
|
||||
end
|
||||
|
||||
if reg[0] != 'r'
|
||||
puts "Expected register parameter."
|
||||
return nil
|
||||
end
|
||||
|
||||
if val = reg[1].to_i?
|
||||
if val <= 7
|
||||
return val
|
||||
else
|
||||
puts "Out of bounds register number"
|
||||
end
|
||||
else
|
||||
puts "Register must be a number."
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def decode_int(param)
|
||||
if param.starts_with? "0x"
|
||||
return param[2..-1].to_i?(16)
|
||||
elsif param.starts_with? "0b"
|
||||
return param[2..-1].to_i?(2)
|
||||
else
|
||||
return param.to_i?
|
||||
end
|
||||
end
|
||||
|
||||
def decode_params(params, string): Array(Int32|String)?
|
||||
params = params.dup
|
||||
|
||||
if params.size != string.size
|
||||
puts "Invalid number of parameters."
|
||||
return nil
|
||||
end
|
||||
|
||||
output = string.chars.map do |char|
|
||||
param = params.shift
|
||||
case char
|
||||
when 'r'
|
||||
decode_register(param)
|
||||
when 'c'
|
||||
decode_int(param)
|
||||
when 's'
|
||||
param
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
if output.includes? nil
|
||||
return nil
|
||||
else
|
||||
return output.compact
|
||||
end
|
||||
end
|
||||
|
||||
def decode_inst(labels, params)
|
||||
inst = params.shift
|
||||
if inst_data = INSTS[inst]?
|
||||
code, type = inst_data
|
||||
case type
|
||||
when InstType::RInst
|
||||
data = decode_params(params, "rrr")
|
||||
return nil unless data
|
||||
return code +
|
||||
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
||||
data[1].as(Int32).to_s(2).rjust(3, '0') +
|
||||
data[2].as(Int32).to_s(2).rjust(3, '0') +
|
||||
"00000000000000000"
|
||||
when InstType::IInst
|
||||
data = decode_params(params, "rrc")
|
||||
return nil unless data
|
||||
return code +
|
||||
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
||||
data[1].as(Int32).to_s(2).rjust(3, '0') +
|
||||
"0000" +
|
||||
data[2].as(Int32).to_s(2).rjust(16, '0')
|
||||
when InstType::JInst
|
||||
if inst == "jmp"
|
||||
data = decode_params(params, "s")
|
||||
return nil unless data
|
||||
return code +
|
||||
"0000000000" + labels[data[0].as(String)].to_s(2).rjust(16, '0')
|
||||
else
|
||||
data = decode_params(params, "rs")
|
||||
return nil unless data
|
||||
return code +
|
||||
"000" +
|
||||
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
||||
"0000" +
|
||||
labels[data[1].as(String)].to_s(2).rjust(16, '0')
|
||||
end
|
||||
when InstType::DInst
|
||||
data = decode_params(params, "r")
|
||||
return nil unless data
|
||||
return code +
|
||||
"000" +
|
||||
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
||||
"000" +
|
||||
"00000000000000000"
|
||||
when InstType::SInst
|
||||
data = decode_params(params, "r")
|
||||
return nil unless data
|
||||
return code +
|
||||
data[0].as(Int32).to_s(2).rjust(3, '0') +
|
||||
"000" +
|
||||
"000" +
|
||||
"00000000000000000"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decode_insts(labels, insts)
|
||||
decoded = insts.map {|inst| decode_inst(labels, inst) }
|
||||
if decoded.includes? nil
|
||||
return nil
|
||||
else
|
||||
return decoded.compact
|
||||
end
|
||||
end
|
||||
|
||||
if input == ""
|
||||
puts "Please select an input file."
|
||||
elsif output == ""
|
||||
puts "Please select an output file."
|
||||
elsif !File.exists?(input) || File.directory?(input)
|
||||
puts "Please select a valid input file."
|
||||
else
|
||||
labels = {} of String => Int32
|
||||
raw_instructions = [] of Array(String)
|
||||
lines = File.read_lines(input)
|
||||
lines.map_with_index do |line, index|
|
||||
beforecomment = line.split(/#/).first.strip.split /\s+/
|
||||
if beforecomment[0].ends_with? ":"
|
||||
labels[beforecomment.shift.split(/:/).first] = index
|
||||
end
|
||||
raw_instructions.push(beforecomment) unless beforecomment.empty?
|
||||
end
|
||||
|
||||
if instructions = decode_insts(labels, raw_instructions)
|
||||
dofile = File.open(output, "w")
|
||||
dofile << "force " << prog_pin << " 1 @ " << start << unit << "\n"
|
||||
instructions.each_with_index do |instruction, index|
|
||||
timestamp = (start + index * cycle)
|
||||
dofile << "force " << inst_pin << " {16'b" << instruction << "} @ " << timestamp << unit << "\n"
|
||||
dofile << "force " << addr_pin << " " << index.to_s << " @ " << timestamp << unit << "\n"
|
||||
end
|
||||
dofile << "force " << prog_pin << " 0 @ " << (start + instructions.size * cycle) << unit << "\n"
|
||||
dofile.close
|
||||
else
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user