File:  [Research Unix] / researchv10no / cmd / sml / src / mips / opcodes.nw
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:34 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv10, HEAD
researchv10 Norman

\chapter{Handling the MIPS opcodes}
\section{Introduction}

This file generates the code necessary to handle MIPS instructions
in a natural, mnemonic way from within ML.
All MIPS instructions occupy 32 bits, and since ML has no simple
32~bit data type, we use pairs of integerss to represent MIPS instructions.
A pair [[(hi,lo)]] of 16-bit integers holds the most and least significant
halfwords of the MIPS word.
ML integers are 31 bits, so this is more than adequate.

The biggest hassle in converting between these integer pairs and more
mnemonic representations is that it is too easy to make mistakes
(especially typographical errors) in writing the code.
For that reason, I have added an extra level of indirection to the
whole business by putting all of the instruction descriptions in
tables.
These tables are read by an awk script, which writes two ML files:
{\tt opcodes.sml} and {\tt mipsdecode.sml}.
The {\tt opcodes.sml} file contains the code needed to convert from
a mnemonic like [[add(3,4,9)]] (add the contents of register~3 to
the contents of register~4, placing the result in register~9) to 
the integer pair representation of the actual bits in that add instruction
(in this case [[(137,6176)]]).
The {\tt mipsdecode.sml} file contains a [[decode]] function that converts
from the integer pair representation of instructions to a string
representation.
The string representation is a little hokey at the moment (that is,
it's different from the one used in the MIPS book), but it represents
a nice compromise between being readable and easy to generate.

I have contemplating generating a third file to test the whole
business.
The idea would be to have a function that would write out (to files)
two
parallel representations of the same instruction stream (presumably
one copy of each known instruction).
One representation would be the binary one understood by the MIPS.
The other representation would be a string representation.
We could then use a tool like {\tt gdb} or {\tt adb} to print out
the binary as an instruction sequence (i.e. convert back to
a second string representation) and compare the string representations
to see if they make sense.

\paragraph{Possible bugs}
This code should be gone over with care to make sure that negative
operands (e.g. in [[offset]]) won't break the code.


@
We need a special line in the Makefile to handle this file, since
it writes both an awk program and that program's input.  The input
is in module {\tt @<<opcodes table@>>} so the line is
$$\hbox{[[	$(NOTANGLE) '-Ropcodes table' opcodes.ow > opcodes]]}$$
The input is nothing but a sequence of tables, each labelled, and
processed one after anothing according to the label.
The label is always a single word on a line by itself.
Tables end with blank lines.
@ The opcode-to-pair code is written to the standard output, in 
[[structure Opcodes]].
The pair-to-string code is written to [["mipsdecode.sml"]], in
[[structure MipsDecode]].

We begin by defining and and shift functions.
We make pessimistic assumptions about shifting, trying always to
keep the arguments between 0 and 31 inclusive.
<<BEGIN>>=
print "structure Opcodes = struct"
print "val andb = Bits.andb"
print "fun lshift(op1,amt) = "
print "    if amt<0 then Bits.rshift(op1,0-amt)"
print "    else Bits.lshift(op1,amt)"
print "nonfix sub"	# bug fixes; want [[sub]] to be a MIPS opcode
print "nonfix div"	# bug fixes; want [[div]] to be a MIPS opcode

decode = "mipsdecode.sml";
print "structure MipsDecode = struct" > decode
print "val andb = Bits.andb" > decode
print "fun rshift(op1,amt) = " > decode
print "    if amt<0 then Bits.lshift(op1,0-amt)" > decode
print "    else Bits.rshift(op1,amt)" > decode
<<END>>=
<<write out the definitions of the decoding functions>>
print "end (* Opcodes *)"
print "end (* Decode *)" > decode
@ The sections BEGIN and END are drawn from 
 our universal model of an awk program:
<<*>>=
BEGIN {
  <<BEGIN>>
}
<<functions>>
<<statements>>
END {
  <<END>>
}
@ \section{The opcode tables}
The numeric codes for all the MIPS opcodes are described in three
tables in the MIPS book on page~A-87.
Normal opcodes are six bits, and appear in the [[opcode]] field of the
instruction.
Two opcodes [[special]] and [[bcond]] stand for several instructions.
These instructions are decoded by checking the bit-pattern in the
[[funct]] and [[cond]] fields of the instructions, respectively.

The tables show which opcodes correspond to which bit-patterns.
For example, the [[slti]] corresponds to an [[opcode]] value of octal~12.
The table headed [[opcode]] gives the mnemonics for all six-bit patterns
in the [[opcode]] field.
The [[special]] table shows patterns for the [[funct]] field, used with
the [[special]] opcode.
The [[bcond]] table shows five-bit patterns for the [[cond]] field,
used with the [[bcond]] opcode.
In all tables, stars ([[*]]) stand for unused fields.

Each table is terminated with a blank line.
<<opcodes table>>=
			    opcode
special	bcond	j	jal	beq	bne	blez	bgtz
addi	addiu	slti	sltiu	andi	ori	xori	lui
cop0	cop1	cop2	cop3	*	*	*	*
*	*	*	*	*	*	*	*
lb	lh	lwl	lw	lbu	lhu	lwr	*
sb	sh	swl	sw	*	*	swr	*
lwc0	lwc1	lwc2	lwc3	*	*	*	*
swc0	swc1	swc2	swc3	*	*	*	*

			    special
sll	*	srl	sra	sllv	*	srlv	srav
jr	jalr	*	*	syscall	break	*	*
mfhi	mthi	mflo	mtlo	*	*	*	*
mult	multu	div	divu	*	*	*	*
add	addu	sub	subu	and'	or	xor	nor
*	*	slt	sltu	*	*	*	*
*	*	*	*	*	*	*	*
*	*	*	*	*	*	*	*

			    bcond
bltz	bgez	*	*	*	*	*	*
*	*	*	*	*	*	*	*
bltzal	bgezal	*	*	*	*	*	*
*	*	*	*	*	*	*	*


@ The instructions codes for Coprocessor 1 (floating point)
are takin from page B-28 of the Mips book.
<<opcodes table>>=
			    cop1
add_fmt	sub_fmt	mul_fmt	div_fmt	*	abs_fmt	mov_fmt	neg_fmt
*	*	*	*	*	*	*	*
*	*	*	*	*	*	*	*
*	*	*	*	*	*	*	*
cvt_s	cvt_d	*	*	cvt_w	*	*	*
*	*	*	*	*	*	*	*
c_f	c_un	c_eq	c_ueq	c_olt	c_ult	c_ole	c_ule
c_sf	c_ngle	c_seq	c_ngl	c_lt	c_nge	c_le	c_ngt

@
Now we have to deal with reading these tables, and extracting the
information stored therein.
First of all, for each mnemonic [[$i]] we store the corresponding bit
pattern (as an integer, [[code]]) in the array [[numberof[$i] ]].
Then, we store the type of the mnemonic (ordinary [[OPCODE]], 
[[SPECIAL]], [[BCOND]], of [[COP1]]) in the array [[typeof[$i] ]].
Finally, we store inverse (a map from type and bit pattern to mnemonic)
in the [[opcode]] array.
<<store opcode information>>=
if ($i != "*") {
	numberof[$i] = code
	typeof[$i] = type
	opcode[type,code] = $i
} else {
	opcode[type,code] = "reserved"
}
@ The types are just constants set at the beginning.
<<BEGIN>>=
OPCODE = 1 ; SPECIAL = 2 ; BCOND = 3 ; COP1 = 4
@ We determine the type by scanning the header word that precedes
each table.
Once we see the appropriate table header, we set one of [[opcodes]],
[[specials]], and [[bconds]], so that determining the type is easy:
<<set [[type]]>>=
type = OPCODE * opcodes + SPECIAL * specials + BCOND * bconds + COP1 * cop1s
@ Seeing the right table header causes us to set the right variable.
We also remember the line number, because we use the positions of later
lines to help extract the bit patterns from the table.
<<statements>>=
NF == 1 && $1 == "opcode" {
	startline = NR
	opcodes = 1
	next
}
NF == 1 && $1 == "special" {
	startline = NR
	specials = 1
	next
}
NF == 1 && $1 == "bcond" {
	startline = NR
	bconds = 1
	next
}
NF == 1 && $1 == "cop1" {
	startline = NR
	cop1s = 1
	next
}
@ Any time we see a blank line, that ends the appropriate table.
<<statements>>=
NF == 0 {opcodes = 0; specials = 0; bconds = 0; cop1s = 0
	<<blank line resets>>
}
@ Here is the code that actually extracts the bit patterns from
the opcode tables.
The code is the same for each of the three tables.

The [[insist_fields(8)]] issues an error message and returns false (0)
unless there are exactly 8 fields on the input line.
<<statements>>=
opcodes || specials || bconds || cop1s {
	if (!insist_fields(8)) next
	<<set [[type]]>>
	major = NR - startline - 1		# major octal digit from row
	for (i=1; i<= NF; i++) {
		minor = i-1			# minor octal digit from column
		code = minor + 8 * major
		<<store opcode information>>
	}
}
@ \section{The instruction fields}
Now that we've dealt with the opcodes, we'll handle other fields of
the instruction.
This table tells us the position of each field within the word,
so that if we know a bit-pattern for each field, we can assemble
all the fields into an instruction.

Not all fields are used in all instructions.
Later we'll have a table that indicates exactly which fields are used in
which instructions.
For now, we just list the fields and their positions with the
understanding that some fields will overlap.

The table is taken from the MIPS book, page A-3.
The numbers are the numbers of the starting and ending bit positions,
where 0 is the least and 31 the most significant bit.
The names are exactly those used in the book except [[op']] has been
substituted for [[op]] since [[op]] is a reserved word in ML.

If a field is signed, we put a [[+]]~sign as the first character
of its name.
The sign information is used only in decoding (I think).
<<opcodes table>>=
			fields
op' 26 31
rs 21 25
rt 16 20
+immed 0 15
+offset 0 15
base 21 25
target 0 25
rd 11 15
shamt 6 10
funct 0 5
cond 16 20
<<floating point load/store fields>>
<<floating point computation fields>>

@ From page B-5.  Most fields are the same as the CPU instruction formats.
<<floating point load/store fields>>=
ft 16 20
@ From page B-6.  Many fields are reused from earlier specifications.
The computational instructions all have a one bit in position 25.
Instead of trying to insert special code to handle that, we cheat on
it by making that bit part of the format, and cheating on the format.
Thus:
<<floating point computation fields>>=
fmt 21 25
fs 11 15
fd 6 10
<<write format info>>=
print "val S_fmt = 16+0"
print "val D_fmt = 16+1"
print "val W_fmt = 16+4"

@ The setup for the fields is similar to that used for the opcodes.
<<statements>>=
NF == 1 && $1 == "fields" {
	startline = NR
	fields = 1
	<<write format info>>
	next
}
<<blank line resets>>=
fields = 0
<<statements>>=
fields {
	if (!insist_fields(3)) next
	fieldname = $1;  low = $2; high = $3
	<<look for sign in [[fieldname]] and set [[signed]]>>
	fieldnames[fieldname]= 1	# rememeber all the field names

	<<write to standard output a function to convert bit-pattern to pair>>
	<<write to [[decode]] a function to extract field from pair>>

}
<<look for sign in [[fieldname]] and set [[signed]]>>=
if (substr(fieldname,1,1)=="+") {
	signed = 1
	fieldname = substr(fieldname,2)
} else {
	signed = 0
}
@
The idea is that for each of these fields, we want to write a function
that will take an integer argument and shift it by the right amount.
Since we have to represent the 32-bit quantities as pairs of integers,
we actually use two functions, one for the high half and one for the low.
So, for example, for the [[rd]] field we will produce two function definitions,
[[rdHI]] and [[rdLO]].

The awk function [[function_definition]] is used to compute ML function
definitions.
It takes as arguments the name of the function and the number of arguments
to that function.
The arguments are numbered [[A1]], [[A2]], et cetera.

The functions themselves are all tedious combinations of ands and shifts.
At one time I had convinced myself that this worked.
<<write to standard output a function to convert bit-pattern to pair>>=
if (low >= 16) {
	printf "%s", function_definition(fieldname "LO",1); print "0"
} else {
	printf "%s", function_definition(fieldname "LO",1)
        printf "andb(lshift(A1,%d),65535)\n", low
}
if (high < 16) {
	printf "%s", function_definition(fieldname "HI",1); print "0"
} else {
	printf "%s", function_definition(fieldname "HI",1)
        printf "lshift(A1,%s)\n", mlnumber(low - 16)
}
@ The inverse operation is
to extract a bit pattern from a pair.
We'll want that if we ever care to decode instructions.
This time, the function to extract e.g.\ field [[rd]] from a pair
is the function [[THErd]] applied to that pair.

The functions work first by extracting from the low part, then
from the high part, and adding everything together.
If the field is signed, we make the value negative if it is too high.
<<write to [[decode]] a function to extract field from pair>>=
printf "%s", function_definition("THE" fieldname,2) > decode
if (signed) printf "let val n = " > decode
<<print expression for unsigned value>>
if (signed) {
	printf "in if n < %d then n else n - %d\nend\n",
		2**(high-low), 2**(high-low+1) > decode
}

<<print expression for unsigned value>>=
if (low >= 16) {
	printf "0" > decode
} else {
        printf "andb(rshift(A2,%d),%d)", low,
			(2**(min(15,high)-low+1)-1) > decode
}
printf " + " > decode
if (high < 16) {
	printf "0\n" > decode
} else {
        printf "rshift(andb(A1,%d),%s)\n", (2**(high-16+1)-1),
			mlnumber(low - 16) > decode
}
@ ML uses a strange minus sign ([[~]] instead of [[-]]), 
so we print numbers that might be negative like this:
<<functions>>=
function mlnumber(n, s) {
	if (n<0) s = sprintf("~%d", -n)
	else s = sprintf("%d", n)
	return s
}
@ For reasons best known to its designers, awk has no [[min]] function.
<<functions>>=
function min(x,y){
	if (x<y) return x
	else return y
}
@ \section{The list of instructions and their formats}
This is the section that tells which fields are used in what instructions,
and in what order the fields appear.
The information is from Appendix A
of the MIPS book and should be proofread.

To cut down on the number of ML functions generated, we can comment out
instructions with a [[#]] in the first column.
This means that no code will be generated for the instruction, and
it won't appear in the [[structure Opcodes]].
<<opcodes table>>=
			instructions
add rd rs rt
addi rt rs immed
addiu rt rs immed
addu rd rs rt
and' rd rs rt
andi rt rs immed
beq rs rt offset
bgez rs offset
bgezal rs offset
bgtz rs offset
blez rs offset
bltz rs offset
bltzal rs offset
bne rs rt offset
break
div rs rt
divu rs rt
j target
jal target
jalr rs rd
jr rs
lb rt offset base
lbu rt offset base
lh rt offset base
lb rt offset base
lhu rt offset base
lui rt immed
lw rt offset base
lwl rt offset base
lwr rt offset base
mfhi rd
mflo rd
mthi rs
mtlo rs
mult rs rt
multu rs rt
nor rd rs rt
or rd rs rt
ori rt rs immed
sb rt offset base
sh rt offset base
sll rd rt shamt
sllv rd rt rs
slt rd rs rt
slti rt rs immed
sltiu rt rs immed
sltu rd rs rt
sra rd rt shamt
srav rd rt rs
srl rd rt shamt
srlv rd rt rs
sub rd rs rt
subu rd rs rt
sw rt offset base
swl rt offset base
swr rt offset base
syscall
xor rd rs rt
xori rt rs immed
<<floating point instructions>>


@ We define only those floating point instructions we seem likely to need.
To distinguish them as floating point we append an f to their names.
<<floating point instructions>>=
add_fmt fmt fd fs ft
div_fmt fmt fd fs ft
lwc1 ft offset base
mul_fmt fmt fd fs ft
neg_fmt fmt fd fs
sub_fmt fmt fd fs ft
swc1 ft offset base
c_seq fmt fs ft
c_lt fmt fs ft
@
 Here is a terrible hack to enable us to construct branch on coprocessor~1
true or false.
We will use [[fun bc1f offset = cop1(0,offset)]] and
	[[fun bc1t offset = cop1(1,offset)]].
<<floating point instructions>>=
cop1 rs rt offset
@


@ For each instruction, we define an ML function with the appropriate
number of arguments.
When that function is given an integer in each argument,
it converts the whole thing to one MIPS instruction, represented as an
integer pair.

The implementation is a bit of a grubby mess.
Doing the fields is straightforward enough, but
for each mnemonic we have to do something different based
on its type, because each type of opcode goes in a different
field.
Moreover, for mnemonics of type [[SPECIAL]], [[BCOND]], and [[COP1]] we
have to generate [[special]], [[bcond]], and [[cop1]] in the [[op']] field.
Finally, we have to do it all twice; once for the high order
halfword and once for the low order halfword.
<<compute function that generates this instruction>>=
	printf "%s", function_definition(opname, NF-1)
	printf "("	# open parenthesis for pair
	for (i=2; i<= NF; i++) {
		if (!($i in fieldnames)) <<bad field name>>
		printf "%sHI(A%d)+", $i, i-1
	}
	if (typeof[opname]==OPCODE) {
		printf "op'HI(%d)", numberof[opname]
	} else if (typeof[opname]==SPECIAL) {
		printf "op'HI(%d)+", numberof["special"]
		printf "functHI(%d)", numberof[opname]
	} else if (typeof[opname]==BCOND) {
		printf "op'HI(%d)+", numberof["bcond"]
		printf "condHI(%d)", numberof[opname]
	} else if (typeof[opname]==COP1) {
		printf "op'HI(%d)+", numberof["cop1"]
		printf "functHI(%d)", numberof[opname]
	} else <<bad operator name>>
	printf ", "
	for (i=2; i<= NF; i++) {
		if (!($i in fieldnames)) <<bad field name>>
		printf "%sLO(A%d)+", $i, i-1
	}
	if (typeof[opname]==OPCODE) {
		printf "op'LO(%d)", numberof[opname]
	} else if (typeof[opname]==SPECIAL) {
		printf "op'LO(%d)+", numberof["special"]
		printf "functLO(%d)", numberof[opname]
	} else if (typeof[opname]==BCOND) {
		printf "op'LO(%d)+", numberof["bcond"]
		printf "condLO(%d)", numberof[opname]
	} else if (typeof[opname]==COP1) {
		printf "op'LO(%d)+", numberof["cop1"]
		printf "functLO(%d)", numberof[opname]
	} else <<bad operator name>>
	printf ")\n"
@
Setup is as before.
<<statements>>=
NF == 1 && $1 == "instructions" {
	startline = NR
	instructions = 1
	next
}
<<blank line resets>>=
instructions= 0
<<statements>>=
instructions && $0 !~ /^#/ {
	opname = $1

	<<compute string displayed when this instruction is decoded>>
########	gsub("[^a-z']+"," ")   ### ill-advised

	<<compute function that generates this instruction>>
}

@ \paragraph{Decoding instructions}
When we've decoded an instruction, we have to display some sort of
string representation that tells us what the instruction is.
Ideally we should display either just what the assembler expects,
or perhaps just what dbx displays when asked about actual instructions
in memory images.

For now, we just give the mnemonic for the instruction, followed
by a description of each field (followed by a newline).
The fields are described as name-value pairs.

We rely on the fact that for a field e.g.\ [[rd]], the string
representation of the value of that field is in [[Srd]].
<<compute string displayed when this instruction is decoded>>=
temp = "\"" opname " \""
for (i=2; i<=NF; i++) {
	temp = sprintf( "%s ^ \"%s = \" ^ S%s", temp, $i, $i)
	if (i<NF) temp = sprintf("%s ^ \",\" ", temp)
}
displayof[opname]=temp " ^ \"\\n\""

@ The implementation of the decoding function is split into several parts.
First, we have to be able to extract any field from an instruction.
Then, we have to be able to decode four kinds of opcodes:
[[OPCODE]]s, [[BCOND]]s,  [[SPECIAL]]s, and [[COP1]]s.
The main function is the one that does ordinary opcodes.
The others are auxiliary.
<<write out the definitions of the decoding functions>>=
printf "%s", function_definition("decode",2) > decode
print "let" > decode
  <<write definitions of integer and string representations of each field>>
  <<write expression that decodes the [[funct]] field for [[special]]s>>
  <<write expression that decodes the [[cond]] field for [[bcond]]s>>
  <<write expression that decodes the [[funct]] field for [[cop1]]s>>
print "in" > decode
  <<write [[case]] expression that decodes the [[op']] field for each instruction>>
print "end" > decode
@ We give each field its own name for an integer version, and its name
preceded by [[S]] for its string version.
These values are all computed just once, from the arguments to the
enclosing function ([[decode]]).
<<write definitions of integer and string representations of each field>>=
for (f in fieldnames) {
	printf "val %s = THE%s(A1,A2)\n", f, f  > decode
	printf "val S%s = Integer.makestring %s\n", f, f  > decode
}
@ The next three functions are very much of a piece.
They are just enormous [[case]] expressions that match up integers
(bit patterns) to strings.
The fundamental operation is printing out a decimal value and a string
for each opcode:
<<if [[name]] is known, display a case for it>>=
if (name != ""  && name != "reserved") {
	<<print space or bar ([[|]])>>
	disp = displayof[name]
	if (disp=="") disp="\"" name "(??? unknown format???)\\n\""
	printf "%d => %s\n", code, disp > decode
}
@ Cases must be separated by vertical bars.
We do the separation by putting a vertical bar before each case except
the first.
We use a hack to discover the first; we assume that code~0 is always
defined, and so it will always be the first.
<<print space or bar ([[|]])>>=
if (code!=0) printf " | "  > decode # hack but it works
else printf "   " > decode
<<write expression that decodes the [[funct]] field for [[special]]s>>=
print "val do_special ="  > decode
print "(case funct of" > decode
for (code=0; code<256; code++) {
	name = opcode[SPECIAL,code]
	<<if [[name]] is known, display a case for it>>
}
printf " | _ => \"unknown special\\n\"\n" > decode
print "   ) " > decode
<<write expression that decodes the [[cond]] field for [[bcond]]s>>=
print "val do_bcond =" > decode
print "(case cond of" > decode
for (code=0; code<256; code++) {
	name = opcode[BCOND,code]
	<<if [[name]] is known, display a case for it>>
}
printf " | _ => \"unknown bcond\\n\"\n" > decode
print "   ) " > decode
<<write expression that decodes the [[funct]] field for [[cop1]]s>>=
print "val do_cop1 =" > decode
print "(case funct of" > decode
for (code=0; code<256; code++) {
	name = opcode[COP1,code]
	<<if [[name]] is known, display a case for it>>
}
printf " | _ => \"unknown cop1\\n\"\n" > decode
print "   ) " > decode
@ The major expression is a little more complicated, because it has to
check for [[special]], [[bcond]], and [[cop1]] and handle those separately.
<<write [[case]] expression that decodes the [[op']] field for each instruction>>=
print "(case op' of" > decode
for (code=0; code<256; code++) {
	name = opcode[OPCODE,code]
	if (name=="special") {
		<<print space or bar ([[|]])>>
		printf "%d => %s\n", code, "do_special" > decode
	} else if (name=="bcond") {
		<<print space or bar ([[|]])>>
		printf "%d => %s\n", code, "do_bcond" > decode
	} else if (name=="cop1") {
		<<print space or bar ([[|]])>>
		printf "%d => %s\n", code, "do_cop1" > decode
	} else <<if [[name]] is known, display a case for it>>
}
printf " | _ => \"unknown opcode\\n\"\n" > decode
print "   ) " > decode
@ \section{testing}
One day someone will have to modify the instruction handler so that
it generates a test invocation of each instruction.
Then the results can be handed to something like adb or dbx and we can
see whether the system agrees with us about what we're generating.

@ \section{Defining ML functions}
The awk function [[function_definition]] is used to
come up with ML function definitions.
It takes as arguments the name of the function and the number of arguments
to that function, and returns a string containing the initial part of
the function definition.
Writing an expression following that string will result in a complete
ML function.

If we ever wanted to define these things as C preprocessor macros instead,
we could do it by substituting [[macro_definition]].
I'm not sure it would ever make sense to do so, but I'm leaving the
code here anyway.
<<functions>>=
function function_definition(name, argc,  i, temp) {
	if (argc==0) {
		temp = sprintf("val %s = ", name)
	} else {
		temp = sprintf( "fun %s(", name)
		for (i=1; i< argc; i++) temp = sprintf("%sA%d,", temp,i)
		temp = sprintf( "%sA%d) = ", temp, argc)
	}
	return temp
}
<<useless functions>>=
function macro_definition(name, argc,  i, temp) {
	if (argc==0) {
		temp = sprintf("#define %s ", name)
	} else {
		temp = sprintf( "#define %s(", name)
		for (i=1; i< argc; i++) temp = sprintf("%sA%d,", temp,i)
		temp = sprintf( "%sA%d) ", temp, argc)
	}
	return temp
}
@ \section{Handling error conditions}
Here are a bunch of uninteresting functions and modules
that handle error conditions.
<<bad operator name>>=
{
	print "unknown opcode", opname, "on line", NR > stderr
	next
}
<<bad field name>>=
{
	print "unknown field", $i, "on line", NR > stderr
	next
}
<<BEGIN>>=
stderr="/dev/tty"
<<functions>>=
function insist_fields(n) {
	if (NF != n) {
		print "Must have", n, "fields on line",NR ":", $0 > stderr
		return 0
	} else {
		return 1
	}
}
@ \section{Leftover junk}
Like a pack rat, I never throw out anything that might be useful again later.
<<junk>>=
function thetype(n) {
	if (n==OPCODE) return "OPCODE"
	else if (n==SPECIAL) return "SPECIAL"
	else if (n==BCOND) return "BCOND"
	else if (n==COP1) return "COP1"
	else return "BADTYPE"
}
<<decoding junk>>=
for (f in fieldnames) {
	printf "^ \"\\n%s = \" ^ Integer.makestring %s\n",f,f > decode
}
printf "^\"\\n\"\n" > decode

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.