From f04973834d92f201d15f6182e354c58ee2718307 Mon Sep 17 00:00:00 2001 From: Altheos Date: Mon, 12 Aug 2019 17:27:57 +0200 Subject: [PATCH 1/5] GUS soundcard improvements : IO address flexibilty 256k, 512k or 1Mo total memory available --- src/devices/sound/snd_gus.c | 55 +++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/devices/sound/snd_gus.c b/src/devices/sound/snd_gus.c index 336afa8..8ed45a1 100644 --- a/src/devices/sound/snd_gus.c +++ b/src/devices/sound/snd_gus.c @@ -8,7 +8,7 @@ * * Implementation of the Gravis UltraSound sound device. * - * Version: @(#)snd_gus.c 1.0.15 2019/05/17 + * Version: @(#)snd_gus.c 1.0.16 2019/06/27 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -136,6 +136,7 @@ typedef struct { int irq, dma, irq_midi; + uint16_t base; int latch_enable; uint8_t sb_2xa, @@ -692,8 +693,9 @@ gus_read(uint16_t addr, priv_t priv) { gus_t *dev = (gus_t *)priv; uint8_t val = 0xff; + uint16_t port = addr - dev->base; - switch (addr) { + switch (port) { case 0x340: /*MIDI status*/ val = dev->midi_status; break; @@ -1189,11 +1191,11 @@ gus_init(const device_t *info, UNUSED(void *parent)) switch(info->local) { case 0: /* Standard GUS */ - io_sethandler(0x0240, 16, + io_sethandler(dev->base, 16, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); - io_sethandler(0x0340, 16, + io_sethandler(dev->base+0x0100, 16, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); - io_sethandler(0x0746, 1, + io_sethandler(dev->base+0x0506, 1, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); io_sethandler(0x0388, 2, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); @@ -1206,15 +1208,15 @@ gus_init(const device_t *info, UNUSED(void *parent)) cs423x_setirq(&dev->cs423x, 5); /*Default irq and dma from GUS SDK*/ cs423x_setdma(&dev->cs423x, 3); - io_sethandler(0x0240, 16, + io_sethandler(dev->base, 16, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(0x0340, 9, + io_sethandler(dev->base+0x0100, 9, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(0x0746, 1, + io_sethandler(dev->base+0x0506, 1, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); io_sethandler(0x0388, 2, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(0x034c, 4, + io_sethandler(dev->base+0x010c, 4, cs423x_read,NULL,NULL, cs423x_write,NULL,NULL, &dev->cs423x); break; @@ -1259,8 +1261,39 @@ speed_changed(priv_t priv) #endif } +static const device_config_t gus_config[] = { + { + "base", "Address", CONFIG_HEX16, "", 0x0350, + { + { + "210H", 0x0210 + }, + { + "220H", 0x0220 + }, + { + "230H", 0x0230 + }, + { + "240H", 0x0240 + }, + { + "250H", 0x0250 + }, + { + "260H", 0x0260 + }, + { + "" + } + }, + }, + { + "", "", -1 + } +}; -const device_t gus_device = { +static const device_t gus_device = { "Gravis UltraSound", DEVICE_ISA, 0, @@ -1272,7 +1305,7 @@ const device_t gus_device = { }; #if defined(DEV_BRANCH) && defined(USE_GUSMAX) -const device_t gusmax_device = { +static const device_t gusmax_device = { "Gravis UltraSound MAX", DEVICE_ISA, 1, From dbe7610497314bae56b8fc2c4d944d7a77597596 Mon Sep 17 00:00:00 2001 From: Altheos Date: Mon, 12 Aug 2019 17:39:01 +0200 Subject: [PATCH 2/5] GUS soundcard improvements second commit --- src/devices/sound/snd_gus.c | 204 +++++++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 63 deletions(-) diff --git a/src/devices/sound/snd_gus.c b/src/devices/sound/snd_gus.c index 8ed45a1..621b3eb 100644 --- a/src/devices/sound/snd_gus.c +++ b/src/devices/sound/snd_gus.c @@ -124,6 +124,7 @@ typedef struct { samp_latch; uint8_t *ram; + uint32_t gus_end_ram; int pos; int16_t buffer[2][SOUNDBUFLEN]; @@ -246,12 +247,18 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) uint16_t ioport; #endif int c, d, old; + uint16_t port; - if (dev->latch_enable && addr != 0x24b) - dev->latch_enable = 0; + if ((addr == 0x388) || (addr == 0x389)) + port = addr; + else + port = addr & 0xf0f; /* Bit masking GUS dynamic IO*/ - switch (addr) { - case 0x340: /*MIDI control*/ + if (dev->latch_enable && port != 0x20b) + dev->latch_enable = 0; + + switch (port) { + case 0x300: /*MIDI control*/ old = dev->midi_ctrl; dev->midi_ctrl = val; @@ -262,7 +269,7 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) midi_update_int_status(dev); break; - case 0x341: /*MIDI data*/ + case 0x301: /*MIDI data*/ if (dev->midi_loopback) { dev->midi_status |= MIDI_INT_RECEIVE; dev->midi_data = val; @@ -271,15 +278,15 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) dev->midi_status |= MIDI_INT_TRANSMIT; break; - case 0x342: /*Voice select*/ + case 0x302: /*Voice select*/ dev->voice = val & 31; break; - case 0x343: /*Global select*/ + case 0x303: /*Global select*/ dev->global = val; break; - case 0x344: /*Global low*/ + case 0x304: /*Global low*/ switch (dev->global) { case 0: /*Voice control*/ dev->ctrl[dev->voice] = val; @@ -338,7 +345,7 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } break; - case 0x345: /*Global high*/ + case 0x305: /*Global high*/ switch (dev->global) { case 0: /*Voice control*/ if (!(val & 1) && dev->ctrl[dev->voice] & 1) { @@ -532,12 +539,13 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } break; - case 0x347: /*DRAM access*/ - dev->ram[dev->addr] = val; + case 0x307: /*DRAM access*/ dev->addr &= 0xfffff; + if (dev->addr < dev->gus_end_ram) + dev->ram[dev->addr] = val; break; - case 0x248: + case 0x208: case 0x388: dev->adcommand = val; break; @@ -573,12 +581,12 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } break; - case 0x240: + case 0x200: dev->midi_loopback = val & 0x20; dev->latch_enable = (val & 0x40) ? 2 : 1; break; - case 0x24b: + case 0x20b: switch (dev->reg_ctrl & 0x07) { case 0: if (dev->latch_enable == 1) @@ -629,7 +637,7 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } break; - case 0x246: + case 0x206: dev->ad_status |= 0x08; if (dev->sb_ctrl & 0x20) { if (dev->sb_nmi) @@ -639,11 +647,11 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } break; - case 0x24a: + case 0x20a: dev->sb_2xa = val; break; - case 0x24c: + case 0x20c: dev->ad_status |= 0x10; if (dev->sb_ctrl & 0x20) { if (dev->sb_nmi) @@ -653,19 +661,19 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) } /*FALLTHOUGH*/ - case 0x24d: + case 0x20d: dev->sb_2xc = val; break; - case 0x24e: + case 0x20e: dev->sb_2xe = val; break; - case 0x24f: + case 0x20f: dev->reg_ctrl = val; break; - case 0x346: case 0x746: + case 0x306: case 0x706: if (dev->dma >= 4) val |= 0x30; dev->max_ctrl = (val >> 6) & 1; @@ -693,45 +701,51 @@ gus_read(uint16_t addr, priv_t priv) { gus_t *dev = (gus_t *)priv; uint8_t val = 0xff; - uint16_t port = addr - dev->base; + uint16_t port; + + if ((addr == 0x388) || (addr == 0x389)) + port = addr; + else + port = addr & 0xf0f; /* Bit masking GUS dynamic IO*/ + switch (port) { - case 0x340: /*MIDI status*/ + case 0x300: /*MIDI status*/ val = dev->midi_status; break; - case 0x341: /*MIDI data*/ + case 0x301: /*MIDI data*/ val = dev->midi_data; dev->midi_status &= ~MIDI_INT_RECEIVE; midi_update_int_status(dev); break; - case 0x240: + case 0x200: val = 0x00; break; - case 0x246: /*IRQ status*/ + case 0x206: /*IRQ status*/ val = dev->irqstatus & ~0x10; if (dev->ad_status & 0x19) val |= 0x10; break; - case 0x24F: + case 0x20F: if (dev->max_ctrl) val = 0x02; else val = 0x00; break; - case 0x342: + case 0x302: /*GF1 Page Register*/ val = dev->voice; break; - case 0x343: + case 0x303: /*GF1 Global Register Select*/ val = dev->global; break; - case 0x344: /*Global low*/ + case 0x304: /*Global low*/ switch (dev->global) { case 0x82: /*Start addr high*/ val = dev->start[dev->voice] >> 16; @@ -769,7 +783,7 @@ gus_read(uint16_t addr, priv_t priv) } break; - case 0x345: /*Global high*/ + case 0x305: /*Global high*/ switch (dev->global) { case 0x80: /*Voice control*/ val = dev->ctrl[dev->voice] | (dev->waveirqs[dev->voice] ? 0x80 : 0); @@ -832,23 +846,26 @@ gus_read(uint16_t addr, priv_t priv) } break; - case 0x346: case 0x746: + case 0x306: case 0x706: if (dev->max_ctrl) val = 0x0a; /* GUS MAX */ else val = 0xff; /*Pre 3.7 - no mixer*/ break; - case 0x347: /*DRAM access*/ - val = dev->ram[dev->addr]; + case 0x307: /*DRAM access*/ dev->addr &= 0xFFFFF; + if (dev->addr < dev->gus_end_ram) + val = dev->ram[dev->addr]; + else + val = 0; break; - case 0x349: + case 0x309: val = 0x00; break; - case 0x24b: + case 0x20B: switch (dev->reg_ctrl & 0x07) { case 1: val = dev->gp1; @@ -868,17 +885,17 @@ gus_read(uint16_t addr, priv_t priv) } break; - case 0x24c: + case 0x20C: val = dev->sb_2xc; if (dev->reg_ctrl & 0x20) dev->sb_2xc &= 0x80; break; - case 0x24e: + case 0x20E: val = dev->sb_2xe; break; - case 0x248: + case 0x208: /* Timer Control Register */ case 0x388: if (dev->tctrl & GUS_TIMER_CTRL_AUTO) val = dev->sb_2xa; @@ -889,16 +906,16 @@ gus_read(uint16_t addr, priv_t priv) } break; - case 0x249: + case 0x209: /* Timer Data */ dev->ad_status &= ~0x01; nmi = 0; /*FALLTHROUGH?*/ - case 0x389: + case 0x389:/* Adlib port 389 */ val = dev->ad_data; break; - case 0x24A: + case 0x20A: val = dev->adcommand; break; @@ -1163,12 +1180,14 @@ gus_init(const device_t *info, UNUSED(void *parent)) gus_t *dev; double out = 1.0; int c; + uint8_t gus_ram = device_get_config_int("gus_ram"); dev = (gus_t *)mem_alloc(sizeof(gus_t)); memset(dev, 0x00, sizeof(gus_t)); - dev->ram = (uint8_t *)mem_alloc(1 << 20); - memset(dev->ram, 0x00, 1 << 20); + dev->gus_end_ram = 1 << (18 + gus_ram); + dev->ram = (uint8_t *)mem_alloc(dev->gus_end_ram); + memset(dev->ram, 0x00, (dev->gus_end_ram)); for (c = 0; c < 32; c++) { dev->ctrl[c] = 1; @@ -1189,13 +1208,15 @@ gus_init(const device_t *info, UNUSED(void *parent)) dev->t1l = dev->t2l = 0xff; + dev->base = device_get_config_hex16("base"); + switch(info->local) { case 0: /* Standard GUS */ io_sethandler(dev->base, 16, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); - io_sethandler(dev->base+0x0100, 16, + io_sethandler(0x0100+dev->base, 16, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); - io_sethandler(dev->base+0x0506, 1, + io_sethandler(0x0506+dev->base, 1, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); io_sethandler(0x0388, 2, gus_read,NULL,NULL, gus_write,NULL,NULL, dev); @@ -1210,13 +1231,13 @@ gus_init(const device_t *info, UNUSED(void *parent)) io_sethandler(dev->base, 16, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(dev->base+0x0100, 9, + io_sethandler(0x0100+dev->base, 9, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(dev->base+0x0506, 1, + io_sethandler(0x0506+dev->base, 1, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); io_sethandler(0x0388, 2, gus_read,NULL,NULL, gus_write,NULL, NULL, dev); - io_sethandler(dev->base+0x010c, 4, + io_sethandler(0x10C+dev->base, 4, cs423x_read,NULL,NULL, cs423x_write,NULL,NULL, &dev->cs423x); break; @@ -1263,37 +1284,94 @@ speed_changed(priv_t priv) static const device_config_t gus_config[] = { { - "base", "Address", CONFIG_HEX16, "", 0x0350, + "base", "Address", CONFIG_HEX16, "", 0x220, { { - "210H", 0x0210 + "210H", 0x210 }, { - "220H", 0x0220 + "220H", 0x220 }, { - "230H", 0x0230 + "230H", 0x230 }, { - "240H", 0x0240 + "240H", 0x240 }, { - "250H", 0x0250 + "250H", 0x250 }, { - "260H", 0x0260 + "260H", 0x260 }, - { - "" - } }, }, + { + "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 0, + { + { + "256 KB", 0 + }, + { + "512 KB", 1 + }, + { + "1 MB", 2 + }, + { + NULL + } + } + }, { "", "", -1 } }; -static const device_t gus_device = { +static const device_config_t gus_max_config[] = { + { + "base", "Address", CONFIG_HEX16, "", 0x220, +{ + { + "210H", 0x210 + }, + { + "220H", 0x220 + }, + { + "230H", 0x230 + }, + { + "240H", 0x240 + }, + { + "250H", 0x250 + }, + { + "260H", 0x260 + }, +}, + }, + { + "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 1, +{ + { + "512 KB", 1 + }, + { + "1 MB", 2 + }, + { + NULL + } +} + }, + { + "", "", -1 + } +}; + +const device_t gus_device = { "Gravis UltraSound", DEVICE_ISA, 0, @@ -1301,11 +1379,11 @@ static const device_t gus_device = { gus_init, gus_close, NULL, NULL, speed_changed, NULL, NULL, - NULL + gus_config }; #if defined(DEV_BRANCH) && defined(USE_GUSMAX) -static const device_t gusmax_device = { +const device_t gusmax_device = { "Gravis UltraSound MAX", DEVICE_ISA, 1, @@ -1313,6 +1391,6 @@ static const device_t gusmax_device = { gus_init, gus_close, NULL, NULL, speed_changed, NULL, NULL, - NULL + gus_max_config }; #endif From ddd4e8ee025f366576e956cbe12dac7750ba7575 Mon Sep 17 00:00:00 2001 From: Altheos Date: Tue, 13 Aug 2019 11:06:27 +0200 Subject: [PATCH 3/5] minor cleanup to GUS code --- src/devices/sound/snd_gus.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/devices/sound/snd_gus.c b/src/devices/sound/snd_gus.c index 621b3eb..83b8787 100644 --- a/src/devices/sound/snd_gus.c +++ b/src/devices/sound/snd_gus.c @@ -124,7 +124,7 @@ typedef struct { samp_latch; uint8_t *ram; - uint32_t gus_end_ram; + uint32_t gus_end_ram; int pos; int16_t buffer[2][SOUNDBUFLEN]; @@ -244,10 +244,10 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) { gus_t *dev = (gus_t *)priv; #if defined(DEV_BRANCH) && defined(USE_GUSMAX) - uint16_t ioport; + uint16_t csioport; #endif int c, d, old; - uint16_t port; + uint16_t port; if ((addr == 0x388) || (addr == 0x389)) port = addr; @@ -680,12 +680,12 @@ gus_write(uint16_t addr, uint8_t val, priv_t priv) #if defined(DEV_BRANCH) && defined(USE_GUSMAX) if (val & 0x40) { if ((val & 0xF) != ((addr >> 4) & 0xF)) { /* Fix me : why is DOS application attempting to relocate the CODEC ? */ - ioport = 0x30c | ((addr >> 4) & 0xf); - io_removehandler(ioport, 4, + csioport = 0x30c | ((addr >> 4) & 0xf); + io_removehandler(csioport, 4, cs423x_read,NULL,NULL, cs423x_write,NULL,NULL,&dev->cs423x); - ioport = 0x30c | ((val & 0xf) << 4); - io_sethandler(ioport, 4, + csioport = 0x30c | ((val & 0xf) << 4); + io_sethandler(csioport, 4, cs423x_read,NULL,NULL, cs423x_write,NULL,NULL, &dev->cs423x); } @@ -1180,13 +1180,13 @@ gus_init(const device_t *info, UNUSED(void *parent)) gus_t *dev; double out = 1.0; int c; - uint8_t gus_ram = device_get_config_int("gus_ram"); + uint8_t gus_ram = device_get_config_int("gus_ram"); dev = (gus_t *)mem_alloc(sizeof(gus_t)); memset(dev, 0x00, sizeof(gus_t)); - dev->gus_end_ram = 1 << (18 + gus_ram); - dev->ram = (uint8_t *)mem_alloc(dev->gus_end_ram); + dev->gus_end_ram = 1 << (18 + gus_ram); + dev->ram = (uint8_t *)mem_alloc(dev->gus_end_ram); memset(dev->ram, 0x00, (dev->gus_end_ram)); for (c = 0; c < 32; c++) { @@ -1208,7 +1208,7 @@ gus_init(const device_t *info, UNUSED(void *parent)) dev->t1l = dev->t2l = 0xff; - dev->base = device_get_config_hex16("base"); + dev->base = device_get_config_hex16("base"); switch(info->local) { case 0: /* Standard GUS */ From f7c5a6927ba8aedc0a409a6679c2d41cf0e9e73b Mon Sep 17 00:00:00 2001 From: Altheos Date: Tue, 13 Aug 2019 11:42:26 +0200 Subject: [PATCH 4/5] More GUS code cleanup --- src/devices/sound/snd_gus.c | 135 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 68 deletions(-) diff --git a/src/devices/sound/snd_gus.c b/src/devices/sound/snd_gus.c index 83b8787..b142f8a 100644 --- a/src/devices/sound/snd_gus.c +++ b/src/devices/sound/snd_gus.c @@ -137,7 +137,7 @@ typedef struct { int irq, dma, irq_midi; - uint16_t base; + uint16_t base; int latch_enable; uint8_t sb_2xa, @@ -701,13 +701,12 @@ gus_read(uint16_t addr, priv_t priv) { gus_t *dev = (gus_t *)priv; uint8_t val = 0xff; - uint16_t port; - - if ((addr == 0x388) || (addr == 0x389)) - port = addr; - else - port = addr & 0xf0f; /* Bit masking GUS dynamic IO*/ + uint16_t port; + if ((addr == 0x388) || (addr == 0x389)) + port = addr; + else + port = addr & 0xf0f; /* Bit masking GUS dynamic IO*/ switch (port) { case 0x300: /*MIDI status*/ @@ -1286,41 +1285,41 @@ static const device_config_t gus_config[] = { { "base", "Address", CONFIG_HEX16, "", 0x220, { - { - "210H", 0x210 - }, - { - "220H", 0x220 - }, - { - "230H", 0x230 - }, - { - "240H", 0x240 - }, - { - "250H", 0x250 - }, - { - "260H", 0x260 - }, + { + "210H", 0x210 + }, + { + "220H", 0x220 + }, + { + "230H", 0x230 + }, + { + "240H", 0x240 + }, + { + "250H", 0x250 + }, + { + "260H", 0x260 + }, }, }, { "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 0, { - { - "256 KB", 0 - }, - { - "512 KB", 1 - }, - { - "1 MB", 2 - }, - { - NULL - } + { + "256 KB", 0 + }, + { + "512 KB", 1 + }, + { + "1 MB", 2 + }, + { + NULL + } } }, { @@ -1331,40 +1330,40 @@ static const device_config_t gus_config[] = { static const device_config_t gus_max_config[] = { { "base", "Address", CONFIG_HEX16, "", 0x220, -{ - { - "210H", 0x210 - }, - { - "220H", 0x220 - }, - { - "230H", 0x230 - }, - { - "240H", 0x240 - }, - { - "250H", 0x250 - }, - { - "260H", 0x260 - }, -}, + { + { + "210H", 0x210 + }, + { + "220H", 0x220 + }, + { + "230H", 0x230 + }, + { + "240H", 0x240 + }, + { + "250H", 0x250 + }, + { + "260H", 0x260 + }, + }, }, { "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 1, -{ - { - "512 KB", 1 - }, - { - "1 MB", 2 - }, - { - NULL - } -} + { + { + "512 KB", 1 + }, + { + "1 MB", 2 + }, + { + NULL + } + } }, { "", "", -1 From e69497217d630e1d81b485203056669dfba1334d Mon Sep 17 00:00:00 2001 From: Altheos Date: Tue, 13 Aug 2019 11:47:05 +0200 Subject: [PATCH 5/5] More more GUS code cleanup --- src/devices/sound/snd_gus.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/devices/sound/snd_gus.c b/src/devices/sound/snd_gus.c index b142f8a..fcfa3d5 100644 --- a/src/devices/sound/snd_gus.c +++ b/src/devices/sound/snd_gus.c @@ -8,7 +8,7 @@ * * Implementation of the Gravis UltraSound sound device. * - * Version: @(#)snd_gus.c 1.0.16 2019/06/27 + * Version: @(#)snd_gus.c 1.0.16 2019/08/13 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -1286,22 +1286,22 @@ static const device_config_t gus_config[] = { "base", "Address", CONFIG_HEX16, "", 0x220, { { - "210H", 0x210 + "210H", 0x210 }, { - "220H", 0x220 + "220H", 0x220 }, { - "230H", 0x230 + "230H", 0x230 }, { - "240H", 0x240 + "240H", 0x240 }, { - "250H", 0x250 + "250H", 0x250 }, { - "260H", 0x260 + "260H", 0x260 }, }, }, @@ -1309,16 +1309,16 @@ static const device_config_t gus_config[] = { "gus_ram", "Onboard RAM", CONFIG_SELECTION, "", 0, { { - "256 KB", 0 + "256 KB", 0 }, { - "512 KB", 1 + "512 KB", 1 }, { - "1 MB", 2 + "1 MB", 2 }, { - NULL + NULL } } },