//go:build avr && atmega1280

package machine

import (
	"device/avr"
	"runtime/interrupt"
	"runtime/volatile"
)

const irq_USART0_RX = avr.IRQ_USART0_RX

const (
	portA Pin = iota * 8
	portB
	portC
	portD
	portE
	portF
	portG
	portH
	portJ
	portK
	portL
)

const (
	PA0 = portA + 0
	PA1 = portA + 1
	PA2 = portA + 2
	PA3 = portA + 3
	PA4 = portA + 4
	PA5 = portA + 5
	PA6 = portA + 6
	PA7 = portA + 7
	PB0 = portB + 0
	PB1 = portB + 1
	PB2 = portB + 2
	PB3 = portB + 3
	PB4 = portB + 4 // peripherals: Timer2 channel A
	PB5 = portB + 5 // peripherals: Timer1 channel A
	PB6 = portB + 6 // peripherals: Timer1 channel B
	PB7 = portB + 7 // peripherals: Timer0 channel A
	PC0 = portC + 0
	PC1 = portC + 1
	PC2 = portC + 2
	PC3 = portC + 3
	PC4 = portC + 4
	PC5 = portC + 5
	PC6 = portC + 6
	PC7 = portC + 7
	PD0 = portD + 0 // peripherals: I2C0 SCL
	PD1 = portD + 1 // peripherals: I2C0 SDA
	PD2 = portD + 2
	PD3 = portD + 3
	PD7 = portD + 7
	PE0 = portE + 0
	PE1 = portE + 1
	PE3 = portE + 3 // peripherals: Timer3 channel A
	PE4 = portE + 4 // peripherals: Timer3 channel B
	PE5 = portE + 5 // peripherals: Timer3 channel C
	PE6 = portE + 6
	PF0 = portF + 0
	PF1 = portF + 1
	PF2 = portF + 2
	PF3 = portF + 3
	PF4 = portF + 4
	PF5 = portF + 5
	PF6 = portF + 6
	PF7 = portF + 7
	PG0 = portG + 0
	PG1 = portG + 1
	PG2 = portG + 2
	PG5 = portG + 5 // peripherals: Timer0 channel B
	PH0 = portH + 0
	PH1 = portH + 1
	PH3 = portH + 3 // peripherals: Timer4 channel A
	PH4 = portH + 4 // peripherals: Timer4 channel B
	PH5 = portH + 5 // peripherals: Timer4 channel C
	PH6 = portH + 6 // peripherals: Timer0 channel B
	PJ0 = portJ + 0
	PJ1 = portJ + 1
	PK0 = portK + 0
	PK1 = portK + 1
	PK2 = portK + 2
	PK3 = portK + 3
	PK4 = portK + 4
	PK5 = portK + 5
	PK6 = portK + 6
	PK7 = portK + 7
	PL0 = portL + 0
	PL1 = portL + 1
	PL2 = portL + 2
	PL3 = portL + 3 // peripherals: Timer5 channel A
	PL4 = portL + 4 // peripherals: Timer5 channel B
	PL5 = portL + 5 // peripherals: Timer5 channel C
	PL6 = portL + 6
	PL7 = portL + 7
)

// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
	switch {
	case p >= PA0 && p <= PA7:
		return avr.PORTA, 1 << uint8(p-portA)
	case p >= PB0 && p <= PB7:
		return avr.PORTB, 1 << uint8(p-portB)
	case p >= PC0 && p <= PC7:
		return avr.PORTC, 1 << uint8(p-portC)
	case p >= PD0 && p <= PD7:
		return avr.PORTD, 1 << uint8(p-portD)
	case p >= PE0 && p <= PE6:
		return avr.PORTE, 1 << uint8(p-portE)
	case p >= PF0 && p <= PF7:
		return avr.PORTF, 1 << uint8(p-portF)
	case p >= PG0 && p <= PG5:
		return avr.PORTG, 1 << uint8(p-portG)
	case p >= PH0 && p <= PH6:
		return avr.PORTH, 1 << uint8(p-portH)
	case p >= PJ0 && p <= PJ1:
		return avr.PORTJ, 1 << uint8(p-portJ)
	case p >= PK0 && p <= PK7:
		return avr.PORTK, 1 << uint8(p-portK)
	case p >= PL0 && p <= PL7:
		return avr.PORTL, 1 << uint8(p-portL)
	default:
		return avr.PORTA, 255
	}
}

// PWM is one PWM peripheral, which consists of a counter and two output
// channels (that can be connected to two fixed pins). You can set the frequency
// using SetPeriod, but only for all the channels in this PWM peripheral at
// once.
type PWM struct {
	num uint8
}

var (
	Timer0 = PWM{0} // 8 bit timer for PB7 and PG5
	Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
	Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
	Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
	Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
	Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
)

// Configure enables and configures this PWM.
//
// For the two 8 bit timers, there is only a limited number of periods
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
// 128µs, 1024µs, 4096µs, or 16384µs.
func (pwm PWM) Configure(config PWMConfig) error {

	switch pwm.num {
	case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
		// Calculate the timer prescaler.
		// While we could configure a flexible top, that would sacrifice one of
		// the PWM output compare registers and thus a PWM channel. I've chosen
		// to instead limit this timer to a fixed number of frequencies.
		var prescaler uint8
		switch config.Period {
		case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
			prescaler = 1
		case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
			prescaler = 2
		case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
			prescaler = 3
		case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
			prescaler = 4
		case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
			prescaler = 5
		default:
			return ErrPWMPeriodTooLong
		}

		if pwm.num == 0 {
			avr.TCCR0B.Set(prescaler)
			// Set the PWM mode to fast PWM (mode = 3).
			avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
			// monotonic timer is using the same time as PWM:0
			// we must adjust internal settings of monotonic timer when PWM:0 settings changed
			adjustMonotonicTimer()
		} else {
			avr.TCCR2B.Set(prescaler)
			// Set the PWM mode to fast PWM (mode = 3).
			avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
		}
	case 1, 3, 4, 5:
		// The top value is the number of PWM ticks a PWM period takes. It is
		// initially picked assuming an unlimited counter top and no PWM
		// prescaler.
		var top uint64
		if config.Period == 0 {
			// Use a top appropriate for LEDs. Picking a relatively low period
			// here (0xff) for consistency with the other timers.
			top = 0xff
		} else {
			// The formula below calculates the following formula, optimized:
			//     top = period * (CPUFrequency() / 1e9)
			// By dividing the CPU frequency first (an operation that is easily
			// optimized away) the period has less chance of overflowing.
			top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
		}

		// The ideal PWM period may be larger than would fit in the PWM counter,
		// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
		// speed lower with a prescaler to make the top value fit the maximum
		// top value.

		const maxTop = 0x10000
		var prescalingTop uint8
		switch {
		case top <= maxTop:
			prescalingTop = 3<<3 | 1 // no prescaling
		case top/8 <= maxTop:
			prescalingTop = 3<<3 | 2 // divide by 8
			top /= 8
		case top/64 <= maxTop:
			prescalingTop = 3<<3 | 3 // divide by 64
			top /= 64
		case top/256 <= maxTop:
			prescalingTop = 3<<3 | 4 // divide by 256
			top /= 256
		case top/1024 <= maxTop:
			prescalingTop = 3<<3 | 5 // divide by 1024
			top /= 1024
		default:
			return ErrPWMPeriodTooLong
		}

		// A top of 0x10000 is at 100% duty cycle. Subtract one because the
		// counter counts from 0, not 1 (avoiding an off-by-one).
		top -= 1

		switch pwm.num {
		case 1:
			avr.TCCR1A.Set(avr.TCCR1A_WGM11)
			avr.TCCR1B.Set(prescalingTop)
			avr.ICR1H.Set(uint8(top >> 8))
			avr.ICR1L.Set(uint8(top))
		case 3:
			avr.TCCR3A.Set(avr.TCCR3A_WGM31)
			avr.TCCR3B.Set(prescalingTop)
			avr.ICR3H.Set(uint8(top >> 8))
			avr.ICR3L.Set(uint8(top))
		case 4:
			avr.TCCR4A.Set(avr.TCCR4A_WGM41)
			avr.TCCR4B.Set(prescalingTop)
			avr.ICR4H.Set(uint8(top >> 8))
			avr.ICR4L.Set(uint8(top))
		case 5:
			avr.TCCR5A.Set(avr.TCCR5A_WGM51)
			avr.TCCR5B.Set(prescalingTop)
			avr.ICR5H.Set(uint8(top >> 8))
			avr.ICR5L.Set(uint8(top))
		}
	}
	return nil
}

// SetPeriod updates the period of this PWM peripheral.
// To set a particular frequency, use the following formula:
//
//	period = 1e9 / frequency
//
// If you use a period of 0, a period that works well for LEDs will be picked.
//
// SetPeriod will not change the prescaler, but also won't change the current
// value in any of the channels. This means that you may need to update the
// value for the particular channel.
//
// Note that you cannot pick any arbitrary period after the PWM peripheral has
// been configured. If you want to switch between frequencies, pick the lowest
// frequency (longest period) once when calling Configure and adjust the
// frequency here as needed.
func (pwm PWM) SetPeriod(period uint64) error {
	if pwm.num == 0 || pwm.num == 2 {
		return ErrPWMPeriodTooLong // TODO better error message
	}

	// The top value is the number of PWM ticks a PWM period takes. It is
	// initially picked assuming an unlimited counter top and no PWM
	// prescaler.
	var top uint64
	if period == 0 {
		// Use a top appropriate for LEDs. Picking a relatively low period
		// here (0xff) for consistency with the other timers.
		top = 0xff
	} else {
		// The formula below calculates the following formula, optimized:
		//     top = period * (CPUFrequency() / 1e9)
		// By dividing the CPU frequency first (an operation that is easily
		// optimized away) the period has less chance of overflowing.
		top = period * (uint64(CPUFrequency()) / 1000000) / 1000
	}

	var prescaler uint8

	switch pwm.num {
	case 1:
		prescaler = avr.TCCR1B.Get() & 0x7
	case 3:
		prescaler = avr.TCCR3B.Get() & 0x7
	case 4:
		prescaler = avr.TCCR4B.Get() & 0x7
	case 5:
		prescaler = avr.TCCR5B.Get() & 0x7
	}

	switch prescaler {
	case 1:
		top /= 1
	case 2:
		top /= 8
	case 3:
		top /= 64
	case 4:
		top /= 256
	case 5:
		top /= 1024
	}

	// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
	// counts from 0, not 1 (avoiding an off-by-one).
	top -= 1

	if top > 0xffff {
		return ErrPWMPeriodTooLong
	}

	switch pwm.num {
	case 1:
		// Warning: this change is not atomic!
		avr.ICR1H.Set(uint8(top >> 8))
		avr.ICR1L.Set(uint8(top))

		// ... and because of that, set the counter back to zero to avoid most of
		// the effects of this non-atomicity.
		avr.TCNT1H.Set(0)
		avr.TCNT1L.Set(0)
	case 3:
		// Warning: this change is not atomic!
		avr.ICR3H.Set(uint8(top >> 8))
		avr.ICR3L.Set(uint8(top))

		// ... and because of that, set the counter back to zero to avoid most of
		// the effects of this non-atomicity.
		avr.TCNT3H.Set(0)
		avr.TCNT3L.Set(0)
	case 4:
		// Warning: this change is not atomic!
		avr.ICR4H.Set(uint8(top >> 8))
		avr.ICR4L.Set(uint8(top))

		// ... and because of that, set the counter back to zero to avoid most of
		// the effects of this non-atomicity.
		avr.TCNT4H.Set(0)
		avr.TCNT4L.Set(0)
	case 5:
		// Warning: this change is not atomic!
		avr.ICR5H.Set(uint8(top >> 8))
		avr.ICR5L.Set(uint8(top))

		// ... and because of that, set the counter back to zero to avoid most of
		// the effects of this non-atomicity.
		avr.TCNT5H.Set(0)
		avr.TCNT5L.Set(0)
	}

	return nil
}

// Top returns the current counter top, for use in duty cycle calculation. It
// will only change with a call to Configure or SetPeriod, otherwise it is
// constant.
//
// The value returned here is hardware dependent. In general, it's best to treat
// it as an opaque value that can be divided by some number and passed to Set
// (see Set documentation for more information).
func (pwm PWM) Top() uint32 {
	switch pwm.num {
	case 1:
		// Timer 1 has a configurable top value.
		low := avr.ICR1L.Get()
		high := avr.ICR1H.Get()
		return uint32(high)<<8 | uint32(low) + 1
	case 3:
		// Timer 3 has a configurable top value.
		low := avr.ICR3L.Get()
		high := avr.ICR3H.Get()
		return uint32(high)<<8 | uint32(low) + 1
	case 4:
		// Timer 4 has a configurable top value.
		low := avr.ICR4L.Get()
		high := avr.ICR4H.Get()
		return uint32(high)<<8 | uint32(low) + 1
	case 5:
		// Timer 5 has a configurable top value.
		low := avr.ICR5L.Get()
		high := avr.ICR5H.Get()
		return uint32(high)<<8 | uint32(low) + 1
	}

	// Other timers go from 0 to 0xff (0x100 or 256 in total).
	return 256
}

// Counter returns the current counter value of the timer in this PWM
// peripheral. It may be useful for debugging.
func (pwm PWM) Counter() uint32 {
	switch pwm.num {
	case 0:
		return uint32(avr.TCNT0.Get())
	case 1:
		mask := interrupt.Disable()
		low := avr.TCNT1L.Get()
		high := avr.TCNT1H.Get()
		interrupt.Restore(mask)
		return uint32(high)<<8 | uint32(low)
	case 2:
		return uint32(avr.TCNT2.Get())
	case 3:
		mask := interrupt.Disable()
		low := avr.TCNT3L.Get()
		high := avr.TCNT3H.Get()
		interrupt.Restore(mask)
		return uint32(high)<<8 | uint32(low)
	case 4:
		mask := interrupt.Disable()
		low := avr.TCNT4L.Get()
		high := avr.TCNT4H.Get()
		interrupt.Restore(mask)
		return uint32(high)<<8 | uint32(low)
	case 5:
		mask := interrupt.Disable()
		low := avr.TCNT5L.Get()
		high := avr.TCNT5H.Get()
		interrupt.Restore(mask)
		return uint32(high)<<8 | uint32(low)
	}

	// Unknown PWM.
	return 0
}

// Period returns the used PWM period in nanoseconds. It might deviate slightly
// from the configured period due to rounding.
func (pwm PWM) Period() uint64 {
	var prescaler uint8
	switch pwm.num {
	case 0:
		prescaler = avr.TCCR0B.Get() & 0x7
	case 1:
		prescaler = avr.TCCR1B.Get() & 0x7
	case 2:
		prescaler = avr.TCCR2B.Get() & 0x7
	case 3:
		prescaler = avr.TCCR3B.Get() & 0x7
	case 4:
		prescaler = avr.TCCR4B.Get() & 0x7
	case 5:
		prescaler = avr.TCCR5B.Get() & 0x7
	}
	top := uint64(pwm.Top())
	switch prescaler {
	case 1: // prescaler 1
		return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
	case 2: // prescaler 8
		return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
	case 3: // prescaler 64
		return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
	case 4: // prescaler 256
		return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
	case 5: // prescaler 1024
		return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
	default: // unknown clock source
		return 0
	}
}

// Channel returns a PWM channel for the given pin.
func (pwm PWM) Channel(pin Pin) (uint8, error) {
	pin.Configure(PinConfig{Mode: PinOutput})
	pin.Low()
	switch pwm.num {
	case 0:
		switch pin {
		case PB7: // channel A
			avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
			return 0, nil
		case PG5: // channel B
			avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
			return 1, nil
		}
	case 1:
		switch pin {
		case PB5: // channel A
			avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
			return 0, nil
		case PB6: // channel B
			avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
			return 1, nil
		}
	case 2:
		switch pin {
		case PB4: // channel A
			avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
			return 0, nil
		case PH6: // channel B
			avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
			return 1, nil
		}
	case 3:
		switch pin {
		case PE3: // channel A
			avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
			return 0, nil
		case PE4: //channel B
			avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
			return 1, nil
		case PE5: //channel C
			avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
			return 2, nil
		}
	case 4:
		switch pin {
		case PH3: // channel A
			avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
			return 0, nil
		case PH4: //channel B
			avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
			return 1, nil
		case PH5: //channel C
			avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
			return 2, nil
		}
	case 5:
		switch pin {
		case PL3: // channel A
			avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
			return 0, nil
		case PL4: //channel B
			avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
			return 1, nil
		case PL5: //channel C
			avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
			return 2, nil
		}
	}
	return 0, ErrInvalidOutputPin
}

// SetInverting sets whether to invert the output of this channel.
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
// the time and low for the rest. Inverting flips the output as if a NOT gate
// was placed at the output, meaning that the output would be 25% low and 75%
// high with a duty cycle of 25%.
//
// Note: the invert state may not be applied on the AVR until the next call to
// ch.Set().
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
	switch pwm.num {
	case 0:
		switch channel {
		case 0: // channel A, PB7
			if inverting {
				avr.PORTB.SetBits(1 << 7) // PB7 high
				avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
			} else {
				avr.PORTB.ClearBits(1 << 7) // PB7 low
				avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
			}
		case 1: // channel B, PG5
			if inverting {
				avr.PORTG.SetBits(1 << 5) // PG5 high
				avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
			} else {
				avr.PORTG.ClearBits(1 << 5) // PG5 low
				avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
			}
		}
	case 1:
		// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
		// It will be set the following call to Set(), however.
		switch channel {
		case 0: // channel A, PB5
			if inverting {
				avr.PORTB.SetBits(1 << 5) // PB5 high
			} else {
				avr.PORTB.ClearBits(1 << 5) // PB5 low
			}
		case 1: // channel B, PB6
			if inverting {
				avr.PORTB.SetBits(1 << 6) // PB6 high
			} else {
				avr.PORTB.ClearBits(1 << 6) // PB6 low
			}
		}
	case 2:
		switch channel {
		case 0: // channel A, PB4
			if inverting {
				avr.PORTB.SetBits(1 << 4) // PB4 high
				avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
			} else {
				avr.PORTB.ClearBits(1 << 4) // PB4 low
				avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
			}
		case 1: // channel B, PH6
			if inverting {
				avr.PORTH.SetBits(1 << 6) // PH6 high
				avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
			} else {
				avr.PORTH.ClearBits(1 << 6) // PH6 low
				avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
			}
		}
	case 3:
		// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
		// It will be set the following call to Set(), however.
		switch channel {
		case 0: // channel A, PE3
			if inverting {
				avr.PORTE.SetBits(1 << 3) // PE3 high
			} else {
				avr.PORTE.ClearBits(1 << 3) // PE3 low
			}
		case 1: // channel B, PE4
			if inverting {
				avr.PORTE.SetBits(1 << 4) // PE4 high
			} else {
				avr.PORTE.ClearBits(1 << 4) // PE4 low
			}
		case 2: // channel C, PE5
			if inverting {
				avr.PORTE.SetBits(1 << 5) // PE4 high
			} else {
				avr.PORTE.ClearBits(1 << 5) // PE4 low
			}
		}
	case 4:
		// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
		// It will be set the following call to Set(), however.
		switch channel {
		case 0: // channel A, PH3
			if inverting {
				avr.PORTH.SetBits(1 << 3) // PH3 high
			} else {
				avr.PORTH.ClearBits(1 << 3) // PH3 low
			}
		case 1: // channel B, PH4
			if inverting {
				avr.PORTH.SetBits(1 << 4) // PH4 high
			} else {
				avr.PORTH.ClearBits(1 << 4) // PH4 low
			}
		case 2: // channel C, PH5
			if inverting {
				avr.PORTH.SetBits(1 << 5) // PH4 high
			} else {
				avr.PORTH.ClearBits(1 << 5) // PH4 low
			}
		}
	case 5:
		// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
		// It will be set the following call to Set(), however.
		switch channel {
		case 0: // channel A, PL3
			if inverting {
				avr.PORTL.SetBits(1 << 3) // PL3 high
			} else {
				avr.PORTL.ClearBits(1 << 3) // PL3 low
			}
		case 1: // channel B, PL4
			if inverting {
				avr.PORTL.SetBits(1 << 4) // PL4 high
			} else {
				avr.PORTL.ClearBits(1 << 4) // PL4 low
			}
		case 2: // channel C, PH5
			if inverting {
				avr.PORTL.SetBits(1 << 5) // PL4 high
			} else {
				avr.PORTL.ClearBits(1 << 5) // PL4 low
			}
		}
	}
}

// Set updates the channel value. This is used to control the channel duty
// cycle, in other words the fraction of time the channel output is high (or low
// when inverted). For example, to set it to a 25% duty cycle, use:
//
//	pwm.Set(channel, pwm.Top() / 4)
//
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
func (pwm PWM) Set(channel uint8, value uint32) {
	switch pwm.num {
	case 0:
		value := uint16(value)
		switch channel {
		case 0: // channel A
			if value == 0 {
				avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
			} else {
				avr.OCR0A.Set(uint8(value - 1))
				avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
			}
		case 1: // channel B
			if value == 0 {
				avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
			} else {
				avr.OCR0B.Set(uint8(value) - 1)
				avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
			}
		}
		// monotonic timer is using the same time as PWM:0
		// we must adjust internal settings of monotonic timer when PWM:0 settings changed
		adjustMonotonicTimer()
	case 1:
		mask := interrupt.Disable()
		switch channel {
		case 0: // channel A, PB5
			if value == 0 {
				avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR1AH.Set(uint8(value >> 8))
				avr.OCR1AL.Set(uint8(value))
				if avr.PORTB.HasBits(1 << 5) { // is PB1 high?
					// Yes, set the inverting bit.
					avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
				} else {
					// No, output is non-inverting.
					avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
				}
			}
		case 1: // channel B, PB6
			if value == 0 {
				avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR1BH.Set(uint8(value >> 8))
				avr.OCR1BL.Set(uint8(value))
				if avr.PORTB.HasBits(1 << 6) { // is PB6 high?
					// Yes, set the inverting bit.
					avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
				} else {
					// No, output is non-inverting.
					avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
				}
			}
		}
		interrupt.Restore(mask)
	case 2:
		value := uint16(value)
		switch channel {
		case 0: // channel A
			if value == 0 {
				avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
			} else {
				avr.OCR2A.Set(uint8(value - 1))
				avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
			}
		case 1: // channel B
			if value == 0 {
				avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
			} else {
				avr.OCR2B.Set(uint8(value - 1))
				avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
			}
		}
	case 3:
		mask := interrupt.Disable()
		switch channel {
		case 0: // channel A, PE3
			if value == 0 {
				avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR3AH.Set(uint8(value >> 8))
				avr.OCR3AL.Set(uint8(value))
				if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
					// Yes, set the inverting bit.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
				} else {
					// No, output is non-inverting.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
				}
			}
		case 1: // channel B, PE4
			if value == 0 {
				avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR3BH.Set(uint8(value >> 8))
				avr.OCR3BL.Set(uint8(value))
				if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
					// Yes, set the inverting bit.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
				} else {
					// No, output is non-inverting.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
				}
			}
		case 2: // channel C, PE5
			if value == 0 {
				avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR3CH.Set(uint8(value >> 8))
				avr.OCR3CL.Set(uint8(value))
				if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
					// Yes, set the inverting bit.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
				} else {
					// No, output is non-inverting.
					avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
				}
			}
		}
		interrupt.Restore(mask)
	case 4:
		mask := interrupt.Disable()
		switch channel {
		case 0: // channel A, PH3
			if value == 0 {
				avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR4AH.Set(uint8(value >> 8))
				avr.OCR4AL.Set(uint8(value))
				if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
					// Yes, set the inverting bit.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
				} else {
					// No, output is non-inverting.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
				}
			}
		case 1: // channel B, PH4
			if value == 0 {
				avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR4BH.Set(uint8(value >> 8))
				avr.OCR4BL.Set(uint8(value))
				if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
					// Yes, set the inverting bit.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
				} else {
					// No, output is non-inverting.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
				}
			}
		case 2: // channel C, PH5
			if value == 0 {
				avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR4CH.Set(uint8(value >> 8))
				avr.OCR4CL.Set(uint8(value))
				if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
					// Yes, set the inverting bit.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
				} else {
					// No, output is non-inverting.
					avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
				}
			}
		}
		interrupt.Restore(mask)
	case 5:
		mask := interrupt.Disable()
		switch channel {
		case 0: // channel A, PL3
			if value == 0 {
				avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR5AH.Set(uint8(value >> 8))
				avr.OCR5AL.Set(uint8(value))
				if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
					// Yes, set the inverting bit.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
				} else {
					// No, output is non-inverting.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
				}
			}
		case 1: // channel B, PL4
			if value == 0 {
				avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR5BH.Set(uint8(value >> 8))
				avr.OCR5BL.Set(uint8(value))
				if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
					// Yes, set the inverting bit.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
				} else {
					// No, output is non-inverting.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
				}
			}
		case 2: // channel C, PL5
			if value == 0 {
				avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
			} else {
				value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
				avr.OCR5CH.Set(uint8(value >> 8))
				avr.OCR5CL.Set(uint8(value))
				if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
					// Yes, set the inverting bit.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
				} else {
					// No, output is non-inverting.
					avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
				}
			}
		}
		interrupt.Restore(mask)
	}
}

// SPI configuration
var SPI0 = &SPI{
	spcr: avr.SPCR,
	spdr: avr.SPDR,
	spsr: avr.SPSR,
	sck:  PB1,
	sdo:  PB2,
	sdi:  PB3,
	cs:   PB0}
