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