; RS232<->MIDI Gateway for use with Host Port software and/or YAMAHA CBX driver
; Hardware/Software : Benoît BOUCHEZ for ELEKTOR
; Version 1.00 : 10/1999
; Version 1.10 : 05/2000 : 	- New buffering system for MIDI->RS
;			- P1.6 pushbutton to clear overflow LED
;			- MIDI RESET message on RS232 clears overflow LED
;			- Simplified RTS/CTS handling (CTS set to 1)
;			- T2 = test output for MIDI Clock

	CPU "C:\WINC32\8051.TBL"	; Use 8051 assembly model
	HOF "INT8"		; Output format is Intel HEX

	INCL "INC80320.ASM"	; Add 80C320 specific registers

; Target specific definitions
RI_MIDI:		EQU RI0		; MIDI is on port 0
TI_MIDI:		EQU TI0
SBUF_MIDI:	EQU SBUF0
RI_RS:		EQU RI1		; RS232 is on port 1
TI_RS:		EQU TI1
SBUF_RS:	EQU SBUF1
RTS_RS:		EQU P1+4	; RTS line status
CTS_RS:		EQU P1+5	; CTS line control
RAZ_OVR:	EQU P1+6	; Push Button input for overflow clear
LED_OVR:	EQU P1+7	; Overflow indicator

; System variables (bytes)
	ORG $50
CHAR_RS:	DFS 1		; Byte read on RS
PTR_IN:		DFS 1		; FIFO input pointer
PTR_OUT:	DFS 1		; FIFO output pointer
MIDI_DATA:	DFS 1		; MIDI Byte waiting for retransmission on RS

	ORG $80			; Indirect RAM = FIFO Buffer
FIFO:		DFS 128

; System variables (bits)
	ORG $70
MIDI_DATA_AVAIL:	DFS 1

; CODE Section
	ORG $0

	LJMP START		; RESET vector

; ****************************************
; Microcontroller configuration
; ****************************************
START:

; ****************************************
; Timer 2 is clock source for serial port 0 (31250 bauds * 16=500kHz)
	MOV T2MOD, #$02		; Outputs clock for test purpose
	CLR CT2			; Source Timer 2 = Internal clock after divide / 2

	SETB TCLK2		; Set Timer 2 as Baud Rate Generator
	SETB RCLK2

	CLR TR2			; Freeze Timer 2

	MOV RCAP2L, #$E8		; 65536-24=65512 (FFE8)
	MOV RCAP2H, #$FF
	MOV TL2, #$E8
	MOV TH2, #$FF

	SETB TR2

; Setup serial port 0
	MOV SCON0, #01010000B

; ****************************************
; Timer 1 is clock source for serial port 1 (38400 * 16=614400Hz from external 1.8432MHz)

	SETB SMOD1		; Baud Clock divider off
	CLR TR1			; Freeze Timer 1
	MOV TMOD, #$62		; Setup Timer 1 and Timer 0
	MOV TH1, #253
	MOV TL1, #253
	SETB TR1

; Setup serial port 1
	MOV SCON1, #01010000B

; ****************************************
; Setup application variables
	CLR LED_OVR
	MOV PTR_IN, #FIFO	; FIFO init
	MOV PTR_OUT, #FIFO
	SETB TI_MIDI
	SETB TI_RS
	CLR RI_MIDI
	CLR RI_RS
	CLR MIDI_DATA_AVAIL	; No MIDI data available
	CLR CTS_RS		; Activate CTS (allow transmission)

; Main application loop
MAINLOOP:
	LCALL GETMIDI
	LCALL MIDI2RS

	LCALL GETRS
	LCALL RS2MIDI

	JB RAZ_OVR, END_MAINLOOP	; If CLEAR pushbutton depressed
	CLR LED_OVR			; then clears LED

END_MAINLOOP:
	JMP MAINLOOP

; ********************************************
; GETMIDI : get MIDI Byte and store it temporarily to let RS232 buffer empty completely
; before sending it
GETMIDI:
	JNB RI_MIDI, END_GETMIDI		; MIDI Byte available
	MOV A, SBUF_MIDI			; Yes : get it
	CLR RI_MIDI
	MOV MIDI_DATA, A			; Save it
	SETB MIDI_DATA_AVAIL		; Mark MIDI byte pending

CHK_SYS_RESET:
	CJNE A, #$FF, END_GETMIDI		; If MIDI System Reset message
	CLR LED_OVR			; then clear overflow LED

END_GETMIDI:
	RET

; ********************************************
; MIDI2RS : Send received MIDI byte to RS232
MIDI2RS:
	JNB TI_RS, END_MIDI2RS		; If RS232 buffer empty
	JNB MIDI_DATA_AVAIL, END_MIDI2RS	; and MIDI byte to send
	MOV A, MIDI_DATA			; Get back MIDI byte
	CLR TI_RS
	CLR MIDI_DATA_AVAIL		; Mark no MIDI byte pending
	MOV SBUF_RS, A			; Send MIDI byte to RS232

END_MIDI2RS:
	RET

; ********************************************
; GETRS : Read RS received byte et store it in FIFO
GETRS:
	JNB RI_RS, END_GETRS		; Something available on RS port ?
GETRS_CHAR:
	MOV A, SBUF_RS			; Yes :
	MOV CHAR_RS, A			; CHAR_RS:=SBUF_RS
	CLR RI_RS			; Prepare uC for next RS byte

CHK_FIFO:				; Enough room in FIFO ?
	MOV A, PTR_IN			; Yes :
	INC A				;  A:=PTR_IN+1
	JNZ NO_LOOP_IN			; If FIFO loop back
	MOV A, #FIFO			; then A:=Debut file
		
NO_LOOP_IN:
	CJNE A, PTR_OUT, FIFO_LIBRE	; Compare A with PTR_OUT

FIFO_PLEIN:				; FIFO is full :
	SETB LED_OVR			; Show the problem to user
	JMP END_GETRS

FIFO_LIBRE:				; FIFO is not full :
	MOV PTR_IN, A			; PTR_IN:=Computed pointer
	MOV R0, A
	MOV A, CHAR_RS
	MOV @R0, A			; FIFO[PTR_IN]:=CHAR_RS
	
END_GETRS:
	RET

; ********************************************
; RS2MIDI : Send to MIDI datas stored in FIFO if MIDI port is free
RS2MIDI:
	MOV A, PTR_OUT			; Something waiting in FIFO ?
	CJNE A, PTR_IN, DATA_IN_FIFO	; Yes if PTR_IN<>PTR_OUT
	JMP END_RS2MIDI

DATA_IN_FIFO:
	INC A				; Compute pointer to next data
	JNZ NO_LOOP_OUT		; If FIFO end is reached
	MOV A, #FIFO			; return to beginning

NO_LOOP_OUT:
	JNB TI_MIDI, END_RS2MIDI		; If MIDI buffer not empty, leave process (PTR_OUT is not yet affected)
	CLR TI_MIDI

SBUF_MIDI_VIDE:
	MOV PTR_OUT, A			; Store new pointer value
	MOV R0, A
	MOV A, @R0			; Get data waiting in FIFO
	MOV SBUF_MIDI, A			; Send it on MIDI

END_RS2MIDI:
	RET

	END
