1 |
15 |
hellwig |
% This file is part of the MMIXware package (c) Donald E Knuth 1999
|
2 |
|
|
@i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
|
3 |
|
|
|
4 |
|
|
\def\title{MMOTYPE}
|
5 |
|
|
\def\MMIX{\.{MMIX}}
|
6 |
|
|
\def\MMIXAL{\.{MMIXAL}}
|
7 |
|
|
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant
|
8 |
|
|
|
9 |
|
|
@* Introduction. This program reads a binary \.{mmo} file output by
|
10 |
|
|
the \MMIXAL\ processor and lists it in human-readable form. It lists
|
11 |
|
|
only the symbol table, if invoked with the \.{-s} option. It lists
|
12 |
|
|
also the tetrabytes of input, if invoked with the \.{-v} option.
|
13 |
|
|
|
14 |
|
|
@s tetra int
|
15 |
|
|
|
16 |
|
|
@c
|
17 |
|
|
#include
|
18 |
|
|
#include
|
19 |
|
|
#include
|
20 |
|
|
#include
|
21 |
|
|
@@;
|
22 |
|
|
@@;
|
23 |
|
|
@@;
|
24 |
|
|
@@;
|
25 |
|
|
@#
|
26 |
|
|
int main(argc,argv)
|
27 |
|
|
int argc;@+char*argv[];
|
28 |
|
|
{
|
29 |
|
|
register int j,delta,postamble=0;
|
30 |
|
|
register char *p;
|
31 |
|
|
@;
|
32 |
|
|
@;
|
33 |
|
|
@;
|
34 |
|
|
do @@;@+while (!postamble);
|
35 |
|
|
@;
|
36 |
|
|
@;
|
37 |
|
|
return 0;
|
38 |
|
|
}
|
39 |
|
|
|
40 |
|
|
@ @=
|
41 |
|
|
listing=1, verbose=0;
|
42 |
|
|
for (j=1;j
|
43 |
|
|
if (argv[j][1]=='s') listing=0;
|
44 |
|
|
else if (argv[j][1]=='v') verbose=1;
|
45 |
|
|
else break;
|
46 |
|
|
}
|
47 |
|
|
if (j!=argc-1) {
|
48 |
|
|
fprintf(stderr,"Usage: %s [-s] [-v] mmofile\n",argv[0]);
|
49 |
|
|
@.Usage: ...@>
|
50 |
|
|
exit(-1);
|
51 |
|
|
}
|
52 |
|
|
|
53 |
|
|
@ @=
|
54 |
|
|
mmo_file=fopen(argv[argc-1],"rb");
|
55 |
|
|
if (!mmo_file) {
|
56 |
|
|
fprintf(stderr,"Can't open file %s!\n",argv[argc-1]);
|
57 |
|
|
@.Can't open...@>
|
58 |
|
|
exit(-2);
|
59 |
|
|
}
|
60 |
|
|
|
61 |
|
|
@ @=
|
62 |
|
|
int listing; /* are we listing everything? */
|
63 |
|
|
int verbose; /* are we also showing the tetras of input as they are read? */
|
64 |
|
|
FILE *mmo_file; /* the input file */
|
65 |
|
|
|
66 |
|
|
@ @=
|
67 |
|
|
#ifdef __STDC__
|
68 |
|
|
#define ARGS(list) list
|
69 |
|
|
#else
|
70 |
|
|
#define ARGS(list) ()
|
71 |
|
|
#endif
|
72 |
|
|
|
73 |
|
|
@ A complete definition of \.{mmo} format appears in the \MMIXAL\ document.
|
74 |
|
|
Here we need to define only the basic constants used for interpretation.
|
75 |
|
|
|
76 |
|
|
@d mm 0x98 /* the escape code of \.{mmo} format */
|
77 |
|
|
@d lop_quote 0x0 /* the quotation lopcode */
|
78 |
|
|
@d lop_loc 0x1 /* the location lopcode */
|
79 |
|
|
@d lop_skip 0x2 /* the skip lopcode */
|
80 |
|
|
@d lop_fixo 0x3 /* the octabyte-fix lopcode */
|
81 |
|
|
@d lop_fixr 0x4 /* the relative-fix lopcode */
|
82 |
|
|
@d lop_fixrx 0x5 /* extended relative-fix lopcode */
|
83 |
|
|
@d lop_file 0x6 /* the file name lopcode */
|
84 |
|
|
@d lop_line 0x7 /* the file position lopcode */
|
85 |
|
|
@d lop_spec 0x8 /* the special hook lopcode */
|
86 |
|
|
@d lop_pre 0x9 /* the preamble lopcode */
|
87 |
|
|
@d lop_post 0xa /* the postamble lopcode */
|
88 |
|
|
@d lop_stab 0xb /* the symbol table lopcode */
|
89 |
|
|
@d lop_end 0xc /* the end-it-all lopcode */
|
90 |
|
|
|
91 |
|
|
@* Low-level arithmetic. This program is intended to work correctly
|
92 |
|
|
whenever an |int| has at least 32 bits.
|
93 |
|
|
|
94 |
|
|
@=
|
95 |
|
|
typedef unsigned char byte; /* a monobyte */
|
96 |
|
|
typedef unsigned int tetra; /* a tetrabyte */
|
97 |
|
|
typedef struct {@+tetra h,l;}@+octa; /* an octabyte */
|
98 |
|
|
|
99 |
|
|
@ The |incr| subroutine adds a signed integer to an (unsigned) octabyte.
|
100 |
|
|
|
101 |
|
|
@=
|
102 |
|
|
octa incr @,@,@[ARGS((octa,int))@];
|
103 |
|
|
octa incr(o,delta)
|
104 |
|
|
octa o;
|
105 |
|
|
int delta;
|
106 |
|
|
{
|
107 |
|
|
register tetra t;
|
108 |
|
|
octa x;
|
109 |
|
|
if (delta>=0) {
|
110 |
|
|
t=0xffffffff-delta;
|
111 |
|
|
if (o.l<=t) x.l=o.l+delta, x.h=o.h;
|
112 |
|
|
else x.l=o.l-t-1, x.h=o.h+1;
|
113 |
|
|
} else {
|
114 |
|
|
t=-delta;
|
115 |
|
|
if (o.l>=t) x.l=o.l-t, x.h=o.h;
|
116 |
|
|
else x.l=o.l+(0xffffffff+delta)+1, x.h=o.h-1;
|
117 |
|
|
}
|
118 |
|
|
return x;
|
119 |
|
|
}
|
120 |
|
|
|
121 |
|
|
@* Low-level input. The tetrabytes of an \.{mmo} file are stored in
|
122 |
|
|
friendly big-endian fashion, but this program is supposed to work also
|
123 |
|
|
on computers that are little-endian. Therefore we read four successive bytes
|
124 |
|
|
and pack them into a tetrabyte, instead of reading a single tetrabyte.
|
125 |
|
|
|
126 |
|
|
@=
|
127 |
|
|
void read_tet @,@,@[ARGS((void))@];
|
128 |
|
|
void read_tet()
|
129 |
|
|
{
|
130 |
|
|
if (fread(buf,1,4,mmo_file)!=4) {
|
131 |
|
|
fprintf(stderr,"Unexpected end of file after %d tetras!\n",count);
|
132 |
|
|
@.Unexpected end of file...@>
|
133 |
|
|
exit(-3);
|
134 |
|
|
}
|
135 |
|
|
yz=(buf[2]<<8)+buf[3];
|
136 |
|
|
tet=(((buf[0]<<8)+buf[1])<<16)+yz;
|
137 |
|
|
if (verbose) printf(" %08x\n",tet);
|
138 |
|
|
count++;
|
139 |
|
|
}
|
140 |
|
|
|
141 |
|
|
@ @=
|
142 |
|
|
byte read_byte @,@,@[ARGS((void))@];
|
143 |
|
|
byte read_byte()
|
144 |
|
|
{
|
145 |
|
|
register byte b;
|
146 |
|
|
if (!byte_count) read_tet();
|
147 |
|
|
b=buf[byte_count];
|
148 |
|
|
byte_count=(byte_count+1)&3;
|
149 |
|
|
return b;
|
150 |
|
|
}
|
151 |
|
|
|
152 |
|
|
@ @=
|
153 |
|
|
int count; /* the number of tetrabytes we've read */
|
154 |
|
|
int byte_count; /* index of the next-to-be-read byte */
|
155 |
|
|
byte buf[4]; /* the most recently read bytes */
|
156 |
|
|
int yz; /* the two least significant bytes */
|
157 |
|
|
tetra tet; /* |buf| bytes packed big-endianwise */
|
158 |
|
|
|
159 |
|
|
@ @=
|
160 |
|
|
count=byte_count=0;
|
161 |
|
|
|
162 |
|
|
@* The main loop. Now for the bread-and-butter part of this program.
|
163 |
|
|
|
164 |
|
|
@=
|
165 |
|
|
{
|
166 |
|
|
read_tet();
|
167 |
|
|
loop:@+if (buf[0]==mm) switch (buf[1]) {
|
168 |
|
|
case lop_quote:@+if (yz!=1)
|
169 |
|
|
err("YZ field of lop_quote should be 1");
|
170 |
|
|
@.YZ field...should be 1@>
|
171 |
|
|
read_tet();@+break;
|
172 |
|
|
@t\4@>@@;
|
173 |
|
|
default: err("Unknown lopcode");
|
174 |
|
|
@.Unknown lopcode@>
|
175 |
|
|
}
|
176 |
|
|
if (listing) @;
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
@ We want to catch all cases where the rules of \.{mmo} format are
|
180 |
|
|
not obeyed. The |err| macro ameliorates this somewhat tedious chore.
|
181 |
|
|
|
182 |
|
|
@d err(m) {@+fprintf(stderr,"Error in tetra %d: %s!\n",count,m);@+ continue;@+}
|
183 |
|
|
@.Error in tetra...@>
|
184 |
|
|
|
185 |
|
|
@ In a normal situation, the newly read tetrabyte is simply supposed
|
186 |
|
|
to be loaded into the current location. We list not only the current
|
187 |
|
|
location but also the current file position, if |cur_line| is nonzero
|
188 |
|
|
and |cur_loc| belongs to segment~0.
|
189 |
|
|
|
190 |
|
|
@=
|
191 |
|
|
{
|
192 |
|
|
printf("%08x%08x: %08x",cur_loc.h,cur_loc.l,tet);
|
193 |
|
|
if (!cur_line) printf("\n");
|
194 |
|
|
else {
|
195 |
|
|
if (cur_loc.h&0xe0000000) printf("\n");
|
196 |
|
|
else {
|
197 |
|
|
if (cur_file==listed_file) printf(" (line %d)\n",cur_line);
|
198 |
|
|
else {
|
199 |
|
|
printf(" (\"%s\", line %d)\n", file_name[cur_file], cur_line);
|
200 |
|
|
listed_file=cur_file;
|
201 |
|
|
}
|
202 |
|
|
}
|
203 |
|
|
cur_line++;
|
204 |
|
|
}
|
205 |
|
|
cur_loc=incr(cur_loc,4);@+ cur_loc.l &=-4;
|
206 |
|
|
}
|
207 |
|
|
|
208 |
|
|
@ @=
|
209 |
|
|
octa cur_loc; /* the current location */
|
210 |
|
|
int listed_file; /* the most recently listed file number */
|
211 |
|
|
int cur_file; /* the most recently selected file number */
|
212 |
|
|
int cur_line; /* the current position in |cur_file| */
|
213 |
|
|
char *file_name[256]; /* file names seen */
|
214 |
|
|
octa tmp; /* an octabyte of temporary interest */
|
215 |
|
|
|
216 |
|
|
@ @=
|
217 |
|
|
cur_loc.h=cur_loc.l=0;
|
218 |
|
|
listed_file=cur_file=-1;
|
219 |
|
|
cur_line=0;
|
220 |
|
|
|
221 |
|
|
@* The simple lopcodes. We have already implemented |lop_quote|, which
|
222 |
|
|
falls through to the normal case after reading an extra tetrabyte.
|
223 |
|
|
Now let's consider the other lopcodes in turn.
|
224 |
|
|
|
225 |
|
|
@d y buf[2] /* the next-to-least significant byte */
|
226 |
|
|
@d z buf[3] /* the least significant byte */
|
227 |
|
|
|
228 |
|
|
@=
|
229 |
|
|
case lop_loc:@+if (z==2) {
|
230 |
|
|
j=y;@+ read_tet();@+ cur_loc.h=(j<<24)+tet;
|
231 |
|
|
}@+else if (z==1) cur_loc.h=y<<24;
|
232 |
|
|
else err("Z field of lop_loc should be 1 or 2");
|
233 |
|
|
@:Z field of lop_loc...}\.{Z field of lop\_loc...@>
|
234 |
|
|
read_tet();@+ cur_loc.l=tet;
|
235 |
|
|
continue;
|
236 |
|
|
case lop_skip: cur_loc=incr(cur_loc,yz);@+continue;
|
237 |
|
|
|
238 |
|
|
@ Fixups load information out of order, when future references have
|
239 |
|
|
been resolved. The current file name and line number are not considered
|
240 |
|
|
relevant.
|
241 |
|
|
|
242 |
|
|
@=
|
243 |
|
|
case lop_fixo:@+if (z==2) {
|
244 |
|
|
j=y;@+ read_tet();@+ tmp.h=(j<<24)+tet;
|
245 |
|
|
}@+else if (z==1) tmp.h=y<<24;
|
246 |
|
|
else err("Z field of lop_fixo should be 1 or 2");
|
247 |
|
|
@:Z field of lop_fixo...}\.{Z field of lop\_fixo...@>
|
248 |
|
|
read_tet();@+ tmp.l=tet;
|
249 |
|
|
if (listing) printf("%08x%08x: %08x%08x\n",tmp.h,tmp.l,cur_loc.h,cur_loc.l);
|
250 |
|
|
continue;
|
251 |
|
|
case lop_fixr: delta=yz; goto fixr;
|
252 |
|
|
case lop_fixrx:j=yz;@+if (j!=16 && j!=24)
|
253 |
|
|
err("YZ field of lop_fixrx should be 16 or 24");
|
254 |
|
|
@:YZ field of lop_fixrx...}\.{YZ field of lop\_fixrx...@>
|
255 |
|
|
read_tet(); delta=tet;
|
256 |
|
|
if (delta&0xfe000000) err("increment of lop_fixrx is too large");
|
257 |
|
|
@.increment...too large@>
|
258 |
|
|
fixr: tmp=incr(cur_loc,-(delta>=0x1000000? (delta&0xffffff)-(1<
|
259 |
|
|
if (listing) printf("%08x%08x: %08x\n",tmp.h,tmp.l,delta);
|
260 |
|
|
continue;
|
261 |
|
|
|
262 |
|
|
@ The space for file names isn't allocated until we are sure we need it.
|
263 |
|
|
|
264 |
|
|
@=
|
265 |
|
|
case lop_file:@+if (file_name[y]) {
|
266 |
|
|
for (j=z;j>0;j--) read_tet();
|
267 |
|
|
cur_file=y;
|
268 |
|
|
if (z) err("Two file names with the same number");
|
269 |
|
|
@.Two file names...@>
|
270 |
|
|
}@+else {
|
271 |
|
|
if (!z) err("No name given for newly selected file");
|
272 |
|
|
@.No name given...@>
|
273 |
|
|
file_name[y]=(char*)calloc(4*z+1,1);
|
274 |
|
|
if (!file_name[y]) {
|
275 |
|
|
fprintf(stderr,"No room to store the file name!\n");@+exit(-4);
|
276 |
|
|
@.No room...@>
|
277 |
|
|
}
|
278 |
|
|
cur_file=y;
|
279 |
|
|
for (j=z,p=file_name[y]; j>0; j--,p+=4) {
|
280 |
|
|
read_tet();
|
281 |
|
|
*p=buf[0];@+*(p+1)=buf[1];@+*(p+2)=buf[2];@+*(p+3)=buf[3];
|
282 |
|
|
}
|
283 |
|
|
}
|
284 |
|
|
cur_line=0;@+continue;
|
285 |
|
|
case lop_line:@+if (cur_file<0) err("No file was selected for lop_line");
|
286 |
|
|
@.No file was selected...@>
|
287 |
|
|
cur_line=yz;@+continue;
|
288 |
|
|
|
289 |
|
|
@ Special bytes in the file might be in synch with the current location
|
290 |
|
|
and/or the current file position, so we list those parameters too.
|
291 |
|
|
|
292 |
|
|
@=
|
293 |
|
|
case lop_spec:@+if (listing) {
|
294 |
|
|
printf("Special data %d at loc %08x%08x", yz, cur_loc.h, cur_loc.l);
|
295 |
|
|
if (!cur_line) printf("\n");
|
296 |
|
|
else if (cur_file==listed_file) printf(" (line %d)\n",cur_line);
|
297 |
|
|
else {
|
298 |
|
|
printf(" (\"%s\", line %d)\n", file_name[cur_file], cur_line);
|
299 |
|
|
listed_file=cur_file;
|
300 |
|
|
}
|
301 |
|
|
}
|
302 |
|
|
while(1) {
|
303 |
|
|
read_tet();
|
304 |
|
|
if (buf[0]==mm) {
|
305 |
|
|
if (buf[1]!=lop_quote || yz!=1) goto loop; /* end of special data */
|
306 |
|
|
read_tet();
|
307 |
|
|
}
|
308 |
|
|
if (listing) printf(" %08x\n",tet);
|
309 |
|
|
}
|
310 |
|
|
|
311 |
|
|
@ The other cases shouldn't appear in the main loop.
|
312 |
|
|
|
313 |
|
|
@=
|
314 |
|
|
case lop_pre: err("Can't have another preamble");
|
315 |
|
|
@.Can't have another...@>
|
316 |
|
|
case lop_post: postamble=1;
|
317 |
|
|
if (y) err("Y field of lop_post should be zero");
|
318 |
|
|
@:Y field of lop_post...}\.{Y field of lop\_post...@>
|
319 |
|
|
if (z<32) err("Z field of lop_post must be 32 or more");
|
320 |
|
|
@:Z field of lop_post...}\.{Z field of lop\_post...@>
|
321 |
|
|
continue;
|
322 |
|
|
case lop_stab: err("Symbol table must follow postamble");
|
323 |
|
|
@.Symbol table...@>
|
324 |
|
|
case lop_end: err("Symbol table can't end before it begins");
|
325 |
|
|
|
326 |
|
|
@* The preamble and postamble. Now here's what we do before and after
|
327 |
|
|
the main loop.
|
328 |
|
|
|
329 |
|
|
@=
|
330 |
|
|
read_tet(); /* read the first tetrabyte of input */
|
331 |
|
|
if (buf[0]!=mm || buf[1]!=lop_pre) {
|
332 |
|
|
fprintf(stderr,"Input is not an MMO file (first two bytes are wrong)!\n");
|
333 |
|
|
@.Input is not...@>
|
334 |
|
|
exit(-5);
|
335 |
|
|
}
|
336 |
|
|
if (y!=1) fprintf(stderr,
|
337 |
|
|
"Warning: I'm reading this file as version 1, not version %d!\n",y);
|
338 |
|
|
@.I'm reading this file...@>
|
339 |
|
|
if (z>0) {
|
340 |
|
|
j=z;
|
341 |
|
|
read_tet();
|
342 |
|
|
if (listing)
|
343 |
|
|
printf("File was created %s",asctime(localtime((time_t*)&tet)));
|
344 |
|
|
for (j--;j>0;j--) {
|
345 |
|
|
read_tet();
|
346 |
|
|
if (listing) printf("Preamble data %08x\n",tet);
|
347 |
|
|
}
|
348 |
|
|
}
|
349 |
|
|
|
350 |
|
|
@ @=
|
351 |
|
|
for (j=z;j<256;j++) {
|
352 |
|
|
read_tet();@+tmp.h=tet;@+read_tet();
|
353 |
|
|
if (listing) {
|
354 |
|
|
if (tmp.h || tet) printf("g%03d: %08x%08x\n",j,tmp.h,tet);
|
355 |
|
|
else printf("g%03d: 0\n",j);
|
356 |
|
|
}
|
357 |
|
|
}
|
358 |
|
|
|
359 |
|
|
@* The symbol table. Finally we come to the symbol table, which is
|
360 |
|
|
the most interesting part of this program because it recursively
|
361 |
|
|
traces an implicit ternary trie structure.
|
362 |
|
|
|
363 |
|
|
@=
|
364 |
|
|
read_tet();
|
365 |
|
|
if (buf[0]!=mm || buf[1]!=lop_stab) {
|
366 |
|
|
fprintf(stderr,"Symbol table does not follow the postamble!\n");
|
367 |
|
|
@.Symbol table...@>
|
368 |
|
|
exit(-6);
|
369 |
|
|
}
|
370 |
|
|
if (yz) fprintf(stderr,"YZ field of lop_stab should be zero!\n");
|
371 |
|
|
@.YZ field...should be zero@>
|
372 |
|
|
printf("Symbol table (beginning at tetra %d):\n",count);
|
373 |
|
|
stab_start=count;
|
374 |
|
|
sym_ptr=sym_buf;
|
375 |
|
|
print_stab();
|
376 |
|
|
@;
|
377 |
|
|
|
378 |
|
|
@ The main work is done by a recursive subroutine called |print_stab|,
|
379 |
|
|
which manipulates a global array |sym_buf| containing the current
|
380 |
|
|
symbol prefix; the global variable |sym_ptr| points to the first
|
381 |
|
|
unfilled character of that array.
|
382 |
|
|
|
383 |
|
|
@=
|
384 |
|
|
void print_stab @,@,@[ARGS((void))@];
|
385 |
|
|
void print_stab()
|
386 |
|
|
{
|
387 |
|
|
register int m=read_byte(); /* the master control byte */
|
388 |
|
|
register int c; /* the character at the current trie node */
|
389 |
|
|
register int j,k;
|
390 |
|
|
if (m&0x40) print_stab(); /* traverse the left subtrie, if it is nonempty */
|
391 |
|
|
if (m&0x2f) {
|
392 |
|
|
@;
|
393 |
|
|
*sym_ptr++=c;
|
394 |
|
|
if (sym_ptr==&sym_buf[sym_length_max]) {
|
395 |
|
|
fprintf(stderr,"Oops, the symbol is too long!\n");@+exit(-7);
|
396 |
|
|
@.Oops...too long@>
|
397 |
|
|
}
|
398 |
|
|
if (m&0xf)
|
399 |
|
|
@;
|
400 |
|
|
if (m&0x20) print_stab(); /* traverse the middle subtrie */
|
401 |
|
|
sym_ptr--;
|
402 |
|
|
}
|
403 |
|
|
if (m&0x10) print_stab(); /* traverse the right subtrie, if it is nonempty */
|
404 |
|
|
}
|
405 |
|
|
|
406 |
|
|
@ The present implementation doesn't support Unicode; characters with
|
407 |
|
|
more than 8-bit codes are printed as `\.?'. However, the changes
|
408 |
|
|
for 16-bit codes would be quite easy if proper fonts for Unicode output
|
409 |
|
|
were available. In that case, |sym_buf| would be an array of wyde characters.
|
410 |
|
|
@^Unicode@>
|
411 |
|
|
@^system dependencies@>
|
412 |
|
|
|
413 |
|
|
@=
|
414 |
|
|
if (m&0x80) j=read_byte(); /* 16-bit character */
|
415 |
|
|
else j=0;
|
416 |
|
|
c=read_byte();
|
417 |
|
|
if (j) c='?'; /* oops, we can't print |(j<<8)+c| easily at this time */
|
418 |
|
|
|
419 |
|
|
@ @=
|
420 |
|
|
{
|
421 |
|
|
*sym_ptr='\0';
|
422 |
|
|
j=m&0xf;
|
423 |
|
|
if (j==15) sprintf(equiv_buf,"$%03d",read_byte());
|
424 |
|
|
else if (j<=8) {
|
425 |
|
|
strcpy(equiv_buf,"#");
|
426 |
|
|
for (;j>0;j--) sprintf(equiv_buf+strlen(equiv_buf),"%02x",read_byte());
|
427 |
|
|
if (strcmp(equiv_buf,"#0000")==0) strcpy(equiv_buf,"?"); /* undefined */
|
428 |
|
|
}@+else {
|
429 |
|
|
strncpy(equiv_buf,"#20000000000000",33-2*j);
|
430 |
|
|
equiv_buf[33-2*j]='\0';
|
431 |
|
|
for (;j>8;j--) sprintf(equiv_buf+strlen(equiv_buf),"%02x",read_byte());
|
432 |
|
|
}
|
433 |
|
|
for (j=k=read_byte();; k=read_byte(),j=(j<<7)+k) if (k>=128) break;
|
434 |
|
|
/* the serial number is now $j-128$ */
|
435 |
|
|
printf(" %s = %s (%d)\n",sym_buf+1,equiv_buf,j-128);
|
436 |
|
|
}
|
437 |
|
|
|
438 |
|
|
@ @d sym_length_max 1000
|
439 |
|
|
|
440 |
|
|
@=
|
441 |
|
|
int stab_start; /* where the symbol table began */
|
442 |
|
|
char sym_buf[sym_length_max];
|
443 |
|
|
/* the characters on middle transitions to current node */
|
444 |
|
|
char *sym_ptr; /* the character in |sym_buf| following the current prefix */
|
445 |
|
|
char equiv_buf[20]; /* equivalent of the current symbol */
|
446 |
|
|
|
447 |
|
|
@ @=
|
448 |
|
|
while (byte_count)
|
449 |
|
|
if (read_byte()) fprintf(stderr,"Nonzero byte follows the symbol table!\n");
|
450 |
|
|
@.Nonzero byte follows...@>
|
451 |
|
|
read_tet();
|
452 |
|
|
if (buf[0]!=mm || buf[1]!=lop_end)
|
453 |
|
|
fprintf(stderr,"The symbol table isn't followed by lop_end!\n");
|
454 |
|
|
@.The symbol table isn't...@>
|
455 |
|
|
else if (count!=stab_start+yz+1)
|
456 |
|
|
fprintf(stderr,"YZ field at lop_end should have been %d!\n",count-yz-1);
|
457 |
|
|
@:YZ field at lop_end...}\.{YZ field at lop\_end...@>
|
458 |
|
|
else {
|
459 |
|
|
if (verbose) printf("Symbol table ends at tetra %d.\n",count);
|
460 |
|
|
if (fread(buf,1,1,mmo_file))
|
461 |
|
|
fprintf(stderr,"Extra bytes follow the lop_end!\n");
|
462 |
|
|
@.Extra bytes follow...@>
|
463 |
|
|
}
|
464 |
|
|
|
465 |
|
|
|
466 |
|
|
@* Index.
|