#!/bin/perl #*************************************************************************** #* Copyright (C) 2008 Lou Deluxe * #* lou.openocd012@fixit.nospammail.net * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU General Public License as published by * #* the Free Software Foundation; either version 2 of the License, or * #* (at your option) any later version. * #* * #* This program is distributed in the hope that it will be useful, * #* but WITHOUT ANY WARRANTY; without even the implied warranty of * #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * #* GNU General Public License for more details. * #* * #* You should have received a copy of the GNU General Public License * #* along with this program; if not, write to the * #* Free Software Foundation, Inc., * #* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * #*************************************************************************** # A rudimentary assembler for DTC code. # It is not robust, by any means, but it gets the job done. {package DTC_as; my($i); # for later loop to generate reverse lookup sub new { my($self) = bless{}; $self->{'pagewidth'} = 60; $self; } %status_bit_arg = ( 'STOP' => 0x01, 'ERROR' => 0x02, ); %cp_arg = ( 'A=>X' => 0x00, 'A 0x01, 'CARRY' => 0x02, 'ALWAYS' => 0x03, 'ADR_BUFFER0=>CMP0' => 0x04, 'ADR_BUFFER0 0x05, 'ADR_BUFFER1=>CMP1' => 0x06, 'ADR_BUFFER1 0x07, ); %shift_unit_arg = ( 'CARD' => 0x00, 'MPEG' => 0x08, ); %shift_pin_arg = ( 'PIN0=>IN' => 0x00, 'PIN1=>IN' => 0x04, 'OUT=>PIN0' => 0x01, 'OUT=>PIN1' => 0x03, ); @ld_arg = ( '', 'X', 'Y', 'MASK', 'ADR_BUFFER00', 'ADR_BUFFER01', 'ADR_BUFFER10', 'ADR_BUFFER11', 'CMP00', 'CMP01', 'CMP10', 'CMP11', 'DATA_FLASH', 'CTRL_FCI', 'CTRL_CARD', 'CTRL_MPEG', 'DR_PARALLEL', 'DDR_PARALLEL', 'OR_PARALLEL', 'DR_CARD', 'DDR_CARD', 'OR_CARD', 'SHIFT_CARD', 'DR_MPEG', 'DDR_MPEG', 'OR_MPEG', 'SHIFT_MPEG', 'DATA_BUFFER0', 'DATA_BUFFER1', 'ECC_CRC', 'TMP_ECC', 'BUFFER_MNGT' ); for($i = 0; $i < @ld_arg; $i++) { $ld_arg{$ld_arg[$i]} = $i; } # ADDER8 / SUB8 sub alu8 { my($self) = shift; my($operand, $i) = shift; if(defined($ld_arg{$operand})) { $i = $ld_arg{$operand}; if($i > 0x00 && $i < 0x04) { return(($i - 0x01) << 3); } } return undef; } # ADDER16 / SUB16 sub alu16 { my($self) = shift; my($operand, $i) = shift; $operand .= '0'; if(defined($ld_arg{$operand})) { $i = $ld_arg{$operand}; if($i > 0x03 && $i < 0x0c) { return(($i - 0x04) << 2); } } return undef; } # BSET / BCLR sub bsetorclr { my($self) = shift; my($ret); if(@_ < 1) { return undef; } $ret = $_[0]; if(($ret < 0) || ($ret > 3)) { return undef; } return $ret; } # Opcode lookup table %op = ( 'NOP' => [ 0x0, ], 'SEC' => [ 0x1, ], 'CLC' => [ 0x2, ], 'RET' => [ 0x3, ], 'STATUS' => [ 0x4, sub { my($self) = shift; my($ret, $i); for $i (@_) { if(!defined($status_bit_arg{"\U$i"})) { return undef; } $ret |= $status_bit_arg{"\U$i"}; } if($ret < 1) { return undef; } return $ret; } ], 'CP' => [ 0x8, sub { my($self) = shift; if((@_ != 1) || (!defined($cp_arg{"\U$_[0]"}))) { return undef; } return($cp_arg{"\U$_[0]"}); } ], 'SHIFT' => [ 0x10, sub { my($self) = shift; my($ret, $i); if((@_ < 2) || (!defined($shift_unit_arg{"\U$_[0]"}))) { return undef; } $ret = $shift_unit_arg{"\U$_[0]"}; shift; for $i (@_) { if(!defined($shift_pin_arg{"\U$i"})) { return undef; } $ret |= $shift_pin_arg{"\U$i"}; } return $ret; } ], 'SUB8' => [ 0x24, \&alu8 ], 'ADDER8' => [ 0x25, \&alu8 ], 'SUB16' => [ 0x26, \&alu16 ], 'ADDER16' => [ 0x27, \&alu16 ], 'BCLR' => [ 0x28, \&bsetorclr ], 'BSET' => [ 0x38, \&bsetorclr ], 'REVERSE' => [ 0x30, ], 'XOR' => [ 0x31, ], 'AND' => [ 0x32, ], 'EXCHANGE' => [ 0x33, ], 'DECY' => [ 0x3c, ], 'INCY' => [ 0x3d, ], 'JP' => [ 0x40, sub { my($self) = shift; my($i); if(@_ != 1) { return undef; } $i = $_[0]; if(!defined($self->{'label'}{$i})) { $i =~ s/^://o; if(!defined($self->{'label'}{$i})) { # not a defined label undef $i; } } if(defined($i)) { $i = $self->{'label'}{$i} - $self->{'pc'}; } else { $i = $_[0]; } if($i =~ m/^([+-]?\d+)$/) { $i = 0 + $1; if(($i > 31) || ($i < -31)) { warn "relative jump ($i) out of range"; return undef; } if($i < 0) { return(0x20 - $1); } else { return(0x00 + $1); } } return undef; } ], 'BRANCH' => [ 0x60, ], 'LD' => [ 0x80, sub { my($self) = shift; my($i); # print STDERR join(", ", LD, @_), "\n"; if(@_ == 1) { $_[1] = 'A'; } if(@_ != 2) { return undef; } if($_[0] =~ m/^([ML])S[BN]$/o) { # MSB/LSB aka MSN/LSN if($1 eq 'L') { $_[0] = 'A.L'; } else { $_[0] = 'A.H'; } } if($_[0] =~ m/^A\.([LH])$/o) { # A.L/A.H my($islsb) = ($1 eq 'L') ? 1 : 0; $i = $_[1]; if($i =~ s/^0x([0-9a-fA-F])$/hex($1)/e) { # print "$i looks hex\n"; } elsif($i =~ m/^\d+$/) { # print "$i looks decimal\n"; } elsif(defined($self->{'label'}{$i})) { # print "label match for $i ($self->{'label'}{$i})\n"; $i = $self->{'label'}{$i}; # print "\$i=$i\n"; # print "\$islsb=$islsb\n"; if($islsb) { $i = ($i & 0xf); } else { $i = ($i >> 4) & 0xf; } # print "\$i=$i\n"; } else { print "no label match for $i\n"; return undef; } if(($i < 0) || ($i > 0xf)) { return undef; } if($islsb) { $i |= 0x10; }; return(0x20 | $i); } elsif($_[0] eq 'A') { if(!defined($ld_arg{$_[1]})) { return undef; } return(0x40 | $ld_arg{$_[1]}); } elsif($_[1] eq 'A') { if(!defined($ld_arg{$_[0]})) { return undef; } return(0x00 | $ld_arg{$_[0]}); } return undef; } ], ); $op{'JR'} = $op{'JP'}; sub pass { my($self, $ifh, $ofh, $passnum) = @_; # passnum=0 for plain parsing pass to populate label values # passnum=1 for actual pass to assemble my($line, $oline, $opcd); if($passnum == 0) { delete($self->{'label'}); delete($self->{'binary'}); delete($self->{'ENTRY'}); delete($self->{'LUT'}); } seek($ifh, 0, 0); # rewind $self->{'pc'} = 0; $self->{'line_number'} = 0; while(defined($line = <$ifh>)) { $self->{'line_number'}++; $line =~ s/\s+$//so; $oline = $line; $line =~ s/;.*//o; $line =~ s/^\s+//o; @_ = split(/[\s,]+/, $line); undef($opcd); if(@_ > 0) { if( ($_[0] =~ s/^://o) || ($_[0] =~ s/:$//o) ) { if($passnum == 0) { if(defined($self->{'label'}{$_[0]})) { die "label redefinition for \"$_[0]\" in line $self->{'line_number'}"; } $self->{'label'}{$_[0]} = $self->{'pc'}; } shift(@_); } if(@_ > 0) { if($passnum == 1) { if((@_ == 3) && ($_[1] eq '=')) { # convert this = that to LD $_[1] = $_[0]; $_[0] = 'LD'; } elsif((@_ == 3) && ($_[1] eq '+=')) { # convert this += that to ADDER8 or ADDER16 if($_[0] eq 'A') { @_ = ('ADDER8', $_[2]); } elsif($_[2] eq 'X') { @_ = ('ADDER16', $_[0]); } } elsif((@_ == 3) && ($_[1] eq '-=')) { # convert this -= that to ADDER8 or ADDER16 if($_[0] eq 'A') { @_ = ('SUB8', $_[2]); } elsif($_[2] eq 'X') { @_ = ('SUB16', $_[0]); } } elsif((@_ == 1) && ($_[0] =~ m/^(B((SET)|(CLR)))([1-4])$/oi)) { # convert BSETn or BCLRn to BSET n-1 or BCLR n-1 $_[0] = $1; $_[1] = $5 - 1; } $op = "\U$_[0]"; if(!defined($op{$op})) { die "unknown instruction: $op in line $self->{'line_number'}"; } shift(@_); $op = $op{$op}; $sub = $op->[1]; if(defined($sub)) { $opcd = &$sub($self, @_); } else { $opcd = 0; } if(!defined($opcd)) { die "bad argument(s) in line $self->{'line_number'}"; } $opcd |= $op->[0]; } $self->{'pc'}++; } } else { if($passnum == 0) { if($oline =~ m/^;LUT; (.*)/o) { my($entry, $label) = split(/\s+/, $1); $entry =~ s/^0x//o; $self->{'LUT'}[hex($entry)] = $label; } if($oline =~ m/^;ENTRY; (.*)/o) { my($id, $label) = split(/\s+/, $1); $self->{'ENTRY'}{$id} = $label; } } } if($passnum == 1) { if(defined($opcd)) { $self->{'binary'} .= chr($opcd); printf $ofh ("/* 0x%02x */ 0x%02x%s /* %-*s */\n", $self->{'pc'} - 1, $opcd, ( ( ($self->{'last pc'} < 0xff) || ($self->{'last pc'} != $self->{'pc'} - 1) ) ? ',' : '' ), $self->{'pagewidth'} - 23, $oline ); } else { if($oline ne '') { print $ofh " /* $oline */\n"; } else { print $ofh "\n"; } } } } if($passnum == 0) { $self->{'last pc'} = $self->{'pc'} - 1; } if($passnum == 1) { while($self->{'pc'} < 0xff) { printf $ofh ("/* 0x%02x */ 0,\n", $self->{'pc'} ); $self->{'pc'}++; } if($self->{'pc'} < 0x100) { printf $ofh ("/* 0x%02x */ 0\n", $self->{'pc'} ); $self->{'pc'}++; } } } } # package DTC_as use Getopt::Std; %opt = ( 't' => 'unsigned char', ); # -t type of arrays (defaults to unsigned char) # -l lookup table array name (no table generated if not provided) # -d DTC code array name (naked elements if not provided) # -i input filename (trailing argument if not provided) # -o output filename (stdout if not provided) getopts('l:d:i:o:t:b', \%opt); if(defined($opt{'i'})) { $infile = $opt{'i'}; } else { $infile = shift; } if(!open(IN, '<', $infile)) { die "$infile: $!"; } if($opt{'b'}) { if(!defined($opt{'o'})) { die "binary format requires -o"; } if(!open(OUT, '>&', *STDOUT)) { die "dup stdout: $!"; } open(STDOUT, '>&', *STDERR); } else { if(defined($opt{'o'})) { if(!open(OUT, '>', $opt{'o'})) { die "$opt{'o'}: $!"; } } else { if(!open(OUT, '>&', *STDOUT)) { die "dup stdout: $!"; } open(STDOUT, '>&', *STDERR); } } $as = new DTC_as; $as->pass(*IN, *OUT, 0); if(defined($opt{'d'})) { print OUT "$opt{'t'} $opt{'d'}", "[0x100] = {\n"; } $as->pass(*IN, *OUT, 1); if(defined($opt{'d'})) { print OUT "};\n\n"; } close(IN); if(defined($opt{'l'})) { print OUT "$opt{'t'} $opt{'l'}", "[0x40] = {\n"; # $end = @{$as->{'LUT'}}; # if($end > 0x100) { # $end = 0x100; # } for($i = 0xc0; $i < 0x100; $i++) { $label = $as->{'LUT'}[$i]; if(defined($label)) { if(defined($as->{'label'}{$label})) { printf OUT ("/* 0x%02x */ 0x%02x%s /* %s */\n", $i, $as->{'label'}{$label}, (($i < 0xff) ? ',' : ''), $label ); } else { die "label $label has not been defined"; } } else { printf OUT ("/* 0x%02x */ 0%s\n", $i, (($i < 0xff) ? ',' : ''), ); } } print OUT "};\n\n"; } close(OUT); sub DTCLOAD_COMMENT { 0; } sub DTCLOAD_ENTRY { 1; } sub DTCLOAD_LOAD { 2; } sub DTCLOAD_RUN { 3; } sub DTCLOAD_LUT_START { 4; } sub DTCLOAD_LUT { 5; } if($opt{'b'}) { open(OUT, ">", $opt{'o'}) || die "$opt{'o'}: $!"; syswrite(OUT, pack('CC', DTCLOAD_LUT_COMMENT, 3 - 1) . 'DTC'); $ref = $as->{'LUT'}; if(@$ref > 0) { for($start = 0; $start < @$ref && !defined($ref->[$start]); $start++) {} for($end = 0xff; $end >= $start && !defined($ref->[$end]); $end--) {} undef($lut); for($i = $start; $i <= $end; $i++) { if(!defined($ref->[$i])) { $lut .= "\0"; next; } $label = $ref->[$i]; if(defined($as->{'label'}{$label})) { $label = $as->{'label'}{$label}; # printf("adding LUT entry 0x%02x\n", $label); $lut .= chr($label); } else { die "label $label has not been defined"; } } if(length($lut) > 0) { syswrite(OUT, pack('CCC', DTCLOAD_LUT_START, 1 - 1, $start)); syswrite(OUT, pack('CC', DTCLOAD_LUT, length($lut) - 1) . $lut); } } while(($key, $label) = each(%{$as->{'ENTRY'}})) { # print "$key = $label\n"; if(defined($as->{'label'}{$label})) { $label = $as->{'label'}{$label}; # print "key=$key\n"; # print "label=$label\n"; syswrite(OUT, pack('CCC', DTCLOAD_ENTRY, length($key), $label) . $key); } else { die "label $label has not been defined"; } } if(length($as->{'binary'})) { # printf("DTC code size: 0x%x\n", length($as->{'binary'})); syswrite(OUT, pack('CC', DTCLOAD_LOAD , length($as->{'binary'}) - 1 ) . $as->{'binary'}); if(%{$as->{'ENTRY'}} < 1) { syswrite(OUT, pack('CCC', DTCLOAD_RUN, 1 - 1, 0x00)); } } close(OUT); } 0;