// ================================
// generate sine wave by controlling MCP4725
//		(c) 2021	1YEN Toru
//
//
//		2021/08/21	ver.1.00
//
// ================================
asm		"mcoc_irom.mif"
#asm		"mcoc_irom.mem"
#asm		"mcoc_irom.dat2",bit16,list
incl	"mcoc115.incl"
incl	"mcvm_lib_wire_h.incl"
# ================================
# string macros
def		prog_name,"sin_wave"
def		prog_vers,"1.00"
def		s,"#"							// simulation="" / real fpga="#"
# constants
equ		max_lbuf,64						// size of lbuf
equ		baud,9600						// baud rate
equ		uart_baud,fcpu/baud-1			// uartbaud setting
equ		stws_baud,fcpu/400000-1			// i2c: clock speed
$(s)// ********************************
$(s)equ		stws_baud,fcpu/400000/3-1	// i2c: clock speed for sim
$(s)// ********************************
equ		i2c_mcp4725,0x60				// i2c: MCP4725 slave address
equ		chr_lf,0x0a						// lf code
equ		chr_cr,0x0d						// cr code
equ		chr_cma,0x2c					// comma ',' code
equ		sin_msec,500					// length of sine wave [ms]
# variables
equ		va,ramtop
equ		sin_hz,va						// frequency of sine wave [Hz]
equ		va,va+2
equ		sin_peri,va						// period of sine wave [us]
equ		va,va+2
equ		sin_tsiz,va						// sin table size [word]
equ		va,va+2
equ		static_va,va
# array variables
equ		lbuf,va							// receive data buffer
equ		va,va+max_lbuf
equ		array_va,va
# ================================

// ================================
// setup
// ================================

// set sp to the bottom of ram area
ldwi	r7,idrgramt						// idrg: ram top addr.
ldw		r7,[r7]
ldwi	r6,idrgrams						// idrg: ram size
ldw		r6,[r6]
add		r7,r6
movtc	sp,r7

// clear variables
ldwi	r7,ramtop
ldwi	r1,static_va
ldbiu	r0,0
st_clrvar:
stw		[r7],r0
addi	r7,2
cmp		r7,r1
blo		st_clrvar

// uart setting: RXE, TXE, baud rate
ldwi	r7,uartbaud
ldwi	r0,uart_baud
stw		[r7],r0
ldwi	r7,uartctl
ldbiu	r0,uart_rxe|uart_txe
stw		[r7],r0

// stwser setting: clock speed
ldwi	r0,stws_baud
ldwi	r7,stwmbaud
stw		[r7],r0

// check mulc16 and divc32
ldbiu	r0,1
mov		r1,r0
mulu	r0,r1
cmpi	r0,1
beq		chk_mul_pass
ldwi	r1,lab_str_err_no_mul
jalwr7	print
chk_mul_pass:
ldbiu	r0,1
mov		r1,r0
divu	r0,r1
cmpi	r0,1
beq		chk_div_pass
ldwi	r1,lab_str_err_no_div
jalwr7	print
chk_div_pass:

// send RDY
ldwi	r1,lab_str_msg_rdy
jalwr7	print

$(s)// ********************************
$(s)// for simulation
$(s)// setup dummy slave
$(s)ldbiu	r0,i2c_mcp4725
$(s)ldwi	r7,stwsadr
$(s)stw		[r7],r0
$(s)// sine wave
$(s)ldwi	r0,440
$(s)ldwi	r7,sin_hz
$(s)stw		[r7],r0
$(s)jalwr7	sin_wave
$(s)// simulation finish
$(s)ldwi	r0,simpass
$(s)ldwi	r7,simfnsh
$(s)stw		[r7],r0
$(s)bra		pcnt-2
$(s)// ********************************

// ================================
// loop
// ================================
loop:

// ================================
// serial monitor handling
// check uartctl.RAVL
ldwi	r7,uartctl
ldw		r0,[r7]
andi	r0,uart_ravl
beq		no_data
jalwr7	get_line

// check "sin," command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_sin
ldbiu	r5,4
jalwr7	strncmp
mov		r0,r0
bne		sin_notid
// ================================
// sin,<freq_hz>
ldwi	r1,lbuf
// <freq_hz>
addi	r1,4
jalwr7	atoi
ldwi	r7,sin_hz
stw		[r7],r0

// sin wave
jalwr7	sin_wave

// response
mov		r0,r0
beq		sin_ok
// ERROR:
ldwi	r1,lab_str_err_sin
jalwr7	print
bra		no_data

sin_ok:
ldwi	r1,lab_str_msg_sin
jalwr7	print
bra		no_data
sin_notid:

// check "daco," command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_daco
ldbiu	r5,5
jalwr7	strncmp
mov		r0,r0
bne		daco_notid
// ================================
// daco,<digt_0_4095>
ldwi	r1,lbuf
// <digt_0_4095>
addi	r1,5
jalwr7	atoi
andi	r0,0x0fff

// make i2c frame
movfc	r7,sp
subi	r7,2*4
movtc	sp,r7
# S [ADR,W]
ldwi	r1,wire_send|wire_star|(i2c_mcp4725<<1)|wire_adrw
stw		[r7],r1
addi	r7,2
# [DAT1]
ldwi	r1,wire_send
mov		r2,r0
lsfti	r2,-8
add		r1,r2
stw		[r7],r1
addi	r7,2
# [DAT2]
ldwi	r1,wire_send
mov		r2,r0
andi	r2,0xff
add		r1,r2
stw		[r7],r1
addi	r7,2
# P
ldbiu	r1,wire_stop
stw		[r7],r1
// wire
movfc	r1,sp
jalwr7	mcvm_wire

// response
mov		r0,r0
beq		daco_ok
// ERROR:
ldwi	r1,lab_str_err_daco
jalwr7	print
bra		no_data

daco_ok:
ldwi	r1,lab_str_msg_daco
jalwr7	print
bra		no_data
daco_notid:

// check "who" command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_who
jalwr7	strcmp
mov		r0,r0
bne		who_notid
// ================================
// who
// response
ldwi	r1,lab_str_msg_who
jalwr7	print
bra		no_data
who_notid:

// check "help" command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_help
jalwr7	strcmp
mov		r0,r0
bne		help_notid
// ================================
// help
// response
ldwi	r1,lab_str_msg_help
jalwr7	print_lst
bra		no_data
help_notid:

// ================================
// ignore empty line
ldwi	r1,lbuf
ldb		r0,[r1]
mov		r0,r0
beq		no_data
// ================================
// ERROR: unsupported command
ldwi	r1,lab_str_err_unspt
jalwr7	print
ldwi	r1,lbuf
jalwr7	print
ldwi	r1,lab_str_crlf
jalwr7	print
bra		no_data

// ERROR: syntax
err_syntx:
ldwi	r1,lab_str_err_syntx
jalwr7	print
bra		no_data

no_data:

// ================================
// end of loop
bra		loop


// ================================
// sine wave
// ================================
sin_wave:
// table size
ldwi	r0,lab_sw_table
ldwi	r1,lab_sw_table_end
sub		r1,r0
lsfti	r1,-1
ldwi	r7,sin_tsiz
stw		[r7],r1
// period of sine wave
ldwi	r7,sin_hz
ldwi	r0,(1000000>>16)
ldwi	r1,(1000000&0xffff)
ldbiu	r2,0
ldw		r3,[r7]
divlu
divlq
ldwi	r7,sin_peri
stw		[r7],r1
mov		r0,r0
beq		sw_freq_ok
ldwi	r1,lab_str_err_bad_frequency
jalwr7	print
ldbiu	r0,1							// rtncode=ErrBadFreq
rtnw

sw_freq_ok:
// reset systim
ldbiu	r0,sytm_rst
ldwi	r7,sytmctl
stw		[r7],r0

// output sine wave
sw_loop:
// load sine value (table look up)
//		r6=sw_table[(usec%sin_peri)*sin_tsiz/sin_peri]
// T=(usec%sin_peri)
ldwi	r7,sytmmcrh
ldw		r0,[r7]
addi	r7,2
ldw		r1,[r7]
ldwi	r7,sin_peri
ldbiu	r2,0
ldw		r3,[r7]
sesr	sreg_b_dr						// calculate remainder
divlu
divlq
mov		r0,r2
mov		r1,r3
// T=(T)*sin_tsiz
ldwi	r7,sin_tsiz
ldwi	r2,0
ldw		r3,[r7]
jalwr7	mulu32
// T=(T)/sin_peri
ldwi	r7,sin_peri
ldbiu	r2,0
ldw		r3,[r7]
divlu
divlq
// r6=sw_table[T]
ldwi	r7,lab_sw_table
add		r7,r1
add		r7,r1
ldw		r6,[r7]

// I2C send: S [ADR,W] [r6>>8] [r6&0xff] P
// make i2c frame
movfc	r7,sp
subi	r7,2*4
movtc	sp,r7
// S [ADR,W]
ldwi	r0,wire_send|wire_star|(i2c_mcp4725<<1)|wire_adrw
stw		[r7],r0
addi	r7,2
// [r6>>8]
mov		r0,r6
lsfti	r0,-8
ldwi	r3,wire_send
add		r0,r3
stw		[r7],r0
addi	r7,2
// [r6&0xff]
mov		r0,r6
andi	r0,0xff
add		r0,r3
stw		[r7],r0
addi	r7,2
// P
ldbiu	r0,wire_stop
stw		[r7],r0
// i2c communication
movfc	r1,sp
jalwr7	mcvm_wire
// destroy i2c frame
movfc	r7,sp
addi	r7,2*4
movtc	sp,r7
// check status
mov		r0,r0
beq		pcnt+4
// ERROR:
addi	r0,0x10							// wire errcod + offset 0x10
rtnw

// end of loop
ldwi	r7,sytmmill
ldw		r0,[r7]
cmpi	r0,sin_msec
blo		sw_loop

// return
ldbiu	r0,0							// rtncode=ErrNo
rtnw

sw_table:
datw	2048
datw	2248
datw	2447
datw	2642
datw	2831
datw	3013
datw	3185
datw	3347
datw	3496
datw	3631
datw	3750
datw	3854
datw	3940
datw	4007
datw	4056
datw	4086
datw	4095
datw	4086
datw	4056
datw	4007
datw	3940
datw	3854
datw	3750
datw	3631
datw	3496
datw	3347
datw	3185
datw	3013
datw	2831
datw	2642
datw	2447
datw	2248
datw	2048
datw	1847
datw	1648
datw	1453
datw	1264
datw	1082
datw	910
datw	748
datw	599
datw	464
datw	345
datw	241
datw	155
datw	88
datw	39
datw	9
datw	0
datw	9
datw	39
datw	88
datw	155
datw	241
datw	345
datw	464
datw	599
datw	748
datw	910
datw	1082
datw	1264
datw	1453
datw	1648
datw	1847
sw_table_end:


// ================================
// get line from uart ; disturbed r0,r5~r7
// ================================
get_line:
ldwi	r7,uartrdat
ldwi	r6,uartctl
ldwi	r5,lbuf
gl_loop:
// polling until RAVL=1
ldw		r0,[r6]
andi	r0,uart_ravl
beq		gl_loop
// get byte
ldw		r0,[r7]
stb		[r5],r0
addi	r5,1
// LF code check
cmpi	r0,chr_lf
beq		gl_got
// size check
ldwi	r0,lbuf+max_lbuf-1
cmp		r5,r0
blo		gl_loop
// got line
gl_got:
// string terminate
ldbiu	r0,0
subi	r5,1
stb		[r5],r0
// check CR code
subi	r5,1
ldb		r0,[r5]
cmpi	r0,chr_cr
bne		gl_notcr
ldbiu	r0,0
stb		[r5],r0
gl_notcr:
rtnw

// ================================
// print r1 ; disturbed r0~r2,r6,r7
// ================================
print:
ldwi	r7,uarttdat
ldwi	r6,uartctl
pr_loop:
// get 1 byte
ldb		r2,[r1]
addi	r1,1
mov		r2,r2
beq		pr_finish
// polling uartctl.TFUL=0
pr_poll:
ldw		r0,[r6]
andi	r0,uart_tful
bne		pr_poll
// send data
stw		[r7],r2
bra		pr_loop
pr_finish:
rtnw

// ================================
// print_lst r1 ; disturbed r0~r2,r6,r7
// ================================
print_lst:
jalwr7	print
ldb		r0,[r1]
mov		r0,r0
bne		print_lst
rtnw

// ================================
// print_hex r1 ; disturbed r0~r7
//		print r1 as 4 digits hexadecimal number
// ================================
print_hex:
ldbiu	r3,4
ldwi	r7,uarttdat
ldwi	r6,uartctl
ldwi	r5,lab_ph_hexdig
ph_loop:
// print nibble
mov		r0,r1
mov		r4,r5
lsfti	r0,-12
andi	r0,0x0f
add		r4,r0
ldb		r2,[r4]
// polling uartctl.TFUL=0
ph_poll:
ldw		r0,[r6]
andi	r0,uart_tful
bne		ph_poll
// send data
stw		[r7],r2
// loop
lsfti	r1,4
subi	r3,1
bne		ph_loop
rtnw

ph_hexdig:
dats	"0123456789abcdef"

// ================================
// string compare: r0=strcmp (r1,r2) ; disturbed r1~r4
// ================================
strcmp:
ldb		r3,[r1]
addi	r1,1
ldb		r4,[r2]
addi	r2,1
mov		r0,r3
sub		r0,r4
bne		sc_finish
// check nil code
mov		r3,r3
beq		sc_finish
mov		r4,r4
bne		strcmp
sc_finish:
rtnw

// ================================
// string n compare: r0=strncmp (r1,r2,r5) ; disturbed r1~r5
// ================================
strncmp:
ldb		r3,[r1]
addi	r1,1
ldb		r4,[r2]
addi	r2,1
mov		r0,r3
sub		r0,r4
bne		snc_finish
// check n character
subi	r5,1
beq		snc_finish
// check nil code
mov		r3,r3
beq		snc_finish
mov		r4,r4
bne		strncmp
snc_finish:
rtnw

// ================================
// ascii to integer: r0=atoi (r1)>=0 ; disturbed r1~r3
// ================================
atoi:
ldbiu	r0,0
// r2=[r1]-'0'
ldb		r2,[r1]
subi	r2,0a0
blo		a2i_finish
cmpi	r2,10
bhs		a2i_finish
// r0=10*r0+r2
mov		r3,r0
lsfti	r3,3
add		r0,r0
add		r0,r3
add		r0,r2
// loop
addi	r1,1
bra		atoi+2
a2i_finish:
rtnw

// ================================
// unsigned 32*32=32 bits multiply routine:
//		r0:r1=r0:r1*r2:r3 ; disturbed r2,r3,sr[ML]
// ================================
#			r0:r1
#			r2:r3
#		*)-------
#			r3:r1	: 3) ML=1
#		 r3:r0		: 1) ML=0
#		 r1:r2		: 2) ML=0
#	  r0:r2			: not calculate
#	+)-----------
#			r0:r1	: 4)
# ================================
mulu32:
clsr	sreg_b_ml						// ML=0
mulu	r0,r3							// 1)
mulu	r2,r1							// 2)
sesr	sreg_b_ml						// ML=1
mulu	r1,r3							// 3)
add		r0,r2							// 4)
add		r0,r3
rtnw

// ================================
// c string data
// ================================
str_msg_who:
dats	"OK: $(prog_name) ver.$(prog_vers)\r\n\0"
str_msg_rdy:
dats	"RDY\r\n\0"
str_msg_help:
dats	"OK+ sin,<freq_hz>\r\n"
dats	"OK+ daco,<digt_0_4095>\r\n"
dats	"OK+ who\r\n"
dats	"OK: help\r\n\0\0"
str_msg_sin:
dats	"OK: sin output finished\r\n\0"
str_msg_daco:
dats	"OK: daco\r\n\0"
str_err_unspt:
dats	"ERR: unsupported command: \0"
str_err_syntx:
dats	"ERR: syntax\r\n\0"
str_err_no_mul:
dats	"ERR: no mulc16\r\n\0"
str_err_no_div:
dats	"ERR: no divc32\r\n\0"
str_err_bad_frequency:
dats	"ERR: sin_wave: bad frequency\r\n\0"
str_err_got_nack:
dats	"ERR: sin_wave: slave did not respond\r\n\0"
str_err_sin:
dats	"ERR: sin returns by error\r\n\0"
str_err_daco:
dats	"ERR: could not communicate mcp4725\r\n\0"
str_cmd_sin:
dats	"sin,\0"
str_cmd_daco:
dats	"daco,\0"
str_cmd_who:
dats	"who\0"
str_cmd_help:
dats	"help\0"
str_crlf:
dats	"\r\n\0"

# ================================
incl	"mcvm_lib_wire_m.incl"

