|
|
1.1 ! root 1: \chapter{Handling the MIPS opcodes} ! 2: \section{Introduction} ! 3: ! 4: This file generates the code necessary to handle MIPS instructions ! 5: in a natural, mnemonic way from within ML. ! 6: All MIPS instructions occupy 32 bits, and since ML has no simple ! 7: 32~bit data type, we use pairs of integerss to represent MIPS instructions. ! 8: A pair [[(hi,lo)]] of 16-bit integers holds the most and least significant ! 9: halfwords of the MIPS word. ! 10: ML integers are 31 bits, so this is more than adequate. ! 11: ! 12: The biggest hassle in converting between these integer pairs and more ! 13: mnemonic representations is that it is too easy to make mistakes ! 14: (especially typographical errors) in writing the code. ! 15: For that reason, I have added an extra level of indirection to the ! 16: whole business by putting all of the instruction descriptions in ! 17: tables. ! 18: These tables are read by an awk script, which writes two ML files: ! 19: {\tt opcodes.sml} and {\tt mipsdecode.sml}. ! 20: The {\tt opcodes.sml} file contains the code needed to convert from ! 21: a mnemonic like [[add(3,4,9)]] (add the contents of register~3 to ! 22: the contents of register~4, placing the result in register~9) to ! 23: the integer pair representation of the actual bits in that add instruction ! 24: (in this case [[(137,6176)]]). ! 25: The {\tt mipsdecode.sml} file contains a [[decode]] function that converts ! 26: from the integer pair representation of instructions to a string ! 27: representation. ! 28: The string representation is a little hokey at the moment (that is, ! 29: it's different from the one used in the MIPS book), but it represents ! 30: a nice compromise between being readable and easy to generate. ! 31: ! 32: I have contemplating generating a third file to test the whole ! 33: business. ! 34: The idea would be to have a function that would write out (to files) ! 35: two ! 36: parallel representations of the same instruction stream (presumably ! 37: one copy of each known instruction). ! 38: One representation would be the binary one understood by the MIPS. ! 39: The other representation would be a string representation. ! 40: We could then use a tool like {\tt gdb} or {\tt adb} to print out ! 41: the binary as an instruction sequence (i.e. convert back to ! 42: a second string representation) and compare the string representations ! 43: to see if they make sense. ! 44: ! 45: \paragraph{Possible bugs} ! 46: This code should be gone over with care to make sure that negative ! 47: operands (e.g. in [[offset]]) won't break the code. ! 48: ! 49: ! 50: @ ! 51: We need a special line in the Makefile to handle this file, since ! 52: it writes both an awk program and that program's input. The input ! 53: is in module {\tt @<<opcodes table@>>} so the line is ! 54: $$\hbox{[[ $(NOTANGLE) '-Ropcodes table' opcodes.ow > opcodes]]}$$ ! 55: The input is nothing but a sequence of tables, each labelled, and ! 56: processed one after anothing according to the label. ! 57: The label is always a single word on a line by itself. ! 58: Tables end with blank lines. ! 59: @ The opcode-to-pair code is written to the standard output, in ! 60: [[structure Opcodes]]. ! 61: The pair-to-string code is written to [["mipsdecode.sml"]], in ! 62: [[structure MipsDecode]]. ! 63: ! 64: We begin by defining and and shift functions. ! 65: We make pessimistic assumptions about shifting, trying always to ! 66: keep the arguments between 0 and 31 inclusive. ! 67: <<BEGIN>>= ! 68: print "structure Opcodes = struct" ! 69: print "val andb = Bits.andb" ! 70: print "fun lshift(op1,amt) = " ! 71: print " if amt<0 then Bits.rshift(op1,0-amt)" ! 72: print " else Bits.lshift(op1,amt)" ! 73: print "nonfix sub" # bug fixes; want [[sub]] to be a MIPS opcode ! 74: print "nonfix div" # bug fixes; want [[div]] to be a MIPS opcode ! 75: ! 76: decode = "mipsdecode.sml"; ! 77: print "structure MipsDecode = struct" > decode ! 78: print "val andb = Bits.andb" > decode ! 79: print "fun rshift(op1,amt) = " > decode ! 80: print " if amt<0 then Bits.lshift(op1,0-amt)" > decode ! 81: print " else Bits.rshift(op1,amt)" > decode ! 82: <<END>>= ! 83: <<write out the definitions of the decoding functions>> ! 84: print "end (* Opcodes *)" ! 85: print "end (* Decode *)" > decode ! 86: @ The sections BEGIN and END are drawn from ! 87: our universal model of an awk program: ! 88: <<*>>= ! 89: BEGIN { ! 90: <<BEGIN>> ! 91: } ! 92: <<functions>> ! 93: <<statements>> ! 94: END { ! 95: <<END>> ! 96: } ! 97: @ \section{The opcode tables} ! 98: The numeric codes for all the MIPS opcodes are described in three ! 99: tables in the MIPS book on page~A-87. ! 100: Normal opcodes are six bits, and appear in the [[opcode]] field of the ! 101: instruction. ! 102: Two opcodes [[special]] and [[bcond]] stand for several instructions. ! 103: These instructions are decoded by checking the bit-pattern in the ! 104: [[funct]] and [[cond]] fields of the instructions, respectively. ! 105: ! 106: The tables show which opcodes correspond to which bit-patterns. ! 107: For example, the [[slti]] corresponds to an [[opcode]] value of octal~12. ! 108: The table headed [[opcode]] gives the mnemonics for all six-bit patterns ! 109: in the [[opcode]] field. ! 110: The [[special]] table shows patterns for the [[funct]] field, used with ! 111: the [[special]] opcode. ! 112: The [[bcond]] table shows five-bit patterns for the [[cond]] field, ! 113: used with the [[bcond]] opcode. ! 114: In all tables, stars ([[*]]) stand for unused fields. ! 115: ! 116: Each table is terminated with a blank line. ! 117: <<opcodes table>>= ! 118: opcode ! 119: special bcond j jal beq bne blez bgtz ! 120: addi addiu slti sltiu andi ori xori lui ! 121: cop0 cop1 cop2 cop3 * * * * ! 122: * * * * * * * * ! 123: lb lh lwl lw lbu lhu lwr * ! 124: sb sh swl sw * * swr * ! 125: lwc0 lwc1 lwc2 lwc3 * * * * ! 126: swc0 swc1 swc2 swc3 * * * * ! 127: ! 128: special ! 129: sll * srl sra sllv * srlv srav ! 130: jr jalr * * syscall break * * ! 131: mfhi mthi mflo mtlo * * * * ! 132: mult multu div divu * * * * ! 133: add addu sub subu and' or xor nor ! 134: * * slt sltu * * * * ! 135: * * * * * * * * ! 136: * * * * * * * * ! 137: ! 138: bcond ! 139: bltz bgez * * * * * * ! 140: * * * * * * * * ! 141: bltzal bgezal * * * * * * ! 142: * * * * * * * * ! 143: ! 144: ! 145: @ The instructions codes for Coprocessor 1 (floating point) ! 146: are takin from page B-28 of the Mips book. ! 147: <<opcodes table>>= ! 148: cop1 ! 149: add_fmt sub_fmt mul_fmt div_fmt * abs_fmt mov_fmt neg_fmt ! 150: * * * * * * * * ! 151: * * * * * * * * ! 152: * * * * * * * * ! 153: cvt_s cvt_d * * cvt_w * * * ! 154: * * * * * * * * ! 155: c_f c_un c_eq c_ueq c_olt c_ult c_ole c_ule ! 156: c_sf c_ngle c_seq c_ngl c_lt c_nge c_le c_ngt ! 157: ! 158: @ ! 159: Now we have to deal with reading these tables, and extracting the ! 160: information stored therein. ! 161: First of all, for each mnemonic [[$i]] we store the corresponding bit ! 162: pattern (as an integer, [[code]]) in the array [[numberof[$i] ]]. ! 163: Then, we store the type of the mnemonic (ordinary [[OPCODE]], ! 164: [[SPECIAL]], [[BCOND]], of [[COP1]]) in the array [[typeof[$i] ]]. ! 165: Finally, we store inverse (a map from type and bit pattern to mnemonic) ! 166: in the [[opcode]] array. ! 167: <<store opcode information>>= ! 168: if ($i != "*") { ! 169: numberof[$i] = code ! 170: typeof[$i] = type ! 171: opcode[type,code] = $i ! 172: } else { ! 173: opcode[type,code] = "reserved" ! 174: } ! 175: @ The types are just constants set at the beginning. ! 176: <<BEGIN>>= ! 177: OPCODE = 1 ; SPECIAL = 2 ; BCOND = 3 ; COP1 = 4 ! 178: @ We determine the type by scanning the header word that precedes ! 179: each table. ! 180: Once we see the appropriate table header, we set one of [[opcodes]], ! 181: [[specials]], and [[bconds]], so that determining the type is easy: ! 182: <<set [[type]]>>= ! 183: type = OPCODE * opcodes + SPECIAL * specials + BCOND * bconds + COP1 * cop1s ! 184: @ Seeing the right table header causes us to set the right variable. ! 185: We also remember the line number, because we use the positions of later ! 186: lines to help extract the bit patterns from the table. ! 187: <<statements>>= ! 188: NF == 1 && $1 == "opcode" { ! 189: startline = NR ! 190: opcodes = 1 ! 191: next ! 192: } ! 193: NF == 1 && $1 == "special" { ! 194: startline = NR ! 195: specials = 1 ! 196: next ! 197: } ! 198: NF == 1 && $1 == "bcond" { ! 199: startline = NR ! 200: bconds = 1 ! 201: next ! 202: } ! 203: NF == 1 && $1 == "cop1" { ! 204: startline = NR ! 205: cop1s = 1 ! 206: next ! 207: } ! 208: @ Any time we see a blank line, that ends the appropriate table. ! 209: <<statements>>= ! 210: NF == 0 {opcodes = 0; specials = 0; bconds = 0; cop1s = 0 ! 211: <<blank line resets>> ! 212: } ! 213: @ Here is the code that actually extracts the bit patterns from ! 214: the opcode tables. ! 215: The code is the same for each of the three tables. ! 216: ! 217: The [[insist_fields(8)]] issues an error message and returns false (0) ! 218: unless there are exactly 8 fields on the input line. ! 219: <<statements>>= ! 220: opcodes || specials || bconds || cop1s { ! 221: if (!insist_fields(8)) next ! 222: <<set [[type]]>> ! 223: major = NR - startline - 1 # major octal digit from row ! 224: for (i=1; i<= NF; i++) { ! 225: minor = i-1 # minor octal digit from column ! 226: code = minor + 8 * major ! 227: <<store opcode information>> ! 228: } ! 229: } ! 230: @ \section{The instruction fields} ! 231: Now that we've dealt with the opcodes, we'll handle other fields of ! 232: the instruction. ! 233: This table tells us the position of each field within the word, ! 234: so that if we know a bit-pattern for each field, we can assemble ! 235: all the fields into an instruction. ! 236: ! 237: Not all fields are used in all instructions. ! 238: Later we'll have a table that indicates exactly which fields are used in ! 239: which instructions. ! 240: For now, we just list the fields and their positions with the ! 241: understanding that some fields will overlap. ! 242: ! 243: The table is taken from the MIPS book, page A-3. ! 244: The numbers are the numbers of the starting and ending bit positions, ! 245: where 0 is the least and 31 the most significant bit. ! 246: The names are exactly those used in the book except [[op']] has been ! 247: substituted for [[op]] since [[op]] is a reserved word in ML. ! 248: ! 249: If a field is signed, we put a [[+]]~sign as the first character ! 250: of its name. ! 251: The sign information is used only in decoding (I think). ! 252: <<opcodes table>>= ! 253: fields ! 254: op' 26 31 ! 255: rs 21 25 ! 256: rt 16 20 ! 257: +immed 0 15 ! 258: +offset 0 15 ! 259: base 21 25 ! 260: target 0 25 ! 261: rd 11 15 ! 262: shamt 6 10 ! 263: funct 0 5 ! 264: cond 16 20 ! 265: <<floating point load/store fields>> ! 266: <<floating point computation fields>> ! 267: ! 268: @ From page B-5. Most fields are the same as the CPU instruction formats. ! 269: <<floating point load/store fields>>= ! 270: ft 16 20 ! 271: @ From page B-6. Many fields are reused from earlier specifications. ! 272: The computational instructions all have a one bit in position 25. ! 273: Instead of trying to insert special code to handle that, we cheat on ! 274: it by making that bit part of the format, and cheating on the format. ! 275: Thus: ! 276: <<floating point computation fields>>= ! 277: fmt 21 25 ! 278: fs 11 15 ! 279: fd 6 10 ! 280: <<write format info>>= ! 281: print "val S_fmt = 16+0" ! 282: print "val D_fmt = 16+1" ! 283: print "val W_fmt = 16+4" ! 284: ! 285: @ The setup for the fields is similar to that used for the opcodes. ! 286: <<statements>>= ! 287: NF == 1 && $1 == "fields" { ! 288: startline = NR ! 289: fields = 1 ! 290: <<write format info>> ! 291: next ! 292: } ! 293: <<blank line resets>>= ! 294: fields = 0 ! 295: <<statements>>= ! 296: fields { ! 297: if (!insist_fields(3)) next ! 298: fieldname = $1; low = $2; high = $3 ! 299: <<look for sign in [[fieldname]] and set [[signed]]>> ! 300: fieldnames[fieldname]= 1 # rememeber all the field names ! 301: ! 302: <<write to standard output a function to convert bit-pattern to pair>> ! 303: <<write to [[decode]] a function to extract field from pair>> ! 304: ! 305: } ! 306: <<look for sign in [[fieldname]] and set [[signed]]>>= ! 307: if (substr(fieldname,1,1)=="+") { ! 308: signed = 1 ! 309: fieldname = substr(fieldname,2) ! 310: } else { ! 311: signed = 0 ! 312: } ! 313: @ ! 314: The idea is that for each of these fields, we want to write a function ! 315: that will take an integer argument and shift it by the right amount. ! 316: Since we have to represent the 32-bit quantities as pairs of integers, ! 317: we actually use two functions, one for the high half and one for the low. ! 318: So, for example, for the [[rd]] field we will produce two function definitions, ! 319: [[rdHI]] and [[rdLO]]. ! 320: ! 321: The awk function [[function_definition]] is used to compute ML function ! 322: definitions. ! 323: It takes as arguments the name of the function and the number of arguments ! 324: to that function. ! 325: The arguments are numbered [[A1]], [[A2]], et cetera. ! 326: ! 327: The functions themselves are all tedious combinations of ands and shifts. ! 328: At one time I had convinced myself that this worked. ! 329: <<write to standard output a function to convert bit-pattern to pair>>= ! 330: if (low >= 16) { ! 331: printf "%s", function_definition(fieldname "LO",1); print "0" ! 332: } else { ! 333: printf "%s", function_definition(fieldname "LO",1) ! 334: printf "andb(lshift(A1,%d),65535)\n", low ! 335: } ! 336: if (high < 16) { ! 337: printf "%s", function_definition(fieldname "HI",1); print "0" ! 338: } else { ! 339: printf "%s", function_definition(fieldname "HI",1) ! 340: printf "lshift(A1,%s)\n", mlnumber(low - 16) ! 341: } ! 342: @ The inverse operation is ! 343: to extract a bit pattern from a pair. ! 344: We'll want that if we ever care to decode instructions. ! 345: This time, the function to extract e.g.\ field [[rd]] from a pair ! 346: is the function [[THErd]] applied to that pair. ! 347: ! 348: The functions work first by extracting from the low part, then ! 349: from the high part, and adding everything together. ! 350: If the field is signed, we make the value negative if it is too high. ! 351: <<write to [[decode]] a function to extract field from pair>>= ! 352: printf "%s", function_definition("THE" fieldname,2) > decode ! 353: if (signed) printf "let val n = " > decode ! 354: <<print expression for unsigned value>> ! 355: if (signed) { ! 356: printf "in if n < %d then n else n - %d\nend\n", ! 357: 2**(high-low), 2**(high-low+1) > decode ! 358: } ! 359: ! 360: <<print expression for unsigned value>>= ! 361: if (low >= 16) { ! 362: printf "0" > decode ! 363: } else { ! 364: printf "andb(rshift(A2,%d),%d)", low, ! 365: (2**(min(15,high)-low+1)-1) > decode ! 366: } ! 367: printf " + " > decode ! 368: if (high < 16) { ! 369: printf "0\n" > decode ! 370: } else { ! 371: printf "rshift(andb(A1,%d),%s)\n", (2**(high-16+1)-1), ! 372: mlnumber(low - 16) > decode ! 373: } ! 374: @ ML uses a strange minus sign ([[~]] instead of [[-]]), ! 375: so we print numbers that might be negative like this: ! 376: <<functions>>= ! 377: function mlnumber(n, s) { ! 378: if (n<0) s = sprintf("~%d", -n) ! 379: else s = sprintf("%d", n) ! 380: return s ! 381: } ! 382: @ For reasons best known to its designers, awk has no [[min]] function. ! 383: <<functions>>= ! 384: function min(x,y){ ! 385: if (x<y) return x ! 386: else return y ! 387: } ! 388: @ \section{The list of instructions and their formats} ! 389: This is the section that tells which fields are used in what instructions, ! 390: and in what order the fields appear. ! 391: The information is from Appendix A ! 392: of the MIPS book and should be proofread. ! 393: ! 394: To cut down on the number of ML functions generated, we can comment out ! 395: instructions with a [[#]] in the first column. ! 396: This means that no code will be generated for the instruction, and ! 397: it won't appear in the [[structure Opcodes]]. ! 398: <<opcodes table>>= ! 399: instructions ! 400: add rd rs rt ! 401: addi rt rs immed ! 402: addiu rt rs immed ! 403: addu rd rs rt ! 404: and' rd rs rt ! 405: andi rt rs immed ! 406: beq rs rt offset ! 407: bgez rs offset ! 408: bgezal rs offset ! 409: bgtz rs offset ! 410: blez rs offset ! 411: bltz rs offset ! 412: bltzal rs offset ! 413: bne rs rt offset ! 414: break ! 415: div rs rt ! 416: divu rs rt ! 417: j target ! 418: jal target ! 419: jalr rs rd ! 420: jr rs ! 421: lb rt offset base ! 422: lbu rt offset base ! 423: lh rt offset base ! 424: lb rt offset base ! 425: lhu rt offset base ! 426: lui rt immed ! 427: lw rt offset base ! 428: lwl rt offset base ! 429: lwr rt offset base ! 430: mfhi rd ! 431: mflo rd ! 432: mthi rs ! 433: mtlo rs ! 434: mult rs rt ! 435: multu rs rt ! 436: nor rd rs rt ! 437: or rd rs rt ! 438: ori rt rs immed ! 439: sb rt offset base ! 440: sh rt offset base ! 441: sll rd rt shamt ! 442: sllv rd rt rs ! 443: slt rd rs rt ! 444: slti rt rs immed ! 445: sltiu rt rs immed ! 446: sltu rd rs rt ! 447: sra rd rt shamt ! 448: srav rd rt rs ! 449: srl rd rt shamt ! 450: srlv rd rt rs ! 451: sub rd rs rt ! 452: subu rd rs rt ! 453: sw rt offset base ! 454: swl rt offset base ! 455: swr rt offset base ! 456: syscall ! 457: xor rd rs rt ! 458: xori rt rs immed ! 459: <<floating point instructions>> ! 460: ! 461: ! 462: @ We define only those floating point instructions we seem likely to need. ! 463: To distinguish them as floating point we append an f to their names. ! 464: <<floating point instructions>>= ! 465: add_fmt fmt fd fs ft ! 466: div_fmt fmt fd fs ft ! 467: lwc1 ft offset base ! 468: mul_fmt fmt fd fs ft ! 469: neg_fmt fmt fd fs ! 470: sub_fmt fmt fd fs ft ! 471: swc1 ft offset base ! 472: c_seq fmt fs ft ! 473: c_lt fmt fs ft ! 474: @ ! 475: Here is a terrible hack to enable us to construct branch on coprocessor~1 ! 476: true or false. ! 477: We will use [[fun bc1f offset = cop1(0,offset)]] and ! 478: [[fun bc1t offset = cop1(1,offset)]]. ! 479: <<floating point instructions>>= ! 480: cop1 rs rt offset ! 481: @ ! 482: ! 483: ! 484: @ For each instruction, we define an ML function with the appropriate ! 485: number of arguments. ! 486: When that function is given an integer in each argument, ! 487: it converts the whole thing to one MIPS instruction, represented as an ! 488: integer pair. ! 489: ! 490: The implementation is a bit of a grubby mess. ! 491: Doing the fields is straightforward enough, but ! 492: for each mnemonic we have to do something different based ! 493: on its type, because each type of opcode goes in a different ! 494: field. ! 495: Moreover, for mnemonics of type [[SPECIAL]], [[BCOND]], and [[COP1]] we ! 496: have to generate [[special]], [[bcond]], and [[cop1]] in the [[op']] field. ! 497: Finally, we have to do it all twice; once for the high order ! 498: halfword and once for the low order halfword. ! 499: <<compute function that generates this instruction>>= ! 500: printf "%s", function_definition(opname, NF-1) ! 501: printf "(" # open parenthesis for pair ! 502: for (i=2; i<= NF; i++) { ! 503: if (!($i in fieldnames)) <<bad field name>> ! 504: printf "%sHI(A%d)+", $i, i-1 ! 505: } ! 506: if (typeof[opname]==OPCODE) { ! 507: printf "op'HI(%d)", numberof[opname] ! 508: } else if (typeof[opname]==SPECIAL) { ! 509: printf "op'HI(%d)+", numberof["special"] ! 510: printf "functHI(%d)", numberof[opname] ! 511: } else if (typeof[opname]==BCOND) { ! 512: printf "op'HI(%d)+", numberof["bcond"] ! 513: printf "condHI(%d)", numberof[opname] ! 514: } else if (typeof[opname]==COP1) { ! 515: printf "op'HI(%d)+", numberof["cop1"] ! 516: printf "functHI(%d)", numberof[opname] ! 517: } else <<bad operator name>> ! 518: printf ", " ! 519: for (i=2; i<= NF; i++) { ! 520: if (!($i in fieldnames)) <<bad field name>> ! 521: printf "%sLO(A%d)+", $i, i-1 ! 522: } ! 523: if (typeof[opname]==OPCODE) { ! 524: printf "op'LO(%d)", numberof[opname] ! 525: } else if (typeof[opname]==SPECIAL) { ! 526: printf "op'LO(%d)+", numberof["special"] ! 527: printf "functLO(%d)", numberof[opname] ! 528: } else if (typeof[opname]==BCOND) { ! 529: printf "op'LO(%d)+", numberof["bcond"] ! 530: printf "condLO(%d)", numberof[opname] ! 531: } else if (typeof[opname]==COP1) { ! 532: printf "op'LO(%d)+", numberof["cop1"] ! 533: printf "functLO(%d)", numberof[opname] ! 534: } else <<bad operator name>> ! 535: printf ")\n" ! 536: @ ! 537: Setup is as before. ! 538: <<statements>>= ! 539: NF == 1 && $1 == "instructions" { ! 540: startline = NR ! 541: instructions = 1 ! 542: next ! 543: } ! 544: <<blank line resets>>= ! 545: instructions= 0 ! 546: <<statements>>= ! 547: instructions && $0 !~ /^#/ { ! 548: opname = $1 ! 549: ! 550: <<compute string displayed when this instruction is decoded>> ! 551: ######## gsub("[^a-z']+"," ") ### ill-advised ! 552: ! 553: <<compute function that generates this instruction>> ! 554: } ! 555: ! 556: @ \paragraph{Decoding instructions} ! 557: When we've decoded an instruction, we have to display some sort of ! 558: string representation that tells us what the instruction is. ! 559: Ideally we should display either just what the assembler expects, ! 560: or perhaps just what dbx displays when asked about actual instructions ! 561: in memory images. ! 562: ! 563: For now, we just give the mnemonic for the instruction, followed ! 564: by a description of each field (followed by a newline). ! 565: The fields are described as name-value pairs. ! 566: ! 567: We rely on the fact that for a field e.g.\ [[rd]], the string ! 568: representation of the value of that field is in [[Srd]]. ! 569: <<compute string displayed when this instruction is decoded>>= ! 570: temp = "\"" opname " \"" ! 571: for (i=2; i<=NF; i++) { ! 572: temp = sprintf( "%s ^ \"%s = \" ^ S%s", temp, $i, $i) ! 573: if (i<NF) temp = sprintf("%s ^ \",\" ", temp) ! 574: } ! 575: displayof[opname]=temp " ^ \"\\n\"" ! 576: ! 577: @ The implementation of the decoding function is split into several parts. ! 578: First, we have to be able to extract any field from an instruction. ! 579: Then, we have to be able to decode four kinds of opcodes: ! 580: [[OPCODE]]s, [[BCOND]]s, [[SPECIAL]]s, and [[COP1]]s. ! 581: The main function is the one that does ordinary opcodes. ! 582: The others are auxiliary. ! 583: <<write out the definitions of the decoding functions>>= ! 584: printf "%s", function_definition("decode",2) > decode ! 585: print "let" > decode ! 586: <<write definitions of integer and string representations of each field>> ! 587: <<write expression that decodes the [[funct]] field for [[special]]s>> ! 588: <<write expression that decodes the [[cond]] field for [[bcond]]s>> ! 589: <<write expression that decodes the [[funct]] field for [[cop1]]s>> ! 590: print "in" > decode ! 591: <<write [[case]] expression that decodes the [[op']] field for each instruction>> ! 592: print "end" > decode ! 593: @ We give each field its own name for an integer version, and its name ! 594: preceded by [[S]] for its string version. ! 595: These values are all computed just once, from the arguments to the ! 596: enclosing function ([[decode]]). ! 597: <<write definitions of integer and string representations of each field>>= ! 598: for (f in fieldnames) { ! 599: printf "val %s = THE%s(A1,A2)\n", f, f > decode ! 600: printf "val S%s = Integer.makestring %s\n", f, f > decode ! 601: } ! 602: @ The next three functions are very much of a piece. ! 603: They are just enormous [[case]] expressions that match up integers ! 604: (bit patterns) to strings. ! 605: The fundamental operation is printing out a decimal value and a string ! 606: for each opcode: ! 607: <<if [[name]] is known, display a case for it>>= ! 608: if (name != "" && name != "reserved") { ! 609: <<print space or bar ([[|]])>> ! 610: disp = displayof[name] ! 611: if (disp=="") disp="\"" name "(??? unknown format???)\\n\"" ! 612: printf "%d => %s\n", code, disp > decode ! 613: } ! 614: @ Cases must be separated by vertical bars. ! 615: We do the separation by putting a vertical bar before each case except ! 616: the first. ! 617: We use a hack to discover the first; we assume that code~0 is always ! 618: defined, and so it will always be the first. ! 619: <<print space or bar ([[|]])>>= ! 620: if (code!=0) printf " | " > decode # hack but it works ! 621: else printf " " > decode ! 622: <<write expression that decodes the [[funct]] field for [[special]]s>>= ! 623: print "val do_special =" > decode ! 624: print "(case funct of" > decode ! 625: for (code=0; code<256; code++) { ! 626: name = opcode[SPECIAL,code] ! 627: <<if [[name]] is known, display a case for it>> ! 628: } ! 629: printf " | _ => \"unknown special\\n\"\n" > decode ! 630: print " ) " > decode ! 631: <<write expression that decodes the [[cond]] field for [[bcond]]s>>= ! 632: print "val do_bcond =" > decode ! 633: print "(case cond of" > decode ! 634: for (code=0; code<256; code++) { ! 635: name = opcode[BCOND,code] ! 636: <<if [[name]] is known, display a case for it>> ! 637: } ! 638: printf " | _ => \"unknown bcond\\n\"\n" > decode ! 639: print " ) " > decode ! 640: <<write expression that decodes the [[funct]] field for [[cop1]]s>>= ! 641: print "val do_cop1 =" > decode ! 642: print "(case funct of" > decode ! 643: for (code=0; code<256; code++) { ! 644: name = opcode[COP1,code] ! 645: <<if [[name]] is known, display a case for it>> ! 646: } ! 647: printf " | _ => \"unknown cop1\\n\"\n" > decode ! 648: print " ) " > decode ! 649: @ The major expression is a little more complicated, because it has to ! 650: check for [[special]], [[bcond]], and [[cop1]] and handle those separately. ! 651: <<write [[case]] expression that decodes the [[op']] field for each instruction>>= ! 652: print "(case op' of" > decode ! 653: for (code=0; code<256; code++) { ! 654: name = opcode[OPCODE,code] ! 655: if (name=="special") { ! 656: <<print space or bar ([[|]])>> ! 657: printf "%d => %s\n", code, "do_special" > decode ! 658: } else if (name=="bcond") { ! 659: <<print space or bar ([[|]])>> ! 660: printf "%d => %s\n", code, "do_bcond" > decode ! 661: } else if (name=="cop1") { ! 662: <<print space or bar ([[|]])>> ! 663: printf "%d => %s\n", code, "do_cop1" > decode ! 664: } else <<if [[name]] is known, display a case for it>> ! 665: } ! 666: printf " | _ => \"unknown opcode\\n\"\n" > decode ! 667: print " ) " > decode ! 668: @ \section{testing} ! 669: One day someone will have to modify the instruction handler so that ! 670: it generates a test invocation of each instruction. ! 671: Then the results can be handed to something like adb or dbx and we can ! 672: see whether the system agrees with us about what we're generating. ! 673: ! 674: @ \section{Defining ML functions} ! 675: The awk function [[function_definition]] is used to ! 676: come up with ML function definitions. ! 677: It takes as arguments the name of the function and the number of arguments ! 678: to that function, and returns a string containing the initial part of ! 679: the function definition. ! 680: Writing an expression following that string will result in a complete ! 681: ML function. ! 682: ! 683: If we ever wanted to define these things as C preprocessor macros instead, ! 684: we could do it by substituting [[macro_definition]]. ! 685: I'm not sure it would ever make sense to do so, but I'm leaving the ! 686: code here anyway. ! 687: <<functions>>= ! 688: function function_definition(name, argc, i, temp) { ! 689: if (argc==0) { ! 690: temp = sprintf("val %s = ", name) ! 691: } else { ! 692: temp = sprintf( "fun %s(", name) ! 693: for (i=1; i< argc; i++) temp = sprintf("%sA%d,", temp,i) ! 694: temp = sprintf( "%sA%d) = ", temp, argc) ! 695: } ! 696: return temp ! 697: } ! 698: <<useless functions>>= ! 699: function macro_definition(name, argc, i, temp) { ! 700: if (argc==0) { ! 701: temp = sprintf("#define %s ", name) ! 702: } else { ! 703: temp = sprintf( "#define %s(", name) ! 704: for (i=1; i< argc; i++) temp = sprintf("%sA%d,", temp,i) ! 705: temp = sprintf( "%sA%d) ", temp, argc) ! 706: } ! 707: return temp ! 708: } ! 709: @ \section{Handling error conditions} ! 710: Here are a bunch of uninteresting functions and modules ! 711: that handle error conditions. ! 712: <<bad operator name>>= ! 713: { ! 714: print "unknown opcode", opname, "on line", NR > stderr ! 715: next ! 716: } ! 717: <<bad field name>>= ! 718: { ! 719: print "unknown field", $i, "on line", NR > stderr ! 720: next ! 721: } ! 722: <<BEGIN>>= ! 723: stderr="/dev/tty" ! 724: <<functions>>= ! 725: function insist_fields(n) { ! 726: if (NF != n) { ! 727: print "Must have", n, "fields on line",NR ":", $0 > stderr ! 728: return 0 ! 729: } else { ! 730: return 1 ! 731: } ! 732: } ! 733: @ \section{Leftover junk} ! 734: Like a pack rat, I never throw out anything that might be useful again later. ! 735: <<junk>>= ! 736: function thetype(n) { ! 737: if (n==OPCODE) return "OPCODE" ! 738: else if (n==SPECIAL) return "SPECIAL" ! 739: else if (n==BCOND) return "BCOND" ! 740: else if (n==COP1) return "COP1" ! 741: else return "BADTYPE" ! 742: } ! 743: <<decoding junk>>= ! 744: for (f in fieldnames) { ! 745: printf "^ \"\\n%s = \" ^ Integer.makestring %s\n",f,f > decode ! 746: } ! 747: printf "^\"\\n\"\n" > decode
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.