; PROGRAM: SMPTE na MIDI
; 
; Program prevadi casovy kod SMPTE na MIDI a slouzi k synchronizaci pc s prof. videorecorderem
; zarizeni se pripojuje k MIDI portu pc pres GND,+5V, a MIDI in    
; SMPTE je prijiman pres dekoder RS 422 - slouzi k potlaceni rusivych napeti
;
;
;----------  Ukazka rolozeni procesoroveho casu
;  FFJJxxxxFFDDxxxxSSJJxxxxSSDDxxxxMMJJxxxxMMDDxxxxHHJJxxxxHHDDxxxx0011111111111101
;  1248    12      1248    124     1248    124     1248    12
;            xx                  xx                  xx                  xx
;  getb    bbxxbbbb getb   bbbbbbxx getb    getb   bbxxbbbb getb   bbbbbbxx11111101
;----------
;  celkem 80 bitu
;  1=2x zmena za 5us
;  0=1x zmena za 5us
;  normalne 25x za sekundu



		device	pic16c54,xt_osc,wdt_off,protect_off

		reset	begin



smptein		=	ra.2		;vstup timekodu, vystup z prijimace RS422

midi_out	=	ra.3		;vystup MIDI, propojen primo do PC

testout		=	rb.4		;vystup pro test led

disableout	=	rb.7		;disable output when log 0

                org     8              ;Start volne pameti RAM  8/12 =51/84


stat		ds	1		;stavovy registr (pouzite jsou bity)
lastin		=	stat.2		;predchozi prijaty bit
inbit		=	stat.0		;odlozeni bitu pri prjmu 

delka		ds	1		;mereni delky periody na vstupu SMPTE
bit_cntr	=	delka		;pocitadlo pro posilani MIDI bitu

tmp		ds	1		;docasny odkladaci registr pri vstupu SMPTE
delay_cntr	=	tmp		;cekaci smycka pro posilani MIDI

incntr		ds	1		;pocet prijatych bitu na zacatku sekvence, pocitadlo
					;pro prevod do HEX kodu

inbyte		ds	1		;posledni prijaty SMPTE BYTE
comm_byte	ds	1		;odesilany MIDI byte

midicntr	ds	1		;pocitadlo, ktery MIDI kod se bude posilat (0 az 7)



datamd		ds	1		;desitky minut		prijate ze SMPTE 
datamj		ds	1		;jednotky minut		prijate ze SMPTE
datasd		ds	1		;desitky sekund		prijate ze SMPTE
datasj		ds	1		;jednotky sekund	prijate ze SMPTE
datafd		ds	1		;desitky frame		prijate ze SMPTE
datafj		ds	1		;jednotky frame		prijate ze SMPTE
frames		ds	2		;2 byte - pripraveno na poslani do MIDI
secs		ds	2		;2 byte - pripraveno na poslani do MIDI
mins		ds	2		;2 byte - pripraveno na poslani do MIDI
datam		ds	2		;2 byte - pripraveno na poslani do MIDI
endb		ds	1		;test na konec pameti
					;  (specialni kody ohledne typu MIDI)

                org     0               ;Zacatek programu

;------------------------------------------------------------
		; Nastaveni I/O portu
;------------------------------------------------------------

begin		mov	endb,#255	;test prekroceni pameti RAM jinak nic
		mov	ra,#00000000
		mov	rb,#0
		mov	!ra, #00010111b	;vstupy, ra.3 - MIDI vystup
		mov	!rb, #11101111b ;vsechny vstupy RB.0 test out
		mov	midicntr,#0	;prvni znak na poslani ....

beginall	
;------------------------------------------------------------
		; Zacatek programove smycky
;------------------------------------------------------------

getstart
		clrb	testout			;clear test byte
		mov	incntr,#0
:again		inc	incntr
		CALL	getsmptebit		;prijmi jeden SMPTE bit
		jc	:again			;prijimej dokud je 1 a pocitej je
		cjb	incntr,#6,getstart	;pokud je bit 0 a jednicek bylo mene nez 5, delej znovu
		CALL	getsmptebit		;jinak prijmi dalsi bit, musi byt jedna
		jnc	getstart		;pokud neni, tak vse znova

	;tedy ceka se na sekvenci 1111101, coz je konec predchozi SMPTE sekvence

		setb	testout			;set test byte
		jnb	disableout,nosend
		
		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o frame jednotky
		mov	datafj,inbyte   

		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getxx			;posli MIDI kod v case prijmu 2 bitu, misto bitu dej 00
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		mov	datafd,inbyte		;uloz prijaty byte do frame desitek 
						;(protoze se uplatnuji pouze 0. a 1. bit, nevadi, ze tam jsou ve 2. a 3. nuly)

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o sec jednotky
		mov	datasj,inbyte

		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getxx			;posli MIDI kod v case prijmu 2 bitu, misto bitu dej 00
		mov	datasd,inbyte		;uloz prijaty byte do sec desitek 
						;maskovane nuly v ted nevadi

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o min jednotky
		mov	datamj,inbyte

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o min desitky
		mov	datamd,inbyte

						;tak a hodiny uz nas nezajimaji.......
						;a jinak uz prijem neni moc dulezity

		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getxx			;posli MIDI kod v case prijmu 2 bitu
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbyte			;proc neprijmout rovnou 8
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getbb			;prijmi 2 bity ze SMPTE
		call 	getxx			;posli MIDI kod v case prijmu 2 bitu

backto
		jnb	midicntr.3,notall	;Data prevadej pouze kazde 2. slovo



	; Nasleduje dekodovani a prevody kodu     (sezere to 76 az 142us)

		and	datafj,#00001111b	;vymaz vsechny uzivatelske a nepotrebne bity
		and	datafd,#00000011b	;vymaz vsechny uzivatelske a nepotrebne bity
		and	datasj,#00001111b	;vymaz vsechny uzivatelske a nepotrebne bity
		and	datasd,#00000111b	;vymaz vsechny uzivatelske a nepotrebne bity
		and	datamj,#00001111b	;vymaz vsechny uzivatelske a nepotrebne bity
		and	datamd,#00000111b	;vymaz vsechny uzivatelske a nepotrebne bity

	; Prevod z BCD kodu do HEX:

		mov	frames,datafj	     	;Frame= frame jednotky + 10 * frame desitky
		mov	incntr,datafd		
		inc	incntr
		jmp	:nextf
:desframe	add	frames,#10
:nextf		djnz	incntr,:desframe

		mov	secs,datasj		;Secs= sec jednotky + 10 * sec desitky
		mov	incntr,datasd
		inc	incntr
		jmp	:nexts
:dessec		add	secs,#10
:nexts		djnz	incntr,:dessec

		mov	mins,datamj		;Mins= min jednotky + 10 * min desitky
		mov	incntr,datamd
		inc	incntr
		jmp	:nextm
:desmin		add	mins,#10
:nextm		djnz	incntr,:desmin

	;Rozlozeni do dvou BYTE na HI a LO:

		mov	frames+1,frames		;Rozkopiruj data vzdy i do 2. byte
		mov	secs+1,secs
		mov	mins+1,mins
		swap	frames+1		;Ve druhem byte zamen horni a dolni bity
		swap	secs+1
		swap	mins+1

		and	frames,#00001111b	;Orizni horni bity
		and	frames+1,#00001111b
		and	secs,#00001111b
		and	secs+1,#00001111b
		and	mins,#00001111b
		and	mins+1,#00001111b

		or	frames+1,#00010000b	;Prodej potrebna cisla bitu (nutne pro posilani MIDI)
		or	secs,#00100000b
		or	secs+1,#00110000b
		or	mins,#01000000b
		or	mins+1,#01010000b

		mov	datam,#060h		;Napln posledni dva byte fixni konstantou
		mov	datam+1,#072h		;Napln posledni dva byte fixni konstantou

		and	midicntr,#4		;Vynuluj dva spodni bity counteru MIDI 

		movb	lastin,smptein		;Vnut hodnotu vstupu SMPTE
		mov	delka,#12		;Trosku poposum SMPTE couter, aby vse zustalo v synchronu
notall		call	getsmpte2		;Prijmi SMPTE bit (teoriteicky by to mela byt jednicka-8bit od konce kodu)
		jmp 	beginall		;Vse opakuj znova... melo by nasledovat sekvence SMPTE 1111101 .......



;------------------------ Prijmi 1 SMPTE BYTE (8 bitu) ------------------
getbyte		mov	incntr,#8         
:again		CALL	getsmptebit
		rrf	inbyte
		djnz	incntr,:again
		ret
;------------------------ Prijmi 2 SMPTE bity --------------------------------- 
getbb		CALL	getsmptebit
		rrf	inbyte
		CALL	getsmptebit
		rrf	inbyte
		ret
;------------------------ Posli MIDI sekvenci ---------------------------------
;    Doba posilani je asi 650 us. Behem te doby prisel jeden SMPTE bit (500 us),
;    ten se ztrati a nahradi 0. Dalsimu SMPTE bitu se vsugeruje,ze counter uz 
;    napocital 150us.  Tak lze zachovat synchronizaci prijmu a vysilani.
;    Druhy SMPTE bit by se teoreticky take dal prijmout, ale neni to nutne,
;    tak ho take nahradim 0	

getxx	 	clrb	c				;Simulace prijmu dvou nul
		rrf	inbyte
		rrf	inbyte		

;    MIDI - popis protokolu...
;    teoreticky se 100x za sekundu (pro 25 snimku) posila sekvence 2 byte
;    prvni je vzdy xF1h, druhy obsahuje ve vyssich bitech informaci o datech, 
;    v nizsich pak data cele to vypada nejak takto:
;	F1 0x   kde x je nizsi HEXA byte frame
;	F1 1x   kde x je vyssi HEXA byte frame
;	F1 2x   kde x je nizsi HEXA byte sec
;	F1 3x   kde x je vyssi HEXA byte sec
;	F1 4x   kde x je nizsi HEXA byte min
;	F1 5x   kde x je vyssi HEXA byte min
;	F1 6x   kde x je nizsi HEXA byte hour  (zde vzdy 0)
;	F1 7x   kde x je specialni kod typu sync. zde 2 = 25sn/sec

;     cela sekvence se periodicky opakuje 12.5x za sekundu

;     zde se pak 2 byte MIDI posilaji po kazdych prijatych 20 bitech SMPTE  (100Hz)

		mov	comm_byte,#0f1h		
		call	sendmidibyte		; posli prvni MIDI byte (vzdy F1)
		mov	fsr,#frames		; nastav registr neprime adresy reristr frames
		add	fsr,midicntr		; pridej hodnotu citace MIDI  (0 az 7)
						;   (nastav aktualni registr, ktery se bude posilat)
		mov	comm_byte,indf		; posli jej
		call	sendmidibyte
		movb	lastin,smptein		; poprav skutecny stav vstupu SMPTE (pokud by se mezitim zmenil)
		inc	midicntr		; zvys midi couner = priste se posila dalsi datovy byte
		mov	delka,#10		; trosku poposun timer rutiny detekce SMPTE (cca o 150us)
		jmp	getsmpte2		;   takze stihne vyhodnotit prave prijimany SMPTE bit

;------------------Prijmi jeden SMPTE bit  ---------------------
;  vysledek je v C a je 0 pro 0 ci chybu, jinak jednicka ------

;  1 = 2x zmena na vstupu za 500us
;  0 = 1x zmena na vstupu za 500us

getsmptebit	mov	delka,#0		;vynuluj casove positadlo
getsmpte2					;skok pro rutinu, ktere maji posunuty cas prijmu
:again		inc	delka			;zvys pocitadlo
		cja	delka,#45,:error	;pokud cas prekrocil cca 650 us, tak error
		mov	tmp,ra
		xor 	tmp,stat
		and	tmp,#00000100b		;test na zmenu vstupu
		jz	:again			;opakuj dokud nenu zmena
		cja	delka,#25,:error   	;pokud je delka vetsi, nez 320us, tak je prijata 0
		movb	lastin,smptein		;pokud je 1, tak: uloz hodnotu vstupniho bitu...
		mov	delka,#0		;znovu vynuluj delku
:again2		inc	delka			
		cja	delka,#45,:error	;pokud je cas delsi nez 650us, tak error
		mov	tmp,ra	
		xor 	tmp,stat
		and	tmp,#00000100b
		jz	:again2			;znovu detekuj cas...
:error		clrb	inbit
		csa	delka,#25		;musi byt opet mensi nez 320us, jinak je error
		setb	inbit		
		movb	lastin,smptein		;uloz hodnotu vstupniho bitu
		movb	c,inbit			;uloz prijaty bit do C
		ret

;--------------- Posle jeden byte na MIDI ------------------------------------------
;  klasicka rutina na posilani ser. informace.
;  rychlost je 31250Bd, 1 start bit, 1 stop bit
;  pokud je pripojeno prim do pc, tak je stav pozitivni, jinak nutno znegovat vystup

SENDMIDIBYTE				
		mov	bit_cntr,#8	;8 bitu
		clrb	midi_out	;nebo setb...   nastav START BIT
		mov	delay_cntr,#9	;cekej 1 bit
:ww1		djnz	delay_cntr,:ww1

:nextbit	rrf	comm_byte	;posli bit - od nejnizsiho se zacina
		movb	midi_out,c	;na vystup s nim (jinak nutno ,/c)
		mov	delay_cntr,#7	;zase cekej
		nop
		nop
:ww2		djnz	delay_cntr,:ww2
		djnz	bit_cntr,:nextbit	;opakuj pro vsech 8 bitu
		nop
		nop
		nop
		nop
		setb	midi_out	;posli stop bit  (jinak mozno clrb)
		mov	delay_cntr,#7	;cekej 1 bit
		nop
:ww3		djnz	delay_cntr,:ww3
		ret


;------------------------ When disableout just receive data-----------------

nosend		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o frame jednotky
		mov	datafj,inbyte   

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o frame desitky
		mov	datafd,inbyte		;uloz prijaty byte do frame desitek 

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o sec jednotky
		mov	datasj,inbyte

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o sec desitky
		mov	datasd,inbyte		;uloz prijaty byte do sec desitek 
						;maskovane nuly v ted nevadi

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o min jednotky
		mov	datamj,inbyte

		call 	getbyte			;prijmi BYTE ze SMPTE - jedna se o min desitky
		mov	datamd,inbyte

		call 	getbyte			;proc neprijmout rovnou 8
		call 	getbyte			;proc neprijmout rovnou 8
		call 	getbyte			;proc neprijmout rovnou 8

		jmp	backto