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{MMMIX}
|
5 |
|
|
\def\MMIX{\.{MMIX}}
|
6 |
|
|
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant
|
7 |
|
|
@s octa int
|
8 |
|
|
@s tetra int
|
9 |
|
|
@s bool int
|
10 |
|
|
@s fetch int
|
11 |
|
|
@s specnode int
|
12 |
|
|
|
13 |
|
|
@* Introduction.
|
14 |
|
|
This \.{CWEB} program simulates how the \MMIX\ computer might be
|
15 |
|
|
implemented with a high-performance pipeline in many different configurations.
|
16 |
|
|
All of the complexities of \MMIX's architecture are treated, except for
|
17 |
|
|
multiprocessing and low-level details of memory mapped input/output.
|
18 |
|
|
|
19 |
|
|
The present program module, which contains the main routine for the
|
20 |
|
|
\MMIX\ meta-simulator, is primarily devoted to administrative tasks. Other modules
|
21 |
|
|
do the actual work after this module has told them what to do.
|
22 |
|
|
|
23 |
|
|
@ A user typically invokes the meta-simulator with a \UNIX/-like command line
|
24 |
|
|
of the general form
|
25 |
|
|
`\.{mmmix}~\.{configfile}~\.{progfile}',
|
26 |
|
|
where the \.{configfile} describes the characteristics
|
27 |
|
|
of an \MMIX\ implementation and the \.{progfile} contains a program to
|
28 |
|
|
be downloaded and run. Rules for configuration files appear in
|
29 |
|
|
the module called \.{mmix-config}. The program file is either
|
30 |
|
|
an ``\MMIX\ binary file'' dumped by {\mc MMIX-SIM}, or an
|
31 |
|
|
ASCII text file that describes hexadecimal data
|
32 |
|
|
in a rudimentary format. It is assumed to be binary if
|
33 |
|
|
its name ends with the extension `\.{.mmb}'.
|
34 |
|
|
|
35 |
|
|
@c
|
36 |
|
|
#include
|
37 |
|
|
#include
|
38 |
|
|
#include
|
39 |
|
|
#include "mmix-pipe.h"
|
40 |
|
|
@#
|
41 |
|
|
char *config_file_name, *prog_file_name;
|
42 |
|
|
@@;
|
43 |
|
|
@@;
|
44 |
|
|
|
45 |
|
|
int main(argc,argv)
|
46 |
|
|
int argc;
|
47 |
|
|
char *argv[];
|
48 |
|
|
{
|
49 |
|
|
@;
|
50 |
|
|
MMIX_config(config_file_name);
|
51 |
|
|
MMIX_init();
|
52 |
|
|
mmix_io_init();
|
53 |
|
|
@;
|
54 |
|
|
@;
|
55 |
|
|
printf("Simulation ended at time %d.\n",ticks.l);
|
56 |
|
|
print_stats();
|
57 |
|
|
return 0;
|
58 |
|
|
}
|
59 |
|
|
|
60 |
|
|
@ The command line might also contain options, some day.
|
61 |
|
|
For now I'm forgetting them and simplifying everything until I gain
|
62 |
|
|
further experience.
|
63 |
|
|
|
64 |
|
|
@=
|
65 |
|
|
if (argc!=3) {
|
66 |
|
|
fprintf(stderr,"Usage: %s configfile progfile\n",argv[0]);
|
67 |
|
|
@.Usage: ...@>
|
68 |
|
|
exit(-3);
|
69 |
|
|
}
|
70 |
|
|
config_file_name=argv[1];
|
71 |
|
|
prog_file_name=argv[2];
|
72 |
|
|
|
73 |
|
|
@ @=
|
74 |
|
|
if (strlen(prog_file_name)>4 &&
|
75 |
|
|
strcmp(prog_file_name+strlen(prog_file_name)-4,".mmb")==0)
|
76 |
|
|
@@;
|
77 |
|
|
else @;
|
78 |
|
|
fclose(prog_file);
|
79 |
|
|
|
80 |
|
|
@* Hexadecimal input to memory.
|
81 |
|
|
A rudimentary hexadecimal input format is implemented here so that the
|
82 |
|
|
@^hexadecimal files@>
|
83 |
|
|
simulator can be run with essentially arbitrary data in the simulated memory.
|
84 |
|
|
The rules of this format are extremely simple: Each line of the file
|
85 |
|
|
either begins with (i)~12 hexadecimal digits followed by a colon; or
|
86 |
|
|
(ii)~a space followed by 16 hexadecimal digits. In case~(i), the 12
|
87 |
|
|
hex digits specify a 48-bit physical address, called the current
|
88 |
|
|
location. In case~(ii), the 16 hex digits specify an octabyte to be
|
89 |
|
|
stored in the current location; the current location is then increased by~8.
|
90 |
|
|
The current location should be a multiple of~8, but its three least
|
91 |
|
|
significant bits are actually ignored. Arbitrary comments can follow
|
92 |
|
|
the specification of a new current location or a new octabyte, as long
|
93 |
|
|
as each line is less than 99 characters long. For example, the file
|
94 |
|
|
$$\vbox{\halign{\tt#\hfil\cr
|
95 |
|
|
0123456789ab: SILLY EXAMPLE\cr
|
96 |
|
|
\ 0123456789abcdef first octabyte\cr
|
97 |
|
|
\ fedbca9876543210 second\cr}}$$
|
98 |
|
|
places the octabyte
|
99 |
|
|
\Hex{0123456789abcdef} into memory location \Hex{0123456789a8}
|
100 |
|
|
and \Hex{fedcba9876543210} into location \Hex{0123456789b0}.
|
101 |
|
|
|
102 |
|
|
@d BUF_SIZE 100
|
103 |
|
|
|
104 |
|
|
@=
|
105 |
|
|
octa cur_loc;
|
106 |
|
|
octa cur_dat;
|
107 |
|
|
bool new_chunk;
|
108 |
|
|
char buffer[BUF_SIZE];
|
109 |
|
|
FILE *prog_file;
|
110 |
|
|
|
111 |
|
|
@ @=
|
112 |
|
|
{
|
113 |
|
|
prog_file=fopen(prog_file_name,"r");
|
114 |
|
|
if (!prog_file) {
|
115 |
|
|
fprintf(stderr,"Panic: Can't open MMIX hexadecimal file %s!\n",prog_file_name);
|
116 |
|
|
@.Can't open...@>
|
117 |
|
|
exit(-3);
|
118 |
|
|
}
|
119 |
|
|
new_chunk=true;
|
120 |
|
|
while (1) {
|
121 |
|
|
if (!fgets(buffer,BUF_SIZE,prog_file)) break;
|
122 |
|
|
if (buffer[strlen(buffer)-1]!='\n') {
|
123 |
|
|
fprintf(stderr,"Panic: Hexadecimal file line too long: `%s...'!\n",buffer);
|
124 |
|
|
@.Hexadecimal file line...@>
|
125 |
|
|
exit(-3);
|
126 |
|
|
}
|
127 |
|
|
if (buffer[12]==':') @@;
|
128 |
|
|
else if (buffer[0]==' ') @@;
|
129 |
|
|
else {
|
130 |
|
|
fprintf(stderr,"Panic: Improper hexadecimal file line: `%s'!\n",buffer);
|
131 |
|
|
@.Improper hexadecimal...@>
|
132 |
|
|
exit(-3);
|
133 |
|
|
}
|
134 |
|
|
}
|
135 |
|
|
}
|
136 |
|
|
|
137 |
|
|
@ @=
|
138 |
|
|
{
|
139 |
|
|
if (sscanf(buffer,"%4x%8x",&cur_loc.h,&cur_loc.l)!=2) {
|
140 |
|
|
fprintf(stderr,"Panic: Improper hexadecimal file location: `%s'!\n",buffer);
|
141 |
|
|
@.Improper hexadecimal...@>
|
142 |
|
|
exit(-3);
|
143 |
|
|
}
|
144 |
|
|
new_chunk=true;
|
145 |
|
|
}
|
146 |
|
|
|
147 |
|
|
@ @=
|
148 |
|
|
{
|
149 |
|
|
if (sscanf(buffer+1,"%8x%8x",&cur_dat.h,&cur_dat.l)!=2) {
|
150 |
|
|
fprintf(stderr,"Panic: Improper hexadecimal file data: `%s'!\n",buffer);
|
151 |
|
|
@.Improper hexadecimal...@>
|
152 |
|
|
exit(-3);
|
153 |
|
|
}
|
154 |
|
|
if (new_chunk) mem_write(cur_loc,cur_dat);
|
155 |
|
|
else mem_hash[last_h].chunk[(cur_loc.l&0xffff)>>3]=cur_dat;
|
156 |
|
|
cur_loc.l+=8;
|
157 |
|
|
if ((cur_loc.l&0xfff8)!=0) new_chunk=false;
|
158 |
|
|
else {
|
159 |
|
|
new_chunk=true;
|
160 |
|
|
if ((cur_loc.l&0xffff0000)==0) cur_loc.h++;
|
161 |
|
|
}
|
162 |
|
|
}
|
163 |
|
|
|
164 |
|
|
@* Binary input to memory.
|
165 |
|
|
When the program file was dumped by {\mc MMIX-SIM}, it
|
166 |
|
|
has the simple format discussed in exercise 1.4.3$'$--20 of the \MMIX\ fascicle.
|
167 |
|
|
@^binary files@>
|
168 |
|
|
@^segments@>
|
169 |
|
|
In this case we assume that the user's program has text, data, pool, and stack
|
170 |
|
|
segments, as in the conventions of that book.
|
171 |
|
|
We load it into four
|
172 |
|
|
$2^{32}$-byte pages of physical memory, one for each segment; page zero of
|
173 |
|
|
segment~$i$ is mapped to physical location $2^{32}i$. Page tables are kept in
|
174 |
|
|
physical locations starting at $2^{32}\times4$; static traps begin at
|
175 |
|
|
$2^{32}\times 5$ and dynamic traps at $2^{32}\times6$. (These conventions
|
176 |
|
|
agree with the special register settings
|
177 |
|
|
$\rm rT=\Hex{8000000500000000}$,
|
178 |
|
|
$\rm rTT=\Hex{8000000600000000}$,
|
179 |
|
|
$\rm rV=\Hex{369c200400000000}$
|
180 |
|
|
assumed by the stripped-down simulator.)
|
181 |
|
|
|
182 |
|
|
@=
|
183 |
|
|
{
|
184 |
|
|
prog_file=fopen(prog_file_name,"rb");
|
185 |
|
|
if (!prog_file) {
|
186 |
|
|
fprintf(stderr,"Panic: Can't open MMIX binary file %s!\n",prog_file_name);
|
187 |
|
|
@.Can't open...@>
|
188 |
|
|
exit(-3);
|
189 |
|
|
}
|
190 |
|
|
while (1) {
|
191 |
|
|
if (!undump_octa()) break;
|
192 |
|
|
new_chunk=true;
|
193 |
|
|
cur_loc=cur_dat;
|
194 |
|
|
if (cur_loc.h&0x9fffffff) bad_address=true;
|
195 |
|
|
else bad_address=false, cur_loc.h >>= 29;
|
196 |
|
|
/* apply trivial mapping function for each segment */
|
197 |
|
|
@;
|
198 |
|
|
}
|
199 |
|
|
@;
|
200 |
|
|
}
|
201 |
|
|
|
202 |
|
|
@ The |undump_octa| routine reads eight bytes from the binary file
|
203 |
|
|
|prog_file| into the global octabyte |cur_dat|,
|
204 |
|
|
taking care as usual to be big-endian regardless of the host computer's bias.
|
205 |
|
|
@^big-endian versus little-endian@>
|
206 |
|
|
@^little-endian versus big-endian@>
|
207 |
|
|
|
208 |
|
|
@=
|
209 |
|
|
static bool undump_octa @,@,@[ARGS((void))@];@+@t}\6{@>
|
210 |
|
|
static bool undump_octa()
|
211 |
|
|
{
|
212 |
|
|
register int t0,t1,t2,t3;
|
213 |
|
|
t0=fgetc(prog_file);@+ if (t0==EOF) return false;
|
214 |
|
|
t1=fgetc(prog_file);@+ if (t1==EOF) goto oops;
|
215 |
|
|
t2=fgetc(prog_file);@+ if (t2==EOF) goto oops;
|
216 |
|
|
t3=fgetc(prog_file);@+ if (t3==EOF) goto oops;
|
217 |
|
|
cur_dat.h=(t0<<24)+(t1<<16)+(t2<<8)+t3;
|
218 |
|
|
t0=fgetc(prog_file);@+ if (t0==EOF) goto oops;
|
219 |
|
|
t1=fgetc(prog_file);@+ if (t1==EOF) goto oops;
|
220 |
|
|
t2=fgetc(prog_file);@+ if (t2==EOF) goto oops;
|
221 |
|
|
t3=fgetc(prog_file);@+ if (t3==EOF) goto oops;
|
222 |
|
|
cur_dat.l=(t0<<24)+(t1<<16)+(t2<<8)+t3;
|
223 |
|
|
return true;
|
224 |
|
|
oops: fprintf(stderr,"Premature end of file on %s!\n",prog_file_name);
|
225 |
|
|
@.Premature end of file...@>
|
226 |
|
|
return false;
|
227 |
|
|
}
|
228 |
|
|
|
229 |
|
|
@ @=
|
230 |
|
|
while (1) {
|
231 |
|
|
if (!undump_octa()) {
|
232 |
|
|
fprintf(stderr,"Unexpected end of file on %s!\n",prog_file_name);
|
233 |
|
|
@.Unexpected end of file...@>
|
234 |
|
|
break;
|
235 |
|
|
}
|
236 |
|
|
if (!(cur_dat.h || cur_dat.l)) break;
|
237 |
|
|
if (bad_address) {
|
238 |
|
|
fprintf(stderr,"Panic: Unsupported virtual address %08x%08x!\n",
|
239 |
|
|
@.Unsupported virtual address@>
|
240 |
|
|
cur_loc.h,cur_loc.l);
|
241 |
|
|
exit(-5);
|
242 |
|
|
}
|
243 |
|
|
if (new_chunk) mem_write(cur_loc,cur_dat);
|
244 |
|
|
else mem_hash[last_h].chunk[(cur_loc.l&0xffff)>>3]=cur_dat;
|
245 |
|
|
cur_loc.l+=8;
|
246 |
|
|
if ((cur_loc.l&0xfff8)!=0) new_chunk=false;
|
247 |
|
|
else {
|
248 |
|
|
new_chunk=true;
|
249 |
|
|
if ((cur_loc.l&0xffff0000)==0) {
|
250 |
|
|
bad_address=true; cur_loc.h=(cur_loc.h<<29)+1;
|
251 |
|
|
}
|
252 |
|
|
}
|
253 |
|
|
}
|
254 |
|
|
|
255 |
|
|
@ The primitive operating system assumed in simple programs of {\sl The
|
256 |
|
|
Art of Computer Programming\/} will set up text segment, data segment,
|
257 |
|
|
pool segment, and stack segment as in {\mc MMIX-SIM}. The runtime stack
|
258 |
|
|
will be initialized if we \.{UNSAVE} from the last location loaded
|
259 |
|
|
in the \.{.mmb} file.
|
260 |
|
|
|
261 |
|
|
@d rQ 16
|
262 |
|
|
|
263 |
|
|
@=
|
264 |
|
|
if (cur_loc.h!=3) {
|
265 |
|
|
fprintf(stderr,"Panic: MMIX binary file didn't set up the stack!\n");
|
266 |
|
|
@.MMIX binary file...@>
|
267 |
|
|
exit(-6);
|
268 |
|
|
}
|
269 |
|
|
inst_ptr.o=mem_read(incr(cur_loc,-8*14)); /* \.{Main} */
|
270 |
|
|
inst_ptr.p=NULL;
|
271 |
|
|
cur_loc.h=0x60000000;
|
272 |
|
|
g[255].o=incr(cur_loc,-8); /* place to \.{UNSAVE} */
|
273 |
|
|
cur_dat.l=0x90;
|
274 |
|
|
if (mem_read(cur_dat).h) inst_ptr.o=cur_dat; /* start at |0x90| if nonzero */
|
275 |
|
|
head->inst=(UNSAVE<<24)+255, tail--; /* prefetch a fabricated command */
|
276 |
|
|
head->loc=incr(inst_ptr.o,-4); /* in case the \.{UNSAVE} is interrupted */
|
277 |
|
|
g[rT].o.h=0x80000005, g[rTT].o.h=0x80000006;
|
278 |
|
|
cur_dat.h=(RESUME<<24)+1, cur_dat.l=0, cur_loc.h=5, cur_loc.l=0;
|
279 |
|
|
mem_write(cur_loc,cur_dat); /* the primitive trap handler */
|
280 |
|
|
cur_dat.l=cur_dat.h, cur_dat.h=(NEGI<<24)+(255<<16)+1;
|
281 |
|
|
cur_loc.h=6, cur_loc.l=8;
|
282 |
|
|
mem_write(cur_loc,cur_dat); /* the primitive dynamic trap handler */
|
283 |
|
|
cur_dat.h=(GET<<24)+rQ, cur_dat.l=(PUTI<<24)+(rQ<<16), cur_loc.l=0;
|
284 |
|
|
mem_write(cur_loc,cur_dat); /* more of the primitive dynamic trap handler */
|
285 |
|
|
cur_dat.h=0, cur_dat.l=7; /* generate a PTE with \.{rwx} permission */
|
286 |
|
|
cur_loc.h=4; /* beginning of skeleton page table */
|
287 |
|
|
mem_write(cur_loc,cur_dat); /* PTE for the text segment */
|
288 |
|
|
ITcache->set[0][0].tag=zero_octa;
|
289 |
|
|
ITcache->set[0][0].data[0]=cur_dat; /* prime the IT cache */
|
290 |
|
|
cur_dat.l=6; /* PTE with read and write permission only */
|
291 |
|
|
cur_dat.h=1, cur_loc.l=3<<13;
|
292 |
|
|
mem_write(cur_loc,cur_dat); /* PTE for the data segment */
|
293 |
|
|
cur_dat.h=2, cur_loc.l=6<<13;
|
294 |
|
|
mem_write(cur_loc,cur_dat); /* PTE for the pool segment */
|
295 |
|
|
cur_dat.h=3, cur_loc.l=9<<13;
|
296 |
|
|
mem_write(cur_loc,cur_dat); /* PTE for the stack segment */
|
297 |
|
|
g[rK].o=neg_one; /* enable all interrupts */
|
298 |
|
|
g[rV].o.h=0x369c2004;
|
299 |
|
|
page_bad=false, page_r=4<<(32-13), page_s=32, page_mask.l=0xffffffff;
|
300 |
|
|
page_b[1]=3, page_b[2]=6, page_b[3]=9, page_b[4]=12;
|
301 |
|
|
|
302 |
|
|
@* Interaction. When prompted for instructions, this simulator
|
303 |
|
|
@.mmmix>@>
|
304 |
|
|
understands the following terse commands:
|
305 |
|
|
|
306 |
|
|
\def\bull{\smallbreak\textindent{$\bullet$}}
|
307 |
|
|
\def\<#1>{$\langle\,$#1$\,\rangle$}
|
308 |
|
|
\bull\: Run for this many clock cycles.
|
309 |
|
|
|
310 |
|
|
\bull\.{@@}\: Set the instruction pointer
|
311 |
|
|
to this virtual address; successive instructions will be fetched from here.
|
312 |
|
|
|
313 |
|
|
\bull\.{b}\: Set the breakpoint
|
314 |
|
|
to this virtual address; simulation will pause when an instruction from the
|
315 |
|
|
breakpoint address enters the fetch buffer.
|
316 |
|
|
|
317 |
|
|
\bull\.v\: Set the desired level of diagnostic
|
318 |
|
|
output; each bit in the hexadecimal integer enables certain printouts
|
319 |
|
|
when the simulator is running. Bit \Hex1 shows instructions when issued,
|
320 |
|
|
deissued, or committed; \Hex2 shows the pipeline and locks after each cycle;
|
321 |
|
|
\Hex4 shows each coroutine activation; \Hex8 each coroutine scheduling;
|
322 |
|
|
\Hex{10} reports when reading from an uninitialized chunk of memory;
|
323 |
|
|
\Hex{20} asks for online input when reading from addresses $\ge2^{48}$;
|
324 |
|
|
\Hex{40} reports all I/O to memory address $\ge2^{48}$;
|
325 |
|
|
\Hex{80} shows details of branch prediction;
|
326 |
|
|
\Hex{100} displays full cache contents including blocks with invalid tags.
|
327 |
|
|
|
328 |
|
|
\bull\.-\: Deissue this many instructions.
|
329 |
|
|
|
330 |
|
|
\bull\.l\ or \.g\: Show current ``hot'' contents
|
331 |
|
|
of a local or global register.
|
332 |
|
|
|
333 |
|
|
\bull\.m\: Show current contents of a physical memory
|
334 |
|
|
address. (This value may not be up to date; newer values might appear
|
335 |
|
|
in the write buffer and/or in the caches.)
|
336 |
|
|
|
337 |
|
|
\bull\.f\: Insert a tetrabyte into the fetch buffer.
|
338 |
|
|
(Use with care!)
|
339 |
|
|
|
340 |
|
|
\bull\.i\: Set the interval counter rI to the given value; this will
|
341 |
|
|
trigger an interrupt after the specified number of cycles.
|
342 |
|
|
|
343 |
|
|
\bull\.{IT}, \.{DT}, \.I, \.D, or \.S: Show current contents of a cache.
|
344 |
|
|
|
345 |
|
|
\bull\.{D*} or \.{S*}: Show dirty blocks of a cache.
|
346 |
|
|
|
347 |
|
|
\bull\.p: Show current contents of the pipeline.
|
348 |
|
|
|
349 |
|
|
\bull\.s: Show current statistics on branch prediction and
|
350 |
|
|
speed of instruction issue.
|
351 |
|
|
|
352 |
|
|
\bull\.h: Help (show the possibilities for interaction).
|
353 |
|
|
|
354 |
|
|
\bull\.q: Quit.
|
355 |
|
|
|
356 |
|
|
@=
|
357 |
|
|
while (1) {
|
358 |
|
|
printf("mmmix> ");@+fflush(stdout);
|
359 |
|
|
@.mmmix>@>
|
360 |
|
|
fgets(buffer,BUF_SIZE,stdin);
|
361 |
|
|
switch (buffer[0]) {
|
362 |
|
|
default: what_say:
|
363 |
|
|
printf("Eh? Sorry, I don't understand. (Type h for help)\n");
|
364 |
|
|
continue;
|
365 |
|
|
case 'q': case 'x': goto done;
|
366 |
|
|
@@;
|
367 |
|
|
}
|
368 |
|
|
}
|
369 |
|
|
done:@;
|
370 |
|
|
|
371 |
|
|
@ @=
|
372 |
|
|
case 'h': case '?': printf("The interactive commands are as follows:\n");
|
373 |
|
|
printf(" to run for n cycles\n");
|
374 |
|
|
printf(" @@ to take next instruction from location x\n");
|
375 |
|
|
printf(" b to pause when location x is fetched\n");
|
376 |
|
|
printf(" v to print specified diagnostics when running;\n");
|
377 |
|
|
printf(" x=1[insts enter/leave pipe]+2[whole pipeline each cycle]+\n");
|
378 |
|
|
printf(" 4[coroutine activations]+8[coroutine scheduling]+\n");
|
379 |
|
|
printf(" 10[uninitialized read]+20[online I/O read]+\n");
|
380 |
|
|
printf(" 40[I/O read/write]+80[branch prediction details]+\n");
|
381 |
|
|
printf(" 100[invalid cache blocks displayed too]\n");
|
382 |
|
|
printf(" - to deissue n instructions\n");
|
383 |
|
|
printf(" l to print current value of local register n\n");
|
384 |
|
|
printf(" g to print current value of global register n\n");
|
385 |
|
|
printf(" m to print current value of memory address x\n");
|
386 |
|
|
printf(" f to insert instruction x into the fetch buffer\n");
|
387 |
|
|
printf(" i to initiate a timer interrupt after n cycles\n");
|
388 |
|
|
printf(" IT, DT, I, D, or S to print current cache contents\n");
|
389 |
|
|
printf(" D* or S* to print dirty blocks of a cache\n");
|
390 |
|
|
printf(" p to print current pipeline contents\n");
|
391 |
|
|
printf(" s to print current stats\n");
|
392 |
|
|
printf(" h to print this message\n");
|
393 |
|
|
printf(" q to exit\n");
|
394 |
|
|
printf("(Here is a decimal integer, is hexadecimal.)\n");
|
395 |
|
|
continue;
|
396 |
|
|
|
397 |
|
|
@ @=
|
398 |
|
|
case '0': case '1': case '2': case '3': case '4':
|
399 |
|
|
case '5': case '6': case '7': case '8': case '9':
|
400 |
|
|
if (sscanf(buffer,"%d",&n)!=1) goto what_say;
|
401 |
|
|
printf("Running %d at time %d",n,ticks.l);
|
402 |
|
|
if (bp.h==(tetra)-1 && bp.l==(tetra)-1) printf("\n");
|
403 |
|
|
else printf(" with breakpoint %08x%08x\n",bp.h,bp.l);
|
404 |
|
|
MMIX_run(n,bp);@+continue;
|
405 |
|
|
case '@@': inst_ptr.o=read_hex(buffer+1);@+inst_ptr.p=NULL;@+continue;
|
406 |
|
|
case 'b': bp=read_hex(buffer+1);@+continue;
|
407 |
|
|
case 'v': verbose=read_hex(buffer+1).l;@+continue;
|
408 |
|
|
|
409 |
|
|
@ @=
|
410 |
|
|
int n,m; /* temporary integer */
|
411 |
|
|
octa bp={-1,-1}; /* breakpoint */
|
412 |
|
|
octa tmp; /* an octabyte of temporary interest */
|
413 |
|
|
static unsigned char d[BUF_SIZE];
|
414 |
|
|
|
415 |
|
|
@ Here's a simple program to read an octabyte in hexadecimal notation
|
416 |
|
|
from a buffer. It changes the buffer by storing a null character
|
417 |
|
|
after the input.
|
418 |
|
|
@^radix conversion@>
|
419 |
|
|
|
420 |
|
|
@=
|
421 |
|
|
octa read_hex @,@,@[ARGS((char *))@];@+@t}\6{@>
|
422 |
|
|
octa read_hex(p)
|
423 |
|
|
char *p;
|
424 |
|
|
{
|
425 |
|
|
register int j,k;
|
426 |
|
|
octa val;
|
427 |
|
|
val.h=val.l=0;
|
428 |
|
|
for (j=0;;j++) {
|
429 |
|
|
if (p[j]>='0' && p[j]<='9') d[j]=p[j]-'0';
|
430 |
|
|
else if (p[j]>='a' && p[j]<='f') d[j]=p[j]-'a'+10;
|
431 |
|
|
else if (p[j]>='A' && p[j]<='F') d[j]=p[j]-'A'+10;
|
432 |
|
|
else break;
|
433 |
|
|
}
|
434 |
|
|
p[j]='\0';
|
435 |
|
|
for (j--,k=0;k<=j;k++) {
|
436 |
|
|
if (k>=8) val.h+=d[j-k]<<(4*k-32);
|
437 |
|
|
else val.l+=d[j-k]<<(4*k);
|
438 |
|
|
}
|
439 |
|
|
return val;
|
440 |
|
|
}
|
441 |
|
|
|
442 |
|
|
@ @=
|
443 |
|
|
case '-':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
|
444 |
|
|
if (cool<=hot) m=hot-cool;@+else m=(hot-reorder_bot)+1+(reorder_top-cool);
|
445 |
|
|
if (n>m) deissues=m;@+else deissues=n;
|
446 |
|
|
continue;
|
447 |
|
|
case 'l':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
|
448 |
|
|
if (n>=lring_size) goto what_say;
|
449 |
|
|
printf(" l[%d]=%08x%08x\n",n,l[n].o.h,l[n].o.l);@+continue;
|
450 |
|
|
case 'm': tmp=mem_read(read_hex(buffer+1));
|
451 |
|
|
printf(" m[%s]=%08x%08x\n",buffer+1,tmp.h,tmp.l);@+continue;
|
452 |
|
|
|
453 |
|
|
@ The register stack pointers, rO and rS, are not kept up to date
|
454 |
|
|
in the |g| array. Therefore we have to deduce their values by
|
455 |
|
|
examining the pipeline.
|
456 |
|
|
|
457 |
|
|
@=
|
458 |
|
|
case 'g':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
|
459 |
|
|
if (n>=256) goto what_say;
|
460 |
|
|
if (n==rO || n==rS) {
|
461 |
|
|
if (hot==cool) /* pipeline empty */
|
462 |
|
|
g[rO].o=sl3(cool_O), g[rS].o=sl3(cool_S);
|
463 |
|
|
else g[rO].o=sl3(hot->cur_O), g[rS].o=sl3(hot->cur_S);
|
464 |
|
|
}
|
465 |
|
|
printf(" g[%d]=%08x%08x\n",n,g[n].o.h,g[n].o.l);
|
466 |
|
|
continue;
|
467 |
|
|
|
468 |
|
|
@ @=
|
469 |
|
|
static octa sl3 @,@,@[ARGS((octa))@];@+@t}\6{@>
|
470 |
|
|
static octa sl3(y) /* shift left by 3 bits */
|
471 |
|
|
octa y;
|
472 |
|
|
{
|
473 |
|
|
register tetra yhl=y.h<<3, ylh=y.l>>29;
|
474 |
|
|
y.h=yhl+ylh;@+ y.l<<=3;
|
475 |
|
|
return y;
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
@ @=
|
479 |
|
|
case 'I': print_cache(buffer[1]=='T'? ITcache: Icache,false);@+continue;
|
480 |
|
|
case 'D': print_cache(buffer[1]=='T'? DTcache: Dcache,@/
|
481 |
|
|
buffer[1]=='*');@+continue;
|
482 |
|
|
case 'S': print_cache(Scache,buffer[1]=='*');@+continue;
|
483 |
|
|
case 'p': print_pipe();@+print_locks();@+continue;
|
484 |
|
|
case 's': print_stats();@+continue;
|
485 |
|
|
case 'i':@+ if (sscanf(buffer+1,"%d",&n)==1) g[rI].o=incr(zero_octa,n);
|
486 |
|
|
continue;
|
487 |
|
|
|
488 |
|
|
@ @=
|
489 |
|
|
case 'f': tmp=read_hex(buffer+1);
|
490 |
|
|
{
|
491 |
|
|
register fetch* new_tail;
|
492 |
|
|
if (tail==fetch_bot) new_tail=fetch_top;
|
493 |
|
|
else new_tail=tail-1;
|
494 |
|
|
if (new_tail==head) printf("Sorry, the fetch buffer is full!\n");
|
495 |
|
|
else {
|
496 |
|
|
tail->loc=inst_ptr.o;
|
497 |
|
|
tail->inst=tmp.l;
|
498 |
|
|
tail->interrupt=0;
|
499 |
|
|
tail->noted=false;
|
500 |
|
|
tail=new_tail;
|
501 |
|
|
}
|
502 |
|
|
continue;
|
503 |
|
|
}
|
504 |
|
|
|
505 |
|
|
@ A hidden case here, for me when debugging.
|
506 |
|
|
It essentially disables the translation caches, by mapping everything
|
507 |
|
|
to zero.
|
508 |
|
|
|
509 |
|
|
@=
|
510 |
|
|
case 'd':@+if (ticks.l)
|
511 |
|
|
printf("Sorry: I disable ITcache and DTcache only at the beginning!\n");
|
512 |
|
|
else {
|
513 |
|
|
ITcache->set[0][0].tag=zero_octa;
|
514 |
|
|
ITcache->set[0][0].data[0]=seven_octa;
|
515 |
|
|
DTcache->set[0][0].tag=zero_octa;
|
516 |
|
|
DTcache->set[0][0].data[0]=seven_octa;
|
517 |
|
|
g[rK].o=neg_one;
|
518 |
|
|
page_bad=false;
|
519 |
|
|
page_mask=neg_one;
|
520 |
|
|
inst_ptr.p=(specnode*)1;
|
521 |
|
|
}@+continue;
|
522 |
|
|
|
523 |
|
|
@ And another case, for me when kludging. At the moment,
|
524 |
|
|
it simply lists the functional unit names.
|
525 |
|
|
|
526 |
|
|
But I might decide to put other stuff here when giving a demo.
|
527 |
|
|
|
528 |
|
|
@=
|
529 |
|
|
case 'k':@+ { register int j;
|
530 |
|
|
for (j=0;j
|
531 |
|
|
printf("unit %s %d\n",funit[j].name,funit[j].k);
|
532 |
|
|
}
|
533 |
|
|
continue;
|
534 |
|
|
|
535 |
|
|
@ @=
|
536 |
|
|
bool bad_address;
|
537 |
|
|
extern bool page_bad;
|
538 |
|
|
extern octa page_mask;
|
539 |
|
|
extern int page_r,page_s,page_b[5];
|
540 |
|
|
extern octa zero_octa;
|
541 |
|
|
extern octa neg_one;
|
542 |
|
|
octa seven_octa={0,7};
|
543 |
|
|
extern octa incr @,@,@[ARGS((octa y,int delta))@];
|
544 |
|
|
/* unsigned $y+\delta$ ($\delta$ is signed) */
|
545 |
|
|
extern void mmix_io_init @,@,@[ARGS((void))@];
|
546 |
|
|
extern void MMIX_config @,@,@[ARGS((char*))@];
|
547 |
|
|
|
548 |
|
|
@* Index.
|