cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

script_asm.pl (28507B)


      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
     68if ($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}
     75else {
     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
     90if ($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}
    110else {
    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
    152print STDERR "value regex = $value\n" if ($debug);
    153
    154$phase = join ('|', keys %scsi_phases);
    155print 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
    162if ($ncr7x0_family) {
    163  $operator = '\||OR|AND|\&|\+';
    164}
    165else {
    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
    205sub 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
    230sub 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;
    240print STDERR "Relative reference $symbol\n" if ($debug);
    241    } elsif ($value =~ /^($identifier)\s*(.*)/) {
    242	$relative = 'ABS';
    243	$symbol = $1;
    244	$value = $2;
    245print STDERR "Absolute reference $symbol\n" if ($debug);
    246    } 
    247
    248    if ($symbol ne '') {
    249print 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})) {
    256print 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
    270sub 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
    320print 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) {
    339print 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
    379while (<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;
    436print 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;
    461print 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;
    485print 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);
    489printf 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) {
    501print 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
    511print STDERR "Forcing register source \n" if ($force && $debug);
    512
    513	    if (!$force && $src =~ 
    514		/^($register)\s+(-|$operator)\s+($value)\s*$/i) {
    515print 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) {
    524print 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) {
    533print 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;
    651print 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	}
    705print 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;
    711print 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; 
    741print 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;
    782if ($#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
    796foreach $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
    811foreach $external (@external) {
    812print 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		
    838print STDERR "referenced external $external at $1\n" if ($debug_external);
    839	}
    840    }
    841}
    842
    843foreach $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
    882open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n";
    883open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n";
    884
    885($_ = $0) =~ s:.*/::;
    886print OUTPUT "/* DO NOT EDIT - Generated automatically by ".$_." */\n";
    887print OUTPUT "static u32 ".$prefix."SCRIPT[] = {\n";
    888$instructions = 0;
    889for ($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}
    916print OUTPUT "};\n\n";
    917
    918foreach $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";
    927printf 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
    939foreach $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#
    948printf OUTPUT "static u32 ".$prefix."LABELPATCHES[] __attribute((unused)) = {\n";
    949for $patch (sort {$a <=> $b} @label_patches) {
    950    printf OUTPUT "\t0x%08x,\n", $patch;
    951}
    952printf OUTPUT "};\n\n";
    953
    954$num_external_patches = 0;
    955printf OUTPUT "static struct {\n\tu32\toffset;\n\tvoid\t\t*address;\n".
    956    "} ".$prefix."EXTERNAL_PATCHES[] __attribute((unused)) = {\n";
    957while ($ident = pop(@external_patches)) {
    958    $off = pop(@external_patches);
    959    printf OUTPUT "\t{0x%08x, &%s},\n", $off, $ident;
    960    ++$num_external_patches;
    961}
    962printf OUTPUT "};\n\n";
    963
    964printf OUTPUT "static u32 ".$prefix."INSTRUCTIONS __attribute((unused))\t= %d;\n", 
    965    $instructions;
    966printf OUTPUT "static u32 ".$prefix."PATCHES __attribute((unused))\t= %d;\n", 
    967    $#label_patches+1;
    968printf OUTPUT "static u32 ".$prefix."EXTERNAL_PATCHES_LEN __attribute((unused))\t= %d;\n",
    969    $num_external_patches;
    970close OUTPUT;
    971close OUTPUTU;