- rlink interface support from Lou Deluxe <lou.openocd012@fixit.nospammail.net>
[openocd.git] / tools / st7_dtc_as / st7_dtc_as.pl
1 #!/bin/perl
2 #***************************************************************************
3 #* Copyright (C) 2008 Lou Deluxe *
4 #* lou.openocd012@fixit.nospammail.net *
5 #* *
6 #* This program is free software; you can redistribute it and/or modify *
7 #* it under the terms of the GNU General Public License as published by *
8 #* the Free Software Foundation; either version 2 of the License, or *
9 #* (at your option) any later version. *
10 #* *
11 #* This program is distributed in the hope that it will be useful, *
12 #* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 #* GNU General Public License for more details. *
15 #* *
16 #* You should have received a copy of the GNU General Public License *
17 #* along with this program; if not, write to the *
18 #* Free Software Foundation, Inc., *
19 #* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20 #***************************************************************************
21
22 # A rudimentary assembler for DTC code.
23 # It is not robust, by any means, but it gets the job done.
24
25 {package DTC_as;
26
27 my($i); # for later loop to generate reverse lookup
28
29 sub new {
30 my($self) = bless{};
31
32 $self->{'pagewidth'} = 60;
33 $self;
34 }
35
36
37
38 %status_bit_arg = (
39 'STOP' => 0x01,
40 'ERROR' => 0x02,
41 );
42
43 %cp_arg = (
44 'A=>X' => 0x00,
45 'A<X' => 0x01,
46 'CARRY' => 0x02,
47 'ALWAYS' => 0x03,
48 'ADR_BUFFER0=>CMP0' => 0x04,
49 'ADR_BUFFER0<CMP0' => 0x05,
50 'ADR_BUFFER1=>CMP1' => 0x06,
51 'ADR_BUFFER1<CMP1' => 0x07,
52 );
53
54 %shift_unit_arg = (
55 'CARD' => 0x00,
56 'MPEG' => 0x08,
57 );
58
59 %shift_pin_arg = (
60 'PIN0=>IN' => 0x00,
61 'PIN1=>IN' => 0x04,
62 'OUT=>PIN0' => 0x01,
63 'OUT=>PIN1' => 0x03,
64 );
65
66 @ld_arg = (
67 '<Y>',
68 'X',
69 'Y',
70 'MASK',
71 'ADR_BUFFER00',
72 'ADR_BUFFER01',
73 'ADR_BUFFER10',
74 'ADR_BUFFER11',
75 'CMP00',
76 'CMP01',
77 'CMP10',
78 'CMP11',
79 'DATA_FLASH',
80 'CTRL_FCI',
81 'CTRL_CARD',
82 'CTRL_MPEG',
83 'DR_PARALLEL',
84 'DDR_PARALLEL',
85 'OR_PARALLEL',
86 'DR_CARD',
87 'DDR_CARD',
88 'OR_CARD',
89 'SHIFT_CARD',
90 'DR_MPEG',
91 'DDR_MPEG',
92 'OR_MPEG',
93 'SHIFT_MPEG',
94 'DATA_BUFFER0',
95 'DATA_BUFFER1',
96 'ECC_CRC',
97 'TMP_ECC',
98 'BUFFER_MNGT'
99 );
100
101 for($i = 0; $i < @ld_arg; $i++) {
102 $ld_arg{$ld_arg[$i]} = $i;
103 }
104
105
106 # ADDER8 / SUB8
107 sub alu8 {
108 my($self) = shift;
109 my($operand, $i) = shift;
110
111 if(defined($ld_arg{$operand})) {
112 $i = $ld_arg{$operand};
113
114 if($i > 0x00 && $i < 0x04) {
115 return(($i - 0x01) << 3);
116 }
117 }
118
119 return undef;
120 }
121
122 # ADDER16 / SUB16
123 sub alu16 {
124 my($self) = shift;
125 my($operand, $i) = shift;
126
127 $operand .= '0';
128
129 if(defined($ld_arg{$operand})) {
130 $i = $ld_arg{$operand};
131
132 if($i > 0x03 && $i < 0x0c) {
133 return(($i - 0x04) << 2);
134 }
135 }
136
137 return undef;
138 }
139
140
141 # BSET / BCLR
142 sub bsetorclr {
143 my($self) = shift;
144 my($ret);
145
146 if(@_ < 1) {
147 return undef;
148 }
149 $ret = $_[0];
150
151 if(($ret < 0) || ($ret > 3)) {
152 return undef;
153 }
154
155 return $ret;
156 }
157
158
159 # Opcode lookup table
160 %op = (
161 'NOP' => [
162 0x0,
163 ],
164 'SEC' => [
165 0x1,
166 ],
167 'CLC' => [
168 0x2,
169 ],
170 'RET' => [
171 0x3,
172 ],
173 'STATUS' => [
174 0x4,
175 sub {
176 my($self) = shift;
177 my($ret, $i);
178
179 for $i (@_) {
180 if(!defined($status_bit_arg{"\U$i"})) {
181 return undef;
182 }
183
184 $ret |= $status_bit_arg{"\U$i"};
185 }
186 if($ret < 1) {
187 return undef;
188 }
189
190 return $ret;
191 }
192 ],
193 'CP' => [
194 0x8,
195 sub {
196 my($self) = shift;
197 if((@_ != 1) || (!defined($cp_arg{"\U$_[0]"}))) {
198 return undef;
199 }
200 return($cp_arg{"\U$_[0]"});
201 }
202 ],
203 'SHIFT' => [
204 0x10,
205 sub {
206 my($self) = shift;
207 my($ret, $i);
208
209 if((@_ < 2) || (!defined($shift_unit_arg{"\U$_[0]"}))) {
210 return undef;
211 }
212 $ret = $shift_unit_arg{"\U$_[0]"};
213 shift;
214
215 for $i (@_) {
216 if(!defined($shift_pin_arg{"\U$i"})) {
217 return undef;
218 }
219
220 $ret |= $shift_pin_arg{"\U$i"};
221 }
222
223 return $ret;
224 }
225 ],
226 'SUB8' => [
227 0x24,
228 \&alu8
229 ],
230 'ADDER8' => [
231 0x25,
232 \&alu8
233 ],
234 'SUB16' => [
235 0x26,
236 \&alu16
237 ],
238 'ADDER16' => [
239 0x27,
240 \&alu16
241 ],
242 'BCLR' => [
243 0x28,
244 \&bsetorclr
245 ],
246 'BSET' => [
247 0x38,
248 \&bsetorclr
249 ],
250 'REVERSE' => [
251 0x30,
252 ],
253 'XOR' => [
254 0x31,
255 ],
256 'AND' => [
257 0x32,
258 ],
259 'EXCHANGE' => [
260 0x33,
261 ],
262 'DECY' => [
263 0x3c,
264 ],
265 'INCY' => [
266 0x3d,
267 ],
268 'JP' => [
269 0x40,
270 sub {
271 my($self) = shift;
272 my($i);
273
274 if(@_ != 1) {
275 return undef;
276 }
277 $i = $_[0];
278 if(!defined($self->{'label'}{$i})) {
279 $i =~ s/^://o;
280 if(!defined($self->{'label'}{$i})) {
281 # not a defined label
282 undef $i;
283 }
284 }
285
286 if(defined($i)) {
287 $i = $self->{'label'}{$i} - $self->{'pc'};
288 } else {
289 $i = $_[0];
290 }
291
292 if($i =~ m/^([+-]?\d+)$/) {
293 $i = 0 + $1;
294 if(($i > 31) || ($i < -31)) {
295 warn "relative jump ($i) out of range";
296 return undef;
297 }
298 if($i < 0) {
299 return(0x20 - $1);
300 } else {
301 return(0x00 + $1);
302 }
303 }
304
305 return undef;
306 }
307 ],
308 'BRANCH' => [
309 0x60,
310 ],
311 'LD' => [
312 0x80,
313 sub {
314 my($self) = shift;
315 my($i);
316
317 # print STDERR join(", ", LD, @_), "\n";
318
319 if(@_ == 1) {
320 $_[1] = 'A';
321 }
322 if(@_ != 2) {
323 return undef;
324 }
325
326
327 if($_[0] =~ m/^([ML])S[BN]$/o) {
328 # MSB/LSB aka MSN/LSN
329 if($1 eq 'L') {
330 $_[0] = 'A.L';
331 } else {
332 $_[0] = 'A.H';
333 }
334 }
335 if($_[0] =~ m/^A\.([LH])$/o) {
336 # A.L/A.H
337 my($islsb) = ($1 eq 'L') ? 1 : 0;
338 $i = $_[1];
339 if($i =~ s/^0x([0-9a-fA-F])$/hex($1)/e) {
340 # print "$i looks hex\n";
341 } elsif($i =~ m/^\d+$/) {
342 # print "$i looks decimal\n";
343 } elsif(defined($self->{'label'}{$i})) {
344 # print "label match for $i ($self->{'label'}{$i})\n";
345 $i = $self->{'label'}{$i};
346 # print "\$i=$i\n";
347 # print "\$islsb=$islsb\n";
348 if($islsb) {
349 $i = ($i & 0xf);
350 } else {
351 $i = ($i >> 4) & 0xf;
352 }
353 # print "\$i=$i\n";
354 } else {
355 print "no label match for $i\n";
356 return undef;
357 }
358 if(($i < 0) || ($i > 0xf)) {
359 return undef;
360 }
361 if($islsb) {
362 $i |= 0x10;
363 };
364 return(0x20 | $i);
365 } elsif($_[0] eq 'A') {
366 if(!defined($ld_arg{$_[1]})) {
367 return undef;
368 }
369 return(0x40 | $ld_arg{$_[1]});
370 } elsif($_[1] eq 'A') {
371 if(!defined($ld_arg{$_[0]})) {
372 return undef;
373 }
374 return(0x00 | $ld_arg{$_[0]});
375 }
376
377 return undef;
378 }
379 ],
380 );
381
382 $op{'JR'} = $op{'JP'};
383
384
385 sub pass {
386 my($self, $ifh, $ofh, $passnum) = @_;
387
388 # passnum=0 for plain parsing pass to populate label values
389 # passnum=1 for actual pass to assemble
390
391 my($line, $oline, $opcd);
392
393 if($passnum == 0) {
394 delete($self->{'label'});
395 delete($self->{'binary'});
396 delete($self->{'ENTRY'});
397 delete($self->{'LUT'});
398 }
399
400 seek($ifh, 0, 0); # rewind
401 $self->{'pc'} = 0;
402 $self->{'line_number'} = 0;
403 while(defined($line = <$ifh>)) {
404 $self->{'line_number'}++;
405 $line =~ s/\s+$//so;
406 $oline = $line;
407 $line =~ s/;.*//o;
408 $line =~ s/^\s+//o;
409 @_ = split(/[\s,]+/, $line);
410
411 undef($opcd);
412
413 if(@_ > 0) {
414
415 if(
416 ($_[0] =~ s/^://o)
417 ||
418 ($_[0] =~ s/:$//o)
419 ) {
420 if($passnum == 0) {
421 if(defined($self->{'label'}{$_[0]})) {
422 die "label redefinition for \"$_[0]\" in line $self->{'line_number'}";
423 }
424 $self->{'label'}{$_[0]} = $self->{'pc'};
425 }
426 shift(@_);
427 }
428
429 if(@_ > 0) {
430 if($passnum == 1) {
431 if((@_ == 3) && ($_[1] eq '=')) {
432 # convert this = that to LD
433 $_[1] = $_[0];
434 $_[0] = 'LD';
435 }
436 elsif((@_ == 3) && ($_[1] eq '+=')) {
437 # convert this += that to ADDER8 or ADDER16
438 if($_[0] eq 'A') {
439 @_ = ('ADDER8', $_[2]);
440 }
441 elsif($_[2] eq 'X') {
442 @_ = ('ADDER16', $_[0]);
443 }
444 }
445 elsif((@_ == 3) && ($_[1] eq '-=')) {
446 # convert this -= that to ADDER8 or ADDER16
447 if($_[0] eq 'A') {
448 @_ = ('SUB8', $_[2]);
449 }
450 elsif($_[2] eq 'X') {
451 @_ = ('SUB16', $_[0]);
452 }
453 }
454 elsif((@_ == 1) && ($_[0] =~ m/^(B((SET)|(CLR)))([1-4])$/oi)) {
455 # convert BSETn or BCLRn to BSET n-1 or BCLR n-1
456 $_[0] = $1;
457 $_[1] = $5 - 1;
458 }
459
460 $op = "\U$_[0]";
461 if(!defined($op{$op})) {
462 die "unknown instruction: $op in line $self->{'line_number'}";
463 }
464 shift(@_);
465
466 $op = $op{$op};
467 $sub = $op->[1];
468 if(defined($sub)) {
469 $opcd = &$sub($self, @_);
470 } else {
471 $opcd = 0;
472 }
473
474 if(!defined($opcd)) {
475 die "bad argument(s) in line $self->{'line_number'}";
476 }
477
478 $opcd |= $op->[0];
479 }
480
481 $self->{'pc'}++;
482 }
483
484 } else {
485 if($passnum == 0) {
486 if($oline =~ m/^;LUT; (.*)/o) {
487 my($entry, $label) = split(/\s+/, $1);
488 $entry =~ s/^0x//o;
489 $self->{'LUT'}[hex($entry)] = $label;
490 }
491 if($oline =~ m/^;ENTRY; (.*)/o) {
492 my($id, $label) = split(/\s+/, $1);
493 $self->{'ENTRY'}{$id} = $label;
494 }
495 }
496 }
497
498 if($passnum == 1) {
499 if(defined($opcd)) {
500 $self->{'binary'} .= chr($opcd);
501
502 printf $ofh ("/* 0x%02x */ 0x%02x%s /* %-*s */\n",
503 $self->{'pc'} - 1,
504 $opcd,
505 (
506 (
507 ($self->{'last pc'} < 0xff)
508 ||
509 ($self->{'last pc'} != $self->{'pc'} - 1)
510 ) ?
511 ','
512 :
513 ''
514 ),
515 $self->{'pagewidth'} - 23,
516 $oline
517 );
518 } else {
519 if($oline ne '') {
520 print $ofh " /* $oline */\n";
521 } else {
522 print $ofh "\n";
523 }
524 }
525 }
526 }
527
528 if($passnum == 0) {
529 $self->{'last pc'} = $self->{'pc'} - 1;
530 }
531
532 if($passnum == 1) {
533 while($self->{'pc'} < 0xff) {
534 printf $ofh ("/* 0x%02x */ 0,\n",
535 $self->{'pc'}
536 );
537 $self->{'pc'}++;
538 }
539 if($self->{'pc'} < 0x100) {
540 printf $ofh ("/* 0x%02x */ 0\n",
541 $self->{'pc'}
542 );
543 $self->{'pc'}++;
544 }
545 }
546 }
547
548 } # package DTC_as
549
550
551 use Getopt::Std;
552
553 %opt = (
554 't' => 'unsigned char',
555 );
556
557 # -t type of arrays (defaults to unsigned char)
558 # -l lookup table array name (no table generated if not provided)
559 # -d DTC code array name (naked elements if not provided)
560 # -i input filename (trailing argument if not provided)
561 # -o output filename (stdout if not provided)
562 getopts('l:d:i:o:t:b', \%opt);
563
564 if(defined($opt{'i'})) {
565 $infile = $opt{'i'};
566 } else {
567 $infile = shift;
568 }
569
570 if(!open(IN, '<', $infile)) {
571 die "$infile: $!";
572 }
573
574
575 if($opt{'b'}) {
576 if(!defined($opt{'o'})) {
577 die "binary format requires -o";
578 }
579 if(!open(OUT, '>&', *STDOUT)) {
580 die "dup stdout: $!";
581 }
582 open(STDOUT, '>&', *STDERR);
583 } else {
584 if(defined($opt{'o'})) {
585 if(!open(OUT, '>', $opt{'o'})) {
586 die "$opt{'o'}: $!";
587 }
588 } else {
589 if(!open(OUT, '>&', *STDOUT)) {
590 die "dup stdout: $!";
591 }
592 open(STDOUT, '>&', *STDERR);
593 }
594 }
595
596
597 $as = new DTC_as;
598
599 $as->pass(*IN, *OUT, 0);
600
601 if(defined($opt{'d'})) {
602 print OUT "$opt{'t'} $opt{'d'}", "[0x100] = {\n";
603 }
604 $as->pass(*IN, *OUT, 1);
605 if(defined($opt{'d'})) {
606 print OUT "};\n\n";
607 }
608
609 close(IN);
610
611 if(defined($opt{'l'})) {
612 print OUT "$opt{'t'} $opt{'l'}", "[0x40] = {\n";
613 # $end = @{$as->{'LUT'}};
614 # if($end > 0x100) {
615 # $end = 0x100;
616 # }
617 for($i = 0xc0; $i < 0x100; $i++) {
618 $label = $as->{'LUT'}[$i];
619 if(defined($label)) {
620 if(defined($as->{'label'}{$label})) {
621 printf OUT ("/* 0x%02x */ 0x%02x%s /* %s */\n",
622 $i,
623 $as->{'label'}{$label},
624 (($i < 0xff) ? ',' : ''),
625 $label
626 );
627 } else {
628 die "label $label has not been defined";
629 }
630 } else {
631 printf OUT ("/* 0x%02x */ 0%s\n",
632 $i,
633 (($i < 0xff) ? ',' : ''),
634 );
635 }
636 }
637 print OUT "};\n\n";
638 }
639
640
641 close(OUT);
642
643 sub DTCLOAD_COMMENT { 0; }
644 sub DTCLOAD_ENTRY { 1; }
645 sub DTCLOAD_LOAD { 2; }
646 sub DTCLOAD_RUN { 3; }
647 sub DTCLOAD_LUT_START { 4; }
648 sub DTCLOAD_LUT { 5; }
649
650
651 if($opt{'b'}) {
652 open(OUT, ">", $opt{'o'}) || die "$opt{'o'}: $!";
653 syswrite(OUT, pack('CC', DTCLOAD_LUT_COMMENT, 3 - 1) . 'DTC');
654
655 $ref = $as->{'LUT'};
656 if(@$ref > 0) {
657 for($start = 0; $start < @$ref && !defined($ref->[$start]); $start++) {}
658 for($end = 0xff; $end >= $start && !defined($ref->[$end]); $end--) {}
659 undef($lut);
660 for($i = $start; $i <= $end; $i++) {
661 if(!defined($ref->[$i])) {
662 $lut .= "\0";
663 next;
664 }
665 $label = $ref->[$i];
666 if(defined($as->{'label'}{$label})) {
667 $label = $as->{'label'}{$label};
668 # printf("adding LUT entry 0x%02x\n", $label);
669 $lut .= chr($label);
670 } else {
671 die "label $label has not been defined";
672 }
673 }
674 if(length($lut) > 0) {
675 syswrite(OUT, pack('CCC', DTCLOAD_LUT_START, 1 - 1, $start));
676 syswrite(OUT, pack('CC', DTCLOAD_LUT, length($lut) - 1) . $lut);
677 }
678 }
679
680 while(($key, $label) = each(%{$as->{'ENTRY'}})) {
681 # print "$key = $label\n";
682 if(defined($as->{'label'}{$label})) {
683 $label = $as->{'label'}{$label};
684 # print "key=$key\n";
685 # print "label=$label\n";
686 syswrite(OUT, pack('CCC', DTCLOAD_ENTRY, length($key), $label) . $key);
687 } else {
688 die "label $label has not been defined";
689 }
690 }
691
692 if(length($as->{'binary'})) {
693 # printf("DTC code size: 0x%x\n", length($as->{'binary'}));
694 syswrite(OUT, pack('CC',
695 DTCLOAD_LOAD ,
696 length($as->{'binary'}) - 1
697 ) . $as->{'binary'});
698
699 if(%{$as->{'ENTRY'}} < 1) {
700 syswrite(OUT, pack('CCC', DTCLOAD_RUN, 1 - 1, 0x00));
701 }
702 }
703
704 close(OUT);
705 }
706
707
708 0;
709