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.
|