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