;    "Divide-by-16" Quadrature divider for attiny13
;    Copyright 2006 Jeff Epler <jepler@unpythonic.net>
;
;    This program is free software; you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation; either version 2 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program; if not, write to the Free Software
;    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#warning "This software is untested"

#include <avr/io.h>
#define PIN _SFR_IO_ADDR(PINB)
#define PORT _SFR_IO_ADDR(PORTB)
#define DDR _SFR_IO_ADDR(DDRB)

#define ZLO r30
#define ZHI r31
#define XLO r26
#define XHI r27

.globl main
main:
    ; set prescaler to 0
    ; this strange way of setting it is required by the datasheet
    ldi r16, 0x80
    ldi r17, 0x00
    out _SFR_IO_ADDR(CLKPR), r16
    out _SFR_IO_ADDR(CLKPR), r17

    ; increase OSCCAL to the maximum value
    ; we're guaranteed to get at least 9.6MHz, but we could get up to
    ; 19.2MHz, or a polling rate of over 2MHz.
    ; running at up to 20MHz is permitted with VCC = 4.5V to 5.5V
    ldi r16, 0x7f
    out _SFR_IO_ADDR(OSCCAL), r16

    ; create outputs on pins 3 and 4
    ldi r16, (1<<3) | (1<<4)
    out DDR, r16

    ; load Z with the pointer to the table
    ldi ZHI, hi8(states)
    ldi ZLO, 0

1:
    ; 9 cycles = 1067 kHz @ 9.6MHz (attiny13 internal RC)
    in R17, PINB      ; 1  1
    andi R17, 3       ; 1  2

    or ZLO, R17       ; 1  3
    lpm ZLO, z        ; 3  6
        
    ; the board layout puts the outputs on PB3, PB4
    ; the bits have been arranged to appear in the right place,
    ; and pull-ups on the unused inputs don't matter
    out PORTB, ZLO    ; 1  7

    ; If that idea is wrong, then the following sequence should be used:
    ; it takes 3 cycles rather than 1, but still gives 800kHz
    ; mov R17, ZLO
    ; and R17, (1<<3) | (1<<4)
    ; out PORTB, r17

    rjmp 1b           ; 2  9


.balign 256
; Quadrature division table N=16
; Divide-by-16, quadrature output in bits 3 and 4 (tiny13 divider)
states: ; 256
.byte  0x00, 0x04, 0xf4, 0x00,   0x00, 0x04, 0x04, 0x20 /* 0x07 */  
.byte  0x08, 0x0c, 0xe4, 0x08,   0x08, 0x0c, 0x0c, 0x28 /* 0x0f */  
.byte  0x10, 0x14, 0xfc, 0x10,   0x10, 0x14, 0x14, 0x30 /* 0x17 */  
.byte  0x18, 0x1c, 0xec, 0x18,   0x18, 0x1c, 0x1c, 0x38 /* 0x1f */  
.byte  0x20, 0x04, 0x24, 0x20,   0x40, 0x24, 0x24, 0x20 /* 0x27 */  
.byte  0x28, 0x0c, 0x2c, 0x28,   0x48, 0x2c, 0x2c, 0x28 /* 0x2f */  
.byte  0x30, 0x14, 0x34, 0x30,   0x50, 0x34, 0x34, 0x30 /* 0x37 */  
.byte  0x38, 0x1c, 0x3c, 0x38,   0x58, 0x3c, 0x3c, 0x38 /* 0x3f */  
.byte  0x40, 0x44, 0x24, 0x40,   0x40, 0x44, 0x44, 0x60 /* 0x47 */  
.byte  0x48, 0x4c, 0x2c, 0x48,   0x48, 0x4c, 0x4c, 0x68 /* 0x4f */  
.byte  0x50, 0x54, 0x34, 0x50,   0x50, 0x54, 0x54, 0x70 /* 0x57 */  
.byte  0x58, 0x5c, 0x3c, 0x58,   0x58, 0x5c, 0x5c, 0x78 /* 0x5f */  
.byte  0x60, 0x44, 0x64, 0x60,   0x80, 0x64, 0x64, 0x60 /* 0x67 */  
.byte  0x68, 0x4c, 0x6c, 0x68,   0x88, 0x6c, 0x6c, 0x68 /* 0x6f */  
.byte  0x70, 0x54, 0x74, 0x70,   0x90, 0x74, 0x74, 0x70 /* 0x77 */  
.byte  0x78, 0x5c, 0x7c, 0x78,   0x98, 0x7c, 0x7c, 0x78 /* 0x7f */  
.byte  0x80, 0x84, 0x64, 0x80,   0x80, 0x84, 0x84, 0xa0 /* 0x87 */  
.byte  0x88, 0x8c, 0x6c, 0x88,   0x88, 0x8c, 0x8c, 0xa8 /* 0x8f */  
.byte  0x90, 0x94, 0x74, 0x90,   0x90, 0x94, 0x94, 0xb0 /* 0x97 */  
.byte  0x98, 0x9c, 0x7c, 0x98,   0x98, 0x9c, 0x9c, 0xb8 /* 0x9f */  
.byte  0xa0, 0x84, 0xa4, 0xa0,   0xc0, 0xa4, 0xa4, 0xa0 /* 0xa7 */  
.byte  0xa8, 0x8c, 0xac, 0xa8,   0xc8, 0xac, 0xac, 0xa8 /* 0xaf */  
.byte  0xb0, 0x94, 0xb4, 0xb0,   0xd0, 0xb4, 0xb4, 0xb0 /* 0xb7 */  
.byte  0xb8, 0x9c, 0xbc, 0xb8,   0xd8, 0xbc, 0xbc, 0xb8 /* 0xbf */  
.byte  0xc0, 0xc4, 0xa4, 0xc0,   0xc0, 0xc4, 0xc4, 0xe0 /* 0xc7 */  
.byte  0xc8, 0xcc, 0xac, 0xc8,   0xc8, 0xcc, 0xcc, 0xe8 /* 0xcf */  
.byte  0xd0, 0xd4, 0xb4, 0xd0,   0xd0, 0xd4, 0xd4, 0xf0 /* 0xd7 */  
.byte  0xd8, 0xdc, 0xbc, 0xd8,   0xd8, 0xdc, 0xdc, 0xf8 /* 0xdf */  
.byte  0xe0, 0xc4, 0xe4, 0xe0,   0x08, 0xe4, 0xe4, 0xe0 /* 0xe7 */  
.byte  0xe8, 0xcc, 0xec, 0xe8,   0x18, 0xec, 0xec, 0xe8 /* 0xef */  
.byte  0xf0, 0xd4, 0xf4, 0xf0,   0x00, 0xf4, 0xf4, 0xf0 /* 0xf7 */  
.byte  0xf8, 0xdc, 0xfc, 0xf8,   0x10, 0xfc, 0xfc, 0xf8 /* 0xff */
