2 #***************************************************************************
3 #* Copyright (C) 2008 Lou Deluxe *
4 #* lou.openocd012@fixit.nospammail.net *
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. *
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. *
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 #* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
20 #***************************************************************************
22 # A rudimentary assembler for DTC code.
23 # It is not robust, by any means, but it gets the job done.
27 my($i); # for later loop to generate reverse lookup
32 $self->{'pagewidth'} = 60;
48 'ADR_BUFFER0=>CMP0' => 0x04,
49 'ADR_BUFFER0<CMP0' => 0x05,
50 'ADR_BUFFER1=>CMP1' => 0x06,
51 'ADR_BUFFER1<CMP1' => 0x07,
101 for($i = 0; $i < @ld_arg; $i++) {
102 $ld_arg{$ld_arg[$i]} = $i;
109 my($operand, $i) = shift;
111 if(defined($ld_arg{$operand})) {
112 $i = $ld_arg{$operand};
114 if($i > 0x00 && $i < 0x04) {
115 return(($i - 0x01) << 3);
125 my($operand, $i) = shift;
129 if(defined($ld_arg{$operand})) {
130 $i = $ld_arg{$operand};
132 if($i > 0x03 && $i < 0x0c) {
133 return(($i - 0x04) << 2);
151 if(($ret < 0) || ($ret > 3)) {
159 # Opcode lookup table
180 if(!defined($status_bit_arg{"\U$i"})) {
184 $ret |= $status_bit_arg{"\U$i"};
197 if((@_ != 1) || (!defined($cp_arg{"\U$_[0]"}))) {
200 return($cp_arg{"\U$_[0]"});
209 if((@_ < 2) || (!defined($shift_unit_arg{"\U$_[0]"}))) {
212 $ret = $shift_unit_arg{"\U$_[0]"};
216 if(!defined($shift_pin_arg{"\U$i"})) {
220 $ret |= $shift_pin_arg{"\U$i"};
278 if(!defined($self->{'label'}{$i})) {
280 if(!defined($self->{'label'}{$i})) {
281 # not a defined label
287 $i = $self->{'label'}{$i} - $self->{'pc'};
292 if($i =~ m/^([+-]?\d+)$/) {
294 if(($i > 31) || ($i < -31)) {
295 warn "relative jump ($i) out of range";
317 # print STDERR join(", ", LD, @_), "\n";
327 if($_[0] =~ m/^([ML])S[BN]$/o) {
328 # MSB/LSB aka MSN/LSN
335 if($_[0] =~ m/^A\.([LH])$/o) {
337 my($islsb) = ($1 eq 'L') ?
1 : 0;
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};
347 # print "\$islsb=$islsb\n";
351 $i = ($i >> 4) & 0xf;
355 print "no label match for $i\n";
358 if(($i < 0) || ($i > 0xf)) {
365 } elsif($_[0] eq 'A') {
366 if(!defined($ld_arg{$_[1]})) {
369 return(0x40 | $ld_arg{$_[1]});
370 } elsif($_[1] eq 'A') {
371 if(!defined($ld_arg{$_[0]})) {
374 return(0x00 | $ld_arg{$_[0]});
382 $op{'JR'} = $op{'JP'};
386 my($self, $ifh, $ofh, $passnum) = @_;
388 # passnum=0 for plain parsing pass to populate label values
389 # passnum=1 for actual pass to assemble
391 my($line, $oline, $opcd);
394 delete($self->{'label'});
395 delete($self->{'binary'});
396 delete($self->{'ENTRY'});
397 delete($self->{'LUT'});
400 seek($ifh, 0, 0); # rewind
402 $self->{'line_number'} = 0;
403 while(defined($line = <$ifh>)) {
404 $self->{'line_number'}++;
409 @_ = split(/[\s,]+/, $line);
421 if(defined($self->{'label'}{$_[0]})) {
422 die "label redefinition for \"$_[0]\" in line $self->{'line_number'}";
424 $self->{'label'}{$_[0]} = $self->{'pc'};
431 if((@_ == 3) && ($_[1] eq '=')) {
432 # convert this = that to LD
436 elsif((@_ == 3) && ($_[1] eq '+=')) {
437 # convert this += that to ADDER8 or ADDER16
439 @_ = ('ADDER8', $_[2]);
441 elsif($_[2] eq 'X') {
442 @_ = ('ADDER16', $_[0]);
445 elsif((@_ == 3) && ($_[1] eq '-=')) {
446 # convert this -= that to ADDER8 or ADDER16
448 @_ = ('SUB8', $_[2]);
450 elsif($_[2] eq 'X') {
451 @_ = ('SUB16', $_[0]);
454 elsif((@_ == 1) && ($_[0] =~ m/^(B((SET)|(CLR)))([1-4])$/oi)) {
455 # convert BSETn or BCLRn to BSET n-1 or BCLR n-1
461 if(!defined($op{$op})) {
462 die "unknown instruction: $op in line $self->{'line_number'}";
469 $opcd = &$sub($self, @_);
474 if(!defined($opcd)) {
475 die "bad argument(s) in line $self->{'line_number'}";
486 if($oline =~ m/^;LUT; (.*)/o) {
487 my($entry, $label) = split(/\s+/, $1);
489 $self->{'LUT'}[hex($entry)] = $label;
491 if($oline =~ m/^;ENTRY; (.*)/o) {
492 my($id, $label) = split(/\s+/, $1);
493 $self->{'ENTRY'}{$id} = $label;
500 $self->{'binary'} .= chr($opcd);
502 printf $ofh ("/* 0x%02x */ 0x%02x%s /* %-*s */\n",
507 ($self->{'last pc'} < 0xff)
509 ($self->{'last pc'} != $self->{'pc'} - 1)
515 $self->{'pagewidth'} - 23,
520 print $ofh " /* $oline */\n";
529 $self->{'last pc'} = $self->{'pc'} - 1;
533 while($self->{'pc'} < 0xff) {
534 printf $ofh ("/* 0x%02x */ 0,\n",
539 if($self->{'pc'} < 0x100) {
540 printf $ofh ("/* 0x%02x */ 0\n",
554 't' => 'unsigned char',
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);
564 if(defined($opt{'i'})) {
570 if(!open(IN
, '<', $infile)) {
576 if(!defined($opt{'o'})) {
577 die "binary format requires -o";
579 if(!open(OUT
, '>&', *STDOUT
)) {
580 die "dup stdout: $!";
582 open(STDOUT
, '>&', *STDERR
);
584 if(defined($opt{'o'})) {
585 if(!open(OUT
, '>', $opt{'o'})) {
589 if(!open(OUT
, '>&', *STDOUT
)) {
590 die "dup stdout: $!";
592 open(STDOUT
, '>&', *STDERR
);
599 $as->pass(*IN
, *OUT
, 0);
601 if(defined($opt{'d'})) {
602 print OUT
"$opt{'t'} $opt{'d'}", "[0x100] = {\n";
604 $as->pass(*IN
, *OUT
, 1);
605 if(defined($opt{'d'})) {
611 if(defined($opt{'l'})) {
612 print OUT
"$opt{'t'} $opt{'l'}", "[0x40] = {\n";
613 # $end = @{$as->{'LUT'}};
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",
623 $as->{'label'}{$label},
624 (($i < 0xff) ?
',' : ''),
628 die "label $label has not been defined";
631 printf OUT
("/* 0x%02x */ 0%s\n",
633 (($i < 0xff) ?
',' : ''),
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; }
652 open(OUT
, ">", $opt{'o'}) || die "$opt{'o'}: $!";
653 syswrite(OUT
, pack('CC', DTCLOAD_LUT_COMMENT
, 3 - 1) . 'DTC');
657 for($start = 0; $start < @
$ref && !defined($ref->[$start]); $start++) {}
658 for($end = 0xff; $end >= $start && !defined($ref->[$end]); $end--) {}
660 for($i = $start; $i <= $end; $i++) {
661 if(!defined($ref->[$i])) {
666 if(defined($as->{'label'}{$label})) {
667 $label = $as->{'label'}{$label};
668 # printf("adding LUT entry 0x%02x\n", $label);
671 die "label $label has not been defined";
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);
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);
688 die "label $label has not been defined";
692 if(length($as->{'binary'})) {
693 # printf("DTC code size: 0x%x\n", length($as->{'binary'}));
694 syswrite(OUT
, pack('CC',
696 length($as->{'binary'}) - 1
697 ) . $as->{'binary'});
699 if(%{$as->{'ENTRY'}} < 1) {
700 syswrite(OUT
, pack('CCC', DTCLOAD_RUN
, 1 - 1, 0x00));