X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=support%2Fscripts%2Finc2h.pl;h=5a242e3f19d36a816a341e159c0b66efad18ca47;hb=13906aff7874da28c033f501fb593bffc2e25dd3;hp=d586ed94637042576c24884ef5d6479e8a03c64d;hpb=085eaef3aed957962961d27e50d47e3aba86b801;p=fw%2Fsdcc diff --git a/support/scripts/inc2h.pl b/support/scripts/inc2h.pl index d586ed94..5a242e3f 100755 --- a/support/scripts/inc2h.pl +++ b/support/scripts/inc2h.pl @@ -1,4 +1,6 @@ -#!/usr/bin/perl +#!/usr/bin/perl -w + +use strict; # Copyright (c) 2002 Kevin L. Pauba @@ -10,165 +12,568 @@ # but explicitly does not cover any code or objects generated by sdcc. # We have not yet decided on a license for the run time libraries, but # it will not put any requirements on code linked against it. See: -# +# # http://www.gnu.org/copyleft/gpl.html # # See http://sdcc.sourceforge.net/ for the latest information on sdcc. +##################################################################### +# >>> How to add a new device to SDCC PIC14 port? +# +# This description assumes that you have a copy of SDCC's sources +# in /path/to/sdcc and a (source or installed) version of gputils +# in /path/to/gputils (the only important point here is that +# /path/to/gputils/headers must exist and contain "p.inc"). +# Furthermore, I assume you want to add support for the 16f887 device +# (note: no 'pic' or 'p' prefix!), change this as desired. +# inc2h.pl can only deal with one single device at a time; repeat the +# ../inc2h.pl-step as often as required. +# +# The proposed sequence of commands is then: +# +# $ cd /path/to/sdcc/support/scripts +# $ mkdir build && cd build +# $ ../inc2h.pl 16f887 /path/to/gputils +# $ mv pic16f887.c /path/to/sdcc/device/lib/pic/libdev +# $ mv pic16f887.h /path/to/sdcc/device/include/pic +# $ vim /path/to/sdcc/device/include/pic/pic14devices.inc +# +# # Use any pure text editor you like (`[Esc]:q![Enter]' quits Vim ;-)). +# # Add a record for the new device to the file; usually you can copy +# # the record of a similar device and adjust the values using the +# # datasheet. The format of pic14devices.txt is explained in the file +# # itself. Please keep the file sorted. +# # When you are done: +# +# $ cd /path/to/sdcc/device/lib +# $ make model-pic14 +# $ make install +# +# Congratulations, you have just added support for a new device to +# the port. You may consider posting your (changes to) +# pic14devices.txt and the generated files (picDEVICE.[ch]) to +# have them included into the official source tree. +# Some testing beforehand would be appreciated, though. +##################################################################### + +my $rcsid = q~$Id$~; +my ($junk, $file, $version, $date, $time, $programmer, $status) + = split(/\s+/, $rcsid); +my ($programName) = ($file =~ /(\S+)/); -$rcsid = q~$Id$~; -($junk, $file, $version, $date, $time, $programmer, $status) - = split(/\s+/, $rcsid); -($programName) = ($file =~ /(\S+),v/); +if ($#ARGV < 0 || $#ARGV > 1 ) { + Usage(); +} +my $processor = uc(shift); +my $path = shift; +my %sfrs = (); +my %alias = (); +my %bits = (); +my %bitmasks = (); +my %addr = (); +my %ram = (); +my $path_delim = "/"; +my $devices = ""; +my %types = (); +my $type = ""; + +# just in time fixes for some register names +sub fixname { + my $name = shift; + $name =~ s/COMCON/CMCON/ig; + # use OPTION_REG instead of OPTION as OPTION is a assembler directive + $name =~ s/OPTION(_REG)?/OPTION_REG/ig; + # often declared as LCDDATn, but bits defined for LCDDATAn, 0 <= n <= 10 + $name =~ s/LCDDAT([^A])/LCDDATA$1/ig; + # LCDSE2 is missing in some headers, but LCDSE3 is declared... + $name =~ s/LCDSE3/LCDSE2/ig; + # XXX: should this be named LININTF or LINPRT? + $name =~ s/LININTF/LINPRT/ig; + # FIXME: duplicate declarations for n in {0,1,2} + $name =~ s/UEPn/UEP0/ig; -if ($#ARGV != 0) { - Usage(); + return $name; +} + +sub checkname { + my $name = shift; + if (not exists $sfrs{$name}) { + print "SFR $name not defined (yet).\n"; + # Find similar ones. + if (exists $sfrs{$name."0"}) { + print " but ".$name."0 exists---using that instead.\n"; + return $name."0"; + } + my $try = $name; + $try =~ s/[0-9]$//; + if (exists $sfrs{$try}) { + print " but $try exists---using that instead.\n"; + return $try; + } + die "Not found a similar SFR---aborting.\n"; + } + return $name; +} + +# exists clone for arrays---does this not exist in Perl?!? +sub contained { + my $name = shift; + my $arr = shift; + + foreach my $item (@$arr) { + return 1 if ($name eq $item); + } + return 0; +} + + +$path = "" if (!defined $path); +if ($^O eq 'MSWin32') { + if ($path eq '') { + if (defined($path = $ENV{'GPUTILS_HEADER_PATH'}) || defined($path = $ENV{'GPUTILS_LKR_PATH'})) { + $path .= '\\..'; + } + else { + die "Could not find gpasm includes.\n"; + } + } + $path_delim = '\\'; +} +else { + # Nathan Hurst : find gputils on Debian + if ($path eq '') { + if ( -x "/usr/share/gputils") { + $path = "/usr/share/gputils"; + } elsif ( -x "/usr/share/gpasm") { + $path = "/usr/share/gpasm"; + } elsif ( -x "/usr/local/share/gputils") { + $path = "/usr/local/share/gputils"; + } else { + die "Could not find gpasm includes.\n"; + } + } + $path_delim = '/'; } # # Read the symbols at the end of this file. # while () { - next if /^\s*#/; + next if /^\s*#/; - if (/^alias\s+(\S+)\s+(\S+)/) { - # - # Set an alias for a special function register. - # Some MPASM include files are not entirely consistent - # with sfr names. - # - $alias{$2} = $1; - } elsif (/^address\s+(\S+)\s+(\S+)/) { - # - # Set a default address for a special function register. - # Some MPASM include files don't specify the address - # of all registers. - # - $addr{"$1"} = $2; - } elsif (/^(\S+)/) { - $type = $1; - } else { - foreach $key (split) { - eval "\$type{'$key'} = $type;"; - } + if (/^\s*alias\s+(\S+)\s+(\S+)/) { + # + # Set an alias for a special function register. + # Some MPASM include files are not entirely consistent + # with sfr names. + # + $alias{fixname($2)} = fixname($1); + } elsif (/^\s*address\s+(\S+)\s+(\S+)/) { + # + # Set a default address for a special function register. + # Some MPASM include files don't specify the address + # of all registers. + # + # $addr{"$1"} = $2; + foreach my $device (split(/[,\s]+/, $devices)) { + $addr{"p$device", "$1"} = $2; } + } elsif (/^\s*bitmask\s+(\S+)\s+/) { + # + # Set the bitmask that will be used in the 'memmap' pragma. + # + my $bitmask = "$1"; + foreach my $register (split(/\s+/, $')) { + $bitmasks{"$register"} = $bitmask; + } + } elsif (/^\s*ram\s+(\S+)\s+(\S+)\s+(\S+)/) { + # This info is now provided in "include/pic/pic14devices.txt". + #$lo = $1; + #$hi = $2; + #my $bitmask = $3; + #foreach $device (split(/[,\s]+/, $devices)) { + # $ram{"p$device"} .= "#pragma memmap $lo $hi RAM $bitmask$'"; + #} + } elsif (/^\s*processor\s+/) { + $devices = $'; + $type = ''; + } elsif (/^\s*(\S+)/) { + $type = $1; + $_ = $'; + foreach my $key (split) { + eval "\$types{'$key'} = $type;"; + } + } else { + foreach my $key (split) { + eval "\$types{'$key'} = $type;"; + } + } } # -# Print the header. +# Read the linker file. # -print <) { +# if (/^(\S+)\s+NAME=(\S+)\s+START=(\S+)\s+END=(\S+)\s+(PROTECTED)?/) { +# $type = $1; +# $name = $2; +# $start = $3; +# $end = $4; +# $protected = 1 if ($5 =~ /protected/i); -#ifndef BIT_AT -#define BIT_AT(base,bitno) bit at ((base<<3)+bitno) -#endif +# if ($type =~ /(SHAREBANK)|(DATABANK)/i) { +# $ram{"p$processor"} .= +# sprintf("#pragma memmap %7s %7s RAM 0x000\t// $name\n", +# $start, $end); +# } +# } elsif (/^SECTION\s+NAME=(\S+)\s+ROM=(\S+)\s+/) { +# } +# } +# Create header for pic${processor}.c file +my $lcproc = "pic" . lc($processor); +my $c_head = < EOT # # Convert the file. # -$defaultType = 'other'; -while (<>) { - if (/^;-* (\S+) Bits/i) { - if (defined($alias{$1})) { - $defaultType = "bits $alias{$1}"; - } else { - $defaultType = "bits $1"; +my $defaultType = 'other'; +my $includeFile = $path.$path_delim.'header'.$path_delim.'p'.lc($processor).'.inc'; +my $headFile = "pic" . lc($processor) . ".h"; +my $defsFile = "pic" . lc($processor) . ".c"; + +my $body = ""; +my $header = ""; +my $addresses = ""; +my $pragmas = ""; + +open(HEADER, "<$includeFile") + || die "$programName: Error: Cannot open include file $includeFile ($!)\n"; + +while (
) { + + if (/^;-+ Register Files/i) { + $defaultType = 'sfr'; + s/;/\/\//; + $body .= "$_"; + } elsif (/^;-+\s*(\S+)\s+Bits/i || /^;-+\s*(\S+)\s+-+/i) { + # The second case is usually bits, but the word Bits is missing + # also accept "UIE/UIR Bits" + foreach my $name (split(/\//, $1)) { + $name = fixname($name); + $name = checkname($name); + + if (defined($alias{$name})) { + $defaultType = "bits $alias{$name}"; + } else { + $defaultType = "bits $name"; + } + } + s/;/\/\//; + $body .= "$_"; + } elsif (/^;=+/i) { + $defaultType = ''; + s/;/\/\//; + $body .= "$_"; + } elsif (/^\s*;/) { + # + # Convert ASM comments to C style. + # + $body .= "//$'"; + } elsif (/^\s*IFNDEF\s+__(\S+)/) { + # + # Processor type. + # + $processor = $1; + $body .= "//$_"; + } elsif (/^\s*(\S+)\s+EQU\s+H'(.+)'/) { + # + # Useful bit of information. + # + my $name = $1; + my $value = $2; + my $rest = $'; + my $bitmask = "0x0000"; + + $rest =~ s/;/\/\//; + chomp($rest); + + if (defined($types{"p$processor", "$name"})) { + $type = $types{"p$processor", "$name"}; + } elsif (defined($types{"$name"})) { + $type = $types{"$name"}; + } else { + $type = $defaultType; + } + #print "$name --> $type\n"; ## DEBUG + + if (defined($bitmasks{"p$processor", "$name"})) { + $bitmask = $bitmasks{"p$processor", "$name"}; +# } elsif (defined($bitmasks{"$name"})) { +# $bitmask = $bitmasks{"$name"}; + } + + if ($type eq 'sfr') { + # + # A special function register. + # +# $pragmas .= sprintf("#pragma memmap %s_ADDR %s_ADDR " +# . "SFR %s\t// %s\n", +# $name, $name, $bitmask, $name); + $name = fixname($name); + if (defined $addr{"p$processor", "$name"}) { + $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $addr{"p$processor", "$name"}); + } else { + $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $value); + } + $body .= sprintf("extern __sfr __at %-30s $name;$rest\n", "(${name}_ADDR)" ); + $c_head .= sprintf("__sfr __at %-30s $name;\n", "(${name}_ADDR)"); + $addr{"p$processor", "$name"} = "0x$value"; + $sfrs{$name}=1; + } elsif ($type eq 'volatile') { + # + # A location that can change without + # direct program manipulation. + # + $name = fixname($name); +# $pragmas .= sprintf("#pragma memmap %s_ADDR %s_ADDR " +# . "SFR %s\t// %s\n", +# $name, $name, $bitmask, $name); + $body .= sprintf("extern __data __at %-30s $name;$rest\n", "(${name}_ADDR) volatile char"); + $c_head .= sprintf("__data __at %-30s $name;\n", "(${name}_ADDR) volatile char"); + if (defined $addr{"p$processor", "$name"}) { + $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $addr{"p$processor", "$name"}); + } else { + $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $value); + } + } elsif ($type =~ /^bits/) { + my ($junk, $register) = split(/\s/, $type); + my $bit = hex($value); + my $addr = $addr{"$register"}; + + # prepare struct declaration + if (0) { # DEBUG + foreach my $key (keys %bits) { + print " $key\n"; } - s/;/\/\//; - print "$_"; - } elsif (/^;-* Register Files/i) { - $defaultType = 'sfr'; - s/;/\/\//; - print "$_"; - } elsif (/^;=*/i) { - $defaultType = ''; - s/;/\/\//; - print "$_"; - } elsif (/^\s*;/) { - # - # Convert ASM comments to C style. - # - print "//$'"; - } elsif (/^\s*IFNDEF __(\S+)/) { + print "$register // $bit // ".$bits{"$register"}."\n"; + } + if (!defined $bits{"$register"}) { + $bits{"$register"} = {}; # reference to empty hash + } + if (!defined $bits{"$register"}->{oct($bit)}) { + $bits{"$register"}->{oct($bit)} = []; # reference to empty array + } + for (my $k=0; $k < scalar @{$bits{"$register"}->{oct($bit)}}; $k++) { + $name = "" if ($bits{"$register"}->{oct($bit)} eq $name) + } + if (($name ne "") + and (1 != contained($name, \@{$bits{"$register"}->{oct($bit)}})) + ) { + push @{$bits{"$register"}->{oct($bit)}}, $name; + } + } else { + # + # Other registers, bits and/or configurations. + # + $name = fixname($name); + if ($type eq 'other') { # - # Processor type. + # A known symbol. # - $processor = $1; - print "//$_"; - } elsif (/^\s*(\S+)\s+EQU\s+H'(.+)'/) { + $body .= sprintf("#define %-20s 0x%s$rest\n", $name, $value); + } else { # - # Useful bit of information. + # A symbol that isn't defined in the data + # section at the end of the file. Let's + # add a comment so that we can add it later. # - $name = $1; - $value = $2; - $rest = $'; - $rest =~ s/;/\/\//; - chomp($rest); - - if (defined($type{"$name"})) { - $type = $type{"$name"}; - } else { - $type = $defaultType; - } + $body .= sprintf("#define %-20s 0x%s$rest\n", + $name, $value); + } + } + } elsif (/^\s*$/) { + # + # Blank line. + # + $body .= "\n"; + } elsif (/__MAXRAM\s+H'([0-9a-fA-F]+)'/) { + my $maxram .= "//\n// Memory organization.\n//\n"; + if (!defined $ram{"p$processor"}) { + $ram{"p$processor"} = ""; + } + $pragmas = $maxram + . $ram{"p$processor"} . "\n" + . $pragmas; + $body .= "// $_"; + } else { + # + # Anything else we'll just comment out. + # + $body .= "// $_"; + } +} +$header .= <) { + s/\r//g; chomp; + if(/^\s*(\*|\w*)\s*([ABCDE0-7,-]+)\s*$/) { + if(lc($1) eq lc($processor)) { + die if defined $pinfo; + $pinfo = $2; + } elsif($1 eq "*") { + die if defined $defpinfo; + $defpinfo = $2; + } + } elsif(/^\s*#/ || /^\s*$/) { + # ignore blanks, comments } else { - # - # Anything else we'll just comment out. - # - print "// $_"; + die "bad line in pic14ports '$_'"; } + } + close P14PORTS; + }; + $defpinfo = "A0-5,B0-7,C0-7,D0-7,E0-2" unless defined $defpinfo; + $pinfo = $defpinfo unless defined $pinfo; + + if(defined $pinfo) { + foreach (split /,/, $pinfo) { + if(/^([ABCDE])([0-7])-([0-7])$/) { + my($prt, $low, $high) = ($1, $2, $3); + next unless defined $sfrs{"PORT$prt"} && defined $sfrs{"TRIS$prt"}; + next if defined $bits{"PORT$prt"}; + for(my $i = $low; $i <= $high; $i++) { + push @{$bits{"PORT$prt"}->{oct($i)}}, "R$prt".$i; + } + next if defined $bits{"TRIS$prt"}; + for(my $i = $low; $i <= $high; $i++) { + push @{$bits{"TRIS$prt"}->{oct($i)}}, "TRIS$prt".$i; + } + } else { die } + } + } } +my $structs = ""; +## create struct declarations +foreach my $reg (sort keys %bits) +{ + $structs .= "// ----- $reg bits --------------------\n"; + $structs .= "typedef union {\n"; + my $idx = 0; + my $max = 1; + do { + $structs .= " struct {\n"; + for (my $i=0; $i < 8; $i++) + { + if (!defined $bits{$reg}) { + #print "bits{$reg} undefined\n"; + } + if (!defined $bits{$reg}->{oct($i)}) { + #print "bits{$reg}->{".oct($i)."} undefined\n"; + $bits{$reg}->{oct($i)} = []; # empty array reference + } + my @names = @{$bits{$reg}->{oct($i)}}; + if ($max < scalar @names) { $max = scalar @names; } + if ($idx >= scalar @names) { + $structs .= " unsigned char :1;\n"; + } else { # (1 == scalar @names) { + $structs .= " unsigned char " . $names[$idx] . ":1;\n"; +# } else { +# $structs .= " union {\n"; +# foreach $name (@names) { +# $structs .= " unsigned char " . $name . ":1;\n"; +# } # foreach +# $structs .= " };\n"; + } + } # for + $structs .= " };\n"; + $idx++; + } while ($idx < $max); + $structs .= "} __${reg}_bits_t;\n"; + #if(defined $sfrs{$reg}) { + $structs .= "extern volatile __${reg}_bits_t __at(${reg}_ADDR) ${reg}_bits;\n\n"; + $c_head .= "volatile __${reg}_bits_t __at(${reg}_ADDR) ${reg}_bits;\n"; + #} + + # emit defines for individual bits + for (my $i=0; $i < 8; $i++) + { + my @names = @{$bits{$reg}->{oct($i)}}; + foreach my $field (@names) { + $structs .= sprintf("#define %-20s ${reg}_bits.$field\n", $field); + } # foreach + } + $structs .= "\n"; +} # foreach + +open(HEAD, ">$headFile") or die "Could not open $headFile for writing."; +print HEAD $header + . $addresses . "\n" + . $pragmas . "\n\n" + . $body . "\n" + . $structs + . "#endif\n"; +close(HEAD); + +open(DEFS, ">$defsFile") or die "Could not open $defsFile for writing."; +print DEFS $c_head . "\n"; +close DEFS; + sub Usage { print STDERR < +# address +# bitmask +# ram +# sfr +# volatile +# bit +# +alias OPTION_REG OPTION +volatile INDF PCL