709 lines
13 KiB
Perl
Executable File
709 lines
13 KiB
Perl
Executable File
#!/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., *
|
|
#* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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<X' => 0x01,
|
|
'CARRY' => 0x02,
|
|
'ALWAYS' => 0x03,
|
|
'ADR_BUFFER0=>CMP0' => 0x04,
|
|
'ADR_BUFFER0<CMP0' => 0x05,
|
|
'ADR_BUFFER1=>CMP1' => 0x06,
|
|
'ADR_BUFFER1<CMP1' => 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 = (
|
|
'<Y>',
|
|
'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;
|