static void apu_regwrite(int chip, int address, /*uint8*/ int value) {
        int chan = (address & 4) != 0 ? 1 : 0;

        cur = APU[chip];
        switch (address) {
            /* squares */
            case APU_WRA0:
            case APU_WRB0:
                cur.squ[chan].regs[0] = value;
                break;

            case APU_WRA1:
            case APU_WRB1:
                cur.squ[chan].regs[1] = value;
                break;

            case APU_WRA2:
            case APU_WRB2:
                cur.squ[chan].regs[2] = value;
                if (cur.squ[chan].enabled) {
                    cur.squ[chan].freq = ((((cur.squ[chan].regs[3] & 7) << 8) + value) + 1) << 16;
                }
                break;

            case APU_WRA3:
            case APU_WRB3:
                cur.squ[chan].regs[3] = value;

                if (cur.squ[chan].enabled) {
                    cur.squ[chan].vbl_length = vbl_times[value >> 3];
                    cur.squ[chan].env_vol = 0;
                    cur.squ[chan].freq = ((((value & 7) << 8) + cur.squ[chan].regs[2]) + 1) << 16;
                }

                break;

            /* triangle */
            case APU_WRC0:
                cur.tri.regs[0] = value;

                if (cur.tri.enabled) {                                          /* ??? */

                    if (false == cur.tri.counter_started) {
                        cur.tri.linear_length = sync_times2[value & 0x7F];
                    }
                }

                break;

            case 0x4009:
                /* unused */
                cur.tri.regs[1] = value;
                break;

            case APU_WRC2:
                cur.tri.regs[2] = value;
                break;

            case APU_WRC3:
                cur.tri.regs[3] = value;

                /* this is somewhat of a hack.  there is some latency on the Real
                 ** Thing between when trireg0 is written to and when the linear
                 ** length counter actually begins its countdown.  we want to prevent
                 ** the case where the program writes to the freq regs first, then
                 ** to reg 0, and the counter accidentally starts running because of
                 ** the sound queue's timestamp processing.
                 **
                 ** set to a few NES sample -- should be sufficient
                 **
                 **     3 * (1789772.727 / 44100) = ~122 cycles, just around one scanline
                 **
                 ** should be plenty of time for the 6502 code to do a couple of table
                 ** dereferences and load up the other triregs
                 */
                cur.tri.write_latency = 3;

                if (cur.tri.enabled) {
                    cur.tri.counter_started = false;
                    cur.tri.vbl_length = vbl_times[value >> 3];
                    cur.tri.linear_length = sync_times2[cur.tri.regs[0] & 0x7F];
                }

                break;

            /* noise */
            case APU_WRD0:
                cur.noi.regs[0] = value;
                break;

            case 0x400D:
                /* unused */
                cur.noi.regs[1] = value;
                break;

            case APU_WRD2:
                cur.noi.regs[2] = value;
                break;

            case APU_WRD3:
                cur.noi.regs[3] = value;

                if (cur.noi.enabled) {
                    cur.noi.vbl_length = vbl_times[value >> 3];
                    cur.noi.env_vol = 0; /* reset envelope */

                }
                break;

            /* DMC */
            case APU_WRE0:
                cur.dpcm.regs[0] = value;
                if (0 == (value & 0x80)) {
                    cur.dpcm.irq_occurred = false;
                }
                break;

            case APU_WRE1: /* 7-bit DAC */

                //cur.dpcm.regs[1] = value - 0x40;

                cur.dpcm.regs[1] = value & 0x7F;
                cur.dpcm.vol = (byte) (cur.dpcm.regs[1] - 64);
                break;

            case APU_WRE2:
                cur.dpcm.regs[2] = value;
                //apu_dpcmreset(cur.dpcm);
                break;

            case APU_WRE3:
                cur.dpcm.regs[3] = value;
                //apu_dpcmreset(cur.dpcm);
                break;

            case APU_SMASK:
                if ((value & 0x01) != 0) {
                    cur.squ[0].enabled = true;
                } else {
                    cur.squ[0].enabled = false;
                    cur.squ[0].vbl_length = 0;
                }

                if ((value & 0x02) != 0) {
                    cur.squ[1].enabled = true;
                } else {
                    cur.squ[1].enabled = false;
                    cur.squ[1].vbl_length = 0;
                }

                if ((value & 0x04) != 0) {
                    cur.tri.enabled = true;
                } else {
                    cur.tri.enabled = false;
                    cur.tri.vbl_length = 0;
                    cur.tri.linear_length = 0;
                    cur.tri.counter_started = false;
                    cur.tri.write_latency = 0;
                }

                if ((value & 0x08) != 0) {
                    cur.noi.enabled = true;
                } else {
                    cur.noi.enabled = false;
                    cur.noi.vbl_length = 0;
                }

                if ((value & 0x10) != 0) {
                    /* only reset dpcm values if DMA is finished */
                    if (false == cur.dpcm.enabled) {
                        cur.dpcm.enabled = true;
                        apu_dpcmreset(cur.dpcm);
                    }
                } else {
                    cur.dpcm.enabled = false;
                }

                cur.dpcm.irq_occurred = false;

                break;
            default:
                if(errorlog!=null) fprintf(errorlog,"invalid apu write: $%02X at $%04X\n", value, address);
                break;
        }
    }