stc_rpncalc/src/main.c

523 lines
11 KiB
C
Raw Normal View History

2019-03-20 05:34:51 +01:00
//
// STC15 RPN calculator
//
#include <stdint.h>
#include "lcd.h"
2019-03-20 08:21:11 +01:00
#include "key.h"
2019-04-01 01:20:28 +02:00
#include "decn/decn.h"
#include "calc.h"
2019-03-20 05:34:51 +01:00
#include "utils.h"
#ifdef DESKTOP
#include <stdio.h>
#include <QSemaphore>
#else
2019-04-03 05:57:28 +02:00
#include "stc15.h"
#endif
2019-03-20 05:34:51 +01:00
2019-03-22 02:03:35 +01:00
#define FOSC 11583000
2019-03-20 05:34:51 +01:00
2019-03-22 02:01:17 +01:00
static const char KEY_MAP[20] = {
'c', '<', 'r', 'm',
'/', '9', '8', '7',
'*', '6', '5', '4',
'-', '3', '2', '1',
'+', '=', '.', '0'
};
#ifdef DESKTOP
QSemaphore KeysAvailable(0);
QSemaphore LcdAvailable(1);
#endif
int8_t NewKeyBuf[4];
2019-03-22 02:01:17 +01:00
volatile uint8_t new_key_write_i;
volatile uint8_t new_key_read_i;
volatile uint8_t NewKeyEmpty;
2019-03-22 02:01:17 +01:00
#define INCR_NEW_KEY_I(i) i = (i + 1) & 3
//#define TRACK_TIME
#ifdef TRACK_TIME
volatile uint8_t SecCount;
#endif
void timer0_isr() SDCC_ISR(1,1)
2019-03-20 05:34:51 +01:00
{
#ifdef TRACK_TIME
2019-03-22 02:01:17 +01:00
static uint8_t count = 0;
static uint8_t min_count = 0, hour_count = 0;
#endif
2019-03-22 02:01:17 +01:00
//scan keyboard
KeyScan();
if (NewKeyPressed != -1){
if (!NewKeyEmpty && (new_key_write_i == new_key_read_i)){
2019-03-22 02:01:17 +01:00
//do not overwrite keymap currently being processed
// INCR_NEW_KEY_I(new_key_write_i);
#ifdef DESKTOP
printf("ERROR: key fifo full\n");
#endif
return;
2019-03-22 02:01:17 +01:00
}
NewKeyBuf[new_key_write_i] = NewKeyPressed;
2019-03-22 02:01:17 +01:00
INCR_NEW_KEY_I(new_key_write_i);
NewKeyEmpty = 0;
2019-03-22 02:01:17 +01:00
}
2019-03-22 02:03:35 +01:00
if (Keys[0] == 8 && Keys[4] == 8){
TURN_OFF();
}
2019-03-22 02:03:35 +01:00
//track time
#ifdef TRACK_TIME
2019-03-22 02:03:35 +01:00
count++;
if (count == 200){
count = 0;
SecCount++;
if (SecCount == 60){
SecCount = 0;
min_count++;
if (min_count == 60){
min_count = 0;
hour_count++;
}
}
}
#endif
2019-03-20 05:34:51 +01:00
}
#ifdef DESKTOP
void Timer0Init(void) { }
static void latch_on(void){ }
#else
2019-03-22 02:03:35 +01:00
// Call timer0_isr() 200/sec: 5 ms period
// Initialize the timer count so that it overflows after 0.01 sec
// THTL = 0x10000 - FOSC / 200 = 0x10000 - 115830 = 7621 = 0x1DC5
void Timer0Init(void)
2019-03-20 05:34:51 +01:00
{
2019-03-22 02:03:35 +01:00
// TMOD = 0; // default: 16-bit auto-reload
AUXR |= 0x80; // use undivided SYSclk for timer0
2019-03-20 05:34:51 +01:00
// Initial values of TL0 and TH0 are stored in hidden reload registers: RL_TL0 and RL_TH0
2019-03-22 02:03:35 +01:00
TL0 = 0xC5; // Initial timer value
TH0 = 0x1D; // Initial timer value
2019-03-20 05:34:51 +01:00
TF0 = 0; // Clear overflow flag
TR0 = 1; // Timer0 start run
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable global interrupt
}
//keep soft power switch latched on
static void latch_on(void)
{
//set (P3_2) as push pull output
P3M1 &= ~(0x4);
P3M0 |= (0x4);
2019-04-01 05:34:13 +02:00
//latch on
P3_2 = 1;
}
#endif //!DESKTOP
2019-03-20 05:34:51 +01:00
2019-04-01 01:20:28 +02:00
__xdata char EntryBuf[MAX_CHARS_PER_LINE + 1];
2019-04-01 07:12:02 +02:00
__xdata uint8_t ExpBuf[2];
__xdata const char VER_STR[32+1] = "STC RPN Calculator v1.09";
2019-04-01 01:20:28 +02:00
enum {
ENTERING_DONE_CLEARED,
ENTERING_DONE,
ENTERING_SIGNIF,
ENTERING_FRAC,
ENTERING_EXP,
ENTERING_EXP_NEG
};
static uint8_t Entry_i = 0;
static uint8_t EnteringExp = ENTERING_DONE;
static uint8_t Exp_i = 0;
static int8_t I_Key;
static inline uint8_t is_entering_done(void){
return EnteringExp <= ENTERING_DONE;
}
2019-10-03 06:33:45 +02:00
static void entering_done(void){
//reset pointers
2019-10-03 05:38:26 +02:00
Entry_i = 0;
Exp_i = 0;
ExpBuf[0] = 0;
ExpBuf[1] = 0;
}
2019-10-03 06:33:45 +02:00
static inline void finish_process_entry(void){
if (!is_entering_done()){
//finish entry
int8_t exponent; //exponent is only 2 digits
exponent = 10*ExpBuf[1] + ExpBuf[0];
if ( EnteringExp == ENTERING_EXP_NEG){
exponent = -exponent;
}
EntryBuf[Entry_i] = '\0';
push_decn(EntryBuf, exponent);
//reset to done
entering_done();
//track entry for RCL and lastX
if (NoLift){
#ifdef DESKTOP
printf("no lift==2\n");
#endif
NoLift++;
}
}
2019-10-03 05:38:26 +02:00
//process cmd
process_cmd(KEY_MAP[I_Key]);
EnteringExp = ENTERING_DONE;
NoLift = 0;
}
#ifdef DESKTOP
static void print_entry_bufs(void){
printf("EntryBuf:~%s~ (%d)\n", EntryBuf, EnteringExp);
printf("ExpBuf:%c%c\n", '0'+ExpBuf[1], '0'+ExpBuf[0]);
}
#endif
2019-04-01 01:20:28 +02:00
//#define DEBUG_UPTIME
2019-03-20 05:34:51 +01:00
/*********************************************/
#ifdef DESKTOP
uint8_t ExitCalcMain;
int calc_main()
#else
2019-03-20 05:34:51 +01:00
int main()
#endif
2019-03-20 05:34:51 +01:00
{
2019-04-01 07:12:02 +02:00
int8_t disp_exponent;
NewKeyEmpty = 1; //initially empty
#ifdef DEBUG_KEYS
uint8_t j = 0;
2019-03-22 02:01:17 +01:00
const uint8_t* keys;
2019-03-20 08:21:11 +01:00
uint8_t key_i;
#endif
#ifdef DEBUG_UPTIME
uint32_t i;
#endif
latch_on();
2019-03-20 05:34:51 +01:00
LCD_Open();
2019-03-20 08:21:11 +01:00
KeyInit();
Timer0Init(); //for reading keyboard
BACKLIGHT_ON(); //turn on led backlight
2019-03-22 02:03:49 +01:00
2019-04-01 07:12:02 +02:00
ExpBuf[0] = 0;
ExpBuf[1] = 0;
2019-10-07 09:19:26 +02:00
LCD_OutString_Initial(VER_STR, 32);
#ifdef DESKTOP
LcdAvailable.release();
#endif
#ifdef DEBUG_UPTIME
2019-03-20 05:34:51 +01:00
i = 0;
#endif
2019-03-20 05:34:51 +01:00
// LOOP
while (1)
{
//turn off?
if (Keys[0] == 8 && Keys[4] == 8){
//check if both shift (mode) and 0 key are held to turn off
//(should work even if rest of calculator is in inifite loop,
// since this is checked within ISR)
TURN_OFF();
}
#ifdef DESKTOP
if (ExitCalcMain){
return 0;
}
#endif
#ifdef DEBUG_UPTIME
LCD_GoTo(0,0);
u32str(i++, Buf, 10);
LCD_OutString(Buf, MAX_CHARS_PER_LINE);
#endif //DEBUG_UPTIME
#ifdef DEBUG_KEYS
2019-03-22 02:01:17 +01:00
//keyboard debug
keys = DebugGetKeys();
2019-03-20 08:21:11 +01:00
for (key_i = 0; key_i < 5; key_i++){
LCD_OutNibble(keys[key_i]);
}
2019-03-22 02:01:17 +01:00
TERMIO_PutChar(',');
2019-03-20 08:21:11 +01:00
//counter
2019-03-22 02:01:17 +01:00
if (SecCount == 0){
LCD_OutString(" ");
} else if (SecCount < 10){
TERMIO_PutChar(' ');
LCD_OutString(u32str(SecCount, Buf, 10));
2019-03-22 02:01:17 +01:00
} else {
LCD_OutString(u32str(SecCount, Buf, 10));
2019-03-22 02:01:17 +01:00
}
#endif //DEBUG_KEYS
2019-03-20 05:34:51 +01:00
///get new key
#ifdef DESKTOP
KeysAvailable.acquire();
#endif
if (!NewKeyEmpty){
I_Key = NewKeyBuf[new_key_read_i];
2019-03-22 02:01:17 +01:00
INCR_NEW_KEY_I(new_key_read_i);
if (new_key_read_i == new_key_write_i){
NewKeyEmpty = 1;
2019-03-22 02:01:17 +01:00
}
#ifdef DESKTOP
printf("\nprocessing key %c (r=%d, w=%d, e=%d)\n",
KEY_MAP[I_Key], new_key_read_i, new_key_write_i, NewKeyEmpty);
printf("entry_i=%d,exp_i=%d\n", Entry_i, Exp_i );
#endif
2019-04-01 01:20:28 +02:00
#ifdef DEBUG_KEYS
2019-03-22 02:01:17 +01:00
LCD_GoTo(1,j);
TERMIO_PutChar(KEY_MAP[i_key]);
j++;
j &= 0x0f;
2019-04-01 01:20:28 +02:00
#endif
//process key
switch(KEY_MAP[I_Key]){
//////////
case '0': {
if (IsShifted){
//off
TURN_OFF();
} else {
if ( EnteringExp >= ENTERING_EXP){
if ( Exp_i == 0){
ExpBuf[0] = 0;
Exp_i = 1;
} else {
ExpBuf[1] = ExpBuf[0];
ExpBuf[0] = 0;
Exp_i++;
if ( Exp_i > 2){
Exp_i = 1;
}
2019-04-01 01:20:28 +02:00
}
} else if (is_entering_done()){
EnteringExp = ENTERING_SIGNIF;
EntryBuf[Entry_i] = KEY_MAP[I_Key];
//do not increment entry_i from 0, until first non-0 entry
} else if ( Entry_i != 0 && Entry_i < MAX_CHARS_PER_LINE - 1 + 1){
EntryBuf[Entry_i] = KEY_MAP[I_Key];
Entry_i++;
}
}
} break;
//////////
case '1': //fallthrough
case '2': //fallthrough
case '3': //fallthrough
case '4': //fallthrough
case '5': //fallthrough
case '6': //fallthrough
case '7': //fallthrough
case '8': //fallthrough
case '9': {
if (IsShifted){
finish_process_entry();
} else if ( EnteringExp >= ENTERING_EXP){
if ( Exp_i == 0){
ExpBuf[0] = KEY_MAP[I_Key] - '0';
Exp_i = 1;
} else {
ExpBuf[1] = ExpBuf[0];
ExpBuf[0] = KEY_MAP[I_Key] - '0';
Exp_i++;
if ( Exp_i > 2){
Exp_i = 1;
2019-04-01 01:20:28 +02:00
}
}
} else if (is_entering_done()){
EnteringExp = ENTERING_SIGNIF;
EntryBuf[Entry_i] = KEY_MAP[I_Key];
Entry_i++;
} else if ( Entry_i < MAX_CHARS_PER_LINE - 1 + 1){
EntryBuf[Entry_i] = KEY_MAP[I_Key];
Entry_i++;
}
} break;
//////////
case '.': {
if (IsShifted){
//STO
finish_process_entry();
} else {
if (is_entering_done()){
EntryBuf[Entry_i++] = '0';
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp == ENTERING_SIGNIF){
if ( Entry_i == 0){
EntryBuf[Entry_i++] = '0';
}
EntryBuf[Entry_i++] = '.';
EnteringExp = ENTERING_FRAC;
} else if ( EnteringExp <= ENTERING_EXP) {
EnteringExp++;
} else { //entering_exp == ENTERING_EXP_NEG
EnteringExp = ENTERING_EXP;
}
}
} break;
//////////
case '=': {
if (IsShifted){ //RCL
finish_process_entry();
} else { //Enter
//track stack lift
finish_process_entry();
NoLift = 1;
}
} break;
//////////
case 'c': {
if (IsShifted || is_entering_done()){
//clear
2019-10-03 05:38:26 +02:00
IsShifted = 0;
NoLift = 1;
2019-10-03 05:38:26 +02:00
entering_done();
EnteringExp = ENTERING_DONE_CLEARED;
//do not increment entry_i from 0, until first non-0 entry
process_cmd(KEY_MAP[I_Key]);
} else if ( EnteringExp >= ENTERING_EXP){
//go back to digit entry
EnteringExp--;
Exp_i = 0;
ExpBuf[0] = 0;
ExpBuf[1] = 0;
} else if ( Entry_i > 0){
//backspace
Entry_i--;
if (EntryBuf[Entry_i] == '.'){
EnteringExp = ENTERING_SIGNIF;
}
}
} break;
//////////
case '+': //fallthrough
case '*': //fallthrough
case '-': //fallthrough
case '/': //fallthrough
case '<': //fallthrough //use as +/-
case 'r': { //use as swap
finish_process_entry();
} break;
//////////
default: process_cmd(KEY_MAP[I_Key]);
//////////
} //switch(KEY_MAP[i_key])
} else { //else for (if found new key pressed)
//no new key pressed
continue;
}
2019-04-01 01:20:28 +02:00
LCD_GoTo(0,0);
//display y register on first line
if (is_entering_done() || NoLift){
disp_exponent = decn_to_str(get_y());
} else {
//display x on 1st line, entered number on 2nd line
disp_exponent = decn_to_str(get_x());
}
if (disp_exponent == 0){
LCD_OutString(Buf, MAX_CHARS_PER_LINE);
} else { //have exponent to display
LCD_OutString(Buf, MAX_CHARS_PER_LINE - 3);
if (disp_exponent < 0){
TERMIO_PutChar(CGRAM_EXP_NEG);
disp_exponent = -disp_exponent;
} else {
TERMIO_PutChar(CGRAM_EXP);
}
TERMIO_PutChar((disp_exponent / 10) + '0');
TERMIO_PutChar((disp_exponent % 10) + '0');
}
2019-04-01 01:20:28 +02:00
//print X
LCD_ClearToEnd(0); //go to 2nd row
#ifdef DESKTOP
print_lcd();
printf("entry_i=%d,exp_i=%d\n", Entry_i, Exp_i );
print_entry_bufs();
#endif
if ( EnteringExp == ENTERING_DONE){ //does not cover cleared case
disp_exponent = decn_to_str(get_x());
2019-04-01 07:12:02 +02:00
if (disp_exponent == 0){
LCD_OutString(Buf, MAX_CHARS_PER_LINE);
} else { //have exponent to display
LCD_OutString(Buf, MAX_CHARS_PER_LINE - 3);
if (disp_exponent < 0){
2019-04-01 08:38:17 +02:00
TERMIO_PutChar(CGRAM_EXP_NEG);
2019-04-01 07:12:02 +02:00
disp_exponent = -disp_exponent;
} else {
2019-04-01 08:38:17 +02:00
TERMIO_PutChar(CGRAM_EXP);
2019-04-01 07:12:02 +02:00
}
TERMIO_PutChar((disp_exponent / 10) + '0');
TERMIO_PutChar((disp_exponent % 10) + '0');
}
} else if ( Entry_i == 0){
2019-04-01 01:20:28 +02:00
TERMIO_PutChar('0');
} else if ( EnteringExp < ENTERING_EXP){
2019-04-01 01:20:28 +02:00
uint8_t idx;
for (idx = 0; idx < Entry_i && idx < MAX_CHARS_PER_LINE; idx++){
2019-04-01 01:20:28 +02:00
TERMIO_PutChar(EntryBuf[idx]);
}
} else {
uint8_t idx;
//print significand
for (idx = 0; idx < Entry_i && idx < MAX_CHARS_PER_LINE - 3; idx++){
2019-04-01 01:20:28 +02:00
TERMIO_PutChar(EntryBuf[idx]);
}
//go to exponent
if (idx < MAX_CHARS_PER_LINE - 3){
//clear until exponent
for ( ; idx < MAX_CHARS_PER_LINE - 3; idx++){
TERMIO_PutChar(' ');
2019-03-22 02:01:17 +01:00
}
2019-04-01 01:20:28 +02:00
} else {
LCD_GoTo(1, MAX_CHARS_PER_LINE - 3);
2019-03-22 02:01:17 +01:00
}
2019-04-01 01:20:28 +02:00
//print exponent sign
if ( EnteringExp == ENTERING_EXP_NEG){
2019-04-01 08:38:17 +02:00
TERMIO_PutChar(CGRAM_EXP_NEG);
2019-04-01 01:20:28 +02:00
} else {
2019-04-01 08:38:17 +02:00
TERMIO_PutChar(CGRAM_EXP);
2019-04-01 01:20:28 +02:00
}
//print exp
TERMIO_PutChar(ExpBuf[1] + '0');
TERMIO_PutChar(ExpBuf[0] + '0');
2019-03-22 02:01:17 +01:00
}
2019-04-01 01:20:28 +02:00
LCD_ClearToEnd(1);
//print shifted status
if (IsShifted){
TERMIO_PutChar('^');
}
#ifdef DESKTOP
print_lcd();
printf("entry_i=%d,exp_i=%d\n", Entry_i, Exp_i );
print_entry_bufs();
LcdAvailable.release();
#endif
//turn backlight back on
BACKLIGHT_ON();
2019-04-01 01:20:28 +02:00
} //while (1)
2019-03-20 05:34:51 +01:00
}
/* ------------------------------------------------------------------------- */