HCS12 Calculator

Discussion in 'Embedded Systems and Microcontrollers' started by jenxin, Nov 26, 2012.

  1. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Hi all, I'm new to the forum(wish i found this awhile back).
    My name's Billy.

    I need help with one of my projects and hopefully you can help.

    I'm using a HCS12 microcontroller to build a simple calculator.
    I'm using a 4x4 matrix keypad(0-9,A-F) and also a Seiko 2x16 LCD Display.

    This will be compiled with assembly using Freescale CodeWarrior.
    I've built the circuit properly already(assume touchpad+LCD works fine).

    This calculator is supposed to support 4 digit operations,
    ie.. 9999 (+,-,x,/) 9999.

    How would I retrieve the numbers and store it into 1 variable?

    Lets say the number 9821, aka $265D. I would store it as a 2 byte variable called DIGIT1. The user should first input 9 then 8, 2 ,1. I will implement this so that if the user presses a +-*/ then it would load the operation, and then ask for the 2nd number.
    Is there a way to make a string out of this number?
    Also is there a easier way to print out to the LCD? Currently I have to have a table of ascii values or I have to print out a table of strings..

    Thanks for the help.
     
  2. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    Calculators store each digit in binary coded decimal (BCD) format. Each digit occupies four bits. This allows data entry and calculation to any number of decimal places.

    In your case, you can simplify the program by storing the values as 16-bit signed integers, limiting the values to -32768 to +32767.

    Going from single digit entry to 16-bit integer is standard decimal-to-binary conversion which is a common exercise in an introductory computer programming course.

    You can do this on the fly or you can store the digits into a string and then do the conversion after.

    After you perform the mathematical operation, the result will be a 16-bit or 32-bit number. You convert this using a binary-to-decimal conversion, again another basic college level exercise. Your result is either a string or it can be sent to the LCD on the fly.

    There are two common methods to convert BCD to ASCII.

    One method is to use a string "0123456789" which is what I prefer.

    The second method is to simply add 48 to the BCD.
     
  3. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Thanks for the quick response,

    here's my plan to store the number.

    I have 4 variables of size byte, LSB LSB2 LSB3 GSB(least sig bit/greatest)
    When the user presses the first number, ie 4, it will be stored in LSB, then if the user presses another number ie. 3, the 4 will be moved to LSB2, and the 3 will be stored in LSB and so on.

    If at anytime the user enters only 2 numbers and presses a operation key, it will then put the operation number(1,2,3, or 4) into the op variable.. then it will ask the user for the next input...(using above method) is there a more efficient way to do this?

    Also when I would like to my number would I have to multiply by ie 1000, 100, 10 and then add them into my 2 byte variable Digit1? Is that the easiest?
     
  4. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    No, that is not the easiest.

    Just to be pedantic, I wouldn't call a digit a number.
    A number can mean any value from minus infinity to positive infinity, including complex numbers.

    0 to 9 are called numerals and I will call this a digit.
    When a digit key is pressed, the key is converted to a binary coded decimal BCD. Store this BCD in a 16-bit variable.
    You don't need to store each BCD digit.
    If another digit key is pressed, multiply the stored value by 10 and add the new BCD.
    Do this until a function key is pressed.
     
  5. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    I've gotten the input methods to work and addition and subtraction working. DIGIT1 is a number with 0-4 digits and so is DIGIT2. It adds and subtracts properly. Now my problem is converting the result (RESULT) back into a string to print out on the LCD. Any tips? (I've still got to implement negative checks for the add.. I've got no clue on how to make this calculator work with signed integers.

    Code ( (Unknown Language)):
    1. ADD:     LDD DIGIT1
    2.          ADDD DIGIT2
    3.          STD RESULT
    4.          LDD RESULT
    5.          
    6. SUB:     LDD DIGIT1
    7.          SUBD DIGIT2
    8.          STD RESULT
    9.          LDD RESULT
    10.          BCS REVISE
    11.          RTS
    12. REVISE:  LDD $FFFF
    13.          SUBD RESULT
    14.          STD RESULT
    15.          LDD RESULT
    16.          COM NFLAGR
     
  6. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    Do one step at a time.
    Set aside signed integers for later.

    Some errors in your code:

    1) Why are you reloading D with LDD RESULT?

    2) Where is the RTS in ADD: and REVISE: ?

    3) There is no need for REVISE. We will deal with negative numbers later.
    Besides, your subtraction from $FFFF is not the correct conversion.
     
  7. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Oh, I was using that to make sure it was adding and subtracting correctly. Here's a patched up version. :)

    Code ( (Unknown Language)):
    1. ADD:     LDD DIGIT1
    2.          ADDD DIGIT2
    3.          STD RESULT
    4.          RTS
    5.          
    6. SUB:     LDD DIGIT1
    7.          SUBD DIGIT2
    8.          STD RESULT
    9.          BCS REVISE
    10.          RTS
    11. REVISE:  LDD $FFFF
    12.          SUBD RESULT
    13.          STD RESULT
    14.          COM NFLAGR
    15.          RTS
     
  8. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Ah alright. Also I'm not sure how I would be able to implement the multiply function. Would I have to split it into 3 variables? Not sure how else is be able to store the final value such as for 99999*99999.
     
  9. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    Let's worry about multiplication function later.
    Let's do the binary-to-BCD conversion.

    The simplest way is to divide by 10.
    Push the remainder on to the stack.
    Count how many times you do this.
    Keep doing this until the numerator is zero.

    Now pop each BCD off the stack, convert to ASCII and send to the display.
     
  10. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Ahh I thought about that. Totally forgot about the remainders. Forgot I had other ways to divide without using idiv :D but pushing it onto stack is a good idea.
     
  11. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Ahh, running into problems..

    Trying to use EDIV(extended divide) and it will work the first time, (gets the remainder and placed into D.) but any subsequent tries will fail. Any clues? Temp is just a temp variable.. LENGTH is initialized as 0.

    Code ( (Unknown Language)):
    1. OUTPRINT:  LDD RESULT
    2.            STD TEMP
    3. B4:        LDD TEMP
    4.            LDX #10
    5.            EDIV
    6.            PSHD
    7.            STY TEMP
    8.            LDAA LENGTH
    9.            INCA    
    10.            STAA LENGTH
    11.            TBNE Y, B4
    12.            RTS
    Fail as in no longer storing the remainder into D. And EDIV no longer does anything..
     
    Last edited: Nov 27, 2012
  12. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Looks like the V is set on the CCR right after EDIV. Now any clue why that would happen?

    "V: Set if the result was > $FFFF; cleared otherwise Undefined after
    division by zero"

    When using FDIV I also get the same error.

    Okay so it works with IDIV. Shouldn't have used FDIV..
     
    Last edited: Nov 27, 2012
  13. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Alright, thanks! Got the printing working, though my code looks ugly. Anyway to optimize this a bit?

    Code ( (Unknown Language)):
    1. OUTPRINT:  LDD RESULT
    2.            STD TEMP
    3.            
    4. B4:        LDD TEMP
    5.            LDX #10
    6.            IDIV
    7.            PSHB
    8.            STX TEMP
    9.            LDAA LENGTH
    10.            INCA    
    11.            STAA LENGTH
    12.            TBNE X, B4
    13.            JSR BOTLEFT
    14.            LDAB LENGTH
    15. B5:        PULA
    16.            JSR OUTPUT
    17.            DBNE B, B5
    18.            RTS
     
  14. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Then again, optimize comes later. I'm still confused on the Multiplication, if you can explain, and thanks for all the help so far.
     
  15. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    This works but.. because i am splitting up the number, it will not detect the 0's in 9999*9999. It will just be a 1 instead of 0001. I went around this with a bunch 4 branch if's but please tell me there's a better way then this.
    Code ( (Unknown Language)):
    1. mult:  
    2.          ldd digit1
    3.          ldy digit2
    4.          emul
    5.          ldx #10000
    6.          ediv
    7.          sty result
    8.          std result2
    9.          jsr outprint
    10.          Ldd result2
    11.          std result
    12.          jsr outprint
    13.  
     
  16. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    Why are you doing:
    Code ( (Unknown Language)):
    1.  
    2.            LDAA LENGTH
    3.            INCA    
    4.            STAA LENGTH
    5.  
    instead of:
    Code ( (Unknown Language)):
    1.  
    2.            INC LENGTH
    3.  
    Where is CLR LENGTH ?

    What does JSR BOTLEFT do?

    OUTPRINT should accept its 16-bit input in D.
     
    Last edited: Nov 27, 2012
  17. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    As you have discovered, EMUL multiplies Y and D and returns a 32-bit result.

    You cannot use EDIV by itself to do the binary-to-BCD conversion because EDIV returns a 16-bit quotient. You have to find a work-around, unless you limit the multiplication to a maximum result of 655359.

    The only solution I can think of off the top of my head is to write your own 32 / 16 division that preserves the 32-bit quotient and do the desired divide-by-10.
     
  18. MrChips

    Moderator

    Oct 2, 2009
    12,414
    3,353
    I now follow what you are saying.

    What you need are two separate output routines, one that outputs 16 bits as BCD with leading zeros suppressed, which is what you have now.

    The second output routine outputs 16-bits (0000-9999) as four digits with leading zeros.
     
  19. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    I made a workaround for multiplication, if the 1st half is greater then 0 and the 2nd half is less then 10 (print 3 0's) if less then 100(print 2 0's) 1000(print 1 0.).. Would division work good like this?

    Number 1 and Number 2 into idiv, take the quotient and place into quotient holder, print decimal, then take the remainder, multiply by 10, then divide by number 2 again, and once more?

    I only need it to be accurate up to 4 digits, so I can just loop this 4 times.

    Is there a better way? Thanks
     
  20. jenxin

    Thread Starter New Member

    Nov 26, 2012
    27
    0
    Also I find that when I press the reset button on my microcontroller, the it only goes back to the START location. It does not reset my RAM and I find I need to actually LDD $0000 and having to store it into all my variables..
     
Loading...