Assembly #3: Multiply and divide

Here we are again. For my first tutorial, see Assembly #1 Reverse a string. Bill set me up for a second challenge, and I took it up. This is another great bite sized exercise to learn how to compare blocks of memory. In this exercise we will compare 2 strings and let the user know if they are equal. We will make use of BASIC again to:

  1. Gather the user input
  2. Poke the strings to address where our assembly program can get to it
  3. Call our machine language program
  4. Peek at the result and tell the user if the two strings are equal.

Getting Started

The purpose of this project is to write another simple assembly language program, and learn to use a few more assembly mnemonics. Specifically the ones for comparing values. You will want to make sure you use the $JR compiler directive to make jumps easier.

The mnemonics for comparing are:

MNEMONIC      OPERATION   FLAGS EFFECTED  
CPIM n        [P] - n     C,Z  
CPIA n        A - n       C,Z
CPMA          [P] - A     C,Z

Use the following truth table to figure out what flags will be set.

TRUTH TABLE
EXAMPLE  FLAGS    MEANING
4 - 5    C=1,Z=0  left < right
5 - 5    C=0,Z=1  left = right
6 - 5    C=0,Z=0  left > right

You’re likely to find CPMA is the real workhorse when dealing with data. CPIA and CPIM are used when dealing with known values; Like if you want to see if something equals 0.

Once you have compared, you will use a branch mnemonic to jump to a new piece of code.

MNEMONIC      OPERATION
JPNZ nm       jump to nm if Z=0
JPNC nm       jump to nm if C=0
JPZ nm        jump to nm if Z=1
JPC nm        jump to nm if C=1
JP nm         jump to nm regardless of flags

As an example, lets say you want to see if the A reg equals the number 5. You would use the following code:

        CMIA    5       # A - n, check if A reg is 5
        JPZ     PLACE1
        # continue as A is not 5
        RTN

PLACE1: # A is 5
        RTN

JPZ jumps to PLACE1 if the zero flag was set, which according to the truth table means A equals 5.

Memory Map

This will be our memory setup. Program start is 49217.

ADDRESS       ASM VAR  COMMENT
49200         RESULT   Result (1 equal | 0 not equal)
49201 - 49207 SRC1     String 1
49208         SRCLEN1  Length of string 1
49209 - 49215 SRC2     String 2
49216         SRCLEN2  Length of string 2
49217                  Program start

Our assembly program will compare the 2 strings SRC1 and SRC2, making note of the string lengths SRCLEN1 & SRCLEN2, and store the result in address RESULT (49200).

Assembly Source

Compile and load at address 49200 (&C030). bin2wav.exe --type=bin --pc=1360 --addr=0xC030 output.bin and CLOAD M on your pocket computer.

    ##############################################################################
    # Compare the strings SRC1 and SRC2 to see if they
    # are equal. Store 0 in RESULT if no, 1 if they are.
    ##############################################################################

            $JR
            org     &C030

    I_REG   EQU     0               # Internal Registers
    J_REG   EQU     1
    A_REG   EQU     2
    B_REG   EQU     3
    XL_REG  EQU     4
    XH_REG  EQU     5
    YL_REG  EQU     6
    YH_REG  EQU     7
    K_REG   EQU     8
    L_REG   EQU     9
    M_REG   EQU     10
    N_REG   EQU     11
    PORTC   EQU     95

    # define data and put some dummy data in place
    RESULT:     DB      0 
    SRC1:       DB      48 49 50 51 52 53 54
    SRCLEN1:    DB      1
    SRC2:       DB      65 66 67 68 69 70 71
    SRCLEN2:    DB      1

    START:
            # Point the X register to SRC1
            LP      XH_REG      # P  [P] 
            LP      XL_REG      # P  [P] => X now contains SRC1
            DX                  # prep for ++X inside check1

            # Point the Y register to SRC2
            LP      YH_REG      # P  [P] 
            LP      YL_REG      # P  [P] => Y now contains SRC2
            DY                  # prep for ++Y inside check1

            # put srclen1 into A and srclen2 into B
            LIDP    SRCLEN2
            LDD                 # [DP] -> A - srclen -> A
            EXAB                # A  B - B contains srclen2
            LIDP    SRCLEN1
            LDD                 # A contains srclen1

            # check if strings are the same length
            LP      B_REG      # P points to B reg
            CPMA               # [P] - A -> C,Z     # compare A and B
            JPNZ    NOT_EQUAL

            # check byte by byte to see if the strings are the same length
            lp      K_REG
            exam            # K now contains srclen1
    check1:
            # put src1 bytes into A, src2 bytes into B, then compare A & B

            # load B from Y
            iy              # ++Y -> DP
            ldd             # [Y] -> A
            exab            # B contains [Y]

            # load A from X
            ixl             # ++X -> DP, [DP] -> A

            lp      B_REG
            cpma            # compare A and B
            jpnz    not_equal

            # decrement K reg
            deck
            jpnz    check1  # keep checking

            # fall through into equal

    equal:        
            lidp    result
            lia     1
            std
            rtn

    not_equal:
            lidp    result
            ra              # 0 -> A
            std
            rtn

BASIC Source

You can type RUN to start this program once loaded.

100 "S" WAIT 0
110 A=49201:R=49200
120 INPUT "String 1:";X$
130 INPUT "String 2:";Y$
135 PRINT "POKING"
140 K = LEN X$
150 L = LEN Y$
200 FOR I=1 TO K
205 V = ASC(MID$(X$,I,1))
210 POKE A,V
220 A=A+1
230 NEXT I
240 A=49209
250 FOR I=1 TO L
265 V = ASC(MID$(Y$,I,1))
270 POKE A,V
280 A=A+1
290 NEXT I
295 POKE 49208,K : POKE 49216,L
300 PRINT "CALLING ML"
310 CALL 49217
320 IF PEEK 49200 = 0 PRINT "NOT EQUAL"
330 IF PEEK 49200 = 1 PRINT "EQUAL"

Feel free to drop me a line at eightbit at axorion dot com. You can also start a thread on Simons Pocket Computer Forum. Best to let me know you did though, in case I don’t see your forum entry right away.

I was a little surprised to find the 1360 didn't have any instructions for multiplication or division. I especially found this hard to believe because this is a scientific calculator. What the heck? Lucky for me, this presented a perfect opportunity to write a couple simple assembly routines. This time we will forgo the BASIC code, and just stick with the assembly. To interact with the routines, we will just `PEEK` and `POKE` memory. You can also check out * [Assembly #1: Reverse a string](http://www.axorion.com/?f=blog&pageId=45) * [Assembly #2: String compare](http://www.axorion.com/?f=blog&pageId=46) # Getting Started # The purpose of this project is to write 2 routines. One will multiply 2 numbers and produce a product, the other will divide 2 numbers and return a quotient and remainder. # Memory Map # Our memory map. ADDRESS ASM VAR 49205 dividend 49204 divisor 49205 quotient 49206 remainder 49207 multiplicand 49208 multiplier 49209 product 49210 division entry point 49249 multiplication entry point # Assembly Source # Multiplication without a multiply instruction is easy. We just keep adding the multiplicand to itself multiplier times. Division by subtraction is a little more interesting. Let's say you want to divide 60 by 12. Using subtraction you would: 60-12=48 count 1 48-12=36 count 2 36-12=24 count 3 24-12=12 count 4 12-12=0 count 5 Thus, 60÷12=5. You can even handle remainders: 64-12=52 count 1 52-12=40 count 2 40-12=28 count 3 28-12=16 count 4 16-12=4 count 5 4 < 12, so 64÷12 is 5 with a remainder of 4. > **Note:** > > Remember that this is only 8-bit math, and we don't handle overflow. So 128*3 will produce the answer 128 instead of 384. ############################################################################## # Math.asm # Sample multiplication and division routines for 8-bit numbers. # Does not handle overflow. So 128*3 will equal 128 instead of 384. # Division will produce a quotient and remainder. # # Division routine: # INPUT: Poke 8-bit numbers to dividend and divisor addresses # OUTPUT: Peek the 8-bit result at quotient and remainder addresses. # # Example: # Divide 17 by 5. Answer is 3 with a remainder of 2 # # PEEK 49205 RETURNS 0 # PEEK 49206 RETURNS 0 # # POKE 49203,17 # POKE 49204,5 # # CALL 49210 # # PEEK 49205 RETURNS 3 # PEEK 49206 RETURNS 2 # # Multiplication routine: # INPUT: Poke 8-bit numbers to dividend and divisor addresses # OUTPUT: Peek the 8-bit result at quotient and remainder addresses. # # Example: # Multiply 5 times 7. Answer is 35. # # PEEK 49209 RETURNS 35 # # POKE 49207,5 # POKE 49208,7 # # CALL 49249 # # PEEK 49209 RETURNS 35 # # http://www.axorion.com ############################################################################## $JR org &C030 I_REG equ 0 # Internal Registers J_REG equ 1 A_REG equ 2 B_REG equ 3 XL_REG equ 4 XH_REG equ 5 YL_REG equ 6 YH_REG equ 7 K_REG equ 8 L_REG equ 9 M_REG equ 10 N_REG equ 11 PORTC equ 95 jp start # skip over allocated memory right into division dividend: db 10 # a number divisor: db 5 # divided by this quotient: db 0 # equals the answer remainder: db 0 # with a remainder multiplicand: db 3 multiplier: db 7 product: db 0 # &45 069 SBM [P] - A -> [P] 1 3 C,Z # &DA 218 EXAB A B 1 3 # &DB 219 EXAM A [P] 1 3 # &63 099 CPIM n [P] - n -> C,Z 2 4 C,Z # &67 103 CPIA n A - n -> C,Z 2 4 C,Z # &C7 199 CPMA [P] - A -> C,Z 1 3 C,Z # To divide 60 by 12 using subtraction: # B A # 60-12=48 count 1 # 48-12=36 count 2 # 36-12=24 count 3 # 24-12=12 count 4 # 12-12=0 count 5. # Thus, 60÷12=5. # You can even handle remainders: # 64-12=52count 1 # 52-12=40count 2 # 40-12=28count 3 # 28-12=16count 4 # 16-12=4 count 5. # 4 < 12, so 64÷12 is 5 with a remainder of 4. start: divide: lp K_REG ra # clear a exam # 0 -> K, K will be our quotient lidp dividend # loading B with dividend ldd exab # A B, B now contains the dividend lidp divisor # loading A with divisor ldd # A now contains the divisor add1: inck # increment the quotient lp B_REG # Point P to B reg sbm # [P] - A -> [P], B=B-A #if B=0 we are done cpim 0 # B == 0? jpz done1 # yup, B == 0 #if B < A we are done, and B will be the remainder cpma # compare B and A jpc done1 # B < A jp add1 done1: lidp quotient # storing the quotient lp K_REG exam # A [P], A now contains K, the quotient std # quotient is now stored lidp remainder # storing the remainer exab # A B, A now contains the remainder std # remainder is now stored rtn multiply: lidp multiplicand # load multiplicand into B ldd exab # B now contains multiplicand lidp multiplier # load multiplier into A ldd deca # correction for loop jpc doneZero # multipling by 0 # keep adding A to B push # push A onto stack for loop, to add B to B A times lidp multiplicand # load multiplicand into A now, so A+B is same as B+B ldd lp B_REG # point to B reg add2: adm # [P] + A -> [P], B=B+A loop add2 sbm # [P] - A -> P,B=B-A to correct for one extra addition in loop done2: exab # A B, A now contains product lidp product # storing A to product std # A -> [DP] store A rtn doneZero: ra # store 0 into product lidp product std rtn Feel free to drop me a line at eightbit at axorion dot com. You can also start a thread on [Simons Pocket Computer Forum](http://30122.forumromanum.com/member/forum/forum.php?action=std_tindex&USER=user_30122&threadid=2). Best to let me know you did though, in case I don't see your forum entry right away.

Here we are again. For my first tutorial, see Assembly #1 Reverse a string. Bill set me up for a second challenge, and I took it up. This is another great bite sized exercise to learn how to compare blocks of memory. In this exercise we will compare 2 strings and let the user know if they are equal. We will make use of BASIC again to:

  1. Gather the user input
  2. Poke the strings to address where our assembly program can get to it
  3. Call our machine language program
  4. Peek at the result and tell the user if the two strings are equal.

Getting Started

The purpose of this project is to write another simple assembly language program, and learn to use a few more assembly mnemonics. Specifically the ones for comparing values. You will want to make sure you use the $JR compiler directive to make jumps easier.

The mnemonics for comparing are:

MNEMONIC      OPERATION   FLAGS EFFECTED  
CPIM n        [P] - n     C,Z  
CPIA n        A - n       C,Z
CPMA          [P] - A     C,Z

Use the following truth table to figure out what flags will be set.

TRUTH TABLE
EXAMPLE  FLAGS    MEANING
4 - 5    C=1,Z=0  left < right
5 - 5    C=0,Z=1  left = right
6 - 5    C=0,Z=0  left > right

You’re likely to find CPMA is the real workhorse when dealing with data. CPIA and CPIM are used when dealing with known values; Like if you want to see if something equals 0.

Once you have compared, you will use a branch mnemonic to jump to a new piece of code.

MNEMONIC      OPERATION
JPNZ nm       jump to nm if Z=0
JPNC nm       jump to nm if C=0
JPZ nm        jump to nm if Z=1
JPC nm        jump to nm if C=1
JP nm         jump to nm regardless of flags

As an example, lets say you want to see if the A reg equals the number 5. You would use the following code:

        CMIA    5       # A - n, check if A reg is 5
        JPZ     PLACE1
        # continue as A is not 5
        RTN

PLACE1: # A is 5
        RTN

JPZ jumps to PLACE1 if the zero flag was set, which according to the truth table means A equals 5.

Memory Map

This will be our memory setup. Program start is 49217.

ADDRESS       ASM VAR  COMMENT
49200         RESULT   Result (1 equal | 0 not equal)
49201 - 49207 SRC1     String 1
49208         SRCLEN1  Length of string 1
49209 - 49215 SRC2     String 2
49216         SRCLEN2  Length of string 2
49217                  Program start

Our assembly program will compare the 2 strings SRC1 and SRC2, making note of the string lengths SRCLEN1 & SRCLEN2, and store the result in address RESULT (49200).

Assembly Source

Compile and load at address 49200 (&C030). bin2wav.exe --type=bin --pc=1360 --addr=0xC030 output.bin and CLOAD M on your pocket computer.

    ##############################################################################
    # Compare the strings SRC1 and SRC2 to see if they
    # are equal. Store 0 in RESULT if no, 1 if they are.
    ##############################################################################

            $JR
            org     &C030

    I_REG   EQU     0               # Internal Registers
    J_REG   EQU     1
    A_REG   EQU     2
    B_REG   EQU     3
    XL_REG  EQU     4
    XH_REG  EQU     5
    YL_REG  EQU     6
    YH_REG  EQU     7
    K_REG   EQU     8
    L_REG   EQU     9
    M_REG   EQU     10
    N_REG   EQU     11
    PORTC   EQU     95

    # define data and put some dummy data in place
    RESULT:     DB      0 
    SRC1:       DB      48 49 50 51 52 53 54
    SRCLEN1:    DB      1
    SRC2:       DB      65 66 67 68 69 70 71
    SRCLEN2:    DB      1

    START:
            # Point the X register to SRC1
            LP      XH_REG      # P  [P] 
            LP      XL_REG      # P  [P] => X now contains SRC1
            DX                  # prep for ++X inside check1

            # Point the Y register to SRC2
            LP      YH_REG      # P  [P] 
            LP      YL_REG      # P  [P] => Y now contains SRC2
            DY                  # prep for ++Y inside check1

            # put srclen1 into A and srclen2 into B
            LIDP    SRCLEN2
            LDD                 # [DP] -> A - srclen -> A
            EXAB                # A  B - B contains srclen2
            LIDP    SRCLEN1
            LDD                 # A contains srclen1

            # check if strings are the same length
            LP      B_REG      # P points to B reg
            CPMA               # [P] - A -> C,Z     # compare A and B
            JPNZ    NOT_EQUAL

            # check byte by byte to see if the strings are the same length
            lp      K_REG
            exam            # K now contains srclen1
    check1:
            # put src1 bytes into A, src2 bytes into B, then compare A & B

            # load B from Y
            iy              # ++Y -> DP
            ldd             # [Y] -> A
            exab            # B contains [Y]

            # load A from X
            ixl             # ++X -> DP, [DP] -> A

            lp      B_REG
            cpma            # compare A and B
            jpnz    not_equal

            # decrement K reg
            deck
            jpnz    check1  # keep checking

            # fall through into equal

    equal:        
            lidp    result
            lia     1
            std
            rtn

    not_equal:
            lidp    result
            ra              # 0 -> A
            std
            rtn

BASIC Source

You can type RUN to start this program once loaded.

100 "S" WAIT 0
110 A=49201:R=49200
120 INPUT "String 1:";X$
130 INPUT "String 2:";Y$
135 PRINT "POKING"
140 K = LEN X$
150 L = LEN Y$
200 FOR I=1 TO K
205 V = ASC(MID$(X$,I,1))
210 POKE A,V
220 A=A+1
230 NEXT I
240 A=49209
250 FOR I=1 TO L
265 V = ASC(MID$(Y$,I,1))
270 POKE A,V
280 A=A+1
290 NEXT I
295 POKE 49208,K : POKE 49216,L
300 PRINT "CALLING ML"
310 CALL 49217
320 IF PEEK 49200 = 0 PRINT "NOT EQUAL"
330 IF PEEK 49200 = 1 PRINT "EQUAL"

Feel free to drop me a line at eightbit at axorion dot com. You can also start a thread on Simons Pocket Computer Forum. Best to let me know you did though, in case I don’t see your forum entry right away.

You Can Also Check Out