Skip to content

Instantly share code, notes, and snippets.

@dodeja
Created December 12, 2013 19:31
Show Gist options
  • Select an option

  • Save dodeja/7933961 to your computer and use it in GitHub Desktop.

Select an option

Save dodeja/7933961 to your computer and use it in GitHub Desktop.
PDP 8 Benchmarking tool - Instruction Set Simulation Tool
#-----------------------------------------------------------------------------------------------------
# => ECE 486 Project 1
# => PDP 8 Benchmarking tool
# => Instruction Set Simulation Tool
# => Akshay Dodeha | Matt Curren | David Richoux | Anil Palli
#-----------------------------------------------------------------------------------------------------
require 'PDP8_stats'
class PDP8
# => Read the object file and load it into memory
#-----------------------------------------------------------------------------------------------------
print "Enter name of Object file: "
test_file = gets
test_file = test_file.strip
f = File.open(test_file,"r")
lines = f.readlines
@n = 0
f.close
# => Setup Data Structures
prog_data = Hash.new { |hash, key| hash[key] = {:addr => "", :data => ""} }
prog_addr = Hash.new
@trace = Array.new
@prog_branch = Hash.new { |hash, key| hash[key] = {:type => "", :exec => 0.0, :taken => 0.0, :percent_taken => 0.0} }
ins = 0 # => Instruction Counter
first_addr = false # => First Instruction
@branch_addr = 0
# => Increment Octal by 1 (address)
#-----------------------------------------------------------------------------------------------------
def self.incre_oct(val)
new_val = (val.to_i(8) + 1).to_s(8)
if(new_val.length > 3)
return new_val
else
return "0"+new_val
end
end
# => Increment Octal (DATA) by 1
#-----------------------------------------------------------------------------------------------------
def self.incre_data(val)
new_val = (val.to_i(8) + 1).to_s(8)
if(new_val.to_i > 7777)
return 0
@lk == 0 ? 1 : 0
else
return new_val
end
end
# => Increment Octal (DATA) by 1
#-----------------------------------------------------------------------------------------------------
def self.comp_oct(val)
new_val = val
0.upto(val.length-1) do |x|
case val[x..x].to_i
when 0
new_val[x..x] = '7'
when 1
new_val[x..x] = '6'
when 2
new_val[x..x] = '5'
when 3
new_val[x..x] = '4'
when 4
new_val[x..x] = '3'
when 5
new_val[x..x] = '2'
when 6
new_val[x..x] = '1'
when 7
new_val[x..x] = '0'
end
end
return new_val
end
# => Parse the Lines into a 2-D Array
#-----------------------------------------------------------------------------------------------------
first_inst = 0
lines.each_index do |x|
if(x % 2 == 0)
if(lines[x].index('1') == 0)
n = 0
prog_data[ins][:addr] = "0" + lines[x][1..2].to_i.to_s + lines[x + 1][1..2]
prog_data[ins][:data] = lines[x + 2][1..2] + lines[x + 3][1..2]
ins = ins + 1
first_addr = true
else
if(!first_addr)
new_oct = incre_oct(prog_data[ins-1][:addr])
prog_data[ins][:addr] = "0" + new_oct
prog_data[ins][:data] = lines[x][1..2] + lines[x + 1][1..2]
ins = ins + 1
else
first_addr = false
end
end
end
end
# => Set up another data Structure for direct access
#-----------------------------------------------------------------------------------------------------
last_inst = 0
0.upto(prog_data.length-1) do |k|
if(prog_data[k][:data] == "7402")
last_inst = k
elsif(prog_data[k][:addr] == "0200")
first_inst = k
end
prog_addr[prog_data[k][:addr].to_i] = prog_data[k][:data]
puts "#{prog_data[k][:addr].to_i} - #{prog_addr[prog_data[k][:addr].to_i]}"
end
@lk = 0
@ac = 0 # => Accumulator
@pc = "200" # => Program Counter starts at 200
@eaddr = ""
@temp_data = 0
# => Get Effective address
#-----------------------------------------------------------------------------------------------------
def self.get_eddr(current_data, prog_addr)
if(current_data[1..1].to_i < 4)
if(current_data[1..1].to_i < 2)
@eaddr = ((current_data[1..1].to_i % 2).to_s + current_data[2..3]).to_i
else
@eaddr = ((@pc[0..1].to_i*100 + (current_data[1..1].to_i % 2)*100 + current_data[2..3].to_i).to_s).to_i
end
elsif( current_data[1..1].to_i > 3 && current_data[1..1].to_i < 6)
@eaddr = ((current_data[1..1].to_i % 2).to_s + current_data[2..3]).to_i
@trace << {:type => 0, :addr => @eaddr} # Read pointer
@eaddr = prog_addr[@eaddr]
else
@eaddr = ((@pc[0..1].to_i*100 + (current_data[1..1].to_i % 2)*100 + current_data[2..3].to_i).to_s).to_i
@trace << {:type => 0, :addr => @eaddr} # Read pointer
@eaddr = prog_addr[@eaddr]
end
if(current_data[1..1].to_i > 3)
if(current_data.to_i >= 10 && current_data.to_i <= 17)
@eaddr = incre_oct(prog_adr[@eaddr])
end
Stat.update({:cycle =>1 })
end
end
# => AND Instruction [case 0]
#-----------------------------------------------------------------------------------------------------
def self.AND_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
get_eddr(current_data, prog_addr)
@temp_data = prog_addr[@eaddr.to_i]
@ac = @temp_data.to_s.oct & @ac.to_s.oct
@ac = @ac.to_s(8).to_i
Stat.update({:AND =>1, :cycle =>2, :inst =>1 })
@trace << {:type => 0, :addr => @eaddr} # => Read Eaddr
end
# => TAD Instruction [case 1]
#-----------------------------------------------------------------------------------------------------
def self.TAD_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
get_eddr(current_data, prog_addr)
@temp_data = prog_addr[@eaddr.to_i]
@ac = @temp_data.to_s.oct + @ac.to_s.oct
@ac = @ac.to_s(8).to_i
if(@ac.to_s.oct > 4095)
@ac = (@ac.to_s.oct - 4096).to_s(8).to_i
@lk == 0 ? 1 : 0
end
Stat.update({:TAD =>1, :cycle =>2, :inst =>1})
@trace << {:type => 0, :addr => @eaddr} # => Read Eaddr
end
# => ISZ Instruction [case 2]
#-----------------------------------------------------------------------------------------------------
def self.ISZ_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
get_eddr(current_data, prog_addr)
@temp_data = prog_addr[@eaddr.to_i]
@temp_data = incre_data(@temp_data)
prog_addr[@eaddr.to_i] = @temp_data
if(@temp_data.to_i == 0)
@prog_branch[@pc]
@prog_branch[@pc][:type] = "conditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:taken] += 1
@prog_branch[@pc][:percent_taken] = (@prog_branch[@pc][:taken] / @prog_branch[@pc][:exec] )*100
@pc = incre_oct(@pc)
else
@prog_branch[@pc]
@prog_branch[@pc][:type] = "conditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:percent_taken] = (@prog_branch[@pc][:taken] / @prog_branch[@pc][:exec] )*100
end
Stat.update({:ISZ =>1, :cycle =>2, :inst =>1 })
@trace << {:type => 0, :addr => @eaddr} # => Read Eaddr
@trace << {:type => 1, :addr => @eaddr} # => C(EAddr) <- C(EAddr) + 1
end
# => DCA Instruction [case 3]
#-----------------------------------------------------------------------------------------------------
def self.DCA_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
get_eddr(current_data, prog_addr)
@temp_data = @ac.to_s
prog_addr[@eaddr.to_i] = @temp_data
@ac = 0
Stat.update({:DCA =>1, :cycle =>2, :inst =>1})
@trace << {:type => 1, :addr => @eaddr} # => C(AC) <- 0
end
# => JMS Instruction [case 4]
#-----------------------------------------------------------------------------------------------------
def self.JMS_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
@prog_branch[@pc]
@prog_branch[@pc][:type] = "unconditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:taken] += 1
@prog_branch[@pc][:percent_taken] = 100
get_eddr(current_data, prog_addr)
@temp_data = incre_oct(@pc.to_s)
prog_addr[@eaddr.to_i] = @temp_data
@pc = @eaddr.to_s
Stat.update({:JMS =>1, :cycle =>2, :inst =>1 })
@trace << {:type => 1, :addr => @eaddr} # => C(EAddr) <- C(PC)
@trace << {:type => 0, :addr => @eaddr} # => Read Eaddr
end
# => JMP Instruction [case 5]
#-----------------------------------------------------------------------------------------------------
def self.JMP_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
@prog_branch[@pc]
@prog_branch[@pc][:type] = "unconditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:taken] += 1
@prog_branch[@pc][:percent_taken] = 100
get_eddr(current_data, prog_addr)
@temp_data = @eaddr
@pc = (@eaddr.to_s.to_i(8) - 1).to_s(8)
Stat.update({:JMP => 1, :cycle => 1, :inst =>1})
@trace << {:type => 0, :addr => @pc} # => C(PC) <- Eaddr
end
# => MICRO Instruction [case 7]
#-----------------------------------------------------------------------------------------------------
def self.MICRO_inst(current_data, prog_addr)
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
code = prog_addr[@pc.to_i].to_i(8).to_s(2)
skip = 0;
if(code[3..3].to_i == 1 && code[11..11].to_i == 1)
Stat.update({:uINS3 => 1})
elsif(code[3..3].to_i == 0)
if(code[4..4].to_i == 1)
@ac = 0
Stat.update({:CLA1 => 1})
end
if(code[5..5].to_i == 1)
@lk = 0
Stat.update({:CLL => 1})
end
if(code[6..6].to_i == 1)
@ac = comp_oct(@ac.to_s)
Stat.update({:CMA => 1})
end
if(code[7..7].to_i == 1)
if(@lk == 0)
@lk = 1
else
@lk = 0
end
Stat.update({:CML => 1})
end
if(code[8..8].to_i == 1)
if(code[10..10].to_i == 1)
iter = 2
Stat.update({:RTR => 1})
else
iter = 1
Stat.update({:RAR => 1})
end
1.upto(iter) do
bin = @ac.to_s.to_i(8).to_s(2)
bin = @lk.to_s + "0" * (12-bin.length) + bin
@ac = (bin[0..11].to_i(2).to_s(8)).to_i
@lk = bin[12..12].to_i
end
end
if(code[9..9].to_i == 1)
if(code[10..10].to_i == 1)
Stat.update({:RTL => 1})
iter = 2
else
iter = 1
Stat.update({:RAL => 1})
end
1.upto(iter) do
bin = @ac.to_s.to_i(8).to_s(2)
bin = "0" * (12-bin.length) + bin + @lk.to_s
@ac = (bin[1..12].to_i(2).to_s(8)).to_i
@lk = bin[0..0].to_i
end
end
if(code[11..11].to_i == 1)
@ac = incre_data(@ac.to_s)
Stat.update({:IAC => 1})
end
if(code[4..11].to_i == 0)
Stat.update({:NOP => 1})
end
elsif(code[3..3].to_i == 1 && code[11..11].to_i == 0)
if(code[4..4].to_i == 1)
@ac = 0
Stat.update({:CLA2 => 1})
end
if(code[8..8].to_i == 1)
skip = 1
if(code[5..5].to_i == 1)
if((@ac.to_s.length == 4 && @ac.to_s[0..0].to_i < 4) || @ac.to_s.length < 4)
skip = skip & 1
else
skip = 0
end
Stat.update({:SPA => 1})
end
if(code[6..6].to_i == 1)
if(@ac != 0)
skip = skip & 1
else
skip = 0
end
Stat.update({:SNA => 1})
end
if(code[7..7].to_i == 1)
if(@lk == 0)
skip = skip & 1
else
skip = 0
end
Stat.update({:SZL => 1})
end
if((code[5..5].to_i | code[6..6].to_i | code[7..7].to_i) == 0)
skip = 1
Stat.update({SKP => 1})
end
else
skip = 0
if(code[5..5].to_i == 1)
if(@ac.to_s.length == 4 && @ac.to_s[0..0].to_i > 3)
skip = 1
end
Stat.update({:SMA => 1})
end
if(code[6..6].to_i == 1)
if(@ac == 0)
skip = 1
end
Stat.update({:SZA => 1})
end
if(code[7..7].to_i == 1)
if(@lk != 0)
skip = 1
end
Stat.update({:SNL => 1})
end
end
if(code[8..8].to_i == 1 || (code[5..7].to_i > 0 && code[8..8].to_i == 0))
if(skip == 1)
@prog_branch[@pc]
@prog_branch[@pc][:type] = "conditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:taken] += 1
@prog_branch[@pc][:percent_taken] = (@prog_branch[@pc][:taken] / @prog_branch[@pc][:exec] )*100
@pc = incre_oct(@pc.to_s)
else
@prog_branch[@pc]
@prog_branch[@pc][:type] = "conditional"
@prog_branch[@pc][:exec] += 1
@prog_branch[@pc][:percent_taken] = (@prog_branch[@pc][:taken] / @prog_branch[@pc][:exec] )*100
end
end
if(code[9..9].to_i == 1)
Stat.update({:OSR => 1})
end
if(code[10..10].to_i == 1)
Stat.update({:HLT => 1})
end
end
Stat.update({:uINS => 1, :inst => 1, :cycle => 1})
# DO TRACE
end
# => Run Loop
#-----------------------------------------------------------------------------------------------------
run_prog = true
while run_prog do
current_data = prog_addr[@pc.to_i]
current_instruction = current_data[0..0].to_i
if(prog_addr[@pc.to_i] == "0000")
current_instruction = 8
end
case current_instruction
when 0
AND_inst(current_data, prog_addr)
when 1
TAD_inst(current_data, prog_addr)
when 2
ISZ_inst(current_data, prog_addr)
when 3
DCA_inst(current_data, prog_addr)
when 4
JMS_inst(current_data, prog_addr)
when 5
JMP_inst(current_data, prog_addr)
when 6
@trace << {:type => 2, :addr => @pc} # => Instruction Fetch
Stat.update({:IO => 1, :inst =>1})
when 7
MICRO_inst(current_data, prog_addr)
when 8
@trace << {:type => 2, :addr => @pc}
Stat.update({:inst => 1})
puts "Error: invalid instruction (0000) encountered"
end
puts "#{@pc} #{prog_addr[@pc.to_i]} | LK = #{@lk} AC = #{@ac} Eaddr = #{@eaddr}"
if(prog_addr[@pc.to_i] == "7402")
run_prog = false
end
@pc = incre_oct(@pc)
end
# => Display Stats and Output stats to a file
#-----------------------------------------------------------------------------------------------------
0.upto(prog_data.length-1) do |k|
puts "#{prog_data[k][:addr].to_i} - #{prog_addr[prog_data[k][:addr].to_i]}"
end
@prog_branch.each_pair {|key, value| puts "#{key} #{value[:type]} exec = #{value[:exec].round} taken = #{value[:taken].round} % = #{value[:percent_taken]}" }
Stat.update({:lk => @lk, :ac => @ac, :pc => @pc.to_i})
Stat.display(test_file, @prog_branch)
puts "TRACE FILE ------"
f2 = File.open(test_file+".MEM.txt","w")
@trace.each_index do |x|
f2.puts "#{@trace[x][:type]} #{@trace[x][:addr].to_i}"
puts "#{@trace[x][:type]} #{@trace[x][:addr].to_i}"
end
f2.close
end
class Stat
@AND = 0
@TAD = 0
@ISZ = 0
@DCA = 0
@JMS = 0
@JMP = 0
@IO = 0
@uINS = 0
@cycles = 0
@insts = 0
@NOP = 0
@CLA1 = 0
@CLA2 = 0
@CLL = 0
@CMA = 0
@CML = 0
@IAC = 0
@RAR = 0
@RTR = 0
@RAL = 0
@RTL = 0
@SMA = 0
@SZA = 0
@SNL = 0
@SPA = 0
@SNA = 0
@SZL = 0
@SKP = 0
@OSR = 0
@HLT = 0
@uINS3 = 0
@lk = 0
@ac = 0
@pc = 0
def self.update(args)
@AND += args[:AND] || 0
@TAD += args[:TAD] || 0
@ISZ += args[:ISZ] || 0
@DCA += args[:DCA] || 0
@JMS += args[:JMS] || 0
@JMP += args[:JMP] || 0
@IO += args[:IO] || 0
@uINS += args[:uINS] || 0
@cycles += args[:cycle] || 0
@insts += args[:inst] || 0
@NOP += args[:NOP] || 0
@CLA1 += args[:CLA1] || 0
@CLA2 += args[:CLA2] || 0
@CLL += args[:CLL] || 0
@CMA += args[:CMA] || 0
@CML += args[:CML] || 0
@IAC += args[:IAC] || 0
@RAR += args[:RAR] || 0
@RTR += args[:RTR] || 0
@RAL += args[:RAL] || 0
@RTL += args[:RTL] || 0
@SMA += args[:SMA] || 0
@SZA += args[:SZA] || 0
@SNL += args[:SNL] || 0
@SPA += args[:SPA] || 0
@SNA += args[:SNA] || 0
@SZL += args[:SZL] || 0
@SKP += args[:SKP] || 0
@OSR += args[:OSR] || 0
@HLT += args[:HLT] || 0
@uINS3 += args[:uINS3] || 0
@lk += args[:lk] || 0
@ac += args[:ac] || 0
@pc += args[:pc] || 0
end
def self.display(file, branch)
f3 = File.open(file+".STATS.txt","w")
branch.each_pair do |key, value|
f3.puts "#{key} #{value[:type]} exec = #{value[:exec].round} taken = #{value[:taken].round} % = #{value[:percent_taken]}"
end
f3.puts "Stats ____________"
f3.puts " | AND = #{@AND}"
f3.puts " | @TAD = #{@TAD}"
f3.puts " | @ISZ = #{@ISZ}"
f3.puts " | @DCA = #{@DCA}"
f3.puts " | @JMS = #{@JMS}"
f3.puts " | @JMP = #{@JMP}"
f3.puts " | @IO = #{@IO}"
f3.puts " | @uINS = #{@uINS}"
f3.puts " | cycles = #{@cycles}"
f3.puts " | insts = #{@insts}"
f3.puts " | NOP = #{@NOP}"
f3.puts " | CLA1 = #{@CLA1}"
f3.puts " | CLA2 = #{@CLA2}"
f3.puts " | CLL = #{@CLL}"
f3.puts " | CMA = #{@CMA}"
f3.puts " | CML = #{@CML}"
f3.puts " | IAC = #{@IAC}"
f3.puts " | RAR = #{@RAR}"
f3.puts " | RTR = #{@RTR}"
f3.puts " | RAL = #{@RAL}"
f3.puts " | RTL = #{@RTL}"
f3.puts " | SMA = #{@SMA}"
f3.puts " | SZA = #{@SZA}"
f3.puts " | SNL = #{@SNL}"
f3.puts " | SPA = #{@SPA}"
f3.puts " | SNA = #{@SNA}"
f3.puts " | SZL = #{@SZL}"
f3.puts " | SKP = #{@SKP}"
f3.puts " | OSR = #{@OSR}"
f3.puts " | HLT = #{@HLT}"
f3.puts " | uINS3 = #{@uINS3}"
f3.puts "Registers____________"
f3.puts " | LK = #{@lk}"
f3.puts " | AC = #{@ac}"
f3.puts " | PC = #{@pc}"
f3.close
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment