#! /usr/bin/perl
#
#  TOPPERS Software
#      Toyohashi Open Platform for Embedded Real-Time Systems
# 
#  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
#                              Toyohashi Univ. of Technology, JAPAN
#  Copyright (C) 2004-2010 by Embedded and Real-Time Systems Laboratory
#              Graduate School of Information Science, Nagoya Univ., JAPAN
# 
#  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
#  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
#  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
#  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
#      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
#      スコード中に含まれていること．
#  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
#      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
#      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
#      の無保証規定を掲載すること．
#  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
#      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
#      と．
#    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
#        作権表示，この利用条件および下記の無保証規定を掲載すること．
#    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
#        報告すること．
#  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
#      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
#      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
#      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
#      免責すること．
# 
#  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
#  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
#  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
#  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
#  の責任を負わない．
# 
#  @(#) $Id: genoffset 1840 2010-07-10 17:29:42Z ertl-hiro $
# 

#
#  初期化
#
$infile = $ARGV[0];
$sil_endian = "";

#
#  解釈できるディレクティブのテーブル
#
%directives = (
	"long", "",
	"dword", "",
	"word", "",
	"hword", "",
	"int", "",
	"short", "",
	"half", "",
	"byte", "",
	"value", "",
	"uaword", "",
	"uashort", "",
	"data32", 4,
	"data16", 2,
	"data8", 1,
	"zero", -1,
	"space", -1,
	"globl", 0,
	"global", 0,
	"align", 0,
	"p2align", 0,
	"even", 0,
	"data", 0,
	"stabs", 0,
	"type", 0,
	"size", 0,
	"section", 0,
	"sdata", 0,
	"code", 0,
);

#
#  オフセット値の出力
#
sub parse_offset {
	my($label, $val) = @_;

	printf "#define %s\t%d\n",$label,$val;
}

#
#  ディレクティブの読み取り
#
sub ref_bit {
	my($size) = @_;
	my($dir, $directive);

	while ($line = <INFILE>) {
		chomp $line;
		next if ($line =~ /^[ \t]*[#;].*$/);

		if ($line =~ /[ \t]*\.([a-zA-Z0-9]+)[ \t]*([^ \t]*)[ \t]*/
						&& defined($dir = $directives{$1})) {
			$directive = $1;
			if ($dir eq "") {
				# 登録すべきディレクティブ
				$directives{$directive} = $size;
			}
			last;
		}
		else {
			# 解析できない行
			print STDERR "genoffset: cannot parse: $line\n";
			$error = 1;
		}
	}
}

#
#  ビットサーチ
#
sub search_bit {
	my($val) = @_;
	my($val_bit);

	return(-1) if ($val == 0);

	$val_bit = 0;
	while (($val & 1) == 0) {
		$val >>= 1;
		$val_bit++;
	}
	return($val_bit);
}

#
#  ビット位置の出力
#
sub parse_bit {
	my($endian, $size, $label) = @_;
	my($offset, $dir, $val, $val_bit);

	if ($sil_endian && $endian ne $sil_endian) {
		# エンディアンの不一致
		print STDERR "genoffset: endian mismatch: $line\n";
		$error = 1;
	}

	$offset = 0;
	while ($line = <INFILE>) {
		chomp $line;
		next if ($line =~ /^[ \t]*[#;].*$/);

		if ($line =~ /[ \t]*\.([a-zA-Z0-9]+)[ \t]*([^ \t]*)[ \t]*/
						&& defined($dir = $directives{$1})) {
			$val = $2;

			# 16進数と8進数の数値への変換処理
			if ($val =~ /^0x(.+)$/) {
				$val = hex($1);
			}
			elsif ($val =~ /^0(.+)$/) {
				$val = oct($1);
			}

			if ($dir eq "") {
				# サイズを知らないディレクティブ
				print STDERR "genoffset: unknown directive: $line\n";
				$error = 1;
			}
			elsif ($dir == 0) {
				# 読み飛ばすべきディレクティブ
			}
			elsif ($dir == -1) {
				# .zeroディレクティブの処理
				$offset += $val;
			}
			elsif ($val == 0) {
				# 値が0のフィールドの処理
				$offset += $dir;
			}
			else {
				# 値が0でないフィールドが見つかればループを終了
				last;
			}
		}
		else {
			# 解析できない行
			print STDERR "genoffset: cannot parse: $line\n";
			$error = 1;
		}
	}

	# ビット位置を探す
	$val_bit = do search_bit($val);

	# バイト単位に換算する
	if ($endian eq "B") {
		$offset += $dir - 1;
		$offset -= $val_bit >> 3;
	}
	else {
		$offset += $val_bit >> 3;
	}
	$val_bit &= 0x07;

	# 出力単位に換算する
	if ($size eq "W") {
		if ($endian eq "B") {
			$val_bit += 24;
			$val_bit -= ($offset & 0x03) << 3;
		}
		else {
			$val_bit += ($offset & 0x03) << 3;
		}
		$offset &= ~0x03;
	}
	elsif ($size eq "H") {
		if ($endian eq "B") {
			$val_bit += 8;
			$val_bit -= ($offset & 0x01) << 3;
		}
		else {
			$val_bit += ($offset & 0x01) << 3;
		}
		$offset &= ~0x01;
	}

	# 定義の出力
	$label =~ s/^_//;
	printf "#define %s\t%d\n",$label,$offset;
	printf "#define %s_bit\t%d\n",$label,$val_bit;
	printf "#define %s_mask\t0x%x\n",$label,(1 << $val_bit);
}

#
#  メインルーチン
#
print "/* This file is generated by genoffset. */\n";
print "\n";

$error = 0;
open(INFILE, $infile) || die "Cannot open $infile";
while ($line = <INFILE>) {
	chomp $line;

	if ($line =~ /^[ \t]*OFFSET_DEF ([^ \t]+) = [#\$]?([^ \t]+)/) {
		do parse_offset($1, $2);
	}
	elsif ($line =~ /^[ \t]*_?BIT_REF_([0-9]+):/) {
		do ref_bit($1);
	}
	elsif ($line =~ /^[ \t]*SIL_ENDIAN = ([BL])/) {
		$sil_endian = $1;
	}
}
#
#  コンパイラがグローバル変数の配置順序を変更すると，BIT_REF_?が下にな
#  る可能性があるため，頭から読み直す．
#
seek(INFILE, 0, SEEK_SET);
while ($line = <INFILE>) {
	chomp $line;

	if ($line =~ /^[ \t]*_?BIT_([BL])([BHW])_([^ \t]+):/) {
		do parse_bit($1, $2, $3);
	}
}
close(INFILE);
exit($error);
