HCS12 Calculator

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.
 

MrChips

Joined Oct 2, 2009
30,806
This calculator is supposed to support 4 digit operations,
ie.. 9999 (+,-,x,/) 9999.
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.

How would I retrieve the numbers and store it into 1 variable?
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.

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

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

Thread Starter

jenxin

Joined Nov 26, 2012
27
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?
 

MrChips

Joined Oct 2, 2009
30,806
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.
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.

Rich (BB code):
ADD:     LDD DIGIT1
         ADDD DIGIT2
         STD RESULT
         LDD RESULT
         
SUB:     LDD DIGIT1
         SUBD DIGIT2
         STD RESULT
         LDD RESULT
         BCS REVISE
         RTS
REVISE:  LDD $FFFF
         SUBD RESULT
         STD RESULT
         LDD RESULT
         COM NFLAGR
 

MrChips

Joined Oct 2, 2009
30,806
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.
 

Thread Starter

jenxin

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

Rich (BB code):
ADD:     LDD DIGIT1
         ADDD DIGIT2
         STD RESULT
         RTS
         
SUB:     LDD DIGIT1
         SUBD DIGIT2
         STD RESULT
         BCS REVISE
         RTS
REVISE:  LDD $FFFF
         SUBD RESULT
         STD RESULT
         COM NFLAGR
         RTS
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.
 

MrChips

Joined Oct 2, 2009
30,806
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.
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.

Rich (BB code):
OUTPRINT:  LDD RESULT
           STD TEMP
B4:        LDD TEMP
           LDX #10
           EDIV
           PSHD
           STY TEMP
           LDAA LENGTH
           INCA    
           STAA LENGTH
           TBNE Y, B4
           RTS
Fail as in no longer storing the remainder into D. And EDIV no longer does anything..
 
Last edited:

Thread Starter

jenxin

Joined Nov 26, 2012
27
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:

Thread Starter

jenxin

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

Rich (BB code):
OUTPRINT:  LDD RESULT
           STD TEMP
           
B4:        LDD TEMP
           LDX #10
           IDIV
           PSHB
           STX TEMP
           LDAA LENGTH
           INCA    
           STAA LENGTH
           TBNE X, B4
           JSR BOTLEFT
           LDAB LENGTH
B5:        PULA
           JSR OUTPUT
           DBNE B, B5
           RTS
 

Thread Starter

jenxin

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

Thread Starter

jenxin

Joined Nov 26, 2012
27
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.
Rich (BB code):
mult:  
         ldd digit1
         ldy digit2
         emul
         ldx #10000
         ediv
         sty result
         std result2
         jsr outprint
         Ldd result2
         std result
         jsr outprint
 

MrChips

Joined Oct 2, 2009
30,806
Alright, thanks! Got the printing working, though my code looks ugly. Anyway to optimize this a bit?

Rich (BB code):
OUTPRINT:  LDD RESULT
           STD TEMP
           
B4:        LDD TEMP
           LDX #10
           IDIV
           PSHB
           STX TEMP
           LDAA LENGTH
           INCA    
           STAA LENGTH
           TBNE X, B4
           JSR BOTLEFT
           LDAB LENGTH
B5:        PULA
           JSR OUTPUT
           DBNE B, B5
           RTS
Why are you doing:
Rich (BB code):
           LDAA LENGTH
           INCA    
           STAA LENGTH
instead of:
Rich (BB code):
           INC LENGTH
Where is CLR LENGTH ?

What does JSR BOTLEFT do?

OUTPRINT should accept its 16-bit input in D.
 
Last edited:

MrChips

Joined Oct 2, 2009
30,806
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.
Rich (BB code):
mult:  
         ldd digit1
         ldy digit2
         emul
         ldx #10000
         ediv
         sty result
         std result2
         jsr outprint
         Ldd result2
         std result
         jsr outprint
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.
 

MrChips

Joined Oct 2, 2009
30,806
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 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.
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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
 

Thread Starter

jenxin

Joined Nov 26, 2012
27
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..
 
Top