OpenCores
URL https://opencores.org/ocsvn/eco32/eco32/trunk

Subversion Repositories eco32

[/] [eco32/] [trunk/] [fp/] [implementation/] [mmix/] [mmix-sim.w] - Blame information for rev 15

Details | Compare with Previous | View Log

Line No. Rev Author Line
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{MMIX-SIM}
5
\def\MMIX{\.{MMIX}}
6
\def\NNIX{\hbox{\mc NNIX}}
7
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant
8
\def\<#1>{\hbox{$\langle\,$#1$\,\rangle$}}\let\is=\longrightarrow
9
\def\dts{\mathinner{\ldotp\ldotp}}
10
\def\bull{\smallskip\textindent{$\bullet$}}
11
@s xor normal @q unreserve a C++ keyword @>
12
@s bool normal @q unreserve a C++ keyword @>
13
 
14
@*Introduction. This program simulates a simplified version of the \MMIX\
15
computer. Its main goal is to help people create and test \MMIX\ programs for
16
{\sl The Art of Computer Programming\/} and related publications. It provides
17
only a rudimentary terminal-oriented interface, but it has enough
18
infrastructure to support a cool graphical user interface --- which could be
19
added by a motivated reader. (Hint, hint.)
20
 
21
\MMIX\ is simplified in the following ways:
22
 
23
\bull
24
There is no pipeline, and there are no
25
caches. Thus, commands like \.{SYNC} and \.{SYNCD} and \.{PREGO} do nothing.
26
 
27
\bull
28
Simulation applies only to user programs, not to an operating system kernel.
29
Thus, all addresses must be nonnegative; ``privileged'' commands such as
30
\.{PUT}~\.{rK,z} or \.{RESUME}~\.1 or \.{LDVTS}~\.{x,y,z} are not allowed;
31
instructions should be executed only from addresses in segment~0
32
(addresses less than \Hex{2000000000000000}).
33
Certain special registers remain constant: $\rm rF=0$,
34
$\rm rK=\Hex{ffffffffffffffff}$,
35
$\rm rQ=0$;
36
$\rm rT=\Hex{8000000500000000}$,
37
$\rm rTT=\Hex{8000000600000000}$,
38
$\rm rV=\Hex{369c200400000000}$.
39
 
40
\bull
41
No trap interrupts are implemented, except for a few special cases of \.{TRAP}
42
that provide rudimentary input-output.
43
@^interrupts@>
44
 
45
\bull
46
All instructions take a fixed amount of time, given by the rough estimates
47
stated in the \MMIX\ documentation. For example, \.{MUL} takes $10\upsilon$,
48
\.{LDB} takes $\mu+\upsilon\mkern1mu$; all times are expressed in terms of
49
$\mu$ and~$\upsilon$, ``mems'' and ``oops.'' The clock register~rC increases by
50
@^mems@>
51
@^oops@>
52
$2^{32}$ for each~$\mu$ and 1~for each~$\upsilon$. But the interval
53
counter~rI decreases by~1 for each instruction, and the usage
54
counter~rU increases by~1 for each instruction.
55
@^rC@>
56
@^rI@>
57
@^rU@>
58
 
59
@ To run this simulator, assuming \UNIX/ conventions, you say
60
`\.{mmix} \ \.{progfile} \.{args...}',
61
where \.{progfile} is an output of the \.{MMIXAL} assembler,
62
\.{args...} is a sequence of optional command-line arguments passed
63
to the simulated program, and \ is any subset of the following:
64
@^command line arguments@>
65
 
66
\bull \.{-t}\quad Trace each instruction the first $n$ times it
67
is executed. (The notation \.{} in this option, and in several
68
other options and interactive commands below, stands for a decimal integer.)
69
 
70
\bull \.{-e}\quad Trace each instruction that raises an arithmetic
71
exception belonging to the given bit pattern. (The notation \.{} in this
72
option, and in several other commands below, stands for a hexadecimal integer.)
73
The exception bits are DVWIOUZX as they appear in rA, namely
74
\Hex{80} for~D (integer divide check), \Hex{40} for~V (integer overflow),
75
\dots, \Hex{01} for~X (floating inexact). The option \.{-e} by itself
76
is equivalent to \.{-eff}, tracing all eight exceptions.
77
 
78
\bull \.{-r}\quad Trace details of the register stack. This option
79
shows all the ``hidden'' loads and stores that occur when octabytes are
80
written from the ring of local registers into memory, or read from memory into
81
that ring. It also shows the full details of \.{SAVE} and \.{UNSAVE}
82
operations.
83
 
84
\bull \.{-l}\quad List the source line corresponding to each traced
85
instruction, filling gaps of length $n$ or less.
86
For example, if one instruction came from line 10 of the source file
87
and the next instruction to be traced came from line 12, line 11 would
88
be shown also, provided that $n\ge1$. If \.{} is omitted it is
89
assumed to be~3.
90
 
91
\bull \.{-s}\quad Show statistics of running time with each traced instruction.
92
 
93
\bull \.{-P}\quad Show the program profile (that is, the frequency counts
94
of each instruction that was executed) when the simulation ends.
95
 
96
\bull \.{-L}\quad List the source lines corresponding to each instruction
97
that appears in the program profile, filling gaps of length $n$ or less.
98
This option implies \.{-P}.  If \.{} is omitted it is assumed to be~3.
99
 
100
\bull \.{-v}\quad Be verbose: \kern-2.5ptTurn on all options.
101
(More precisely, the \.{-v} option is
102
shorthand for \.{-t9999999999}~\.{-e} \.{-r} \.{-s} \.{-l10}~\.{-L10}.)
103
 
104
\bull \.{-q}\quad Be quiet: Cancel all previously specified options.
105
 
106
\bull \.{-i}\quad Go into interactive mode before starting the simulation.
107
 
108
\bull \.{-I}\quad Go into interactive mode when the simulated program
109
halts or pauses for a breakpoint.
110
 
111
\bull \.{-b}\quad Set the buffer size of source lines to $\max(72,n)$.
112
 
113
\bull \.{-c}\quad Set the capacity of the local register ring
114
to $\max(256,n)$; this number must be a power of~2.
115
 
116
\bull \.{-f}\quad Use the named file for standard input to the
117
simulated program. This option should be used whenever the simulator
118
is not being used interactively, because the simulator will not recognize
119
end of file when standard input has been defined in any other way.
120
 
121
\bull \.{-D}\quad Prepare the named file for use by other
122
simulators, instead of actually doing a simulation.
123
 
124
\bull \.{-?}\quad Print the ``\.{Usage}'' message, which summarizes
125
the command-line options.
126
 
127
\smallskip\noindent
128
The author recommends \.{-t2} \.{-l} \.{-L} for initial offline debugging.
129
 
130
While the program is being simulated, an {\it interrupt\/}
131
signal (usually control-C) will cause the simulator to
132
@^interrupts@>
133
break and go into interactive mode after tracing the current instruction,
134
even if \.{-i} and \.{-I} were not specified on the command line.
135
 
136
@ In interactive mode, the user is prompted `\.{mmix>}' and a variety of
137
@.mmix>@>
138
commands can be typed online. Any command-line option can be given
139
in response to such a prompt (including the `\.-' that begins the option),
140
and the following operations are also available:
141
 
142
\bull Simply typing \ or \.n\ to the \.{mmix>} prompt causes
143
one \MMIX\ instruction to be executed and traced; then the user is prompted
144
again.
145
 
146
\bull \.c continues simulation until the program halts or reaches
147
a breakpoint. (Actually the command is `\.c\', but we won't
148
bother to mention the \ in the following description.)
149
 
150
\bull \.q quits (terminates the simulation), after printing the
151
profile (if it was requested) and the final statistics.
152
 
153
\bull \.s prints out the current statistics (the clock times and the
154
current instruction location). We have already discussed the \.{-s} option
155
on the command line, which
156
causes these statistics to be printed automatically;
157
but a lot of statistics can fill up a lot of file space, so users may
158
prefer to see the statistics only on demand.
159
 
160
\bull \.{l}, \.{g}, \.{\$}, \.{rA}, \.{rB}, \dots,
161
\.{rZZ}, and \.{M} will show the current value of a local register,
162
global register, dynamically numbered register, special register, or memory
163
location. Here \.{} specifies the type of value to be displayed;
164
if \.{} is `\.!', the value will be given in decimal notation;
165
if \.{} is `\..' it will be given in floating point notation;
166
if \.{} is `\.\#' it will be given in hexadecimal, and
167
if \.{} is `\."'  it will be given as a string of eight one-byte
168
characters. Just typing \.{} by itself will repeat the most recently shown
169
value, perhaps in another format; for example, the command `\.{l10\#}'
170
will show local register 10 in hexadecimal notation, then the command
171
`\.!' will show it in decimal and `\..' will show it as a floating point
172
number. If \.{} is empty, the previous type will be repeated;
173
the default type is decimal. Register \.{rA} is equivalent to \.{g22},
174
according to the numbering used in \.{GET} and \.{PUT} commands.
175
 
176
The `\.{}' in any of these commands can also have the form
177
`\.{=}', where the value is a decimal or floating point or
178
hexadecimal or string constant. (The syntax rules for floating point constants
179
appear in {\mc MMIX-ARITH}. A string constant is treated as in the
180
\.{BYTE} command of \.{MMIXAL}, but padded at the left with zeros if
181
fewer than eight characters are specified.) This assigns a new value
182
before displaying it. For example, `\.{l10=.1e3}'
183
sets local register 10 equal to 100; `\.{g250="ABCD",\#a}' sets global
184
register 250 equal to \Hex{000000414243440a}; `\.{M1000=-Inf}' sets
185
M$[\Hex{1000}]_8=\Hex{fff0000000000000}$, the representation of $-\infty$.
186
Special registers other than~rI cannot be set to values disallowed by~\.{PUT}.
187
Marginal registers cannot be set to nonzero values.
188
 
189
The command `\.{rI=250}' sets the interval counter to 250; this will
190
cause a break in simulation after 250 instructions have been executed.
191
 
192
\bull \.{+} shows the next $n$ octabytes following the one
193
most recently shown, in format \.{}. For example, after `\.{l10\#}'
194
a subsequent `\.{+30}' will show \.{l11}, \.{l12}, \dots, \.{l40} in
195
hexadecimal notation. After `\.{g200=3}' a subsequent `\.{+30}' will
196
set \.{g201}, \.{g202}, \dots, \.{g230} equal to~3, but a subsequent
197
`\.{+30!}' would merely display \.{g201} through~\.{g230} in decimal
198
notation. Memory addresses will advance by~8 instead of by~1. If \.{}
199
is empty, the default value $n=1$ is used.
200
 
201
\bull \.{@@} sets the address of the next tetrabyte to be
202
simulated, sort of like a \.{GO} command.
203
 
204
\bull \.{t} says that the instruction in tetrabyte location $x$ should
205
always be traced, regardless of its frequency count.
206
 
207
\bull \.{u} undoes the effect of \.{t}.
208
 
209
\bull \.{b[rwx]} sets breakpoints at tetrabyte $x$; here \.{[rwx]}
210
stands for any subset of the letters \.r, \.w, and/or~\.x, meaning to
211
break when the tetrabyte is read, written, and/or executed. For example,
212
`\.{bx1000}' causes a break in the simulation just after the tetrabyte
213
in \Hex{1000} is executed; `\.{b1000}' undoes this breakpoint;
214
`\.{brwx1000}' causes a break just after any simulated instruction loads,
215
stores, or appears in tetrabyte number \Hex{1000}.
216
 
217
\bull \.{T}, \.{D}, \.{P}, \.{S} sets the ``current segment'' to
218
\.{Text\_Segment}, \.{Data\_Segment}, \.{Pool\_Segment}, or
219
\.{Stack\_Segment}, respectively, namely to \Hex{0}, \Hex{2000000000000000},
220
\Hex{4000000000000000}, or \Hex{6000000000000000}. The current segment,
221
initially \Hex{0}, is added to all
222
memory addresses in \.{M}, \.{@@}, \.{t}, \.{u}, and \.{b} commands.
223
@:Text_Segment}\.{Text\_Segment@>
224
@:Data_Segment}\.{Data\_Segment@>
225
@:Pool_Segment}\.{Pool\_Segment@>
226
@:Stack_Segment}\.{Stack\_Segment@>
227
 
228
\bull \.{B} lists all current breakpoints and tracepoints.
229
 
230
\bull \.{i} reads a sequence of interactive commands from the
231
specified file, one command per line, ignoring blank lines. This feature
232
can be used to set many breakpoints or to display a number of key
233
registers, etc. Included lines that begin with \.\% or \.i are ignored;
234
therefore an included file cannot include {\it another\/} file.
235
Included lines that begin with a blank space are reproduced in the standard
236
output, otherwise ignored.
237
 
238
\bull \.h (help) reminds the user of the available interactive commands.
239
 
240
@* Rudimentary I/O.
241
Input and output are provided by the following ten primitive system calls:
242
@^I/O@>
243
@^input/output@>
244
 
245
\bull \.{Fopen}|(handle,name,mode)|. Here |handle| is a
246
one-byte integer, |name| is a string, and |mode| is one of the
247
values \.{TextRead}, \.{TextWrite}, \.{BinaryRead}, \.{BinaryWrite},
248
\.{BinaryReadWrite}. An \.{Fopen} call associates |handle| with the
249
external file called |name| and prepares to do input and/or output
250
on that file. It returns 0 if the file was opened successfully; otherwise
251
returns the value~$-1$. If |mode| is \.{TextWrite}, \.{BinaryWrite}, or
252
\.{BinaryReadWrite},
253
any previous contents of the named file are discarded. If |mode| is
254
\.{TextRead} or \.{TextWrite}, the file consists of ``lines'' terminated
255
by ``newline'' characters, and it is said to be a text file; otherwise
256
the file consists of uninterpreted bytes, and it is said to be a binary file.
257
@.Fopen@>
258
@.TextRead@>
259
@.TextWrite@>
260
@.BinaryRead@>
261
@.BinaryWrite@>
262
@.BinaryReadWrite@>
263
 
264
Text files and binary files are essentially equivalent in cases
265
where this simulator is hosted by an operating system derived from \UNIX/;
266
in such cases files can be written as text and read as binary or vice versa.
267
But with other operating systems, text files and binary files often have
268
quite different representations, and certain characters with byte
269
codes less than~|' '| are forbidden in text. Within any \MMIX\ program,
270
the newline character has byte code $\Hex{0a}=10$.
271
 
272
At the beginning of a program three handles have already been opened: The
273
``standard input'' file \.{StdIn} (handle~0) has mode \.{TextRead}, the
274
``standard output'' file \.{StdOut} (handle~1) has mode \.{TextWrite}, and the
275
``standard error'' file \.{StdErr} (handle~2) also has mode \.{TextWrite}.
276
@.StdIn@>
277
@.StdOut@>
278
@.StdErr@>
279
When this simulator is being run interactively, lines of standard input
280
should be typed following a prompt that says `\.{StdIn>\ }', unless the \.{-f}
281
option has been used.
282
The standard output and standard error files of the simulated program
283
are intermixed with the output of the simulator~itself.
284
 
285
The input/output operations supported by this simulator can perhaps be
286
understood most easily with reference to the standard library \.{stdio}
287
that comes with the \CEE/ language, because the conventions of~\CEE/
288
have been explained in hundreds of books. If we declare an array
289
|FILE *file[256]| and set |file[0]=stdin|, |file[1]=stdout|, and
290
|file[2]=stderr|, then the simulated system call \.{Fopen}|(handle,name,mode)|
291
is essentially equivalent to the \CEE/ expression
292
$$\displaylines{
293
\hskip5em\hbox{(|file[handle]|?
294
     |(file[handle]=freopen(name,mode_string[mode],file[handle]))|:}\hfill\cr
295
\hfill\hbox{|(file[handle]=fopen(name,mode_string[mode]))|)? 0: $-1$},%
296
      \hskip5em\cr}$$
297
if we set |mode_string|[\.{TextRead}]~=~|"r"|,
298
|mode_string|[\.{TextWrite}]~=~|"w"|,
299
|mode_string|[\.{BinaryRead}]~=~|"rb"|,
300
|mode_string|[\.{BinaryWrite}]~=~|"wb"|, and
301
|mode_string|[\.{BinaryReadWrite}]~=~|"wb+"|.
302
 
303
\bull \.{Fclose}|(handle)|. If the given file handle has been opened, it is
304
closed---no longer associated with any file. Again the result is 0 if
305
successful, or $-1$ if the file was already closed or unclosable.
306
The \CEE/ equivalent is
307
$$\hbox{|fclose(file[handle])? -1: 0|}$$
308
with the additional side effect of setting |file[handle]=NULL|.
309
 
310
\bull \.{Fread}|(handle,buffer,size)|.
311
The file handle should have been opened with mode \.{TextRead},
312
\.{BinaryRead}, or \.{BinaryReadWrite}.
313
@.Fread@>
314
The next |size| characters are read into \MMIX's memory starting at address
315
|buffer|. If an error occurs, the value |-1-size| is returned;
316
otherwise, if the end of file does not intervene, 0~is returned;
317
otherwise the negative value |n-size| is returned, where |n|~is the number of
318
characters successfully read and stored. The statement
319
$$\hbox{|fread(buffer,1,size,file[handle])-size|}$$
320
has the equivalent effect in \CEE/, in the absence of file errors.
321
 
322
\bull \.{Fgets}|(handle,buffer,size)|.
323
The file handle should have been opened with mode \.{TextRead},
324
\.{BinaryRead}, or \.{BinaryReadWrite}.
325
@.Fgets@>
326
Characters are read into \MMIX's memory starting at address |buffer|, until
327
either |size-1| characters have been read and stored or a newline character has
328
been read and stored; the next byte in memory is then set to zero.
329
If an error or end of file occurs before reading is complete, the memory
330
contents are undefined and the value $-1$ is returned; otherwise
331
the number of characters successfully read and stored is returned.
332
The equivalent in \CEE/ is
333
$$\hbox{|fgets(buffer,size,file[handle])? strlen(buffer): -1|}$$
334
if we assume that no null characters were read in; null characters may,
335
however, precede a newline, and they are counted just like other characters.
336
 
337
\bull \.{Fgetws}|(handle,buffer,size)|.
338
@.Fgetws@>
339
This command is the same as \.{Fgets}, except that it applies to wyde
340
characters instead of one-byte characters. Up to |size-1| wyde
341
characters are read; a wyde newline is $\Hex{000a}$. The \CEE/~version,
342
using conventions of the ISO multibyte string extension (MSE), is
343
@^MSE@>
344
approximately
345
$$\hbox{|fgetws(buffer,size,file[handle])? wcslen(buffer): -1|}$$
346
where |buffer| now has type |wchar_t*|.
347
 
348
\bull \.{Fwrite}|(handle,buffer,size)|.
349
The file handle should have been opened with one of the modes \.{TextWrite},
350
\.{BinaryWrite}, or \.{BinaryReadWrite}.
351
@.Fwrite@>
352
The next |size| characters are written from \MMIX's memory starting at address
353
|buffer|. If no error occurs, 0~is returned;
354
otherwise the negative value |n-size| is returned, where |n|~is the number of
355
characters successfully written. The statement
356
$$\hbox{|fwrite(buffer,1,size,file[handle])-size|}$$
357
together with |fflush(file[handle])| has the equivalent effect in \CEE/.
358
 
359
\bull \.{Fputs}|(handle,string)|.
360
The file handle should have been opened with mode \.{TextWrite},
361
\.{BinaryWrite}, or \.{BinaryReadWrite}.
362
@.Fputs@>
363
One-byte characters are written from \MMIX's memory to the file, starting
364
at address |string|, up to but not including the first byte equal to~zero.
365
The number of bytes written is returned, or $-1$ on error.
366
The \CEE/ version is
367
$$\hbox{|fputs(string,file[handle])>=0? strlen(string): -1|,}$$
368
together with |fflush(file[handle])|.
369
 
370
\bull \.{Fputws}|(handle,string)|.
371
The file handle should have been opened with mode \.{TextWrite},
372
\.{BinaryWrite}, or \.{BinaryReadWrite}.
373
@.Fputws@>
374
Wyde characters are written from \MMIX's memory to the file, starting
375
at address |string|, up to but not including the first wyde equal to~zero.
376
The number of wydes written is returned, or $-1$ on error.
377
The \CEE/+MSE version is
378
$$\hbox{|fputws(string,file[handle])>=0? wcslen(string): -1|}$$
379
together with |fflush(file[handle])|, where |string| now has type |wchar_t*|.
380
 
381
\bull \.{Fseek}|(handle,offset)|.
382
The file handle should have been opened with mode \.{BinaryRead},
383
\.{BinaryWrite}, or \.{BinaryReadWrite}.
384
@.Fseek@>
385
This operation causes the next input or output operation to begin at
386
|offset| bytes from the beginning of the file, if |offset>=0|, or at
387
|-offset-1| bytes before the end of the file, if |offset<0|. (For
388
example, |offset=0| ``rewinds'' the file to its very beginning;
389
|offset=-1| moves forward all the way to the end.) The result is 0
390
if successful, or $-1$ if the stated positioning could not be done.
391
The \CEE/ version is
392
$$\hbox{|fseek(file[handle],@,offset<0? offset+1: offset,@,
393
              offset<0? SEEK_END: SEEK_SET)|? $-1$: 0.}$$
394
If a file in mode \.{BinaryReadWrite} is used for both reading and writing,
395
an \.{Fseek} command must be given when switching from input to output
396
or from output to input.
397
 
398
\bull \.{Ftell}|(handle)|.
399
The file handle should have been opened with mode \.{BinaryRead},
400
\.{BinaryWrite}, or \.{BinaryReadWrite}.
401
@.Ftell@>
402
This operation returns the current file position, measured in bytes
403
from the beginning, or $-1$ if an error has occurred. In this case the
404
\CEE/ function
405
$$\hbox{|ftell(file[handle])|}$$
406
has exactly the same meaning.
407
 
408
\smallskip
409
Although these ten operations are quite primitive, they provide
410
the necessary functionality for extremely complex input/output behavior.
411
For example, every function in the \.{stdio} library of \CEE/,
412
with the exception of the two administrative operations \\{remove} and
413
\\{rename}, can be implemented as a subroutine in terms of the six basic
414
operations \.{Fopen}, \.{Fclose}, \.{Fread}, \.{Fwrite}, \.{Fseek}, and
415
\.{Ftell}.
416
 
417
Notice that the \MMIX\ function calls are much more consistent than
418
those in the \CEE/ library. The first argument is always a handle;
419
the second, if present, is always an address; the third, if present,
420
is always a size. {\it The result returned is always nonnegative if the
421
operation was successful, negative if an anomaly arose.} These common
422
features make the functions reasonably easy to remember.
423
 
424
@ The ten input/output operations of the previous section are invoked by
425
\.{TRAP} commands with $\rm X=0$, $\rm Y=\.{Fopen}$ or \.{Fclose} or \dots~or
426
\.{Ftell}, and $\rm Z=\.{Handle}$. If~there are two arguments, the
427
second argument is placed in \$255. If there are three arguments,
428
the address of the second is placed in~\$255; the second argument
429
is M$[\$255]_8$ and the third argument is M$[\$255+8]_8$. The returned
430
value will be in \$255 when the system call is finished. (See the
431
example below.)
432
 
433
@ The user program starts at symbolic location \.{Main}. At this time
434
@.Main@>
435
@:Pool_Segment}\.{Pool\_Segment@>
436
the global registers are initialized according to the \.{GREG}
437
statements in the \.{MMIXAL} program, and \$255 is set to the
438
numeric equivalent of~\.{Main}. Local register~\$0 is
439
initially set to the number of {\it command-line arguments\/}; and
440
@^command line arguments@>
441
local register~\$1 points to the first such argument, which
442
is always a pointer to the program name. Each command-line argument is a
443
pointer to a string; the last such pointer is M$[\$0\ll3+\$1]_8$, and
444
M$[\$0\ll3+\$1+8]_8$ is zero. (Register~\$1 will point to an octabyte in
445
\.{Pool\_Segment}, and the command-line strings will be in that segment
446
too.) Location M[\.{Pool\_Segment}] will be the address of the first
447
unused octabyte of the pool segment.
448
 
449
Registers rA, rB, rD, rE, rF, rH, rI, rJ, rM, rP, rQ, and rR
450
are initially zero, and $\rm rL=2$.
451
 
452
A subroutine library loaded with the user program might need to initialize
453
itself. If an instruction has been loaded into tetrabyte M$[\Hex{90}]_4$,
454
the simulator actually begins execution at \Hex{90} instead of at~\.{Main};
455
in this case \$255 holds the location of~\.{Main}.
456
@^subroutine library initialization@>
457
@^initialization of a user program@>
458
(The routine at \Hex{90} can pass control to \.{Main} without increasing~rL,
459
if it starts with the slightly tricky sequence
460
$$\.{PUT rW, \$255;{ } PUT rB, \$255;{ } SETML \$255,\#F700;{ } % PUTI rB,0!
461
      PUT rX,\$255}$$
462
and eventually says \.{RESUME}; this \.{RESUME} command will restore
463
\$255 and~rB. But the user program should {\it not\/} really count on
464
the fact that rL is initially~2.)
465
 
466
@ The main program ends when \MMIX\ executes the system
467
call \.{TRAP}~\.{0}, which is often symbolically written
468
`\.{TRAP}~\.{0,Halt,0}' to make its intention clear. The contents
469
of \$255 at that time are considered to be the value ``returned''
470
by the main program, as in the |exit| statement of~\CEE/; a nonzero
471
value indicates an anomalous exit. All open files are closed
472
@.Halt@>
473
when the program ends.
474
 
475
@ Here, for example, is a complete program that copies a text file
476
to the standard output, given the name of the file to be copied.
477
It includes all necessary error checking.
478
\vskip-14pt
479
$$\baselineskip=10pt
480
\obeyspaces\halign{\qquad\.{#}\hfil\cr
481
* SAMPLE PROGRAM: COPY A GIVEN FILE TO STANDARD OUTPUT\cr
482
\noalign{\smallskip}
483
t        IS   \$255\cr
484
argc     IS   \$0\cr
485
argv     IS   \$1\cr
486
s        IS   \$2\cr
487
Buf\_Size IS   1000\cr
488
{}         LOC  Data\_Segment\cr
489
Buffer   LOC  @@+Buf\_Size\cr
490
{}         GREG @@\cr
491
Arg0     OCTA 0,TextRead\cr
492
Arg1     OCTA Buffer,Buf\_Size\cr
493
\noalign{\smallskip}
494
{}         LOC  \#200              main(argc,argv) \{\cr
495
Main     CMP  t,argc,2          if (argc==2) goto openit\cr
496
{}         PBZ  t,OpenIt\cr
497
{}         GETA t,1F              fputs("Usage: ",stderr)\cr
498
{}         TRAP 0,Fputs,StdErr\cr
499
{}         LDOU t,argv,0          fputs(argv[0],stderr)\cr
500
{}         TRAP 0,Fputs,StdErr\cr
501
{}         GETA t,2F              fputs(" filename\\n",stderr)\cr
502
Quit     TRAP 0,Fputs,StdErr    \cr
503
{}         NEG  t,0,1             quit: exit(-1)\cr
504
{}         TRAP 0,Halt,0\cr
505
1H       BYTE "Usage: ",0\cr
506
{}         LOC  (@@+3)\&-4          align to tetrabyte\cr
507
2H       BYTE " filename",\#a,0\cr
508
\noalign{\smallskip}
509
OpenIt   LDOU s,argv,8          openit: s=argv[1]\cr
510
{}         STOU s,Arg0\cr
511
{}         LDA  t,Arg0            fopen(argv[1],"r",file[3])\cr
512
{}         TRAP 0,Fopen,3\cr
513
{}         PBNN t,CopyIt          if (no error) goto copyit\cr
514
{}         GETA t,1F              fputs("Can't open file ",stderr)\cr
515
{}         TRAP 0,Fputs,StdErr\cr
516
{}         SET  t,s               fputs(argv[1],stderr)\cr
517
{}         TRAP 0,Fputs,StdErr\cr
518
{}         GETA t,2F              fputs("!\\n",stderr)\cr
519
{}         JMP  Quit              goto quit\cr
520
1H       BYTE "Can't open file ",0\cr
521
{}         LOC  (@@+3)\&-4          align to tetrabyte\cr
522
2H       BYTE "!",\#a,0\cr
523
\noalign{\smallskip}
524
CopyIt   LDA  t,Arg1            copyit:\cr
525
{}         TRAP 0,Fread,3         items=fread(buffer,1,buf\_size,file[3])\cr
526
{}         BN   t,EndIt           if (items < buf\_size) goto endit\cr
527
{}         LDA  t,Arg1            items=fwrite(buffer,1,buf\_size,stdout)\cr
528
{}         TRAP 0,Fwrite,StdOut\cr
529
{}         PBNN t,CopyIt          if (items >= buf\_size) goto copyit\cr
530
Trouble  GETA t,1F              trouble: fputs("Trouble w...!",stderr)\cr
531
{}         JMP  Quit              goto quit\cr
532
1H       BYTE "Trouble writing StdOut!",\#a,0\cr
533
\noalign{\smallskip}
534
EndIt    INCL t,Buf\_Size\cr
535
{}         BN   t,ReadErr         if (ferror(file[3])) goto readerr\cr
536
{}         STO  t,Arg1+8\cr
537
{}         LDA  t,Arg1            n=fwrite(buffer,1,items,stdout)\cr
538
{}         TRAP 0,Fwrite,StdOut\cr
539
{}         BN   t,Trouble         if (n < items) goto trouble\cr
540
{}         TRAP 0,Halt,0          exit(0)\cr
541
ReadErr  GETA t,1F              readerr: fputs("Trouble r...!",stderr)\cr
542
{}         JMP  Quit              goto quit \}\cr
543
1H       BYTE "Trouble reading!",\#a,0\cr
544
}$$
545
 
546
@* Basics. To get started, we define a type that provides semantic sugar.
547
 
548
@=
549
typedef enum {@!false,@!true}@+@!bool;
550
 
551
@ This program for the 64-bit \MMIX\ architecture is based on 32-bit integer
552
arithmetic, because nearly every computer available to the author at the time
553
of writing (1999) was limited in that way. It uses subroutines
554
from the {\mc MMIX-ARITH} module, assuming only that type \&{tetra}
555
represents unsigned 32-bit integers. The definition of \&{tetra}
556
given here should be changed, if necessary, to agree with the
557
definition in that module.
558
@^system dependencies@>
559
 
560
@=
561
typedef unsigned int tetra;
562
  /* for systems conforming to the LP-64 data model */
563
typedef struct {tetra h,l;} octa; /* two tetrabytes make one octabyte */
564
typedef unsigned char byte; /* a monobyte */
565
 
566
@ We declare subroutines twice, once with a prototype and once
567
with the old-style~\CEE/ conventions. The following hack makes
568
this work with new compilers as well as the old standbys.
569
 
570
@=
571
#ifdef __STDC__
572
#define ARGS(list) list
573
#else
574
#define ARGS(list) ()
575
#endif
576
 
577
@ @=
578
void print_hex @,@,@[ARGS((octa))@];@+@t}\6{@>
579
void print_hex(o)
580
  octa o;
581
{
582
  if (o.h) printf("%x%08x",o.h,o.l);
583
  else printf("%x",o.l);
584
}
585
 
586
@ Most of the subroutines in {\mc MMIX-ARITH} return an octabyte as
587
a function of two octabytes; for example, |oplus(y,z)| returns the
588
sum of octabytes |y| and~|z|. Division inputs the high
589
half of a dividend in the global variable~|aux| and returns
590
the remainder in~|aux|.
591
 
592
@=
593
extern octa zero_octa; /* |zero_octa.h=zero_octa.l=0| */
594
extern octa neg_one; /* |neg_one.h=neg_one.l=-1| */
595
extern octa aux,val; /* auxiliary data */
596
extern bool overflow; /* flag set by signed multiplication and division */
597
extern int exceptions; /* bits set by floating point operations */
598
extern int cur_round; /* the current rounding mode */
599
extern char *next_char; /* where a scanned constant ended */
600
extern octa oplus @,@,@[ARGS((octa y,octa z))@];
601
  /* unsigned $y+z$ */
602
extern octa ominus @,@,@[ARGS((octa y,octa z))@];
603
  /* unsigned $y-z$ */
604
extern octa incr @,@,@[ARGS((octa y,int delta))@];
605
  /* unsigned $y+\delta$ ($\delta$ is signed) */
606
extern octa oand @,@,@[ARGS((octa y,octa z))@];
607
  /* $y\land z$ */
608
extern octa shift_left @,@,@[ARGS((octa y,int s))@];
609
  /* $y\LL s$, $0\le s\le64$ */
610
extern octa shift_right @,@,@[ARGS((octa y,int s,int uns))@];
611
  /* $y\GG s$, signed if |!uns| */
612
extern octa omult @,@,@[ARGS((octa y,octa z))@];
613
  /* unsigned $(|aux|,x)=y\times z$ */
614
extern octa signed_omult @,@,@[ARGS((octa y,octa z))@];
615
  /* signed $x=y\times z$ */
616
extern octa odiv @,@,@[ARGS((octa x,octa y,octa z))@];
617
  /* unsigned $(x,y)/z$; $|aux|=(x,y)\bmod z$ */
618
extern octa signed_odiv @,@,@[ARGS((octa y,octa z))@];
619
  /* signed $x=y/z$ */
620
extern int count_bits @,@,@[ARGS((tetra z))@];
621
  /* $x=\nu(z)$ */
622
extern tetra byte_diff @,@,@[ARGS((tetra y,tetra z))@];
623
  /* half of \.{BDIF} */
624
extern tetra wyde_diff @,@,@[ARGS((tetra y,tetra z))@];
625
  /* half of \.{WDIF} */
626
extern octa bool_mult @,@,@[ARGS((octa y,octa z,bool xor))@];
627
  /* \.{MOR} or \.{MXOR} */
628
extern octa load_sf @,@,@[ARGS((tetra z))@];
629
  /* load short float */
630
extern tetra store_sf @,@,@[ARGS((octa x))@];
631
  /* store short float */
632
extern octa fplus @,@,@[ARGS((octa y,octa z))@];
633
  /* floating point $x=y\oplus z$ */
634
extern octa fmult @,@,@[ARGS((octa y ,octa z))@];
635
  /* floating point $x=y\otimes z$ */
636
extern octa fdivide @,@,@[ARGS((octa y,octa z))@];
637
  /* floating point $x=y\oslash z$ */
638
extern octa froot @,@,@[ARGS((octa,int))@];
639
  /* floating point $x=\sqrt z$ */
640
extern octa fremstep @,@,@[ARGS((octa y,octa z,int delta))@];
641
  /* floating point $x\,{\rm rem}\,z=y\,{\rm rem}\,z$ */
642
extern octa fintegerize @,@,@[ARGS((octa z,int mode))@];
643
  /* floating point $x={\rm round}(z)$ */
644
extern int fcomp @,@,@[ARGS((octa y,octa z))@];
645
  /* $-1$, 0, 1, or 2 if $yz$, $y\parallel z$ */
646
extern int fepscomp @,@,@[ARGS((octa y,octa z,octa eps,int sim))@];
647
  /* $x=|sim|?\ [y\sim z\ (\epsilon)]:\ [y\approx z\ (\epsilon)]$ */
648
extern octa floatit @,@,@[ARGS((octa z,int mode,int unsgnd,int shrt))@];
649
  /* fix to float */
650
extern octa fixit @,@,@[ARGS((octa z,int mode))@];
651
  /* float to fix */
652
extern void print_float @,@,@[ARGS((octa z))@];
653
  /* print octabyte as floating decimal */
654
extern int scan_const @,@,@[ARGS((char* buf))@];
655
  /* |val| = floating or integer constant; returns the type */
656
 
657
@ Here's a quick check to see if arithmetic is in trouble.
658
 
659
@d panic(m) {@+fprintf(stderr,"Panic: %s!\n",m);@+exit(-2);@+}
660
@=
661
if (shift_left(neg_one,1).h!=0xffffffff)
662
  panic("Incorrect implementation of type tetra");
663
@.Incorrect implementation...@>
664
 
665
@ Binary-to-decimal conversion is used when we want to see an octabyte
666
as a signed integer. The identity $\lfloor(an+b)/10\rfloor=
667
\lfloor a/10\rfloor n+\lfloor((a\bmod 10)n+b)/10\rfloor$ is helpful here.
668
 
669
@d sign_bit ((unsigned)0x80000000)
670
 
671
@=
672
void print_int @,@,@[ARGS((octa))@];@+@t}\6{@>
673
void print_int(o)
674
  octa o;
675
{
676
  register tetra hi=o.h, lo=o.l, r, t;
677
  register int j;
678
  char dig[20];
679
  if (lo==0 && hi==0) printf("0");
680
  else {
681
    if (hi&sign_bit) {
682
      printf("-");
683
      if (lo==0) hi=-hi;
684
      else lo=-lo, hi=~hi;
685
    }
686
    for (j=0;hi;j++) { /* 64-bit division by 10 */
687
      r=((hi%10)<<16)+(lo>>16);
688
      hi=hi/10;
689
      t=((r%10)<<16)+(lo&0xffff);
690
      lo=((r/10)<<16)+(t/10);
691
      dig[j]=t%10;
692
    }
693
    for (;lo;j++) {
694
      dig[j]=lo%10;
695
      lo=lo/10;
696
    }
697
    for (j--;j>=0;j--) printf("%c",dig[j]+'0');
698
  }
699
}
700
 
701
@* Simulated memory. Chunks of simulated memory, 2048 bytes each,
702
are kept in a tree structure organized as a {\it treap},
703
following ideas of Vuillemin, Aragon, and Seidel
704
@^Vuillemin, Jean Etienne@>
705
@^Aragon, Cecilia Rodriguez@>
706
@^Seidel, Raimund@>
707
[{\sl Communications of the ACM\/ \bf23} (1980), 229--239;
708
{\sl IEEE Symp.\ on Foundations of Computer Science\/ \bf30} (1989), 540--546].
709
Each node of the treap has two keys: One, called |loc|, is the
710
base address of 512 simulated tetrabytes; it follows the conventions
711
of an ordinary binary search tree, with all locations in the left subtree
712
less than the |loc| of a node and all locations in the right subtree
713
greater than that~|loc|. The other, called |stamp|, can be thought of as the
714
time the node was inserted into the tree; all subnodes of a given node
715
have a larger~|stamp|. By assigning time stamps at random, we maintain
716
a tree structure that almost always is fairly well balanced.
717
 
718
Each simulated tetrabyte has an associated frequency count and
719
source file reference.
720
 
721
@=
722
typedef struct {
723
  tetra tet; /* the tetrabyte of simulated memory */
724
  tetra freq; /* the number of times it was obeyed as an instruction */
725
  unsigned char bkpt; /* breakpoint information for this tetrabyte */
726
  unsigned char file_no; /* source file number, if known */
727
  unsigned short line_no; /* source line number, if known */
728
} mem_tetra;
729
@#
730
typedef struct mem_node_struct {
731
  octa loc; /* location of the first of 512 simulated tetrabytes */
732
  tetra stamp; /* time stamp for treap balancing */
733
  struct mem_node_struct *left, *right; /* pointers to subtrees */
734
  mem_tetra dat[512]; /* the chunk of simulated tetrabytes */
735
} mem_node;
736
 
737
@ The |stamp| value is actually only pseudorandom, based on the
738
idea of Fibonacci hashing [see {\sl Sorting and Searching}, Section~6.4].
739
This is good enough for our purposes, and it guarantees that
740
no two stamps will be identical.
741
 
742
@=
743
mem_node* new_mem @,@,@[ARGS((void))@];@+@t}\6{@>
744
mem_node* new_mem()
745
{
746
  register mem_node *p;
747
  p=(mem_node*)calloc(1,sizeof(mem_node));
748
  if (!p) panic("Can't allocate any more memory");
749
@.Can't allocate...@>
750
  p->stamp=priority;
751
  priority+=0x9e3779b9; /* $\lfloor2^{32}(\phi-1)\rfloor$ */
752
  return p;
753
}
754
 
755
@ Initially we start with a chunk for the pool segment, since
756
the simulator will be putting command-line information there before
757
it runs the program.
758
 
759
@=
760
mem_root=new_mem();
761
mem_root->loc.h=0x40000000;
762
last_mem=mem_root;
763
 
764
@ @=
765
tetra priority=314159265; /* pseudorandom time stamp counter */
766
mem_node *mem_root; /* root of the treap */
767
mem_node *last_mem; /* the memory node most recently read or written */
768
 
769
@ The |mem_find| routine finds a given tetrabyte in the simulated
770
memory, inserting a new node into the treap if necessary.
771
 
772
@=
773
mem_tetra* mem_find @,@,@[ARGS((octa))@];@+@t}\6{@>
774
mem_tetra* mem_find(addr)
775
  octa addr;
776
{
777
  octa key;
778
  register int offset;
779
  register mem_node *p=last_mem;
780
  key.h=addr.h;
781
  key.l=addr.l&0xfffff800;
782
  offset=addr.l&0x7fc;
783
  if (p->loc.l!=key.l || p->loc.h!=key.h)
784
    @
785
        setting |last_mem| and |p| to its location@>;
786
  return &p->dat[offset>>2];
787
}
788
 
789
@ @=
790
{@+register mem_node **q;
791
  for (p=mem_root; p; ) {
792
    if (key.l==p->loc.l && key.h==p->loc.h) goto found;
793
    if ((key.lloc.l && key.h<=p->loc.h) || key.hloc.h) p=p->left;
794
    else p=p->right;
795
  }
796
  for (p=mem_root,q=&mem_root; p && p->stamp
797
    if ((key.lloc.l && key.h<=p->loc.h) || key.hloc.h) q=&p->left;
798
    else q=&p->right;
799
  }
800
  *q=new_mem();
801
  (*q)->loc=key;
802
  @;
803
  p=*q;
804
found: last_mem=p;
805
}
806
 
807
@ At this point we want to split the binary search tree |p| into two
808
parts based on the given |key|, forming the left and right subtrees
809
of the new node~|q|. The effect will be as if |key| had been inserted
810
before all of |p|'s nodes.
811
 
812
@=
813
{
814
  register mem_node **l=&(*q)->left,**r=&(*q)->right;
815
  while (p) {
816
    if ((key.lloc.l && key.h<=p->loc.h) || key.hloc.h)
817
      *r=p, r=&p->left, p=*r;
818
    else *l=p, l=&p->right, p=*l;
819
  }
820
  *l=*r=NULL;
821
}
822
 
823
@* Loading an object file. To get the user's program into memory,
824
we read in an \MMIX\ object, using modifications of the routines
825
in the utility program \.{MMOtype}. Complete details of \.{mmo}
826
format appear in the program for {\mc MMIXAL}; a reader
827
who hopes to understand this section ought to at least skim
828
that documentation.
829
Here we need to define only the basic constants used for interpretation.
830
 
831
@d mm 0x98 /* the escape code of \.{mmo} format */
832
@d lop_quote 0x0 /* the quotation lopcode */
833
@d lop_loc 0x1 /* the location lopcode */
834
@d lop_skip 0x2 /* the skip lopcode */
835
@d lop_fixo 0x3 /* the octabyte-fix lopcode */
836
@d lop_fixr 0x4 /* the relative-fix lopcode */
837
@d lop_fixrx 0x5 /* extended relative-fix lopcode */
838
@d lop_file 0x6 /* the file name lopcode */
839
@d lop_line 0x7 /* the file position lopcode */
840
@d lop_spec 0x8 /* the special hook lopcode */
841
@d lop_pre 0x9 /* the preamble lopcode */
842
@d lop_post 0xa /* the postamble lopcode */
843
@d lop_stab 0xb /* the symbol table lopcode */
844
@d lop_end 0xc /* the end-it-all lopcode */
845
 
846
@ We do not load the symbol table. (A more ambitious simulator could
847
implement \.{MMIXAL}-style expressions for interactive debugging,
848
but such enhancements are left to the interested reader.)
849
 
850
@=
851
mmo_file=fopen(mmo_file_name,"rb");
852
if (!mmo_file) {
853
  register char *alt_name=(char*)calloc(strlen(mmo_file_name)+5,sizeof(char));
854
  if (!alt_name) panic("Can't allocate file name buffer");
855
@.Can't allocate...@>
856
  sprintf(alt_name,"%s.mmo",mmo_file_name);
857
  mmo_file=fopen(alt_name,"rb");
858
  if (!mmo_file) {
859
    fprintf(stderr,"Can't open the object file %s or %s!\n",
860
@.Can't open...@>
861
               mmo_file_name,alt_name);
862
    exit(-3);
863
  }
864
  free(alt_name);
865
}
866
byte_count=0;
867
 
868
@ @=
869
FILE *mmo_file; /* the input file */
870
int postamble; /* have we encountered |lop_post|? */
871
int byte_count; /* index of the next-to-be-read byte */
872
byte buf[4]; /* the most recently read bytes */
873
int yzbytes; /* the two least significant bytes */
874
int delta; /* difference for relative fixup */
875
tetra tet; /* |buf| bytes packed big-endianwise */
876
 
877
@ The tetrabytes of an \.{mmo} file are stored in
878
friendly big-endian fashion, but this program is supposed to work also
879
on computers that are little-endian. Therefore we read four successive bytes
880
and pack them into a tetrabyte, instead of reading a single tetrabyte.
881
 
882
@d mmo_err {
883
     fprintf(stderr,"Bad object file! (Try running MMOtype.)\n");
884
@.Bad object file@>
885
     exit(-4);
886
   }
887
 
888
@=
889
void read_tet @,@,@[ARGS((void))@];@+@t}\6{@>
890
void read_tet()
891
{
892
  if (fread(buf,1,4,mmo_file)!=4) mmo_err;
893
  yzbytes=(buf[2]<<8)+buf[3];
894
  tet=(((buf[0]<<8)+buf[1])<<16)+yzbytes;
895
}
896
 
897
@ @=
898
byte read_byte @,@,@[ARGS((void))@];@+@t}\6{@>
899
byte read_byte()
900
{
901
  register byte b;
902
  if (!byte_count) read_tet();
903
  b=buf[byte_count];
904
  byte_count=(byte_count+1)&3;
905
  return b;
906
}
907
 
908
@ @=
909
read_tet(); /* read the first tetrabyte of input */
910
if (buf[0]!=mm || buf[1]!=lop_pre) mmo_err;
911
if (ybyte!=1) mmo_err;
912
if (zbyte==0) obj_time=0xffffffff;
913
else {
914
  j=zbyte-1;
915
  read_tet();@+ obj_time=tet; /* file creation time */
916
  for (;j>0;j--) read_tet();
917
}
918
 
919
@ @=
920
{
921
  read_tet();
922
 loop:@+if (buf[0]==mm) switch (buf[1]) {
923
   case lop_quote:@+if (yzbytes!=1) mmo_err;
924
    read_tet();@+break;
925
   @t\4@>@@;
926
   case lop_post: postamble=1;
927
     if (ybyte || zbyte<32) mmo_err;
928
     continue;
929
   default: mmo_err;
930
  }
931
  @;
932
}
933
 
934
@ In a normal situation, the newly read tetrabyte is simply supposed
935
to be loaded into the current location. We load not only the current
936
location but also the current file position, if |cur_line| is nonzero
937
and |cur_loc| belongs to segment~0.
938
 
939
@d mmo_load(loc,val) ll=mem_find(loc), ll->tet^=val
940
 
941
@=
942
{
943
  mmo_load(cur_loc,tet);
944
  if (cur_line) {
945
    ll->file_no=cur_file;
946
    ll->line_no=cur_line;
947
    cur_line++;
948
  }
949
  cur_loc=incr(cur_loc,4);@+ cur_loc.l &=-4;
950
}
951
 
952
@ @=
953
octa cur_loc; /* the current location */
954
int cur_file=-1; /* the most recently selected file number */
955
int cur_line; /* the current position in |cur_file|, if nonzero */
956
octa tmp; /* an octabyte of temporary interest */
957
tetra obj_time; /* when the object file was created */
958
 
959
@ @=
960
cur_loc.h=cur_loc.l=0;
961
cur_file=-1;
962
cur_line=0;
963
@;
964
do @@;@+while (!postamble);
965
@;
966
fclose(mmo_file);
967
cur_line=0;
968
 
969
@ We have already implemented |lop_quote|, which
970
falls through to the normal case after reading an extra tetrabyte.
971
Now let's consider the other lopcodes in turn.
972
 
973
@d ybyte buf[2] /* the next-to-least significant byte */
974
@d zbyte buf[3] /* the least significant byte */
975
 
976
@=
977
case lop_loc:@+if (zbyte==2) {
978
   j=ybyte;@+ read_tet();@+ cur_loc.h=(j<<24)+tet;
979
 }@+else if (zbyte==1) cur_loc.h=ybyte<<24;
980
 else mmo_err;
981
 read_tet();@+ cur_loc.l=tet;
982
 continue;
983
case lop_skip: cur_loc=incr(cur_loc,yzbytes);@+continue;
984
 
985
@ Fixups load information out of order, when future references have
986
been resolved. The current file name and line number are not considered
987
relevant.
988
 
989
@=
990
case lop_fixo:@+if (zbyte==2) {
991
   j=ybyte;@+ read_tet();@+ tmp.h=(j<<24)+tet;
992
 }@+else if (zbyte==1) tmp.h=ybyte<<24;
993
 else mmo_err;
994
 read_tet();@+ tmp.l=tet;
995
 mmo_load(tmp,cur_loc.h);
996
 mmo_load(incr(tmp,4),cur_loc.l);
997
 continue;
998
case lop_fixr: delta=yzbytes; goto fixr;
999
case lop_fixrx:j=yzbytes;@+if (j!=16 && j!=24) mmo_err;
1000
 read_tet(); delta=tet;
1001
 if (delta&0xfe000000) mmo_err;
1002
fixr: tmp=incr(cur_loc,-(delta>=0x1000000? (delta&0xffffff)-(1<
1003
 mmo_load(tmp,delta);
1004
 continue;
1005
 
1006
@ The space for file names isn't allocated until we are sure we need it.
1007
 
1008
@=
1009
case lop_file:@+if (file_info[ybyte].name) {
1010
   if (zbyte) mmo_err;
1011
   cur_file=ybyte;
1012
 }@+else {
1013
   if (!zbyte) mmo_err;
1014
   file_info[ybyte].name=(char*)calloc(4*zbyte+1,1);
1015
   if (!file_info[ybyte].name) {
1016
     fprintf(stderr,"No room to store the file name!\n");@+exit(-5);
1017
@.No room...@>
1018
   }
1019
   cur_file=ybyte;
1020
   for (j=zbyte,p=file_info[ybyte].name; j>0; j--,p+=4) {
1021
     read_tet();
1022
     *p=buf[0];@+*(p+1)=buf[1];@+*(p+2)=buf[2];@+*(p+3)=buf[3];
1023
   }
1024
 }
1025
 cur_line=0;@+continue;
1026
case lop_line:@+if (cur_file<0) mmo_err;
1027
 cur_line=yzbytes;@+continue;
1028
 
1029
@ Special bytes are ignored (at least for now).
1030
 
1031
@=
1032
case lop_spec:@+ while(1) {
1033
   read_tet();
1034
   if (buf[0]==mm) {
1035
     if (buf[1]!=lop_quote || yzbytes!=1) goto loop; /* end of special data */
1036
     read_tet();
1037
   }
1038
 }
1039
 
1040
@ Since a chunk of memory holds 512 tetrabytes, the |ll| pointer in the
1041
following loop stays in the same chunk (namely, the first chunk
1042
of segment~3, also known as \.{Stack\_Segment}).
1043
@:Stack_Segment}\.{Stack\_Segment@>
1044
@:Pool_Segment}\.{Pool\_Segment@>
1045
 
1046
@=
1047
aux.h=0x60000000;@+ aux.l=0x18;
1048
ll=mem_find(aux);
1049
(ll-1)->tet=2; /* this will ultimately set |rL=2| */
1050
(ll-5)->tet=argc; /* and $\$0=|argc|$ */
1051
(ll-4)->tet=0x40000000;
1052
(ll-3)->tet=0x8; /* and $\$1=\.{Pool\_Segment}+8$ */
1053
G=zbyte;@+ L=0;
1054
for (j=G+G;j<256+256;j++,ll++,aux.l+=4) read_tet(), ll->tet=tet;
1055
inst_ptr.h=(ll-2)->tet, inst_ptr.l=(ll-1)->tet; /* \.{Main} */
1056
(ll+2*12)->tet=G<<24;
1057
g[255]=incr(aux,12*8); /* we will |UNSAVE| from here, to get going */
1058
 
1059
@* Loading and printing source lines.
1060
The loaded program generally contains cross references to the lines
1061
of symbolic source files, so that the context of each instruction
1062
can be understood. The following sections of this program
1063
make such information available when it is desired.
1064
 
1065
Source file data is kept in a \&{file\_node} structure:
1066
 
1067
@=
1068
typedef struct {
1069
  char *name; /* name of source file */
1070
  int line_count; /* number of lines in the file */
1071
  long *map; /* pointer to map of file positions */
1072
} file_node;
1073
 
1074
@ In partial preparation for the day when source files are in
1075
Unicode, we define a type \&{Char} for the source characters.
1076
 
1077
@=
1078
typedef char Char; /* bytes that will become wydes some day */
1079
 
1080
@ @=
1081
file_node file_info[256]; /* data about each source file */
1082
int buf_size; /* size of buffer for source lines */
1083
Char *buffer;
1084
 
1085
@ As in \.{MMIXAL}, we prefer source lines of length 72 characters or less,
1086
but the user is allowed to increase the limit. (Longer lines will silently
1087
be truncated to the buffer size when the simulator lists them.)
1088
 
1089
@=
1090
if (buf_size<72) buf_size=72;
1091
buffer=(Char*)calloc(buf_size+1,sizeof(Char));
1092
if (!buffer) panic("Can't allocate source line buffer");
1093
@.Can't allocate...@>
1094
 
1095
@ The first time we are called upon to list a line from a given source
1096
file, we make a map of starting locations for each line. Source files
1097
should contain at most 65535 lines. We assume that they contain
1098
no null characters.
1099
 
1100
@=
1101
void make_map @,@,@[ARGS((void))@];@+@t}\6{@>
1102
void make_map()
1103
{
1104
  long map[65536];
1105
  register int k,l;
1106
  register long*p;
1107
  @;
1108
  for (l=1;l<65536 && !feof(src_file);l++) {
1109
    map[l]=ftell(src_file);
1110
   loop:@+if (!fgets(buffer,buf_size,src_file)) break;
1111
    if (buffer[strlen(buffer)-1]!='\n') goto loop;
1112
  }
1113
  file_info[cur_file].line_count=l;
1114
  file_info[cur_file].map=p=(long*)calloc(l,sizeof(long));
1115
  if (!p) panic("No room for a source-line map");
1116
@.No room...@>
1117
  for (k=1;k
1118
}
1119
 
1120
@ We want to warn the user if the source file has changed since the
1121
object file was written. The standard \CEE/ library doesn't provide
1122
the information we need; so we use the \UNIX/ system function |stat|,
1123
in hopes that other operating systems provide a similar way to do the job.
1124
@^system dependencies@>
1125
 
1126
@=
1127
#include 
1128
#include 
1129
 
1130
@ @=
1131
@^system dependencies@>
1132
{
1133
  struct stat stat_buf;
1134
  if (stat(file_info[cur_file].name,&stat_buf)>=0)
1135
    if ((tetra)stat_buf.st_mtime > obj_time)
1136
      fprintf(stderr,
1137
         "Warning: File %s was modified; it may not match the program!\n",
1138
@.File...was modified@>
1139
         file_info[cur_file].name);
1140
}
1141
 
1142
@ Source lines are listed by the |print_line| routine, preceded by
1143
12 characters containing the line number. If a file error occurs,
1144
nothing is printed---not even an error message; the absence of
1145
listed data is itself a message.
1146
 
1147
@=
1148
void print_line @,@,@[ARGS((int))@];@+@t}\6{@>
1149
void print_line(k)
1150
  int k;
1151
{
1152
  char buf[11];
1153
  if (k>=file_info[cur_file].line_count) return;
1154
  if (fseek(src_file,file_info[cur_file].map[k],SEEK_SET)!=0) return;
1155
  if (!fgets(buffer,buf_size,src_file)) return;
1156
  sprintf(buf,"%d:    ",k);
1157
  printf("line %.6s %s",buf,buffer);
1158
  if (buffer[strlen(buffer)-1]!='\n') printf("\n");
1159
  line_shown=true;
1160
}
1161
 
1162
@ @=
1163
#ifndef SEEK_SET
1164
#define SEEK_SET 0 /* code for setting the file pointer to a given offset */
1165
#endif
1166
 
1167
@ The |show_line| routine is called when we want to output line |cur_line|
1168
of source file number |cur_file|, assuming that |cur_line!=0|. Its job
1169
is primarily to maintain continuity, by opening or reopening the |src_file|
1170
if the source file changes, and by connecting the previously output
1171
lines to the new one. Sometimes no output is necessary, because the
1172
desired line has already been printed.
1173
 
1174
@=
1175
void show_line @,@,@[ARGS((void))@];@+@t}\6{@>
1176
void show_line()
1177
{
1178
  register int k;
1179
  if (shown_file!=cur_file) @@;
1180
  else if (shown_line==cur_line) return; /* already shown */
1181
  if (cur_line>shown_line+gap+1 || cur_line
1182
    if (shown_line>0)
1183
      if (cur_line
1184
      else printf("     ...\n"); /* indicate the gap */
1185
    print_line(cur_line);
1186
  }@+else@+ for (k=shown_line+1;k<=cur_line;k++) print_line(k);
1187
  shown_line=cur_line;
1188
}
1189
 
1190
@ @=
1191
FILE *src_file; /* the currently open source file */
1192
int shown_file=-1; /* index of the most recently listed file */
1193
int shown_line; /* the line most recently listed in |shown_file| */
1194
int gap; /* minimum gap between consecutively listed source lines */
1195
bool line_shown; /* did we list anything recently? */
1196
bool showing_source; /* are we listing source lines? */
1197
int profile_gap; /* the |gap| when printing final frequencies */
1198
bool profile_showing_source; /* |showing_source| within final frequencies */
1199
 
1200
@ @=
1201
{
1202
  if (!src_file) src_file=fopen(file_info[cur_file].name,"r");
1203
  else freopen(file_info[cur_file].name,"r",src_file);
1204
  if (!src_file) {
1205
    fprintf(stderr,"Warning: I can't open file %s; source listing omitted.\n",
1206
@.I can't open...@>
1207
               file_info[cur_file].name);
1208
    showing_source=false;
1209
    return;
1210
  }
1211
  printf("\"%s\"\n",file_info[cur_file].name);
1212
  shown_file=cur_file;
1213
  shown_line=0;
1214
  if (!file_info[cur_file].map) make_map();
1215
}
1216
 
1217
@ Here is a simple application of |show_line|. It is a recursive routine that
1218
prints the frequency counts of all instructions that occur in a
1219
given subtree of the simulated memory and that were executed at least once.
1220
The subtree is traversed in symmetric order; therefore the frequencies
1221
appear in increasing order of the instruction locations.
1222
 
1223
@=
1224
void print_freqs @,@,@[ARGS((mem_node*))@];@+@t}\6{@>
1225
void print_freqs(p)
1226
  mem_node *p;
1227
{
1228
  register int j;
1229
  octa cur_loc;
1230
  if (p->left) print_freqs(p->left);
1231
  for (j=0;j<512;j++) if (p->dat[j].freq)
1232
    @loc+4*j|@>;
1233
  if (p->right) print_freqs(p->right);
1234
}
1235
 
1236
@ An ellipsis (\.{...}) is printed between frequency data for nonconsecutive
1237
instructions, unless source line information intervenes.
1238
 
1239
@=
1240
{
1241
  cur_loc=incr(p->loc,4*j);
1242
  if (showing_source && p->dat[j].line_no) {
1243
    cur_file=p->dat[j].file_no, cur_line=p->dat[j].line_no;
1244
    line_shown=false;
1245
    show_line();
1246
    if (line_shown) goto loc_implied;
1247
  }
1248
  if (cur_loc.l!=implied_loc.l || cur_loc.h!=implied_loc.h)
1249
    if (profile_started) printf("         0.        ...\n");
1250
 loc_implied: printf("%10d. %08x%08x: %08x (%s)\n",
1251
      p->dat[j].freq, cur_loc.h, cur_loc.l, p->dat[j].tet,
1252
      info[p->dat[j].tet>>24].name);
1253
  implied_loc=incr(cur_loc,4);@+ profile_started=true;
1254
}
1255
 
1256
@ @=
1257
octa implied_loc; /* location following the last shown frequency data */
1258
bool profile_started; /* have we printed at least one frequency count? */
1259
 
1260
@ @=
1261
{
1262
  printf("\nProgram profile:\n");
1263
  shown_file=cur_file=-1;@+ shown_line=cur_line=0;
1264
  gap=profile_gap;
1265
  showing_source=profile_showing_source;
1266
  implied_loc=neg_one;
1267
  print_freqs(mem_root);
1268
}
1269
 
1270
@* Lists. This simulator needs to deal with 256 different opcodes,
1271
so we might as well enumerate them~now.
1272
 
1273
@=
1274
typedef enum{@/
1275
@!TRAP,@!FCMP,@!FUN,@!FEQL,@!FADD,@!FIX,@!FSUB,@!FIXU,@/
1276
@!FLOT,@!FLOTI,@!FLOTU,@!FLOTUI,@!SFLOT,@!SFLOTI,@!SFLOTU,@!SFLOTUI,@/
1277
@!FMUL,@!FCMPE,@!FUNE,@!FEQLE,@!FDIV,@!FSQRT,@!FREM,@!FINT,@/
1278
@!MUL,@!MULI,@!MULU,@!MULUI,@!DIV,@!DIVI,@!DIVU,@!DIVUI,@/
1279
@!ADD,@!ADDI,@!ADDU,@!ADDUI,@!SUB,@!SUBI,@!SUBU,@!SUBUI,@/
1280
@!IIADDU,@!IIADDUI,@!IVADDU,@!IVADDUI,@!VIIIADDU,@!VIIIADDUI,@!XVIADDU,@!XVIADDUI,@/
1281
@!CMP,@!CMPI,@!CMPU,@!CMPUI,@!NEG,@!NEGI,@!NEGU,@!NEGUI,@/
1282
@!SL,@!SLI,@!SLU,@!SLUI,@!SR,@!SRI,@!SRU,@!SRUI,@/
1283
@!BN,@!BNB,@!BZ,@!BZB,@!BP,@!BPB,@!BOD,@!BODB,@/
1284
@!BNN,@!BNNB,@!BNZ,@!BNZB,@!BNP,@!BNPB,@!BEV,@!BEVB,@/
1285
@!PBN,@!PBNB,@!PBZ,@!PBZB,@!PBP,@!PBPB,@!PBOD,@!PBODB,@/
1286
@!PBNN,@!PBNNB,@!PBNZ,@!PBNZB,@!PBNP,@!PBNPB,@!PBEV,@!PBEVB,@/
1287
@!CSN,@!CSNI,@!CSZ,@!CSZI,@!CSP,@!CSPI,@!CSOD,@!CSODI,@/
1288
@!CSNN,@!CSNNI,@!CSNZ,@!CSNZI,@!CSNP,@!CSNPI,@!CSEV,@!CSEVI,@/
1289
@!ZSN,@!ZSNI,@!ZSZ,@!ZSZI,@!ZSP,@!ZSPI,@!ZSOD,@!ZSODI,@/
1290
@!ZSNN,@!ZSNNI,@!ZSNZ,@!ZSNZI,@!ZSNP,@!ZSNPI,@!ZSEV,@!ZSEVI,@/
1291
@!LDB,@!LDBI,@!LDBU,@!LDBUI,@!LDW,@!LDWI,@!LDWU,@!LDWUI,@/
1292
@!LDT,@!LDTI,@!LDTU,@!LDTUI,@!LDO,@!LDOI,@!LDOU,@!LDOUI,@/
1293
@!LDSF,@!LDSFI,@!LDHT,@!LDHTI,@!CSWAP,@!CSWAPI,@!LDUNC,@!LDUNCI,@/
1294
@!LDVTS,@!LDVTSI,@!PRELD,@!PRELDI,@!PREGO,@!PREGOI,@!GO,@!GOI,@/
1295
@!STB,@!STBI,@!STBU,@!STBUI,@!STW,@!STWI,@!STWU,@!STWUI,@/
1296
@!STT,@!STTI,@!STTU,@!STTUI,@!STO,@!STOI,@!STOU,@!STOUI,@/
1297
@!STSF,@!STSFI,@!STHT,@!STHTI,@!STCO,@!STCOI,@!STUNC,@!STUNCI,@/
1298
@!SYNCD,@!SYNCDI,@!PREST,@!PRESTI,@!SYNCID,@!SYNCIDI,@!PUSHGO,@!PUSHGOI,@/
1299
@!OR,@!ORI,@!ORN,@!ORNI,@!NOR,@!NORI,@!XOR,@!XORI,@/
1300
@!AND,@!ANDI,@!ANDN,@!ANDNI,@!NAND,@!NANDI,@!NXOR,@!NXORI,@/
1301
@!BDIF,@!BDIFI,@!WDIF,@!WDIFI,@!TDIF,@!TDIFI,@!ODIF,@!ODIFI,@/
1302
@!MUX,@!MUXI,@!SADD,@!SADDI,@!MOR,@!MORI,@!MXOR,@!MXORI,@/
1303
@!SETH,@!SETMH,@!SETML,@!SETL,@!INCH,@!INCMH,@!INCML,@!INCL,@/
1304
@!ORH,@!ORMH,@!ORML,@!ORL,@!ANDNH,@!ANDNMH,@!ANDNML,@!ANDNL,@/
1305
@!JMP,@!JMPB,@!PUSHJ,@!PUSHJB,@!GETA,@!GETAB,@!PUT,@!PUTI,@/
1306
@!POP,@!RESUME,@!SAVE,@!UNSAVE,@!SYNC,@!SWYM,@!GET,@!TRIP}@+@!mmix_opcode;
1307
 
1308
@ We also need to enumerate the special names for special registers.
1309
 
1310
@=
1311
typedef enum{
1312
@!rB,@!rD,@!rE,@!rH,@!rJ,@!rM,@!rR,@!rBB,
1313
 @!rC,@!rN,@!rO,@!rS,@!rI,@!rT,@!rTT,@!rK,@!rQ,@!rU,@!rV,@!rG,@!rL,
1314
 @!rA,@!rF,@!rP,@!rW,@!rX,@!rY,@!rZ,@!rWW,@!rXX,@!rYY,@!rZZ} @!special_reg;
1315
 
1316
@ @=
1317
char *special_name[32]={"rB","rD","rE","rH","rJ","rM","rR","rBB",
1318
 "rC","rN","rO","rS","rI","rT","rTT","rK","rQ","rU","rV","rG","rL",
1319
 "rA","rF","rP","rW","rX","rY","rZ","rWW","rXX","rYY","rZZ"};
1320
 
1321
@ Here are the bit codes for arithmetic exceptions. These codes, except
1322
|H_BIT|, are defined also in {\mc MMIX-ARITH}.
1323
 
1324
@d X_BIT (1<<8) /* floating inexact */
1325
@d Z_BIT (1<<9) /* floating division by zero */
1326
@d U_BIT (1<<10) /* floating underflow */
1327
@d O_BIT (1<<11) /* floating overflow */
1328
@d I_BIT (1<<12) /* floating invalid operation */
1329
@d W_BIT (1<<13) /* float-to-fix overflow */
1330
@d V_BIT (1<<14) /* integer overflow */
1331
@d D_BIT (1<<15) /* integer divide check */
1332
@d H_BIT (1<<16) /* trip */
1333
 
1334
@ The |bkpt| field associated with each tetrabyte of memory has
1335
bits associated with forced tracing and/or
1336
breaking for reading, writing, and/or execution.
1337
 
1338
@d trace_bit (1<<3)
1339
@d read_bit (1<<2)
1340
@d write_bit (1<<1)
1341
@d exec_bit (1<<0)
1342
 
1343
@ To complete our lists of lists,
1344
we enumerate the rudimentary operating system calls
1345
that are built in to \.{MMIXAL}.
1346
 
1347
@d max_sys_call Ftell
1348
 
1349
@=
1350
typedef enum{
1351
@!Halt,@!Fopen,@!Fclose,@!Fread,@!Fgets,@!Fgetws,
1352
@!Fwrite,@!Fputs,@!Fputws,@!Fseek,@!Ftell} @!sys_call;
1353
 
1354
@* The main loop. Now let's plunge in to the guts of the simulator,
1355
the master switch that controls most of the action.
1356
 
1357
@=
1358
{
1359
  if (resuming) loc=incr(inst_ptr,-4), inst=g[rX].l;
1360
  else @;
1361
  op=inst>>24;@+xx=(inst>>16)&0xff;@+yy=(inst>>8)&0xff;@+zz=inst&0xff;
1362
  f=info[op].flags;@+yz=inst&0xffff;
1363
  x=y=z=a=b=zero_octa;@+ exc=0;@+ old_L=L;
1364
  if (f&rel_addr_bit) @;
1365
  @;
1366
  if (f&X_is_dest_bit) @
1367
          adjusting the register stack if necessary@>;
1368
  w=oplus(y,z);
1369
  if (loc.h>=0x20000000) goto privileged_inst;
1370
  switch(op) {
1371
  @t\4@>@;
1372
  }
1373
  @;
1374
  @;
1375
  @;
1376
  if (resuming && op!=RESUME) resuming=false;
1377
}
1378
 
1379
@ Operands |x| and |a| are usually destinations (results), computed from
1380
the source operands |y|, |z|, and/or~|b|.
1381
 
1382
@=
1383
octa w,x,y,z,a,b,ma,mb; /* operands */
1384
octa *x_ptr; /* destination */
1385
octa loc; /* location of the current instruction */
1386
octa inst_ptr; /* location of the next instruction */
1387
tetra inst; /* the current instruction */
1388
int old_L; /* value of |L| before the current instruction */
1389
int exc; /* exceptions raised by the current instruction */
1390
int tracing_exceptions; /* exception bits that cause tracing */
1391
int rop; /* ropcode of a resumed instruction */
1392
int round_mode; /* the style of floating point rounding just used */
1393
bool resuming; /* are we resuming an interrupted instruction? */
1394
bool halted; /* did the program come to a halt? */
1395
bool breakpoint; /* should we pause after the current instruction? */
1396
bool tracing; /* should we trace the current instruction? */
1397
bool stack_tracing; /* should we trace details of the register stack? */
1398
bool interacting; /* are we in interactive mode? */
1399
bool interact_after_break; /* should we go into interactive mode? */
1400
bool tripping; /* are we about to go to a trip handler? */
1401
bool good; /* did the last branch instruction guess correctly? */
1402
tetra trace_threshold; /* each instruction should be traced this many times */
1403
 
1404
@ @=
1405
register mmix_opcode op; /* operation code of the current instruction */
1406
register int xx,yy,zz,yz; /* operand fields of the current instruction */
1407
register tetra f; /* properties of the current |op| */
1408
register int i,j,k; /* miscellaneous indices */
1409
register mem_tetra *ll; /* current place in the simulated memory */
1410
register char *p; /* current place in a string */
1411
 
1412
@ @=
1413
{
1414
  loc=inst_ptr;
1415
  ll=mem_find(loc);
1416
  inst=ll->tet;
1417
  cur_file=ll->file_no;
1418
  cur_line=ll->line_no;
1419
  ll->freq++;
1420
  if (ll->bkpt&exec_bit) breakpoint=true;
1421
  tracing=breakpoint||(ll->bkpt&trace_bit)||(ll->freq<=trace_threshold);
1422
  inst_ptr=incr(inst_ptr,4);
1423
}
1424
 
1425
@ Much of the simulation is table-driven, based on a static data
1426
structure called the \&{op\_info} for each operation code.
1427
 
1428
@=
1429
typedef struct {
1430
  char *name; /* symbolic name of an opcode */
1431
  unsigned char flags; /* its instruction format */
1432
  unsigned char third_operand; /* its special register input */
1433
  unsigned char mems; /* how many $\mu$ it costs */
1434
  unsigned char oops; /* how many $\upsilon$ it costs */
1435
  char *trace_format; /* how it appears when traced */
1436
} op_info;
1437
 
1438
@ For example, the |flags| field of |info[op]|
1439
tells us how to obtain the operands from the X, Y, and~Z fields
1440
of the current instruction. Each entry records special properties of an
1441
operation code, in binary notation:
1442
\Hex{1}~means Z~is an immediate value, \Hex{2}~means rZ is
1443
a source operand, \Hex{4}~means Y~is an immediate value, \Hex{8}~means rY is a
1444
source operand, \Hex{10}~means rX is a source operand, \Hex{20}~means
1445
rX is a destination, \Hex{40}~means YZ is part of a relative address,
1446
\Hex{80}~means a push or pop or unsave instruction.
1447
 
1448
The |trace_format| field will be explained later.
1449
 
1450
@d Z_is_immed_bit 0x1
1451
@d Z_is_source_bit 0x2
1452
@d Y_is_immed_bit 0x4
1453
@d Y_is_source_bit 0x8
1454
@d X_is_source_bit 0x10
1455
@d X_is_dest_bit 0x20
1456
@d rel_addr_bit 0x40
1457
@d push_pop_bit 0x80
1458
 
1459
@=
1460
op_info info[256]={
1461
@,
1462
@,
1463
@,
1464
@};
1465
 
1466
@ @=
1467
{"TRAP",0x0a,255,0,5,"%r"},@|
1468
{"FCMP",0x2a,0,0,1,"%l = %.y cmp %.z = %x"},@|
1469
{"FUN",0x2a,0,0,1,"%l = [%.y(||)%.z] = %x"},@|
1470
{"FEQL",0x2a,0,0,1,"%l = [%.y(==)%.z] = %x"},@|
1471
{"FADD",0x2a,0,0,4,"%l = %.y %(+%) %.z = %.x"},@|
1472
{"FIX",0x26,0,0,4,"%l = %(fix%) %.z = %x"},@|
1473
{"FSUB",0x2a,0,0,4,"%l = %.y %(-%) %.z = %.x"},@|
1474
{"FIXU",0x26,0,0,4,"%l = %(fix%) %.z = %#x"},@|
1475
{"FLOT",0x26,0,0,4,"%l = %(flot%) %z = %.x"},@|
1476
{"FLOTI",0x25,0,0,4,"%l = %(flot%) %z = %.x"},@|
1477
{"FLOTU",0x26,0,0,4,"%l = %(flot%) %#z = %.x"},@|
1478
{"FLOTUI",0x25,0,0,4,"%l = %(flot%) %z = %.x"},@|
1479
{"SFLOT",0x26,0,0,4,"%l = %(sflot%) %z = %.x"},@|
1480
{"SFLOTI",0x25,0,0,4,"%l = %(sflot%) %z = %.x"},@|
1481
{"SFLOTU",0x26,0,0,4,"%l = %(sflot%) %#z = %.x"},@|
1482
{"SFLOTUI",0x25,0,0,4,"%l = %(sflot%) %z = %.x"},@|
1483
{"FMUL",0x2a,0,0,4,"%l = %.y %(*%) %.z = %.x"},@|
1484
{"FCMPE",0x2a,rE,0,4,"%l = %.y cmp %.z (%.b)) = %x"},@|
1485
{"FUNE",0x2a,rE,0,1,"%l = [%.y(||)%.z (%.b)] = %x"},@|
1486
{"FEQLE",0x2a,rE,0,4,"%l = [%.y(==)%.z (%.b)] = %x"},@|
1487
{"FDIV",0x2a,0,0,40,"%l = %.y %(/%) %.z = %.x"},@|
1488
{"FSQRT",0x26,0,0,40,"%l = %(sqrt%) %.z = %.x"},@|
1489
{"FREM",0x2a,0,0,4,"%l = %.y %(rem%) %.z = %.x"},@|
1490
{"FINT",0x26,0,0,4,"%l = %(int%) %.z = %.x"},@|
1491
{"MUL",0x2a,0,0,10,"%l = %y * %z = %x"},@|
1492
{"MULI",0x29,0,0,10,"%l = %y * %z = %x"},@|
1493
{"MULU",0x2a,0,0,10,"%l = %#y * %#z = %#x, rH=%#a"},@|
1494
{"MULUI",0x29,0,0,10,"%l = %#y * %z = %#x, rH=%#a"},@|
1495
{"DIV",0x2a,0,0,60,"%l = %y / %z = %x, rR=%a"},@|
1496
{"DIVI",0x29,0,0,60,"%l = %y / %z = %x, rR=%a"},@|
1497
{"DIVU",0x2a,rD,0,60,"%l = %#b%0y / %#z = %#x, rR=%#a"},@|
1498
{"DIVUI",0x29,rD,0,60,"%l = %#b%0y / %z = %#x, rR=%#a"},@|
1499
{"ADD",0x2a,0,0,1,"%l = %y + %z = %x"},@|
1500
{"ADDI",0x29,0,0,1,"%l = %y + %z = %x"},@|
1501
{"ADDU",0x2a,0,0,1,"%l = %#y + %#z = %#x"},@|
1502
{"ADDUI",0x29,0,0,1,"%l = %#y + %z = %#x"},@|
1503
{"SUB",0x2a,0,0,1,"%l = %y - %z = %x"},@|
1504
{"SUBI",0x29,0,0,1,"%l = %y - %z = %x"},@|
1505
{"SUBU",0x2a,0,0,1,"%l = %#y - %#z = %#x"},@|
1506
{"SUBUI",0x29,0,0,1,"%l = %#y - %z = %#x"},@|
1507
{"2ADDU",0x2a,0,0,1,"%l = %#y <<1+ %#z = %#x"},@|
1508
{"2ADDUI",0x29,0,0,1,"%l = %#y <<1+ %z = %#x"},@|
1509
{"4ADDU",0x2a,0,0,1,"%l = %#y <<2+ %#z = %#x"},@|
1510
{"4ADDUI",0x29,0,0,1,"%l = %#y <<2+ %z = %#x"},@|
1511
{"8ADDU",0x2a,0,0,1,"%l = %#y <<3+ %#z = %#x"},@|
1512
{"8ADDUI",0x29,0,0,1,"%l = %#y <<3+ %z = %#x"},@|
1513
{"16ADDU",0x2a,0,0,1,"%l = %#y <<4+ %#z = %#x"},@|
1514
{"16ADDUI",0x29,0,0,1,"%l = %#y <<4+ %z = %#x"},@|
1515
{"CMP",0x2a,0,0,1,"%l = %y cmp %z = %x"},@|
1516
{"CMPI",0x29,0,0,1,"%l = %y cmp %z = %x"},@|
1517
{"CMPU",0x2a,0,0,1,"%l = %#y cmp %#z = %x"},@|
1518
{"CMPUI",0x29,0,0,1,"%l = %#y cmp %z = %x"},@|
1519
{"NEG",0x26,0,0,1,"%l = %y - %z = %x"},@|
1520
{"NEGI",0x25,0,0,1,"%l = %y - %z = %x"},@|
1521
{"NEGU",0x26,0,0,1,"%l = %y - %#z = %#x"},@|
1522
{"NEGUI",0x25,0,0,1,"%l = %y - %z = %#x"},@|
1523
{"SL",0x2a,0,0,1,"%l = %y << %#z = %x"},@|
1524
{"SLI",0x29,0,0,1,"%l = %y << %z = %x"},@|
1525
{"SLU",0x2a,0,0,1,"%l = %#y << %#z = %#x"},@|
1526
{"SLUI",0x29,0,0,1,"%l = %#y << %z = %#x"},@|
1527
{"SR",0x2a,0,0,1,"%l = %y >> %#z = %x"},@|
1528
{"SRI",0x29,0,0,1,"%l = %y >> %z = %x"},@|
1529
{"SRU",0x2a,0,0,1,"%l = %#y >> %#z = %#x"},@|
1530
{"SRUI",0x29,0,0,1,"%l = %#y >> %z = %#x"}
1531
 
1532
@ @=
1533
{"BN",0x50,0,0,1,"%b<0? %t%g"},@|
1534
{"BNB",0x50,0,0,1,"%b<0? %t%g"},@|
1535
{"BZ",0x50,0,0,1,"%b==0? %t%g"},@|
1536
{"BZB",0x50,0,0,1,"%b==0? %t%g"},@|
1537
{"BP",0x50,0,0,1,"%b>0? %t%g"},@|
1538
{"BPB",0x50,0,0,1,"%b>0? %t%g"},@|
1539
{"BOD",0x50,0,0,1,"%b odd? %t%g"},@|
1540
{"BODB",0x50,0,0,1,"%b odd? %t%g"},@|
1541
{"BNN",0x50,0,0,1,"%b>=0? %t%g"},@|
1542
{"BNNB",0x50,0,0,1,"%b>=0? %t%g"},@|
1543
{"BNZ",0x50,0,0,1,"%b!=0? %t%g"},@|
1544
{"BNZB",0x50,0,0,1,"%b!=0? %t%g"},@|
1545
{"BNP",0x50,0,0,1,"%b<=0? %t%g"},@|
1546
{"BNPB",0x50,0,0,1,"%b<=0? %t%g"},@|
1547
{"BEV",0x50,0,0,1,"%b even? %t%g"},@|
1548
{"BEVB",0x50,0,0,1,"%b even? %t%g"},@|
1549
{"PBN",0x50,0,0,1,"%b<0? %t%g"},@|
1550
{"PBNB",0x50,0,0,1,"%b<0? %t%g"},@|
1551
{"PBZ",0x50,0,0,1,"%b==0? %t%g"},@|
1552
{"PBZB",0x50,0,0,1,"%b==0? %t%g"},@|
1553
{"PBP",0x50,0,0,1,"%b>0? %t%g"},@|
1554
{"PBPB",0x50,0,0,1,"%b>0? %t%g"},@|
1555
{"PBOD",0x50,0,0,1,"%b odd? %t%g"},@|
1556
{"PBODB",0x50,0,0,1,"%b odd? %t%g"},@|
1557
{"PBNN",0x50,0,0,1,"%b>=0? %t%g"},@|
1558
{"PBNNB",0x50,0,0,1,"%b>=0? %t%g"},@|
1559
{"PBNZ",0x50,0,0,1,"%b!=0? %t%g"},@|
1560
{"PBNZB",0x50,0,0,1,"%b!=0? %t%g"},@|
1561
{"PBNP",0x50,0,0,1,"%b<=0? %t%g"},@|
1562
{"PBNPB",0x50,0,0,1,"%b<=0? %t%g"},@|
1563
{"PBEV",0x50,0,0,1,"%b even? %t%g"},@|
1564
{"PBEVB",0x50,0,0,1,"%b even? %t%g"},@|
1565
{"CSN",0x3a,0,0,1,"%l = %y<0? %z: %b = %x"},@|
1566
{"CSNI",0x39,0,0,1,"%l = %y<0? %z: %b = %x"},@|
1567
{"CSZ",0x3a,0,0,1,"%l = %y==0? %z: %b = %x"},@|
1568
{"CSZI",0x39,0,0,1,"%l = %y==0? %z: %b = %x"},@|
1569
{"CSP",0x3a,0,0,1,"%l = %y>0? %z: %b = %x"},@|
1570
{"CSPI",0x39,0,0,1,"%l = %y>0? %z: %b = %x"},@|
1571
{"CSOD",0x3a,0,0,1,"%l = %y odd? %z: %b = %x"},@|
1572
{"CSODI",0x39,0,0,1,"%l = %y odd? %z: %b = %x"},@|
1573
{"CSNN",0x3a,0,0,1,"%l = %y>=0? %z: %b = %x"},@|
1574
{"CSNNI",0x39,0,0,1,"%l = %y>=0? %z: %b = %x"},@|
1575
{"CSNZ",0x3a,0,0,1,"%l = %y!=0? %z: %b = %x"},@|
1576
{"CSNZI",0x39,0,0,1,"%l = %y!=0? %z: %b = %x"},@|
1577
{"CSNP",0x3a,0,0,1,"%l = %y<=0? %z: %b = %x"},@|
1578
{"CSNPI",0x39,0,0,1,"%l = %y<=0? %z: %b = %x"},@|
1579
{"CSEV",0x3a,0,0,1,"%l = %y even? %z: %b = %x"},@|
1580
{"CSEVI",0x39,0,0,1,"%l = %y even? %z: %b = %x"},@|
1581
{"ZSN",0x2a,0,0,1,"%l = %y<0? %z: 0 = %x"},@|
1582
{"ZSNI",0x29,0,0,1,"%l = %y<0? %z: 0 = %x"},@|
1583
{"ZSZ",0x2a,0,0,1,"%l = %y==0? %z: 0 = %x"},@|
1584
{"ZSZI",0x29,0,0,1,"%l = %y==0? %z: 0 = %x"},@|
1585
{"ZSP",0x2a,0,0,1,"%l = %y>0? %z: 0 = %x"},@|
1586
{"ZSPI",0x29,0,0,1,"%l = %y>0? %z: 0 = %x"},@|
1587
{"ZSOD",0x2a,0,0,1,"%l = %y odd? %z: 0 = %x"},@|
1588
{"ZSODI",0x29,0,0,1,"%l = %y odd? %z: 0 = %x"},@|
1589
{"ZSNN",0x2a,0,0,1,"%l = %y>=0? %z: 0 = %x"},@|
1590
{"ZSNNI",0x29,0,0,1,"%l = %y>=0? %z: 0 = %x"},@|
1591
{"ZSNZ",0x2a,0,0,1,"%l = %y!=0? %z: 0 = %x"},@|
1592
{"ZSNZI",0x29,0,0,1,"%l = %y!=0? %z: 0 = %x"},@|
1593
{"ZSNP",0x2a,0,0,1,"%l = %y<=0? %z: 0 = %x"},@|
1594
{"ZSNPI",0x29,0,0,1,"%l = %y<=0? %z: 0 = %x"},@|
1595
{"ZSEV",0x2a,0,0,1,"%l = %y even? %z: 0 = %x"},@|
1596
{"ZSEVI",0x29,0,0,1,"%l = %y even? %z: 0 = %x"}
1597
 
1598
@ @=
1599
{"LDB",0x2a,0,1,1,"%l = M1[%#y+%#z] = %x"},@|
1600
{"LDBI",0x29,0,1,1,"%l = M1[%#y%?+] = %x"},@|
1601
{"LDBU",0x2a,0,1,1,"%l = M1[%#y+%#z] = %#x"},@|
1602
{"LDBUI",0x29,0,1,1,"%l = M1[%#y%?+] = %#x"},@|
1603
{"LDW",0x2a,0,1,1,"%l = M2[%#y+%#z] = %x"},@|
1604
{"LDWI",0x29,0,1,1,"%l = M2[%#y%?+] = %x"},@|
1605
{"LDWU",0x2a,0,1,1,"%l = M2[%#y+%#z] = %#x"},@|
1606
{"LDWUI",0x29,0,1,1,"%l = M2[%#y%?+] = %#x"},@|
1607
{"LDT",0x2a,0,1,1,"%l = M4[%#y+%#z] = %x"},@|
1608
{"LDTI",0x29,0,1,1,"%l = M4[%#y%?+] = %x"},@|
1609
{"LDTU",0x2a,0,1,1,"%l = M4[%#y+%#z] = %#x"},@|
1610
{"LDTUI",0x29,0,1,1,"%l = M4[%#y%?+] = %#x"},@|
1611
{"LDO",0x2a,0,1,1,"%l = M8[%#y+%#z] = %x"},@|
1612
{"LDOI",0x29,0,1,1,"%l = M8[%#y%?+] = %x"},@|
1613
{"LDOU",0x2a,0,1,1,"%l = M8[%#y+%#z] = %#x"},@|
1614
{"LDOUI",0x29,0,1,1,"%l = M8[%#y%?+] = %#x"},@|
1615
{"LDSF",0x2a,0,1,1,"%l = (M4[%#y+%#z]) = %.x"},@|
1616
{"LDSFI",0x29,0,1,1,"%l = (M4[%#y%?+]) = %.x"},@|
1617
{"LDHT",0x2a,0,1,1,"%l = M4[%#y+%#z]<<32 = %#x"},@|
1618
{"LDHTI",0x29,0,1,1,"%l = M4[%#y%?+]<<32 = %#x"},@|
1619
{"CSWAP",0x3a,0,2,2,"%l = [M8[%#y+%#z]==%a] = %x, %r"},@|
1620
{"CSWAPI",0x39,0,2,2,"%l = [M8[%#y%?+]==%a] = %x, %r"},@|
1621
{"LDUNC",0x2a,0,1,1,"%l = M8[%#y+%#z] = %#x"},@|
1622
{"LDUNCI",0x29,0,1,1,"%l = M8[%#y%?+] = %#x"},@|
1623
{"LDVTS",0x2a,0,0,1,""},@|
1624
{"LDVTSI",0x29,0,0,1,""},@|
1625
{"PRELD",0x0a,0,0,1,"[%#y+%#z .. %#x]"},@|
1626
{"PRELDI",0x09,0,0,1,"[%#y%?+ .. %#x]"},@|
1627
{"PREGO",0x0a,0,0,1,"[%#y+%#z .. %#x]"},@|
1628
{"PREGOI",0x09,0,0,1,"[%#y%?+ .. %#x]"},@|
1629
{"GO",0x2a,0,0,3,"%l = %#x, -> %#y+%#z"},@|
1630
{"GOI",0x29,0,0,3,"%l = %#x, -> %#y%?+"},@|
1631
{"STB",0x1a,0,1,1,"M1[%#y+%#z] = %b, M8[%#w]=%#a"},@|
1632
{"STBI",0x19,0,1,1,"M1[%#y%?+] = %b, M8[%#w]=%#a"},@|
1633
{"STBU",0x1a,0,1,1,"M1[%#y+%#z] = %#b, M8[%#w]=%#a"},@|
1634
{"STBUI",0x19,0,1,1,"M1[%#y%?+] = %#b, M8[%#w]=%#a"},@|
1635
{"STW",0x1a,0,1,1,"M2[%#y+%#z] = %b, M8[%#w]=%#a"},@|
1636
{"STWI",0x19,0,1,1,"M2[%#y%?+] = %b, M8[%#w]=%#a"},@|
1637
{"STWU",0x1a,0,1,1,"M2[%#y+%#z] = %#b, M8[%#w]=%#a"},@|
1638
{"STWUI",0x19,0,1,1,"M2[%#y%?+] = %#b, M8[%#w]=%#a"},@|
1639
{"STT",0x1a,0,1,1,"M4[%#y+%#z] = %b, M8[%#w]=%#a"},@|
1640
{"STTI",0x19,0,1,1,"M4[%#y%?+] = %b, M8[%#w]=%#a"},@|
1641
{"STTU",0x1a,0,1,1,"M4[%#y+%#z] = %#b, M8[%#w]=%#a"},@|
1642
{"STTUI",0x19,0,1,1,"M4[%#y%?+] = %#b, M8[%#w]=%#a"},@|
1643
{"STO",0x1a,0,1,1,"M8[%#y+%#z] = %b"},@|
1644
{"STOI",0x19,0,1,1,"M8[%#y%?+] = %b"},@|
1645
{"STOU",0x1a,0,1,1,"M8[%#y+%#z] = %#b"},@|
1646
{"STOUI",0x19,0,1,1,"M8[%#y%?+] = %#b"},@|
1647
{"STSF",0x1a,0,1,1,"%(M4[%#y+%#z]%) = %.b, M8[%#w]=%#a"},@|
1648
{"STSFI",0x19,0,1,1,"%(M4[%#y%?+]%) = %.b, M8[%#w]=%#a"},@|
1649
{"STHT",0x1a,0,1,1,"M4[%#y+%#z] = %#b>>32, M8[%#w]=%#a"},@|
1650
{"STHTI",0x19,0,1,1,"M4[%#y%?+] = %#b>>32, M8[%#w]=%#a"},@|
1651
{"STCO",0x0a,0,1,1,"M8[%#y+%#z] = %b"},@|
1652
{"STCOI",0x09,0,1,1,"M8[%#y%?+] = %b"},@|
1653
{"STUNC",0x1a,0,1,1,"M8[%#y+%#z] = %#b"},@|
1654
{"STUNCI",0x19,0,1,1,"M8[%#y%?+] = %#b"},@|
1655
{"SYNCD",0x0a,0,0,1,"[%#y+%#z .. %#x]"},@|
1656
{"SYNCDI",0x09,0,0,1,"[%#y%?+ .. %#x]"},@|
1657
{"PREST",0x0a,0,0,1,"[%#y+%#z .. %#x]"},@|
1658
{"PRESTI",0x09,0,0,1,"[%#y%?+ .. %#x]"},@|
1659
{"SYNCID",0x0a,0,0,1,"[%#y+%#z .. %#x]"},@|
1660
{"SYNCIDI",0x09,0,0,1,"[%#y%?+ .. %#x]"},@|
1661
{"PUSHGO",0xaa,0,0,3,"%lrO=%#b, rL=%a, rJ=%#x, -> %#y+%#z"},@|
1662
{"PUSHGOI",0xa9,0,0,3,"%lrO=%#b, rL=%a, rJ=%#x, -> %#y%?+"}
1663
 
1664
@ @=
1665
{"OR",0x2a,0,0,1,"%l = %#y | %#z = %#x"},@|
1666
{"ORI",0x29,0,0,1,"%l = %#y | %z = %#x"},@|
1667
{"ORN",0x2a,0,0,1,"%l = %#y |~ %#z = %#x"},@|
1668
{"ORNI",0x29,0,0,1,"%l = %#y |~ %z = %#x"},@|
1669
{"NOR",0x2a,0,0,1,"%l = %#y ~| %#z = %#x"},@|
1670
{"NORI",0x29,0,0,1,"%l = %#y ~| %z = %#x"},@|
1671
{"XOR",0x2a,0,0,1,"%l = %#y ^ %#z = %#x"},@|
1672
{"XORI",0x29,0,0,1,"%l = %#y ^ %z = %#x"},@|
1673
{"AND",0x2a,0,0,1,"%l = %#y & %#z = %#x"},@|
1674
{"ANDI",0x29,0,0,1,"%l = %#y & %z = %#x"},@|
1675
{"ANDN",0x2a,0,0,1,"%l = %#y \\ %#z = %#x"},@|
1676
{"ANDNI",0x29,0,0,1,"%l = %#y \\ %z = %#x"},@|
1677
{"NAND",0x2a,0,0,1,"%l = %#y ~& %#z = %#x"},@|
1678
{"NANDI",0x29,0,0,1,"%l = %#y ~& %z = %#x"},@|
1679
{"NXOR",0x2a,0,0,1,"%l = %#y ~^ %#z = %#x"},@|
1680
{"NXORI",0x29,0,0,1,"%l = %#y ~^ %z = %#x"},@|
1681
{"BDIF",0x2a,0,0,1,"%l = %#y bdif %#z = %#x"},@|
1682
{"BDIFI",0x29,0,0,1,"%l = %#y bdif %z = %#x"},@|
1683
{"WDIF",0x2a,0,0,1,"%l = %#y wdif %#z = %#x"},@|
1684
{"WDIFI",0x29,0,0,1,"%l = %#y wdif %z = %#x"},@|
1685
{"TDIF",0x2a,0,0,1,"%l = %#y tdif %#z = %#x"},@|
1686
{"TDIFI",0x29,0,0,1,"%l = %#y tdif %z = %#x"},@|
1687
{"ODIF",0x2a,0,0,1,"%l = %#y odif %#z = %#x"},@|
1688
{"ODIFI",0x29,0,0,1,"%l = %#y odif %z = %#x"},@|
1689
{"MUX",0x2a,rM,0,1,"%l = %#b? %#y: %#z = %#x"},@|
1690
{"MUXI",0x29,rM,0,1,"%l = %#b? %#y: %z = %#x"},@|
1691
{"SADD",0x2a,0,0,1,"%l = nu(%#y\\%#z) = %x"},@|
1692
{"SADDI",0x29,0,0,1,"%l = nu(%#y%?\\) = %x"},@|
1693
{"MOR",0x2a,0,0,1,"%l = %#y mor %#z = %#x"},@|
1694
{"MORI",0x29,0,0,1,"%l = %#y mor %z = %#x"},@|
1695
{"MXOR",0x2a,0,0,1,"%l = %#y mxor %#z = %#x"},@|
1696
{"MXORI",0x29,0,0,1,"%l = %#y mxor %z = %#x"},@|
1697
{"SETH",0x20,0,0,1,"%l = %#z"},@|
1698
{"SETMH",0x20,0,0,1,"%l = %#z"},@|
1699
{"SETML",0x20,0,0,1,"%l = %#z"},@|
1700
{"SETL",0x20,0,0,1,"%l = %#z"},@|
1701
{"INCH",0x30,0,0,1,"%l = %#y + %#z = %#x"},@|
1702
{"INCMH",0x30,0,0,1,"%l = %#y + %#z = %#x"},@|
1703
{"INCML",0x30,0,0,1,"%l = %#y + %#z = %#x"},@|
1704
{"INCL",0x30,0,0,1,"%l = %#y + %#z = %#x"},@|
1705
{"ORH",0x30,0,0,1,"%l = %#y | %#z = %#x"},@|
1706
{"ORMH",0x30,0,0,1,"%l = %#y | %#z = %#x"},@|
1707
{"ORML",0x30,0,0,1,"%l = %#y | %#z = %#x"},@|
1708
{"ORL",0x30,0,0,1,"%l = %#y | %#z = %#x"},@|
1709
{"ANDNH",0x30,0,0,1,"%l = %#y \\ %#z = %#x"},@|
1710
{"ANDNMH",0x30,0,0,1,"%l = %#y \\ %#z = %#x"},@|
1711
{"ANDNML",0x30,0,0,1,"%l = %#y \\ %#z = %#x"},@|
1712
{"ANDNL",0x30,0,0,1,"%l = %#y \\ %#z = %#x"},@|
1713
{"JMP",0x40,0,0,1,"-> %#z"},@|
1714
{"JMPB",0x40,0,0,1,"-> %#z"},@|
1715
{"PUSHJ",0xe0,0,0,1,"%lrO=%#b, rL=%a, rJ=%#x, -> %#z"},@|
1716
{"PUSHJB",0xe0,0,0,1,"%lrO=%#b, rL=%a, rJ=%#x, -> %#z"},@|
1717
{"GETA",0x60,0,0,1,"%l = %#z"},@|
1718
{"GETAB",0x60,0,0,1,"%l = %#z"},@|
1719
{"PUT",0x02,0,0,1,"%s = %r"},@|
1720
{"PUTI",0x01,0,0,1,"%s = %r"},@|
1721
{"POP",0x80,rJ,0,3,"%lrL=%a, rO=%#b, -> %#y%?+"},@|
1722
{"RESUME",0x00,0,0,5,"{%#b} -> %#z"},@|
1723
{"SAVE",0x20,0,20,1,"%l = %#x"},@|
1724
{"UNSAVE",0x82,0,20,1,"%#z: rG=%x, ..., rL=%a"},@|
1725
{"SYNC",0x01,0,0,1,""},@|
1726
{"SWYM",0x00,0,0,1,""},@|
1727
{"GET",0x20,0,0,1,"%l = %s = %#x"},@|
1728
{"TRIP",0x0a,255,0,5,"rW=%#w, rX=%#x, rY=%#y, rZ=%#z, rB=%#b, g[255]=%#a"}
1729
 
1730
@ @=
1731
{
1732
  if ((op&0xfe)==JMP) yz=inst&0xffffff;
1733
  if (op&1) yz-=(op==JMPB? 0x1000000: 0x10000);
1734
  y=inst_ptr;@+ z=incr(loc,yz<<2);
1735
}
1736
 
1737
@ @=
1738
if (resuming && rop!=RESUME_AGAIN)
1739
  @@;
1740
else {
1741
  if (f&0x10) @;
1742
  if (info[op].third_operand) @;
1743
  if (f&0x1) z.l=zz;
1744
  else if (f&0x2) @@;
1745
  else if ((op&0xf0)==SETH) @;
1746
  if (f&0x4) y.l=yy;
1747
  else if (f&0x8) @;
1748
}
1749
 
1750
@ There are 256 global registers, |g[0]| through |g[255]|; the
1751
first 32 of them are used for the special registers |rA|, |rB|, etc.
1752
There are |lring_mask+1| local registers, usually 256 but the
1753
user can increase this to a larger power of~2 if desired.
1754
 
1755
The current values of rL, rG, rO, and rS are kept in separate variables
1756
called |L|, |G|, |O|, and |S| for convenience. (In fact, |O| and |S|
1757
actually hold the values rO/8 and rS/8, modulo |lring_size|.)
1758
 
1759
@=
1760
{
1761
  if (zz>=G) z=g[zz];
1762
  else if (zz
1763
}
1764
 
1765
@ @=
1766
{
1767
  if (yy>=G) y=g[yy];
1768
  else if (yy
1769
}
1770
 
1771
@ @=
1772
{
1773
  if (xx>=G) b=g[xx];
1774
  else if (xx
1775
}
1776
 
1777
@ @=
1778
register int G,L,O; /* accessible copies of key registers */
1779
 
1780
@ @=
1781
octa g[256]; /* global registers */
1782
octa *l; /* local registers */
1783
int lring_size; /* the number of local registers (a power of 2) */
1784
int lring_mask; /* one less than |lring_size| */
1785
int S; /* congruent to $\rm rS\GG 3$ modulo |lring_size| */
1786
 
1787
@ Several of the global registers have constant values, because
1788
of the way \MMIX\ has been simplified in this simulator.
1789
 
1790
Special register rN has a constant value identifying the time of compilation.
1791
(The macro \.{ABSTIME} is defined externally in the file \.{abstime.h},
1792
which should have just been created by {\mc ABSTIME}\kern.05em;
1793
{\mc ABSTIME} is
1794
a trivial program that computes the value of the standard library function
1795
|time(NULL)|. We assume that this number, which is the number of seconds in
1796
the ``{\mc UNIX} epoch,'' is less than~$2^{32}$. Beware: Our assumption will
1797
fail in February of 2106.)
1798
@^system dependencies@>
1799
 
1800
@d VERSION 1 /* version of the \MMIX\ architecture that we support */
1801
@d SUBVERSION 0 /* secondary byte of version number */
1802
@d SUBSUBVERSION 1 /* further qualification to version number */
1803
 
1804
@=
1805
g[rK]=neg_one;
1806
g[rN].h=(VERSION<<24)+(SUBVERSION<<16)+(SUBSUBVERSION<<8);
1807
g[rN].l=ABSTIME; /* see comment and warning above */
1808
g[rT].h=0x80000005;
1809
g[rTT].h=0x80000006;
1810
g[rV].h=0x369c2004;
1811
if (lring_size<256) lring_size=256;
1812
lring_mask=lring_size-1;
1813
if (lring_size&lring_mask)
1814
  panic("The number of local registers must be a power of 2");
1815
@.The number of local...@>
1816
l=(octa*)calloc(lring_size,sizeof(octa));
1817
if (!l) panic("No room for the local registers");
1818
@.No room...@>
1819
cur_round=ROUND_NEAR;
1820
 
1821
@ In operations like |INCH|, we want |z| to be the |yz| field,
1822
shifted left 48 bits. We also want |y| to be register~X, which has
1823
previously been placed in |b|; then |INCH| can be simulated as if
1824
it were |ADDU|.
1825
 
1826
@=
1827
{
1828
  switch (op&3) {
1829
 case 0: z.h=yz<<16;@+break;
1830
 case 1: z.h=yz;@+break;
1831
 case 2: z.l=yz<<16;@+break;
1832
 case 3: z.l=yz;@+break;
1833
  }
1834
  y=b;
1835
}
1836
 
1837
@ @=
1838
b=g[info[op].third_operand];
1839
 
1840
@ @=
1841
if (xx>=G) {
1842
  sprintf(lhs,"$%d=g[%d]",xx,xx);
1843
  x_ptr=&g[xx];
1844
}@+else {
1845
  while (xx>=L) @;
1846
  sprintf(lhs,"$%d=l[%d]",xx,(O+xx)&lring_mask);
1847
  x_ptr=&l[(O+xx)&lring_mask];
1848
}
1849
 
1850
@ @=
1851
{
1852
  l[(O+L)&lring_mask]=zero_octa;
1853
  L=g[rL].l=L+1;
1854
  if (((S-O-L)&lring_mask)==0) stack_store();
1855
}
1856
 
1857
@ The |stack_store| routine advances the ``gamma'' pointer in the
1858
ring of local registers, by storing the oldest local register into memory
1859
location~rS and advancing rS.
1860
 
1861
@d test_store_bkpt(ll) if ((ll)->bkpt&write_bit) breakpoint=tracing=true
1862
 
1863
@=
1864
void stack_store @,@,@[ARGS((void))@];@+@t}\6{@>
1865
void stack_store()
1866
{
1867
  register mem_tetra *ll=mem_find(g[rS]);
1868
  register int k=S&lring_mask;
1869
  ll->tet=l[k].h;@+test_store_bkpt(ll);
1870
  (ll+1)->tet=l[k].l;@+test_store_bkpt(ll+1);
1871
  if (stack_tracing) {
1872
    tracing=true;
1873
    if (cur_line) show_line();
1874
    printf("             M8[#%08x%08x]=l[%d]=#%08x%08x, rS+=8\n",
1875
              g[rS].h,g[rS].l,k,l[k].h,l[k].l);
1876
  }
1877
  g[rS]=incr(g[rS],8),  S++;
1878
}
1879
 
1880
@ The |stack_load| routine is essentially the inverse of |stack_store|.
1881
 
1882
@d test_load_bkpt(ll) if ((ll)->bkpt&read_bit) breakpoint=tracing=true
1883
 
1884
@=
1885
void stack_load @,@,@[ARGS((void))@];@+@t}\6{@>
1886
void stack_load()
1887
{
1888
  register mem_tetra *ll;
1889
  register int k;
1890
  S--, g[rS]=incr(g[rS],-8);
1891
  ll=mem_find(g[rS]);
1892
  k=S&lring_mask;
1893
  l[k].h=ll->tet;@+test_load_bkpt(ll);
1894
  l[k].l=(ll+1)->tet;@+test_load_bkpt(ll+1);
1895
  if (stack_tracing) {
1896
    tracing=true;
1897
    if (cur_line) show_line();
1898
    printf("             rS-=8, l[%d]=M8[#%08x%08x]=#%08x%08x\n",
1899
              k,g[rS].h,g[rS].l,l[k].h,l[k].l);
1900
  }
1901
}
1902
 
1903
@* Simulating the instructions. The master switch branches in 256
1904
directions, one for each \MMIX\ instruction.
1905
 
1906
Let's start with |ADD|, since it is somehow the most typical case---not
1907
too easy, and not too hard. The task is to compute |x=y+z|, and to
1908
signal overflow if the sum is out of range. Overflow occurs if and
1909
only if |y| and |z| have the same sign but the sum has a different sign.
1910
 
1911
Overflow is one of the eight arithmetic exceptions. We record such
1912
exceptions in a variable called~|exc|, which is set to
1913
zero at the beginning of each cycle and used to update~rA at the end.
1914
 
1915
The main control routine has put the input operands into octabytes
1916
|y| and~|z|. It has also made |x_ptr| point to the octabyte where the
1917
result should be placed.
1918
 
1919
@=
1920
case ADD: case ADDI: x=w; /* |w=oplus(y,z)| */
1921
 if (((y.h^z.h)&sign_bit)==0 && ((y.h^x.h)&sign_bit)!=0) exc|=V_BIT;
1922
store_x: *x_ptr=x;@+break;
1923
 
1924
@ Other cases of signed and unsigned addition and subtraction are,
1925
of course, similar. Overflow occurs in the calculation |x=y-z| if and
1926
only if it occurs in the calculation |y=x+z|.
1927
 
1928
@=
1929
case SUB: case SUBI: case NEG: case NEGI: x=ominus(y,z);
1930
 if (((x.h^z.h)&sign_bit)==0 && ((x.h^y.h)&sign_bit)!=0) exc|=V_BIT;
1931
 goto store_x;
1932
case ADDU: case ADDUI: case INCH: case INCMH: case INCML: case INCL:
1933
 x=w;@+goto store_x;
1934
case SUBU: case SUBUI: case NEGU: case NEGUI: x=ominus(y,z);@+goto store_x;
1935
case IIADDU: case IIADDUI: case IVADDU: case IVADDUI:
1936
case VIIIADDU: case VIIIADDUI: case XVIADDU: case XVIADDUI:
1937
 x=oplus(shift_left(y,((op&0xf)>>1)-3),z);@+goto store_x;
1938
case SETH: case SETMH: case SETML: case SETL: case GETA: case GETAB:
1939
 x=z;@+goto store_x;
1940
 
1941
@ Let's get the simple bitwise operations out of the way too.
1942
 
1943
@=
1944
case OR: case ORI: case ORH: case ORMH: case ORML: case ORL:
1945
 x.h=y.h|z.h;@+ x.l=y.l|z.l;@+ goto store_x;
1946
case ORN: case ORNI:
1947
 x.h=y.h|~z.h;@+ x.l=y.l|~z.l;@+ goto store_x;
1948
case NOR: case NORI:
1949
 x.h=~(y.h|z.h);@+ x.l=~(y.l|z.l);@+ goto store_x;
1950
case XOR: case XORI:
1951
 x.h=y.h^z.h;@+ x.l=y.l^z.l;@+ goto store_x;
1952
case AND: case ANDI:
1953
 x.h=y.h&z.h;@+ x.l=y.l&z.l;@+ goto store_x;
1954
case ANDN: case ANDNI: case ANDNH: case ANDNMH: case ANDNML: case ANDNL:
1955
 x.h=y.h&~z.h;@+ x.l=y.l&~z.l;@+ goto store_x;
1956
case NAND: case NANDI:
1957
 x.h=~(y.h&z.h);@+ x.l=~(y.l&z.l);@+ goto store_x;
1958
case NXOR: case NXORI:
1959
 x.h=~(y.h^z.h);@+ x.l=~(y.l^z.l);@+ goto store_x;
1960
 
1961
@ The less simple bit manipulations are almost equally simple,
1962
given the subroutines of {\mc MMIX-ARITH}.
1963
The |MUX| operation has three inputs;
1964
in such cases the inputs appear in |y|, |z|, and~|b|.
1965
 
1966
@d shift_amt (z.h || z.l>=64? 64: z.l)
1967
 
1968
@=
1969
case SL: case SLI: x=shift_left(y,shift_amt);
1970
  a=shift_right(x,shift_amt,0);
1971
  if (a.h!=y.h || a.l!=y.l) exc|=V_BIT;
1972
  goto store_x;
1973
case SLU: case SLUI: x=shift_left(y,shift_amt);@+goto store_x;
1974
case SR: case SRI: case SRU: case SRUI:
1975
  x=shift_right(y,shift_amt,op&0x2);@+goto store_x;
1976
case MUX: case MUXI:
1977
 x.h=(y.h&b.h)|(z.h&~b.h);@+ x.l=(y.l&b.l)|(z.l&~b.l);
1978
 goto store_x;
1979
case SADD: case SADDI:
1980
 x.l=count_bits(y.h&~z.h)+count_bits(y.l&~z.l);@+goto store_x;
1981
case MOR: case MORI:
1982
 x=bool_mult(y,z,false);@+goto store_x;
1983
case MXOR: case MXORI:
1984
 x=bool_mult(y,z,true);@+goto store_x;
1985
case BDIF: case BDIFI:
1986
 x.h=byte_diff(y.h,z.h);@+x.l=byte_diff(y.l,z.l);@+goto store_x;
1987
case WDIF: case WDIFI:
1988
 x.h=wyde_diff(y.h,z.h);@+x.l=wyde_diff(y.l,z.l);@+goto store_x;
1989
case TDIF: case TDIFI:@+
1990
 if (y.h>z.h) x.h=y.h-z.h;
1991
tdif_l:@+ if (y.l>z.l) x.l=y.l-z.l;@+ goto store_x;
1992
case ODIF: case ODIFI:@+if (y.h>z.h) x=ominus(y,z);
1993
 else if (y.h==z.h) goto tdif_l;
1994
 goto store_x;
1995
 
1996
@ When an operation has two outputs, the primary output is placed in~|x|
1997
and the auxiliary output is placed in~|a|.
1998
 
1999
@=
2000
case MUL: case MULI: x=signed_omult(y,z);
2001
test_overflow:@+if (overflow) exc|=V_BIT;
2002
 goto store_x;
2003
case MULU: case MULUI: x=omult(y,z);@+a=g[rH]=aux;@+goto store_x;
2004
case DIV: case DIVI:@+if (!z.l && !z.h) aux=y, exc|=D_BIT, overflow=false;
2005
 else x=signed_odiv(y,z);
2006
 a=g[rR]=aux;@+goto test_overflow;
2007
case DIVU: case DIVUI: x=odiv(b,y,z);@+a=g[rR]=aux;@+goto store_x;
2008
 
2009
@ The floating point routines of {\mc MMIX-ARITH} record exceptional
2010
events in a variable called |exceptions|. Here we simply merge those bits into
2011
the |exc| variable. The |U_BIT| is not exactly the
2012
same as ``underflow,'' but the true definition of underflow will be applied
2013
when |exc| is combined with~rA.
2014
 
2015
@=
2016
case FADD: x=fplus(y,z);
2017
 fin_float: round_mode=cur_round;
2018
 store_fx: exc|=exceptions;@+ goto store_x;
2019
case FSUB: a=z;@+if (fcomp(a,zero_octa)!=2) a.h^=sign_bit;
2020
 x=fplus(y,a);@+goto fin_float;
2021
case FMUL: x=fmult(y,z);@+goto fin_float;
2022
case FDIV: x=fdivide(y,z);@+goto fin_float;
2023
case FREM: x=fremstep(y,z,2500);@+goto fin_float;
2024
case FSQRT: x=froot(z,y.l);
2025
 fin_unifloat:@+if (y.h || y.l>4) goto illegal_inst;
2026
 round_mode=(y.l? y.l: cur_round);@+goto store_fx;
2027
case FINT: x=fintegerize(z,y.l);@+goto fin_unifloat;
2028
case FIX: x=fixit(z,y.l);@+goto fin_unifloat;
2029
case FIXU: x=fixit(z,y.l);@+exceptions&=~W_BIT;@+goto fin_unifloat;
2030
case FLOT: case FLOTI: case FLOTU: case FLOTUI:
2031
case SFLOT: case SFLOTI: case SFLOTU: case SFLOTUI:
2032
 x=floatit(z,y.l,op&0x2,op&0x4);@+goto fin_unifloat;
2033
 
2034
@ We have now done all of the arithmetic operations except for the
2035
cases that compare two registers and yield a value of $-1$~or~0~or~1.
2036
 
2037
@d cmp_zero store_x /* |x| is 0 by default */
2038
 
2039
@=
2040
case CMP: case CMPI:@+if ((y.h&sign_bit)>(z.h&sign_bit)) goto cmp_neg;
2041
 if ((y.h&sign_bit)<(z.h&sign_bit)) goto cmp_pos;
2042
case CMPU: case CMPUI:@+if (y.h
2043
 if (y.h>z.h) goto cmp_pos;
2044
 if (y.l
2045
 if (y.l==z.l) goto cmp_zero;
2046
cmp_pos: x.l=1;@+goto store_x;
2047
cmp_neg: x=neg_one;@+goto store_x;
2048
case FCMPE: k=fepscomp(y,z,b,true);
2049
 if (k) goto cmp_zero_or_invalid;
2050
case FCMP: k=fcomp(y,z);
2051
 if (k<0) goto cmp_neg;
2052
cmp_fin:@+ if (k==1) goto cmp_pos;
2053
cmp_zero_or_invalid:@+ if (k==2) exc|=I_BIT;
2054
 goto cmp_zero;
2055
case FUN:@+ if (fcomp(y,z)==2) goto cmp_pos;@+else goto cmp_zero;
2056
case FEQL:@+ if (fcomp(y,z)==0) goto cmp_pos;@+else goto cmp_zero;
2057
case FEQLE: k=fepscomp(y,z,b,false);
2058
  goto cmp_fin;
2059
case FUNE:@+if (fepscomp(y,z,b,true)==2) goto cmp_pos;@+else goto cmp_zero;
2060
 
2061
@ We have now done all the register-register operations except for
2062
the conditional commands. Conditional commands and branch commands
2063
all make use of a simple subroutine that determines whether a given
2064
octabyte satisfies the condition of a given opcode.
2065
 
2066
@=
2067
int register_truth @,@,@[ARGS((octa,mmix_opcode))@];@+@t}\6{@>
2068
int register_truth(o,op)
2069
  octa o;
2070
  mmix_opcode op;
2071
{@+register int b;
2072
  switch ((op>>1) & 0x3) {
2073
 case 0: b=o.h>>31;@+break; /* negative? */
2074
 case 1: b=(o.h==0 && o.l==0);@+break; /* zero? */
2075
 case 2: b=(o.h
2076
 case 3: b=o.l&0x1;@+break; /* odd? */
2077
}
2078
  if (op&0x8) return b^1;
2079
  else return b;
2080
}
2081
 
2082
@ The |b| operand will be zero on the \.{ZS} operations; it will be
2083
the contents of register~X on the \.{CS} operations.
2084
 
2085
@=
2086
case CSN: case CSNI: case CSZ: case CSZI:@/
2087
case CSP: case CSPI: case CSOD: case CSODI:@/
2088
case CSNN: case CSNNI: case CSNZ: case CSNZI:@/
2089
case CSNP: case CSNPI: case CSEV: case CSEVI:@/
2090
case ZSN: case ZSNI: case ZSZ: case ZSZI:@/
2091
case ZSP: case ZSPI: case ZSOD: case ZSODI:@/
2092
case ZSNN: case ZSNNI: case ZSNZ: case ZSNZI:@/
2093
case ZSNP: case ZSNPI: case ZSEV: case ZSEVI:@/
2094
 x=register_truth(y,op)? z: b;@+goto store_x;
2095
 
2096
@ Didn't that feel good, when 32 opcodes reduced to a single case?
2097
We get to do it one more time. Happiness!
2098
 
2099
@=
2100
case BN: case BNB: case BZ: case BZB:@/
2101
case BP: case BPB: case BOD: case BODB:@/
2102
case BNN: case BNNB: case BNZ: case BNZB:@/
2103
case BNP: case BNPB: case BEV: case BEVB:@/
2104
case PBN: case PBNB: case PBZ: case PBZB:@/
2105
case PBP: case PBPB: case PBOD: case PBODB:@/
2106
case PBNN: case PBNNB: case PBNZ: case PBNZB:@/
2107
case PBNP: case PBNPB: case PBEV: case PBEVB:@/
2108
 x.l=register_truth(b,op);
2109
 if (x.l) {
2110
   inst_ptr=z;
2111
   good=(op>=PBN);
2112
 }@+else good=(op
2113
 if (good) good_guesses++;
2114
 else bad_guesses++, g[rC].l+=2; /* penalty is $2\upsilon$ for bad guess */
2115
 break;
2116
 
2117
@ Memory operations are next on our agenda. The memory address,
2118
|y+z|, has already been placed in~|w|.
2119
 
2120
@=
2121
case LDB: case LDBI: case LDBU: case LDBUI:@/
2122
 i=56;@+j=(w.l&0x3)<<3; goto fin_ld;
2123
case LDW: case LDWI: case LDWU: case LDWUI:@/
2124
 i=48;@+j=(w.l&0x2)<<3; goto fin_ld;
2125
case LDT: case LDTI: case LDTU: case LDTUI:@/
2126
 i=32;@+j=0;@+ goto fin_ld;
2127
case LDHT: case LDHTI: i=j=0;
2128
fin_ld: ll=mem_find(w);@+test_load_bkpt(ll);
2129
 x.h=ll->tet;
2130
 x=shift_right(shift_left(x,j),i,op&0x2);
2131
check_ld:@+if (w.h&sign_bit) goto privileged_inst;
2132
 goto store_x;
2133
case LDO: case LDOI: case LDOU: case LDOUI: case LDUNC: case LDUNCI:
2134
 w.l&=-8;@+ ll=mem_find(w);
2135
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
2136
 x.h=ll->tet;@+ x.l=(ll+1)->tet;
2137
 goto check_ld;
2138
case LDSF: case LDSFI: ll=mem_find(w);@+test_load_bkpt(ll);
2139
 x=load_sf(ll->tet);@+ goto check_ld;
2140
 
2141
@ @=
2142
case STB: case STBI: case STBU: case STBUI:@/
2143
 i=56;@+j=(w.l&0x3)<<3; goto fin_pst;
2144
case STW: case STWI: case STWU: case STWUI:@/
2145
 i=48;@+j=(w.l&0x2)<<3; goto fin_pst;
2146
case STT: case STTI: case STTU: case STTUI:@/
2147
 i=32;@+j=0;
2148
fin_pst: ll=mem_find(w);
2149
 if ((op&0x2)==0) {
2150
   a=shift_right(shift_left(b,i),i,0);
2151
   if (a.h!=b.h || a.l!=b.l) exc|=V_BIT;
2152
 }
2153
 ll->tet^=(ll->tet^(b.l<<(i-32-j))) & ((((tetra)-1)<<(i-32))>>j);
2154
 goto fin_st;
2155
case STSF: case STSFI: ll=mem_find(w);
2156
 ll->tet=store_sf(b);@+exc=exceptions;
2157
 goto fin_st;
2158
case STHT: case STHTI: ll=mem_find(w);@+ ll->tet=b.h;
2159
fin_st: test_store_bkpt(ll);
2160
 w.l&=-8;@+ll=mem_find(w);
2161
 a.h=ll->tet;@+ a.l=(ll+1)->tet; /* for trace output */
2162
 goto check_st;
2163
case STCO: case STCOI: b.l=xx;
2164
case STO: case STOI: case STOU: case STOUI: case STUNC: case STUNCI:
2165
 w.l&=-8;@+ll=mem_find(w);
2166
 test_store_bkpt(ll);@+ test_store_bkpt(ll+1);
2167
 ll->tet=b.h;@+ (ll+1)->tet=b.l;
2168
check_st:@+if (w.h&sign_bit) goto privileged_inst;
2169
 break;
2170
 
2171
@ The |CSWAP| operation has elements of both loading and storing.
2172
We shuffle some of
2173
the operands around so that they will appear correctly in the trace output.
2174
 
2175
@=
2176
case CSWAP: case CSWAPI: w.l&=-8;@+ll=mem_find(w);
2177
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
2178
 a=g[rP];
2179
 if (ll->tet==a.h && (ll+1)->tet==a.l) {
2180
   x.h=0, x.l=1;
2181
   test_store_bkpt(ll);@+test_store_bkpt(ll+1);
2182
   ll->tet=b.h, (ll+1)->tet=b.l;
2183
   strcpy(rhs,"M8[%#w]=%#b");
2184
 }@+else {
2185
   b.h=ll->tet, b.l=(ll+1)->tet;
2186
   g[rP]=b;
2187
   strcpy(rhs,"rP=%#b");
2188
 }
2189
 goto check_ld;
2190
 
2191
@ The |GET| command is permissive, but |PUT| is restrictive.
2192
 
2193
@=
2194
case GET:@+if (yy!=0 || zz>=32) goto illegal_inst;
2195
  x=g[zz];
2196
  goto store_x;
2197
case PUT: case PUTI:@+ if (yy!=0 || xx>=32) goto illegal_inst;
2198
  strcpy(rhs,"%z = %#z");
2199
  if (xx>=8) {
2200
    if (xx<=11) goto illegal_inst; /* can't change rC, rN, rO, rS */
2201
    if (xx<=18) goto privileged_inst;
2202
    if (xx==rA) @@;
2203
    else if (xx==rL) @@;
2204
    else if (xx==rG) @;
2205
  }
2206
  g[xx]=z;@+zz=xx;@+break;
2207
 
2208
@ @=
2209
{
2210
  x=z;@+ strcpy(rhs,z.h? "min(rL,%#x) = %z": "min(rL,%x) = %z");
2211
  if (z.l>L || z.h) z.h=0, z.l=L;
2212
  else old_L=L=z.l;
2213
}
2214
 
2215
@ @=
2216
{
2217
  if (z.h!=0 || z.l>255 || z.l
2218
  for (j=z.l; j
2219
  G=z.l;
2220
}
2221
 
2222
@ @d ROUND_OFF 1
2223
@d ROUND_UP 2
2224
@d ROUND_DOWN 3
2225
@d ROUND_NEAR 4
2226
 
2227
@=
2228
{
2229
  if (z.h!=0 || z.l>=0x40000) goto illegal_inst;
2230
  cur_round=(z.l>=0x10000? z.l>>16: ROUND_NEAR);
2231
}
2232
 
2233
@ Pushing and popping are rather delicate, because we want to trace
2234
them coherently.
2235
 
2236
@=
2237
case PUSHGO: case PUSHGOI: inst_ptr=w;@+goto push;
2238
case PUSHJ: case PUSHJB: inst_ptr=z;
2239
push:@+if (xx>=G) {
2240
   xx=L++;
2241
   if (((S-O-L)&lring_mask)==0) stack_store();
2242
 }
2243
 x.l=xx;@+l[(O+xx)&lring_mask]=x; /* the ``hole'' records the amount pushed */
2244
 sprintf(lhs,"l[%d]=%d, ",(O+xx)&lring_mask,xx);
2245
 x=g[rJ]=incr(loc,4);
2246
 L-=xx+1;@+ O+=xx+1;
2247
 b=g[rO]=incr(g[rO],(xx+1)<<3);
2248
sync_L: a.l=g[rL].l=L;@+break;
2249
case POP:@+if (xx!=0 && xx<=L) y=l[(O+xx-1)&lring_mask];
2250
 if (g[rS].l==g[rO].l) stack_load();
2251
 k=l[(O-1)&lring_mask].l&0xff;
2252
 while ((tetra)(O-S)<=(tetra)k) stack_load();
2253
 L=k+(xx<=L? xx: L+1);
2254
 if (L>G) L=G;
2255
 if (L>k) {
2256
   l[(O-1)&lring_mask]=y;
2257
   if (y.h) sprintf(lhs,"l[%d]=#%x%08x, ",(O-1)&lring_mask,y.h,y.l);
2258
   else sprintf(lhs,"l[%d]=#%x, ",(O-1)&lring_mask,y.l);
2259
 }@+else lhs[0]='\0';
2260
 y=g[rJ];@+ z.l=yz<<2;@+ inst_ptr=oplus(y,z);
2261
 O-=k+1;@+ b=g[rO]=incr(g[rO],-((k+1)<<3));
2262
 goto sync_L;
2263
 
2264
@ To complete our simulation of \MMIX's register stack, we need
2265
to implement |SAVE| and |UNSAVE|.
2266
 
2267
@=
2268
case SAVE:@+if (xx
2269
 l[(O+L)&lring_mask].l=L, L++;
2270
 if (((S-O-L)&lring_mask)==0) stack_store();
2271
 O+=L;@+ g[rO]=incr(g[rO],L<<3);
2272
 L=g[rL].l=0;
2273
 while (g[rO].l!=g[rS].l) stack_store();
2274
 for (k=G;;) {
2275
   @;
2276
   if (k==255) k=rB;
2277
   else if (k==rR) k=rP;
2278
   else if (k==rZ+1) break;
2279
   else k++;
2280
 }
2281
 O=S, g[rO]=g[rS];
2282
 x=incr(g[rO],-8);@+goto store_x;
2283
 
2284
@ This part of the program naturally has a lot in common with the
2285
|stack_store| subroutine. (There's a little white lie in the
2286
section name; if |k|~is |rZ+1|, we store rG and~rA, not |g[k]|.)
2287
 
2288
@=
2289
ll=mem_find(g[rS]);
2290
if (k==rZ+1) x.h=G<<24, x.l=g[rA].l;
2291
else x=g[k];
2292
ll->tet=x.h;@+test_store_bkpt(ll);
2293
(ll+1)->tet=x.l;@+test_store_bkpt(ll+1);
2294
if (stack_tracing) {
2295
  tracing=true;
2296
  if (cur_line) show_line();
2297
  if (k>=32) printf("             M8[#%08x%08x]=g[%d]=#%08x%08x, rS+=8\n",
2298
            g[rS].h,g[rS].l,k,x.h,x.l);
2299
  else printf("             M8[#%08x%08x]=%s=#%08x%08x, rS+=8\n",
2300
            g[rS].h,g[rS].l,k==rZ+1? "(rG,rA)": special_name[k],x.h,x.l);
2301
}
2302
S++, g[rS]=incr(g[rS],8);
2303
 
2304
@ @=
2305
case UNSAVE:@+if (xx!=0 || yy!=0) goto illegal_inst;
2306
 z.l&=-8;@+g[rS]=incr(z,8);
2307
 for (k=rZ+1;;) {
2308
   @;
2309
   if (k==rP) k=rR;
2310
   else if (k==rB) k=255;
2311
   else if (k==G) break;
2312
   else k--;
2313
 }
2314
 S=g[rS].l>>3;
2315
 stack_load();
2316
 k=l[S&lring_mask].l&0xff;
2317
 for (j=0;j
2318
 O=S;@+ g[rO]=g[rS];
2319
 L=k>G? G: k;
2320
 g[rL].l=L;@+a=g[rL];
2321
 g[rG].l=G;@+break;
2322
 
2323
@ @=
2324
g[rS]=incr(g[rS],-8);
2325
ll=mem_find(g[rS]);
2326
test_load_bkpt(ll);@+test_load_bkpt(ll+1);
2327
if (k==rZ+1) x.l=G=g[rG].l=ll->tet>>24, a.l=g[rA].l=(ll+1)->tet&0x3ffff;
2328
else g[k].h=ll->tet, g[k].l=(ll+1)->tet;
2329
if (stack_tracing) {
2330
  tracing=true;
2331
  if (cur_line) show_line();
2332
  if (k>=32) printf("             rS-=8, g[%d]=M8[#%08x%08x]=#%08x%08x\n",
2333
            k,g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
2334
  else if (k==rZ+1) printf("             (rG,rA)=M8[#%08x%08x]=#%08x%08x\n",
2335
            g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
2336
  else printf("             rS-=8, %s=M8[#%08x%08x]=#%08x%08x\n",
2337
            special_name[k],g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
2338
}
2339
 
2340
@ The cache maintenance instructions don't affect this simulation,
2341
because there are no caches. But if the user has invoked them, we do
2342
provide a bit of information when tracing, indicating the scope of the
2343
instruction.
2344
 
2345
@=
2346
case SYNCID: case SYNCIDI: case PREST: case PRESTI:
2347
case SYNCD: case SYNCDI: case PREGO: case PREGOI:
2348
case PRELD: case PRELDI: x=incr(w,xx);@+break;
2349
 
2350
@ Several loose ends remain to be nailed down.
2351
% (Incidentally, a ``loose end'' should never be confused with ``Lucent.'')
2352
 
2353
@=
2354
case GO: case GOI: x=inst_ptr;@+inst_ptr=w;@+goto store_x;
2355
case JMP: case JMPB: inst_ptr=z;
2356
case SWYM: break;
2357
case SYNC:@+if (xx!=0 || yy!=0 || zz>7) goto illegal_inst;
2358
 if (zz<=3) break;
2359
case LDVTS: case LDVTSI: privileged_inst: strcpy(lhs,"!privileged");
2360
 goto break_inst;
2361
illegal_inst: strcpy(lhs,"!illegal");
2362
break_inst: breakpoint=tracing=true;
2363
 if (!interacting && !interact_after_break) halted=true;
2364
 break;
2365
 
2366
@* Trips and traps. We have now implemented 253 of the 256 instructions: all
2367
but \.{TRIP}, \.{TRAP}, and \.{RESUME}.
2368
 
2369
The |TRIP| instruction simply turns |H_BIT| on in the |exc| variable;
2370
this will trigger an interruption to location~0.
2371
@^interrupts@>
2372
 
2373
The |TRAP| instruction is not simulated, except for the system calls
2374
mentioned in the introduction.
2375
 
2376
@=
2377
case TRIP: exc|=H_BIT;@+break;
2378
case TRAP:@+if (xx!=0 || yy>max_sys_call) goto privileged_inst;
2379
 strcpy(rhs,trap_format[yy]);
2380
 g[rWW]=inst_ptr;
2381
 g[rXX].h=sign_bit, g[rXX].l=inst;
2382
 g[rYY]=y, g[rZZ]=z;
2383
 z.h=0, z.l=zz;
2384
 a=incr(b,8);
2385
 @;
2386
 switch (yy) {
2387
case Halt: @;@+g[rBB]=g[255];@+break;
2388
case Fopen: g[rBB]=mmix_fopen((unsigned char)zz,mb,ma);@+break;
2389
case Fclose: g[rBB]=mmix_fclose((unsigned char)zz);@+break;
2390
case Fread: g[rBB]=mmix_fread((unsigned char)zz,mb,ma);@+break;
2391
case Fgets: g[rBB]=mmix_fgets((unsigned char)zz,mb,ma);@+break;
2392
case Fgetws: g[rBB]=mmix_fgetws((unsigned char)zz,mb,ma);@+break;
2393
case Fwrite: g[rBB]=mmix_fwrite((unsigned char)zz,mb,ma);@+break;
2394
case Fputs: g[rBB]=mmix_fputs((unsigned char)zz,b);@+break;
2395
case Fputws: g[rBB]=mmix_fputws((unsigned char)zz,b);@+break;
2396
case Fseek: g[rBB]=mmix_fseek((unsigned char)zz,b);@+break;
2397
case Ftell: g[rBB]=mmix_ftell((unsigned char)zz);@+break;
2398
}
2399
 x=g[255]=g[rBB];@+break;
2400
 
2401
@ @=
2402
if (!zz) halted=breakpoint=true;
2403
else if (zz==1) {
2404
  if (loc.h || loc.l>=0x90) goto privileged_inst;
2405
  print_trip_warning(loc.l>>4,incr(g[rW],-4));
2406
}@+else goto privileged_inst;
2407
 
2408
@ @=
2409
char arg_count[]={1,3,1,3,3,3,3,2,2,2,1};
2410
char *trap_format[]={
2411
"Halt(%z)",
2412
"$255 = Fopen(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
2413
"$255 = Fclose(%!z) = %x",
2414
"$255 = Fread(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
2415
"$255 = Fgets(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
2416
"$255 = Fgetws(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
2417
"$255 = Fwrite(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
2418
"$255 = Fputs(%!z,%#b) = %x",
2419
"$255 = Fputws(%!z,%#b) = %x",
2420
"$255 = Fseek(%!z,%b) = %x",
2421
"$255 = Ftell(%!z) = %x"};
2422
 
2423
@ @=
2424
if (arg_count[yy]==3) {
2425
  ll=mem_find(b);@+test_load_bkpt(ll);@+test_load_bkpt(ll+1);
2426
  mb.h=ll->tet, mb.l=(ll+1)->tet;
2427
  ll=mem_find(a);@+test_load_bkpt(ll);@+test_load_bkpt(ll+1);
2428
  ma.h=ll->tet, ma.l=(ll+1)->tet;
2429
}
2430
 
2431
@ The input/output operations invoked by \.{TRAP}s are
2432
done by subroutines in an auxiliary program module called {\mc MMIX-IO}.
2433
Here we need only declare those subroutines, and write three primitive
2434
interfaces on which they depend.
2435
 
2436
@ @=
2437
extern void mmix_io_init @,@,@[ARGS((void))@];
2438
extern octa mmix_fopen @,@,@[ARGS((unsigned char,octa,octa))@];
2439
extern octa mmix_fclose @,@,@[ARGS((unsigned char))@];
2440
extern octa mmix_fread @,@,@[ARGS((unsigned char,octa,octa))@];
2441
extern octa mmix_fgets @,@,@[ARGS((unsigned char,octa,octa))@];
2442
extern octa mmix_fgetws @,@,@[ARGS((unsigned char,octa,octa))@];
2443
extern octa mmix_fwrite @,@,@[ARGS((unsigned char,octa,octa))@];
2444
extern octa mmix_fputs @,@,@[ARGS((unsigned char,octa))@];
2445
extern octa mmix_fputws @,@,@[ARGS((unsigned char,octa))@];
2446
extern octa mmix_fseek @,@,@[ARGS((unsigned char,octa))@];
2447
extern octa mmix_ftell @,@,@[ARGS((unsigned char))@];
2448
extern void print_trip_warning @,@,@[ARGS((int,octa))@];
2449
extern void mmix_fake_stdin @,@,@[ARGS((FILE*))@];
2450
 
2451
@ The subroutine |mmgetchars(buf,size,addr,stop)| reads characters
2452
starting at address |addr| in the simulated memory and stores them
2453
in |buf|, continuing until |size| characters have been read or
2454
some other stopping criterion has been met. If |stop<0| there is
2455
no other criterion; if |stop=0| a null character will also terminate
2456
the process; otherwise |addr| is even, and two consecutive null bytes
2457
starting at an even address will terminate the process. The number
2458
of bytes read and stored, exclusive of terminating nulls, is returned.
2459
 
2460
@=
2461
int mmgetchars @,@,@[ARGS((char*,int,octa,int))@];@+@t}\6{@>
2462
int mmgetchars(buf,size,addr,stop)
2463
  char *buf;
2464
  int size;
2465
  octa addr;
2466
  int stop;
2467
{
2468
  register char *p;
2469
  register int m;
2470
  register mem_tetra *ll;
2471
  register tetra x;
2472
  octa a;
2473
  for (p=buf,m=0,a=addr; m
2474
    ll=mem_find(a);@+test_load_bkpt(ll);
2475
    x=ll->tet;
2476
    if ((a.l&0x3) || m>size-4) @@;
2477
    else @@;
2478
  }
2479
  return size;
2480
}
2481
 
2482
@ @=
2483
{
2484
  *p=(x>>(8*((~a.l)&0x3)))&0xff;
2485
  if (!*p && stop>=0) {
2486
    if (stop==0) return m;
2487
    if ((a.l&0x1) && *(p-1)=='\0') return m-1;
2488
  }
2489
  p++,m++,a=incr(a,1);
2490
}
2491
 
2492
@ @=
2493
{
2494
  *p=x>>24;
2495
  if (!*p && (stop==0 || (stop>0 && x<0x10000))) return m;
2496
  *(p+1)=(x>>16)&0xff;
2497
  if (!*(p+1) && stop==0) return m+1;
2498
  *(p+2)=(x>>8)&0xff;
2499
  if (!*(p+2) && (stop==0 || (stop>0 && (x&0xffff)==0))) return m+2;
2500
  *(p+3)=x&0xff;
2501
  if (!*(p+3) && stop==0) return m+3;
2502
  p+=4,m+=4,a=incr(a,4);
2503
}
2504
 
2505
@ The subroutine |mmputchars(buf,size,addr)| puts |size| characters
2506
into the simulated memory starting at address |addr|.
2507
 
2508
@=
2509
void mmputchars @,@,@[ARGS((unsigned char*,int,octa))@];@+@t}\6{@>
2510
void mmputchars(buf,size,addr)
2511
  unsigned char *buf;
2512
  int size;
2513
  octa addr;
2514
{
2515
  register unsigned char *p;
2516
  register int m;
2517
  register mem_tetra *ll;
2518
  octa a;
2519
  for (p=buf,m=0,a=addr; m
2520
    ll=mem_find(a);@+test_store_bkpt(ll);
2521
    if ((a.l&0x3) || m>size-4) @@;
2522
    else @;
2523
  }
2524
}
2525
 
2526
@ @=
2527
{
2528
  register int s=8*((~a.l)&0x3);
2529
  ll->tet^=(((ll->tet>>s)^*p)&0xff)<
2530
  p++,m++,a=incr(a,1);
2531
}
2532
 
2533
@ @=
2534
{
2535
  ll->tet=(*p<<24)+(*(p+1)<<16)+(*(p+2)<<8)+*(p+3);
2536
  p+=4,m+=4,a=incr(a,4);
2537
}
2538
 
2539
@ When standard input is being read by the simulated program at the same time
2540
as it is being used for interaction, we try to keep the two uses separate
2541
by maintaining a private buffer for the simulated program's \.{StdIn}.
2542
Online input is usually transmitted from the keyboard to a \CEE/ program
2543
a line at a time; therefore an
2544
|fgets| operation works much better than |fread| when we prompt
2545
for new input. But there is a slight complication, because |fgets|
2546
might read a null character before coming to a newline character.
2547
We cannot deduce the number of characters read by |fgets| simply
2548
by looking at |strlen(stdin_buf)|.
2549
 
2550
@=
2551
char stdin_chr @,@,@[ARGS((void))@];@+@t}\6{@>
2552
char stdin_chr()
2553
{
2554
  register char* p;
2555
  while (stdin_buf_start==stdin_buf_end) {
2556
    if (interacting) {
2557
      printf("StdIn> ");@+fflush(stdout);
2558
@.StdIn>@>
2559
    }
2560
    if (!fgets(stdin_buf,256,stdin))
2561
      panic("End of file on standard input; use the -f option, not <");
2562
    stdin_buf_start=stdin_buf;
2563
    for (p=stdin_buf;p
2564
    stdin_buf_end=p+1;
2565
  }
2566
  return *stdin_buf_start++;
2567
}
2568
 
2569
@ @=
2570
char stdin_buf[256]; /* standard input to the simulated program */
2571
char *stdin_buf_start; /* current position in that buffer */
2572
char *stdin_buf_end; /* current end of that buffer */
2573
 
2574
@ Just after executing each instruction, we do the following.
2575
Underflow that is exact and not enabled is ignored. (This applies
2576
also to underflow that was triggered by |RESUME_SET|.)
2577
 
2578
@=
2579
if ((exc&(U_BIT+X_BIT))==U_BIT && !(g[rA].l&U_BIT)) exc &=~U_BIT;
2580
if (exc) {
2581
  if (exc&tracing_exceptions) tracing=true;
2582
  j=exc&(g[rA].l|H_BIT); /* find all exceptions that have been enabled */
2583
  if (j) @;
2584
  g[rA].l |= exc>>8;
2585
}
2586
 
2587
@ @=
2588
{
2589
  tripping=true;
2590
  for (k=0; !(j&H_BIT); j<<=1, k++) ;
2591
  exc&=~(H_BIT>>k); /* trips taken are not logged as events */
2592
  g[rW]=inst_ptr;
2593
  inst_ptr.h=0, inst_ptr.l=k<<4;
2594
  g[rX].h=sign_bit, g[rX].l=inst;
2595
  if ((op&0xe0)==STB) g[rY]=w, g[rZ]=b;
2596
  else g[rY]=y, g[rZ]=z;
2597
  g[rB]=g[255];
2598
  g[255]=g[rJ];
2599
  if (op==TRIP) w=g[rW], x=g[rX], a=g[255];
2600
}
2601
 
2602
@ We are finally ready for the last case.
2603
 
2604
@=
2605
case RESUME:@+if (xx || yy || zz) goto illegal_inst;
2606
inst_ptr=z=g[rW];
2607
b=g[rX];
2608
if (!(b.h&sign_bit)) @;
2609
break;
2610
 
2611
@ Here we check to see if the ropcode restrictions hold.
2612
If so, the ropcode will actually be obeyed on the next fetch phase.
2613
 
2614
@d RESUME_AGAIN 0 /* repeat the command in rX as if in location $\rm rW-4$ */
2615
@d RESUME_CONT 1 /* same, but substitute rY and rZ for operands */
2616
@d RESUME_SET 2 /* set r[X] to rZ */
2617
 
2618
@=
2619
{
2620
  rop=b.h>>24; /* the ropcode is the leading byte of rX */
2621
  switch (rop) {
2622
 case RESUME_CONT:@+if ((1<<(b.l>>28))&0x8f30) goto illegal_inst;
2623
 case RESUME_SET: k=(b.l>>16)&0xff;
2624
   if (k>=L && k
2625
 case RESUME_AGAIN:@+if ((b.l>>24)==RESUME) goto illegal_inst;
2626
   break;
2627
 default: goto illegal_inst;
2628
  }
2629
  resuming=true;
2630
}
2631
 
2632
@ @=
2633
if (rop==RESUME_SET) {
2634
    op=ORI;
2635
    y=g[rZ];
2636
    z=zero_octa;
2637
    exc=g[rX].h&0xff00;
2638
    f=X_is_dest_bit;
2639
}@+else { /* |RESUME_CONT| */
2640
  y=g[rY];
2641
  z=g[rZ];
2642
}
2643
 
2644
@ We don't want to count the |UNSAVE| that bootstraps the whole process.
2645
 
2646
@=
2647
if (g[rU].l || g[rU].h || !resuming) {
2648
  g[rC].h+=info[op].mems; /* clock goes up by $2^{32}$ for each $\mu$ */
2649
  g[rC]=incr(g[rC],info[op].oops); /* clock goes up by 1 for each $\upsilon$ */
2650
  g[rU]=incr(g[rU],1); /* usage counter counts total instructions simulated */
2651
  g[rI]=incr(g[rI],-1); /* interval timer counts down by 1 only */
2652
  if (g[rI].l==0 && g[rI].h==0) tracing=breakpoint=true;
2653
}
2654
 
2655
@* Tracing. After an instruction has been executed, we often want
2656
to display its effect. This part of the program prints out a
2657
symbolic interpretation of what has just happened.
2658
 
2659
@=
2660
if (tracing) {
2661
  if (showing_source && cur_line) show_line();
2662
  @;
2663
  @;
2664
  if (showing_stats || breakpoint) show_stats(breakpoint);
2665
  just_traced=true;
2666
}@+else if (just_traced) {
2667
  printf(" ...............................................\n");
2668
  just_traced=false;
2669
  shown_line=-gap-1; /* gap will not be filled */
2670
}
2671
 
2672
@ @=
2673
bool showing_stats; /* should traced instructions also show the statistics? */
2674
bool just_traced; /* was the previous instruction traced? */
2675
 
2676
@ @=
2677
if (resuming && op!=RESUME) {
2678
  switch (rop) {
2679
 case RESUME_AGAIN: printf("           (%08x%08x: %08x (%s)) ",
2680
                     loc.h,loc.l,inst,info[op].name);@+break;
2681
 case RESUME_CONT: printf("           (%08x%08x: %04xrYrZ (%s)) ",
2682
                     loc.h,loc.l,inst>>16,info[op].name);@+break;
2683
 case RESUME_SET: printf("           (%08x%08x: ..%02x..rZ (SET)) ",
2684
                     loc.h,loc.l,(inst>>16)&0xff);@+break;
2685
  }
2686
}@+else {
2687
  ll=mem_find(loc);
2688
  printf("%10d. %08x%08x: %08x (%s) ",ll->freq,loc.h,loc.l,inst,info[op].name);
2689
}
2690
 
2691
@ This part of the simulator was inspired by ideas of E.~H. Satterthwaite,
2692
@^Satterthwaite, Edwin Hallowell, Jr.@>
2693
{\sl Software---Practice and Experience\/ \bf2} (1972), 197--217.
2694
Online debugging tools have improved significantly since Satterthwaite
2695
published his work, but good offline tools are still valuable;
2696
alas, today's algebraic programming languages do not provide tracing
2697
facilities that come anywhere close to the level of quality that Satterthwaite
2698
was able to demonstrate for {\mc ALGOL} in 1970.
2699
 
2700
@=
2701
if (lhs[0]=='!') printf("%s instruction!\n",lhs+1); /* privileged or illegal */
2702
else {
2703
  @;
2704
  if (z.l==0 && (op==ADDUI||op==ORI)) p="%l = %y = %#x"; /* \.{LDA}, \.{SET} */
2705
  else p=info[op].trace_format;
2706
  for (;*p;p++) @;
2707
  if (exc) printf(", rA=#%05x", g[rA].l);
2708
  if (tripping) tripping=false, printf(", -> #%02x", inst_ptr.l);
2709
  printf("\n");
2710
}
2711
 
2712
@ Push, pop, and \.{UNSAVE} instructions display changes to rL and rO
2713
explicitly; otherwise the change is implicit, if |L!=old_L|.
2714
 
2715
@=
2716
if (L!=old_L && !(f&push_pop_bit)) printf("rL=%d, ",L);
2717
 
2718
@ Each \MMIX\ instruction has a {\it trace format\/} string, which defines
2719
its symbolic representation. For example, the string for \.{ADD} is
2720
|"%l = %y + %z = %x"|; if the instruction is, say, \.{ADD}~\.{\$1,\$2,\$3}
2721
with $\$2=5$ and $\$3=8$, and if the stack offset is 100, the trace output
2722
will be |"$1=l[101] = 5 + 8 = 13"|.
2723
 
2724
Percent signs (\.\%) induce special format conventions, as follows:
2725
 
2726
\bull \.{\%a}, \.{\%b}, \.{\%p}, \.{\%q}, \.{\%w}, \.{\%x}, \.{\%y}, and
2727
\.{\%z} stand for the numeric contents of octabytes |a|, |b|, |ma|, |mb|, |w|,
2728
|x|, |y|, and~|z|, respectively; a ``style'' character may follow the
2729
percent sign in this case, as explained below.
2730
 
2731
\bull \.{\%(} and \.{\%)} are brackets that indicate the mode of
2732
floating point rounding. If |round_mode=ROUND_NEAR|, |ROUND_OFF|,
2733
|ROUND_UP|, |ROUND_DOWN|, the corresponding brackets are
2734
\.(~and~\.), \.[~and~\.], \.\^~and~\.\^, \.\_~and~\.\_.
2735
Such brackets are placed around a floating point operator;
2736
for example, floating point addition is denoted
2737
by `\.{[+]}' when the current rounding mode is rounding-off.
2738
 
2739
\bull \.{\%l} stands for the string |lhs|, which usually represents the
2740
``left hand side'' of the
2741
instruction just performed, formatted as a register number and
2742
its equivalent in the ring of local registers (e.g., `\.{\$1=l[101]}') or
2743
as a register number and its equivalent in the array of global registers
2744
(e.g., `\.{\$255=g[255]}'). The \.{POP} instruction
2745
uses |lhs| to indicate how the ``hole'' in the register stack was plugged.
2746
 
2747
\bull \.{\%r} means to switch to string |rhs| and continue formatting
2748
from there. This mechanism allows us to use variable formats for opcodes like
2749
\.{TRAP} that have several variants.
2750
 
2751
\bull \.{\%t} means to print either `\.{Yes, ->loc}' (where \.{loc} is
2752
the location of the next instruction) or `\.{No}', depending on the
2753
value of~|x|.
2754
 
2755
\bull \.{\%g} means to print `\.{ (bad guess)}' if |good| is |false|.
2756
 
2757
\bull \.{\%s} stands for the name of special register |g[zz]|.
2758
 
2759
\bull \.{\%?} stands for omission of
2760
the following operator if |z=0|. For example, the
2761
memory address of \.{LDBI} is described by `\.{\%\#y\%?+}'; this
2762
means to treat the address as simply `\.{\%\#y}' if |z=0|,
2763
otherwise as `\.{\%\#y+\%z}'. This case is used only when
2764
|z| is a relatively small number (|z.h=0|).
2765
 
2766
@=
2767
{
2768
  if (*p!='%') fputc(*p,stdout);
2769
  else {
2770
    style=decimal;
2771
  char_switch:  switch (*++p) {
2772
 @t\4@>@;
2773
   default: printf("BUG!!"); /* can't happen */
2774
    }
2775
  }
2776
}
2777
 
2778
@ Octabytes are printed as decimal numbers unless a
2779
``style'' character intervenes between the percent sign and the
2780
name of the octabyte: `\.\#' denotes hexadecimal notation, prefixed by~\.\#;
2781
`\.0' denotes hexadecimal notation with no prefixed~\.\# and with leading zeros not suppressed;
2782
`\..' denotes floating decimal notation; and
2783
`\.!' means to use the names \.{StdIn}, \.{StdOut}, or \.{StdErr}
2784
if the value is 0, 1, or~2.
2785
@.StdIn@>
2786
@.StdOut@>
2787
@.StdErr@>
2788
 
2789
@=
2790
case '#': style=hex;@+ goto char_switch;
2791
case '0': style=zhex;@+ goto char_switch;
2792
case '.': style=floating;@+ goto char_switch;
2793
case '!': style=handle;@+ goto char_switch;
2794
 
2795
@ @=
2796
typedef enum {@!decimal,@!hex,@!zhex,@!floating,@!handle} fmt_style;
2797
 
2798
@ @=
2799
case 'a': trace_print(a);@+break;
2800
case 'b': trace_print(b);@+break;
2801
case 'p': trace_print(ma);@+break;
2802
case 'q': trace_print(mb);@+break;
2803
case 'w': trace_print(w);@+break;
2804
case 'x': trace_print(x);@+break;
2805
case 'y': trace_print(y);@+break;
2806
case 'z': trace_print(z);@+break;
2807
 
2808
@ @=
2809
fmt_style style;
2810
char *stream_name[]={"StdIn","StdOut","StdErr"};
2811
@.StdIn@>
2812
@.StdOut@>
2813
@.StdErr@>
2814
@#
2815
void trace_print @,@,@[ARGS((octa))@];@+@t}\6{@>
2816
void trace_print(o)
2817
  octa o;
2818
{
2819
  switch (style) {
2820
 case decimal: print_int(o);@+return;
2821
 case hex: fputc('#',stdout);@+print_hex(o);@+return;
2822
 case zhex: printf("%08x%08x",o.h,o.l);@+return;
2823
 case floating: print_float(o);@+return;
2824
 case handle:@+if (o.h==0 && o.l<3) printf(stream_name[o.l]);
2825
    else print_int(o);@+return;
2826
  }
2827
}
2828
 
2829
@ @=
2830
case '(': fputc(left_paren[round_mode],stdout);@+break;
2831
case ')': fputc(right_paren[round_mode],stdout);@+break;
2832
case 't':@+if (x.l) printf(" Yes, -> #"),print_hex(inst_ptr);
2833
   else printf(" No");@+break;
2834
case 'g':@+if (!good) printf(" (bad guess)");@+break;
2835
case 's': printf(special_name[zz]);@+break;
2836
case '?': p++;@+if (z.l) printf("%c%d",*p,z.l);@+break;
2837
case 'l': printf(lhs);@+break;
2838
case 'r': p=switchable_string;@+break;
2839
 
2840
@ @d rhs &switchable_string[1]
2841
 
2842
@=
2843
char left_paren[]={0,'[','^','_','('}; /* denotes the rounding mode */
2844
char right_paren[]={0,']','^','_',')'}; /* denotes the rounding mode */
2845
char switchable_string[48]; /* holds |rhs|; position 0 is ignored */
2846
 /* |switchable_string| must be able to hold any |trap_format| */
2847
char lhs[32];
2848
int good_guesses, bad_guesses; /* branch prediction statistics */
2849
 
2850
@ @=
2851
void show_stats @,@,@[ARGS((bool))@];@+@t}\6{@>
2852
void show_stats(verbose)
2853
  bool verbose;
2854
{
2855
  octa o;
2856
  printf("  %d instruction%s, %d mem%s, %d oop%s; %d good guess%s, %d bad\n",
2857
  g[rU].l,g[rU].l==1? "": "s",@|
2858
  g[rC].h,g[rC].h==1? "": "s",@|
2859
  g[rC].l,g[rC].l==1? "": "s",@|
2860
  good_guesses,good_guesses==1? "": "es",bad_guesses);
2861
  if (!verbose) return;
2862
  o = halted? incr(inst_ptr,-4): inst_ptr;
2863
  printf("  (%s at location #%08x%08x)\n",
2864
     halted? "halted": "now", o.h, o.l);
2865
}
2866
 
2867
@* Running the program. Now we are ready to fit the pieces together into a
2868
working simulator.
2869
 
2870
@c
2871
#include 
2872
#include 
2873
#include 
2874
#include 
2875
#include 
2876
#include "abstime.h"
2877
@@;
2878
@@;
2879
@@;
2880
@@;
2881
@#
2882
int main(argc,argv)
2883
  int argc;
2884
  char *argv[];
2885
{
2886
  @;
2887
  mmix_io_init();
2888
  @;
2889
  @;
2890
  @;
2891
  @;
2892
  while (1) {
2893
    if (interrupt && !breakpoint) breakpoint=interacting=true, interrupt=false;
2894
    else {
2895
      breakpoint=false;
2896
      if (interacting) @;
2897
    }
2898
    if (halted) break;
2899
    do @@;
2900
    while ((!interrupt && !breakpoint) || resuming);
2901
    if (interact_after_break) interacting=true, interact_after_break=false;
2902
  }
2903
 end_simulation:@+if (profiling) @;
2904
  if (interacting || profiling || showing_stats) show_stats(true);
2905
  return g[255].l; /* provide rudimentary feedback for non-interactive runs */
2906
}
2907
 
2908
@ Here we process the command-line options; when we finish, |*cur_arg|
2909
should be the name of the object file to be loaded and simulated.
2910
 
2911
@d mmo_file_name *cur_arg
2912
 
2913
@=
2914
myself=argv[0];
2915
for (cur_arg=argv+1;*cur_arg && (*cur_arg)[0]=='-'; cur_arg++)
2916
  scan_option(*cur_arg+1,true);
2917
if (!*cur_arg) scan_option("?",true); /* exit with usage note */
2918
argc -= cur_arg-argv; /* this is the |argc| of the user program */
2919
 
2920
@ Careful readers of the following subroutine will notice a little white bug:
2921
A tracing specification like
2922
\.{t1000000000} or even \.{t0000000000} or even \.{t!!!!!!!!!!}
2923
is silently converted to \.{t4294967295}.
2924
 
2925
The \.{-b} and \.{-c} options are effective only on the command line, but they
2926
are harmless while interacting.
2927
 
2928
@=
2929
void scan_option @,@,@[ARGS((char*,bool))@];@+@t}\6{@>
2930
void scan_option(arg,usage)
2931
  char *arg; /* command-line argument (without the `\.-') */
2932
  bool usage; /* should we exit with usage note if unrecognized? */
2933
{
2934
  register int k;
2935
  switch (*arg) {
2936
 case 't':@+if (strlen(arg)>10) trace_threshold=0xffffffff;
2937
  else if (sscanf(arg+1,"%d",&trace_threshold)!=1) trace_threshold=0;
2938
  return;
2939
 case 'e':@+if (!*(arg+1)) tracing_exceptions=0xff;
2940
  else if (sscanf(arg+1,"%x",&tracing_exceptions)!=1) tracing_exceptions=0;
2941
  return;
2942
 case 'r': stack_tracing=true;@+return;
2943
 case 's': showing_stats=true;@+return;
2944
 case 'l':@+if (!*(arg+1)) gap=3;
2945
  else if (sscanf(arg+1,"%d",&gap)!=1) gap=0;
2946
  showing_source=true;@+return;
2947
 case 'L':@+if (!*(arg+1)) profile_gap=3;
2948
  else if (sscanf(arg+1,"%d",&profile_gap)!=1) profile_gap=0;
2949
  profile_showing_source=true;
2950
 case 'P': profiling=true;@+return;
2951
 case 'v': trace_threshold=0xffffffff;@+ tracing_exceptions=0xff;
2952
  stack_tracing=true; @+ showing_stats=true;
2953
  gap=10, showing_source=true;
2954
  profile_gap=10, profile_showing_source=true, profiling=true;
2955
  return;
2956
 case 'q': trace_threshold=tracing_exceptions=0;
2957
  stack_tracing=showing_stats=showing_source=false;
2958
  profiling=profile_showing_source=false;
2959
  return;
2960
 case 'i': interacting=true;@+return;
2961
 case 'I': interact_after_break=true;@+return;
2962
 case 'b':@+if (sscanf(arg+1,"%d",&buf_size)!=1) buf_size=0;@+return;
2963
 case 'c':@+if (sscanf(arg+1,"%d",&lring_size)!=1) lring_size=0;@+return;
2964
 case 'f': @;@+return;
2965
 case 'D': @;@+return;
2966
 default:@+if (usage) {
2967
    fprintf(stderr,
2968
        "Usage: %s  progfile command-line-args...\n",myself);
2969
@.Usage: ...@>
2970
    for (k=0;usage_help[k][0];k++) fprintf(stderr,usage_help[k]);
2971
    exit(-1);
2972
  }@+else@+ for (k=0;usage_help[k][1]!='b';k++) printf(usage_help[k]);
2973
  return;
2974
  }
2975
}
2976
 
2977
@ @=
2978
char *myself; /* |argv[0]|, the name of this simulator */
2979
char **cur_arg; /* pointer to current place in the argument vector */
2980
bool interrupt; /* has the user interrupted the simulation recently? */
2981
bool profiling; /* should we print the profile at the end? */
2982
FILE *fake_stdin; /* file substituted for the simulated \.{StdIn} */
2983
FILE *dump_file; /* file used for binary dumps */
2984
char *usage_help[]={@/
2985
" with these options: (=decimal number, =hex number)\n",@|
2986
"-t trace each instruction the first n times\n",@|
2987
"-e trace each instruction with an exception matching x\n",@|
2988
"-r    trace hidden details of the register stack\n",@|
2989
"-l list source lines when tracing, filling gaps <= n\n",@|
2990
"-s    show statistics after each traced instruction\n",@|
2991
"-P    print a profile when simulation ends\n",@|
2992
"-L list source lines with the profile\n",@|
2993
"-v    be verbose: show almost everything\n",@|
2994
"-q    be quiet: show only the simulated standard output\n",@|
2995
"-i    run interactively (prompt for online commands)\n",@|
2996
"-I    interact, but only after the program halts\n",@|
2997
"-b change the buffer size for source lines\n",@|
2998
"-c change the cyclic local register ring size\n",@|
2999
"-f use given file to simulate standard input\n",@|
3000
"-D dump a file for use by other simulators\n",@|
3001
""};
3002
char *interactive_help[]={@/
3003
"The interactive commands are:\n",@|
3004
"  trace one instruction\n",@|
3005
"n         trace one instruction\n",@|
3006
"c         continue until halt or breakpoint\n",@|
3007
"q         quit the simulation\n",@|
3008
"s         show current statistics\n",@|
3009
"l   set and/or show local register in format t\n",@|
3010
"g   set and/or show global register in format t\n",@|
3011
"rA     set and/or show register rA in format t\n",@|
3012
"$   set and/or show dynamic register in format t\n",@|
3013
"M   set and/or show memory octabyte in format t\n",@|
3014
"+   set and/or show n additional octabytes in format t\n",@|
3015
"  is ! (decimal) or . (floating) or # (hex) or \" (string)\n",@|
3016
"     or  (previous ) or = (change value)\n",@|
3017
"@@      go to location x\n",@|
3018
"b[rwx] set or reset breakpoint at location x\n",@|
3019
"t      trace location x\n",@|
3020
"u      untrace location x\n",@|
3021
"T         set current segment to Text_Segment\n",@|
3022
"D         set current segment to Data_Segment\n",@|
3023
"P         set current segment to Pool_Segment\n",@|
3024
"S         set current segment to Stack_Segment\n",@|
3025
"B         show all current breakpoints and tracepoints\n",@|
3026
"i   insert commands from file\n",@|
3027
"-
3028
"-?        show the tracing/listing/profile options  \n",@|
3029
""};
3030
 
3031
@ @=
3032
if (fake_stdin) fclose(fake_stdin);
3033
fake_stdin=fopen(arg+1,"r");
3034
if (!fake_stdin) fprintf(stderr,"Sorry, I can't open file %s!\n",arg+1);
3035
@.Sorry, I can't open...@>
3036
else mmix_fake_stdin(fake_stdin);
3037
 
3038
@ @=
3039
dump_file=fopen(arg+1,"wb");
3040
if (!dump_file) fprintf(stderr,"Sorry, I can't open file %s!\n",arg+1);
3041
@.Sorry, I can't open...@>
3042
 
3043
@ @=
3044
signal(SIGINT,catchint); /* now |catchint| will catch the first interrupt */
3045
 
3046
@ @=
3047
void catchint @,@,@[ARGS((int))@];@+@t}\6{@>
3048
void catchint(n)
3049
  int n;
3050
{
3051
  interrupt=true;
3052
  signal(SIGINT,catchint); /* now |catchint| will catch the next interrupt */
3053
}
3054
 
3055
@ @=
3056
{@+register int repeating;
3057
 interact: @;
3058
  p=command_buf;
3059
  repeating=0;
3060
  switch (*p) {
3061
  case '\n': case 'n': breakpoint=tracing=true; /* trace one inst and break */
3062
  case 'c': goto resume_simulation; /* continue until breakpoint */
3063
  case 'q': goto end_simulation;
3064
  case 's': show_stats(true);@+goto interact;
3065
  case '-': k=strlen(p);@+if (p[k-1]=='\n') p[k-1]='\0';
3066
    scan_option(p+1,false);@+goto interact;
3067
  @t\4@>@;
3068
  @t\4@>@;
3069
  @t\4@>@;
3070
  default: what_say: k=strlen(command_buf);
3071
    if (k<10 && command_buf[k-1]=='\n') command_buf[k-1]='\0';
3072
    else strcpy(command_buf+9,"...");
3073
    printf("Eh? Sorry, I don't understand `%s'. (Type h for help)\n",
3074
         command_buf);
3075
    goto interact;
3076
  case 'h':@+ for (k=0;interactive_help[k][0];k++) printf(interactive_help[k]);
3077
    goto interact;
3078
  }
3079
 check_syntax:@+ if (*p!='\n') {
3080
   if (!*p) incomplete_str: printf("Syntax error: Incomplete command!\n");
3081
   else {
3082
     p[strlen(p)-1]='\0';
3083
     printf("Syntax error; I'm ignoring `%s'!\n",p);
3084
   }
3085
 }
3086
 while (repeating) @;
3087
 goto interact;
3088
resume_simulation:;
3089
}
3090
 
3091
@ @=
3092
{@+register bool ready=false;
3093
 incl_read:@+ while (incl_file && !ready)
3094
    if (!fgets(command_buf,command_buf_size,incl_file)) {
3095
      fclose(incl_file);
3096
      incl_file=NULL;
3097
    }@+else if (command_buf[0]!='\n' && command_buf[0]!='i' &&
3098
              command_buf[0]!='%')
3099
      if (command_buf[0]==' ') printf(command_buf);
3100
      else ready=true;
3101
  while (!ready) {
3102
    printf("mmix> ");@+fflush(stdout);
3103
@.mmix>@>
3104
    if (!fgets(command_buf,command_buf_size,stdin)) command_buf[0]='q';
3105
    if (command_buf[0]!='i') ready=true;
3106
    else {
3107
      command_buf[strlen(command_buf)-1]='\0';
3108
      incl_file=fopen(command_buf+1,"r");
3109
      if (incl_file) goto incl_read;
3110
      if (isspace(command_buf[1])) incl_file=fopen(command_buf+2,"r");
3111
      if (incl_file) goto incl_read;
3112
      printf("Can't open file `%s'!\n",command_buf+1);
3113
    }
3114
  }
3115
}
3116
 
3117
@ @d command_buf_size 1024 /* make it plenty long, for floating point tests */
3118
 
3119
@=
3120
char command_buf[command_buf_size];
3121
FILE *incl_file; /* file of commands included by `\.i' */
3122
char cur_disp_mode='l'; /* |'l'| or |'g'| or |'$'| or |'M'| */
3123
char cur_disp_type='!'; /* |'!'| or |'.'| or |'#'| or |'"'| */
3124
bool cur_disp_set; /* was the last \.{} of the form \.{=}? */
3125
octa cur_disp_addr; /* the |h| half is relevant only in mode |'M'| */
3126
octa cur_seg; /* current segment offset */
3127
char spec_reg_code[]={rA,rB,rC,rD,rE,rF,rG,rH,rI,rJ,rK,rL,rM,
3128
      rN,rO,rP,rQ,rR,rS,rT,rU,rV,rW,rX,rY,rZ};
3129
char spec_regg_code[]={0,rBB,0,0,0,0,0,0,0,0,0,0,0,
3130
    0,0,0,0,0,0,rTT,0,0,rWW,rXX,rYY,rZZ};
3131
 
3132
@ @=
3133
case 'l': case 'g': case '$': cur_disp_mode=*p++;
3134
 for (cur_disp_addr.l=0; isdigit(*p); p++)
3135
   cur_disp_addr.l=10*cur_disp_addr.l + *p - '0';
3136
 goto new_mode;
3137
case 'r': p++;@+ cur_disp_mode='g';
3138
 if (*p<'A' || *p>'Z') goto what_say;
3139
 if (*(p+1)!=*p) cur_disp_addr.l=spec_reg_code[*p-'A'],p++;
3140
 else if (spec_regg_code[*p-'A']) cur_disp_addr.l=spec_regg_code[*p-'A'],p+=2;
3141
 else goto what_say;
3142
 goto new_mode;
3143
case 'M': cur_disp_mode=*p;
3144
 cur_disp_addr=scan_hex(p+1,cur_seg);@+ cur_disp_addr.l&=-8;@+ p=next_char;
3145
new_mode: cur_disp_set=false; /* the `\.=' is remembered only by `\.+' */
3146
 repeating=1;
3147
 goto scan_type;
3148
case '+':@+ if (!isdigit(*(p+1))) repeating=1;
3149
 for (p++; isdigit(*p); p++)
3150
   repeating=10*repeating + *p - '0';
3151
 if (repeating) {
3152
   if (cur_disp_mode=='M') cur_disp_addr=incr(cur_disp_addr,8);
3153
   else cur_disp_addr.l++;
3154
 }
3155
 goto scan_type;
3156
 
3157
@ @=
3158
case '!': case '.': case '#': case '"': cur_disp_set=false;
3159
 repeating=1;
3160
set_type: cur_disp_type=*p++;@+break;
3161
scan_type:@+ if (*p=='!' || *p=='.' || *p=='#' || *p=='"') goto set_type;
3162
 if (*p!='=') break;
3163
 goto scan_eql;
3164
case '=': repeating=1;
3165
scan_eql: cur_disp_set=true;
3166
 val=zero_octa;
3167
 if (*++p=='#') cur_disp_type=*p, val=scan_hex(p+1,zero_octa);
3168
 else if (*p=='"' || *p=='\'') goto scan_string;
3169
 else cur_disp_type=(scan_const(p)>0? '.': '!');
3170
 p=next_char;
3171
 if (*p!=',') break;
3172
 val.h=0;@+ val.l&=0xff;
3173
scan_string: cur_disp_type='"';
3174
 @;@+break;
3175
 
3176
@ @=
3177
octa scan_hex @,@,@[ARGS((char*,octa))@];@+@t}\6{@>
3178
octa scan_hex(s,offset)
3179
  char *s;
3180
  octa offset;
3181
{
3182
  register char *p;
3183
  octa o;
3184
  o=zero_octa;
3185
  for (p=s;isxdigit(*p);p++) {
3186
    o=incr(shift_left(o,4),*p-'0');
3187
    if (*p>='a') o=incr(o,'0'-'a'+10);
3188
    else if (*p>='A') o=incr(o,'0'-'A'+10);
3189
  }
3190
  next_char=p;
3191
  return oplus(o,offset);
3192
}
3193
 
3194
@ @=
3195
while (*p==',') {
3196
  if (*++p=='#') {
3197
    aux=scan_hex(p+1,zero_octa), p=next_char;
3198
    val=incr(shift_left(val,8),aux.l&0xff);
3199
  }@+else if (isdigit(*p)) {
3200
    for (k=*p++ - '0';isdigit(*p);p++) k=(10*k + *p - '0')&0xff;
3201
    val=incr(shift_left(val,8),k);
3202
  }
3203
  else if (*p=='\n') goto incomplete_str;
3204
}
3205
if (*p=='\'' && *(p+2)==*p) *p=*(p+2)='"';
3206
if (*p=='"') {
3207
  for (p++;*p && *p!='\n' && *p!='"'; p++)
3208
    val=incr(shift_left(val,8),*p);
3209
  if (*p && *p++=='"')
3210
    if (*p==',') goto scan_string;
3211
}
3212
 
3213
@ @=
3214
{
3215
  if (cur_disp_set) @;
3216
  @;
3217
  fputc('\n',stdout);
3218
  repeating--;
3219
  if (!repeating) break;
3220
  if (cur_disp_mode=='M') cur_disp_addr=incr(cur_disp_addr,8);
3221
  else cur_disp_addr.l++;
3222
}
3223
 
3224
@ @=
3225
switch (cur_disp_mode) {
3226
 case 'l': l[cur_disp_addr.l&lring_mask]=val;@+break;
3227
 case '$': k=cur_disp_addr.l&0xff;
3228
  if (k=G) g[k]=val;
3229
  break;
3230
 case 'g': k=cur_disp_addr.l&0xff;
3231
  if (k<32) @;
3232
  g[k]=val;@+break;
3233
 case 'M':@+if (!(cur_disp_addr.h&sign_bit)) {
3234
    ll=mem_find(cur_disp_addr);
3235
    ll->tet=val.h;@+ (ll+1)->tet=val.l;
3236
  }@+break;
3237
}
3238
 
3239
@ Here we essentially simulate a |PUT| command, but we simply |break|
3240
if the |PUT| is illegal or privileged.
3241
 
3242
@=
3243
if (k>=9 && k!=rI) {
3244
  if (k<=19) break;
3245
  if (k==rA) {
3246
    if (val.h!=0 || val.l>=0x40000) break;
3247
    cur_round=(val.l>=0x10000? val.l>>16: ROUND_NEAR);
3248
  }@+else if (k==rG) {
3249
    if (val.h!=0 || val.l>255 || val.l
3250
    for (j=val.l; j
3251
    G=val.l;
3252
  }@+else if (k==rL) {
3253
    if (val.h==0 && val.l
3254
    else break;
3255
  }
3256
}
3257
 
3258
@ @=
3259
switch (cur_disp_mode) {
3260
 case 'l': k=cur_disp_addr.l&lring_mask;
3261
  printf("l[%d]=",k);@+ aux=l[k];@+ break;
3262
 case '$': k=cur_disp_addr.l&0xff;
3263
  if (k
3264
  else if (k>=G) printf("$%d=g[%d]=",k,k), aux=g[k];
3265
  else printf("$%d=",k), aux=zero_octa;
3266
  break;
3267
 case 'g': k=cur_disp_addr.l&0xff;
3268
  printf("g[%d]=",k);@+ aux=g[k];@+ break;
3269
 case 'M':@+if (cur_disp_addr.h&sign_bit) aux=zero_octa;
3270
  else {
3271
    ll=mem_find(cur_disp_addr);
3272
    aux.h=ll->tet;@+ aux.l=(ll+1)->tet;
3273
  }
3274
  printf("M8[#");@+ print_hex(cur_disp_addr);@+ printf("]=");@+break;
3275
}
3276
switch (cur_disp_type) {
3277
 case '!': print_int(aux);@+break;
3278
 case '.': print_float(aux);@+break;
3279
 case '#': fputc('#',stdout);@+print_hex(aux);@+break;
3280
 case '"': print_string(aux);@+break;
3281
}
3282
 
3283
@ @=
3284
void print_string @,@,@[ARGS((octa))@];@+@t}\6{@>
3285
void print_string(o)
3286
  octa o;
3287
{
3288
  register int k, state, b;
3289
  for (k=state=0; k<8; k++) {
3290
    b=((k<4? o.h>>(8*(3-k)): o.l>>(8*(7-k))))&0xff;
3291
    if (b==0) {
3292
      if (state) printf("%s,0",state>1? "\"": ""), state=1;
3293
    }@+else if (b>=' ' && b<='~')
3294
        printf("%s%c",state>1? "": state==1? ",\"": "\"",b), state=2;
3295
    else printf("%s#%x",state>1? "\",": state==1? ",": "",b), state=1;
3296
  }
3297
  if (state==0) printf("0");
3298
  else if (state>1) printf("\"");
3299
}
3300
 
3301
@ @=
3302
case '@@': inst_ptr=scan_hex(p+1,cur_seg);@+ p=next_char;
3303
 halted=false;@+break;
3304
case 't': case 'u': k=*p;
3305
 val=scan_hex(p+1,cur_seg);@+ p=next_char;
3306
 if (val.h<0x20000000) {
3307
   ll=mem_find(val);
3308
   if (k=='t') ll->bkpt |= trace_bit;
3309
   else ll->bkpt &=~trace_bit;
3310
 }
3311
 break;
3312
case 'b':@+ for (k=0,p++; !isxdigit(*p); p++)
3313
   if (*p=='r') k|=read_bit;
3314
   else if (*p=='w') k|=write_bit;
3315
   else if (*p=='x') k|=exec_bit;
3316
 val=scan_hex(p,cur_seg);@+ p=next_char;
3317
 if (!(val.h&sign_bit)) {
3318
   ll=mem_find(val);
3319
   ll->bkpt=(ll->bkpt&-8)|k;
3320
 }
3321
 break;
3322
case 'T': cur_seg.h=0;@+goto passit;
3323
case 'D': cur_seg.h=0x20000000;@+goto passit;
3324
case 'P': cur_seg.h=0x40000000;@+goto passit;
3325
case 'S': cur_seg.h=0x60000000;@+goto passit;
3326
case 'B': show_breaks(mem_root);
3327
passit: p++;@+break;
3328
 
3329
@ @=
3330
void show_breaks @,@,@[ARGS((mem_node*))@];@+@t}\6{@>
3331
void show_breaks(p)
3332
  mem_node *p;
3333
{
3334
  register int j;
3335
  octa cur_loc;
3336
  if (p->left) show_breaks(p->left);
3337
  for (j=0;j<512;j++) if (p->dat[j].bkpt) {
3338
    cur_loc=incr(p->loc,4*j);
3339
    printf("  %08x%08x %c%c%c%c\n",cur_loc.h,cur_loc.l,@|
3340
             p->dat[j].bkpt&trace_bit? 't': '-',
3341
             p->dat[j].bkpt&read_bit? 'r': '-',
3342
             p->dat[j].bkpt&write_bit? 'w': '-',
3343
             p->dat[j].bkpt&exec_bit? 'x': '-');
3344
  }
3345
  if (p->right) show_breaks(p->right);
3346
}
3347
 
3348
@ We put pointers to the command-line strings in
3349
M$[\.{Pool\_Segment}+8*(k+1)]_8$ for $0\le k<|argc|$;
3350
the strings themselves are octabyte-aligned, starting at
3351
M$[\.{Pool\_Segment}+8*(|argc|+2)]_8$. The location of the first free
3352
octabyte in the pool segment is placed in M$[\.{Pool\_Segment}]_8$.
3353
@:Pool_Segment}\.{Pool\_Segment@>
3354
@^command line arguments@>
3355
 
3356
@=
3357
x.h=0x40000000, x.l=0x8;
3358
loc=incr(x,8*(argc+1));
3359
for (k=0; k
3360
  ll=mem_find(x);
3361
  ll->tet=loc.h, (ll+1)->tet=loc.l;
3362
  ll=mem_find(loc);
3363
  mmputchars((unsigned char *)*cur_arg,strlen(*cur_arg),loc);
3364
  x.l+=8, loc.l+=8+(strlen(*cur_arg)&-8);
3365
}
3366
x.l=0;@+ll=mem_find(x);@+ll->tet=loc.h, (ll+1)->tet=loc.l;
3367
 
3368
@ @=
3369
x.h=0, x.l=0x90;
3370
ll=mem_find(x);
3371
if (ll->tet) inst_ptr=x;
3372
@^subroutine library initialization@>
3373
@^initialization of a user program@>
3374
resuming=true;
3375
rop=RESUME_AGAIN;
3376
g[rX].l=((tetra)UNSAVE<<24)+255;
3377
if (dump_file) {
3378
  x.l=1;
3379
  dump(mem_root);
3380
  dump_tet(0),dump_tet(0);
3381
  exit(0);
3382
}
3383
 
3384
@ The special option `\.{-D}' can be used to prepare binary files
3385
needed by the \MMIX-in-\MMIX\ simulator of Section 1.4.3\'{}. This option
3386
puts big-endian octa\-bytes into a given file; a location~$l$ is followed
3387
by one or more nonzero octabytes M$_8[l]$, M$_8[l+8]$, M$_8[l+16]$, \dots,
3388
followed by zero. The simulated simulator knows how to load programs
3389
in such a format (see exercise 1.4.3\'{}--20), and so does
3390
the meta-simulator {\mc MMMIX}.
3391
 
3392
@=
3393
void dump @,@,@[ARGS((mem_node*))@];@+@t}\6{@>
3394
void dump_tet @,@,@[ARGS((tetra))@];@+@t}\6{@>
3395
void dump(p)
3396
  mem_node *p;
3397
{
3398
  register int j;
3399
  octa cur_loc;
3400
  if (p->left) dump(p->left);
3401
  for (j=0;j<512;j+=2) if (p->dat[j].tet || p->dat[j+1].tet) {
3402
    cur_loc=incr(p->loc,4*j);
3403
    if (cur_loc.l!=x.l || cur_loc.h!=x.h) {
3404
      if (x.l!=1) dump_tet(0),dump_tet(0);
3405
      dump_tet(cur_loc.h);@+dump_tet(cur_loc.l);@+x=cur_loc;
3406
    }
3407
    dump_tet(p->dat[j].tet);
3408
    dump_tet(p->dat[j+1].tet);
3409
    x=incr(x,8);
3410
  }
3411
  if (p->right) dump(p->right);
3412
}
3413
 
3414
@ @=
3415
void dump_tet(t)
3416
  tetra t;
3417
{
3418
  fputc(t>>24,dump_file);
3419
  fputc((t>>16)&0xff,dump_file);
3420
  fputc((t>>8)&0xff,dump_file);
3421
  fputc(t&0xff,dump_file);
3422
}
3423
 
3424
@* Index.

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.