chiark / gitweb /
permit empty comments
[trains.git] / cebpic / morse-generator
1 #!/usr/bin/perl
2 # Converts morse.messages to *+morse.asm or morse+auto.inc.
3 # morse-auto.asm specifies some flash contents, as follows:
4 #
5 # Each message XY produces a symbol
6 #   morse_XY
7 # which is in the flash between morse_messages_start and
8 # morse_messages_end, and which is guaranteed to be aligned to a
9 # multiple of 4 bytes.
10 #
11 # At that symbol will be a message in the following format:
12 #       RAAAMMMM        header byte, contents as follows:
13 #                        M (4 bits) number of bytes of morse pattern
14 #                        R reserved bit, always zero
15 #                        A (3 bits) number of bytes of addresses
16 #       M bytes         morse pattern, as bit string of LED on/off
17 #                        values (1=on) for equal-length periods
18 #                        (1 period = 1 morse dot), MS bit first
19 #                        trailing zeroes in last byte are probably
20 #                        best elided
21 #       A bytes         zero or more addresses which should be read
22 #                        and the corresponding value shown in binary.
23 #                        Addresses are only one byte each, interpreted
24 #                        like an access bank reference.
25 #
26 # morse_messages_start and morse_messages_end should be defined in
27 # morse-defs.inc, and should also be 4-byte aligned.  morse-defs.inc
28 # should also arrange to define any symbolic addresse referred to
29 # in morse-auto.messages, eg by including the pXXXX.inc.
30 #
31 # *+morse.asm contains the actual source and morse+auto.inc
32 # contains `equ' definitions for the addresses morse_XY.
33
34 use IO::File;
35 use IO::Handle;
36
37 sub badusage () {
38     die ("usages:\n".
39          " morse-generator <input>... asm\n".
40          " morse-generator <input>... [<file>.map] inc\n");
41 }
42
43 @ARGV or badusage();
44
45 $includefile= 'morse-defs.inc';
46 if ($ARGV[$#ARGV] =~ s/^\-I//) {
47     $includefile= pop @ARGV;
48 }
49
50 $which= pop @ARGV;
51 $which eq 'asm' or $which eq 'inc' or badusage();
52
53 while (<DATA>) {
54     ($let,$morse) = (m/^([0-9A-Z])\s+([-.]+)$/) or die;
55     $morse =~ s/\./10/g;
56     $morse =~ s/\-/1110/g;
57     $morse =~ s/0$//;
58     $morse{$let}= $morse;
59 }
60
61 sub oops ($) { die "morse-generator: $ARGV:$.: $_[0]\n"; }
62
63 $mapfile= $ARGV[$#ARGV];
64
65 if ($mapfile =~ m/\.map$/) {
66     pop @ARGV;
67     $maph= new IO::File "$mapfile", 'r' or die "$0: $mapfile: $!\n";
68     while (<$maph>) {
69         next unless m/^\s+Symbols\s+$/ .. !/\S/;
70         next unless m/\S/;
71         next if m/^\s+Symbols\s+$/;
72         next if m/^\s+Name\s+Address\s+Location\s+Storage\s+File\s*$/;
73         next if m/^(?:\s+\-\-+){5}\s*$/;
74         m/^\s*([0-9a-zA-Z_]+)\s+(0+|0x[0-9a-f]+)\s+(program|data)\s+(static|extern)\s+(\S+)\s*$/
75             or die "$0: $mapfile:$.: cannot parse ?!\n";
76         ($sym,$val,$progdata,$staticext,$filename) = ($1,$2,$3,$4,$5);
77         next if $progdata ne 'data';
78         $filename =~ s/\.asm$//;
79         $sym= $staticext eq 'static' ? "$filename:$sym" : "::$sym";
80         $symval{$sym}= $val;
81     }
82     die "$0: $mapfile: $!\n" if $maph->error;
83 }
84
85 $"=',';
86
87 print <<'END' or die $!
88 ; autogenerated - do not edit
89 END
90     ;
91
92 print <<END or die $!
93
94         include $includefile
95         radix dec
96
97 xxx_unknown_xxx equ     0
98 morse_section   org     morse_messages_start
99
100 END
101     if $which eq 'asm';
102
103 $bytes= 0;
104 $.= 0;
105
106 while (<>) {
107     chomp;
108     s/\#.*//;
109     next unless m/\S/;
110     s/\s+$//;
111     m/^([A-Z0-9]+)(?:\s+([^\t ;]+))?(?:\s+\;\s*(.*))?$/
112         or oops("syntax error");
113     $morse_name= $1;
114     @morse= split //, $morse_name;
115     @addrs= defined($2) ? split /\,/, $2 : ();
116     $comment= $3;
117
118     @addrs < 8 or oops("only up to 7 addrs are supported");
119
120     @data= ();
121
122     $morse= join '000', map { $morse{$_} } @morse;
123     $morse .= '0' x 7; # padding to fill any partial byte
124     while ($morse =~ s/^([01]{8})//) {
125         push @data, "$&b";
126     }
127     $morse =~ m/^0{0,7}$/ or die;
128     $morse_bytes= scalar(@data);
129
130     unshift @data, sprintf "0x%x%x", scalar(@addrs), $morse_bytes;
131     push @data, map {
132         s/^\:/ $filename.':' /e;
133         $filename= $1 if m/^([^:]+)\:/;
134         $_ = $symval{$_} if exists $symval{$_};
135         m/\:/ ? ' xxx_unknown_xxx' : "$_ - (0xf00 * !(($_ & 0xf00)^0xf00))"
136     } @addrs;
137
138     push @data, ('0') x (3 - (scalar(@data) + 3) % 4);
139
140     print("morse_$morse_name db @data\n") or die $!
141         if $which eq 'asm';
142
143     printf("morse_$morse_name equ morse_messages_start+0x%x\n",
144            $bytes) or die $!
145         if $which eq 'inc';
146
147     $bytes += scalar @data;
148 }
149
150 print <<'END' or die $!
151
152         if $ > morse_messages_end
153          error "too much morse - extends beyond morse_messages_end"
154         endif
155
156         end
157 END
158     if $which eq 'asm';
159
160 STDIN->error and die $!;
161 STDOUT->error and die $!;
162
163 __DATA__
164 A       .-
165 B       -...
166 C       -.-.
167 D       -..
168 E       .
169 F       ..-.
170 G       --.
171 H       ....
172 I       ..
173 J       .---
174 K       -.-
175 L       .-..
176 M       --
177 N       -.
178 O       ---
179 P       .--.
180 Q       --.-
181 R       .-.
182 S       ...
183 T       -
184 U       ..-
185 V       ...-
186 W       .--
187 X       -..-
188 Y       -.--
189 Z       --..
190 0       -----
191 1       .----
192 2       ..---
193 3       ...--
194 4       ....-
195 5       .....
196 6       -....
197 7       --...
198 8       ---..
199 9       ----.