#!/usr/bin/perl

# $Id: makerawblt.pl,v 1.4 1995/10/29 23:49:06 mat Exp $

# This is how many times to unwrap the blitting loop
$max_unwrap_factor = 8;

print "/* This file is machine-generated; DO NOT EDIT! */\n\n";

print ("\#define JUMP_TO_NEXT                        \\\n" .
       "do \{                                        \\\n" .
       "  const void *next = s\[1\].label;            \\\n" .
       "  NEXT_ROW;				    \\\n" .
       "  arg = s\[1\].arg;                           \\\n" .
       "  s = (const blt_section_t *) ((const uint8 *) s + sec_size); \\\n" .
       "  goto *next;                               \\\n" .
       "} while (0)\n" .
       "\n" .
       "/* Maximum number of times we'll unwrap any blitter loops. */\n" .
       "\#define MAX_LOOP_UNWRAP	$max_unwrap_factor" . "U\n" .
       "\#define REPEAT_MOD_0_STUB	0U\n" .
       "\#define MASK_STUB		(REPEAT_MOD_0_STUB + MAX_LOOP_UNWRAP)\n" .
       "\#define DONE_STUB		(MASK_STUB + 1)\n" .
       "\#define FUNC_PTR		(DONE_STUB + 1)\n");

$func_number = 0;
$func_empty = 1;

while (length ($line = &next_line)) {
    @cmd = split (/\s+/, $line);
    if ($cmd[0] eq 'begin_pat_func' || $cmd[0] eq 'begin_src_func') {
	&output_func;
	&reset_func;

	$patblt = ($cmd[0] eq 'begin_pat_func');
	$prefix = $patblt ? "xdblt" : "srcblt";

	$init = "";
	$inloop = "";
	while (length ($line = &next_line) && $line ne 'end_func') {
	    @cmd = split (/\s+/, $line);
	    ($rest) = ($line =~ /^\s*\w+\s+(.*)/);
	    $rest .= "\n";
	    if ($cmd[0] eq 'init') {
		$init .= $rest;
	    } elsif ($cmd[0] eq 'inloop') {
		$inloop .= $rest;
	    } else {
		die "Unknown func command $cmd[0] on line $lineno.\n";
	    }
	}
    } elsif ($cmd[0] eq 'begin_mode') {
	$mode_name = $cmd[1];

	$unwrap_factor = $cmd[2];
	if ($unwrap_factor eq 'max_unwrap') {
	    $unwrap_factor = $max_unwrap_factor;
	} elsif ($unwrap_factor < 1
		 || $unwrap_factor > $max_unwrap_factor
		 || $max_unwrap_factor % $unwrap_factor) {
	    die "Illegal unwrap factor on line $lineno.\n";
	}

	if ($unwrap_factor != $max_unwrap_factor) {
	    die ("unwrap factor must equal max_unwrap for now...the problem " .
		 "is with how much to offset dst by for the initial cruft..." .
		 "it would vary with the unwrap factor.\n");
	}

	$repeat = "";
	$mask = "";

	while (length ($line = &next_line) && $line ne 'end_mode') {
	    @cmd = split (/\s+/, $line);
	    ($rest) = ($line =~ /^\s*\w+\s+(.*)/);
	    $rest .= "\n";
	    if ($cmd[0] eq 'repeat') {
		$repeat .= $rest;
	    } elsif ($cmd[0] eq 'mask') {
		$mask .= $rest;
	    } else {
		die "Unknown mode command $cmd[0] on line $lineno.\n";
	    }
	}
	&process_mode ($mode_name, $repeat, $mask);
    }
}

# Handle any leftovers
&output_func;


sub output_func {
    return if ($func_empty);

    print ("\n" .
	   "\n" .
	   "extern const void \*$label_array_name\[$label_array_index\] " .
	   "asm (\"_$label_array_name\");\n" .
	   "\n" .
	   "const void * const *\n" .
	   "$prefix" . "_func_$func_number (const blt_section_t *section,\n");

    if ($patblt) {
	print "uint8 *row_base, long num_rows, long y)\n";
    } else {
	print ("const uint8 *src_row_base, uint8 *dst_row_base, " .
	       "long num_rows)\n");
    }
	   
    print ("\{\n" .
	   $label_arrays .
	   "\};\n" .
	   "\#if defined (mc68000)\n" .
	   "/* Convince gcc to use one register addl, not two addqw's */\n" .
	   "register uint32 sec_size = sizeof (section[0]);\n" .
	   "\#elif !defined (sec_size)\n" .
	   "#define sec_size (sizeof (section[0]))\n" .
	   "\#endif\n" .
	   $init .
	   "  do \{\n" .
	   $inloop .
	   "  const blt_section_t *s = section;\n" .
	   "  int32 arg = s->arg;\n");

	   if ($patblt) {
	       print "uint32 *dst = (uint32 *) \&row_base\[s->offset\];\n";
	   } else {
	       print ("const uint32 *src = (const uint32 *) " .
		      "\&src_row_base\[s->offset\];\n" .
		      "uint32 *dst = (uint32 *) " .
		      "\&dst_row_base\[s->offset\];\n");
	   }

    print ("  goto *(s->label);\n" .
	   "\n" .
	   "$code" .
	   " done:\n");

    if ($patblt) {
	print "  row_base += $prefix" . "_dst_row_bytes;\n";
    } else {
	print "  src_row_base += $prefix" . "_src_row_stride;\n";
	print "  dst_row_base += $prefix" . "_dst_row_stride;\n";
    }

    print ("  \} while (--num_rows > 0);\n" .
	   "  return labels;  /* Let gcc know labels are used. */\n" .
	   "\}\n");

    ++$func_number;
}

sub reset_func {
    $code = "";
    $label_array_name = "$prefix" . "_func_$func_number" . "_labels";
    $label_arrays = ("static const void *labels\[\] " .
		     "asm (\"_$label_array_name\") = \{\n");
    $label_array_index = 0;
    $func_empty = 1;

    undef %mask_labels_seen;
    undef %repeat_labels_seen;
}


sub process_mode {
    local ($tag, $repeat, $mask) = @_;
    local ($l);

    if (defined ($mask_labels_seen{$mask})) {
	$mask_label = $mask_labels_seen{$mask};
    } else {
	$mask_label = "$tag" . "_mask";
	$mask_labels_seen{$mask} = $mask_label;
	($new = $mask) =~ s/\@dst\@/\*dst/g;
	$new =~ s/\@src\@/\*src/g;
	$new =~ s/\@src_plus_1\@/src\[1\]/g;
	$code .= ("$mask_label:\n" .
		  "$new" .
		  "JUMP_TO_NEXT;\n");
    }

    if (defined ($repeat_labels_seen{$repeat})) {
	$repeat_label = $repeat_labels_seen{$repeat};
    } else {
	$repeat_label = $tag . "_many";
	$repeat_labels_seen{$repeat} = $repeat_label;

	$loop = (" $repeat_label" . "_loop:\n" .
		 "\#if !defined (i386)\n");
	if (!$patblt) {
	    $loop .= "src += $unwrap_factor;\n";
	}
	$loop .= ("  dst += $unwrap_factor;\n" .
		  "\#endif\n");

	for ($i = 0; $i < $unwrap_factor; $i++) {
	    $loop .= "$repeat_label" . "_" . ($max_unwrap_factor - $i) . ":\n";
	    ($new = $repeat) =~ s/\@dst\@/dst\[$i\]/g;
	    $new =~ s/\@src\@/src\[$i\]/g;
	    $p1 = $i + 1;
	    $new =~ s/\@src_plus_1\@/src\[$p1\]/g;

	    $loop .= "  $new";
	}

	$loop .= "\#if defined (i386)\n";
	if (!$patblt) {
	    $loop .= "src += $unwrap_factor; /* Avoids AGI stall here. */\n";
	}
	$loop .= ("  dst += $unwrap_factor; /* Avoids AGI stall here. */\n" .
		  "\#endif\n" .
		  "  if ((arg -= $unwrap_factor) > 0)\n" .
		  "    goto $repeat_label" . "_loop;\n" .
		  "  JUMP_TO_NEXT;\n");

	$code .= $loop;
    }

    $this_label_array = "";
    for ($i = 0; $i < $max_unwrap_factor; $i++) {
	$this_label_array .= ("\&\&$repeat_label" . "_" .
			      ((($i - 1) & ($unwrap_factor - 1)) + 1) . ",\n");
    }

    $this_label_array .= "   \&\&$mask_label,\n";
    $this_label_array .= "   \&\&done,\n";
    $this_label_array .= "   $prefix" . "_func_$func_number,\n";

    if (defined ($label_arrays_seen{$this_label_array})) {
	$index = $label_arrays_seen{$this_label_array};
    } else {
	$index = $label_array_index;
	$label_arrays_seen{$this_label_array} = $index;
	$label_array_index += $max_unwrap_factor + 3;
	$label_arrays .= $this_label_array;
    }
    
    $label_arrays .= ("\#define $prefix" . "_$tag" . "_labels " .
		      "(\&$label_array_name\[$index\])\n");
    $func_empty = 0;
}


# Returns the next non-empty line after stripping out comments, or the
# empty string if there are no more lines.
sub next_line {
    local ($line);
    while (<>) {
	++$lineno;
	($line) = /(^[^\#]*)/;	# We begin comments with '#' characters
	$line =~ s/\s+$//;		# Strip trailing whitespace
	$line =~ s/^\s+//;		# Strip leading whitespace
	return $line if (length ($line));
    }

    "";
}
