; This code may not be used for commercial gain without prior agreement
; Copyright March 2000, Colin Fraser
;
; Modifications for Boss DR-110 by Benjamin Riggs 2004... cheers Colin :)
;
; revision history:
;
; 30/03/2004 - fixed a bug which could cause it to transmit midiclocks from
;              poweron. just inserted a 1 second delay at poweron and it
;              now seems to work fine.
;
; 08/03/2004 - modified code to sync a BOSS-DR110 to midi.
;              now outputs 12PPM sync on pin 3(GP4),
;              pin 2(GP5) goes high / pin6(GP1) goes high Z on start for 4ms,
;              pin 6(GP1) goes high / pin2(GP5) goes high Z on stop for 4ms,
;
;              modified code to send midi sync from Boss DR-110 internal clock
;              now outputs 24 midi clocks per metronome from 12ppm internal DR-110 clock
;              send midi start on rising edge of pulse on pin 2 (GP5)
;              send midi stop on rising edge of pulse on pin 6 (GP1)
;
; 15/05/2003 - fixed config option for MCLRE - oops!
;
; 26/11/2001 - added sync to midi conversion
;
; 6/3/2000 - modified midi bit timing to fix rare sync problem when consecutive
;            bytes were received with no gap
;
;
;
;   PIC12C508 Midi to Boss DR-110 Sync
;
;   This program uses Microchip assembler mnemonics and the Microchip
; MPASM assembler (http://www.microchip.com/).  Default config options
; are set in the __CONFIG line below: MCLRE on, CP off, WDT off, OSC=INTRC.
;
;                  _______  _______
;                 |       \/       |
;     (+5V) Vdd --+ 1            8 +-- Vss (GND)
;                 |                |
;   X1/CLKI/GP5 --+ 2            7 +-- GP0
;    (Sync Start) |                |  (Midi output) 
;   X2/CLKO/GP4 --+ 3            6 +-- GP1 
;    (Sync Clock) |                |  (Sync Stop)
;     GP3/!MCLR --+ 4            5 +-- GP2
;    (mode sel)   |  (PIC12C508)   |  (Midi input)
;                 +----------------+
;
;   This program converts incoming midi clock bytes
;   to Boss DR-110 compatible sync pulses
;
;

;	list	p=12c508
	list	p=12C508a
	radix	dec
;	include "p12c508.inc"
	include	"p12c508a.inc"

	__CONFIG	_MCLRE_OFF & _CP_OFF & _WDT_OFF & 0xFFE  ;Internal osc.

	cblock	0x07	;Variables
	midi			;incoming byte
	din				;output status
	dinstat			;din input status
	x
	y
	z
	endc

	org	0x00            

	movwf	OSCCAL

	movlw	b'11000111'	;Turn off T0CKI, prescale for TMR0 = 1:256
	option			;/

	movlw	0xFE
	tris	GPIO
	movlw	b'00000001'	;Preset outputs
	movwf	GPIO		;/

;	wait a second while powering up	
	movlw	0x1F	;31 * 32ms= 992ms
	movwf	y
poweronloop ;1sec
	clrf	TMR0
poweronloop1 ;32ms
	btfss	TMR0,7
	goto	poweronloop1
	decfsz	y,F		
	goto	poweronloop

; ====================
; midi to sync section
;
; din (written to GPIO for sync output)
;  b0 MIDI Ouput
;  b1 DIN stop. Always high as stop is triggered by selecting
;     data direction on port. output for +5V, input for HIGH Z.
;  b2 unused
;  b3 unused
;  b4 DIN Clock
;  b5 DIN start. Always high as with DIN stop.
;  b6 unused
;  b7 Run Status
; ====================

m2s
	movlw	b'00100011'
	movwf	din

; if time elapsed write out din to clear clock/cont bits

rescc
	movlw	b'00101110'	;Set the I/O direction, start and stop pins high Z
	tris	GPIO
	clrf	TMR0

;
;   rx - receive midi byte at 31250 bps.
;

rx
	btfss	GPIO,3		; Check mode select
	goto	s2m
	btfsc	TMR0,4		; Check timer
	goto	rescc
	btfsc	GPIO,2		; Detect start bit
	goto	rx

;
; Wait to get to centre of start bit.
;

	nop	;1
	nop	;2
	nop	;3
	nop	;4
	nop	;5
	nop	;6
	nop	;7
	nop	;8

	movlw	8		; Eight bits of data to get
	movwf	x		
	clrf	midi		
;
;   Delay one bit-width (32 cycles) to get to center of LSB.
;   Gather the bits into midi.
;
mbin	movlw	6		;(1)
	movwf	y		;(1)

del1	nop			;\
	decfsz	y,F		;((5 x 4) + 3) = 23
	goto	del1		;/

	bcf	STATUS,C	;(1) Default 0
	rrf	midi,F		;(1) Put 0 in MSB of recv, then
	btfsc	GPIO,2		;(1/2) sample the serial data and
	bsf	midi,7		;(1) set the bit if sample was=1
	decfsz	x,F		;((7 x 3) + 2) = 23, Do all bits
	goto	mbin		;/ [total cycles = 32, MSB = 31]
;
;   Time to first third of stop bit.  (22 cycles)
;
	movlw	6		;(1)
	movwf	y		;(1)
del2	decfsz	y,F		;((6 x 3) + 2) = 20

	goto	del2		;/
	btfss	GPIO,2		;Stop bit showed up?
	goto	rx		;If not, wait for another byte

;
; Do clock stuff
; midi reg now contains received midi byte
;

;  Check for Start

	movfw	midi
	xorlw	0xFA
	btfsc	STATUS,Z
	call	clockstart

;  Check for Clock pulse

	movfw	midi
	xorlw	0xF8
	btfsc	STATUS,Z
	call	clockpulse

;  Check for Stop

	movfw	midi
	xorlw	0xFC
	btfsc	STATUS,Z
    call	clockstop
	goto	rx

;  Set start pin high / stop pin high Z

clockstart
	bsf		din,7		;set run status

	movlw	b'00001110'	;Set the I/O direction
	tris	GPIO
	movfw	din			;pulse start pin
	movwf	GPIO
	clrf	TMR0
	retlw	0x00

;	Set stop pin high / start pin high Z

clockstop
	bcf		din,7		;clear run status

	bcf		din,4		;clear clock pin

	movlw	b'00101100'	;Set the I/O direction
	tris	GPIO
	movfw	din			;pulse stop pin.
	movwf	GPIO
	clrf	TMR0
	retlw	0x00

;	Toggle clock pin

clockpulse
	btfss	din,7		;check run status
	retlw	0x00		;continue if running

	movlw	0x10
	xorwf	din,1		;toggle clock pin
	movfw	din
	movwf	GPIO
	retlw	0x00

; ====================
; sync to midi section
;
; dinstat
;  b0 previous status of clock pin
;  b1 previous status of start pin
;  b2 previous status of start pin
;  b4 current run status. set on start, reset on stop
;  b5 initial clock status. used to ensure first midi clock on first rising edge only.
;  b6 unused
;  b7 unused
; ====================

s2m
	movlw	b'00111110'	;Set the I/O direction
	tris	GPIO

	clrw
	movwf	dinstat

starttest
	btfsc	GPIO,3		; check mode select	
	goto	m2s			; go back to midi to sync if required

	btfss	GPIO,5		; Check for DIN start = 1
	goto	lowstart	; branch if start = 0
	goto	highstart	; branch if start = 1

clktest	
	btfss	GPIO,4		; Check DIN Clock
	goto	lowclock	; branch if clock = 0
	goto	highclock	; branch if clock = 1

stoptest
	btfss	GPIO,1		; Check for DIN stop = 1
	goto	lowstop		; branch if stop = 0
	goto	highstop	; branch if stop = 1

lowstart
	btfss	dinstat,1	; was it already 0 ?
	goto	clktest		; branch if start pin not changed

	bcf		dinstat,1	; start status = 0

	goto	clktest

highstart
	btfsc	dinstat,1	; was it already 1 ?
	goto	clktest		; branch if startpin not changed

	bsf		dinstat,1	; start status = 1

	btfsc	dinstat,3	; are we already running?
	goto	clktest		; skip midi tx if yes

	bsf		dinstat,3	; run status = 1

	movlw	0xFA		; tx midi start byte
	call	miditx
	
	goto	clktest
	
lowstop
	btfss	dinstat,2	; was it already 0 ?
	goto	starttest	; branch if stop pin not changed

	bcf		dinstat,2	; stop status = 0

	goto	starttest

highstop
	btfsc	dinstat,2	; was it already 0 ?
	goto	starttest	; branch if stop pin not changed

	bsf		dinstat,2	; stop status = 1

	btfss	dinstat,3	; are we already stopped?
	goto	starttest	; skip midi tx if yes

	bcf		dinstat,3	; run status = 0

	btfsc	dinstat,4
	bcf		dinstat,4	; clear initial clock status

	movlw	0xFC		; tx midi start byte
	call	miditx

	goto	starttest

lowclock

	btfss	dinstat,0	; was previous clock 1 ?
	goto	stoptest	; don't send midi if no
	bcf		dinstat,0	; clock status = 0

	btfss	dinstat,3	; check run status
	goto	stoptest	; don't send midi if not running

	btfss	dinstat,4	; have we had an initial rising edge clock ?
	goto	stoptest	; don't send midi if no

	movlw	0xF8		; tx midi clock byte
	call	miditx

	goto	stoptest

highclock

	btfsc	dinstat,0	; was previous clock 0 ?
	goto	stoptest	; don't send midi if no
	bsf		dinstat,0	; clock status = 1

	btfss	dinstat,3	; check run status
	goto	stoptest	; don't send midi if not running

	btfss	dinstat,4
	bsf		dinstat,4	; set initial clock status

	movlw	0xF8		; tx midi clock byte
	call	miditx

	goto	starttest


miditx
	movwf	x		; store tx byte
	clrf	GPIO		; start bit

;pause for one bit
	movlw	8		;(1)
	movwf	z		;(1)
pause1
	decfsz	z		;(1/2) \
	goto	pause1		;(2)   / * z = 8 * 3 + 1 = 25
	
	movlw	0x8		;(1)
	movwf	y		;(1)
	nop			;(1)

mtxloop
	movfw	x		;(1)
	movwf	GPIO		;(1) lsb of tx byte to midi out

;pause for one bit
	movlw	7		;(1)
	movwf	z		;(1)
pause2
	decfsz	z		;(1/2) \
	goto	pause2		;(2)   / * z = 7 * 3 + 1 = 22 
	
	bsf	STATUS,C	;(1)
	rrf	x,1		;(1)
	nop			;(1)
	nop			;(1)
	decfsz	y		;(1)
	goto	mtxloop		;(2)
	
	nop
	retlw	0x00

	end
