Improved caching of pointers and division
[fw/sdcc] / device / lib / z80 / div.s
1         ;; Originally from GBDK by Pascal Felber.
2
3         .area   _CODE
4
5         ;; Unsigned
6 __divuchar_rrx_s::
7         ld      hl,#2+1
8         add     hl,sp
9
10         ld      e,(hl)
11         dec     hl
12         ld      l,(hl)
13
14         ;; Fall through
15 __divuchar_rrx_hds::
16         ld      c,l
17         call    __divu8
18
19         ld      l,c
20         ld      h,b
21
22         ret
23
24 __divuint_rrx_s::
25         ld      hl,#2+3
26         add     hl,sp
27
28         ld      d,(hl)
29         dec     hl
30         ld      e,(hl)
31         dec     hl
32         ld      a,(hl)
33         dec     hl
34         ld      l,(hl)
35         ld      h,a
36
37         ;; Fall through
38 __divuint_rrx_hds::
39         ld      b,h
40         ld      c,l
41         call    __divu16
42
43         ld      l,c
44         ld      h,b
45
46         ret
47
48 __divsuchar_rrx_s::
49         ld      hl,#2+1
50         add     hl,sp
51
52         ld      e,(hl)
53         dec     hl
54         ld      c,(hl)
55         ld      b, #0
56
57         call    signexte
58
59         ld      l,c
60         ld      h,b
61
62         ret
63
64 __modsuchar_rrx_s::
65         ld      hl,#2+1
66         add     hl,sp
67
68         ld      e,(hl)
69         dec     hl
70         ld      c,(hl)
71         ld      b, #0
72
73         call    signexte
74
75         ld      l,e
76         ld      h,d
77
78         ret
79
80 __divuschar_rrx_s::
81         ld      hl,#2+1
82         add     hl,sp
83
84         ld      e,(hl)
85         ld      d, #0
86         dec     hl
87         ld      c,(hl)
88
89         ld      a,c             ; Sign extend
90         rlca
91         sbc     a
92         ld      b,a
93
94         call    __div16
95
96         ld      l,c
97         ld      h,b
98
99         ret
100
101 __div8::
102 .mod8::
103         ld      a,c             ; Sign extend
104         rlca
105         sbc     a
106         ld      b,a
107 signexte:
108         ld      a,e             ; Sign extend
109         rlca
110         sbc     a
111         ld      d,a
112
113         ; Fall through to __div16
114
115         ;; 16-bit division
116         ;;
117         ;; Entry conditions
118         ;;   BC = dividend
119         ;;   DE = divisor
120         ;;
121         ;; Exit conditions
122         ;;   BC = quotient
123         ;;   DE = remainder
124         ;;   If divisor is non-zero, carry=0
125         ;;   If divisor is 0, carry=1 and both quotient and remainder are 0
126         ;;
127         ;; Register used: AF,BC,DE,HL
128 __div16::
129 .mod16::
130         ;; Determine sign of quotient by xor-ing high bytes of dividend
131         ;;  and divisor. Quotient is positive if signs are the same, negative
132         ;;  if signs are different
133         ;; Remainder has same sign as dividend
134         ld      a,b             ; Get high byte of dividend
135         push    af              ; Save as sign of remainder
136         xor     d               ; Xor with high byte of divisor
137         push    af              ; Save sign of quotient
138
139         ;; Take absolute value of divisor
140         bit     7,d
141         jr      Z,.chkde        ; Jump if divisor is positive
142         sub     a               ; Substract divisor from 0
143         sub     e
144         ld      e,a
145         sbc     a               ; Propagate borrow (A=0xFF if borrow)
146         sub     d
147         ld      d,a
148         ;; Take absolute value of dividend
149 .chkde:
150         bit     7,b
151         jr      Z,.dodiv        ; Jump if dividend is positive
152         sub     a               ; Substract dividend from 0
153         sub     c
154         ld      c,a
155         sbc     a               ; Propagate borrow (A=0xFF if borrow)
156         sub     b
157         ld      b,a
158         ;; Divide absolute values
159 .dodiv:
160         call    __divu16
161         jr      C,.exit         ; Exit if divide by zero
162         ;; Negate quotient if it is negative
163         pop     af              ; recover sign of quotient
164         and     #0x80
165         jr      Z,.dorem        ; Jump if quotient is positive
166         sub     a               ; Substract quotient from 0
167         sub     c
168         ld      c,a
169         sbc     a               ; Propagate borrow (A=0xFF if borrow)
170         sub     b
171         ld      b,a
172 .dorem:
173         ;; Negate remainder if it is negative
174         pop     af              ; recover sign of remainder
175         and     #0x80
176         ret     Z               ; Return if remainder is positive
177         sub     a               ; Substract remainder from 0
178         sub     e
179         ld      e,a
180         sbc     a               ; Propagate remainder (A=0xFF if borrow)
181         sub     d
182         ld      d,a
183         ret
184 .exit:
185         pop     af
186         pop     af
187         ret
188
189 __divu8::
190 .modu8::
191         ld      b,#0x00
192         ld      d,b
193         ; Fall through to divu16
194
195 __divu16::
196 .modu16::
197         ;; Check for division by zero
198         ld      a,e
199         or      d
200         jr      NZ,.divide      ; Branch if divisor is non-zero
201         ld      b,d             ; Divide by zero error: D=E=0
202         ld      c,e
203         scf                     ; Set carry, invalid result
204         ret
205 .divide:
206         ld      hl,#0
207         ld      a,b
208         ld      b,#16           ; 16 bits in dividend
209 .dvloop:
210         ;; Cleaned up on April 2009 by Marco Bodrato(http://bodrato.it/ )
211         ;; Shift next bit of quotient into bit 0 of dividend
212         ;; Shift next MSB of dividend into LSB of remainder
213         ;; AC holds both dividend and quotient. While we shift a bit from
214         ;;  MSB of dividend, we shift next bit of quotient in from carry
215         ;; HL holds remainder
216         ;; Do a 32-bit left shift, shifting carry to C, C to A,
217         ;;  A to HL
218         rl      c               ; Carry (next bit of quotient) to bit 0
219         rla
220         adc     hl,hl           ; HL < 32768 before, no carry, ever.
221
222         ;; If remainder is >= divisor, next bit of quotient is 1. This
223         ;;  bit goes to carry
224         sbc     hl,de
225         jr      NC,.nodrop      ; Jump if remainder is >= dividend
226         add     hl,de           ; Otherwise, restore remainder
227         ;; The add above sets the carry, because sbc hl,de did set it.
228 .nodrop:
229         ccf                     ; Complement borrow so 1 indicates a
230                                 ;  successful substraction (this is the
231                                 ;  next bit of quotient)
232         djnz    .dvloop
233         ;; Shift last carry bit into quotient
234         ex      de,hl           ; DE = remainder, HL = divisor...
235         ;; HL does not contain remainder, it contains the divisor!
236         ; ld      d,h             ; DE = remainder
237         ; ld      e,l
238         rl      c               ; Carry to C
239         rla
240         ld      b,a             ; BC = quotient
241         ;; Carry now contains the same value it contained before
242         ;; entering .dvloop[*]: "0" = valid result.
243         ret
244