static byte apu_square(square_t chan) {
        int env_delay;
        int sweep_delay;
        byte output;

        /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle
         ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on
         ** reg2: 8 bits of freq
         ** reg3: 0-2=high freq, 7-4=vbl length counter
         */
        if (false == chan.enabled) {
            return 0;
        }

        /* enveloping */
        env_delay = sync_times1[chan.regs[0] & 0x0F];
        /* decay is at a rate of (env_regs + 1) / 240 secs */
        chan.env_phase -= 4;
        while (chan.env_phase < 0) {
            chan.env_phase += env_delay;
            if ((chan.regs[0] & 0x20) != 0) {
                chan.env_vol = ((chan.env_vol + 1) & 15) & 0xFF;
            } else if (chan.env_vol < 15) {
                chan.env_vol = (chan.env_vol + 1) & 0xFF;
            }
        }

        /* vbl length counter */
        if (chan.vbl_length > 0 && 0 == (chan.regs[0] & 0x20)) {
            chan.vbl_length--;
        }

        if (0 == chan.vbl_length) {
            return 0;
        }

        /* freqsweeps */
        if ((chan.regs[1] & 0x80) != 0 && (chan.regs[1] & 7) != 0) {
            sweep_delay = sync_times1[(chan.regs[1] >> 4) & 7];
            chan.sweep_phase -= 2;
            while (chan.sweep_phase < 0) {
                chan.sweep_phase += sweep_delay;
                if ((chan.regs[1] & 8) != 0) {
                    chan.freq -= chan.freq >> (chan.regs[1] & 7);
                } else {
                    chan.freq += chan.freq >> (chan.regs[1] & 7);
                }
            }
        }

        if ((0 == (chan.regs[1] & 8) && (chan.freq >> 16) > freq_limit[chan.regs[1] & 7])
                || (chan.freq >> 16) < 4) {
            return 0;
        }
        chan.phaseacc -= (float) apu_incsize; /* # of cycles per sample */

        while (chan.phaseacc < 0) {
            chan.phaseacc += (chan.freq >> 16);
            chan.adder = (chan.adder + 1) & 0x0F;
        }

        if ((chan.regs[0] & 0x10) != 0) /* fixed volume */ {
            output = (byte) (chan.regs[0] & 0x0F);
        } else {
            output = (byte) (0x0F - chan.env_vol);
        }

        if (chan.adder < (duty_lut[chan.regs[0] >> 6])) {
            output = (byte) -output;
        }

        return (byte) output;
    }