;*******************************************************************************
;* Module    : BIGMULT.SUB
;* Programmer: Tony Papadimitriou <tonyp@acm.org>
;* Language  : Motorola/Freescale/NXP 68HC11 Assembly Language (aspisys.com/ASM11)
;* Purpose   : General-purpose re-entrant multi-byte multiplication (example code)
;* Status    : FREEWARE Copyright (c) 2020 by Tony Papadimitriou <tonyp@acm.org>
;* History   : 09.06.28 v1.00 Optimized for size and speed
;*           : 11.02.25 v1.01 Fixed COP reset by using related macro
;*           : 13.01.27       Moved test code at EOF (for #EXIT optimization)
;*******************************************************************************

#ifmain ;-----------------------------------------------------------------------
                    #ListOff
                    #Uses     mcu.inc
                    #ListOn
#endif ;------------------------------------------------------------------------

;*******************************************************************************
; Purpose: Multiply two numbers of any byte size (both operands of same size for simplicity)
; Input  : B = size for Multiplier or Multiplicand (range 1..127)
;        :      +--------------+------------+---------+
;        : X -> | Multiplicand | Multiplier | Product |
;        :      +--------------+------------+---------+
; Output : Product filled with product of multiplication
; Size   : 128 bytes

Multiply            proc
                    #temp
ix_b@@              next      :temp               ;index B
ix_a@@              next      :temp               ;index A
.product@@          next      :temp,2             ;pointer to product
.multiplier@@       next      :temp,2             ;pointer to multiplier
.multiplicand@@     next      :temp,2             ;pointer to multiplicand
op_size@@           next      :temp               ;operand size

                    tstb
                    beq       Done@@              ;zero size won't work
                    cmpb      #127
                    bhi       Done@@              ;over 127-byte number won't work

                    pshy
                    psha

                    pshb                          ;operand size
                    pshx                          ;HX -> Multiplicand

                    abx                           ;HX -> Multiplier
                    pshx

                    abx                           ;HX -> Product
                    pshx

                    pshb                          ;index to current multiplicand digit
                    pshb                          ;index to current multiplier digit

                    tsy

                    lslb                          ;product is twice the size
Zero@@              clr       ,x                  ;initialize product to zero
                    inx
                    decb
                    bne       Zero@@

Loop@@              ldx       .multiplicand@@,y
                    ldb       ix_a@@,y
                    abx
                    dex                           ;zero-based offset
                    lda       ,x                  ;A = current multiplicand digit

                    ldx       .multiplier@@,y
                    ldb       ix_b@@,y
                    abx
                    dex                           ;zero-based offset
                    ldb       ,x                  ;X = current multiplier digit

                    mul                           ;XA = sub-product
                    pshd                          ;save sub-product temporarily

                    ldx       .product@@,y
                    ldb       ix_a@@,y
                    addb      ix_b@@,y
                    abx
                    dex:2                         ;backup one for word & one for zero-based offset

                    puld                          ;update product with sub-product
                    addd      ,x
                    std       ,x

          ;cascade possible Carry all the way to beginning of Product

                    bcc       NextDigit@@         ;skip over Carry cascade

                    ldb       ix_b@@,y
                    addb      ix_a@@,y
                    subb      #2                  ;zero-based index & less the one we're at
                    beq       NextDigit@@         ;skip over Carry cascade

                    sec                           ;always a Carry from here
Carry@@             dex

                    clra
                    adca      ,x
                    sta       ,x

                    decb
                    bne       Carry@@

          ;done with current multiplier digit

NextDigit@@         @cop                          ;in case of many iterations
                    dec       ix_b@@,y
                    bne       Loop@@

                    lda       op_size@@,y         ;restore multiplier...
                    sta       ix_b@@,y            ; ...digit counter

          ;done with current multiplicand digit

                    dec       ix_a@@,y
                    bne       Loop@@

                    tsx
                    ldb       #6                  ;de-allocate local variables
                    abx
                    txs

                    pulx                          ;restore caller's registers
                    pulb
                    pula
                    puly

Done@@              rts

;*******************************************************************************
                    #Exit
;*******************************************************************************
                    #Message  Big Multiply is {*-Multiply} bytes long

MULT_OPERAND_SIZE   def       4                   ;default operand size

          #ifz MULT_OPERAND_SIZE
                    #Error    MULT_OPERAND_SIZE must be non-zero
          #endif

          #if MULT_OPERAND_SIZE > 127
                    #Error    Current coding supports up to 127 byte operands
          #endif

                    #RAM
MyVars_Begin

Multiplicand        rmb       MULT_OPERAND_SIZE   ;|
Multiplier          rmb       *-Multiplicand      ; > keep together and in order
Product             rmb       *-Multiplicand      ;|

MyVars_End
                    #ROM

Start               proc
                    @rsp

                    @ClrRange #MyVars_Begin,#MyVars_End

                    ldd       #$1234              ;Multiplicand; $123456
                    std       Multiplicand
                    ldd       #$5678
                    std       Multiplicand+2

                    ldd       #$1234
                    std       Multiplier          ;Multiplier: $1234
                    ldd       #$3456
                    std       Multiplier+2

                    ldx       #Multiplicand
                    ldb       #MULT_OPERAND_SIZE
                    jsr       Multiply            ;Product: $14B60AD78

                    bra       *

                    @vector   Vreset,Start