* Improved the pop params code
[fw/sdcc] / device / lib / z80 / div.s
diff --git a/device/lib/z80/div.s b/device/lib/z80/div.s
new file mode 100644 (file)
index 0000000..5917c24
--- /dev/null
@@ -0,0 +1,318 @@
+       ;; Originally from GBDK by Pascal Felber.
+       .area   _CODE
+
+__divschar::   
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      e,9(ix)
+       call    .div8
+
+       ld      l,c
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+       
+__modschar::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      e,9(ix)
+       call    .div8
+
+       ld      l,e
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+
+__divsint::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      b,9(ix)
+       ld      e,10(ix)
+       ld      d,11(ix)
+       call    .div16
+
+       ld      l,c
+       ld      h,b
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+       
+__modsint::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      b,9(ix)
+       ld      e,10(ix)
+       ld      d,11(ix)
+       call    .div16
+
+       ld      l,e
+       ld      h,d
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+
+       ;; Unsigned
+__divuchar::   
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      e,9(ix)
+       call    .divu8
+
+       ld      l,c
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+       
+__moduchar::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      e,9(ix)
+       call    .divu8
+
+       ld      l,e
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+
+__divuint::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      b,9(ix)
+       ld      e,10(ix)
+       ld      d,11(ix)
+       call    .divu16
+
+       ld      l,c
+       ld      h,b
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+       
+__moduint::
+       push    de
+       push    bc
+       push    ix
+       ld      ix,#0
+       add     ix,sp
+
+       ld      c,8(ix)
+       ld      b,9(ix)
+       ld      e,10(ix)
+       ld      d,11(ix)
+       call    .divu16
+
+       ld      l,e
+       ld      h,d
+       
+       pop     ix
+       pop     bc
+       pop     de
+       ret
+       
+.div8::
+.mod8::
+       LD      A,C             ; Sign extend
+       RLCA
+       SBC     A
+       LD      B,A
+       LD      A,E             ; Sign extend
+       RLCA
+       SBC     A
+       LD      D,A
+
+       ; Fall through to .div16
+       
+       ;; 16-bit division
+       ;; 
+       ;; Entry conditions
+       ;;   BC = dividend
+       ;;   DE = divisor
+       ;; 
+       ;; Exit conditions
+       ;;   BC = quotient
+       ;;   DE = remainder
+       ;;   If divisor is non-zero, carry=0
+       ;;   If divisor is 0, carry=1 and both quotient and remainder are 0
+       ;;
+       ;; Register used: AF,BC,DE,HL
+.div16::
+.mod16::
+       ;; Determine sign of quotient by xor-ing high bytes of dividend
+       ;;  and divisor. Quotient is positive if signs are the same, negative
+       ;;  if signs are different
+       ;; Remainder has same sign as dividend
+       LD      A,B             ; Get high byte of dividend
+       LD      (.srem),A       ; Save as sign of remainder
+       XOR     D               ; Xor with high byte of divisor
+       LD      (.squot),A      ; Save sign of quotient
+       ;; Take absolute value of divisor
+       BIT     7,D
+       JR      Z,.chkde        ; Jump if divisor is positive
+       SUB     A               ; Substract divisor from 0
+       SUB     E
+       LD      E,A
+       SBC     A               ; Propagate borrow (A=0xFF if borrow)
+       SUB     D
+       LD      D,A
+       ;; Take absolute value of dividend
+.chkde:
+       BIT     7,B
+       JR      Z,.dodiv        ; Jump if dividend is positive
+       SUB     A               ; Substract dividend from 0
+       SUB     C
+       LD      C,A
+       SBC     A               ; Propagate borrow (A=0xFF if borrow)
+       SUB     B
+       LD      B,A
+       ;; Divide absolute values
+.dodiv:
+       CALL    .divu16
+       RET     C               ; Exit if divide by zero
+       ;; Negate quotient if it is negative
+       LD      A,(.squot)
+       AND     #0x80
+       JR      Z,.dorem        ; Jump if quotient is positive
+       SUB     A               ; Substract quotient from 0
+       SUB     C
+       LD      C,A
+       SBC     A               ; Propagate borrow (A=0xFF if borrow)
+       SUB     B
+       LD      B,A
+.dorem:
+       ;; Negate remainder if it is negative
+       LD      A,(.srem)
+       AND     #0x80
+       RET     Z               ; Return if remainder is positive
+       SUB     A               ; Substract remainder from 0
+       SUB     E
+       LD      E,A
+       SBC     A               ; Propagate remainder (A=0xFF if borrow)
+       SUB     D
+       LD      D,A
+       RET
+
+.divu8::
+.modu8::
+       LD      B,#0x00
+       LD      D,B
+       ; Fall through to divu16
+
+.divu16::
+.modu16::
+       ;; Check for division by zero
+       LD      A,E
+       OR      D
+       JR      NZ,.divide      ; Branch if divisor is non-zero
+       LD      BC,#0x00        ; Divide by zero error
+       LD      D,B
+       LD      E,C
+       SCF                     ; Set carry, invalid result
+       RET
+.divide:
+       LD      L,C             ; L = low byte of dividend/quotient
+       LD      H,B             ; H = high byte of dividend/quotient
+       LD      BC,#0x00        ; BC = remainder
+       OR      A               ; Clear carry to start
+       LD      A,#16           ; 16 bits in dividend
+.dvloop:
+       ;; Shift next bit of quotient into bit 0 of dividend
+       ;; Shift next MSB of dividend into LSB of remainder
+       ;; BC holds both dividend and quotient. While we shift a bit from
+       ;;  MSB of dividend, we shift next bit of quotient in from carry
+       ;; HL holds remainder
+       ;; Do a 32-bit left shift, shifting carry to L, L to H,
+       ;;  H to C, C to B
+       LD      (.dcnt),A
+       RL      L               ; Carry (next bit of quotient) to bit 0
+       RL      H               ; Shift remaining bytes
+       RL      C
+       RL      B               ; Clears carry since BC was 0
+       ;; If remainder is >= divisor, next bit of quotient is 1. This
+       ;;  bit goes to carry
+       PUSH    BC              ; Save current remainder
+       LD      A,C             ; Substract divisor from remainder
+       SBC     E
+       LD      C,A
+       LD      A,B
+       SBC     D
+       LD      B,A
+       CCF                     ; Complement borrow so 1 indicates a
+                               ;  successful substraction (this is the
+                               ;  next bit of quotient)
+       JR      C,.drop         ; Jump if remainder is >= dividend
+       POP     BC              ; Otherwise, restore remainder
+       JR      .nodrop
+.drop:
+       INC     SP
+       INC     SP
+.nodrop:
+       LD      A,(.dcnt)
+       DEC     A               ; DEC does not affect carry flag
+       JR      NZ,.dvloop
+       ;; Shift last carry bit into quotient
+       LD      D,B             ; DE = remainder
+       LD      E,C
+       RL      L               ; Carry to L
+       LD      C,L             ; C = low byte of quotient
+       RL      H
+       LD      B,H             ; B = high byte of quotient
+       OR      A               ; Clear carry, valid result
+       RET
+
+       .area   _BSS
+
+.srem:
+       .ds 0x01                ; Sign of quotient
+.squot:
+       .ds 0x01                ; Sign of remainder
+.dcnt:
+       .ds 0x01                ; Counter for division