static byte apu_triangle(triangle_t chan) {
        int freq;
        byte output;
        /* reg0: 7=holdnote, 6-0=linear length counter
         ** reg2: low 8 bits of frequency
         ** reg3: 7-3=length counter, 2-0=high 3 bits of frequency
         */

        if (false == chan.enabled) {
            return 0;
        }

        if (false == chan.counter_started && 0 == (chan.regs[0] & 0x80)) {
            if (chan.write_latency != 0) {
                chan.write_latency--;
            }
            if (0 == chan.write_latency) {
                chan.counter_started = true;
            }
        }

        if (chan.counter_started) {
            if (chan.linear_length > 0) {
                chan.linear_length--;
            }
            if (chan.vbl_length != 0 && 0 == (chan.regs[0] & 0x80)) {
                chan.vbl_length--;
            }

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

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

        freq = (((chan.regs[3] & 7) << 8) + chan.regs[2]) + 1;

        if (freq < 4) /* inaudible */ {
            return 0;
        }

        chan.phaseacc -= (float) apu_incsize; /* # of cycles per sample */

        while (chan.phaseacc < 0) {
            chan.phaseacc += freq;
            chan.adder = (chan.adder + 1) & 0x1F;

            output = (byte) ((chan.adder & 7) << 1);
            if ((chan.adder & 8) != 0) {
                output = (byte) (0x10 - output);
            }
            if ((chan.adder & 0x10) != 0) {
                output = (byte) -output;
            }

            chan.output_vol = (byte) output;
        }

        return (byte) chan.output_vol;
    }