# 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.

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,636
3,455
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,636
3,455
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)):
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,636
3,455
Do one step at a time.
Set aside signed integers for later.

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)):
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,636
3,455
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 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,636
3,455
Why are you doing:
Code ( (Unknown Language)):
1.
2.            LDAA LENGTH
3.            INCA
4.            STAA LENGTH
5.
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,636
3,455
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,636
3,455
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..