divmodhi.S revision 1.1.1.4
1/* HImode div/mod functions for the GCC support library for the Renesas RL78 processors.
2   Copyright (C) 2012-2018 Free Software Foundation, Inc.
3   Contributed by Red Hat.
4
5   This file is part of GCC.
6
7   GCC is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3, or (at your option)
10   any later version.
11
12   GCC is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   Under Section 7 of GPL version 3, you are granted additional
18   permissions described in the GCC Runtime Library Exception, version
19   3.1, as published by the Free Software Foundation.
20
21   You should have received a copy of the GNU General Public License and
22   a copy of the GCC Runtime Library Exception along with this program;
23   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24   <http://www.gnu.org/licenses/>.  */
25
26#include "vregs.h"
27
28#if defined __RL78_MUL_G14__
29
30START_FUNC ___divhi3
31	;; r8 = 4[sp] / 6[sp]
32
33	;; Test for a negative denumerator.
34	movw	ax, [sp+6]
35	mov1	cy, a.7
36	movw	de, ax
37	bc	$__div_neg_den
38
39	;; Test for a negative numerator.
40	movw	ax, [sp+4]
41	mov1	cy, a.7
42	bc	$__div_neg_num
43
44	;; Neither are negative - we can use the unsigned divide instruction.
45__div_no_convert:
46	push	psw
47	di
48	divhu
49	pop	psw
50
51	movw	r8, ax
52	ret
53
54__div_neg_den:
55	;; Negate the denumerator (which is in DE)
56	clrw	ax
57	subw	ax, de
58	movw	de, ax
59
60	;; Test for a negative numerator.
61	movw	ax, [sp+4]
62	mov1	cy, a.7
63	;; If it is not negative then we perform the division and then negate the result.
64	bnc	$__div_then_convert
65
66	;; Otherwise we negate the numerator and then go with an unsigned division.
67	movw	bc, ax
68	clrw	ax
69	subw	ax, bc
70	br	$__div_no_convert
71
72__div_neg_num:
73	;; Negate the numerator (which is in AX)
74	;; We know that the denumerator is positive.
75	movw	bc, ax
76	clrw	ax
77	subw	ax, bc
78
79__div_then_convert:
80	push	psw
81	di
82	divhu
83	pop	psw
84
85	;; Negate result and transfer into r8
86	movw	bc, ax
87	clrw	ax
88	subw	ax, bc
89	movw	r8, ax
90	ret
91
92END_FUNC ___divhi3
93
94;----------------------------------------------------------------------
95
96START_FUNC ___modhi3
97	;; r8 = 4[sp] % 6[sp]
98
99	;; Test for a negative denumerator.
100	movw	ax, [sp+6]
101	mov1	cy, a.7
102	movw	de, ax
103	bc	$__mod_neg_den
104
105	;; Test for a negative numerator.
106	movw	ax, [sp+4]
107	mov1	cy, a.7
108	bc	$__mod_neg_num
109
110	;; Neither are negative - we can use the unsigned divide instruction.
111__mod_no_convert:
112	push	psw
113	di
114	divhu
115	pop	psw
116
117	movw	ax, de
118	movw	r8, ax
119	ret
120
121__mod_neg_den:
122	;; Negate the denumerator (which is in DE)
123	clrw	ax
124	subw	ax, de
125	movw	de, ax
126
127	;; Test for a negative numerator.
128	movw	ax, [sp+4]
129	mov1	cy, a.7
130	;; If it is not negative then we perform the modulo operation without conversion.
131	bnc	$__mod_no_convert
132
133	;; Otherwise we negate the numerator and then go with an unsigned modulo operation.
134	movw	bc, ax
135	clrw	ax
136	subw	ax, bc
137	br	$__mod_then_convert
138
139__mod_neg_num:
140	;; Negate the numerator (which is in AX)
141	;; We know that the denumerator is positive.
142	movw	bc, ax
143	clrw	ax
144	subw	ax, bc
145
146__mod_then_convert:
147	push	psw
148	di
149	divhu
150	pop	psw
151
152	;; Negate result and transfer into r8
153	clrw	  ax
154	subw	  ax, de
155	movw	  r8, ax
156	ret
157
158END_FUNC ___modhi3
159
160;----------------------------------------------------------------------
161
162#elif defined __RL78_MUL_G13__
163
164	;; The G13 S2 core does not have a 16 bit divide peripheral.
165	;; So instead we perform a 32-bit divide and twiddle the inputs
166	;; as necessary.
167
168	;; Hardware registers.  Note - these values match the silicon, not the documentation.
169	MDAL = 0xffff0
170	MDAH = 0xffff2
171	MDBL = 0xffff6
172	MDBH = 0xffff4
173	MDCL = 0xf00e0
174	MDCH = 0xf00e2
175	MDUC = 0xf00e8
176
177.macro _Negate src, dest
178	movw	ax, !\src
179	movw	bc, ax
180	clrw	ax
181	subw	ax, bc
182	movw	\dest, ax
183.endm
184
185;----------------------------------------------------------------------
186
187START_FUNC ___divhi3
188	;; r8 = 4[sp] / 6[sp] (signed division)
189
190	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
191	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation
192
193	clrw	ax     		; Clear the top 16-bits of the divisor and dividend
194	movw	MDBH, ax
195	movw	MDAH, ax
196
197	;; Load and test for a negative denumerator.
198	movw	ax, [sp+6]
199	movw	MDBL, ax
200	mov1	cy, a.7
201	bc	$__div_neg_den
202
203	;; Load and test for a negative numerator.
204	movw	ax, [sp+4]
205	mov1	cy, a.7
206	movw	MDAL, ax
207	bc	$__div_neg_num
208
209	;; Neither are negative - we can use the unsigned divide hardware.
210__div_no_convert:
211	mov	a, #0xC1	; Set the DIVST bit in MDUC
212	mov	!MDUC, a	; This starts the division op
213
2141:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
215	bt	a.0, $1b
216
217  	movw	ax, MDAL	; Read the result
218	movw	r8, ax
219	ret
220
221__div_neg_den:
222	;; Negate the denumerator (which is in MDBL)
223	_Negate MDBL MDBL
224
225	;; Load and test for a negative numerator.
226	movw	ax, [sp+4]
227	mov1	cy, a.7
228	movw	MDAL, ax
229	;; If it is not negative then we perform the division and then negate the result.
230	bnc	$__div_then_convert
231
232	;; Otherwise we negate the numerator and then go with a straightforward unsigned division.
233	_Negate MDAL MDAL
234	br	$!__div_no_convert
235
236__div_neg_num:
237	;; Negate the numerator (which is in MDAL)
238	;; We know that the denumerator is positive.
239	_Negate MDAL MDAL
240
241__div_then_convert:
242	mov	a, #0xC1	; Set the DIVST bit in MDUC
243	mov	!MDUC, a	; This starts the division op
244
2451:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
246	bt	a.0, $1b
247
248	;; Negate result and transfer into r8
249	_Negate MDAL r8
250	ret
251
252END_FUNC ___divhi3
253
254;----------------------------------------------------------------------
255
256START_FUNC ___modhi3
257	;; r8 = 4[sp] % 6[sp] (signed modulus)
258
259	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
260	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation
261
262	clrw	ax     		; Clear the top 16-bits of the divisor and dividend
263	movw	MDBH, ax
264	movw	MDAH, ax
265
266	;; Load and test for a negative denumerator.
267	movw	ax, [sp+6]
268	movw	MDBL, ax
269	mov1	cy, a.7
270	bc	$__mod_neg_den
271
272	;; Load and test for a negative numerator.
273	movw	ax, [sp+4]
274	mov1	cy, a.7
275	movw	MDAL, ax
276	bc	$__mod_neg_num
277
278	;; Neither are negative - we can use the unsigned divide hardware
279__mod_no_convert:
280	mov	a, #0xC1	; Set the DIVST bit in MDUC
281	mov	!MDUC, a	; This starts the division op
282
2831:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
284	bt	a.0, $1b
285
286  	movw	ax, !MDCL	; Read the remainder
287	movw	r8, ax
288	ret
289
290__mod_neg_den:
291	;; Negate the denumerator (which is in MDBL)
292	_Negate MDBL MDBL
293
294	;; Load and test for a negative numerator.
295	movw	ax, [sp+4]
296	mov1	cy, a.7
297	movw	MDAL, ax
298	;; If it is not negative then we perform the modulo operation without conversion.
299	bnc	$__mod_no_convert
300
301	;; Otherwise we negate the numerator and then go with a modulo followed by negation.
302	_Negate MDAL MDAL
303	br	$!__mod_then_convert
304
305__mod_neg_num:
306	;; Negate the numerator (which is in MDAL)
307	;; We know that the denumerator is positive.
308	_Negate MDAL MDAL
309
310__mod_then_convert:
311	mov	a, #0xC1	; Set the DIVST bit in MDUC
312	mov	!MDUC, a	; This starts the division op
313
3141:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
315	bt	a.0, $1b
316
317	_Negate	MDCL r8
318	ret
319
320END_FUNC ___modhi3
321
322;----------------------------------------------------------------------
323
324START_FUNC ___udivhi3
325	;; r8 = 4[sp] / 6[sp] (unsigned division)
326
327	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
328	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation
329
330	movw	ax, [sp+4]	; Load the divisor
331	movw	MDAL, ax
332	movw	ax, [sp+6]	; Load the dividend
333	movw	MDBL, ax
334	clrw	ax
335	movw	MDAH, ax
336	movw	MDBH, ax
337
338	mov	a, #0xC1	; Set the DIVST bit in MDUC
339	mov	!MDUC, a	; This starts the division op
340
3411:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
342	bt	a.0, $1b
343
344  	movw	ax, !MDAL	; Read the remainder
345	movw	r8, ax
346	ret
347
348END_FUNC   ___udivhi3
349
350;----------------------------------------------------------------------
351
352START_FUNC ___umodhi3
353	;; r8 = 4[sp] % 6[sp] (unsigned modulus)
354
355	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
356	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation
357
358	movw	ax, [sp+4]	; Load the divisor
359	movw	MDAL, ax
360	movw	ax, [sp+6]	; Load the dividend
361	movw	MDBL, ax
362	clrw	ax
363	movw	MDAH, ax
364	movw	MDBH, ax
365
366	mov	a, #0xC1	; Set the DIVST bit in MDUC
367	mov	!MDUC, a	; This starts the division op
368
3691:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
370	bt	a.0, $1b
371
372  	movw	ax, !MDCL	; Read the remainder
373	movw	r8, ax
374	ret
375
376END_FUNC   ___umodhi3
377
378;----------------------------------------------------------------------
379
380#elif defined __RL78_MUL_NONE__
381
382.macro MAKE_GENERIC  which,need_result
383
384	.if \need_result
385	quot = r8
386	num = r10
387	den = r12
388	bit = r14
389	.else
390	num = r8
391	quot = r10
392	den = r12
393	bit = r14
394	.endif
395
396	quotB0 = quot
397	quotB1 = quot+1
398
399	numB0 = num
400	numB1 = num+1
401
402	denB0 = den
403	denB1 = den+1
404
405	bitB0 = bit
406	bitB1 = bit+1
407
408#define bit	bc
409#define bitB0	c
410#define bitB1	b
411
412	START_FUNC __generic_hidivmod\which
413
414num_lt_den\which:
415	.if \need_result
416	movw	r8, #0
417	.else
418	movw	ax, [sp+8]
419	movw	r8, ax
420	.endif
421	ret
422
423	;; These routines leave DE alone - the signed functions use DE
424	;; to store sign information that must remain intact
425
426	.if \need_result
427	.global __generic_hidiv
428__generic_hidiv:
429
430	.else
431
432	.global __generic_himod
433__generic_himod:
434
435	.endif
436
437	;; (quot,rem) = 8[sp] /% 10[sp]
438
439	movw	hl, sp
440	movw	ax, [hl+10] ; denH
441	cmpw	ax, [hl+8] ; numH
442	bh	$num_lt_den\which
443
444	;; (quot,rem) = 16[sp] /% 20[sp]
445
446	;; copy numerator
447	movw	ax, [hl+8]
448	movw	num, ax
449
450	;; copy denomonator
451	movw	ax, [hl+10]
452	movw	den, ax
453
454	movw	ax, den
455	cmpw	ax, #0
456	bnz	$den_not_zero\which
457	.if \need_result
458	movw    quot, #0
459	.else
460	movw	num, #0
461	.endif
462	ret
463
464den_not_zero\which:
465	.if \need_result
466	;; zero out quot
467	movw	quot, #0
468	.endif
469
470	;; initialize bit to 1
471	movw	bit, #1
472
473; while (den < num && !(den & (1L << BITS_MINUS_1)))
474
475shift_den_bit\which:
476	movw	ax, den
477	mov1	cy,a.7
478	bc	$enter_main_loop\which
479	cmpw	ax, num
480	bh	$enter_main_loop\which
481
482	;; den <<= 1
483;	movw	ax, den		; already has it from the cmpw above
484	shlw	ax, 1
485	movw	den, ax
486
487	;; bit <<= 1
488	.if \need_result
489#ifdef bit
490	shlw	bit, 1
491#else
492	movw	ax, bit
493	shlw	ax, 1
494	movw	bit, ax
495#endif
496	.else
497	;; if we don't need to compute the quotent, we don't need an
498	;; actual bit *mask*, we just need to keep track of which bit
499	inc	bitB0
500	.endif
501
502	br	$shift_den_bit\which
503
504main_loop\which:
505
506	;; if (num >= den) (cmp den > num)
507	movw	ax, den
508	cmpw	ax, num
509	bh	$next_loop\which
510
511	;; num -= den
512	movw	ax, num
513	subw	ax, den
514	movw	num, ax
515
516	.if \need_result
517	;; res |= bit
518	mov	a, quotB0
519	or	a, bitB0
520	mov	quotB0, a
521	mov	a, quotB1
522	or	a, bitB1
523	mov	quotB1, a
524	.endif
525
526next_loop\which:
527
528	;; den >>= 1
529	movw	ax, den
530	shrw	ax, 1
531	movw	den, ax
532
533	.if \need_result
534	;; bit >>= 1
535	movw	ax, bit
536	shrw	ax, 1
537	movw	bit, ax
538	.else
539	dec	bitB0
540	.endif
541
542enter_main_loop\which:
543	.if \need_result
544	movw	ax, bit
545	cmpw	ax, #0
546	.else
547	cmp0	bitB0
548	.endif
549	bnz	$main_loop\which
550
551main_loop_done\which:
552	ret
553	END_FUNC __generic_hidivmod\which
554.endm
555;----------------------------------------------------------------------
556
557	MAKE_GENERIC _d 1
558	MAKE_GENERIC _m 0
559
560;----------------------------------------------------------------------
561
562START_FUNC ___udivhi3
563	;; r8 = 4[sp] / 6[sp]
564	call	$!__generic_hidiv
565	ret
566END_FUNC ___udivhi3
567
568
569START_FUNC ___umodhi3
570	;; r8 = 4[sp] % 6[sp]
571	call	$!__generic_himod
572	ret
573END_FUNC ___umodhi3
574
575;----------------------------------------------------------------------
576
577.macro NEG_AX
578	movw	hl, ax
579	movw	ax, #0
580	subw	ax, [hl]
581	movw	[hl], ax
582.endm
583
584;----------------------------------------------------------------------
585
586START_FUNC ___divhi3
587	;; r8 = 4[sp] / 6[sp]
588	movw	de, #0
589	mov	a, [sp+5]
590	mov1	cy, a.7
591	bc	$div_signed_num
592	mov	a, [sp+7]
593	mov1	cy, a.7
594	bc	$div_signed_den
595	call	$!__generic_hidiv
596	ret
597
598div_signed_num:
599	;; neg [sp+4]
600	movw	ax, sp
601	addw	ax, #4
602	NEG_AX
603	mov	d, #1
604	mov	a, [sp+7]
605	mov1	cy, a.7
606	bnc	$div_unsigned_den
607div_signed_den:
608	;; neg [sp+6]
609	movw	ax, sp
610	addw	ax, #6
611	NEG_AX
612	mov	e, #1
613div_unsigned_den:
614	call	$!__generic_hidiv
615
616	mov	a, d
617	cmp0	a
618	bz	$div_skip_restore_num
619	;;  We have to restore the numerator [sp+4]
620	movw	ax, sp
621	addw	ax, #4
622	NEG_AX
623	mov	a, d
624div_skip_restore_num:
625	xor	a, e
626	bz	$div_no_neg
627	movw	ax, #r8
628	NEG_AX
629div_no_neg:
630	mov	a, e
631	cmp0	a
632	bz	$div_skip_restore_den
633	movw	ax, sp
634	addw	ax, #6
635	NEG_AX
636div_skip_restore_den:
637	ret
638END_FUNC ___divhi3
639
640
641START_FUNC ___modhi3
642	;; r8 = 4[sp] % 6[sp]
643	movw	de, #0
644	mov	a, [sp+5]
645	mov1	cy, a.7
646	bc	$mod_signed_num
647	mov	a, [sp+7]
648	mov1	cy, a.7
649	bc	$mod_signed_den
650	call	$!__generic_himod
651	ret
652
653mod_signed_num:
654	;; neg [sp+4]
655	movw	ax, sp
656	addw	ax, #4
657	NEG_AX
658	mov	d, #1
659	mov	a, [sp+7]
660	mov1	cy, a.7
661	bnc	$mod_unsigned_den
662mod_signed_den:
663	;; neg [sp+6]
664	movw	ax, sp
665	addw	ax, #6
666	NEG_AX
667mod_unsigned_den:
668	call	$!__generic_himod
669
670	mov	a, d
671	cmp0	a
672	bz	$mod_no_neg
673	movw	ax, #r8
674	NEG_AX
675	;;  Also restore numerator
676	movw 	ax, sp
677	addw	ax, #4
678	NEG_AX
679mod_no_neg:
680	mov	a, e
681	cmp0	a
682	bz	$mod_skip_restore_den
683	movw	ax, sp
684	addw	ax, #6
685	NEG_AX
686mod_skip_restore_den:
687	ret
688END_FUNC ___modhi3
689
690;----------------------------------------------------------------------
691
692#else
693
694#error "Unknown RL78 hardware multiply/divide support"
695
696#endif
697