You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

710 lines
13 KiB

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