qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 04/14] pc: support NEC PC-9821 family


From: Alexander Graf
Subject: Re: [Qemu-devel] [PATCH 04/14] pc: support NEC PC-9821 family
Date: Thu, 10 Sep 2009 08:59:02 +0200


On 09.09.2009, at 17:41, 武田 俊也 wrote:

This patch is to add NEC PC-9821 family initialization.

diff -ur a/hw/pc.c b/hw/pc.c
--- a/hw/pc.c   Tue Sep  8 21:26:50 2009
+++ b/hw/pc.c   Wed Sep  9 21:52:05 2009
@@ -1462,6 +1462,385 @@
        rtc_set_memory(rtc_state, 0xF, 0xFE);
}

+/* NEC PC-98x1 */
+
+#define PC98_BIOS_FILENAME "pc98bios.bin"
+#define PC98_BIOS_FILESIZE 0x18000
+#define PC98_ITF_FILENAME "pc98itf.bin"
+#define PC98_ITF_FILESIZE 0x8000
+#define PC98_IDE_FILENAME "pc98ide.bin"
+#define PC98_IDE_FILESIZE 0x2000

Why do you need the file sizes? Can't you get them from the sizes you actually read, just like VGA does?

+
+static ram_addr_t pc98_ram_addr[512];
+static ram_addr_t pc98_bios_addr;
+static ram_addr_t pc98_itf_addr;
+static ram_addr_t pc98_ide_addr;
+
+static uint8_t pc98_bios_ram_selected;
+static uint8_t pc98_itf_selected;
+static uint8_t pc98_ram_window_map;
+
+static void pc98_reset(void *opaque)
+{
+    if (pc98_bios_ram_selected || !pc98_itf_selected) {
+ cpu_register_physical_memory(0x000d8000, 0x2000, pc98_ide_addr | IO_MEM_ROM); + cpu_register_physical_memory(0x000f8000, 0x8000, pc98_itf_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xfffd8000, 0x2000, pc98_ide_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xffff8000, 0x8000, pc98_itf_addr | IO_MEM_ROM);
+        pc98_bios_ram_selected = 0;
+        pc98_itf_selected = 1;
+    }
+    if (pc98_ram_window_map != 0x08) {
+ cpu_register_physical_memory(0x00080000, 0x8000, pc98_ram_addr[16]); + cpu_register_physical_memory(0x00088000, 0x8000, pc98_ram_addr[17]); + cpu_register_physical_memory(0x00090000, 0x8000, pc98_ram_addr[18]); + cpu_register_physical_memory(0x00098000, 0x8000, pc98_ram_addr[19]); + cpu_register_physical_memory(0xfff80000, 0x8000, pc98_ram_addr[20]); + cpu_register_physical_memory(0xfff88000, 0x8000, pc98_ram_addr[21]); + cpu_register_physical_memory(0xfff90000, 0x8000, pc98_ram_addr[22]); + cpu_register_physical_memory(0xfff98000, 0x8000, pc98_ram_addr[23]);
+        pc98_ram_window_map = 0x08;
+    }
+}
+
+static void pc98_ioportF0_write(void *opaque, uint32_t addr, uint32_t data)

I guess it's an unwritten rule not to use capital letters in function (and variable) names. It definitely looks awkward and Windows-stylish.

static void pc98_ioport_f0_write(...) looks better :-). I've seen that in several places, sometime later with DMA too. The comment applies to all those.

Looking through more code, it looks like this capital letter style is there quite a lot and takeda-san has only copied the style that was there already. So it might as well be good to keep it as is.

Any code stylists around? :-)

+{
+    qemu_cpu_reset_request();
+}
+
+static void pc98_ioportF2_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    ioport_set_a20(1);
+}
+
+static uint32_t pc98_ioportF2_read(void *opaque, uint32_t addr)
+{
+    return (ioport_get_a20() ^ 1) | 0x2e;
+}
+
+static void pc98_ioportF6_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    switch (data) {
+    case 0x02:
+        ioport_set_a20(1);
+        break;
+    case 0x03:
+        ioport_set_a20(0);
+        break;
+    }
+}
+
+static uint32_t pc98_ioportF6_read(void *opaque, uint32_t addr)
+{
+    return (ioport_get_a20() ^ 1) | 0x5e;
+}
+
+static void pc98_ioport43D_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    switch (data) {
+    case 0x00:
+    case 0x10:
+        if (!pc98_itf_selected) {
+ cpu_register_physical_memory(0x000f8000, 0x8000, pc98_itf_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xffff8000, 0x8000, pc98_itf_addr | IO_MEM_ROM);
+            pc98_itf_selected = 1;
+        }
+        break;
+    case 0x02:
+    case 0x12:
+        if (pc98_itf_selected) {
+            if (pc98_bios_ram_selected) {
+ cpu_register_physical_memory(0x000e8000, 0x8000, pc98_ram_addr[0xe8000 >> 15]); + cpu_register_physical_memory(0x000f0000, 0x8000, pc98_ram_addr[0xf0000 >> 15]); + cpu_register_physical_memory(0x000f8000, 0x8000, pc98_ram_addr[0xf8000 >> 15]); + cpu_register_physical_memory(0xfffe8000, 0x8000, pc98_ram_addr[0xe8000 >> 15]); + cpu_register_physical_memory(0xffff0000, 0x8000, pc98_ram_addr[0xf0000 >> 15]); + cpu_register_physical_memory(0xffff8000, 0x8000, pc98_ram_addr[0xf8000 >> 15]);
+            } else {
+ cpu_register_physical_memory(0x000e8000, 0x18000, pc98_bios_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xfffe8000, 0x18000, pc98_bios_addr | IO_MEM_ROM);
+            }
+            pc98_itf_selected = 0;
+        }
+        break;
+    }
+}
+
+static void pc98_ioport461_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    if ((pc98_ram_window_map & 0xfe) != (data & 0xfe)) {
+        uint32_t bank = (data & 0xfe) << 1;
+ cpu_register_physical_memory(0x00080000, 0x8000, pc98_ram_addr[bank + 0]); + cpu_register_physical_memory(0x00088000, 0x8000, pc98_ram_addr[bank + 1]); + cpu_register_physical_memory(0x00090000, 0x8000, pc98_ram_addr[bank + 2]); + cpu_register_physical_memory(0x00098000, 0x8000, pc98_ram_addr[bank + 3]); + cpu_register_physical_memory(0xfff80000, 0x8000, pc98_ram_addr[bank + 0]); + cpu_register_physical_memory(0xfff88000, 0x8000, pc98_ram_addr[bank + 1]); + cpu_register_physical_memory(0xfff90000, 0x8000, pc98_ram_addr[bank + 2]); + cpu_register_physical_memory(0xfff98000, 0x8000, pc98_ram_addr[bank + 3]);
+        pc98_ram_window_map = data;
+    }
+}
+
+static uint32_t pc98_ioport461_read(void *opaque, uint32_t addr)
+{
+    return pc98_ram_window_map;
+}
+
+static uint32_t pc98_ioport534_read(void *opaque, uint32_t addr)
+{
+    return 0xec;
+}
+
+static void pc98_ioport53D_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    if (!pc98_bios_ram_selected && (data & 0x02)) {
+ cpu_register_physical_memory(0x000c0000, 0x8000, pc98_ram_addr[0xc0000 >> 15]); + cpu_register_physical_memory(0x000c8000, 0x8000, pc98_ram_addr[0xc8000 >> 15]); + cpu_register_physical_memory(0x000d0000, 0x8000, pc98_ram_addr[0xd0000 >> 15]); + cpu_register_physical_memory(0x000d8000, 0x8000, pc98_ram_addr[0xd8000 >> 15]); + cpu_register_physical_memory(0xfffc0000, 0x8000, pc98_ram_addr[0xc0000 >> 15]); + cpu_register_physical_memory(0xfffc8000, 0x8000, pc98_ram_addr[0xc8000 >> 15]); + cpu_register_physical_memory(0xfffd0000, 0x8000, pc98_ram_addr[0xd0000 >> 15]); + cpu_register_physical_memory(0xfffd8000, 0x8000, pc98_ram_addr[0xd8000 >> 15]);
+        if (!pc98_itf_selected) {
+ cpu_register_physical_memory(0x000e8000, 0x8000, pc98_ram_addr[0xe8000 >> 15]); + cpu_register_physical_memory(0x000f0000, 0x8000, pc98_ram_addr[0xf0000 >> 15]); + cpu_register_physical_memory(0x000f8000, 0x8000, pc98_ram_addr[0xf8000 >> 15]); + cpu_register_physical_memory(0xfffe8000, 0x8000, pc98_ram_addr[0xe8000 >> 15]); + cpu_register_physical_memory(0xffff0000, 0x8000, pc98_ram_addr[0xf0000 >> 15]); + cpu_register_physical_memory(0xffff8000, 0x8000, pc98_ram_addr[0xf8000 >> 15]);
+        }
+        pc98_bios_ram_selected = 1;
+    } else if (pc98_bios_ram_selected && !(data & 0x02)) {
+ cpu_register_physical_memory(0x000d8000, 0x2000, pc98_ide_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xfffd8000, 0x2000, pc98_ide_addr | IO_MEM_ROM);
+        if (!pc98_itf_selected) {
+ cpu_register_physical_memory(0x000e8000, 0x18000, pc98_bios_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xfffe8000, 0x18000, pc98_bios_addr | IO_MEM_ROM);
+        }
+        pc98_bios_ram_selected = 0;
+    }
+}
+
+static uint32_t pc98_ioport63D_read(void *opaque, uint32_t addr)
+{
+    return 0x04;
+}
+
+static uint32_t pc98_ioport9894_read(void *opaque, uint32_t addr)
+{
+    return 0x90;
+}
+
+static void pc98_init1(ram_addr_t ram_size,
+                       const char *cpu_model,
+                       int pci_enabled)
+{
+    char cpu_model_opt[64];
+    char *filename;
+    int ret, i;
+    uint32_t addr;
+    PCIBus *pci_bus;
+    /*ISADevice *isa_dev;*/
+    int piix3_devfn = -1;
+    CPUState *env = NULL;
+    qemu_irq *cpu_irq;
+    qemu_irq *isa_irq;
+    qemu_irq *i8259;
+    IsaIrqState *isa_irq_state;
+    DriveInfo *dinfo;
+    DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+    BlockDriverState *fd[MAX_FD];
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+#ifdef TARGET_X86_64
+        cpu_model = "qemu64";
+#else
+        cpu_model = "qemu32";
+#endif
+    }
+    sprintf(cpu_model_opt, "%s,+a20mask", cpu_model);
+
+    for (i = 0; i < smp_cpus; i++) {
+        env = pc_new_cpu(cpu_model_opt);
+    }
+
+    /* allocate RAM */
+    for (i = 0; i < 512; i++) {
+        pc98_ram_addr[i] = qemu_ram_alloc(0x8000);
+    }
+    for (addr = 0; addr < 0xa0000; addr += 0x8000) {
+ cpu_register_physical_memory(0x00000000 | addr, 0x8000, pc98_ram_addr[addr >> 15]); + cpu_register_physical_memory(0xfff00000 | addr, 0x8000, pc98_ram_addr[addr >> 15]);
+    }
+    for (addr = 0x100000; addr < 0x1000000; addr += 0x8000) {
+ cpu_register_physical_memory(0x00000000 | addr, 0x8000, pc98_ram_addr[addr >> 15]);
+    }
+    if (ram_size > 0xf00000) {
+        ram_addr_t ram_addr = qemu_ram_alloc(ram_size - 0xf00000);
+ cpu_register_physical_memory(0x1000000, ram_size - 0xf00000, ram_addr);
+    }
+
+    /* BIOS load */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PC98_BIOS_FILENAME);
+    pc98_bios_addr = qemu_ram_alloc(PC98_BIOS_FILESIZE);
+    ret = load_image(filename, qemu_get_ram_ptr(pc98_bios_addr));
+    if (ret != PC98_BIOS_FILESIZE) {
+ fprintf(stderr, "qemu: could not load PC98 BIOS '%s'\n", filename);
+        exit(1);
+    }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PC98_ITF_FILENAME);
+    pc98_itf_addr = qemu_ram_alloc(PC98_ITF_FILESIZE);
+    ret = load_image(filename, qemu_get_ram_ptr(pc98_itf_addr));
+    if (ret != PC98_ITF_FILESIZE) {
+ fprintf(stderr, "qemu: could not load PC98 ITF '%s'\n", filename);
+        exit(1);
+    }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PC98_IDE_FILENAME);
+    pc98_ide_addr = qemu_ram_alloc(PC98_IDE_FILESIZE);
+    ret = load_image(filename, qemu_get_ram_ptr(pc98_ide_addr));
+    if (ret != PC98_IDE_FILESIZE) {
+ fprintf(stderr, "qemu: could not load PC98 IDE BIOS '%s'\n", filename);
+        exit(1);
+    }
+
+ cpu_register_physical_memory(0x000d8000, 0x2000, pc98_ide_addr | IO_MEM_ROM); + cpu_register_physical_memory(0x000f8000, 0x8000, pc98_itf_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xfffd8000, 0x2000, pc98_ide_addr | IO_MEM_ROM); + cpu_register_physical_memory(0xffff8000, 0x8000, pc98_itf_addr | IO_MEM_ROM);
+
+    pc98_bios_ram_selected = 0;
+    pc98_itf_selected = 1;
+    pc98_ram_window_map = 0x08;
+
+    cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
+    i8259 = pc98_i8259_init(cpu_irq[0]);
+    isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
+    isa_irq_state->i8259 = i8259;
+    isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
+
+    if (pci_enabled) {
+        pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq);
+    } else {
+        pci_bus = NULL;
+        isa_bus_new(NULL);
+    }
+    isa_bus_irqs(isa_irq);
+
+    ferr_irq = isa_reserve_irq(8);
+
+    /* init basic PC hardware */
+    register_ioport_write(0xf0, 1, 1, pc98_ioportF0_write, env);
+    register_ioport_write(0xf2, 1, 1, pc98_ioportF2_write, env);
+    register_ioport_read(0xf2, 1, 1, pc98_ioportF2_read, env);
+    register_ioport_write(0xf6, 1, 1, pc98_ioportF6_write, env);
+    register_ioport_read(0xf6, 1, 1, pc98_ioportF6_read, env);
+    register_ioport_write(0x43d, 1, 1, pc98_ioport43D_write, env);
+    register_ioport_write(0x461, 1, 1, pc98_ioport461_write, env);
+    register_ioport_read(0x461, 1, 1, pc98_ioport461_read, env);
+    register_ioport_read(0x534, 1, 1, pc98_ioport534_read, env);
+    register_ioport_write(0x53d, 1, 1, pc98_ioport53D_write, env);
+    register_ioport_read(0x63d, 1, 1, pc98_ioport63D_read, env);
+    register_ioport_read(0x9894, 1, 1, pc98_ioport9894_read, env);
+
+    qemu_register_reset(pc98_reset, env);
+
+    if (pci_enabled) {
+        isa_irq_state->ioapic = ioapic_init();
+    }
+    pit = pc98_pit_init(0x71, isa_reserve_irq(0));
+    /*pcspk_init(pit);*/
+
+/*
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        if (serial_hds[i]) {
+ serial_init(serial_io[i], isa_reserve_irq(serial_irq[i]), 115200,
+                        serial_hds[i]);
+        }
+    }
+
+    for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+        if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], isa_reserve_irq(parallel_irq[i]),
+                          parallel_hds[i]);
+        }
+    }
+
+    for(i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+
+ if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
+            pc_init_ne2k_isa(nd);
+        else
+            pci_nic_init(nd, "e1000", NULL);
+    }
+*/
+    piix4_acpi_system_hot_add_init();
+
+    if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+        fprintf(stderr, "qemu: too many IDE bus\n");
+        exit(1);
+    }
+
+    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+    }
+
+//    if (pci_enabled) {
+//        pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
+//    } else {
+ pc98_ide_init(isa_reserve_irq(9), hd[0], hd[1], hd[2], hd[3]);
+//    }
+
+    pc98_DMA_init(1);
+
+    /* they may be c-bus(isa) devices */
+    pc98_vga_init(isa_reserve_irq(2));
+    pc98_sys_init(isa_reserve_irq(15));
+    pc98_kbd_init(isa_reserve_irq(1));
+    pc98_mouse_init(isa_reserve_irq(13));
+
+#ifdef HAS_AUDIO
+    /*audio_init(pci_enabled ? pci_bus : NULL, isa_irq);*/
+#endif
+
+    for(i = 0; i < MAX_FD; i++) {
+        dinfo = drive_get(IF_FLOPPY, 0, i);
+        fd[i] = dinfo ? dinfo->bdrv : NULL;
+    }
+    floppy_controller = pc98_fdctrl_init(11, 2, 0x90, fd);
+
+//    if (pci_enabled && usb_enabled) {
+//        usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+//    }
+
+    if (i440fx_state) {
+        i440fx_init_memory_mappings(i440fx_state);
+    }
+}

This looks like quite some copy&paste work. Isn't there a way to combine the normal pc_init and the pc98 inits?

The problem with copy&paste is that for something like pc98, you'll end up having changes in pc_init (which changes a lot these days), but those probably won't be reflected in pc98_init. So over time they diverge, probably breaking pc98.

If on the other hand both would be calling common code, the common code would be changed and pc98 magically stays alive.

But maybe machine description files really are the only solution to that... sigh.

+
+static void pc98_init_pci(ram_addr_t ram_size,
+                          const char *boot_device,
+                          const char *kernel_filename,
+                          const char *kernel_cmdline,
+                          const char *initrd_filename,
+                          const char *cpu_model)
+{
+    pc98_init1(ram_size, cpu_model, 1);
+}
+
+static void pc98_init_isa(ram_addr_t ram_size,
+                          const char *boot_device,
+                          const char *kernel_filename,
+                          const char *kernel_cmdline,
+                          const char *initrd_filename,
+                          const char *cpu_model)
+{
+    pc98_init1(ram_size, cpu_model, 0);
+}
+
static QEMUMachine pc_machine = {
    .name = "pc-0.11",
    .alias = "pc",
@@ -1505,11 +1884,27 @@
    .max_cpus = 1,
};

+static QEMUMachine pc98pci_machine = {
+    .name = "pc98pci",
+    .desc = "NEC PC-9821 with PCI",
+    .init = pc98_init_pci,
+    .max_cpus = 2,
+};
+
+static QEMUMachine pc98_machine = {
+    .name = "pc98",
+    .desc = "NEC PC-9821",
+    .init = pc98_init_isa,
+    .max_cpus = 1,
+};
+
static void pc_machine_init(void)
{
    qemu_register_machine(&pc_machine);
    qemu_register_machine(&pc_machine_v0_10);
    qemu_register_machine(&isapc_machine);
+    qemu_register_machine(&pc98pci_machine);
+    qemu_register_machine(&pc98_machine);

IMHO pc.c is way too cluttered already. Putting the pc98 target in there as well makes it even more crowded.

So why not putting all the pc98 specific code (IO port handlers, init, static variables, ...) in pc98.c and call pc98_machine_init() here? That way the pc98 code would be isolated, but still belong to the PC target somehow.

}

machine_init(pc_machine_init);
diff -ur a/hw/pc.h b/hw/pc.h
--- a/hw/pc.h   Tue Sep  8 21:26:50 2009
+++ b/hw/pc.h   Wed Sep  9 21:51:09 2009

I think putting those changes into pc98.h would make sense. Then pc.h would simply #include pc98.h.

@@ -5,6 +5,9 @@

/* PC-style peripherals (also used by other machines).  */

+#define PC98_SYSCLOCK_5MHZ
+/*#define PC98_SYSCLOCK_8MHZ*/
+
/* serial.c */

SerialState *serial_init(int base, qemu_irq irq, int baudbase,
@@ -26,6 +29,7 @@
void pic_set_irq(int irq, int level);
void pic_set_irq_new(void *opaque, int irq, int level);
qemu_irq *i8259_init(qemu_irq parent_irq);
+qemu_irq *pc98_i8259_init(qemu_irq parent_irq);
int pic_read_irq(PicState2 *s);
void pic_update_irq(PicState2 *s);
uint32_t pic_intack_read(PicState2 *s);
@@ -51,9 +55,16 @@

#define PIT_FREQ 1193182

+#ifdef PC98_SYSCLOCK_5MHZ
+#define PC98_PIT_FREQ 2457600
+#else
+#define PC98_PIT_FREQ 1996800
+#endif
+
typedef struct PITState PITState;

PITState *pit_init(int base, qemu_irq irq);
+PITState *pc98_pit_init(int base, qemu_irq irq);
void pit_set_gate(PITState *pit, int channel, int val);
int pit_get_gate(PITState *pit, int channel);
int pit_get_initial_count(PITState *pit, int channel);
@@ -147,8 +158,19 @@
void isa_cirrus_vga_init(void);

/* ne2000.c */
-
void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
+
+/* pc98kbd.c */
+void pc98_kbd_init(qemu_irq irq);
+
+/* pc98mouse.c */
+void pc98_mouse_init(qemu_irq irq);
+
+/* pc98sys.c */
+void pc98_sys_init(qemu_irq irq);
+
+/* pc98vga.c */
+int pc98_vga_init(qemu_irq irq);

int cpu_is_bsp(CPUState *env);
#endif
diff -ur a/hw/ide.h b/hw/ide.h
--- a/hw/ide.h  Tue Sep  8 21:26:50 2009
+++ b/hw/ide.h  Wed Sep  9 21:51:51 2009
@@ -22,4 +22,9 @@
                    qemu_irq irq, int shift,
                    DriveInfo *hd0, DriveInfo *hd1);

+/* ide-pc98.c */
+void pc98_ide_init(qemu_irq irq,
+                   DriveInfo *hd0, DriveInfo *hd1,
+                   DriveInfo *hd2, DriveInfo *hd3);
+
#endif /* HW_IDE_H */
diff -ur a/hw/isa.h b/hw/isa.h
--- a/hw/isa.h  Tue Sep  8 21:26:50 2009
+++ b/hw/isa.h  Wed Sep  9 21:51:58 2009
@@ -45,6 +45,7 @@
void DMA_release_DREQ (int nchan);
void DMA_schedule(int nchan);
void DMA_init (int high_page_enable);
+void pc98_DMA_init (int high_page_enable);
void DMA_register_channel (int nchan,
                           DMA_transfer_handler transfer_handler,
                           void *opaque);








reply via email to

[Prev in Thread] Current Thread [Next in Thread]