/[gxemul]/trunk/src/machine.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/machine.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 24 - (show annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 27242 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


1 /*
2 * Copyright (C) 2003-2006 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: machine.c,v 1.671 2006/06/16 18:31:24 debug Exp $
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #ifdef SOLARIS
35 /* TODO: is this strings vs string separation really necessary? */
36 #include <strings.h>
37 #else
38 #include <string.h>
39 #endif
40 #include <time.h>
41 #include <unistd.h>
42
43 #include "arcbios.h"
44 #include "bus_isa.h"
45 #include "bus_pci.h"
46 #include "cpu.h"
47 #include "device.h"
48 #include "devices.h"
49 #include "diskimage.h"
50 #include "emul.h"
51 #include "machine.h"
52 #include "machine_interrupts.h"
53 #include "memory.h"
54 #include "misc.h"
55 #include "net.h"
56 #include "symbol.h"
57
58
59 /* See main.c: */
60 extern int quiet_mode;
61 extern int verbose;
62
63
64 /* This is initialized by machine_init(): */
65 struct machine_entry *first_machine_entry = NULL;
66
67
68 /*
69 * machine_new():
70 *
71 * Returns a reasonably initialized struct machine.
72 */
73 struct machine *machine_new(char *name, struct emul *emul)
74 {
75 struct machine *m;
76 m = malloc(sizeof(struct machine));
77 if (m == NULL) {
78 fprintf(stderr, "machine_new(): out of memory\n");
79 exit(1);
80 }
81
82 memset(m, 0, sizeof(struct machine));
83
84 /* Back pointer: */
85 m->emul = emul;
86
87 m->name = strdup(name);
88
89 /* Sane default values: */
90 m->serial_nr = 1;
91 m->machine_type = MACHINE_NONE;
92 m->machine_subtype = MACHINE_NONE;
93 m->arch_pagesize = 4096; /* Should be overriden in
94 emul.c for other pagesizes. */
95 m->dyntrans_alignment_check = 1;
96 m->prom_emulation = 1;
97 m->speed_tricks = 1;
98 m->byte_order_override = NO_BYTE_ORDER_OVERRIDE;
99 m->boot_kernel_filename = "";
100 m->boot_string_argument = NULL;
101 m->automatic_clock_adjustment = 1;
102 m->x11_scaledown = 1;
103 m->x11_scaleup = 1;
104 m->n_gfx_cards = 1;
105 m->dbe_on_nonexistant_memaccess = 1;
106 m->show_symbolic_register_names = 1;
107 symbol_init(&m->symbol_context);
108
109 return m;
110 }
111
112
113 /*
114 * machine_name_to_type():
115 *
116 * Take a type and a subtype as strings, and convert them into numeric
117 * values used internally throughout the code.
118 *
119 * Return value is 1 on success, 0 if there was no match.
120 * Also, any errors/warnings are printed using fatal()/debug().
121 */
122 int machine_name_to_type(char *stype, char *ssubtype,
123 int *type, int *subtype, int *arch)
124 {
125 struct machine_entry *me;
126 int i, j, k, nmatches = 0;
127
128 *type = MACHINE_NONE;
129 *subtype = 0;
130
131 /* Check stype, and optionally ssubtype: */
132 me = first_machine_entry;
133 while (me != NULL) {
134 for (i=0; i<me->n_aliases; i++)
135 if (strcasecmp(me->aliases[i], stype) == 0) {
136 /* Found a type: */
137 *type = me->machine_type;
138 *arch = me->arch;
139
140 if (me->n_subtypes == 0)
141 return 1;
142
143 /* Check for subtype: */
144 for (j=0; j<me->n_subtypes; j++)
145 for (k=0; k<me->subtype[j]->n_aliases;
146 k++)
147 if (strcasecmp(ssubtype,
148 me->subtype[j]->aliases[k]
149 ) == 0) {
150 *subtype = me->subtype[
151 j]->machine_subtype;
152 return 1;
153 }
154
155 fatal("Unknown subtype '%s' for emulation"
156 " '%s'\n", ssubtype, stype);
157 if (!ssubtype[0])
158 fatal("(Maybe you forgot the -e"
159 " command line option?)\n");
160 exit(1);
161 }
162
163 me = me->next;
164 }
165
166 /* Not found? Then just check ssubtype: */
167 me = first_machine_entry;
168 while (me != NULL) {
169 if (me->n_subtypes == 0) {
170 me = me->next;
171 continue;
172 }
173
174 /* Check for subtype: */
175 for (j=0; j<me->n_subtypes; j++)
176 for (k=0; k<me->subtype[j]->n_aliases; k++)
177 if (strcasecmp(ssubtype, me->subtype[j]->
178 aliases[k]) == 0) {
179 *type = me->machine_type;
180 *arch = me->arch;
181 *subtype = me->subtype[j]->
182 machine_subtype;
183 nmatches ++;
184 }
185
186 me = me->next;
187 }
188
189 switch (nmatches) {
190 case 0: fatal("\nSorry, emulation \"%s\"", stype);
191 if (ssubtype != NULL && ssubtype[0] != '\0')
192 fatal(" (subtype \"%s\")", ssubtype);
193 fatal(" is unknown.\n");
194 break;
195 case 1: return 1;
196 default:fatal("\nSorry, multiple matches for \"%s\"", stype);
197 if (ssubtype != NULL && ssubtype[0] != '\0')
198 fatal(" (subtype \"%s\")", ssubtype);
199 fatal(".\n");
200 }
201
202 *type = MACHINE_NONE;
203 *subtype = 0;
204
205 fatal("Use the -H command line option to get a list of "
206 "available types and subtypes.\n\n");
207
208 return 0;
209 }
210
211
212 /*
213 * machine_add_tickfunction():
214 *
215 * Adds a tick function (a function called every now and then, depending on
216 * clock cycle count) to a machine.
217 *
218 * If tickshift is non-zero, a tick will occur every (1 << tickshift) cycles.
219 * This is used for the normal (fast dyntrans) emulation modes.
220 *
221 * If tickshift is zero, then this is a cycle-accurate tick function.
222 * The hz value is used in this case.
223 */
224 void machine_add_tickfunction(struct machine *machine, void (*func)
225 (struct cpu *, void *), void *extra, int tickshift, double hz)
226 {
227 int n = machine->n_tick_entries;
228
229 if (n >= MAX_TICK_FUNCTIONS) {
230 fprintf(stderr, "machine_add_tickfunction(): too "
231 "many tick functions\n");
232 exit(1);
233 }
234
235 if (!machine->cycle_accurate) {
236 /*
237 * The dyntrans subsystem wants to run code in relatively
238 * large chunks without checking for external interrupts,
239 * so we cannot allow too low tickshifts:
240 */
241 if (tickshift < N_SAFE_DYNTRANS_LIMIT_SHIFT) {
242 fatal("ERROR! tickshift = %i, less than "
243 "N_SAFE_DYNTRANS_LIMIT_SHIFT (%i)\n",
244 tickshift, N_SAFE_DYNTRANS_LIMIT_SHIFT);
245 exit(1);
246 }
247 }
248
249 machine->ticks_till_next[n] = 0;
250 machine->ticks_reset_value[n] = 1 << tickshift;
251 machine->tick_func[n] = func;
252 machine->tick_extra[n] = extra;
253 machine->tick_hz[n] = hz;
254
255 machine->n_tick_entries ++;
256 }
257
258
259 /*
260 * machine_bus_register():
261 *
262 * Registers a bus in a machine.
263 */
264 void machine_bus_register(struct machine *machine, char *busname,
265 void (*debug_dump)(void *), void *extra)
266 {
267 struct machine_bus *tmp, *last = NULL, *new;
268
269 new = zeroed_alloc(sizeof(struct machine_bus));
270 new->name = strdup(busname);
271 new->debug_dump = debug_dump;
272 new->extra = extra;
273
274 /* Register last in the bus list: */
275 tmp = machine->first_bus;
276 while (tmp != NULL) {
277 last = tmp;
278 tmp = tmp->next;
279 }
280
281 if (last == NULL)
282 machine->first_bus = new;
283 else
284 last->next = new;
285
286 machine->n_busses ++;
287 }
288
289
290 /*
291 * machine_dump_bus_info():
292 *
293 * Dumps info about registered busses.
294 */
295 void machine_dump_bus_info(struct machine *m)
296 {
297 struct machine_bus *bus = m->first_bus;
298 int iadd = DEBUG_INDENTATION;
299
300 if (m->n_busses > 0)
301 debug("busses:\n");
302 debug_indentation(iadd);
303 while (bus != NULL) {
304 bus->debug_dump(bus->extra);
305 bus = bus->next;
306 }
307 debug_indentation(-iadd);
308 }
309
310
311 /*
312 * machine_dumpinfo():
313 *
314 * Dumps info about a machine in some kind of readable format. (Used by
315 * the 'machine' debugger command.)
316 */
317 void machine_dumpinfo(struct machine *m)
318 {
319 int i;
320
321 debug("serial nr: %i", m->serial_nr);
322 if (m->nr_of_nics > 0)
323 debug(" (nr of NICs: %i)", m->nr_of_nics);
324 debug("\n");
325
326 debug("memory: %i MB", m->physical_ram_in_mb);
327 if (m->memory_offset_in_mb != 0)
328 debug(" (offset by %i MB)", m->memory_offset_in_mb);
329 if (m->random_mem_contents)
330 debug(", randomized contents");
331 if (m->dbe_on_nonexistant_memaccess)
332 debug(", dbe_on_nonexistant_memaccess");
333 debug("\n");
334
335 debug("clock: ");
336 if (m->automatic_clock_adjustment)
337 debug("adjusted automatically");
338 else
339 debug("fixed at %i Hz", m->emulated_hz);
340 debug("\n");
341
342 if (!m->prom_emulation)
343 debug("PROM emulation disabled\n");
344
345 for (i=0; i<m->ncpus; i++)
346 cpu_dumpinfo(m, m->cpus[i]);
347
348 if (m->ncpus > 1)
349 debug("Bootstrap cpu is nr %i\n", m->bootstrap_cpu);
350
351 if (m->slow_serial_interrupts_hack_for_linux)
352 debug("Using slow_serial_interrupts_hack_for_linux\n");
353
354 if (m->use_x11) {
355 debug("Using X11");
356 if (m->x11_scaledown > 1)
357 debug(", scaledown %i", m->x11_scaledown);
358 if (m->x11_scaleup > 1)
359 debug(", scaleup %i", m->x11_scaleup);
360 if (m->x11_n_display_names > 0) {
361 for (i=0; i<m->x11_n_display_names; i++) {
362 debug(i? ", " : " (");
363 debug("\"%s\"", m->x11_display_names[i]);
364 }
365 debug(")");
366 }
367 debug("\n");
368 }
369
370 machine_dump_bus_info(m);
371
372 diskimage_dump_info(m);
373
374 if (m->force_netboot)
375 debug("Forced netboot\n");
376 }
377
378
379 /*
380 * dump_mem_string():
381 *
382 * Dump the contents of emulated RAM as readable text. Bytes that aren't
383 * readable are dumped in [xx] notation, where xx is in hexadecimal.
384 * Dumping ends after DUMP_MEM_STRING_MAX bytes, or when a terminating
385 * zero byte is found.
386 */
387 #define DUMP_MEM_STRING_MAX 45
388 void dump_mem_string(struct cpu *cpu, uint64_t addr)
389 {
390 int i;
391 for (i=0; i<DUMP_MEM_STRING_MAX; i++) {
392 unsigned char ch = '\0';
393
394 cpu->memory_rw(cpu, cpu->mem, addr + i, &ch, sizeof(ch),
395 MEM_READ, CACHE_DATA | NO_EXCEPTIONS);
396 if (ch == '\0')
397 return;
398 if (ch >= ' ' && ch < 126)
399 debug("%c", ch);
400 else
401 debug("[%02x]", ch);
402 }
403 }
404
405
406 /*
407 * store_byte():
408 *
409 * Stores a byte in emulated ram. (Helper function.)
410 */
411 void store_byte(struct cpu *cpu, uint64_t addr, uint8_t data)
412 {
413 if ((addr >> 32) == 0)
414 addr = (int64_t)(int32_t)addr;
415 cpu->memory_rw(cpu, cpu->mem,
416 addr, &data, sizeof(data), MEM_WRITE, CACHE_DATA);
417 }
418
419
420 /*
421 * store_string():
422 *
423 * Stores chars into emulated RAM until a zero byte (string terminating
424 * character) is found. The zero byte is also copied.
425 * (strcpy()-like helper function, host-RAM-to-emulated-RAM.)
426 */
427 void store_string(struct cpu *cpu, uint64_t addr, char *s)
428 {
429 do {
430 store_byte(cpu, addr++, *s);
431 } while (*s++);
432 }
433
434
435 /*
436 * add_environment_string():
437 *
438 * Like store_string(), but advances the pointer afterwards. The most
439 * obvious use is to place a number of strings (such as environment variable
440 * strings) after one-another in emulated memory.
441 */
442 void add_environment_string(struct cpu *cpu, char *s, uint64_t *addr)
443 {
444 store_string(cpu, *addr, s);
445 (*addr) += strlen(s) + 1;
446 }
447
448
449 /*
450 * add_environment_string_dual():
451 *
452 * Add "dual" environment strings, one for the variable name and one for the
453 * value, and update pointers afterwards.
454 */
455 void add_environment_string_dual(struct cpu *cpu,
456 uint64_t *ptrp, uint64_t *addrp, char *s1, char *s2)
457 {
458 uint64_t ptr = *ptrp, addr = *addrp;
459
460 store_32bit_word(cpu, ptr, addr);
461 ptr += sizeof(uint32_t);
462 if (addr != 0) {
463 store_string(cpu, addr, s1);
464 addr += strlen(s1) + 1;
465 }
466 store_32bit_word(cpu, ptr, addr);
467 ptr += sizeof(uint32_t);
468 if (addr != 0) {
469 store_string(cpu, addr, s2);
470 addr += strlen(s2) + 1;
471 }
472
473 *ptrp = ptr;
474 *addrp = addr;
475 }
476
477
478 /*
479 * store_64bit_word():
480 *
481 * Stores a 64-bit word in emulated RAM. Byte order is taken into account.
482 * Helper function.
483 */
484 int store_64bit_word(struct cpu *cpu, uint64_t addr, uint64_t data64)
485 {
486 unsigned char data[8];
487 if ((addr >> 32) == 0)
488 addr = (int64_t)(int32_t)addr;
489 data[0] = (data64 >> 56) & 255;
490 data[1] = (data64 >> 48) & 255;
491 data[2] = (data64 >> 40) & 255;
492 data[3] = (data64 >> 32) & 255;
493 data[4] = (data64 >> 24) & 255;
494 data[5] = (data64 >> 16) & 255;
495 data[6] = (data64 >> 8) & 255;
496 data[7] = (data64) & 255;
497 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
498 int tmp = data[0]; data[0] = data[7]; data[7] = tmp;
499 tmp = data[1]; data[1] = data[6]; data[6] = tmp;
500 tmp = data[2]; data[2] = data[5]; data[5] = tmp;
501 tmp = data[3]; data[3] = data[4]; data[4] = tmp;
502 }
503 return cpu->memory_rw(cpu, cpu->mem,
504 addr, data, sizeof(data), MEM_WRITE, CACHE_DATA);
505 }
506
507
508 /*
509 * store_32bit_word():
510 *
511 * Stores a 32-bit word in emulated RAM. Byte order is taken into account.
512 * (This function takes a 64-bit word as argument, to suppress some
513 * warnings, but only the lowest 32 bits are used.)
514 */
515 int store_32bit_word(struct cpu *cpu, uint64_t addr, uint64_t data32)
516 {
517 unsigned char data[4];
518 if (cpu->machine->arch == ARCH_MIPS && (addr >> 32) == 0)
519 addr = (int64_t)(int32_t)addr;
520 data[0] = (data32 >> 24) & 255;
521 data[1] = (data32 >> 16) & 255;
522 data[2] = (data32 >> 8) & 255;
523 data[3] = (data32) & 255;
524 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
525 int tmp = data[0]; data[0] = data[3]; data[3] = tmp;
526 tmp = data[1]; data[1] = data[2]; data[2] = tmp;
527 }
528 return cpu->memory_rw(cpu, cpu->mem,
529 addr, data, sizeof(data), MEM_WRITE, CACHE_DATA);
530 }
531
532
533 /*
534 * store_16bit_word():
535 *
536 * Stores a 16-bit word in emulated RAM. Byte order is taken into account.
537 * (This function takes a 64-bit word as argument, to suppress some
538 * warnings, but only the lowest 16 bits are used.)
539 */
540 int store_16bit_word(struct cpu *cpu, uint64_t addr, uint64_t data16)
541 {
542 unsigned char data[2];
543 if (cpu->machine->arch == ARCH_MIPS && (addr >> 32) == 0)
544 addr = (int64_t)(int32_t)addr;
545 data[0] = (data16 >> 8) & 255;
546 data[1] = (data16) & 255;
547 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
548 int tmp = data[0]; data[0] = data[1]; data[1] = tmp;
549 }
550 return cpu->memory_rw(cpu, cpu->mem,
551 addr, data, sizeof(data), MEM_WRITE, CACHE_DATA);
552 }
553
554
555 /*
556 * store_buf():
557 *
558 * memcpy()-like helper function, from host RAM to emulated RAM.
559 */
560 void store_buf(struct cpu *cpu, uint64_t addr, char *s, size_t len)
561 {
562 size_t psize = 1024; /* 1024 256 64 16 4 1 */
563
564 if (cpu->machine->arch == ARCH_MIPS && (addr >> 32) == 0)
565 addr = (int64_t)(int32_t)addr;
566
567 while (len != 0) {
568 if ((addr & (psize-1)) == 0) {
569 while (len >= psize) {
570 cpu->memory_rw(cpu, cpu->mem, addr,
571 (unsigned char *)s, psize, MEM_WRITE,
572 CACHE_DATA);
573 addr += psize;
574 s += psize;
575 len -= psize;
576 }
577 }
578 psize >>= 2;
579 }
580
581 while (len-- != 0)
582 store_byte(cpu, addr++, *s++);
583 }
584
585
586 /*
587 * store_pointer_and_advance():
588 *
589 * Stores a 32-bit or 64-bit pointer in emulated RAM, and advances the
590 * target address. (Useful for e.g. ARCBIOS environment initialization.)
591 */
592 void store_pointer_and_advance(struct cpu *cpu, uint64_t *addrp,
593 uint64_t data, int flag64)
594 {
595 uint64_t addr = *addrp;
596 if (flag64) {
597 store_64bit_word(cpu, addr, data);
598 addr += 8;
599 } else {
600 store_32bit_word(cpu, addr, data);
601 addr += 4;
602 }
603 *addrp = addr;
604 }
605
606
607 /*
608 * load_32bit_word():
609 *
610 * Helper function. Prints a warning and returns 0, if the read failed.
611 * Emulated byte order is taken into account.
612 */
613 uint32_t load_32bit_word(struct cpu *cpu, uint64_t addr)
614 {
615 unsigned char data[4];
616
617 if (cpu->machine->arch == ARCH_MIPS && (addr >> 32) == 0)
618 addr = (int64_t)(int32_t)addr;
619 cpu->memory_rw(cpu, cpu->mem,
620 addr, data, sizeof(data), MEM_READ, CACHE_DATA);
621
622 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
623 int tmp = data[0]; data[0] = data[3]; data[3] = tmp;
624 tmp = data[1]; data[1] = data[2]; data[2] = tmp;
625 }
626
627 return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
628 }
629
630
631 /*
632 * load_16bit_word():
633 *
634 * Helper function. Prints a warning and returns 0, if the read failed.
635 * Emulated byte order is taken into account.
636 */
637 uint16_t load_16bit_word(struct cpu *cpu, uint64_t addr)
638 {
639 unsigned char data[2];
640
641 if (cpu->machine->arch == ARCH_MIPS && (addr >> 32) == 0)
642 addr = (int64_t)(int32_t)addr;
643 cpu->memory_rw(cpu, cpu->mem,
644 addr, data, sizeof(data), MEM_READ, CACHE_DATA);
645
646 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
647 int tmp = data[0]; data[0] = data[1]; data[1] = tmp;
648 }
649
650 return (data[0] << 8) + data[1];
651 }
652
653
654 /*
655 * store_64bit_word_in_host():
656 *
657 * Stores a 64-bit word in the _host's_ RAM. Emulated byte order is taken
658 * into account. This is useful when building structs in the host's RAM
659 * which will later be copied into emulated RAM.
660 */
661 void store_64bit_word_in_host(struct cpu *cpu,
662 unsigned char *data, uint64_t data64)
663 {
664 data[0] = (data64 >> 56) & 255;
665 data[1] = (data64 >> 48) & 255;
666 data[2] = (data64 >> 40) & 255;
667 data[3] = (data64 >> 32) & 255;
668 data[4] = (data64 >> 24) & 255;
669 data[5] = (data64 >> 16) & 255;
670 data[6] = (data64 >> 8) & 255;
671 data[7] = (data64) & 255;
672 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
673 int tmp = data[0]; data[0] = data[7]; data[7] = tmp;
674 tmp = data[1]; data[1] = data[6]; data[6] = tmp;
675 tmp = data[2]; data[2] = data[5]; data[5] = tmp;
676 tmp = data[3]; data[3] = data[4]; data[4] = tmp;
677 }
678 }
679
680
681 /*
682 * store_32bit_word_in_host():
683 *
684 * See comment for store_64bit_word_in_host().
685 *
686 * (Note: The data32 parameter is a uint64_t. This is done to suppress
687 * some warnings.)
688 */
689 void store_32bit_word_in_host(struct cpu *cpu,
690 unsigned char *data, uint64_t data32)
691 {
692 data[0] = (data32 >> 24) & 255;
693 data[1] = (data32 >> 16) & 255;
694 data[2] = (data32 >> 8) & 255;
695 data[3] = (data32) & 255;
696 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
697 int tmp = data[0]; data[0] = data[3]; data[3] = tmp;
698 tmp = data[1]; data[1] = data[2]; data[2] = tmp;
699 }
700 }
701
702
703 /*
704 * store_16bit_word_in_host():
705 *
706 * See comment for store_64bit_word_in_host().
707 */
708 void store_16bit_word_in_host(struct cpu *cpu,
709 unsigned char *data, uint16_t data16)
710 {
711 data[0] = (data16 >> 8) & 255;
712 data[1] = (data16) & 255;
713 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
714 int tmp = data[0]; data[0] = data[1]; data[1] = tmp;
715 }
716 }
717
718
719 /*
720 * machine_setup():
721 *
722 * This (rather large) function initializes memory, registers, and/or devices
723 * required by specific machine emulations.
724 */
725 void machine_setup(struct machine *machine)
726 {
727 struct memory *mem;
728 struct machine_entry *me;
729
730 /* Abreviation: :-) */
731 struct cpu *cpu = machine->cpus[machine->bootstrap_cpu];
732
733 machine->bootdev_id = diskimage_bootdev(machine,
734 &machine->bootdev_type);
735
736 mem = cpu->mem;
737 machine->machine_name = NULL;
738
739 /* TODO: Move this somewhere else? */
740 if (machine->boot_string_argument == NULL) {
741 switch (machine->machine_type) {
742 case MACHINE_ARC:
743 machine->boot_string_argument = "-aN";
744 break;
745 case MACHINE_CATS:
746 machine->boot_string_argument = "-A";
747 break;
748 case MACHINE_PMAX:
749 machine->boot_string_argument = "-a";
750 break;
751 default:
752 /* Important, because boot_string_argument should
753 not be set to NULL: */
754 machine->boot_string_argument = "";
755 }
756 }
757
758
759 /*
760 * If the machine has a setup function in src/machines/machine_*.c
761 * then use that one, otherwise use the old hardcoded stuff here:
762 */
763
764 me = first_machine_entry;
765 while (me != NULL) {
766 if (machine->machine_type == me->machine_type &&
767 me->setup != NULL) {
768 me->setup(machine, cpu);
769 break;
770 }
771 me = me->next;
772 }
773
774 if (me == NULL) {
775 fatal("Unknown emulation type %i\n", machine->machine_type);
776 exit(1);
777 }
778
779 if (machine->machine_name != NULL)
780 debug("machine: %s", machine->machine_name);
781
782 if (machine->emulated_hz > 0)
783 debug(" (%.2f MHz)", (float)machine->emulated_hz / 1000000);
784 debug("\n");
785
786 /* Default fake speed: 5 MHz */
787 if (machine->emulated_hz < 1)
788 machine->emulated_hz = 5000000;
789
790 if (machine->bootstr != NULL) {
791 debug("bootstring%s: %s", (machine->bootarg!=NULL &&
792 strlen(machine->bootarg) >= 1)? "(+bootarg)" : "",
793 machine->bootstr);
794 if (machine->bootarg != NULL && strlen(machine->bootarg) >= 1)
795 debug(" %s", machine->bootarg);
796 debug("\n");
797 }
798
799 if (verbose >= 2)
800 machine_dump_bus_info(machine);
801
802 if (!machine->stable)
803 fatal("!\n! NOTE: This machine type is not implemented well"
804 " enough yet to run\n! any real-world code!"
805 " (At least, it hasn't been verified to do so.)\n!\n"
806 "! Please read the GXemul documentation for information"
807 " about which\n! machine types that actually work.\n!\n");
808 }
809
810
811 /*
812 * machine_memsize_fix():
813 *
814 * Sets physical_ram_in_mb (if not already set), and memory_offset_in_mb,
815 * depending on machine type.
816 */
817 void machine_memsize_fix(struct machine *m)
818 {
819 if (m == NULL) {
820 fatal("machine_defaultmemsize(): m == NULL?\n");
821 exit(1);
822 }
823
824 if (m->physical_ram_in_mb == 0) {
825 struct machine_entry *me = first_machine_entry;
826 while (me != NULL) {
827 if (m->machine_type == me->machine_type &&
828 me->set_default_ram != NULL) {
829 me->set_default_ram(m);
830 break;
831 }
832 me = me->next;
833 }
834 }
835
836 /* Special hack for hpcmips machines: */
837 if (m->machine_type == MACHINE_HPCMIPS) {
838 m->dbe_on_nonexistant_memaccess = 0;
839 }
840
841 /* Special SGI memory offsets: (TODO: move this somewhere else) */
842 if (m->machine_type == MACHINE_SGI) {
843 switch (m->machine_subtype) {
844 case 20:
845 case 22:
846 case 24:
847 case 26:
848 m->memory_offset_in_mb = 128;
849 break;
850 case 28:
851 case 30:
852 m->memory_offset_in_mb = 512;
853 break;
854 }
855 }
856
857 if (m->physical_ram_in_mb == 0)
858 m->physical_ram_in_mb = DEFAULT_RAM_IN_MB;
859 }
860
861
862 /*
863 * machine_default_cputype():
864 *
865 * Sets m->cpu_name, if it isn't already set, depending on the machine type.
866 */
867 void machine_default_cputype(struct machine *m)
868 {
869 struct machine_entry *me;
870
871 if (m == NULL) {
872 fatal("machine_default_cputype(): m == NULL?\n");
873 exit(1);
874 }
875
876 /* Already set? Then return. */
877 if (m->cpu_name != NULL)
878 return;
879
880 me = first_machine_entry;
881 while (me != NULL) {
882 if (m->machine_type == me->machine_type &&
883 me->set_default_cpu != NULL) {
884 me->set_default_cpu(m);
885 break;
886 }
887 me = me->next;
888 }
889
890 if (m->cpu_name == NULL) {
891 fprintf(stderr, "machine_default_cputype(): no default"
892 " cpu for machine type %i subtype %i\n",
893 m->machine_type, m->machine_subtype);
894 exit(1);
895 }
896 }
897
898
899 /*
900 * machine_entry_new():
901 *
902 * This function creates a new machine_entry struct, and fills it with some
903 * valid data; it is up to the caller to add additional data that weren't
904 * passed as arguments to this function.
905 */
906 struct machine_entry *machine_entry_new(const char *name,
907 int arch, int oldstyle_type, int n_aliases, int n_subtypes)
908 {
909 struct machine_entry *me;
910
911 me = malloc(sizeof(struct machine_entry));
912 if (me == NULL) {
913 fprintf(stderr, "machine_entry_new(): out of memory (1)\n");
914 exit(1);
915 }
916
917 memset(me, 0, sizeof(struct machine_entry));
918
919 me->name = name;
920 me->arch = arch;
921 me->machine_type = oldstyle_type;
922 me->n_aliases = n_aliases;
923 me->aliases = malloc(sizeof(char *) * n_aliases);
924 if (me->aliases == NULL) {
925 fprintf(stderr, "machine_entry_new(): out of memory (2)\n");
926 exit(1);
927 }
928 me->n_subtypes = n_subtypes;
929 me->setup = NULL;
930
931 if (n_subtypes > 0) {
932 me->subtype = malloc(sizeof(struct machine_entry_subtype *) *
933 n_subtypes);
934 if (me->subtype == NULL) {
935 fprintf(stderr, "machine_entry_new(): out of "
936 "memory (3)\n");
937 exit(1);
938 }
939 }
940
941 return me;
942 }
943
944
945 /*
946 * machine_entry_subtype_new():
947 *
948 * This function creates a new machine_entry_subtype struct, and fills it with
949 * some valid data; it is up to the caller to add additional data that weren't
950 * passed as arguments to this function.
951 *
952 * For internal use.
953 */
954 struct machine_entry_subtype *machine_entry_subtype_new(
955 const char *name, int oldstyle_type, int n_aliases)
956 {
957 struct machine_entry_subtype *mes;
958
959 mes = malloc(sizeof(struct machine_entry_subtype));
960 if (mes == NULL) {
961 fprintf(stderr, "machine_entry_subtype_new(): out "
962 "of memory (1)\n");
963 exit(1);
964 }
965
966 memset(mes, 0, sizeof(struct machine_entry_subtype));
967 mes->name = name;
968 mes->machine_subtype = oldstyle_type;
969 mes->n_aliases = n_aliases;
970 mes->aliases = malloc(sizeof(char *) * n_aliases);
971 if (mes->aliases == NULL) {
972 fprintf(stderr, "machine_entry_subtype_new(): "
973 "out of memory (2)\n");
974 exit(1);
975 }
976
977 return mes;
978 }
979
980
981 /*
982 * machine_entry_add():
983 *
984 * Inserts a new machine_entry into the machine entries list.
985 */
986 void machine_entry_add(struct machine_entry *me, int arch)
987 {
988 struct machine_entry *prev, *next;
989
990 /* Only insert it if the architecture is implemented in this
991 emulator configuration: */
992 if (cpu_family_ptr_by_number(arch) == NULL)
993 return;
994
995 prev = NULL;
996 next = first_machine_entry;
997
998 for (;;) {
999 if (next == NULL)
1000 break;
1001 if (strcasecmp(me->name, next->name) < 0)
1002 break;
1003
1004 prev = next;
1005 next = next->next;
1006 }
1007
1008 if (prev != NULL)
1009 prev->next = me;
1010 else
1011 first_machine_entry = me;
1012 me->next = next;
1013 }
1014
1015
1016 /*
1017 * machine_list_available_types_and_cpus():
1018 *
1019 * List all available machine types (for example when running the emulator
1020 * with just -H as command line argument).
1021 */
1022 void machine_list_available_types_and_cpus(void)
1023 {
1024 struct machine_entry *me;
1025 int iadd = DEBUG_INDENTATION * 2;
1026
1027 debug("Available CPU types:\n\n");
1028
1029 debug_indentation(iadd);
1030 cpu_list_available_types();
1031 debug_indentation(-iadd);
1032
1033 debug("\nMost of the CPU types are bogus, and not really implemented."
1034 " The main effect of\nselecting a specific CPU type is to choose "
1035 "what kind of 'id' it will have.\n\nAvailable machine types (with "
1036 "aliases) and their subtypes:\n\n");
1037
1038 debug_indentation(iadd);
1039 me = first_machine_entry;
1040
1041 if (me == NULL)
1042 fatal("No machines defined!\n");
1043
1044 while (me != NULL) {
1045 int i, j, iadd = DEBUG_INDENTATION;
1046
1047 debug("%s [%s] (", me->name,
1048 cpu_family_ptr_by_number(me->arch)->name);
1049 for (i=0; i<me->n_aliases; i++)
1050 debug("%s\"%s\"", i? ", " : "", me->aliases[i]);
1051 debug(")\n");
1052
1053 debug_indentation(iadd);
1054 for (i=0; i<me->n_subtypes; i++) {
1055 struct machine_entry_subtype *mes;
1056 mes = me->subtype[i];
1057 debug("- %s", mes->name);
1058 debug(" (");
1059 for (j=0; j<mes->n_aliases; j++)
1060 debug("%s\"%s\"", j? ", " : "",
1061 mes->aliases[j]);
1062 debug(")\n");
1063 }
1064 debug_indentation(-iadd);
1065
1066 me = me->next;
1067 }
1068 debug_indentation(-iadd);
1069
1070 debug("\nMost of the machine types are bogus too. Please read the "
1071 "GXemul documentation\nfor information about which machine types "
1072 "that actually work. Use the alias\nwhen selecting a machine type "
1073 "or subtype, not the real name.\n");
1074
1075 #ifdef UNSTABLE_DEVEL
1076 debug("\n");
1077
1078 useremul_list_emuls();
1079 debug("Userland emulation works for programs with the complexity"
1080 " of Hello World,\nbut not much more.\n");
1081 #endif
1082 }
1083
1084
1085 /*
1086 * machine_init():
1087 *
1088 * This function should be called before any other machine_*() function
1089 * is used. automachine_init() registers all machines in src/machines/.
1090 */
1091 void machine_init(void)
1092 {
1093 if (first_machine_entry != NULL) {
1094 fatal("machine_init(): already called?\n");
1095 exit(1);
1096 }
1097
1098 automachine_init();
1099 }
1100

  ViewVC Help
Powered by ViewVC 1.1.26