1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 \chapter{Fasload File Format}% -*- Dictionary: design -*- \section{General} The purpose of Fasload files is to allow concise storage and rapid loading of Lisp data, particularly function definitions. The intent is that loading a Fasload file has the same effect as loading the ASCII file from which the Fasload file was compiled, but accomplishes the tasks more efficiently. One noticeable difference, of course, is that function definitions may be in compiled form rather than S-expression form. Another is that Fasload files may specify in what parts of memory the Lisp data should be allocated. For example, constant lists used by compiled code may be regarded as read-only. In some Lisp implementations, Fasload file formats are designed to allow sharing of code parts of the file, possibly by direct mapping of pages of the file into the address space of a process. This technique produces great performance improvements in a paged time-sharing system. Since the Mach project is to produce a distributed personal-computer network system rather than a time-sharing system, efficiencies of this type are explicitly {\it not} a goal for the CMU Common Lisp Fasload file format. On the other hand, CMU Common Lisp is intended to be portable, as it will eventually run on a variety of machines. Therefore an explicit goal is that Fasload files shall be transportable among various implementations, to permit efficient distribution of programs in compiled form. The representations of data objects in Fasload files shall be relatively independent of such considerations as word length, number of type bits, and so on. If two implementations interpret the same macrocode (compiled code format), then Fasload files should be completely compatible. If they do not, then files not containing compiled code (so-called "Fasdump" data files) should still be compatible. While this may lead to a format which is not maximally efficient for a particular implementation, the sacrifice of a small amount of performance is deemed a worthwhile price to pay to achieve portability. The primary assumption about data format compatibility is that all implementations can support I/O on finite streams of eight-bit bytes. By "finite" we mean that a definite end-of-file point can be detected irrespective of the content of the data stream. A Fasload file will be regarded as such a byte stream. \section{Strategy} A Fasload file may be regarded as a human-readable prefix followed by code in a funny little language. When interpreted, this code will cause the construction of the encoded data structures. The virtual machine which interprets this code has a {\it stack} and a {\it table}, both initially empty. The table may be thought of as an expandable register file; it is used to remember quantities which are needed more than once. The elements of both the stack and the table are Lisp data objects. Operators of the funny language may take as operands following bytes of the data stream, or items popped from the stack. Results may be pushed back onto the stack or pushed onto the table. The table is an indexable stack that is never popped; it is indexed relative to the base, not the top, so that an item once pushed always has the same index. More precisely, a Fasload file has the following macroscopic organization. It is a sequence of zero or more groups concatenated together. End-of-file must occur at the end of the last group. Each group begins with a series of seven-bit ASCII characters terminated by one or more bytes of all ones \verb|#xFF|; this is called the {\it header}. Following the bytes which terminate the header is the {\it body}, a stream of bytes in the funny binary language. The body of necessity begins with a byte other than \verb|#xFF|. The body is terminated by the operation {\tt FOP-END-GROUP}. The first nine characters of the header must be "{\tt FASL FILE}" in upper-case letters. The rest may be any ASCII text, but by convention it is formatted in a certain way. The header is divided into lines, which are grouped into paragraphs. A paragraph begins with a line which does {\it not} begin with a space or tab character, and contains all lines up to, but not including, the next such line. The first word of a paragraph, defined to be all characters up to but not including the first space, tab, or end-of-line character, is the {\it name} of the paragraph. A Fasload file header might look something like this: \begin{verbatim} FASL FILE >SteelesPerq>User>Guy>IoHacks>Pretty-Print.Slisp Package Pretty-Print Compiled 31-Mar-1988 09:01:32 by some random luser Compiler Version 1.6, Lisp Version 3.0. Functions: INITIALIZE DRIVER HACK HACK1 MUNGE MUNGE1 GAZORCH MINGLE MUDDLE PERTURB OVERDRIVE GOBBLE-KEYBOARD FRY-USER DROP-DEAD HELP CLEAR-MICROCODE %AOS-TRIANGLE %HARASS-READTABLE-MAYBE Macros: PUSH POP FROB TWIDDLE \end{verbatim} {\it one or more bytes of \verb|#xFF|} The particular paragraph names and contents shown here are only intended as suggestions. \section{Fasload Language} Each operation in the binary Fasload language is an eight-bit (one-byte) opcode. Each has a name beginning with "{\tt FOP-}". In the following descriptions, the name is followed by operand descriptors. Each descriptor denotes operands that follow the opcode in the input stream. A quantity in parentheses indicates the number of bytes of data from the stream making up the operand. Operands which implicitly come from the stack are noted in the text. The notation "$\Rightarrow$ stack" means that the result is pushed onto the stack; "$\Rightarrow$ table" similarly means that the result is added to the table. A construction like "{\it n}(1) {\it value}({\it n})" means that first a single byte {\it n} is read from the input stream, and this byte specifies how many bytes to read as the operand named {\it value}. All numeric values are unsigned binary integers unless otherwise specified. Values described as "signed" are in two's-complement form unless otherwise specified. When an integer read from the stream occupies more than one byte, the first byte read is the least significant byte, and the last byte read is the most significant (and contains the sign bit as its high-order bit if the entire integer is signed). Some of the operations are not necessary, but are rather special cases of or combinations of others. These are included to reduce the size of the file or to speed up important cases. As an example, nearly all strings are less than 256 bytes long, and so a special form of string operation might take a one-byte length rather than a four-byte length. As another example, some implementations may choose to store bits in an array in a left-to-right format within each word, rather than right-to-left. The Fasload file format may support both formats, with one being significantly more efficient than the other for a given implementation. The compiler for any implementation may generate the more efficient form for that implementation, and yet compatibility can be maintained by requiring all implementations to support both formats in Fasload files. Measurements are to be made to determine which operation codes are worthwhile; little-used operations may be discarded and new ones added. After a point the definition will be "frozen", meaning that existing operations may not be deleted (though new ones may be added; some operations codes will be reserved for that purpose). \begin{description} \item[0:] \hspace{2em} {\tt FOP-NOP} \\ No operation. (This is included because it is recognized that some implementations may benefit from alignment of operands to some operations, for example to 32-bit boundaries. This operation can be used to pad the instruction stream to a desired boundary.) \item[1:] \hspace{2em} {\tt FOP-POP} \hspace{2em} $\Rightarrow$ \hspace{2em} table \\ One item is popped from the stack and added to the table. \item[2:] \hspace{2em} {\tt FOP-PUSH} \hspace{2em} {\it index}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Item number {\it index} of the table is pushed onto the stack. The first element of the table is item number zero. \item[3:] \hspace{2em} {\tt FOP-BYTE-PUSH} \hspace{2em} {\it index}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Item number {\it index} of the table is pushed onto the stack. The first element of the table is item number zero. \item[4:] \hspace{2em} {\tt FOP-EMPTY-LIST} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The empty list ({\tt ()}) is pushed onto the stack. \item[5:] \hspace{2em} {\tt FOP-TRUTH} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The standard truth value ({\tt T}) is pushed onto the stack. \item[6:] \hspace{2em} {\tt FOP-SYMBOL-SAVE} \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The four-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the default package, and the resulting symbol is both pushed onto the stack and added to the table. \item[7:] \hspace{2em} {\tt FOP-SMALL-SYMBOL-SAVE} \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The one-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the default package, and the resulting symbol is both pushed onto the stack and added to the table. \item[8:] \hspace{2em} {\tt FOP-SYMBOL-IN-PACKAGE-SAVE} \hspace{2em} {\it index}(4) \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The four-byte {\it index} specifies a package stored in the table. The four-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the specified package, and the resulting symbol is both pushed onto the stack and added to the table. \item[9:] \hspace{2em} {\tt FOP-SMALL-SYMBOL-IN-PACKAGE-SAVE} \hspace{2em} {\it index}(4) \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The four-byte {\it index} specifies a package stored in the table. The one-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the specified package, and the resulting symbol is both pushed onto the stack and added to the table. \item[10:] \hspace{2em} {\tt FOP-SYMBOL-IN-BYTE-PACKAGE-SAVE} \hspace{2em} {\it index}(1) \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The one-byte {\it index} specifies a package stored in the table. The four-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the specified package, and the resulting symbol is both pushed onto the stack and added to the table. \item[11:]\hspace{2em} {\tt FOP-SMALL-SYMBOL-IN-BYTE-PACKAGE-SAVE} \hspace{2em} {\it index}(1) \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ The one-byte {\it index} specifies a package stored in the table. The one-byte operand {\it n} specifies the length of the print name of a symbol. The name follows, one character per byte, with the first byte of the print name being the first read. The name is interned in the specified package, and the resulting symbol is both pushed onto the stack and added to the table. \item[12:] \hspace{2em} {\tt FOP-UNINTERNED-SYMBOL-SAVE} \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SYMBOL-SAVE}, except that it creates an uninterned symbol. \item[13:] \hspace{2em} {\tt FOP-UNINTERNED-SMALL-SYMBOL-SAVE} \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SMALL-SYMBOL-SAVE}, except that it creates an uninterned symbol. \item[14:] \hspace{2em} {\tt FOP-PACKAGE} \hspace{2em} $\Rightarrow$ \hspace{2em} table \\ An item is popped from the stack; it must be a symbol. The package of that name is located and pushed onto the table. \item[15:] \hspace{2em} {\tt FOP-LIST} \hspace{2em} {\it length}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The unsigned operand {\it length} specifies a number of operands to be popped from the stack. These are made into a list of that length, and the list is pushed onto the stack. The first item popped from the stack becomes the last element of the list, and so on. Hence an iterative loop can start with the empty list and perform "pop an item and cons it onto the list" {\it length} times. (Lists of length greater than 255 can be made by using {\tt FOP-LIST*} repeatedly.) \item[16:] \hspace{2em} {\tt FOP-LIST*} \hspace{2em} {\it length}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ This is like {\tt FOP-LIST} except that the constructed list is terminated not by {\tt ()} (the empty list), but by an item popped from the stack before any others are. Therefore {\it length}+1 items are popped in all. Hence an iterative loop can start with a popped item and perform "pop an item and cons it onto the list" {\it length}+1 times. \item[17-24:] \hspace{2em} {\tt FOP-LIST-1}, {\tt FOP-LIST-2}, ..., {\tt FOP-LIST-8} \\ {\tt FOP-LIST-{\it k}} is like {\tt FOP-LIST} with a byte containing {\it k} following it. These exist purely to reduce the size of Fasload files. Measurements need to be made to determine the useful values of {\it k}. \item[25-32:] \hspace{2em} {\tt FOP-LIST*-1}, {\tt FOP-LIST*-2}, ..., {\tt FOP-LIST*-8} \\ {\tt FOP-LIST*-{\it k}} is like {\tt FOP-LIST*} with a byte containing {\it k} following it. These exist purely to reduce the size of Fasload files. Measurements need to be made to determine the useful values of {\it k}. \item[33:] \hspace{2em} {\tt FOP-INTEGER} \hspace{2em} {\it n}(4) \hspace{2em} {\it value}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A four-byte unsigned operand specifies the number of following bytes. These bytes define the value of a signed integer in two's-complement form. The first byte of the value is the least significant byte. \item[34:] \hspace{2em} {\tt FOP-SMALL-INTEGER} \hspace{2em} {\it n}(1) \hspace{2em} {\it value}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A one-byte unsigned operand specifies the number of following bytes. These bytes define the value of a signed integer in two's-complement form. The first byte of the value is the least significant byte. \item[35:] \hspace{2em} {\tt FOP-WORD-INTEGER} \hspace{2em} {\it value}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A four-byte signed integer (in the range $-2^{31}$ to $2^{31}-1$) follows the operation code. A LISP integer (fixnum or bignum) with that value is constructed and pushed onto the stack. \item[36:] \hspace{2em} {\tt FOP-BYTE-INTEGER} \hspace{2em} {\it value}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A one-byte signed integer (in the range -128 to 127) follows the operation code. A LISP integer (fixnum or bignum) with that value is constructed and pushed onto the stack. \item[37:] \hspace{2em} {\tt FOP-STRING} \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a string to construct. The characters of the string follow, one per byte. The constructed string is pushed onto the stack. \item[38:] \hspace{2em} {\tt FOP-SMALL-STRING} \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The one-byte operand {\it n} specifies the length of a string to construct. The characters of the string follow, one per byte. The constructed string is pushed onto the stack. \item[39:] \hspace{2em} {\tt FOP-VECTOR} \hspace{2em} {\it n}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a vector of LISP objects to construct. The elements of the vector are popped off the stack; the first one popped becomes the last element of the vector. The constructed vector is pushed onto the stack. \item[40:] \hspace{2em} {\tt FOP-SMALL-VECTOR} \hspace{2em} {\it n}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The one-byte operand {\it n} specifies the length of a vector of LISP objects to construct. The elements of the vector are popped off the stack; the first one popped becomes the last element of the vector. The constructed vector is pushed onto the stack. \item[41:] \hspace{2em} {\tt FOP-UNIFORM-VECTOR} \hspace{2em} {\it n}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a vector of LISP objects to construct. A single item is popped from the stack and used to initialize all elements of the vector. The constructed vector is pushed onto the stack. \item[42:] \hspace{2em} {\tt FOP-SMALL-UNIFORM-VECTOR} \hspace{2em} {\it n}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The one-byte operand {\it n} specifies the length of a vector of LISP objects to construct. A single item is popped from the stack and used to initialize all elements of the vector. The constructed vector is pushed onto the stack. \item[43:] \hspace{2em} {\tt FOP-INT-VECTOR} \hspace{2em} {\it len}(4) \hspace{2em} {\it size}(1) \hspace{2em} {\it data}($\left\lceil len*count/8\right\rceil$) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a vector of unsigned integers to be constructed. Each integer is {\it size} bits long, and is packed according to the machine's native byte ordering. {\it size} must be a directly supported i-vector element size. Currently supported values are 1,2,4,8,16 and 32. \item[44:] \hspace{2em} {\tt FOP-UNIFORM-INT-VECTOR} \hspace{2em} {\it n}(4) \hspace{2em} {\it size}(1) \hspace{2em} {\it value}(@ceiling<{\it size}/8>) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a vector of unsigned integers to construct. Each integer is {\it size} bits big, and is initialized to the value of the operand {\it value}. The constructed vector is pushed onto the stack. \item[45:] Unused \item[46:] \hspace{2em} {\tt FOP-SINGLE-FLOAT} \hspace{2em} {\it data}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The {\it data} bytes are read as an integer, then turned into an IEEE single float (as though by {\tt make-single-float}). \item[47:] \hspace{2em} {\tt FOP-DOUBLE-FLOAT} \hspace{2em} {\it data}(8) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The {\it data} bytes are read as an integer, then turned into an IEEE double float (as though by {\tt make-double-float}). \item[48:] \hspace{2em} {\tt FOP-STRUCT} \hspace{2em} {\it n}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length structure to construct. The elements of the vector are popped off the stack; the first one popped becomes the last element of the structure. The constructed vector is pushed onto the stack. \item[49:] \hspace{2em} {\tt FOP-SMALL-STRUCT} \hspace{2em} {\it n}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The one-byte operand {\it n} specifies the length structure to construct. The elements of the vector are popped off the stack; the first one popped becomes the last element of the structure. The constructed vector is pushed onto the stack. \item[50-52:] Unused \item[53:] \hspace{2em} {\tt FOP-EVAL} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Pop an item from the stack and evaluate it (give it to {\tt EVAL}). Push the result back onto the stack. \item[54:] \hspace{2em} {\tt FOP-EVAL-FOR-EFFECT} \\ Pop an item from the stack and evaluate it (give it to {\tt EVAL}). The result is ignored. \item[55:] \hspace{2em} {\tt FOP-FUNCALL} \hspace{2em} {\it nargs}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Pop {\it nargs}+1 items from the stack and apply the last one popped as a function to all the rest as arguments (the first one popped being the last argument). Push the result back onto the stack. \item[56:] \hspace{2em} {\tt FOP-FUNCALL-FOR-EFFECT} \hspace{2em} {\it nargs}(1) \\ Pop {\it nargs}+1 items from the stack and apply the last one popped as a function to all the rest as arguments (the first one popped being the last argument). The result is ignored. \item[57:] \hspace{2em} {\tt FOP-CODE-FORMAT} \hspace{2em} {\it implementation}(1) \hspace{2em} {\it version}(1) \\ This FOP specifiers the code format for following code objects. The operations {\tt FOP-CODE} and its relatives may not occur in a group until after {\tt FOP-CODE-FORMAT} has appeared; there is no default format. The {\it implementation} is an integer indicating the target hardware and environment. See {\tt compiler/generic/vm-macs.lisp} for the currently defined implementations. {\it version} for an implementation is increased whenever there is a change that renders old fasl files unusable. \item[58:] \hspace{2em} {\tt FOP-CODE} \hspace{2em} {\it nitems}(4) \hspace{2em} {\it size}(4) \hspace{2em} {\it code}({\it size}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A compiled function is constructed and pushed onto the stack. This object is in the format specified by the most recent occurrence of {\tt FOP-CODE-FORMAT}. The operand {\it nitems} specifies a number of items to pop off the stack to use in the "boxed storage" section. The operand {\it code} is a string of bytes constituting the compiled executable code. \item[59:] \hspace{2em} {\tt FOP-SMALL-CODE} \hspace{2em} {\it nitems}(1) \hspace{2em} {\it size}(2) \hspace{2em} {\it code}({\it size}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A compiled function is constructed and pushed onto the stack. This object is in the format specified by the most recent occurrence of {\tt FOP-CODE-FORMAT}. The operand {\it nitems} specifies a number of items to pop off the stack to use in the "boxed storage" section. The operand {\it code} is a string of bytes constituting the compiled executable code. \item[60-61:] Unused \item[62:] \hspace{2em} {\tt FOP-VERIFY-TABLE-SIZE} \hspace{2em} {\it size}(4) \\ If the current size of the table is not equal to {\it size}, then an inconsistency has been detected. This operation is inserted into a Fasload file purely for error-checking purposes. It is good practice for a compiler to output this at least at the end of every group, if not more often. \item[63:] \hspace{2em} {\tt FOP-VERIFY-EMPTY-STACK} \\ If the stack is not currently empty, then an inconsistency has been detected. This operation is inserted into a Fasload file purely for error-checking purposes. It is good practice for a compiler to output this at least at the end of every group, if not more often. \item[64:] \hspace{2em} {\tt FOP-END-GROUP} \\ This is the last operation of a group. If this is not the last byte of the file, then a new group follows; the next nine bytes must be "{\tt FASL FILE}". \item[65:] \hspace{2em} {\tt FOP-POP-FOR-EFFECT} \hspace{2em} stack \hspace{2em} $\Rightarrow$ \hspace{2em} \\ One item is popped from the stack. \item[66:] \hspace{2em} {\tt FOP-MISC-TRAP} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ A trap object is pushed onto the stack. \item[67:] Unused \item[68:] \hspace{2em} {\tt FOP-CHARACTER} \hspace{2em} {\it character}(3) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The three bytes are read as an integer then converted to a character. This FOP is currently rather useless, as extended characters are not supported. \item[69:] \hspace{2em} {\tt FOP-SHORT-CHARACTER} \hspace{2em} {\it character}(1) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The one byte specifies the code of a Common Lisp character object. A character is constructed and pushed onto the stack. \item[70:] \hspace{2em} {\tt FOP-RATIO} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Creates a ratio from two integers popped from the stack. The denominator is popped first, the numerator second. \item[71:] \hspace{2em} {\tt FOP-COMPLEX} \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ Creates a complex number from two numbers popped from the stack. The imaginary part is popped first, the real part second. \item[72-73:] Unused \item[74:] \hspace{2em} {\tt FOP-FSET} \hspace{2em} \\ Except in the cold loader (Genesis), this is a no-op with two stack arguments. In the initial core this is used to make DEFUN functions defined at cold-load time so that global functions can be called before top-level forms are run (which normally installs definitions.) Genesis pops the top two things off of the stack and effectively does (SETF SYMBOL-FUNCTION). \item[75:] \hspace{2em} {\tt FOP-LISP-SYMBOL-SAVE} \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SYMBOL-SAVE}, except that it creates a symbol in the LISP package. \item[76:] \hspace{2em} {\tt FOP-LISP-SMALL-SYMBOL-SAVE} \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SMALL-SYMBOL-SAVE}, except that it creates a symbol in the LISP package. \item[77:] \hspace{2em} {\tt FOP-KEYWORD-SYMBOL-SAVE} \hspace{2em} {\it n}(4) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SYMBOL-SAVE}, except that it creates a symbol in the KEYWORD package. \item[78:] \hspace{2em} {\tt FOP-KEYWORD-SMALL-SYMBOL-SAVE} \hspace{2em} {\it n}(1) \hspace{2em} {\it name}({\it n}) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \& table\\ Like {\tt FOP-SMALL-SYMBOL-SAVE}, except that it creates a symbol in the KEYWORD package. \item[79-80:] Unused \item[81:] \hspace{2em} {\tt FOP-NORMAL-LOAD}\\ This FOP is used in conjunction with the cold loader (Genesis) to read top-level package manipulation forms. These forms are to be read as though by the normal loaded, so that they can be evaluated at cold load time, instead of being dumped into the initial core image. A no-op in normal loading. \item[82:] \hspace{2em} {\tt FOP-MAYBE-COLD-LOAD}\\ Undoes the effect of {\tt FOP-NORMAL-LOAD}. \item[83:] \hspace{2em} {\tt FOP-ARRAY} \hspace{2em} {\it rank}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ This operation creates a simple array header (used for simple-arrays with rank /= 1). The data vector is popped off of the stack, and then {\it rank} dimensions are popped off of the stack (the highest dimensions is on top.) \item[84-139:] Unused \item[140:] \hspace{2em} {\tt FOP-ALTER-CODE} \hspace{2em} {\it index}(4)\\ This operation modifies the constants part of a code object (necessary for creating certain circular function references.) It pops the new value and code object are off of the stack, storing the new value at the specified index. \item[141:] \hspace{2em} {\tt FOP-BYTE-ALTER-CODE} \hspace{2em} {\it index}(1)\\ Like {\tt FOP-ALTER-CODE}, but has only a one byte offset. \item[142:] \hspace{2em} {\tt FOP-FUNCTION-ENTRY} \hspace{2em} {\it index}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ Initializes a function-entry header inside of a pre-existing code object, and returns the corresponding function descriptor. {\it index} is the byte offset inside of the code object where the header should be plunked down. The stack arguments to this operation are the code object, function name, function debug arglist and function type. \item[143:] Unused \item[144:] \hspace{2em} {\tt FOP-ASSEMBLER-CODE} \hspace{2em} {\it length}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ This operation creates a code object holding assembly routines. {\it length} bytes of code are read and placed in the code object, and the code object descriptor is pushed on the stack. This FOP is only recognized by the cold loader (Genesis.) \item[145:] \hspace{2em} {\tt FOP-ASSEMBLER-ROUTINE} \hspace{2em} {\it offset}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ This operation records an entry point into an assembler code object (for use with {\tt FOP-ASSEMBLER-FIXUP}). The routine name (a symbol) is on stack top. The code object is underneath. The entry point is defined at {\it offset} bytes inside the code area of the code object, and the code object is left on stack top (allowing multiple uses of this FOP to be chained.) This FOP is only recognized by the cold loader (Genesis.) \item[146:] Unused \item[147:] \hspace{2em} {\tt FOP-FOREIGN-FIXUP} \hspace{2em} {\it len}(1) \hspace{2em} {\it name}({\it len}) \hspace{2em} {\it offset}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ This operation resolves a reference to a foreign (C) symbol. {\it len} bytes are read and interpreted as the symbol {\it name}. First the {\it kind} and the code-object to patch are popped from the stack. The kind is a target-dependent symbol indicating the instruction format of the patch target (at {\it offset} bytes from the start of the code area.) The code object is left on stack top (allowing multiple uses of this FOP to be chained.) \item[148:] \hspace{2em} {\tt FOP-ASSEMBLER-FIXUP} \hspace{2em} {\it offset}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ This operation resolves a reference to an assembler routine. The stack args are ({\it routine-name}, {\it kind} and {\it code-object}). The kind is a target-dependent symbol indicating the instruction format of the patch target (at {\it offset} bytes from the start of the code area.) The code object is left on stack top (allowing multiple uses of this FOP to be chained.) \item[149-199:] Unused \item[200:] \hspace{2em} {\tt FOP-RPLACA} \hspace{2em} {\it table-idx}(4) \hspace{2em} {\it cdr-offset}(4)\\ \item[201:] \hspace{2em} {\tt FOP-RPLACD} \hspace{2em} {\it table-idx}(4) \hspace{2em} {\it cdr-offset}(4)\\ These operations destructively modify a list entered in the table. {\it table-idx} is the table entry holding the list, and {\it cdr-offset} designates the cons in the list to modify (like the argument to {\tt nthcdr}.) The new value is popped off of the stack, and stored in the {\tt car} or {\tt cdr}, respectively. \item[202:] \hspace{2em} {\tt FOP-SVSET} \hspace{2em} {\it table-idx}(4) \hspace{2em} {\it vector-idx}(4)\\ Destructively modifies a {\tt simple-vector} entered in the table. Pops the new value off of the stack, and stores it in the {\it vector-idx} element of the contents of the table entry {\it table-idx.} \item[203:] \hspace{2em} {\tt FOP-NTHCDR} \hspace{2em} {\it cdr-offset}(4) \hspace{2em} $\Rightarrow$ \hspace{2em} stack\\ Does {\tt nthcdr} on the top-of stack, leaving the result there. \item[204:] \hspace{2em} {\tt FOP-STRUCTSET} \hspace{2em} {\it table-idx}(4) \hspace{2em} {\it vector-idx}(4)\\ Like {\tt FOP-SVSET}, except it alters structure slots. \item[255:] \hspace{2em} {\tt FOP-END-HEADER} \\ Indicates the end of a group header, as described above. \end{description}