/[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

Annotation of /trunk/src/machine.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 28 - (hide annotations)
Mon Oct 8 16:20:26 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 30936 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1298 2006/07/22 11:27:46 debug Exp $
20060626	Continuing on SPARC emulation (beginning on the 'save'
		instruction, register windows, etc).
20060629	Planning statistics gathering (new -s command line option),
		and renaming speed_tricks to allow_instruction_combinations.
20060630	Some minor manual page updates.
		Various cleanups.
		Implementing the -s command line option.
20060701	FINALLY found the bug which prevented Linux and Ultrix from
		running without the ugly hack in the R2000/R3000 cache isol
		code; it was the phystranslation hint array which was buggy.
		Removing the phystranslation hint code completely, for now.
20060702	Minor dyntrans cleanups; invalidation of physpages now only
		invalidate those parts of a page that have actually been
		translated. (32 parts per page.)
		Some MIPS non-R3000 speed fixes.
		Experimenting with MIPS instruction combination for some
		addiu+bne+sw loops, and sw+sw+sw.
		Adding support (again) for larger-than-4KB pages in MIPS tlbw*.
		Continuing on SPARC emulation: adding load/store instructions.
20060704	Fixing a virtual vs physical page shift bug in the new tlbw*
		implementation. Problem noticed by Jakub Jermar. (Many thanks.)
		Moving rfe and eret to cpu_mips_instr.c, since that is the
		only place that uses them nowadays.
20060705	Removing the BSD license from the "testmachine" include files,
		placing them in the public domain instead; this enables the
		testmachine stuff to be used from projects which are
		incompatible with the BSD license for some reason.
20060707	Adding instruction combinations for the R2000/R3000 L1
		I-cache invalidation code used by NetBSD/pmax 3.0, lui+addiu,
		various branches followed by addiu or nop, and jr ra followed
		by addiu. The time it takes to perform a full NetBSD/pmax R3000
		install on the laptop has dropped from 573 seconds to 539. :-)
20060708	Adding a framebuffer controller device (dev_fbctrl), which so
		far can be used to change the fb resolution during runtime, but
		in the future will also be useful for accelerated block fill/
		copy, and possibly also simplified character output.
		Adding an instruction combination for NetBSD/pmax' strlen.
20060709	Minor fixes: reading raw files in src/file.c wasn't memblock
		aligned, removing buggy multi_sw MIPS instruction combination,
		etc.
20060711	Adding a machine_qemu.c, which contains a "qemu_mips" machine.
		(It mimics QEMU's MIPS machine mode, so that a test kernel
		made for QEMU_MIPS also can run in GXemul... at least to some
		extent.)  Adding a short section about how to run this mode to
		doc/guestoses.html.
20060714	Misc. minor code cleanups.
20060715	Applying a patch which adds getchar() to promemul/yamon.c
		(from Oleksandr Tymoshenko).
		Adding yamon.h from NetBSD, and rewriting yamon.c to use it
		(instead of ugly hardcoded numbers) + some cleanup.
20060716	Found and fixed the bug which broke single-stepping of 64-bit
		programs between 0.4.0 and 0.4.0.1 (caused by too quick
		refactoring and no testing). Hopefully this fix will not
		break too many other things.
20060718	Continuing on the 8253 PIT; it now works with Linux/QEMU_MIPS.
		Re-adding the sw+sw+sw instr comb (the problem was that I had
		ignored endian issues); however, it doesn't seem to give any
		big performance gain.
20060720	Adding a dummy Transputer mode (T414, T800 etc) skeleton (only
		the 'j' and 'ldc' instructions are implemented so far). :-}
20060721	Adding gtreg.h from NetBSD, updating dev_gt.c to use it, plus
		misc. other updates to get Linux 2.6 for evbmips/malta working
		(thanks to Alec Voropay for the details).
		FINALLY found and fixed the bug which made tlbw* for non-R3000
		buggy; it was a reference count problem in the dyntrans core.
20060722	Testing stuff; things seem stable enough for a new release.

==============  RELEASE 0.4.1  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26