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