読者です 読者をやめる 読者になる 読者になる

mcommit's message

大阪でソフトウェア開発の仕事をしている simotinこと宮崎といいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

モトローラSレコードフォーマットをRubyで吐く。

アセンブラの勉強もかねてGR-SAKURAで遊んでいたのですが、
今までスルーしてきたmotファイルの構造が気になりだしました。

勉強のために、こちらのページを参考にさせて頂きました。

勉強がてらということで、Rubyでmotファイルを出力するコードを書いてみました。
出力するといってもバイナリファイルからではなく、数値型のアドレスとデータ(配列)
を元に出力するという仕様です。

■mot.rb

class Fixnum
  def to_h bits_size=8
  	if bits_size == 32
  		sprintf("%08X", self)
		elsif bits_size == 16
  		sprintf("%04X", self)
  	else
  		sprintf("%02X", self)
  	end
  end
end
class Bignum
  def to_h bits_size=nil
  	if bits_size == 32
  		sprintf("%08X", self)
		elsif bits_size == 16
  		sprintf("%04X", self)
  	else
  		sprintf("%02X", self)
  	end
  end
end


class Mot
	def S0 comment
		length = 3 + comment.length
		comment_hex = ""
		comment.each_byte do |c|
			comment_hex << c.ord.to_h
		end
		record = "#{length.to_h}0000#{comment_hex}"
		check_sum = length
		comment.each_byte do |c|
			check_sum += c.ord & 0xFF
		end
		check_sum = ~check_sum & 0xFF
		"S0#{record}#{check_sum.to_h}"
	end

	def S1 addr, code_ary
		length = 1 + 2 + (code_ary.length)
		check_sum = length
		check_sum += calc_sum(addr, 16)
		code_ary.each do|code|
			check_sum += calc_sum(code, 8)
		end
		check_sum = ~check_sum  & 0xFF
		code_str =""
		code_ary.each {|code| code_str += code.to_h}
		"S1#{length.to_h}#{addr.to_h(16)}#{code_str}#{check_sum.to_h}"
	end

	def S2 addr, code_ary
		length = 1 + 3 + (code_ary.length)
		check_sum = length
		check_sum += calc_sum(addr, 24)
		code_ary.each do|code|
			check_sum += calc_sum(code, 8)
		end
		check_sum = ~check_sum  & 0xFF
		code_str =""
		code_ary.each {|code| code_str += code.to_h}
		"S2#{length.to_h}#{addr.to_h(24)}#{code_str}#{check_sum.to_h}"
	end

	def S3 addr, code_ary
		length = 1 + 4 + (code_ary.length)
		check_sum = length
		check_sum += calc_sum(addr, 32)
		code_ary.each do|code|
			check_sum += calc_sum(code, 8)
		end
		check_sum = ~check_sum  & 0xFF
		code_str =""
		code_ary.each {|code| code_str += code.to_h}
		"S3#{length.to_h}#{addr.to_h(32)}#{code_str}#{check_sum.to_h}"
	end

	def S7 start_addr=0x00000000
		hex = start_addr.to_h(32)
		length = 5
		check_sum = length
		check_sum += calc_sum(start_addr, 32)
		check_sum = ~check_sum  & 0xFF
		"S7#{length.to_h}#{start_addr.to_h(32)}#{check_sum.to_h}"
	end

	def S8 start_addr=0x000000
		hex = start_addr.to_h(24)
		length = 4
		check_sum = length
		check_sum += calc_sum(start_addr, 24)
		check_sum = ~check_sum  & 0xFF
		"S7#{length.to_h}#{start_addr.to_h(24)}#{check_sum.to_h}"
	end

	def S9 start_addr=0x0000
		hex = start_addr.to_h(16)
		length = 3
		check_sum = length
		check_sum += calc_sum(start_addr, 16)
		check_sum = ~check_sum  & 0xFF
		"S7#{length.to_h}#{start_addr.to_h(16)}#{check_sum.to_h}"
	end

	private
	def calc_sum data, bits_size=8
			check_sum = 0
			check_sum += (data & 0x000000FF) & 0xFF
			check_sum += (data & 0x0000FF00) >> 8 & 0xFF if 8 < bits_size
			check_sum += (data & 0x00FF0000) >> 16 & 0xFF if 16 < bits_size
			check_sum += (data & 0xFF000000) >> 24 & 0xFF if 24 < bits_size
			return check_sum
	end
end

# ==== TEST CODE ====
mot = Mot.new
puts mot.S0 "Gr_sakurmot"
puts "S00E000047725F73616B75726D6F7463"
puts mot.S1 0x0000, [0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00]
puts "S113000000000100000001000000010000000100E8"
puts mot.S7 0xFFFFC960
puts "S705FFFFC960D3"
puts mot.S3 0xFFF00000, [0x05, 0x79, 0x0D, 0x0F, 0x02]
puts "S30AFFF0000005790D0F026A"

puts mot.S9 0x0100
puts "S9030100FB"

■使い方

Sレコードフォーマットを標準出力します。
S0 :コメントを指定するとS0レコードを出力します。
S1~S3:アドレスは書き込みデータは1byteずつ配列で指定します。
あとは、
ソースの# ==== TEST CODE ====
以下をご参考に。。。
※まぁ需要はあまりないと思いますが。

■感想

モトローラSレコードフォーマットについては、
基本的には書き込み先の「アドレス」「データ」をASCIIのHEXで表現したものという理解でよさげ。
コードをみての通り、S0,S3,S7は真面目にテストしてみました。(RXは32bitなので)

・モンキーパッチのto_hはひそかに気に入っています。
※あとto_bで2進数出力もほしい。まぁRubyの数値クラスはサイズが不定なのがあれだけど。

■補足

motファイル出力クラスを書いてみたのは、実はCコンパイラRubyで自作してみようかなと思ったからです。