#!/bin/perl
# converter state (sequence) table to EPROM content
# (c) 2001 Andreas Romeyke
# Version 0.5
# - added as output a truth-table with Hex and bin output to verify the programm
# Version 0.4
# - fixing "don't care", handling was complete rewritten
# - added modus "create csv" to verify "elimination" of don't cares with
#   all variants of 0 or 1 instead.
# - speedup (using hashs instead array)
# - fixing Intel Hex, should work now
# - accepts commentlines in CSV-file in Shellstyle aka "# comment"
# Version 0.3
# - rearrangement of intelHex - Binary do and for(){}, to speed up
# - "why you should use intelhex" added
# - added BUGs-Section 
# Version 0.2
# - added Intel Hex Format, added POD-Documentation
# Version 0.1
# - first init
# distributed under the terms of the GNU General Public License
# please check http://www.gnu.org for license.
# this script is runnable under all variations (also windowversion) of perl 5.xx

=pod

=head1 NAME

=over 5

converter state (sequence) table to EPROM content

Version 0.5

=back

=head1 CALL

=over 5

epr_prg -h -> print this help

epr_prg -i -> exports into Intel Hex format

epr_prg -b -> exports into a binary format

epr_prg -c -> converts CSV with "don't cares" into CVS with all variations

epr_prg -t -> converts CSV to a truth-table to verify programming and to debug something

=head2 Examples:

cat test.csv|perl epr_prg -i > test.ihex

cat test.csv|perl epr_prg -b > test.bin

=back

=head1 DESCRIPTION

=over 5

This script converts a truth-table in CSV-Format into a binary-File 
or Intel-Hex based file to program EPROMs

The output will be sent to STDOUT, the input will be read from STDIN.

This script is runnable under all variations of perl 5.xx 
(also the windowsversion)

=back

=head2 Format of inputfiles

=over 5

CSV (comma separated value) based File:

0,1,x,x,x,x,0,x,x,x

0,1,1,1,1,1,1,1,1,0

1,1,1,1,1,1,0,1,0,1

2,1,1,1,0,1,1,1,1,1

First column means outputwire (0..7) as an Integer
second to n-th means adresswire as an Boolean (0,1,x)

If a table has not all combination-rows, we use missing combinations as
zero output.

All rows must be the same length.

You can use comments in your CSV-file, a comment has a "#" as first character
and goes to end of line.

The CSV is accepted, if is in ISO-Latin1-Format, without LF/CR aka DOS.
Windows-programms could also be used fine, but you should prefer Linux :)

In example above, I mean: 

output o0 is high, if A0 high and A5 is low

output o0 is high, if A0-A7 are high and A8 is low

output o1 is high, if A0-A4 are high, A5 low, A6 high, A7 low and A8 is high.

output o2 is high, if A0-A2 are high, A3 is low, and A4-A8 are high

or in short o0=(A0 & ~A5) | (A0 & A1 & A2 & A3 & A4 & A5 & A6 & A7 & ~A8) 

etc.


The adress-size is automagic calculated with counts of columns, it means 
if you have 13 columns (remember, first one is output, 13 columns means 
12 adresswire) then you have selected a 2732-EPROM or 4096x8 EPROM, else you
have 16 columns then you have selected a 27256-EPROM.
If you do not need higher adresswires, but you must use a 27256, then use 'x' or '0'
in unused columns.

=back

=head2 Format of outputfile

=over 5

epr_prg.pl can handle with two outputformats: binary and Intel-Hex. If you
have small EPROMs or you have a EPROMmer which does not support Intel-Hex,
you can use the binary format. If you can, use Intel-Hex, because it generates
mostly smaller outputfiles, but only if you have to program less than half of all
possible adresses.

epr_prg.pl can also generate a new CSV-file to eliminate "don't cares" with
all possible variations of "0" and "1".

=back

=head2 Format of outputfile: binary

=over 5

means the byte 1 in file is equal the byte in EPROM at address h0000,
the byte 2 in file is equal the byte in EPROM at address h0001,
and so on.

=back

=head2 Format of outputfile: Intel-Hex Format

=over 5

- every line beginning with ":" closed by CR and LF is a record

- there are two types of records: datarecord, endrecord

- all values are HEX-digits

           datarecord: ":llaaaattdddddd...dddddcc"

             - ll -> length of databytes (ddd...ddd), 2 Hex-digits, 00->20

             - aaaa -> startadress of EPROM where databytes of this record will be programmed, MSB first a

             - tt -> type of record: data is 00, end is 01, extended adress is 02

             - ddd...ddd -> ll-sized sequence of bytes programmed at adress aaaa

             - cc -> 0-SUMME{llaaaattddddd...ddd}mod256

           endrecord: ":00000001FF"

=back

=head1 TODO

=over 5

- Verify EPROM-programfile in Intel-HEX (not done yet, BIN works)

- verify ERRORs like different columns in CSV, contrary bitcombinations

- and so on.

=back

=head1 BUGs

=over 5

No known BUGs there.

If you find them, please do not hesitate to contact me.

=back

=head1 AUTHOR

(c) 2001 Andreas Romeyke (mailto: andreas.romeyke@web.de)

http://andreas-romeyke.de/privat

=head1 License

=over 5

Distributed under the terms of the GNU General Public License.
Please check http://www.gnu.org for license.

=back

=cut

($switch) = shift;
if (($switch eq "-h")||($switch eq "")) {
    system ("perldoc ./$0");
    exit(0);
}
if ($switch eq "-b") {
    $output="binary";
}
if ($switch eq "-i") {
    $output="intelhex";
}
if ($switch eq "-c") {
    $output="csv";
}
if ($switch eq "-t") {
    $output="table";
}
#print "Phase1\n";
%out=(); # clear hash
while (<>) {
    s/\n//;
    s/^#.*$//;
    $table{$_}=1;
}
$tabline=join ('', keys (%table));
#print "Phase2\n";

while ($tabline=~m/x/) {
#    $max=%table;
    %newtable=();
#    print "TABLINE $max\n";
    foreach $line (keys(%table)) {
	if ($line=~m/x/) {
	    $a=$line;
	    $a=~s/x/0/;
	    $b=$line;
	    $b=~s/x/1/;
	    $newtable{$a}=1;
	    $newtable{$b}=1;
#	    print "l: $line\n";
#	    print "a: $a\n";
#	    print "b: $b\n";
#	    print "----------------\n";
	}
	else {
	    $newtable{$line}=1;
	}
    }
    %table=%newtable;
    $tabline=join ('', %table);
}
#print "Phase 3\n";
if ($output eq "csv") {
    foreach $_ (sort keys(%table)) {
	print $_."\n";
    }
}
else {
    foreach $_ (keys(%table)) { # while input
	($outwire, @tmp)=split(/,/); # get first row as outputwire, next
# as adresswire
	$adress=0; #set startadress to zero
	for ($i=@tmp+1; $i>=0; $i--) { #with every adresswire	
		$adress=($adress + $tmp[$i]* 2 ** $i); #calculate adress
	} 
	$out{$adress}=($out{$adress} | (2 ** $outwire)); #combine byte at adress
    }
}
if ($output eq "binary") {	
    for ($i=0; $i<2 ** @tmp; $i++) { #for every adress, which is possible
        #print value to program into EPROM
	if (defined $out{$i}) {
            print pack ('C', $out{$i}); #print calculated value
        }
        else {
            print pack ('C', 0); #print default ZERO
        }
    }
}
if ($output eq "intelhex") {
    for ($i=0; $i<2 ** @tmp; $i++) { #for every adress, which is possible
    #print value to program into EPROM
	if (defined $out{$i}) {
	    print ":"; # record-begin-tag
	    printf ("%02X",1); # recordlen
	    printf ("%04X",$i); # adress
	    printf ("%02X",0);# type
	    printf ("%X",$out{$i}); # data
	    printf ("%02X",(0-(1+$i+0+$out{$i})%256)&0x0000ff); #checksum
	    print pack('C',0xD); # cr
	    print pack('C',0xA); # lf
	}
    }
    print ":00000001FF"; # complete end-record
    print pack('C',0xD);
    print pack('C',0xA);
}
if ($output eq "table") {
    for ($i=0; $i<2 ** @tmp; $i++) { #for every adress
	if ($i % 32 == 0) {
	    print "----------------------------------------------------\n";
	    print "Adr.     V /// A24-A16   A15-A8   A7-A0     Q7-Q0\n";
	    print "----------------------------------------------------\n";
	}
	#print a truth table
	printf ("%04X -> %X /// ",$i, $out{$i});
	print unpack ('B8',pack ('C', $i>>16));
	print " ";
	print unpack ('B8',pack ('C', $i>>8));
	print " ";
	print unpack ('B8',pack ('C', $i   )); #adress as binaer
	print " | ";
	print unpack ('B8',pack ('C',$out{$i})); #data as binaer
	print "\n";
    }
}