#include "common.inc"

    global  AritBuffer
    global  DecNumber
    global  Multiply
    global  Divide
    global  DivideFraction
    global  ToBCDInteger
    global  ToBCDFraction

    global  TimeBaseAdd
    global  TimeBaseRead

    udata

;
; Assuming everything fits into bank 0
;

AritBuffer  res .17 ; 129 bit Arithmetics Buffer
VarA        res 1   ; General purpose variable.
VarB        res 1   ; General purpose variable.
VarC        res 1   ; General purpose variable.
SaveFSR     res 1   ; FSR saving place
SaveStatus  res 1   ; STATUS,IRP saving place

DecNumber   res .10 ; 20 digit of floating point BCD fraction. One digit 4 bit.

    code

    ; This function does not use FSR.
    ; Non-reentrant, uses VarA, VarB
    ;
    ; -> AritBuffer[0]: 4 byte multiplicand 
    ; -> AritBuffer[8]: 4 byte multiplicator
    ; <- AritBuffer[0]: 8 byte result 
    ;
Multiply:

    swapf   STATUS,W
    BANKSEL SaveStatus
    movwf   SaveStatus

    ; clear upper 4 byte of result

    clrf    AritBuffer+.4
    clrf    AritBuffer+.5
    clrf    AritBuffer+.6
    clrf    AritBuffer+.7

    ; cycle 32 times
    movlw   .32
    movwf   VarA

MultiplyLoop:    

    bcf     STATUS,C
    btfsc   AritBuffer,0          

    ; with a goto instead of call we save stack

    goto    AddMultiply ; it might set carry, which we will add

MultiplyBack:

    rrf     AritBuffer+.7,F 
    rrf     AritBuffer+.6,F 
    rrf     AritBuffer+.5,F 
    rrf     AritBuffer+.4,F 
    rrf     AritBuffer+.3,F 
    rrf     AritBuffer+.2,F 
    rrf     AritBuffer+.1,F 
    rrf     AritBuffer+0,F 

    decfsz  VarA,F
    goto    MultiplyLoop

    BANKSEL SaveStatus
    swapf   SaveStatus,W
    movwf   STATUS

    return

AddMultiply:
    ;
    ; Add AritBuffer[8] to AritBuffer[4] store it in AritBuffer[4], C
    ;

    clrf    VarB    ; Carry

    ; Byte 0

    movf    AritBuffer+.8,W
    addwf   AritBuffer+.4,F
    btfsc   STATUS,C
    bsf     VarB,0
    
    ; Byte 1

    movf    AritBuffer+.9,W
    addwf   VarB,W
    bcf     VarB,0
    btfsc   STATUS,C
    bsf     VarB,0
    addwf   AritBuffer+.5,F
    btfsc   STATUS,C
    bsf     VarB,0
    
    ; Byte 2

    movf    AritBuffer+.10,W
    addwf   VarB,W
    bcf     VarB,0
    btfsc   STATUS,C
    bsf     VarB,0
    addwf   AritBuffer+.6,F
    btfsc   STATUS,C
    bsf     VarB,0

    ; Byte 2

    movf    AritBuffer+.11,W
    addwf   VarB,W
    bcf     VarB,0
    btfsc   STATUS,C
    bsf     VarB,0
    addwf   AritBuffer+.7,F
    btfsc   STATUS,C
    bsf     VarB,0

    ; set carry

    bcf     STATUS,C
    btfsc   VarB,0
    bsf     STATUS,C

    goto    MultiplyBack
    

Divide:
    ; This function does not use FSR.
    ; Non-reentrant, uses VarA, VarB
    ;
    ; -> AritBuffer[0]: 8 byte dividend 
    ; -> AritBuffer[8]: 4 byte divisor
    ; <- AritBuffer[0]: 8 byte quotient
    ; <- AritBuffer[.12]: 4 byte remainder

    swapf   STATUS,W
    BANKSEL SaveStatus
    movwf   SaveStatus

    clrf    AritBuffer+.12
    clrf    AritBuffer+.13
    clrf    AritBuffer+.14
    clrf    AritBuffer+.15

    goto    DivideNoClear

DivideFraction:

    ; This function does not use FSR.
    ; Non-reentrant, uses VarA, VarB
    ;
    ; -> AritBuffer[8]: 4 byte divisor from previous division
    ; -> AritBuffer[.12]: 4 byte remainder from previous division
    ; <- AritBuffer[0]: 8 byte binary fraction

    swapf   STATUS,W
    BANKSEL SaveStatus
    movwf   SaveStatus

    clrf    AritBuffer+.0
    clrf    AritBuffer+.1
    clrf    AritBuffer+.2
    clrf    AritBuffer+.3
    clrf    AritBuffer+.4
    clrf    AritBuffer+.5
    clrf    AritBuffer+.6
    clrf    AritBuffer+.7

DivideNoClear:

    ; cycle 64 times
    movlw   .64
    movwf   VarA

DivideLoop:

    clrf    AritBuffer+.16

    bcf     STATUS,C
    rlf     AritBuffer+.0,F
    rlf     AritBuffer+.1,F
    rlf     AritBuffer+.2,F
    rlf     AritBuffer+.3,F
    rlf     AritBuffer+.4,F
    rlf     AritBuffer+.5,F
    rlf     AritBuffer+.6,F
    rlf     AritBuffer+.7,F

    ; destination is after divisor

    rlf     AritBuffer+.12,F
    rlf     AritBuffer+.13,F
    rlf     AritBuffer+.14,F
    rlf     AritBuffer+.15,F
    rlf     AritBuffer+.16,F

    goto    SubstactDivide  

DivideBack:

    btfsc   STATUS,C     ; skip borrow
    bsf     AritBuffer,0

    decfsz  VarA,F
    goto    DivideLoop

    BANKSEL SaveStatus
    swapf   SaveStatus,W
    movwf   STATUS

    return

SubstactDivide:

    ; Substract AritBuffer[8] (4bytes) from AritBuffer[12] 4 bytes
    ; if it can be substracted without borrow.
    ; return with STATUS,C set if it was substracted.

    ; Test run. The actual substraction is not taking place now.


    ; Byte 0
    clrf    VarB
    movf    AritBuffer+.8,W
    subwf   AritBuffer+.12,W
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    ; Byte 1

    movf    AritBuffer+.9,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.13,W
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    ; Byte 2

    movf    AritBuffer+.10,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.14,W
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    ; Byte 3

    movf    AritBuffer+.11,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.15,W
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0

    btfss   VarB,0      ; Skip Borrow
    goto    DivideNoborrow

    bcf     STATUS,C    ; Borrow
    btfss   AritBuffer+.16,0;  There is one more bit
    goto    DivideBack 


DivideNoborrow:

    ; Real substract - everything is same except F instead of W

    ; Byte 0

    clrf    VarB
    movf    AritBuffer+.8,W
    subwf   AritBuffer+.12,F
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0

    ; Byte 1

    movf    AritBuffer+.9,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.13,F
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    ; Byte 2

    movf    AritBuffer+.10,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.14,F
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    ; Byte 3

    movf    AritBuffer+.11,W
    addwf   VarB,W
    clrf    VarB
    btfsc   STATUS,C
    bsf     VarB,0
    subwf   AritBuffer+.15,F
    btfss   STATUS,C    ; Skip not borrow
    bsf     VarB,0
    
    bsf     STATUS,C    ; Not borrow. Borrow already returned
    goto    DivideBack


ToBCDInteger:

    ; Convert binary integer to BCD
    ; -> AritBuffer[0]: 8 byte binary integer (AritBuffer[0] LSB)
    ; <- DecNumber BCD integer
    ; The algo uses shift right (multiply by 2) operation
    ; to shift out binary and shift in BCD.

    swapf   STATUS,W
    BANKSEL SaveStatus
    movwf   SaveStatus

    movf    FSR,W
    movwf   SaveFSR

    bcf     STATUS,IRP  ; FSR bank 0,1

    ;
    ; Clear DecNumber buffer
    ;
    movlw   .10
    movwf   VarA

    movlw   DecNumber
    movwf   FSR

ToBCDIntegerLoop1:

    clrf    INDF
    incf    FSR,F
    decfsz  VarA,F
    goto    ToBCDIntegerLoop1

    ; Shift out all 8 bytes from AritBuffer[7] into
    ; Decbuffer[0]..Decbuffer[.19]

    movlw   .64     ; number of bits
    movwf   VarA

ToBCDIntegerLoop2

    bcf     STATUS,C
    rlf     AritBuffer+.0,F
    rlf     AritBuffer+.1,F
    rlf     AritBuffer+.2,F
    rlf     AritBuffer+.3,F
    rlf     AritBuffer+.4,F
    rlf     AritBuffer+.5,F
    rlf     AritBuffer+.6,F
    rlf     AritBuffer+.7,F

    ; Shift carry into DecNumber[0-.19] decimally, multiplying by 2

    movlw   .10     ; 20 nibbles = 10 bytes
    movwf   VarB

    movlw   DecNumber
    movwf   FSR

ToBCDIntegerLoop3

    clrf    VarC
    btfsc   STATUS,C   
    bsf     VarC,0      ; carry in VarC LSB

    ; Prepare for BCD multiply 2 via shift

    movf    INDF,W

    addlw   H'0b'       ; test 5 or greater become 0 or greater
    btfsc   STATUS,DC   ; add 3 to it
    addlw   H'3' 
    addlw   -H'0b'      ; restore

    addlw   H'0b0'      ; test 50 or greater become 0 or greater
    btfsc   STATUS,C    ; add 30 to it
    addlw   H'30' 
    addlw   -H'0b0'     ; restore

    movwf   INDF

    bcf     STATUS,C
    btfsc   VarC,0
    bsf     STATUS,C
    
    rlf     INDF,F

    incf    FSR,F
    decfsz  VarB,F
    goto    ToBCDIntegerLoop3

    decfsz  VarA,F
    goto    ToBCDIntegerLoop2

    ; Restore FSR

    movf    SaveFSR,W
    movwf   FSR

    BANKSEL SaveStatus
    swapf   SaveStatus,W
    movwf   STATUS

    return

ToBCDFraction:
    ; Convert binary fraction to BCD
    ; -> AritBuffer[0]: 8 byte binary fraction (AritBuffer[0] LSB)
    ; <- DecNumber[0] .10 byte BCD fraction  DecNumber[.9] MSB
    ; The algo uses shift left (divide by 2) operation
    ; to shift out binary and shift in BCD.
    ; for instance, 0.125 =  DecNumber[.8]= 05,  DecNumber[.9] = 21

    swapf   STATUS,W
    BANKSEL SaveStatus
    movwf   SaveStatus

    movf    FSR,W
    movwf   SaveFSR

    bcf     STATUS,IRP  ; FSR bank 0,1

    ;
    ; Clear DecNumber buffer
    ;
    movlw   .10
    movwf   VarA

    movlw   DecNumber
    movwf   FSR

ToBCDFractionLoop1:

    clrf    INDF
    incf    FSR,F
    decfsz  VarA,F
    goto    ToBCDFractionLoop1

    ; Shift out all 8 bytes from AritBuffer[7] into
    ; Decbuffer[0]..Decbuffer[.19]

    movlw   .64     ; number of bits
    movwf   VarA

ToBCDFractionLoop2

    bcf     STATUS,C
    rrf     AritBuffer+.7,F
    rrf     AritBuffer+.6,F
    rrf     AritBuffer+.5,F
    rrf     AritBuffer+.4,F
    rrf     AritBuffer+.3,F
    rrf     AritBuffer+.2,F
    rrf     AritBuffer+.1,F
    rrf     AritBuffer+.0,F

    ; Shift carry into DecNumber[0-.19] decimally, multiplying by 2

    movlw   .10     ; 20 nibbles = 10 bytes
    movwf   VarB

    movlw   DecNumber+.9
    movwf   FSR

ToBCDFractionLoop3

    rrf     INDF,F

    clrf    VarC
    btfsc   STATUS,C   
    bsf     VarC,0      ; carry in VarC LSB

    ; Prepare for BCD divison by 2 via shift

    movf    INDF,W

    btfsc   INDF,7     ; test if 80 came in
    addlw   -H'30'     ; substract 30

    btfsc   INDF,3     ; test if 8 came in
    addlw   -H'3'      ; substract 3

    movwf   INDF

    bcf     STATUS,C
    btfsc   VarC,0
    bsf     STATUS,C
    

    decf    FSR,F
    decfsz  VarB,F
    goto    ToBCDFractionLoop3

    decfsz  VarA,F
    goto    ToBCDFractionLoop2

    ; Restore FSR

    movf    SaveFSR,W
    movwf   FSR

    BANKSEL SaveStatus
    swapf   SaveStatus,W
    movwf   STATUS

    return

TimeBaseAdd:

    ; Add W to current timebase that is stored in EEPROM
    ; W can be negative.

    BANKSEL VarA
    movwf   VarA
    ; Read data into AritBuffer

    movlw   0
    call    TimeBaseRead
    BANKSEL AritBuffer
    movwf   AritBuffer+0

    movlw   1
    call    TimeBaseRead
    BANKSEL AritBuffer
    movwf   AritBuffer+1

    movlw   2
    call    TimeBaseRead
    BANKSEL AritBuffer
    movwf   AritBuffer+2

    movlw   3
    call    TimeBaseRead
    BANKSEL AritBuffer
    movwf   AritBuffer+3

    ; Add W into aritbuffer
    movf    VarA,W
    btfsc   VarA,7
    goto    TimeBaseSub

    ; Add
    addwf   AritBuffer,F
    btfss   STATUS,C
    goto    TimeBaseAddDone

    movlw   1
    addwf   AritBuffer+1,F
    btfss   STATUS,C
    goto    TimeBaseAddDone

    addwf   AritBuffer+2,F
    btfss   STATUS,C
    goto    TimeBaseAddDone

    addwf   AritBuffer+3,F
    goto    TimeBaseAddDone

TimeBaseSub:

    ; Substract

    addwf   AritBuffer,F
    btfsc   STATUS,C            ; Skip BORROW
    goto    TimeBaseAddDone

    movlw   -1
    addwf   AritBuffer+1,F
    btfsc   STATUS,C
    goto    TimeBaseAddDone

    addwf   AritBuffer+2,F
    btfsc   STATUS,C
    goto    TimeBaseAddDone

    addwf   AritBuffer+3,F
    ; goto    TimeBaseAddDone
    
TimeBaseAddDone:
    movlw   0
    call    TimeBaseWriteByte
    movlw   1
    call    TimeBaseWriteByte
    movlw   2
    call    TimeBaseWriteByte
    movlw   3
    call    TimeBaseWriteByte

    return

TimeBaseWriteByte:

    ; Write AritBuffer[W] to EEPROM[W]


    BANKSEL EEADR
    movwf   EEADR       ; Address to write

    BANKSEL VarA
    movwf   VarA        ; Same bank as SaveFSR

    movf    FSR,W
    movwf   SaveFSR

    movf    VarA,W
    addlw   AritBuffer
    movwf   FSR 
    bcf     STATUS,IRP  ; FSR bank 0,1

    ; Write bytes

    movf    INDF,W

    BANKSEL EEDATA
    movwf   EEDATA      ; Data to write

    BANKSEL SaveFSR
    movf    SaveFSR,W
    movwf   FSR
    
    BANKSEL EECON1 
    bcf     EECON1,EEPGD    ; Point to data memory 
    bsf     EECON1,WREN     ; Enable write

    ; Disable interrups

    BANKSEL INTCON
    bcf     INTCON,GIE

    BANKSEL EECON2
    movlw   h'55'
    movwf   EECON2          ; Assume EECON2 is same bank as EECOM1
    movlw   h'AA'
    movwf   EECON2          ; Assume EECON2 is same bank as EECOM1
    bsf     EECON1,WR
   
    ; Enable interrupts 
    BANKSEL INTCON
    bsf     INTCON,GIE 

    bcf     EECON1,WREN     ; Disable write

    BANKSEL EECON1

TimeBaseWait1:

    btfsc   EECON1,WR       ; Wait for preveious write to complete
    goto    TimeBaseWait1

    return
    

TimeBaseRead:
    ; Read TimeBase at W. 0 is LSB.
    ; return byte in W
    BANKSEL EEADR
    movwf   EEADR           ; Address to read

    BANKSEL EECON1
    bcf     EECON1,EEPGD    ; Point to data memory

    bsf     EECON1,RD       ; EEprom read
    BANKSEL EEDATA
    movf    EEDATA,W
    BANKSEL AritBuffer      ; Select page 0

    return

    org     0x2100 ; EEPROM area
    
;
; This is the CPLD reference clock frequency
; 9953280 => 97E000
; When measure it was: 9953280 * 1.00000350 = 9953314
;
;   LSB first

ERRORLEVEL -220 ; Address exceeds maximum range for this processor

    ; de       0x00, 0xe0, 0x97, 0x00
    ; 27MHz = 19BFCC0
    ; de       0xc0, 0xfc, 0x9b, 0x01
    ; 100MHz = 5F5E100
    de       0x00, 0xe1, 0xf5, 0x05
    de      'S', 'i', 'n', 'a', 'i', ' '
    de      'G', 'a', 's', 'p', 'a', 'r', '\n'
    de      'e', 'm', 'a', 'i', 'l', ':'
    de      'g', 'a', 's', 'p', 'a', 'r'
    de      '@', 'y', 'u', 'd', 'i', 't'
    de      '.', 'o', 'r', 'g', '\n'
    de      'Y', 'u', 'p', 'r', 'a', '\n'
    de      'v', '1', '.', '0', '.', '0', '\n'
    de      '2', '0', '1', '0', '-', '0', '5', '-', '2', '3', '\n'
    de      'T', 'o', 'k', 'y', 'o', '\n'
    de      0x00

    end
