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