#	$NetBSD: makemodes.awk,v 1.7 2008/04/28 20:23:14 martin Exp $

#
# Copyright (c) 1998 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Mark Brinicombe
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

# This parses a Acorn monitor definition file and constructs an array of
# parameters for each mode.
# Once the file has been parsed the list of modes is examined to find modes
# that match the mode specifications specified on the command line.
# The matching mode definitions are written to stdout in the form of a C file.
# Parsing information is written to stderr.
#
#
# Syntax for using this program
#
# awk -f makemodes.awk <MDF file> <mode spec> [<mode spec> ...]
#
# where <mode spec> is
#	<x>,<y>
#	<x>,<y>,<f>
#	<x>,<y>,<c>,<f>
#
# Note: Spaces are NOT allowed in a mode specifier
#
# where	x = x resolution
#	y = y resolution
#	f = frame rate
#	c = colour depth (16, 256, 32768, 65536)
#

BEGIN {
	# Number of modes parsed and valid in the modes array.
	mode = 0;

	# MDF file globals
	monitor = "";
	dpms = 0;

	# Non zero if we are translating a mode
	translate = 0;

	# ':'  character is used to separate the tokens.
	FS=":";

	# Note the real number of arguments and truncate ARGC so that only the first
	# argument is used as a filename.
	realargc = ARGC;
	ARGC=2;

	# Translation of sync_pol to videomode.flags
	pol[0] = "HP|VP";
	pol[1] = "HN|VP";
	pol[2] = "HP|VN";
	pol[3] = "HN|VN";
}

# MDF File format
/^file_format/ {
	# Currently we only understand format 1 MDF files
	if ($2 != 1) {
		printf("Unrecognised MDF format (%d)\n", $2);
		exit;
	}
}

# Monitor name
/^monitor_title/ {
	monitor = $2;
}

# Monitor DPMS state
/^DPMS_state/ {
	dpms = $2;
}

# Start of mode definition
/^startmode/ {
	translate = 1;
}

# End of mode definition
/^endmode/ {
	translate = 0;
	mode = mode + 1;
}

# The mode definition name (only valid within startmode/endmode section)
/^mode_name:/ {
	if (!translate)
		next;
	modes[mode, 0] = $2;
	next;
}

# The horizontal resolution (only valid within startmode/endmode section)
/^x_res:/ {
	if (!translate)
		next;
	modes[mode, 1] = $2;
	next;
}

# The vertical resolution (only valid within startmode/endmode section)
/^y_res:/ {
	if (!translate)
		next;
	modes[mode, 2] = $2;
	next;
}

# The pixel rate (only valid within startmode/endmode section)
/^pixel_rate:/ {
	if (!translate)
		next;
	modes[mode, 3] = $2;
	next;
}

# The horizontal timings (only valid within startmode/endmode section)
/^h_timings:/ {
	if (!translate)
		next;
	modes[mode, 4] = $2;
	next;
}

# The vertical timings (only valid within startmode/endmode section)
/^v_timings:/ {
	if (!translate)
		next;
	modes[mode, 5] = $2;
	next;
}

# The sync polarity (only valid within startmode/endmode section)
/^sync_pol:/ {
	if (!translate)
		next;
	modes[mode, 6] = $2;
	next;
}

END {
	#
	# Now generate the C file
	#

	# Create the file header
	printf("/*\n");
	printf(" * MACHINE GENERATED: DO NOT EDIT\n");
	printf(" *\n");
	printf(" * Created from %s\n", FILENAME);
	printf(" */\n\n");
	printf("#include <sys/types.h>\n");
	printf("#include <arm/iomd/vidc.h>\n\n");
	printf("const char * const monitor = \"%s\";\n", monitor);
	printf("const int dpms = %d;\n", dpms);
	printf("#define HP VID_PHSYNC\n");
	printf("#define HN VID_NHSYNC\n");
	printf("#define VP VID_PVSYNC\n");
	printf("#define VN VID_NVSYNC\n");
	printf("\n");

	# Now define the modes array
	printf("const struct videomode vidc_videomode_list[] = {\n");
	nmodes = 0

	# Loop round all the modespecs on the command line
	for (res = 2; res < realargc; res = res + 1) {
		pos = -1;
		found = -1;
		closest = 200;

		# Report the mode specifier being processed
		printf("%s ==> ", ARGV[res]) | "cat 1>&2";

		# Pull apart the modespec
		args = split(ARGV[res], modespec, ",");

		# We need at least 2 arguments
		if (args < 2) {
			printf("Invalid mode specifier\n") | "cat 1>&2";
			continue;
		}

		# If we only have x,y default c and f
		if (args == 2) {
			modespec[3] = 256;
			modespec[4] = -1;
		}
		# If we have x,y,f default c and re-arrange.
		if (args == 3) {
			modespec[4] = modespec[3];
			modespec[3] = 256;
		}

		# Report the full mode specifier
		printf("%d x %d x %d x %d : ", modespec[1], modespec[2],
		    modespec[3], modespec[4]) | "cat 1>&2";

		# Now loop round all the modes we parsed and find the matches
		for (loop = 0; loop < mode; loop = loop + 1) {
			# Match X & Y
			if (modespec[1] != modes[loop, 1]) continue;
			if (modespec[2] != modes[loop, 2]) continue;

			# Split the horizontal and vertical timings
			# This is needed for the frame rate calculation
			ht = split(modes[loop, 4], htimings, ",");
			if (ht != 6) continue;
			vt = split(modes[loop, 5], vtimings, ",");
			if (vt != 6) continue;

			# Calculate the frame rate
			fr = modes[loop, 3] / (htimings[1] + htimings[2] + \
			    htimings[3] + htimings[4] + htimings[5] + \
			    htimings[6]) / ( vtimings[1] + vtimings[2] + \
			    vtimings[3] + vtimings[4] + vtimings[5] + \
			    vtimings[6]);
			fr = fr * 1000;

			# Remember the frame rate
			modes[loop, 7] = int(fr + 0.5);

			# Create the internal version of the timings
			modes[loop, "timings"] = \
			    sprintf( \
			    "{ %d, %d,%d,%d,%d, %d,%d,%d,%d, %s, \"%s\" }",\
			    modes[loop, 3], htimings[4], \
			    htimings[4] + htimings[5] + htimings[6], \
			    htimings[4] + htimings[5] + htimings[6] + \
			    htimings[1], \
			    htimings[4] + htimings[5] + htimings[6] + \
			    htimings[1] + htimings[2] + htimings[3], \
			    vtimings[4], \
			    vtimings[4] + vtimings[5] + vtimings[6], \
			    vtimings[4] + vtimings[5] + vtimings[6] + \
			    vtimings[1], \
			    vtimings[4] + vtimings[5] + vtimings[6] + \
			    vtimings[1] + vtimings[2] + vtimings[3], \
			    pol[modes[loop, 6]], modes[loop, 0]);

			# Report the frame rate
			printf("%d ", modes[loop, 7]) | "cat 1>&2";

			# Is this the closest
			if (closest > mod(modes[loop, 7] - modespec[4])) {
				closest = mod(modes[loop, 7] - modespec[4]);
				pos = loop;
			}

			# Do we have an exact match ?
			if (modes[loop, 7] == modespec[4])
				found = pos;
		}

		# If no exact match use the nearest
		if (found == -1)
			found = pos;

		# Did we find any sort of match ?
		if (found == -1) {
			printf("Cannot find mode") | "cat 1>&2";
			continue;
		}

		# Report the frame rate matched
		printf("- %d", modes[found, 7]) | "cat 1>&2";

		# Output the mode as part of the mode definition array
		printf("\t%s,\n", modes[found, "timings"]);

		printf("\n") | "cat 1>&2";
		nmodes++;
	}

	# Close the array.
	printf("};\n\n");
	printf("const int vidc_videomode_count = %d;\n", nmodes);
}

#
# Simple mod() function
#
function mod(a) {
	if (a < 0)
		return -a;
	return a;
}
