* src/pic16/devices.inc,
[fw/sdcc] / support / scripts / inc2h.pl
1 #!/usr/bin/perl
2
3 # Copyright (c) 2002 Kevin L. Pauba
4
5
6 # License:
7 #
8 # SDCC is licensed under the GNU Public license (GPL) v2.  Note that
9 # this license covers the code to the compiler and other executables,
10 # but explicitly does not cover any code or objects generated by sdcc.
11 # We have not yet decided on a license for the run time libraries, but
12 # it will not put any requirements on code linked against it. See:
13
14 # http://www.gnu.org/copyleft/gpl.html
15 #
16 # See http://sdcc.sourceforge.net/ for the latest information on sdcc.
17
18
19 $rcsid = q~$Id$~;
20 ($junk, $file, $version, $date, $time, $programmer, $status)
21     = split(/\s+/, $rcsid);
22 ($programName) = ($file =~ /(\S+)/);
23
24 if ($#ARGV < 0 || $#ARGV > 1 ) {
25     Usage();
26 }
27 $processor = uc(shift);
28 $path = shift;
29
30
31 # just in time fixes for some register names
32 sub fixname {
33     my $name = shift;
34     $name =~ s/COMCON/CMCON/ig;
35     # use OPTION_REG instead of OPTION as OPTION is a assembler directive
36     $name =~ s/OPTION(_REG)?/OPTION_REG/ig;
37     # often declared as LCDDATn, but bits defined for LCDDATAn, 0 <= n <= 10
38     $name =~ s/LCDDAT([^A])/LCDDATA$1/ig;
39     # LCDSE2 is missing in some headers, but LCDSE3 is declared...
40     $name =~ s/LCDSE3/LCDSE2/ig;
41     # XXX: should this be named LININTF or LINPRT?
42     $name =~ s/LININTF/LINPRT/ig;
43     # FIXME: duplicate declarations for n in {0,1,2}
44     $name =~ s/UEPn/UEP0/ig;
45
46     return $name;
47 }
48
49 sub checkname {
50     my $name = shift;
51     if (not exists $sfrs{$name}) {
52         print "SFR $name not defined (yet).\n";
53         # Find similar ones.
54         if (exists $sfrs{$name."0"}) {
55             print "  but ".$name."0 exists---using that instead.\n";
56             return $name."0";
57         }
58         $try = $name;
59         $try =~ s/[0-9]$//;
60         if (exists $sfrs{$try}) {
61             print "  but $try exists---using that instead.\n";
62             return $try;
63         }
64         die "Not found a similar SFR---aborting.\n";
65     }
66     return $name;
67 }
68
69 # exists clone for arrays---does this not exist in Perl?!?
70 sub contained {
71     my $name = shift;
72     my $arr = shift;
73
74     foreach my $item (@$arr) {
75         return 1 if ($name eq $item); 
76     }
77     return 0;
78 }
79
80
81 if ($^O eq 'MSWin32') {
82     if ($path eq '') {
83         if (defined($path = $ENV{'GPUTILS_HEADER_PATH'}) || defined($path = $ENV{'GPUTILS_LKR_PATH'})) {
84             $path .= '\\..';
85         }
86         else {
87             die "Could not find gpasm includes.\n";
88         }
89     }
90     $path_delim = '\\';
91 }
92 else {
93     # Nathan Hurst <njh@mail.csse.monash.edu.au>: find gputils on Debian
94     if ($path eq '') {
95         if ( -x "/usr/share/gputils") {
96             $path = "/usr/share/gputils";
97         } elsif ( -x "/usr/share/gpasm") {
98             $path = "/usr/share/gpasm";
99         } elsif ( -x "/usr/local/share/gputils") {
100             $path = "/usr/local/share/gputils";
101         } else {
102             die "Could not find gpasm includes.\n";
103         }
104     }
105     $path_delim = '/';
106 }
107
108 #
109 # Read the symbols at the end of this file.
110 #
111 while (<DATA>) {
112     next if /^\s*#/;
113
114     if (/^\s*alias\s+(\S+)\s+(\S+)/) {
115         #
116         # Set an alias for a special function register.
117         # Some MPASM include files are not entirely consistent
118         # with sfr names.
119         #
120         $alias{fixname($2)} = fixname($1);
121     } elsif (/^\s*address\s+(\S+)\s+(\S+)/) {
122         #
123         # Set a default address for a special function register.
124         # Some MPASM include files don't specify the address
125         # of all registers.
126         # 
127         # $addr{"$1"} = $2;
128         foreach $device (split(/[,\s]+/, $devices)) {
129             $addr{"p$device", "$1"} = $2;
130         }
131     } elsif (/^\s*bitmask\s+(\S+)\s+/) {
132         #
133         # Set the bitmask that will be used in the 'memmap' pragma.
134         #
135         $bitmask = "$1";
136         foreach $register (split(/\s+/, $')) {
137             $bitmask{"$register"} = $bitmask;
138         }
139     } elsif (/^\s*ram\s+(\S+)\s+(\S+)\s+(\S+)/) {
140         # This info is now provided in "include/pic/pic14devices.txt".
141         #$lo = $1;
142         #$hi = $2;
143         #$bitmask = $3;
144         #foreach $device (split(/[,\s]+/, $devices)) {
145         #    $ram{"p$device"} .= "#pragma memmap $lo $hi RAM $bitmask$'";
146         #}
147     } elsif (/^\s*processor\s+/) {
148         $devices = $';
149         $type = '';
150     } elsif (/^\s*(\S+)/) {
151         $type = $1;
152         $_ = $';
153         foreach $key (split) {
154             eval "\$type{'$key'} = $type;";
155         }
156     } else {
157         foreach $key (split) {
158             eval "\$type{'$key'} = $type;";
159         }
160     }
161 }
162
163 #
164 # Read the linker file.
165 #
166 #  $linkFile = "$path/lkr/" . lc $processor . ".lkr";
167 #  open(LINK, "<$linkFile")
168 #      || die "$programName: Error: Cannot open linker file $linkFile ($!)\n";
169 #  while (<LINK>) {
170 #      if (/^(\S+)\s+NAME=(\S+)\s+START=(\S+)\s+END=(\S+)\s+(PROTECTED)?/) {
171 #       $type = $1;
172 #       $name = $2;
173 #       $start = $3;
174 #       $end = $4;
175 #       $protected = 1 if ($5 =~ /protected/i);
176
177 #       if ($type =~ /(SHAREBANK)|(DATABANK)/i) {
178 #           $ram{"p$processor"} .=
179 #               sprintf("#pragma memmap %7s %7s RAM 0x000\t// $name\n",
180 #                       $start, $end);
181 #       }
182 #      } elsif (/^SECTION\s+NAME=(\S+)\s+ROM=(\S+)\s+/) {
183 #      }
184 #  }
185
186 # Create header for pic${processor}.c file
187 $lcproc = "pic" . lc($processor);
188 $c_head = <<EOT;
189 /* Register definitions for $lcproc.
190  * This file was automatically generated by:
191  *   $programName V$version
192  *   Copyright (c) 2002, Kevin L. Pauba, All Rights Reserved
193  */
194 #include <${lcproc}.h>
195
196 EOT
197
198 #
199 # Convert the file.
200 #
201 %sfrs = ();
202 $defaultType = 'other';
203 $includeFile = $path.$path_delim.'header'.$path_delim.'p'.lc($processor).'.inc';
204 $headFile = "pic" . lc($processor) . ".h";
205 $defsFile = "pic" . lc($processor) . ".c";
206 open(HEADER, "<$includeFile")
207     || die "$programName: Error: Cannot open include file $includeFile ($!)\n";
208
209 while (<HEADER>) {
210     
211     if (/^;-+ Register Files/i) {
212         $defaultType = 'sfr';
213         s/;/\/\//;
214         $body .= "$_";
215     } elsif (/^;-+\s*(\S+)\s+Bits/i || /^;-+\s*(\S+)\s+-+/i) {
216         # The second case is usually bits, but the word Bits is missing
217         # also accept "UIE/UIR Bits"
218         foreach $name (split(/\//, $1)) {
219             $name = fixname($name);
220             $name = checkname($name);
221
222             if (defined($alias{$name})) {
223                 $defaultType = "bits $alias{$name}";
224             } else {
225                 $defaultType = "bits $name";
226             }
227         }
228         s/;/\/\//;
229         $body .= "$_";
230     } elsif (/^;=+/i) {
231         $defaultType = '';
232         s/;/\/\//;
233         $body .= "$_";
234     } elsif (/^\s*;/) {
235         #
236         # Convert ASM comments to C style.
237         #
238         $body .= "//$'";
239     } elsif (/^\s*IFNDEF\s+__(\S+)/) {
240         #
241         # Processor type.
242         #
243         $processor = $1;
244         $body .= "//$_";
245     } elsif (/^\s*(\S+)\s+EQU\s+H'(.+)'/) {
246         #
247         # Useful bit of information.
248         #
249         $name = $1;
250         $value = $2;
251         $rest = $';
252         $rest =~ s/;/\/\//;
253         chomp($rest);
254
255         if (defined($type{"p$processor", "$name"})) {
256             $type = $type{"p$processor", "$name"};
257         } elsif (defined($type{"$name"})) {
258             $type = $type{"$name"};
259         } else {
260             $type = $defaultType;
261         }
262
263         if (defined($bitmask{"p$processor", "$name"})) {
264             $bitmask = $bitmask{"p$processor", "$name"};
265 #       } elsif (defined($bitmask{"$name"})) {
266 #           $bitmask = $bitmask{"$name"};
267         } else {
268             $bitmask = "0x000";
269         }
270
271         if ($type eq 'sfr') {
272             #
273             # A special function register.
274             #
275 #           $pragmas .= sprintf("#pragma memmap %s_ADDR %s_ADDR "
276 #                               . "SFR %s\t// %s\n",
277 #                               $name, $name, $bitmask, $name);
278             $name = fixname($name);
279             if (defined $addr{"p$processor", "$name"}) {
280                 $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $addr{"p$processor", "$name"});
281             } else {
282                 $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $value);
283             }
284             $body .= sprintf("extern __sfr  __at %-30s $name;$rest\n", "(${name}_ADDR)" );
285             $c_head .= sprintf("__sfr  __at %-30s $name;\n", "(${name}_ADDR)");
286             $addr{"p$processor", "$name"} = "0x$value";
287             $sfrs{$name}=1;
288         } elsif ($type eq 'volatile') {
289             #
290             # A location that can change without 
291             # direct program manipulation.
292             #
293             $name = fixname($name);
294 #           $pragmas .= sprintf("#pragma memmap %s_ADDR %s_ADDR "
295 #                               . "SFR %s\t// %s\n",
296 #                               $name, $name, $bitmask, $name);
297             $body .= sprintf("extern __data __at %-30s $name;$rest\n", "(${name}_ADDR) volatile char");
298             $c_head .= sprintf("__data __at %-30s $name;\n", "(${name}_ADDR) volatile char");
299             if (defined $addr{"p$processor", "$name"}) {
300                 $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $addr{"p$processor", "$name"});
301             } else {
302                 $addresses .= sprintf("#define %s_ADDR\t0x%s\n", $name, $value);
303             }
304         } elsif ($type =~ /^bits/) {
305             ($junk, $register) = split(/\s/, $type);
306             $bit = hex($value);
307             $addr = $addr{"$register"};
308             # prepare struct declaration
309             for ($k=0; $k < scalar @{$bits{"$register"}->{oct($bit)}}; $k++) {
310               $name = "" if ($bits{"$register"}->{oct($bit)} eq $name)
311             }
312             if (($name ne "")
313                 and (1 != contained($name, \@{$bits{"$register"}->{oct($bit)}}))
314             ) {
315               push @{$bits{"$register"}->{oct($bit)}}, $name;
316             }
317         } else {
318             #
319             # Other registers, bits and/or configurations.
320             #
321             $name = fixname($name);
322             if ($type eq 'other') {
323                 #
324                 # A known symbol.
325                 #
326                 $body .= sprintf("#define %-20s 0x%s$rest\n", $name, $value);
327             } else {
328                 #
329                 # A symbol that isn't defined in the data
330                 # section at the end of the file.  Let's 
331                 # add a comment so that we can add it later.
332                 #
333                 $body .= sprintf("#define %-20s 0x%s$rest\n",
334                                  $name, $value);
335             }
336         }
337     } elsif (/^\s*$/) {
338         #
339         # Blank line.
340         #
341         $body .= "\n";
342     } elsif (/__MAXRAM\s+H'([0-9a-fA-F]+)'/) {
343         $maxram .= "//\n// Memory organization.\n//\n";
344         $pragmas = $maxram
345             . $ram{"p$processor"} . "\n"
346                 . $pragmas;
347         $body .= "// $_";
348     } else {
349         #
350         # Anything else we'll just comment out.
351         #
352         $body .= "// $_";
353     }
354 }
355 $header .= <<EOT;
356 //
357 // Register Declarations for Microchip $processor Processor
358 //
359 //
360 // This header file was automatically generated by:
361 //
362 //\t$programName V$version
363 //
364 //\tCopyright (c) 2002, Kevin L. Pauba, All Rights Reserved
365 //
366 //\tSDCC is licensed under the GNU Public license (GPL) v2.  Note that
367 //\tthis license covers the code to the compiler and other executables,
368 //\tbut explicitly does not cover any code or objects generated by sdcc.
369 //\tWe have not yet decided on a license for the run time libraries, but
370 //\tit will not put any requirements on code linked against it. See:
371 // 
372 //\thttp://www.gnu.org/copyleft/gpl/html
373 //
374 //\tSee http://sdcc.sourceforge.net/ for the latest information on sdcc.
375 //
376 // 
377 #ifndef P${processor}_H
378 #define P${processor}_H
379
380 //
381 // Register addresses.
382 //
383 EOT
384
385 $c_head .= <<EOT;
386
387 // 
388 // bitfield definitions
389 // 
390 EOT
391
392 # Add PORT* and TRIS* bit entries
393 # file format is:
394 #    16f84   A0-4,B0-7
395 #    *       A0-5,B0-7,C0-7,D0-7,E0-2
396 {
397   my $pinfo = undef;
398   my $defpinfo = undef;
399   open(P14PORTS, "< pic14ports.txt") && do {
400     while(<P14PORTS>) {
401         s/\r//g; chomp;
402         if(/^\s*(\*|\w*)\s*([ABCDE0-7,-]+)\s*$/) {
403             if(lc($1) eq lc($processor)) {
404                 die if defined $pinfo;
405                 $pinfo = $2;
406             } elsif($1 eq "*") {
407                 die if defined $defpinfo;
408                 $defpinfo = $2;
409             }
410         } elsif(/^\s*#/ || /^\s*$/) {
411             # ignore blanks, comments
412         } else {
413             die "bad line in pic14ports '$_'";
414         }
415     }
416     close P14PORTS;
417   };
418   $defpinfo = "A0-5,B0-7,C0-7,D0-7,E0-2" unless defined $defpinfo;
419   $pinfo = $defpinfo unless defined $pinfo;
420
421   if(defined $pinfo) {
422     foreach  (split /,/, $pinfo) {
423         if(/^([ABCDE])([0-7])-([0-7])$/) {
424             my($prt, $low, $high) = ($1, $2, $3);
425             next unless defined $sfrs{"PORT$prt"} && defined $sfrs{"TRIS$prt"};
426             next if     defined $bits{"PORT$prt"};
427             for(my $i = $low; $i <= $high; $i++) {
428                 push @{$bits{"PORT$prt"}->{oct($i)}}, "R$prt".$i;
429             }
430             next if     defined $bits{"TRIS$prt"};
431             for(my $i = $low; $i <= $high; $i++) {
432                 push @{$bits{"TRIS$prt"}->{oct($i)}}, "TRIS$prt".$i;
433             }
434         } else { die }
435     }
436   }
437 }
438
439 $structs = "";
440 ## create struct declarations
441 foreach $reg (sort keys %bits)
442 {
443   $structs .= "// ----- $reg bits --------------------\n";
444   $structs .= "typedef union {\n";
445   $idx = 0; $max = 1;
446   do {
447     $structs .= "  struct {\n";
448     for ($i=0; $i < 8; $i++)
449     {
450       @names = @{$bits{$reg}->{oct($i)}};
451       if ($max < scalar @names) { $max = scalar @names; }
452       if ($idx >= scalar @names) {
453         $structs .= "    unsigned char :1;\n";
454       } else { # (1 == scalar @names) {
455         $structs .= "    unsigned char " . $names[$idx] . ":1;\n";
456 #      } else {
457 #       $structs .= "  union {\n";
458 #       foreach $name (@names) {
459 #         $structs .= "    unsigned char " . $name . ":1;\n";
460 #       } # foreach
461 #       $structs .= "  };\n";
462       }
463     } # for
464     $structs .= "  };\n";
465     $idx++;
466   } while ($idx < $max);
467   $structs .= "} __${reg}_bits_t;\n";
468   #if(defined $sfrs{$reg}) {
469     $structs .= "extern volatile __${reg}_bits_t __at(${reg}_ADDR) ${reg}_bits;\n\n";
470     $c_head .= "volatile __${reg}_bits_t __at(${reg}_ADDR) ${reg}_bits;\n";
471   #}
472   
473   # emit defines for individual bits
474   for ($i=0; $i < 8; $i++)
475   {
476     @names = @{$bits{$reg}->{oct($i)}};
477     foreach $field (@names) {
478       $structs .= sprintf("#define %-20s ${reg}_bits.$field\n", $field);
479     } # foreach
480   }
481   $structs .= "\n";
482 } # foreach
483
484 open(HEAD, ">$headFile") or die "Could not open $headFile for writing.";
485 print HEAD $header
486     . $addresses . "\n"
487     . $pragmas . "\n\n"
488     . $body . "\n"
489     . $structs
490     . "#endif\n";
491 close(HEAD);
492
493 open(DEFS, ">$defsFile") or die "Could not open $defsFile for writing.";
494 print DEFS $c_head . "\n";
495 close DEFS;
496
497 sub Usage {
498         print STDERR <<EOT;
499
500 inc2h.pl - A utility to convert MPASM include files to header files
501            suitable for the SDCC compiler.
502
503 License: Copyright (c) 2002 Kevin L. Pauba
504
505          SDCC is licensed under the GNU Public license (GPL) v2; see
506          http://www.gnu.org/copyleft/gpl.html See http://sdcc.sourceforge.net/
507          for the latest information on sdcc.
508
509 Usage:   $programName processor [path]
510
511          where:
512
513          processor      The name of the processor (16f84, 16f877, etc.)
514
515          path           The path to the parent of the "header" and "lkr"
516                         directories.  The default is "/usr/share/gpasm".
517
518          The header file will be written to the standard output.
519
520          $#ARGV
521 EOT
522         exit;
523 }
524
525 __END__
526
527 #
528 # processor <processor_name>
529 # address <register_name> <hex_address>
530 # bitmask <bitmask> <register_list>
531 # ram <lo_address> <hi_address> <bitmask>
532 # sfr <register_list>
533 # volatile <address_list>
534 # bit <address_list>
535 #
536
537 alias OPTION_REG OPTION
538 volatile INDF PCL