/[gxemul]/trunk/src/promemul/of.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

Diff of /trunk/src/promemul/of.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 14 by dpavlin, Mon Oct 8 16:18:51 2007 UTC revision 42 by dpavlin, Mon Oct 8 16:22:32 2007 UTC
# Line 1  Line 1 
1  /*  /*
2   *  Copyright (C) 2005  Anders Gavare.  All rights reserved.   *  Copyright (C) 2005-2007  Anders Gavare.  All rights reserved.
3   *   *
4   *  Redistribution and use in source and binary forms, with or without   *  Redistribution and use in source and binary forms, with or without
5   *  modification, are permitted provided that the following conditions are met:   *  modification, are permitted provided that the following conditions are met:
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: of.c,v 1.3 2005/09/01 13:27:13 debug Exp $   *  $Id: of.c,v 1.24 2007/06/15 17:02:40 debug Exp $
29   *   *
30   *  OpenFirmware emulation.   *  OpenFirmware emulation.
31   *   *
32   *  Development notes: Care must be taken so that the code in here works   *  NOTE: OpenFirmware is used on quite a variety of different hardware archs,
33   *  with (at least) POWER/PowerPC, ARM, and SPARC.   *        at least POWER/PowerPC, ARM, and SPARC, so the code in this module
34     *        must always remain architecture agnostic.
35     *
36     *  NOTE: Some things, e.g. 32-bit integers as returned by the "getprop"
37     *        service, are always fixed to big-endian. (According to the standard.)
38     *
39     *  TODO: o) 64-bit OpenFirmware?
40     *        o) More devices...
41   */   */
42    
43  #include <stdio.h>  #include <stdio.h>
# Line 38  Line 45 
45  #include <string.h>  #include <string.h>
46  #include <sys/types.h>  #include <sys/types.h>
47    
48    #define OF_C
49    
50  #include "console.h"  #include "console.h"
51  #include "cpu.h"  #include "cpu.h"
52    #include "device.h"
53    #include "devices.h"
54    #include "diskimage.h"
55  #include "machine.h"  #include "machine.h"
56  #include "memory.h"  #include "memory.h"
57  #include "misc.h"  #include "misc.h"
58    #include "of.h"
59    
60    
61  #define N_MAX_ARGS      10  /*  #define debug fatal  */
 #define ARG_MAX_LEN     4096  
62    
63  extern int quiet_mode;  extern int quiet_mode;
64    extern int verbose;
 /*  TODO: IMPORTANT! Change this into something else, to allow multiple  
         opens of the same device:  */  
 #define HANDLE_STDIN    0  
 #define HANDLE_STDOUT   1  
 #define HANDLE_STDERR   2  
 #define HANDLE_MMU      3  
 #define HANDLE_MEMORY   4  
 #define HANDLE_CHOSEN   5  
65    
66    
67  /*  /*
# Line 86  static void readstr(struct cpu *cpu, uin Line 90  static void readstr(struct cpu *cpu, uin
90    
91    
92  /*  /*
93     *  of_store_32bit_in_host():
94     *
95     *  Store big-endian. OpenFirmware properties returned by getprop etc are
96     *  always big-endian, even on little-endian machines.
97     */
98    static void of_store_32bit_in_host(unsigned char *d, uint32_t x)
99    {
100            d[0] = x >> 24; d[1] = x >> 16;
101            d[2] = x >>  8; d[3] = x;
102    }
103    
104    
105    /*
106     *  find_device_handle():
107     *
108     *  name may consist of multiple names, separaed with slashes.
109     */
110    static int find_device_handle(struct of_data *ofd, char *name)
111    {
112            int handle = 1, cur_parent = 1;
113    
114            if (name[0] == 0)
115                    return 0;
116    
117            for (;;) {
118                    struct of_device *od = ofd->of_devices;
119                    char tmp[200];
120                    int i;
121    
122                    while (name[0] == '/')
123                            name++;
124                    if (name[0] == '\0')
125                            break;
126                    snprintf(tmp, sizeof(tmp), "%s", name);
127                    i = 0;
128                    while (tmp[i] != '\0' && tmp[i] != '/')
129                            i++;
130                    tmp[i] = '\0';
131    
132                    OF_FIND(od, strcmp(od->name, tmp) == 0 &&
133                        od->parent == cur_parent);
134                    if (od == NULL)
135                            return -1;
136    
137                    handle = cur_parent = od->handle;
138                    name += strlen(tmp);
139            }
140    
141            return handle;
142    }
143    
144    
145    /*****************************************************************************/
146    
147    
148    OF_SERVICE(call_method_2_2)
149    {
150            fatal("[ of: call_method_2_2('%s'): TODO ]\n", arg[0]);
151            return -1;
152    }
153    
154    
155    OF_SERVICE(call_method_3_4)
156    {
157            fatal("[ of: call_method_3_4('%s'): TODO ]\n", arg[0]);
158            return -1;
159    }
160    
161    
162    OF_SERVICE(call_method_5_2)
163    {
164            if (strcmp(arg[0], "set-colors") == 0) {
165                    /*  Used by OpenBSD/macppc:  */
166                    struct vfb_data *v = cpu->machine->md.of_data->vfb_data;
167                    int color = OF_GET_ARG(3);
168                    uint64_t ptr = OF_GET_ARG(4);
169                    unsigned char rgb[3];
170                    cpu->memory_rw(cpu, cpu->mem, ptr, rgb, 3, MEM_READ,
171                        CACHE_DATA | NO_EXCEPTIONS);
172                    if (v != NULL) {
173                            memcpy(v->rgb_palette + 3 * color, rgb, 3);
174                            v->update_x1 = v->update_y1 = 0;
175                            v->update_x2 = v->xsize - 1;
176                            v->update_y2 = v->ysize - 1;
177                    }
178            } else {
179                    fatal("[ of: call_method_5_2('%s'): TODO ]\n", arg[0]);
180                    return -1;
181            }
182            return 0;
183    }
184    
185    
186    OF_SERVICE(call_method_6_1)
187    {
188            fatal("[ of: call_method_6_1('%s'): TODO ]\n", arg[0]);
189            return -1;
190    }
191    
192    
193    OF_SERVICE(call_method_6_2)
194    {
195            fatal("[ of: call_method_6_2('%s'): TODO ]\n", arg[0]);
196            return -1;
197    }
198    
199    
200    OF_SERVICE(child)
201    {
202            struct of_device *od = cpu->machine->md.of_data->of_devices;
203            int handle = OF_GET_ARG(0);
204            OF_FIND(od, od->parent == handle);
205            store_32bit_word(cpu, base + retofs, od == NULL? 0 : od->handle);
206            return 0;
207    }
208    
209    
210    OF_SERVICE(exit)
211    {
212            cpu->running = 0;
213            return 0;
214    }
215    
216    
217    OF_SERVICE(finddevice)
218    {
219            int h = find_device_handle(cpu->machine->md.of_data, arg[0]);
220            store_32bit_word(cpu, base + retofs, h);
221            return h>0? 0 : -1;
222    }
223    
224    
225    OF_SERVICE(getprop)
226    {
227            struct of_device *od = cpu->machine->md.of_data->of_devices;
228            struct of_device_property *pr;
229            int handle = OF_GET_ARG(0), i, len_returned = 0;
230            uint64_t buf = OF_GET_ARG(2);
231            uint64_t max = OF_GET_ARG(3);
232    
233            OF_FIND(od, od->handle == handle);
234            if (od == NULL) {
235                    fatal("[ of: WARNING: getprop handle=%i; no such handle ]\n",
236                        handle);
237                    return -1;
238            }
239    
240            pr = od->properties;
241            OF_FIND(pr, strcmp(pr->name, arg[1]) == 0);
242            if (pr == NULL) {
243                    fatal("[ of: WARNING: getprop: no property '%s' at handle"
244                        " %i (device '%s') ]\n", arg[1], handle, od->name);
245                    /*  exit(1);  */
246                    return -1;
247            }
248    
249            if (pr->data == NULL) {
250                    fatal("[ of: WARNING: property '%s' of '%s' has no data! ]\n",
251                        arg[1], od->name);
252                    goto ret;
253            }
254    
255            /*  Return the property into emulated RAM:  */
256            len_returned = pr->len <= max? pr->len : max;
257    
258            for (i=0; i<len_returned; i++) {
259                    if (!cpu->memory_rw(cpu, cpu->mem, buf + i, pr->data + i,
260                        1, MEM_WRITE, CACHE_DATA | NO_EXCEPTIONS)) {
261                            fatal("[ of: getprop memory_rw() error ]\n");
262                            exit(1);
263                    }
264            }
265    
266    ret:
267            store_32bit_word(cpu, base + retofs, len_returned);
268            return 0;
269    }
270    
271    
272    OF_SERVICE(getproplen)
273    {
274            struct of_device *od = cpu->machine->md.of_data->of_devices;
275            struct of_device_property *pr;
276            int handle = OF_GET_ARG(0);
277    
278            OF_FIND(od, od->handle == handle);
279            if (od == NULL) {
280                    fatal("[ of: WARNING: getproplen handle=%i; no such handle ]\n",
281                        handle);
282                    exit(1);
283                    /*  return -1;  */
284            }
285    
286            pr = od->properties;
287            OF_FIND(pr, strcmp(pr->name, arg[1]) == 0);
288            if (pr == NULL) {
289                    fatal("[ of: WARNING: getproplen: no property '%s' at handle"
290                        " %i (device '%s') ]\n", arg[1], handle, od->name);
291                    exit(1);
292            }
293    
294            store_32bit_word(cpu, base + retofs, pr->len);
295            return 0;
296    }
297    
298    
299    OF_SERVICE(instance_to_package)
300    {
301            int handle = OF_GET_ARG(0);
302            /*  TODO: actually do something here? :-)  */
303            store_32bit_word(cpu, base + retofs, handle);
304            return 0;
305    }
306    
307    
308    OF_SERVICE(interpret_1)
309    {
310            if (strcmp(arg[0], "#lines 2 - to line#") == 0) {
311            } else {
312                    fatal("[ of: interpret_1('%s'): TODO ]\n", arg[0]);
313                    return -1;
314            }
315            return 0;
316    }
317    
318    
319    OF_SERVICE(interpret_2)
320    {
321            store_32bit_word(cpu, base + retofs, 0);        /*  ?  TODO  */
322            if (strcmp(arg[0], "#columns") == 0) {
323                    store_32bit_word(cpu, base + retofs + 4, 80);
324            } else if (strcmp(arg[0], "#lines") == 0) {
325                    store_32bit_word(cpu, base + retofs + 4, 40);
326            } else if (strcmp(arg[0], "char-height") == 0) {
327                    store_32bit_word(cpu, base + retofs + 4, 15);
328            } else if (strcmp(arg[0], "char-width") == 0) {
329                    store_32bit_word(cpu, base + retofs + 4, 10);
330            } else if (strcmp(arg[0], "line#") == 0) {
331                    store_32bit_word(cpu, base + retofs + 4, 0);
332            } else if (strcmp(arg[0], "font-adr") == 0) {
333                    store_32bit_word(cpu, base + retofs + 4, 0);
334            } else {
335                    fatal("[ of: interpret_2('%s'): TODO ]\n", arg[0]);
336                    return -1;
337            }
338            return 0;
339    }
340    
341    
342    OF_SERVICE(package_to_path)
343    {
344            fatal("[ of: package-to-path: TODO ]\n");
345            return -1;
346    }
347    
348    
349    OF_SERVICE(parent)
350    {
351            struct of_device *od = cpu->machine->md.of_data->of_devices;
352            int handle = OF_GET_ARG(0);
353            OF_FIND(od, od->handle == handle);
354            store_32bit_word(cpu, base + retofs, od == NULL? 0 : od->parent);
355            return 0;
356    }
357    
358    
359    OF_SERVICE(peer)
360    {
361            struct of_device *od = cpu->machine->md.of_data->of_devices;
362            int handle = OF_GET_ARG(0), parent = 0, peer = 0, seen_self = 1;
363    
364            if (handle == 0) {
365                    /*  Return the handle of the root node (1):  */
366                    store_32bit_word(cpu, base + retofs, 1);
367                    return 0;
368            }
369    
370            OF_FIND(od, od->handle == handle);
371            if (od == NULL) {
372                    fatal("[ of: peer(): can't find handle %i ]\n", handle);
373                    exit(1);
374            }
375            parent = od->parent;
376            seen_self = 0;
377    
378            od = cpu->machine->md.of_data->of_devices;
379    
380            while (od != NULL) {
381                    if (od->parent == parent) {
382                            if (seen_self) {
383                                    peer = od->handle;
384                                    break;
385                            }
386                            if (od->handle == handle)
387                                    seen_self = 1;
388                    }
389                    od = od->next;
390            }
391            store_32bit_word(cpu, base + retofs, peer);
392            return 0;
393    }
394    
395    
396    OF_SERVICE(read)
397    {
398            /*  int handle = OF_GET_ARG(0);  */
399            uint64_t ptr = OF_GET_ARG(1);
400            /*  int len = OF_GET_ARG(2);  */
401            int c;
402            unsigned char ch;
403    
404            /*  TODO: check handle! This just reads chars from the console!  */
405            /*  TODO: This is blocking!  */
406    
407            c = console_readchar(cpu->machine->main_console_handle);
408            ch = c;
409            if (!cpu->memory_rw(cpu, cpu->mem, ptr, &ch, 1, MEM_WRITE,
410                CACHE_DATA | NO_EXCEPTIONS)) {
411                    fatal("[ of: read: memory_rw() error ]\n");
412                    exit(1);
413            }
414    
415            store_32bit_word(cpu, base + retofs, c == -1? 0 : 1);
416            return c == -1? -1 : 0;
417    }
418    
419    
420    OF_SERVICE(write)
421    {
422            /*  int handle = OF_GET_ARG(0);  */
423            uint64_t ptr = OF_GET_ARG(1);
424            int n_written = 0, i, len = OF_GET_ARG(2);
425    
426            /*  TODO: check handle! This just dumps the data to the console!  */
427    
428            for (i=0; i<len; i++) {
429                    unsigned char ch;
430                    if (!cpu->memory_rw(cpu, cpu->mem, ptr + i, &ch,
431                        1, MEM_READ, CACHE_DATA | NO_EXCEPTIONS)) {
432                            fatal("[ of: write: memory_rw() error ]\n");
433                            exit(1);
434                    }
435                    if (ch != 7)
436                            console_putchar(cpu->machine->main_console_handle, ch);
437                    n_written ++;
438            }
439    
440            store_32bit_word(cpu, base + retofs, n_written);
441            return 0;
442    }
443    
444    
445    /*****************************************************************************/
446    
447    
448    /*
449     *  of_get_unused_device_handle():
450     *
451     *  Returns an unused device handle number (1 or higher).
452     */
453    static int of_get_unused_device_handle(struct of_data *of_data)
454    {
455            int max_handle = 0;
456            struct of_device *od = of_data->of_devices;
457    
458            while (od != NULL) {
459                    if (od->handle > max_handle)
460                            max_handle = od->handle;
461                    od = od->next;
462            }
463    
464            return max_handle + 1;
465    }
466    
467    
468    /*
469     *  of_add_device():
470     *
471     *  Adds a device.
472     */
473    static struct of_device *of_add_device(struct of_data *of_data, char *name,
474            char *parentname)
475    {
476            struct of_device *od;
477    
478            CHECK_ALLOCATION(od = malloc(sizeof(struct of_device)));
479            memset(od, 0, sizeof(struct of_device));
480    
481            CHECK_ALLOCATION(od->name = strdup(name));
482    
483            od->handle = of_get_unused_device_handle(of_data);
484            od->parent = find_device_handle(of_data, parentname);
485            if (od->parent < 0) {
486                    fatal("of_add_device(): adding '%s' to parent '%s' failed: "
487                        "parent not found!\n", name, parentname);
488                    exit(1);
489            }
490    
491            od->next = of_data->of_devices;
492            of_data->of_devices = od;
493    
494            return od;
495    }
496    
497    
498    /*
499     *  of_add_prop():
500     *
501     *  Adds a property to a device.
502     */
503    static void of_add_prop(struct of_data *of_data, char *devname,
504            char *propname, unsigned char *data, uint32_t len, int flags)
505    {
506            struct of_device_property *pr;
507            struct of_device *od = of_data->of_devices;
508            int h = find_device_handle(of_data, devname);
509    
510            CHECK_ALLOCATION(pr = malloc(sizeof(struct of_device_property)));
511            memset(pr, 0, sizeof(struct of_device_property));
512    
513            OF_FIND(od, od->handle == h);
514            if (od == NULL) {
515                    fatal("of_add_prop(): device '%s' not registered\n", devname);
516                    exit(1);
517            }
518    
519            CHECK_ALLOCATION(pr->name = strdup(propname));
520            pr->data = data;
521            pr->len = len;
522            pr->flags = flags;
523    
524            pr->next = od->properties;
525            od->properties = pr;
526    }
527    
528    
529    /*
530     *  of_add_service():
531     *
532     *  Adds a service.
533     */
534    static void of_add_service(struct of_data *of_data, char *name,
535            int (*f)(OF_SERVICE_ARGS), int n_args, int n_ret_args)
536    {
537            struct of_service *os;
538    
539            CHECK_ALLOCATION(os = malloc(sizeof(struct of_service)));
540            memset(os, 0, sizeof(struct of_service));
541    
542            CHECK_ALLOCATION(os->name = strdup(name));
543    
544            os->f = f;
545            os->n_args = n_args;
546            os->n_ret_args = n_ret_args;
547    
548            os->next = of_data->of_services;
549            of_data->of_services = os;
550    }
551    
552    
553    /*
554     *  of_dump_devices():
555     *
556     *  Debug dump helper.
557     */
558    static void of_dump_devices(struct of_data *ofd, int parent)
559    {
560            int iadd = DEBUG_INDENTATION;
561            struct of_device *od = ofd->of_devices;
562    
563            while (od != NULL) {
564                    struct of_device_property *pr = od->properties;
565                    if (od->parent != parent) {
566                            od = od->next;
567                            continue;
568                    }
569                    debug("\"%s\"\n", od->name, od->handle);
570                    debug_indentation(iadd);
571                    while (pr != NULL) {
572                            debug("(%s: ", pr->name);
573                            if (pr->flags == OF_PROP_STRING)
574                                    debug("\"%s\"", pr->data);
575                            else
576                                    debug("%i bytes", pr->len);
577                            debug(")\n");
578                            pr = pr->next;
579                    }
580                    of_dump_devices(ofd, od->handle);
581                    debug_indentation(-iadd);
582                    od = od->next;
583            }
584    }
585    
586    
587    /*
588     *  of_dump_all():
589     *
590     *  Debug dump.
591     */
592    static void of_dump_all(struct of_data *ofd)
593    {
594            int iadd = DEBUG_INDENTATION;
595            struct of_service *os;
596    
597            debug("openfirmware debug dump:\n");
598            debug_indentation(iadd);
599    
600            /*  Devices:  */
601            of_dump_devices(ofd, 0);
602    
603            /*  Services:  */
604            os = ofd->of_services;
605            while (os != NULL) {
606                    debug("service '%s'", os->name);
607                    if (os->n_ret_args > 0 || os->n_args > 0) {
608                            debug(" (");
609                            if (os->n_args > 0) {
610                                    debug("%i arg%s", os->n_args,
611                                        os->n_args > 1? "s" : "");
612                                    if (os->n_ret_args > 0)
613                                            debug(", ");
614                            }
615                            if (os->n_ret_args > 0)
616                                    debug("%i return value%s", os->n_ret_args,
617                                        os->n_ret_args > 1? "s" : "");
618                            debug(")");
619                    }
620                    debug("\n");
621                    os = os->next;
622            }
623    
624            debug_indentation(-iadd);
625    }
626    
627    
628    /*
629     *  of_add_prop_int32():
630     *
631     *  Helper function.
632     */
633    static void of_add_prop_int32(struct of_data *ofd,
634            char *devname, char *propname, uint32_t x)
635    {
636            unsigned char *p;
637    
638            CHECK_ALLOCATION(p = malloc(sizeof(int32_t)));
639    
640            of_store_32bit_in_host(p, x);
641            of_add_prop(ofd, devname, propname, p, sizeof(int32_t),
642                OF_PROP_INT);
643    }
644    
645    
646    /*
647     *  of_add_prop_str():
648     *
649     *  Helper function.
650     */
651    static void of_add_prop_str(struct machine *machine, struct of_data *ofd,
652            char *devname, char *propname, char *data)
653    {
654            char *p;
655    
656            CHECK_ALLOCATION(p = strdup(data));
657    
658            of_add_prop(ofd, devname, propname, (unsigned char *)p, strlen(p) + 1,
659                OF_PROP_STRING);
660    }
661    
662    
663    /*
664     *  of_emul_init_isa():
665     */
666    void of_emul_init_isa(struct machine *machine)
667    {
668            struct of_data *ofd = machine->md.of_data;
669            unsigned char *isa_ranges;
670    
671            of_add_device(ofd, "isa", "/");
672    
673            CHECK_ALLOCATION(isa_ranges = malloc(32));
674            memset(isa_ranges, 0, 32);
675    
676            /*  2 *: isa_phys_hi, isa_phys_lo, parent_phys_start, size  */
677            /*  MEM space:  */
678            of_store_32bit_in_host(isa_ranges + 0, 0);
679            of_store_32bit_in_host(isa_ranges + 4, 0xc0000000);
680            /*  I/O space: low bit if isa_phys_hi set  */
681            of_store_32bit_in_host(isa_ranges + 16, 1);
682            of_store_32bit_in_host(isa_ranges + 20, 0xd0000000);
683    
684            of_add_prop(ofd, "/isa", "ranges", isa_ranges, 32, 0);
685    }
686    
687    
688    /*
689     *  of_emul_init_adb():
690     */
691    void of_emul_init_adb(struct machine *machine)
692    {
693            struct of_data *ofd = machine->md.of_data;
694            unsigned char *adb_interrupts, *adb_reg;
695    
696            CHECK_ALLOCATION(adb_interrupts = malloc(4 * sizeof(uint32_t)));
697            CHECK_ALLOCATION(adb_reg = malloc(8 * sizeof(uint32_t)));
698    
699            of_add_device(ofd, "adb", "/bandit/gc");
700            of_add_prop_str(machine, ofd, "/bandit/gc/adb", "name", "via-cuda");
701            of_store_32bit_in_host(adb_interrupts + 0, 25);
702            of_store_32bit_in_host(adb_interrupts + 4, 0);
703            of_store_32bit_in_host(adb_interrupts + 8, 0);
704            of_store_32bit_in_host(adb_interrupts + 12, 0);
705            of_add_prop(ofd, "/bandit/gc/adb", "interrupts", adb_interrupts,
706                4*sizeof(uint32_t), 0);
707            of_store_32bit_in_host(adb_reg + 0, 0x16000);
708            of_store_32bit_in_host(adb_reg + 4, 0x2000);
709            of_store_32bit_in_host(adb_reg + 8, 0);
710            of_store_32bit_in_host(adb_reg + 12, 0);
711            of_store_32bit_in_host(adb_reg + 16, 0);
712            of_store_32bit_in_host(adb_reg + 20, 0);
713            of_store_32bit_in_host(adb_reg + 24, 0);
714            of_store_32bit_in_host(adb_reg + 28, 0);
715            of_add_prop(ofd, "/bandit/gc/adb", "reg", adb_reg,
716                8*sizeof(uint32_t), 0);
717    }
718    
719    
720    /*
721     *  of_emul_init_zs():
722     */
723    void of_emul_init_zs(struct machine *machine)
724    {
725            struct of_data *ofd = machine->md.of_data;
726            unsigned char *zs_interrupts, *zs_reg;
727    
728            CHECK_ALLOCATION(zs_reg = malloc(6 * sizeof(uint32_t)));
729    
730            /*  The controller:  */
731            of_add_device(ofd, "zs", "/bandit/gc");
732            of_add_prop_str(machine, ofd, "/bandit/gc/zs", "device_type", "serial");
733            of_add_prop_str(machine, ofd, "/bandit/gc/zs", "name", "escc");
734            of_store_32bit_in_host(zs_reg + 0, 0x13000);
735            of_store_32bit_in_host(zs_reg + 4, 0x40);
736            of_store_32bit_in_host(zs_reg + 8, 0x100);
737            of_store_32bit_in_host(zs_reg + 12, 0x100);
738            of_store_32bit_in_host(zs_reg + 16, 0x200);
739            of_store_32bit_in_host(zs_reg + 20, 0x100);
740            of_add_prop(ofd, "/bandit/gc/zs", "reg", zs_reg, 6*sizeof(uint32_t), 0);
741    
742            /*  Port 1:  */
743            CHECK_ALLOCATION(zs_interrupts = malloc(3 * sizeof(uint32_t)));
744            CHECK_ALLOCATION(zs_reg = malloc(6 * sizeof(uint32_t)));
745    
746            of_add_device(ofd, "zstty1", "/bandit/gc/zs");
747            of_add_prop_str(machine, ofd, "/bandit/gc/zs/zstty1", "name", "ch-a");
748            of_store_32bit_in_host(zs_interrupts + 0, 16);
749            of_store_32bit_in_host(zs_interrupts + 4, 0);
750            of_store_32bit_in_host(zs_interrupts + 8, 0);
751            of_add_prop(ofd, "/bandit/gc/zs/zstty1", "interrupts", zs_interrupts,
752                3*sizeof(uint32_t), 0);
753            of_store_32bit_in_host(zs_reg + 0, 0x13800);
754            of_store_32bit_in_host(zs_reg + 4, 0x100);
755            of_store_32bit_in_host(zs_reg + 8, 0x100);
756            of_store_32bit_in_host(zs_reg + 12, 0x100);
757            of_store_32bit_in_host(zs_reg + 16, 0x200);
758            of_store_32bit_in_host(zs_reg + 20, 0x100);
759            of_add_prop(ofd, "/bandit/gc/zs/zstty1",
760                "reg", zs_reg, 6*sizeof(uint32_t), 0);
761    
762            /*  Port 0:  */
763            CHECK_ALLOCATION(zs_interrupts = malloc(3 * sizeof(uint32_t)));
764            CHECK_ALLOCATION(zs_reg = malloc(6 * sizeof(uint32_t)));
765    
766            of_add_device(ofd, "zstty0", "/bandit/gc/zs");
767            of_add_prop_str(machine, ofd, "/bandit/gc/zs/zstty0", "name", "ch-b");
768            of_store_32bit_in_host(zs_interrupts + 0, 15);
769            of_store_32bit_in_host(zs_interrupts + 4, 0);
770            of_store_32bit_in_host(zs_interrupts + 8, 0);
771            of_add_prop(ofd, "/bandit/gc/zs/zstty0", "interrupts", zs_interrupts,
772                3*sizeof(uint32_t), 0);
773            of_store_32bit_in_host(zs_reg + 0, 0x13400);
774            of_store_32bit_in_host(zs_reg + 4, 0x100);
775            of_store_32bit_in_host(zs_reg + 8, 0x100);
776            of_store_32bit_in_host(zs_reg + 12, 0x100);
777            of_store_32bit_in_host(zs_reg + 16, 0x200);
778            of_store_32bit_in_host(zs_reg + 20, 0x100);
779            of_add_prop(ofd, "/bandit/gc/zs/zstty0",
780                "reg", zs_reg, 6*sizeof(uint32_t), 0);
781    }
782    
783    
784    /*
785     *  of_emul_init_uninorth():
786     */
787    void of_emul_init_uninorth(struct machine *machine)
788    {
789            struct of_data *ofd = machine->md.of_data;
790            unsigned char *uninorth_reg, *uninorth_bus_range, *uninorth_ranges;
791            unsigned char *macio_aa, *ata_interrupts, *ata_reg;
792            struct of_device *ic;
793            char *n = "pci@e2000000";
794            char *macio = "mac-io";
795    
796            of_add_device(ofd, n, "/");
797            of_add_prop_str(machine, ofd, n, "name", "pci");
798            of_add_prop_str(machine, ofd, n, "device_type", "pci");
799            of_add_prop_str(machine, ofd, n, "compatible", "uni-north");
800    
801            CHECK_ALLOCATION(uninorth_reg = malloc(2 * sizeof(uint32_t)));
802            CHECK_ALLOCATION(uninorth_bus_range = malloc(2 * sizeof(uint32_t)));
803            CHECK_ALLOCATION(uninorth_ranges = malloc(12 * sizeof(uint32_t)));
804            CHECK_ALLOCATION(macio_aa = malloc(5 * sizeof(uint32_t)));
805            CHECK_ALLOCATION(ata_interrupts = malloc(6 * sizeof(uint32_t)));
806            CHECK_ALLOCATION(ata_reg = malloc(8 * sizeof(uint32_t)));
807    
808            of_store_32bit_in_host(uninorth_reg + 0, 0xe2000000);
809            of_store_32bit_in_host(uninorth_reg + 4, 0);    /*  not used?  */
810            of_add_prop(ofd, n, "reg", uninorth_reg, 2*sizeof(uint32_t), 0);
811    
812            of_store_32bit_in_host(uninorth_bus_range + 0, 0);
813            of_store_32bit_in_host(uninorth_bus_range + 4, 0);
814            of_add_prop(ofd, n, "bus-range", uninorth_bus_range,
815                2*sizeof(uint32_t), 0);
816    
817            /*  MEM:  */
818            of_store_32bit_in_host(uninorth_ranges + 0, 0x02000000);
819            of_store_32bit_in_host(uninorth_ranges + 4, 0);
820            of_store_32bit_in_host(uninorth_ranges + 8, 0);
821            of_store_32bit_in_host(uninorth_ranges + 12, 0xd0000000);
822            of_store_32bit_in_host(uninorth_ranges + 16, 0);
823            of_store_32bit_in_host(uninorth_ranges + 20, 0x04000000);
824            /*  IO:  */
825            of_store_32bit_in_host(uninorth_ranges + 24, 0x01000000);
826            of_store_32bit_in_host(uninorth_ranges + 28, 0);
827            of_store_32bit_in_host(uninorth_ranges + 32, 0);
828            of_store_32bit_in_host(uninorth_ranges + 36, 0xe2000000);
829            of_store_32bit_in_host(uninorth_ranges + 40, 0);
830            of_store_32bit_in_host(uninorth_ranges + 44, 0x01000000);
831            of_add_prop(ofd, n, "ranges", uninorth_ranges,
832                12*sizeof(uint32_t), 0);
833    
834            ic = of_add_device(ofd, macio, "/");
835            memset(macio_aa, 0, 20);
836            of_store_32bit_in_host(macio_aa + 0, 15 << 11); /* pci tag */
837            of_store_32bit_in_host(macio_aa + 8, 0xf3000000);
838            of_add_prop(ofd, macio, "assigned-addresses", macio_aa,
839                5*sizeof(uint32_t), 0);
840    /*      of_add_prop(ofd, n, "assigned-addresses", macio_aa,
841                5*sizeof(uint32_t), 0); */
842            of_add_prop_int32(ofd, "/chosen", "interrupt-controller", ic->handle);
843    
844            of_add_device(ofd, "bandit", "/");
845            of_add_device(ofd, "gc", "/bandit");
846            of_add_prop(ofd, "/bandit/gc", "assigned-addresses", macio_aa,
847                5*sizeof(uint32_t), 0);
848    
849            if (diskimage_exist(machine, 0, DISKIMAGE_IDE) ||
850                diskimage_exist(machine, 1, DISKIMAGE_IDE)) {
851                    char tmpstr[400];
852                    of_add_device(ofd, "ata", "/bandit/gc");
853                    of_add_prop_str(machine, ofd, "/bandit/gc/ata", "name", "ata");
854                    of_add_prop_str(machine, ofd, "/bandit/gc/ata", "compatible",
855                        "heathrow-ata");
856                    of_store_32bit_in_host(ata_interrupts + 0, 13);
857                    of_store_32bit_in_host(ata_interrupts + 4, 0);
858                    of_store_32bit_in_host(ata_interrupts + 8, 0);
859                    of_store_32bit_in_host(ata_interrupts + 12, 0);
860                    of_store_32bit_in_host(ata_interrupts + 16, 0);
861                    of_store_32bit_in_host(ata_interrupts + 20, 0);
862                    of_add_prop(ofd, "/bandit/gc/ata", "interrupts", ata_interrupts,
863                        6*sizeof(uint32_t), 0);
864                    of_store_32bit_in_host(ata_reg + 0, 0x20000);
865                    of_store_32bit_in_host(ata_reg + 4, 0);
866                    of_store_32bit_in_host(ata_reg + 8, 0x21000);
867                    of_store_32bit_in_host(ata_reg + 12, 0x22000);
868                    of_store_32bit_in_host(ata_reg + 16, 0);
869                    of_store_32bit_in_host(ata_reg + 20, 0);
870                    of_store_32bit_in_host(ata_reg + 24, 0);
871                    of_store_32bit_in_host(ata_reg + 28, 0);
872                    of_add_prop(ofd, "/bandit/gc/ata", "reg", ata_reg,
873                        8*sizeof(uint32_t), 0);
874    
875                    snprintf(tmpstr, sizeof(tmpstr), "wdc addr=0xf3020000 "
876                        "irq=%s.cpu[%i].gc.lo.21 addr_mult=0x10", machine->path,
877                        machine->bootstrap_cpu);
878                    device_add(machine, tmpstr);
879            }
880    }
881    
882    
883    /*
884     *  of_emul_init():
885     *
886     *  This function creates an OpenFirmware emulation instance.
887     */
888    struct of_data *of_emul_init(struct machine *machine, struct vfb_data *vfb_data,
889            uint64_t fb_addr, int fb_xsize, int fb_ysize)
890    {
891            unsigned char *memory_reg, *memory_av;
892            unsigned char *zs_assigned_addresses;
893            struct of_device *mmu, *devstdout, *devstdin;
894            struct of_data *ofd;
895            int i;
896    
897            CHECK_ALLOCATION(ofd = malloc(sizeof(struct of_data)));
898            memset(ofd, 0, sizeof(struct of_data));
899    
900            ofd->vfb_data = vfb_data;
901    
902            /*  Devices:  */
903    
904            /*  Root = device 1  */
905            of_add_device(ofd, "", "");
906    
907            of_add_device(ofd, "io", "/");
908            devstdin  = of_add_device(ofd, "stdin", "/io");
909            devstdout = of_add_device(ofd, "stdout", "/io");
910    
911            if (machine->x11_md.in_use) {
912                    fatal("!\n!  TODO: keyboard + framebuffer for MacPPC\n!\n");
913    
914                    of_add_prop_str(machine, ofd, "/io/stdin", "name",
915                        "keyboard");
916                    of_add_prop_str(machine, ofd, "/io", "name", "adb");
917    
918                    of_add_prop_str(machine, ofd, "/io/stdout", "device_type",
919                        "display");
920                    of_add_prop_int32(ofd, "/io/stdout", "width", fb_xsize);
921                    of_add_prop_int32(ofd, "/io/stdout", "height", fb_ysize);
922                    of_add_prop_int32(ofd, "/io/stdout", "linebytes", fb_xsize * 1);
923                    of_add_prop_int32(ofd, "/io/stdout", "depth", 8);
924                    of_add_prop_int32(ofd, "/io/stdout", "address", fb_addr);
925            } else {
926                    CHECK_ALLOCATION(zs_assigned_addresses = malloc(12));
927                    memset(zs_assigned_addresses, 0, 12);
928    
929                    of_add_prop_str(machine, ofd, "/io/stdin", "name", "ch-b");
930                    of_add_prop_str(machine, ofd, "/io/stdin", "device_type",
931                        "serial");
932                    of_add_prop_int32(ofd, "/io/stdin", "reg", 0xf3013000);
933                    of_add_prop(ofd, "/io/stdin", "assigned-addresses",
934                        zs_assigned_addresses, 12, 0);
935    
936                    of_add_prop_str(machine, ofd, "/io/stdout", "device_type",
937                        "serial");
938            }
939    
940            of_add_device(ofd, "cpus", "/");
941            for (i=0; i<machine->ncpus; i++) {
942                    char tmp[50];
943                    snprintf(tmp, sizeof(tmp), "@%x", i);
944                    of_add_device(ofd, tmp, "/cpus");
945                    snprintf(tmp, sizeof(tmp), "/cpus/@%x", i);
946                    of_add_prop_str(machine, ofd, tmp, "device_type", "cpu");
947                    of_add_prop_int32(ofd, tmp, "timebase-frequency",
948                        machine->emulated_hz / 4);
949                    of_add_prop_int32(ofd, tmp, "clock-frequency",
950                        machine->emulated_hz);
951                    of_add_prop_int32(ofd, tmp, "reg", i);
952            }
953    
954            mmu = of_add_device(ofd, "mmu", "/");
955    
956            /*  TODO:  */
957            of_add_prop(ofd, "/mmu", "translations", NULL, 0, 0);
958    
959            of_add_device(ofd, "chosen", "/");
960            of_add_prop_int32(ofd, "/chosen", "mmu", mmu->handle);
961            of_add_prop_int32(ofd, "/chosen", "stdin", devstdin->handle);
962            of_add_prop_int32(ofd, "/chosen", "stdout", devstdout->handle);
963    
964            of_add_device(ofd, "memory", "/");
965            CHECK_ALLOCATION(memory_reg = malloc(2 * sizeof(uint32_t)));
966            CHECK_ALLOCATION(memory_av = malloc(2 * sizeof(uint32_t)));
967    
968            of_store_32bit_in_host(memory_reg + 0, 0);
969            of_store_32bit_in_host(memory_reg + 4, machine->physical_ram_in_mb<<20);
970            of_store_32bit_in_host(memory_av + 0, 10 << 20);
971            of_store_32bit_in_host(memory_av + 4,
972                (machine->physical_ram_in_mb - 10) << 20);
973            of_add_prop(ofd, "/memory", "reg", memory_reg, 2 * sizeof(uint32_t), 0);
974            of_add_prop(ofd, "/memory", "available",memory_av,2*sizeof(uint32_t),0);
975            of_add_prop_str(machine, ofd, "/memory","device_type","memory"/*?*/);
976    
977            /*  Services:  */
978            of_add_service(ofd, "call-method", of__call_method_2_2, 2, 2);
979            of_add_service(ofd, "call-method", of__call_method_3_4, 3, 4);
980            of_add_service(ofd, "call-method", of__call_method_5_2, 5, 2);
981            of_add_service(ofd, "call-method", of__call_method_6_1, 6, 1);
982            of_add_service(ofd, "call-method", of__call_method_6_2, 6, 2);
983            of_add_service(ofd, "child", of__child, 1, 1);
984            of_add_service(ofd, "exit", of__exit, 0, 0);
985            of_add_service(ofd, "finddevice", of__finddevice, 1, 1);
986            of_add_service(ofd, "getprop", of__getprop, 4, 1);
987            of_add_service(ofd, "getproplen", of__getproplen, 2, 1);
988            of_add_service(ofd, "instance-to-package",
989                of__instance_to_package, 1, 1);
990            of_add_service(ofd, "interpret", of__interpret_1, 1, 1);
991            of_add_service(ofd, "interpret", of__interpret_2, 1, 2);
992            of_add_service(ofd, "package-to-path", of__package_to_path, 3, 1);
993            of_add_service(ofd, "parent", of__parent, 1, 1);
994            of_add_service(ofd, "peer", of__peer, 1, 1);
995            of_add_service(ofd, "read", of__read, 3, 1);
996            of_add_service(ofd, "write", of__write, 3, 1);
997    
998            if (verbose >= 2)
999                    of_dump_all(ofd);
1000    
1001            machine->md.of_data = ofd;
1002    
1003            return ofd;
1004    }
1005    
1006    
1007    /*
1008   *  of_emul():   *  of_emul():
1009   *   *
1010   *  OpenFirmware call emulation.   *  OpenFirmware call emulation.
1011   */   */
1012  int of_emul(struct cpu *cpu)  int of_emul(struct cpu *cpu)
1013  {  {
1014          int i, nargs, nret, ofs, handle, retval;          int i, nargs, nret, ofs, retval = 0;
1015          char service[50];          char service[50];
1016          char arg[N_MAX_ARGS][ARG_MAX_LEN];          char *arg[OF_N_MAX_ARGS];
         char tmpstr[ARG_MAX_LEN];  
1017          uint64_t base, ptr;          uint64_t base, ptr;
1018          uint64_t buf, buflen;          struct of_service *os;
1019            struct of_data *of_data = cpu->machine->md.of_data;
1020    
1021            if (of_data == NULL) {
1022                    fatal("of_emul(): no of_data struct?\n");
1023                    exit(1);
1024            }
1025    
1026          /*          /*
1027           *  The first argument register points to "prom_args":           *  The first argument register points to "prom_args":
# Line 115  int of_emul(struct cpu *cpu) Line 1039  int of_emul(struct cpu *cpu)
1039          case ARCH_PPC:          case ARCH_PPC:
1040                  base = cpu->cd.ppc.gpr[3];                  base = cpu->cd.ppc.gpr[3];
1041                  break;                  break;
1042          default:          default:fatal("of_emul(): unimplemented arch (TODO)\n");
                 fatal("of_emul(): TODO: unimplemented arch\n");  
1043                  exit(1);                  exit(1);
1044          }          }
1045    
# Line 130  int of_emul(struct cpu *cpu) Line 1053  int of_emul(struct cpu *cpu)
1053          debug("[ of: %s(", service);          debug("[ of: %s(", service);
1054          ofs = 12;          ofs = 12;
1055          for (i=0; i<nargs; i++) {          for (i=0; i<nargs; i++) {
1056                    int x;
1057                  if (i > 0)                  if (i > 0)
1058                          debug(", ");                          debug(", ");
1059                  if (i >= N_MAX_ARGS) {                  if (i >= OF_N_MAX_ARGS) {
1060                          fatal("TOO MANY ARGS!");                          fatal("TOO MANY ARGS!");
1061                          continue;                          continue;
1062                  }                  }
1063    
1064                  ptr = load_32bit_word(cpu, base + ofs);                  ptr = load_32bit_word(cpu, base + ofs);
1065                  readstr(cpu, ptr, arg[i], ARG_MAX_LEN);  
1066                  if (arg[i][0])                  CHECK_ALLOCATION(arg[i] = malloc(OF_ARG_MAX_LEN + 1));
1067                          debug("\"%s\"", arg[i]);                  memset(arg[i], 0, OF_ARG_MAX_LEN + 1);
1068                  else {  
1069                          int x = ptr;                  x = ptr;
1070                          if (x > -256 && x < 256)                  if (x > -256 && x < 256) {
1071                                  debug("%i", x);                          debug("%i", x);
1072                    } else {
1073                            readstr(cpu, ptr, arg[i], OF_ARG_MAX_LEN);
1074                            if (arg[i][0])
1075                                    debug("\"%s\"", arg[i]);
1076                          else                          else
1077                                  debug("0x%x", x);                                  debug("0x%x", x);
1078                  }                  }
# Line 151  int of_emul(struct cpu *cpu) Line 1080  int of_emul(struct cpu *cpu)
1080          }          }
1081          debug(") ]\n");          debug(") ]\n");
1082    
         /*  Return value:  */  
         retval = 0;  
   
1083          /*  Note: base + ofs points to the first return slot.  */          /*  Note: base + ofs points to the first return slot.  */
1084    
1085          if (strcmp(service, "exit") == 0) {          os = of_data->of_services;
1086                  cpu->running = 0;          while (os != NULL) {
1087          } else if (strcmp(service, "finddevice") == 0) {                  if (strcmp(service, os->name) == 0 &&
1088                  /*  Return a handle in ret[0]:  */                      nargs == os->n_args && nret == os->n_ret_args) {
1089                  if (nret < 1) {                          retval = os->f(cpu, arg, base, ofs);
                         fatal("[ of: finddevice(\"%s\"): nret < 1! ]\n",  
                             arg[0]);  
                 } else if (strcmp(arg[0], "/memory") == 0) {      
                         store_32bit_word(cpu, base + ofs, HANDLE_MEMORY);  
                 } else if (strcmp(arg[0], "/chosen") == 0) {      
                         store_32bit_word(cpu, base + ofs, HANDLE_CHOSEN);  
                 } else {  
                         /*  Device not found.  */  
                         fatal("[ of: finddevice(\"%s\"): not yet"  
                             " implemented ]\n", arg[0]);  
                         retval = -1;  
                 }  
         } else if (strcmp(service, "getprop") == 0) {  
                 handle = load_32bit_word(cpu, base + 12 + 4*0);  
                 ptr    = load_32bit_word(cpu, base + 12 + 4*1);  
                 buf    = load_32bit_word(cpu, base + 12 + 4*2);  
                 buflen = load_32bit_word(cpu, base + 12 + 4*3);  
                 readstr(cpu, ptr, tmpstr, sizeof(tmpstr));  
   
                 /*  TODO: rewrite this  */  
                 switch (handle) {  
                 case HANDLE_MEMORY:  
                         if (strcmp(tmpstr, "available") == 0) {  
                                 store_32bit_word(cpu, base + ofs, 2*8);  
                                 /*  TODO.  {start, size}  */  
                                 store_32bit_word(cpu, buf, 0);  
                                 store_32bit_word(cpu, buf+4,  
                                     cpu->machine->physical_ram_in_mb * 1048576  
                                     - 65536);  
                                 store_32bit_word(cpu, buf+8, 0);  
                                 store_32bit_word(cpu, buf+12, 0);  
                         } else if (strcmp(tmpstr, "reg") == 0) {  
                                 /*  TODO  */  
                                 store_32bit_word(cpu, base + ofs, 33*8);  
                                 store_32bit_word(cpu, buf, 0);  
                                 store_32bit_word(cpu, buf+4,  
                                     cpu->machine->physical_ram_in_mb * 1048576);  
                                 store_32bit_word(cpu, buf+8, 0);  
                                 store_32bit_word(cpu, buf+12, 0);  
                         } else {  
                                 fatal("[ of: getprop(%i,\"%s\"): not yet"  
                                     " implemented ]\n", (int)handle, arg[1]);  
                                 retval = -1;  
                         }  
                         break;  
                 case HANDLE_CHOSEN:  
                         if (strcmp(tmpstr, "stdin") == 0) {  
                                 if (buflen >= 4)  
                                         store_32bit_word(cpu, buf,  
                                             HANDLE_STDIN);  
                                 store_32bit_word(cpu, base + ofs, 4);  
                         } else if (strcmp(tmpstr, "stdout") == 0) {  
                                 if (buflen >= 4)  
                                         store_32bit_word(cpu, buf,  
                                             HANDLE_STDOUT);  
                                 store_32bit_word(cpu, base + ofs, 4);  
                         } else if (strcmp(tmpstr, "mmu") == 0) {  
                                 if (buflen >= 4)  
                                         store_32bit_word(cpu, buf,  
                                             HANDLE_MMU);  
                                 store_32bit_word(cpu, base + ofs, 4);  
                         } else {  
                                 fatal("[ of: getprop(%i,\"%s\"): not yet"  
                                     " implemented ]\n", (int)handle, arg[1]);  
                                 retval = -1;  
                         }  
1090                          break;                          break;
                 default:  
                         fatal("[ of: getprop(%i,\"%s\"): not yet"  
                             " implemented ]\n", (int)handle, arg[1]);  
                         retval = -1;  
1091                  }                  }
1092          } else if (strcmp(service, "instance-to-package") == 0) {                  os = os->next;
1093                  /*  TODO: a package handle  */          }
1094                  store_32bit_word(cpu, base + ofs, 1000);  
1095          } else if (strcmp(service, "getproplen") == 0) {          if (os == NULL) {
                 /*  TODO  */  
                 store_32bit_word(cpu, base + ofs, 0);  
         } else if (strcmp(service, "peer") == 0) {  
                 /*  TODO  */  
                 store_32bit_word(cpu, base + ofs, 0);  
         } else {  
1096                  quiet_mode = 0;                  quiet_mode = 0;
1097                  cpu_register_dump(cpu->machine, cpu, 1, 0);                  cpu_register_dump(cpu->machine, cpu, 1, 0);
1098                  printf("\n");                  printf("\n");
1099                  fatal("[ of_emul(): unimplemented service \"%s\" ]\n", service);                  fatal("[ of: unimplemented service \"%s\" with %i input "
1100                        "args and %i output values ]\n", service, nargs, nret);
1101                  cpu->running = 0;                  cpu->running = 0;
                 cpu->dead = 1;  
1102          }          }
1103    
1104            for (i=0; i<nargs; i++)
1105                    free(arg[i]);
1106    
1107            /*  Return:  */
1108          switch (cpu->machine->arch) {          switch (cpu->machine->arch) {
1109          case ARCH_ARM:          case ARCH_ARM:
1110                  cpu->cd.arm.r[0] = retval;                  cpu->cd.arm.r[0] = retval;
# Line 258  int of_emul(struct cpu *cpu) Line 1112  int of_emul(struct cpu *cpu)
1112          case ARCH_PPC:          case ARCH_PPC:
1113                  cpu->cd.ppc.gpr[3] = retval;                  cpu->cd.ppc.gpr[3] = retval;
1114                  break;                  break;
1115          default:          default:fatal("of_emul(): TODO: unimplemented arch (Retval)\n");
                 fatal("of_emul(): TODO: unimplemented arch (Retval)\n");  
1116                  exit(1);                  exit(1);
1117          }          }
1118    

Legend:
Removed from v.14  
changed lines
  Added in v.42

  ViewVC Help
Powered by ViewVC 1.1.26