GNU Linux-libre 5.10.219-gnu1
[releases.git] / drivers / scsi / script_asm.pl
1 #!/usr/bin/perl -s
2 # SPDX-License-Identifier: GPL-2.0-or-later
3
4 # NCR 53c810 script assembler
5 # Sponsored by 
6 #       iX Multiuser Multitasking Magazine
7 #
8 # Copyright 1993, Drew Eckhardt
9 #      Visionary Computing 
10 #      (Unix and Linux consulting and custom programming)
11 #      drew@Colorado.EDU
12 #      +1 (303) 786-7975 
13 #
14 #   Support for 53c710 (via -ncr7x0_family switch) added by Richard
15 #   Hirst <richard@sleepie.demon.co.uk> - 15th March 1997
16 #
17 # TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
18 #
19
20
21 # Basically, I follow the NCR syntax documented in the NCR53c710 
22 # Programmer's guide, with the new instructions, registers, etc.
23 # from the NCR53c810.
24 #
25 # Differences between this assembler and NCR's are that 
26 # 1.  PASS, REL (data, JUMPs work fine), and the option to start a new 
27 #       script,  are unimplemented, since I didn't use them in my scripts.
28
29 # 2.  I also emit a script_u.h file, which will undefine all of 
30 #       the A_*, E_*, etc. symbols defined in the script.  This 
31 #       makes including multiple scripts in one program easier
32 #       
33 # 3.  This is a single pass assembler, which only emits 
34 #       .h files.
35 #
36
37
38 # XXX - set these with command line options
39 $debug = 0;             # Print general debugging messages
40 $debug_external = 0;    # Print external/forward reference messages
41 $list_in_array = 1;     # Emit original SCRIPTS assembler in comments in
42                         # script.h
43 #$prefix;               # (set by perl -s)
44                         # define all arrays having this prefix so we 
45                         # don't have name space collisions after 
46                         # assembling this file in different ways for
47                         # different host adapters
48
49 # Constants
50
51
52 # Table of the SCSI phase encodings
53 %scsi_phases = (                        
54     'DATA_OUT', 0x00_00_00_00, 'DATA_IN', 0x01_00_00_00, 'CMD', 0x02_00_00_00,
55     'STATUS', 0x03_00_00_00, 'MSG_OUT', 0x06_00_00_00, 'MSG_IN', 0x07_00_00_00
56 );
57
58 # XXX - replace references to the *_810 constants with general constants
59 # assigned at compile time based on chip type.
60
61 # Table of operator encodings
62 # XXX - NCR53c710 only implements 
63 #       move (nop) = 0x00_00_00_00
64 #       or = 0x02_00_00_00
65 #       and = 0x04_00_00_00
66 #       add = 0x06_00_00_00
67
68 if ($ncr7x0_family) {
69   %operators = (
70     '|', 0x02_00_00_00, 'OR', 0x02_00_00_00,
71     '&', 0x04_00_00_00, 'AND', 0x04_00_00_00,
72     '+', 0x06_00_00_00
73   );
74 }
75 else {
76   %operators = (
77     'SHL',  0x01_00_00_00, 
78     '|', 0x02_00_00_00, 'OR', 0x02_00_00_00, 
79     'XOR', 0x03_00_00_00, 
80     '&', 0x04_00_00_00, 'AND', 0x04_00_00_00, 
81     'SHR', 0x05_00_00_00, 
82     # Note : low bit of the operator bit should be set for add with 
83     # carry.
84     '+', 0x06_00_00_00 
85   );
86 }
87
88 # Table of register addresses
89
90 if ($ncr7x0_family) {
91   %registers = (
92     'SCNTL0', 0, 'SCNTL1', 1, 'SDID', 2, 'SIEN', 3,
93     'SCID', 4, 'SXFER', 5, 'SODL', 6, 'SOCL', 7,
94     'SFBR', 8, 'SIDL', 9, 'SBDL', 10, 'SBCL', 11,
95     'DSTAT', 12, 'SSTAT0', 13, 'SSTAT1', 14, 'SSTAT2', 15,
96     'DSA0', 16, 'DSA1', 17, 'DSA2', 18, 'DSA3', 19,
97     'CTEST0', 20, 'CTEST1', 21, 'CTEST2', 22, 'CTEST3', 23,
98     'CTEST4', 24, 'CTEST5', 25, 'CTEST6', 26, 'CTEST7', 27,
99     'TEMP0', 28, 'TEMP1', 29, 'TEMP2', 30, 'TEMP3', 31,
100     'DFIFO', 32, 'ISTAT', 33, 'CTEST8', 34, 'LCRC', 35,
101     'DBC0', 36, 'DBC1', 37, 'DBC2', 38, 'DCMD', 39,
102     'DNAD0', 40, 'DNAD1', 41, 'DNAD2', 42, 'DNAD3', 43,
103     'DSP0', 44, 'DSP1', 45, 'DSP2', 46, 'DSP3', 47,
104     'DSPS0', 48, 'DSPS1', 49, 'DSPS2', 50, 'DSPS3', 51,
105     'SCRATCH0', 52, 'SCRATCH1', 53, 'SCRATCH2', 54, 'SCRATCH3', 55,
106     'DMODE', 56, 'DIEN', 57, 'DWT', 58, 'DCNTL', 59,
107     'ADDER0', 60, 'ADDER1', 61, 'ADDER2', 62, 'ADDER3', 63,
108   );
109 }
110 else {
111   %registers = (
112     'SCNTL0', 0, 'SCNTL1', 1, 'SCNTL2', 2, 'SCNTL3', 3,
113     'SCID', 4, 'SXFER', 5, 'SDID', 6, 'GPREG', 7,
114     'SFBR', 8, 'SOCL', 9, 'SSID', 10, 'SBCL', 11,
115     'DSTAT', 12, 'SSTAT0', 13, 'SSTAT1', 14, 'SSTAT2', 15,
116     'DSA0', 16, 'DSA1', 17, 'DSA2', 18, 'DSA3', 19,
117     'ISTAT', 20,
118     'CTEST0', 24, 'CTEST1', 25, 'CTEST2', 26, 'CTEST3', 27,
119     'TEMP0', 28, 'TEMP1', 29, 'TEMP2', 30, 'TEMP3', 31,
120     'DFIFO', 32, 'CTEST4', 33, 'CTEST5', 34, 'CTEST6', 35,
121     'DBC0', 36, 'DBC1', 37, 'DBC2', 38, 'DCMD', 39,
122     'DNAD0', 40, 'DNAD1', 41, 'DNAD2', 42, 'DNAD3', 43,
123     'DSP0', 44, 'DSP1', 45, 'DSP2', 46, 'DSP3', 47,
124     'DSPS0', 48, 'DSPS1', 49, 'DSPS2', 50, 'DSPS3', 51,
125     'SCRATCH0', 52, 'SCRATCH1', 53, 'SCRATCH2', 54, 'SCRATCH3', 55,
126     'SCRATCHA0', 52, 'SCRATCHA1', 53, 'SCRATCHA2', 54, 'SCRATCHA3', 55,
127     'DMODE', 56, 'DIEN', 57, 'DWT', 58, 'DCNTL', 59,
128     'ADDER0', 60, 'ADDER1', 61, 'ADDER2', 62, 'ADDER3', 63,
129     'SIEN0', 64, 'SIEN1', 65, 'SIST0', 66, 'SIST1', 67,
130     'SLPAR', 68,              'MACNTL', 70, 'GPCNTL', 71,
131     'STIME0', 72, 'STIME1', 73, 'RESPID', 74, 
132     'STEST0', 76, 'STEST1', 77, 'STEST2', 78, 'STEST3', 79,
133     'SIDL', 80,
134     'SODL', 84,
135     'SBDL', 88,
136     'SCRATCHB0', 92, 'SCRATCHB1', 93, 'SCRATCHB2', 94, 'SCRATCHB3', 95
137   );
138 }
139
140 # Parsing regular expressions
141 $identifier = '[A-Za-z_][A-Za-z_0-9]*';         
142 $decnum = '-?\\d+';
143 $hexnum = '0[xX][0-9A-Fa-f]+';          
144 $constant = "$hexnum|$decnum";
145
146 # yucky - since we can't control grouping of # $constant, we need to 
147 # expand out each alternative for $value.
148
149 $value = "$identifier|$identifier\\s*[+\-]\\s*$decnum|".
150     "$identifier\\s*[+-]\s*$hexnum|$constant";
151
152 print STDERR "value regex = $value\n" if ($debug);
153
154 $phase = join ('|', keys %scsi_phases);
155 print STDERR "phase regex = $phase\n" if ($debug);
156 $register = join ('|', keys %registers);
157
158 # yucky - since %operators includes meta-characters which must
159 # be escaped, I can't use the join() trick I used for the register
160 # regex
161
162 if ($ncr7x0_family) {
163   $operator = '\||OR|AND|\&|\+';
164 }
165 else {
166   $operator = '\||OR|AND|XOR|\&|\+';
167 }
168
169 # Global variables
170
171 %symbol_values = (%registers) ;         # Traditional symbol table
172
173 %symbol_references = () ;               # Table of symbol references, where
174                                         # the index is the symbol name, 
175                                         # and the contents a white space 
176                                         # delimited list of address,size
177                                         # tuples where size is in bytes.
178
179 @code = ();                             # Array of 32 bit words for SIOP 
180
181 @entry = ();                            # Array of entry point names
182
183 @label = ();                            # Array of label names
184
185 @absolute = ();                         # Array of absolute names
186
187 @relative = ();                         # Array of relative names
188
189 @external = ();                         # Array of external names
190
191 $address = 0;                           # Address of current instruction
192
193 $lineno = 0;                            # Line number we are parsing
194
195 $output = 'script.h';                   # Output file
196 $outputu = 'scriptu.h';
197
198 # &patch ($address, $offset, $length, $value) patches $code[$address]
199 #       so that the $length bytes at $offset have $value added to
200 #       them.  
201
202 @inverted_masks = (0x00_00_00_00, 0x00_00_00_ff, 0x00_00_ff_ff, 0x00_ff_ff_ff, 
203     0xff_ff_ff_ff);
204
205 sub patch {
206     local ($address, $offset, $length, $value) = @_;
207     if ($debug) {
208         print STDERR "Patching $address at offset $offset, length $length to $value\n";
209         printf STDERR "Old code : %08x\n", $code[$address];
210      }
211
212     $mask = ($inverted_masks[$length] << ($offset * 8));
213    
214     $code[$address] = ($code[$address] & ~$mask) | 
215         (($code[$address] & $mask) + ($value << ($offset * 8)) & 
216         $mask);
217     
218     printf STDERR "New code : %08x\n", $code[$address] if ($debug);
219 }
220
221 # &parse_value($value, $word, $offset, $length) where $value is 
222 #       an identifier or constant, $word is the word offset relative to 
223 #       $address, $offset is the starting byte within that word, and 
224 #       $length is the length of the field in bytes.
225 #
226 # Side effects are that the bytes are combined into the @code array
227 #       relative to $address, and that the %symbol_references table is 
228 #       updated as appropriate.
229
230 sub parse_value {
231     local ($value, $word, $offset, $length) = @_;
232     local ($tmp);
233
234     $symbol = '';
235
236     if ($value =~ /^REL\s*\(\s*($identifier)\s*\)\s*(.*)/i) {
237         $relative = 'REL';
238         $symbol = $1;
239         $value = $2;
240 print STDERR "Relative reference $symbol\n" if ($debug);
241     } elsif ($value =~ /^($identifier)\s*(.*)/) {
242         $relative = 'ABS';
243         $symbol = $1;
244         $value = $2;
245 print STDERR "Absolute reference $symbol\n" if ($debug);
246     } 
247
248     if ($symbol ne '') {
249 print STDERR "Referencing symbol $1, length = $length in $_\n" if ($debug);
250         $tmp = ($address + $word) * 4 + $offset;
251         if ($symbol_references{$symbol} ne undef) {
252             $symbol_references{$symbol} = 
253                 "$symbol_references{$symbol} $relative,$tmp,$length";
254         } else {
255             if (!defined($symbol_values{$symbol})) {
256 print STDERR "forward $1\n" if ($debug_external);
257                 $forward{$symbol} = "line $lineno : $_";
258             } 
259             $symbol_references{$symbol} = "$relative,$tmp,$length";
260         }
261     } 
262
263     $value = eval $value;
264     &patch ($address + $word, $offset, $length, $value);
265 }
266
267 # &parse_conditional ($conditional) where $conditional is the conditional
268 # clause from a transfer control instruction (RETURN, CALL, JUMP, INT).
269
270 sub parse_conditional {
271     local ($conditional) = @_;
272     if ($conditional =~ /^\s*(IF|WHEN)\s*(.*)/i) {
273         $if = $1;
274         $conditional = $2;
275         if ($if =~ /WHEN/i) {
276             $allow_atn = 0;
277             $code[$address] |= 0x00_01_00_00;
278             $allow_atn = 0;
279             print STDERR "$0 : parsed WHEN\n" if ($debug);
280         } else {
281             $allow_atn = 1;
282             print STDERR "$0 : parsed IF\n" if ($debug);
283         }
284     } else {
285             die "$0 : syntax error in line $lineno : $_
286         expected IF or WHEN
287 ";
288     }
289
290     if ($conditional =~ /^NOT\s+(.*)$/i) {
291         $not = 'NOT ';
292         $other = 'OR';
293         $conditional = $1;
294         print STDERR "$0 : parsed NOT\n" if ($debug);
295     } else {
296         $code[$address] |= 0x00_08_00_00;
297         $not = '';
298         $other = 'AND'
299     }
300
301     $need_data = 0;
302     if ($conditional =~ /^ATN\s*(.*)/i) {#
303         die "$0 : syntax error in line $lineno : $_
304         WHEN conditional is incompatible with ATN 
305 " if (!$allow_atn);
306         $code[$address] |= 0x00_02_00_00;
307         $conditional = $1;
308         print STDERR "$0 : parsed ATN\n" if ($debug);
309     } elsif ($conditional =~ /^($phase)\s*(.*)/i) {
310         $phase_index = "\U$1\E";
311         $p = $scsi_phases{$phase_index};
312         $code[$address] |= $p | 0x00_02_00_00;
313         $conditional = $2;
314         print STDERR "$0 : parsed phase $phase_index\n" if ($debug);
315     } else {
316         $other = '';
317         $need_data = 1;
318     }
319
320 print STDERR "Parsing conjunction, expecting $other\n" if ($debug);
321     if ($conditional =~ /^(AND|OR)\s*(.*)/i) {
322         $conjunction = $1;
323         $conditional = $2;
324         $need_data = 1;
325         die "$0 : syntax error in line $lineno : $_
326             Illegal use of $1.  Valid uses are 
327             ".$not."<phase> $1 data
328             ".$not."ATN $1 data
329 " if ($other eq '');
330         die "$0 : syntax error in line $lineno : $_
331         Illegal use of $conjunction.  Valid syntaxes are 
332                 NOT <phase>|ATN OR data
333                 <phase>|ATN AND data
334 " if ($conjunction !~ /\s*$other\s*/i);
335         print STDERR "$0 : parsed $1\n" if ($debug);
336     }
337
338     if ($need_data) {
339 print STDERR "looking for data in $conditional\n" if ($debug);
340         if ($conditional=~ /^($value)\s*(.*)/i) {
341             $code[$address] |= 0x00_04_00_00;
342             $conditional = $2;
343             &parse_value($1, 0, 0, 1);
344             print STDERR "$0 : parsed data\n" if ($debug);
345         } else {
346         die "$0 : syntax error in line $lineno : $_
347         expected <data>.
348 ";
349         }
350     }
351
352     if ($conditional =~ /^\s*,\s*(.*)/) {
353         $conditional = $1;
354         if ($conditional =~ /^AND\s\s*MASK\s\s*($value)\s*(.*)/i) {
355             &parse_value ($1, 0, 1, 1);
356             print STDERR "$0 parsed AND MASK $1\n" if ($debug);
357             die "$0 : syntax error in line $lineno : $_
358         expected end of line, not \"$2\"
359 " if ($2 ne '');
360         } else {
361             die "$0 : syntax error in line $lineno : $_
362         expected \",AND MASK <data>\", not \"$2\"
363 ";
364         }
365     } elsif ($conditional !~ /^\s*$/) { 
366         die "$0 : syntax error in line $lineno : $_
367         expected end of line" . (($need_data) ? " or \"AND MASK <data>\"" : "") . "
368         not \"$conditional\"
369 ";
370     }
371 }
372
373 # Parse command line
374 $output = shift;
375 $outputu = shift;
376
377     
378 # Main loop
379 while (<STDIN>) {
380     $lineno = $lineno + 1;
381     $list[$address] = $list[$address].$_;
382     s/;.*$//;                           # Strip comments
383
384
385     chop;                               # Leave new line out of error messages
386
387 # Handle symbol definitions of the form label:
388     if (/^\s*($identifier)\s*:(.*)/) {
389         if (!defined($symbol_values{$1})) {
390             $symbol_values{$1} = $address * 4;  # Address is an index into
391             delete $forward{$1};                # an array of longs
392             push (@label, $1);
393             $_ = $2;
394         } else {
395             die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
396         }
397     }
398
399 # Handle symbol definitions of the form ABSOLUTE or RELATIVE identifier = 
400 # value
401     if (/^\s*(ABSOLUTE|RELATIVE)\s+(.*)/i) {
402         $is_absolute = $1;
403         $rest = $2;
404         foreach $rest (split (/\s*,\s*/, $rest)) {
405             if ($rest =~ /^($identifier)\s*=\s*($constant)\s*$/) {
406                 local ($id, $cnst) = ($1, $2);
407                 if ($symbol_values{$id} eq undef) {
408                     $symbol_values{$id} = eval $cnst;
409                     delete $forward{$id};
410                     if ($is_absolute =~ /ABSOLUTE/i) {
411                         push (@absolute , $id);
412                     } else {
413                         push (@relative, $id);
414                     }
415                 } else {
416                     die "$0 : redefinition of symbol $id in line $lineno : $_\n";
417                 }
418             } else {
419                 die 
420 "$0 : syntax error in line $lineno : $_
421             expected <identifier> = <value>
422 ";
423             }
424         }
425     } elsif (/^\s*EXTERNAL\s+(.*)/i) {
426         $externals = $1;
427         foreach $external (split (/,/,$externals)) {
428             if ($external =~ /\s*($identifier)\s*$/) {
429                 $external = $1;
430                 push (@external, $external);
431                 delete $forward{$external};
432                 if (defined($symbol_values{$external})) {
433                         die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
434                 }
435                 $symbol_values{$external} = $external;
436 print STDERR "defined external $1 to $external\n" if ($debug_external);
437             } else {
438                 die 
439 "$0 : syntax error in line $lineno : $_
440         expected <identifier>, got $external
441 ";
442             }
443         }
444 # Process ENTRY identifier declarations
445     } elsif (/^\s*ENTRY\s+(.*)/i) {
446         if ($1 =~ /^($identifier)\s*$/) {
447             push (@entry, $1);
448         } else {
449             die
450 "$0 : syntax error in line $lineno : $_
451         expected ENTRY <identifier>
452 ";
453         }
454 # Process MOVE length, address, WITH|WHEN phase instruction
455     } elsif (/^\s*MOVE\s+(.*)/i) {
456         $rest = $1;
457         if ($rest =~ /^FROM\s+($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
458             $transfer_addr = $1;
459             $with_when = $2;
460             $scsi_phase = $3;
461 print STDERR "Parsing MOVE FROM $transfer_addr, $with_when $3\n" if ($debug);
462             $code[$address] = 0x18_00_00_00 | (($with_when =~ /WITH/i) ? 
463                 0x00_00_00_00 : 0x08_00_00_00) | $scsi_phases{$scsi_phase};
464             &parse_value ($transfer_addr, 1, 0, 4);
465             $address += 2;
466         } elsif ($rest =~ /^($value)\s*,\s*(PTR\s+|)($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
467             $transfer_len = $1;
468             $ptr = $2;
469             $transfer_addr = $3;
470             $with_when = $4;
471             $scsi_phase = $5;
472             $code[$address] = (($with_when =~ /WITH/i) ? 0x00_00_00_00 : 
473                 0x08_00_00_00)  | (($ptr =~ /PTR/i) ? (1 << 29) : 0) | 
474                 $scsi_phases{$scsi_phase};
475             &parse_value ($transfer_len, 0, 0, 3);
476             &parse_value ($transfer_addr, 1, 0, 4);
477             $address += 2;
478         } elsif ($rest =~ /^MEMORY\s+(.*)/i) {
479             $rest = $1;
480             $code[$address] = 0xc0_00_00_00; 
481             if ($rest =~ /^($value)\s*,\s*($value)\s*,\s*($value)\s*$/) {
482                 $count = $1;
483                 $source = $2;
484                 $dest =  $3;
485 print STDERR "Parsing MOVE MEMORY $count, $source, $dest\n" if ($debug);
486                 &parse_value ($count, 0, 0, 3);
487                 &parse_value ($source, 1, 0, 4);
488                 &parse_value ($dest, 2, 0, 4);
489 printf STDERR "Move memory instruction = %08x,%08x,%08x\n", 
490                 $code[$address], $code[$address+1], $code[$address +2] if
491                 ($debug);
492                 $address += 3;
493         
494             } else {
495                 die 
496 "$0 : syntax error in line $lineno : $_
497         expected <count>, <source>, <destination>
498 "
499             }
500         } elsif ($1 =~ /^(.*)\s+(TO|SHL|SHR)\s+(.*)/i) {
501 print STDERR "Parsing register to register move\n" if ($debug);
502             $src = $1;
503             $op = "\U$2\E";
504             $rest = $3;
505
506             $code[$address] = 0x40_00_00_00;
507         
508             $force = ($op !~ /TO/i); 
509
510
511 print STDERR "Forcing register source \n" if ($force && $debug);
512
513             if (!$force && $src =~ 
514                 /^($register)\s+(-|$operator)\s+($value)\s*$/i) {
515 print STDERR "register operand  data8 source\n" if ($debug);
516                 $src_reg = "\U$1\E";
517                 $op = "\U$2\E";
518                 if ($op ne '-') {
519                     $data8 = $3;
520                 } else {
521                     die "- is not implemented yet.\n"
522                 }
523             } elsif ($src =~ /^($register)\s*$/i) {
524 print STDERR "register source\n" if ($debug);
525                 $src_reg = "\U$1\E";
526                 # Encode register to register move as a register | 0 
527                 # move to register.
528                 if (!$force) {
529                     $op = '|';
530                 }
531                 $data8 = 0;
532             } elsif (!$force && $src =~ /^($value)\s*$/i) {
533 print STDERR "data8 source\n" if ($debug);
534                 $src_reg = undef;
535                 $op = 'NONE';
536                 $data8 = $1;
537             } else {
538                 if (!$force) {
539                     die 
540 "$0 : syntax error in line $lineno : $_
541         expected <register>
542                 <data8>
543                 <register> <operand> <data8>
544 ";
545                 } else {
546                     die
547 "$0 : syntax error in line $lineno : $_
548         expected <register>
549 ";
550                 }
551             }
552             if ($rest =~ /^($register)\s*(.*)$/i) {
553                 $dst_reg = "\U$1\E";
554                 $rest = $2;
555             } else {
556             die 
557 "$0 : syntax error in $lineno : $_
558         expected <register>, got $rest
559 ";
560             }
561
562             if ($rest =~ /^WITH\s+CARRY\s*(.*)/i) {
563                 $rest = $1;
564                 if ($op eq '+') {
565                     $code[$address] |= 0x01_00_00_00;
566                 } else {
567                     die
568 "$0 : syntax error in $lineno : $_
569         WITH CARRY option is incompatible with the $op operator.
570 ";
571                 }
572             }
573
574             if ($rest !~ /^\s*$/) {
575                 die
576 "$0 : syntax error in $lineno : $_
577         Expected end of line, got $rest
578 ";
579             }
580
581             print STDERR "source = $src_reg, data = $data8 , destination = $dst_reg\n"
582                 if ($debug);
583             # Note that Move data8 to reg is encoded as a read-modify-write
584             # instruction.
585             if (($src_reg eq undef) || ($src_reg eq $dst_reg)) {
586                 $code[$address] |= 0x38_00_00_00 | 
587                     ($registers{$dst_reg} << 16);
588             } elsif ($dst_reg =~ /SFBR/i) {
589                 $code[$address] |= 0x30_00_00_00 |
590                     ($registers{$src_reg} << 16);
591             } elsif ($src_reg =~ /SFBR/i) {
592                 $code[$address] |= 0x28_00_00_00 |
593                     ($registers{$dst_reg} << 16);
594             } else {
595                 die
596 "$0 : Illegal combination of registers in line $lineno : $_
597         Either source and destination registers must be the same,
598         or either source or destination register must be SFBR.
599 ";
600             }
601
602             $code[$address] |= $operators{$op};
603             
604             &parse_value ($data8, 0, 1, 1);
605             $code[$address] |= $operators{$op};
606             $code[$address + 1] = 0x00_00_00_00;# Reserved
607             $address += 2;
608         } else {
609             die 
610 "$0 : syntax error in line $lineno : $_
611         expected (initiator) <length>, <address>, WHEN <phase>
612                  (target) <length>, <address>, WITH <phase>
613                  MEMORY <length>, <source>, <destination>
614                  <expression> TO <register>
615 ";
616         }
617 # Process SELECT {ATN|} id, fail_address
618     } elsif (/^\s*(SELECT|RESELECT)\s+(.*)/i) {
619         $rest = $2;
620         if ($rest =~ /^(ATN|)\s*($value)\s*,\s*($identifier)\s*$/i) {
621             $atn = $1;
622             $id = $2;
623             $alt_addr = $3;
624             $code[$address] = 0x40_00_00_00 | 
625                 (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
626             $code[$address + 1] = 0x00_00_00_00;
627             &parse_value($id, 0, 2, 1);
628             &parse_value($alt_addr, 1, 0, 4);
629             $address += 2;
630         } elsif ($rest =~ /^(ATN|)\s*FROM\s+($value)\s*,\s*($identifier)\s*$/i) {
631             $atn = $1;
632             $addr = $2;
633             $alt_addr = $3;
634             $code[$address] = 0x42_00_00_00 | 
635                 (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
636             $code[$address + 1] = 0x00_00_00_00;
637             &parse_value($addr, 0, 0, 3);
638             &parse_value($alt_addr, 1, 0, 4);
639             $address += 2;
640         } else {
641             die 
642 "$0 : syntax error in line $lineno : $_
643         expected SELECT id, alternate_address or 
644                 SELECT FROM address, alternate_address or 
645                 RESELECT id, alternate_address or
646                 RESELECT FROM address, alternate_address
647 ";
648         }
649     } elsif (/^\s*WAIT\s+(.*)/i) {
650             $rest = $1;
651 print STDERR "Parsing WAIT $rest\n" if ($debug);
652         if ($rest =~ /^DISCONNECT\s*$/i) {
653             $code[$address] = 0x48_00_00_00;
654             $code[$address + 1] = 0x00_00_00_00;
655             $address += 2;
656         } elsif ($rest =~ /^(RESELECT|SELECT)\s+($identifier)\s*$/i) {
657             $alt_addr = $2;
658             $code[$address] = 0x50_00_00_00;
659             &parse_value ($alt_addr, 1, 0, 4);
660             $address += 2;
661         } else {
662             die
663 "$0 : syntax error in line $lineno : $_
664         expected (initiator) WAIT DISCONNECT or 
665                  (initiator) WAIT RESELECT alternate_address or
666                  (target) WAIT SELECT alternate_address
667 ";
668         }
669 # Handle SET and CLEAR instructions.  Note that we should also do something
670 # with this syntax to set target mode.
671     } elsif (/^\s*(SET|CLEAR)\s+(.*)/i) {
672         $set = $1;
673         $list = $2;
674         $code[$address] = ($set =~ /SET/i) ?  0x58_00_00_00 : 
675             0x60_00_00_00;
676         foreach $arg (split (/\s+AND\s+/i,$list)) {
677             if ($arg =~ /ATN/i) {
678                 $code[$address] |= 0x00_00_00_08;
679             } elsif ($arg =~ /ACK/i) {
680                 $code[$address] |= 0x00_00_00_40;
681             } elsif ($arg =~ /TARGET/i) {
682                 $code[$address] |= 0x00_00_02_00;
683             } elsif ($arg =~ /CARRY/i) {
684                 $code[$address] |= 0x00_00_04_00;
685             } else {
686                 die 
687 "$0 : syntax error in line $lineno : $_
688         expected $set followed by a AND delimited list of one or 
689         more strings from the list ACK, ATN, CARRY, TARGET.
690 ";
691             }
692         }
693         $code[$address + 1] = 0x00_00_00_00;
694         $address += 2;
695     } elsif (/^\s*(JUMP|CALL|INT)\s+(.*)/i) {
696         $instruction = $1;
697         $rest = $2;
698         if ($instruction =~ /JUMP/i) {
699             $code[$address] = 0x80_00_00_00;
700         } elsif ($instruction =~ /CALL/i) {
701             $code[$address] = 0x88_00_00_00;
702         } else {
703             $code[$address] = 0x98_00_00_00;
704         }
705 print STDERR "parsing JUMP, rest = $rest\n" if ($debug);
706
707 # Relative jump. 
708         if ($rest =~ /^(REL\s*\(\s*$identifier\s*\))\s*(.*)/i) { 
709             $addr = $1;
710             $rest = $2;
711 print STDERR "parsing JUMP REL, addr = $addr, rest = $rest\n" if ($debug);
712             $code[$address]  |= 0x00_80_00_00;
713             &parse_value($addr, 1, 0, 4);
714 # Absolute jump, requires no more gunk
715         } elsif ($rest =~ /^($value)\s*(.*)/) {
716             $addr = $1;
717             $rest = $2;
718             &parse_value($addr, 1, 0, 4);
719         } else {
720             die
721 "$0 : syntax error in line $lineno : $_
722         expected <address> or REL (address)
723 ";
724         }
725
726         if ($rest =~ /^,\s*(.*)/) {
727             &parse_conditional($1);
728         } elsif ($rest =~ /^\s*$/) {
729             $code[$address] |= (1 << 19);
730         } else {
731             die
732 "$0 : syntax error in line $lineno : $_
733         expected , <conditional> or end of line, got $1
734 ";
735         }
736         
737         $address += 2;
738     } elsif (/^\s*(RETURN|INTFLY)\s*(.*)/i) {
739         $instruction = $1;
740         $conditional = $2; 
741 print STDERR "Parsing $instruction\n" if ($debug);
742         $code[$address] = ($instruction =~ /RETURN/i) ? 0x90_00_00_00 :
743             0x98_10_00_00;
744         if ($conditional =~ /^,\s*(.*)/) {
745             $conditional = $1;
746             &parse_conditional ($conditional);
747         } elsif ($conditional !~ /^\s*$/) {
748             die
749 "$0 : syntax error in line $lineno : $_
750         expected , <conditional> 
751 ";
752         } else {
753             $code[$address] |= 0x00_08_00_00;
754         }
755            
756         $code[$address + 1] = 0x00_00_00_00;
757         $address += 2;
758     } elsif (/^\s*DISCONNECT\s*$/) {
759         $code[$address] = 0x48_00_00_00;
760         $code[$address + 1] = 0x00_00_00_00;
761         $address += 2;
762 # I'm not sure that I should be including this extension, but 
763 # what the hell?
764     } elsif (/^\s*NOP\s*$/i) {
765         $code[$address] = 0x80_88_00_00;
766         $code[$address + 1] = 0x00_00_00_00;
767         $address += 2;
768 # Ignore lines consisting entirely of white space
769     } elsif (/^\s*$/) {
770     } else {
771         die 
772 "$0 : syntax error in line $lineno: $_
773         expected label:, ABSOLUTE, CLEAR, DISCONNECT, EXTERNAL, MOVE, RESELECT,
774             SELECT SET, or WAIT
775 ";
776     }
777 }
778
779 # Fill in label references
780
781 @undefined = keys %forward;
782 if ($#undefined >= 0) {
783     print STDERR "Undefined symbols : \n";
784     foreach $undef (@undefined) {
785         print STDERR "$undef in $forward{$undef}\n";
786     }
787     exit 1;
788 }
789
790 @label_patches = ();
791
792 @external_patches = ();
793
794 @absolute = sort @absolute;
795
796 foreach $i (@absolute) {
797     foreach $j (split (/\s+/,$symbol_references{$i})) {
798         $j =~ /(REL|ABS),(.*),(.*)/;
799         $type = $1;
800         $address = $2;
801         $length = $3;
802         die 
803 "$0 : $symbol $i has invalid relative reference at address $address,
804     size $length\n"
805         if ($type eq 'REL');
806             
807         &patch ($address / 4, $address % 4, $length, $symbol_values{$i});
808     }
809 }
810
811 foreach $external (@external) {
812 print STDERR "checking external $external \n" if ($debug_external);
813     if ($symbol_references{$external} ne undef) {
814         for $reference (split(/\s+/,$symbol_references{$external})) {
815             $reference =~ /(REL|ABS),(.*),(.*)/;
816             $type = $1;
817             $address = $2;
818             $length = $3;
819             
820             die 
821 "$0 : symbol $label is external, has invalid relative reference at $address,
822     size $length\n"
823                 if ($type eq 'REL');
824
825             die 
826 "$0 : symbol $label has invalid reference at $address, size $length\n"
827                 if ((($address % 4) !=0) || ($length != 4));
828
829             $symbol = $symbol_values{$external};
830             $add = $code[$address / 4];
831             if ($add eq 0) {
832                 $code[$address / 4] = $symbol;
833             } else {
834                 $add = sprintf ("0x%08x", $add);
835                 $code[$address / 4] = "$symbol + $add";
836             }
837                 
838 print STDERR "referenced external $external at $1\n" if ($debug_external);
839         }
840     }
841 }
842
843 foreach $label (@label) {
844     if ($symbol_references{$label} ne undef) {
845         for $reference (split(/\s+/,$symbol_references{$label})) {
846             $reference =~ /(REL|ABS),(.*),(.*)/;
847             $type = $1;
848             $address = $2;
849             $length = $3;
850
851             if ((($address % 4) !=0) || ($length != 4)) {
852                 die "$0 : symbol $label has invalid reference at $1, size $2\n";
853             }
854
855             if ($type eq 'ABS') {
856                 $code[$address / 4] += $symbol_values{$label};
857                 push (@label_patches, $address / 4);
858             } else {
859
860 # - The address of the reference should be in the second and last word
861 #       of an instruction
862 # - Relative jumps, etc. are relative to the DSP of the _next_ instruction
863 #
864 # So, we need to add four to the address of the reference, to get 
865 # the address of the next instruction, when computing the reference.
866   
867                 $tmp = $symbol_values{$label} - 
868                     ($address + 4);
869                 die 
870 # Relative addressing is limited to 24 bits.
871 "$0 : symbol $label is too far ($tmp) from $address to reference as 
872     relative/\n" if (($tmp >= 0x80_00_00) || ($tmp < -0x80_00_00));
873                 $code[$address / 4] = $tmp & 0x00_ff_ff_ff;
874             }
875         }
876     }
877 }
878
879 # Output SCRIPT[] array, one instruction per line.  Optionally 
880 # print the original code too.
881
882 open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n";
883 open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n";
884
885 ($_ = $0) =~ s:.*/::;
886 print OUTPUT "/* DO NOT EDIT - Generated automatically by ".$_." */\n";
887 print OUTPUT "static u32 ".$prefix."SCRIPT[] = {\n";
888 $instructions = 0;
889 for ($i = 0; $i < $#code; ) {
890     if ($list_in_array) {
891         printf OUTPUT "/*\n$list[$i]\nat 0x%08x : */", $i;
892     }
893     printf OUTPUT "\t0x%08x,", $code[$i];
894     printf STDERR "Address $i = %x\n", $code[$i] if ($debug);
895     if ($code[$i + 1] =~ /\s*($identifier)(.*)$/) {
896         push (@external_patches, $i+1, $1);
897         printf OUTPUT "0%s,", $2
898     } else {
899         printf OUTPUT "0x%08x,",$code[$i+1];
900     }
901
902     if (($code[$i] & 0xff_00_00_00) == 0xc0_00_00_00) {
903         if ($code[$i + 2] =~ /$identifier/) {
904             push (@external_patches, $i+2, $code[$i+2]);
905             printf OUTPUT "0,\n";
906         } else {
907             printf OUTPUT "0x%08x,\n",$code[$i+2];
908         }
909         $i += 3;
910     } else {
911         printf OUTPUT "\n";
912         $i += 2;
913     }
914     $instructions += 1;
915 }
916 print OUTPUT "};\n\n";
917
918 foreach $i (@absolute) {
919     printf OUTPUT "#define A_$i\t0x%08x\n", $symbol_values{$i};
920     if (defined($prefix) && $prefix ne '') {
921         printf OUTPUT "#define A_".$i."_used ".$prefix."A_".$i."_used\n";
922         printf OUTPUTU "#undef A_".$i."_used\n";
923     }
924     printf OUTPUTU "#undef A_$i\n";
925
926     printf OUTPUT "static u32 A_".$i."_used\[\] __attribute((unused)) = {\n";
927 printf STDERR "$i is used $symbol_references{$i}\n" if ($debug);
928     foreach $j (split (/\s+/,$symbol_references{$i})) {
929         $j =~ /(ABS|REL),(.*),(.*)/;
930         if ($1 eq 'ABS') {
931             $address = $2;
932             $length = $3;
933             printf OUTPUT "\t0x%08x,\n", $address / 4;
934         }
935     }
936     printf OUTPUT "};\n\n";
937 }
938
939 foreach $i (sort @entry) {
940     printf OUTPUT "#define Ent_$i\t0x%08x\n", $symbol_values{$i};
941     printf OUTPUTU "#undef Ent_$i\n", $symbol_values{$i};
942 }
943
944 #
945 # NCR assembler outputs label patches in the form of indices into 
946 # the code.
947 #
948 printf OUTPUT "static u32 ".$prefix."LABELPATCHES[] __attribute((unused)) = {\n";
949 for $patch (sort {$a <=> $b} @label_patches) {
950     printf OUTPUT "\t0x%08x,\n", $patch;
951 }
952 printf OUTPUT "};\n\n";
953
954 $num_external_patches = 0;
955 printf OUTPUT "static struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n".
956     "} ".$prefix."EXTERNAL_PATCHES[] __attribute((unused)) = {\n";
957 while ($ident = pop(@external_patches)) {
958     $off = pop(@external_patches);
959     printf OUTPUT "\t{0x%08x, &%s},\n", $off, $ident;
960     ++$num_external_patches;
961 }
962 printf OUTPUT "};\n\n";
963
964 printf OUTPUT "static u32 ".$prefix."INSTRUCTIONS __attribute((unused))\t= %d;\n", 
965     $instructions;
966 printf OUTPUT "static u32 ".$prefix."PATCHES __attribute((unused))\t= %d;\n", 
967     $#label_patches+1;
968 printf OUTPUT "static u32 ".$prefix."EXTERNAL_PATCHES_LEN __attribute((unused))\t= %d;\n",
969     $num_external_patches;
970 close OUTPUT;
971 close OUTPUTU;