// ================================
// prime factors
//		(c) 2022	1YEN Toru
//
//
//		2022/05/21	ver.1.00
//
// ================================
asm		"mcoc_irom.mif"
#asm		"mcoc_irom.mem"
#asm		"mcoc_irom.dat2",bit16,list
incl	"mcoc115.incl"
# ================================
# string macros
def		prog_name,"prime_fact"
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		chr_lf,0x0a						// lf code
equ		chr_cr,0x0d						// cr code
equ		chr_cma,0x2c					// comma ',' code
equ		max_fact,40						// prf: buffer size
# variables
equ		va,ramtop
equ		prf_num,va						// prf: number
equ		va,va+4
equ		prf_usec,va						// prf: calculation time [us]
equ		va,va+4
equ		static_va,va
# array variables
equ		lbuf,va							// receive data buffer
equ		va,va+max_lbuf
equ		prf_fact,va						// prf: prime factor buffer
equ		va,va+max_fact*4
equ		array_va,va
# ================================

// ================================
// setup
// ================================
// nh=1
sesr	sreg_b_nh

// 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

// port setting
ldbiu	r0,led_builtin
ldwi	r7,porset
stw		[r7],r0
ldwi	r7,pordir
ldw		r0,[r7]
andi	r0,~led_builtin
stw		[r7],r0

// 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

// check Nihonium
movfc	r0,sr
lsfti	r0,-sreg_b_nh
andi	r0,1
bne		chk_nihonium
// ERROR: not Nihonium
ldwi	r1,lab_str_err_not_nh
jalwr7	print
$(s)// ********************************
$(s)ldwi	r0,simfail
$(s)ldwi	r7,simfnsh
$(s)stw		[r7],r0
$(s)// ********************************
pause
bra		pcnt-4
chk_nihonium:

// send RDY
ldwi	r1,lab_str_msg_rdy
jalwr7	print

$(s)// ********************************
$(s)// for simulation
$(s)// prime factors: 32771
$(s)ldwi	r7,prf_num
$(s)ldli	r0,32771
$(s)stl		[r7],r0
#$(s)ldbiu	r0,simc_noto
#$(s)ldwi	r7,simctrl
#1$(s)jalwr7	sim_prime_fact
#1$(s)ldbiu	r0,0
$(s)jalwr7	prime_fact
$(s)// show counts
$(s)ldwi	r7,prindec
$(s)stw		[r7],r0
$(s)// show time
$(s)ldwi	r7,prf_usec
$(s)ldl		r0,[r7]
$(s)ldwi	r7,prindec
$(s)stw		[r7],r0
$(s)// show result
$(s)ldwi	r7,prf_fact
$(s)ldwi	r6,prinhxl
$(s)ldl		r0,[r7]
$(s)addi	r7,4
$(s)stl		[r6],r0
$(s)ldwi	r6,prindec
$(s)sim_loop:
$(s)ldl		r0,[r7]
$(s)addi	r7,4
$(s)stw		[r6],r0
$(s)mov		r0,r0
$(s)bne		sim_loop
#3$(s)// atoi test
#3$(s)ldwi	r1,lab_sim_str_num
#3$(s)jalwr7	atoi
#3$(s)ldwi	r7,prinhxl
#3$(s)stl		[r7],r0
#3$(s)ldbiu	r0,0
#3$(s)bra		sim_finish
#3$(s)sim_str_num:
#3$(s)dats	"1234567\0"
#3$(s)sim_finish:
$(s)// simulation finish
$(s)mov		r0,r0
$(s)beq		pcnt+6
$(s)ldwi	r0,simfail
$(s)bra		pcnt+4
$(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 "prim," command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_prim
ldbiu	r5,5
jalwr7	strncmp
mov		r0,r0
bne		prf_notid
// ================================
// prim,<number>
ldwi	r1,lbuf
// <number>
addi	r1,5
jalwr7	atoi
ldwi	r7,prf_num
stl		[r7],r0

// calculation
$(s)// ********************************
$(s)sim_prime_fact:
$(s)// ********************************
jalwr7	prime_fact

// response
// show time
ldwi	r1,lab_str_msg_prf_tim
jalwr7	print
pushw	r1
ldwi	r7,prf_usec
ldl		r1,[r7]
jalwr7	print_lng_dec
popw	r1
jalwr7	print

// show prime factor
ldwi	r1,lab_str_msg_prf
jalwr7	print
ldwi	r6,prf_fact
ldl		r1,[r6]
addi	r6,4
pushw	r6
jalwr7	print_lng_dec
ldwi	r1,lab_str_msg_prf_equ
jalwr7	print
popw	r6
ldl		r1,[r6]
addi	r6,4
pushw	r6
jalwr7	print_lng_dec
prf_show_loop:
popw	r6
ldl		r1,[r6]
addi	r6,4
pushw	r6
mov		r1,r1
beq		prf_show_break
pushl	r1
ldwi	r1,lab_str_msg_prf_ast
jalwr7	print
popl	r1
jalwr7	print_lng_dec
bra		prf_show_loop
prf_show_break:
popw	r6
ldwi	r1,lab_str_crlf
jalwr7	print
$(s)// ********************************
$(s)rtnw
$(s)// ********************************
bra		no_data
prf_notid:

// check "srsd," command
ldwi	r1,lbuf
ldwi	r2,lab_str_cmd_srsd
ldbiu	r5,5
jalwr7	strncmp
mov		r0,r0
bne		srsd_notid
// ================================
// srsd,<sd_bit_0_1>
ldwi	r1,lbuf
// <sd_bit_0_1>
addi	r1,5
jalwr7	atoi

// set sr register sd bit
mov		r0,r0
beq		srsd_else
// set sd
sesr	sreg_b_sd
bra		srsd_endif
srsd_else:
// clear sd
clsr	sreg_b_sd
srsd_endif:
// read sd bit
movfc	r0,sr
lsfti	r0,-sreg_b_sd
andi	r0,0x01
pushw	r0

// response
ldwi	r1,lab_str_msg_srsd
jalwr7	print
mov		r0,r1
popw	r1
pushw	r0
jalwr7	print_lng_dec
popw	r1
jalwr7	print
bra		no_data
srsd_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


// ================================
// prime factors
//		input: prf_num
//		output: prf_fact[]=prime factors, 0
//		return: r0=count of prime factors
//		r4=k1
//		r5=min_quo
//		r6=nn
//		r7=prf_fact
// ================================
prime_fact:
sesr	sreg_b_dr
// set offset
ldwi	r7,sytmctl
ldbiu	r0,0
stw		[r7],r0
ldbiu	r0,sytm_mcoe
stw		[r7],r0
// parameter
ldwi	r6,prf_num
ldwi	r7,prf_fact
ldl		r6,[r6]
stl		[r7],r6
addi	r7,4
// k1=2
// min_quo=nn/2
mov		r5,r6
ldbiu	r4,2
lsfti	r5,-1
// while (k1<=min_quo)
// {
prf_while_k1_ls_mq:
cmp		r5,r4
blo		prf_break_k1_ls_mq
// k1
// k2=k1 + 1
mov		r3,r4
mov		r2,r4
addi	r3,1
// nn
mov		r1,r6
mov		r0,r6							// must update r0 before divu
// rem1=nn%k1;
divu	r0,r2
// rem2=nn%k2;
divu	r1,r3
// min_quo=int (nn/k1);
mov		r5,r0
// if (rem1=0)
mov		r2,r2
bne		prf_if_rem1_eq_0
// {
// quo=nn/k1;
// num2str		buf=k1,"%d";
// strcat	prime_fact,$(buf)" * ";
stl		[r7],r4
addi	r7,4
// nn=quo;
mov		r6,r0
#// k1=2
#ldbiu	r4,2
// min_quo=@(nn/2);
mov		r5,r6
lsfti	r5,-1
// continue;
bra		prf_while_k1_ls_mq
// }
prf_if_rem1_eq_0:
// if (rem2=0)
mov		r3,r3
bne		prf_if_rem2_eq_0
// {
// quo=nn/k2;
// num2str		buf=k2,"%d";
// strcat	prime_fact,$(buf)" * ";
addi	r4,1
stl		[r7],r4
addi	r7,4
// nn=quo;
mov		r6,r1
#// k1=2
#ldbiu	r4,2
// min_quo=@(nn/2);
mov		r5,r6
lsfti	r5,-1
// continue;
bra		prf_while_k1_ls_mq
// }
prf_if_rem2_eq_0:

// // continue
// k1=k1 + 2;
addi	r4,2
// }
bra		prf_while_k1_ls_mq
prf_break_k1_ls_mq:

// if (min_quo<2 & nn%k1=0 & int (nn/k1)>1)
cmpi	r5,2
bhs		prf_if_mq_lo_2
// nn%k1
mov		r0,r6
mov		r2,r4
divu	r0,r2
mov		r2,r2
bne		prf_if_mq_lo_2
cmpi	r0,2
blo		prf_if_mq_lo_2
// {
// quo=nn/k1;
// num2str		buf=k1,"%d";
// strcat	prime_fact,$(buf)" * ";
stl		[r7],r4
addi	r7,4
// nn=quo;
mov		r6,r0
// }
prf_if_mq_lo_2:
clsr	sreg_b_dr
// num2str		buf=nn,"%d";
// strcat	prime_fact,$(buf);
stl		[r7],r6
addi	r7,4

// end of factor
ldbiu	r0,0
stl		[r7],r0
// count of prime factors
mov		r0,r7
ldwi	r7,prf_fact
sub		r0,r7
lsfti	r0,-2
subi	r0,1

// read elapsed time
ldwi	r7,sytmmcrh
ldl		r1,[r7]
ldwi	r7,prf_usec
stl		[r7],r1
ldwi	r7,sytmctl
ldbiu	r1,0
stw		[r7],r1

rtnw


// ================================
// 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
$(s)// ********************************
$(s)ldwi	r7,princhr
$(s)stw		[r7],r2
$(s)bra		pr_loop
$(s)// ********************************
// 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"

// ================================
// print_chr r1 ; disturbed r0,r7
// ================================
print_chr:
$(s)// ********************************
$(s)ldwi	r7,princhr
$(s)stw		[r7],r1
$(s)rtnw
$(s)// ********************************
// polling uartctl.TFUL=0
ldwi	r7,uartctl
pc_poll:
ldw		r0,[r7]
andi	r0,uart_tful
bne		pc_poll
// send data
ldwi	r7,uarttdat
stw		[r7],r1
rtnw

// ================================
// print long decimal r1 ; disturbed r0~r4,r6,r7
// ================================
print_lng_dec:
#$(s)// ********************************
#$(s)ldwi	r7,prindec
#$(s)stw		[r7],r1
#$(s)rtnw
#$(s)// ********************************
mov		r1,r1
bne		pdc_not_0
// 0
ldbiu	r1,0a0
jalwr7	print_chr
bra		pdc_fin

pdc_not_0:
// for each r0=1000000000, ~, 10, 1
ldwi	r6,lab_pdc_tab
ldbiu	r3,true							// omit leading zero flag
pdc_loop:
ldl		r0,[r6]
mov		r0,r0
beq		pdc_fin
// r2=r1/r0
ldbiu	r2,0
pdc_div:
sub		r1,r0
blo		pcnt+4
addi	r2,1
bra		pdc_div

// omit leading zero
mov		r3,r3
beq		pcnt+4
mov		r2,r2
beq		pdc_omit
// print 1 digit
addi	r2,0a0
pushlm	r0,r1,r7
mov		r1,r2
jalwr7	print_chr
poplm	r7,r1,r0
ldbiu	r3,false
pdc_omit:
// add back r1; loop
add		r1,r0
addi	r6,4
bra		pdc_loop

pdc_fin:
rtnw

pdc_tab:
datl	1000000000
datl	100000000
datl	10000000
datl	1000000
datl	100000
datl	10000
datl	1000
datl	100
datl	10
datl	1
datl	0

// ================================
// 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

// ================================
// 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+ prim,<number>\r\n"
dats	"OK+ srsd,<sd_bit_0_1>\r\n"
dats	"OK+ who\r\n"
dats	"OK: help\r\n\0\0"
str_msg_prf:
dats	"OK: prime factors: \0"
str_msg_prf_equ:
dats	" = \0"
str_msg_prf_ast:
dats	" * \0"
str_msg_prf_tim:
dats	"OK+ elapsed time: \0 [us]\r\n\0"
str_msg_srsd:
dats	"OK: srsd=\0\r\n\0"
str_err_unspt:
dats	"ERR: unsupported command: \0"
str_err_syntx:
dats	"ERR: syntax\r\n\0"
str_err_not_nh:
dats	"ERR: not Nihonium, halted.\r\n\0"
str_cmd_prim:
dats	"prim,\0"
str_cmd_srsd:
dats	"srsd,\0"
str_cmd_who:
dats	"who\0"
str_cmd_help:
dats	"help\0"
str_crlf:
dats	"\r\n\0"

