* device/lib/gbz80/crt0.s,
[fw/sdcc] / device / lib / gbz80 / div.s
1         ;; Originally from GBDK by Pascal Felber.
2         .area   _CODE
3
4 __divschar_rrx_s::
5         ld      hl,#2+1
6         add     hl,sp
7
8         ld      e,(hl)
9         dec     hl
10         ld      l,(hl)
11
12         ;; Fall through
13 __divschar_rrx_hds::
14         ld      c,l
15
16         call    .div8
17
18         ld      e,c
19         ld      d,b
20
21         ret
22
23 __modschar_rrx_s::
24         ld      hl,#2+1
25         add     hl,sp
26
27         ld      e,(hl)
28         dec     hl
29         ld      l,(hl)
30
31         ;; Fall through
32 __modschar_rrx_hds::
33         ld      c,l
34
35         call    .div8
36
37         ;; Already in DE
38
39         ret
40
41 __divsint_rrx_s::
42         ld      hl,#2+3
43         add     hl,sp
44
45         ld      d,(hl)
46         dec     hl
47         ld      e,(hl)
48         dec     hl
49         ld      a,(hl)
50         dec     hl
51         ld      l,(hl)
52         ld      h,a
53
54         ;; Fall through
55 __divsint_rrx_hds::
56         ld      b,h
57         ld      c,l
58
59         call    .div16
60
61         ld      e,c
62         ld      d,b
63
64         ret
65
66 __modsint_rrx_s::
67         ld      hl,#2+3
68         add     hl,sp
69
70         ld      d,(hl)
71         dec     hl
72         ld      e,(hl)
73         dec     hl
74         ld      a,(hl)
75         dec     hl
76         ld      l,(hl)
77         ld      h,a
78
79         ;; Fall through
80 __modsint_rrx_hds::
81         ld      b,h
82         ld      c,l
83
84         call    .div16
85
86         ;; Already in DE
87
88         ret
89
90         ;; Unsigned
91 __divuchar_rrx_s::
92         ld      hl,#2+1
93         add     hl,sp
94
95         ld      e,(hl)
96         dec     hl
97         ld      l,(hl)
98
99         ;; Fall through
100 __divuchar_rrx_hds::
101         ld      c,l
102         call    .divu8
103
104         ld      e,c
105         ld      d,b
106
107         ret
108
109 __moduchar_rrx_s::
110         ld      hl,#2+1
111         add     hl,sp
112
113         ld      e,(hl)
114         dec     hl
115         ld      l,(hl)
116
117         ;; Fall through
118 __moduchar_rrx_hds::
119         ld      c,l
120         call    .divu8
121
122         ;; Already in DE
123
124         ret
125
126 __divuint_rrx_s::
127         ld      hl,#2+3
128         add     hl,sp
129
130         ld      d,(hl)
131         dec     hl
132         ld      e,(hl)
133         dec     hl
134         ld      a,(hl)
135         dec     hl
136         ld      l,(hl)
137         ld      h,a
138
139         ;; Fall through
140 __divuint_rrx_hds::
141         ld      b,h
142         ld      c,l
143         call    .divu16
144
145         ld      e,c
146         ld      d,b
147
148         ret
149
150 __moduint_rrx_s::
151         ld      hl,#2+3
152         add     hl,sp
153
154         ld      d,(hl)
155         dec     hl
156         ld      e,(hl)
157         dec     hl
158         ld      a,(hl)
159         dec     hl
160         ld      l,(hl)
161         ld      h,a
162         ;; Fall through
163
164 __moduint_rrx_hds::
165         ld      b,h
166         ld      c,l
167
168         call    .divu16
169
170         ;; Already in DE
171
172         ret
173
174 .div8::
175 .mod8::
176         ld      a,c             ; Sign extend
177         rlca
178         sbc     a
179         ld      b,a
180         ld      a,e             ; Sign extend
181         rlca
182         sbc     a
183         ld      d,a
184
185         ; Fall through to .div16
186
187         ;; 16-bit division
188         ;;
189         ;; Entry conditions
190         ;;   BC = dividend
191         ;;   DE = divisor
192         ;;
193         ;; Exit conditions
194         ;;   BC = quotient
195         ;;   DE = remainder
196         ;;   If divisor is non-zero, carry=0
197         ;;   If divisor is 0, carry=1 and both quotient and remainder are 0
198         ;;
199         ;; Register used: AF,BC,DE,HL
200 .div16::
201 .mod16::
202         ;; Determine sign of quotient by xor-ing high bytes of dividend
203         ;;  and divisor. Quotient is positive if signs are the same, negative
204         ;;  if signs are different
205         ;; Remainder has same sign as dividend
206         ld      a,b             ; Get high byte of dividend
207         ld      (.srem),a       ; Save as sign of remainder
208         xor     d               ; Xor with high byte of divisor
209         ld      (.squot),a      ; Save sign of quotient
210         ;; Take absolute value of divisor
211         bit     7,d
212         jr      Z,.chkde        ; Jump if divisor is positive
213         sub     a               ; Substract divisor from 0
214         sub     e
215         ld      e,a
216         sbc     a               ; Propagate borrow (A=0xFF if borrow)
217         sub     d
218         ld      d,a
219         ;; Take absolute value of dividend
220 .chkde:
221         bit     7,b
222         jr      Z,.dodiv        ; Jump if dividend is positive
223         sub     a               ; Substract dividend from 0
224         sub     c
225         ld      c,a
226         sbc     a               ; Propagate borrow (A=0xFF if borrow)
227         sub     b
228         ld      b,a
229         ;; Divide absolute values
230 .dodiv:
231         call    .divu16
232         ret     C               ; Exit if divide by zero
233         ;; Negate quotient if it is negative
234         ld      a,(.squot)
235         and     #0x80
236         jr      Z,.dorem        ; Jump if quotient is positive
237         sub     a               ; Substract quotient from 0
238         sub     c
239         ld      c,a
240         sbc     a               ; Propagate borrow (A=0xFF if borrow)
241         sub     b
242         ld      b,a
243 .dorem:
244         ;; Negate remainder if it is negative
245         ld      a,(.srem)
246         and     #0x80
247         ret     Z               ; Return if remainder is positive
248         sub     a               ; Substract remainder from 0
249         sub     e
250         ld      e,a
251         sbc     a               ; Propagate remainder (A=0xFF if borrow)
252         sub     d
253         ld      d,a
254         ret
255
256 .divu8::
257 .modu8::
258         ld      b,#0x00
259         ld      d,b
260         ; Fall through to divu16
261
262 .divu16::
263 .modu16::
264         ;; Check for division by zero
265         ld      a,e
266         or      d
267         jr      NZ,.divide      ; Branch if divisor is non-zero
268         ld      bc,#0x00        ; Divide by zero error
269         ld      d,b
270         ld      e,c
271         scf                     ; Set carry, invalid result
272         ret
273 .divide:
274         ld      l,c             ; L = low byte of dividend/quotient
275         ld      h,b             ; H = high byte of dividend/quotient
276         ld      bc,#0x00        ; BC = remainder
277         or      a               ; Clear carry to start
278         ld      a,#16           ; 16 bits in dividend
279 .dvloop:
280         ;; Shift next bit of quotient into bit 0 of dividend
281         ;; Shift next MSB of dividend into LSB of remainder
282         ;; BC holds both dividend and quotient. While we shift a bit from
283         ;;  MSB of dividend, we shift next bit of quotient in from carry
284         ;; HL holds remainder
285         ;; Do a 32-bit left shift, shifting carry to L, L to H,
286         ;;  H to C, C to B
287         ld      (.dcnt),a
288         rl      l               ; Carry (next bit of quotient) to bit 0
289         rl      h               ; Shift remaining bytes
290         rl      c
291         rl      b               ; Clears carry since BC was 0
292         ;; If remainder is >= divisor, next bit of quotient is 1. This
293         ;;  bit goes to carry
294         push    bc              ; Save current remainder
295         ld      a,c             ; Substract divisor from remainder
296         sbc     e
297         ld      c,a
298         ld      a,b
299         sbc     d
300         ld      b,a
301         ccf                     ; Complement borrow so 1 indicates a
302                                 ;  successful substraction (this is the
303                                 ;  next bit of quotient)
304         jr      C,.drop         ; Jump if remainder is >= dividend
305         pop     bc              ; Otherwise, restore remainder
306         jr      .nodrop
307 .drop:
308         inc     sp
309         inc     sp
310 .nodrop:
311         ld      a,(.dcnt)
312         dec     a               ; DEC does not affect carry flag
313         jr      NZ,.dvloop
314         ;; Shift last carry bit into quotient
315         ld      d,b             ; DE = remainder
316         ld      e,c
317         rl      l               ; Carry to L
318         ld      c,l             ; C = low byte of quotient
319         rl      h
320         ld      b,h             ; B = high byte of quotient
321         or      a               ; Clear carry, valid result
322         ret
323
324         .area   _BSS
325
326 .srem:
327         .ds 0x01                ; Sign of quotient
328 .squot:
329         .ds 0x01                ; Sign of remainder
330 .dcnt:
331         .ds 0x01                ; Counter for division