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