//
// Desktop RPN calculator
//
include "inc/cmdsys.plh"
include "inc/fpstr.plh"
include "inc/fpu.plh"
include "inc/conio.plh"
const displayWidth = 15
const displayDefFix = 6
const inputLen = displayWidth+1
const ZEROSTR = $3001
//
// Keypad structure
//
struc t_keypad
  byte keychar
  byte xpos
  byte ypos
  byte keystr[8]
end
struc t_keyinput
  byte keyinfo[t_keypad]
  word phandler
end
predef delKey(pkey)#0, cmdKey(pkey)#0, dropKey(pkey)#0, clearKey(pkey)#0
predef digitKey(pkey)#0, pointKey(pkey)#0, opKey(pkey)#0
predef enterKey(pkey)#0, copyKey(pkey)#0, chsKey(pkey)#0, memKey(pkey)#0
predef elemsKey(pkey)#0
//
// Run state
//
byte quit = FALSE
//
// Current input
//
byte inputStr[32] = ""
//
// Display format state
//
byte displayFix = displayDefFix
byte displayInt = displayWidth - displayDefFix - 2
//
// Store/load memory
//
byte memory[10*t_fpureg]
//
// Key values
//
byte[t_keypad] keypad = $08, 0, 0, ""
word          = @delKey
byte[t_keypad]    = $1B, 0, 0, ""
word          = @cmdKey
byte[t_keypad]    = '=', 0, 0, ""
word          = @copyKey
byte[t_keypad]    = 'Z', 0, 0, ""
word          = @clearKey
byte[t_keypad]    = '7',  3, 10, "[7]"
word          = @digitKey
byte[t_keypad]    = '8',  7, 10, "[8]"
word          = @digitKey
byte[t_keypad]    = '9', 11, 10, "[9]"
word          = @digitKey
byte[t_keypad]    = '/', 15, 10, "[/]"
word          = @opKey
byte[t_keypad]    = '4',  3, 12, "[4]"
word          = @digitKey
byte[t_keypad]    = '5',  7, 12, "[5]"
word          = @digitKey
byte[t_keypad]    = '6', 11, 12, "[6]"
word          = @digitKey
byte[t_keypad]    = '*', 15, 12, "[*]"
word          = @opKey
byte[t_keypad]    = '1',  3, 14, "[1]"
word          = @digitKey
byte[t_keypad]    = '2',  7, 14, "[2]"
word          = @digitKey
byte[t_keypad]    = '3', 11, 14, "[3]"
word          = @digitKey
byte[t_keypad]    = '-', 15, 14, "[-]"
word          = @opKey
byte[t_keypad]    = '0',  3, 16, "[0]"
word          = @digitKey
byte[t_keypad]    = '.',  7, 16, "[.]"
word          = @pointKey
byte[t_keypad]    = 'X', 11, 16, "[X]"
word          = @dropKey
byte[t_keypad]    = '+', 15, 16, "[+]"
word          = @opKey
byte[t_keypad]    = $0D,  3, 18, "[ENTER]"
word          = @enterKey
byte[t_keypad]    = '<', 11, 18, "[<]"
word          = @memKey
byte[t_keypad]    = '>', 15, 18, "[>]"
word          = @memKey
byte[t_keypad]    = 'R', 3, 20, "[SQ(R)]"
word          = @opKey
byte[t_keypad]    = 'H', 11, 20, "[C(H)S]"
word          = @chsKey
byte[t_keypad]    = 'C', 22, 14, "[(C)OS]"
word          = @elemsKey
byte[t_keypad]    = 'S', 22, 16, "[(S)IN]"
word          = @elemsKey
byte[t_keypad]    = 'T', 22, 18, "[(T)AN]"
word          = @elemsKey
byte[t_keypad]    = 'A', 22, 20, "[(A)TN]"
word          = @elemsKey
byte[t_keypad]    = '^', 30, 14, "[X(^)Y]"
word          = @elemsKey
byte[t_keypad]    = 'L', 30, 16, "[(L)G2]"
word          = @elemsKey
byte[t_keypad]    = 'E', 30, 18, "[(E)^X]"
word          = @elemsKey
byte[t_keypad]    = 'N', 30, 20, "[L(N)X]"
word          = @elemsKey
byte          = 0
//
// Utility routines
//
def repc(rep, c)#0
  while rep > 0
    putc(c)
    rep--
  loop
end
def rect(x, y, width, height, frame, title)#0
  byte i

  conio:gotoxy(x + 1, y)
  repc(width - 2, frame ?? '#' :: '-')
  conio:gotoxy(x + 1, y + height - 1)
  repc(width - 2, frame ?? '#' :: '-')
  for i = 1 to height - 1
    conio:gotoxy(x, y + i)
    putc(frame ?? '#' :: '!')
    conio:gotoxy(x + width - 1, y + i)
    putc(frame ?? '#' :: '!')
  next
  conio:gotoxy(x, y)
  putc(frame ?? '/' :: '+')
  conio:gotoxy(x + width - 1, y)
  putc(frame ?? '\\' :: '+')
  conio:gotoxy(x, y + height - 1)
  putc(frame ?? '\\' :: '+')
  conio:gotoxy(x + width - 1, y + height - 1)
  putc(frame ?? '/' :: '+')
  if title
    conio:gotoxy(x + (width - ^title) / 2, y)
    puts(title)
  fin
end
def showStack#0
  byte s
  byte strFP[displayWidth+1]

  for s = 0 to 3
    fpu:storStr(@strFP, displayInt, displayFix, FPSTR_FIXED|FPSTR_FLEX, s)
    conio:gotoxy(4, 5 - s)
    //repc(displayWidth - strFP - 1, ' ')
    repc(displayWidth - strFP, ' ')
    puts(@strFP)
  next
end
def showMem#0
  byte m
  byte strFP[displayWidth+1]

  for m = 0 to 9
    ext2str(@memory[m*t_fpureg], @strFP, displayInt, displayFix, FPSTR_FIXED|FPSTR_FLEX)
    conio:gotoxy(23, 2 + m)
    repc(displayWidth - strFP - 1, ' ')
    puts(@strFP)
  next
end
def showInput#0
  conio:gotoxy(2,7)
  repc(17 - inputStr, ' ')
  puts(@inputStr)
end
def showStatus(pstr)#0
  conio:gotoxy(0,23)
  puts(pstr)
end
def clearStatus#0
  conio:gotoxy(0,23)
  repc(39, ' ')
end
def initInput#0
  inputStr   = 0
end
def updateInput#0
  //
  // Lift stack if something input
  //
  if inputStr
    fpu:pushStr(@inputStr)
  fin
  initInput
  showInput
end
def initDisplay#0
  byte i
  word pkeys

  conio:home()
  rect(0, 0, 40, 23, 1, "<RPN Calculator>")
  rect(1, 1, 19, 6, 0, ":Stack:")
  rect(1, 6, 19, 3, 0, 0)
  conio:gotoxy(2, 2); puts("T:")
  conio:gotoxy(2, 3); puts("Z:")
  conio:gotoxy(2, 4); puts("Y:")
  conio:gotoxy(2, 5); puts("X:")
  rect(20, 1, 19, 12, 0, ":Memory:")
  for i = 0 to 9
    conio:gotoxy(21, 2 + i); puti(i); putc(':')
  next
  pkeys = @keypad
  while ^pkeys
    conio:gotoxy(pkeys->xpos, pkeys->ypos)
    puts(pkeys + keystr)
    pkeys = pkeys + t_keyinput
  loop
end
def initState#0
  byte m

  //
  // Init FPU
  //
  fpu:reset()
  //
  // Fill memory
  //
  for m = 2 to 9
    fpu:storExt(@memory[m*t_fpureg], X_REG)
  next
  //
  // Put some useful constants in there
  //
  fpu:constPi()
  fpu:pullExt(@memory[0*t_fpureg])
  fpu:constE()
  fpu:pullExt(@memory[1*t_fpureg])
end
//
// Keypress input handlers
//
def delKey(pkey)#0
  if inputStr
    inputStr--
  fin
  if inputStr:0 == 1 | ('-' << 8) //inputStr == 1 and inputStr.1 == '-'
    inputStr--
  fin
  showInput
end
def dropKey(pkey)#0
  fpu:pullStr(@inputStr, displayInt, displayFix, FPSTR_STRIP|FPSTR_FLOAT|FPSTR_FLEX)
  if inputStr.1 == ' '
    inputStr--
    memcpy(@inputStr.1, @inputStr.2, inputStr)
  fin
  showInput
  showStack
end
def copyKey(pkey)#0
  fpu:storStr(@inputStr, displayInt, displayFix, FPSTR_STRIP|FPSTR_FLOAT|FPSTR_FLEX, X_REG)
  if inputStr.1 == ' '
    inputStr--
    memcpy(@inputStr.1, @inputStr.2, inputStr)
  fin
  showInput
end
def clearKey(pkey)#0
  initInput
  showInput
end
def digitKey(pkey)#0
  if inputStr < inputLen
    if inputStr:0 <> ZEROSTR
      inputStr++
    fin
    inputStr[inputStr] = ^pkey
    showInput
  fin
end
def pointKey(pkey)#0
  byte c

  if !inputStr
    //
    // Start off with '0' if blank input
    //
    inputStr:0 = ZEROSTR
  else
    //
    // Check for existing decimal point
    //
    for c = 1 to inputStr
      if inputStr[c] == '.'
        return
      fin
    next
  fin
  inputStr++
  inputStr[inputStr] = '.'
  showInput
end
def chsKey(pkey)#0
  if inputStr
    if inputStr.1 <> '-'
      memcpy(@inputStr.2, @inputStr.1, inputStr)
      inputStr++
      inputStr.1 = '-'
    else
      inputStr--
      memcpy(@inputStr.1, @inputStr.2, inputStr)
    fin
    showInput
  fin
end
def enterKey(pkey)#0
  fpu:pushStr(@inputStr)
  showStack
  initInput
  showInput
end
def opKey(pkey)#0
  updateInput
  when ^pkey
    is '+'
      fpu:addXY()
      break
    is '-'
      fpu:subXY()
      break
    is '*'
      fpu:mulXY()
      break
    is '/'
      fpu:divXY()
      break
    is 'R'
      fpu:sqrtX()
      break
  wend
  showStack
end
def memKey(pkey)#0
  word r

  showStatus("Press 0-9 for memory register:")
  r = getc - '0'
  clearStatus
  if r >= 0 and r <= 9
    if ^pkey == '<'
      fpu:pushExt(@memory[r*t_fpureg])
      showStack
    else
      fpu:storExt(@memory[r*t_fpureg], X_REG)
      showMem
    fin
  fin
end
def elemsKey(pkey)#0
  updateInput
  when ^pkey
    is 'C'
      fpu:cosX()
      break
    is 'S'
      fpu:sinX()
      break
    is 'T'
      fpu:tanX()
      break
    is 'A'
      fpu:atanX()
      break
    is '^'
      fpu:powXY()
      break
    is 'L'
      fpu:log2X()
      break
    is 'P'
      fpu:powEX()
      break
    is 'N'
      fpu:lnX()
      break
  wend
  showStack
end
//
// Command line handler
//
def cmdKey(pkey)#0
//  word cmdLine

//  showStatus("Command")
//  cmdLine = gets(':'|$80)
  word d

  showStatus("Press 1-9 for fix point digits(Q=Quit):")
  d = toupper(getc) - '0'
  if d >= 1 and d <= 9
    displayFix = d
    displayInt = displayWidth - displayFix - 2
  elsif d == 'Q' - '0'
    quit = TRUE
  fin
  clearStatus
  //
  // Do something
  //
  initDisplay
  showStack
  showMem
  showInput
end
//
// Keypress handler
//
def inputKey#0
  byte inkey
  word pkeys

  conio:echo(ECHO_OFF)
  while not quit
    pkeys = @keypad
    conio:gotoxy(18, 7)
    inkey = toupper(getc)
    while ^pkeys
      if inkey == ^pkeys
        conio:texttype(INVERSE)
        conio:gotoxy(pkeys->xpos, pkeys->ypos)
        puts(pkeys + keystr)
        conio:texttype(NORMAL)
        pkeys=>phandler(pkeys)#0
        conio:gotoxy(pkeys->xpos, pkeys->ypos)
        puts(pkeys + keystr)
        break
      fin
      pkeys = pkeys + t_keyinput
    loop
  loop
  conio:echo(ECHO_ON)
end
initDisplay
initState
initInput
showStack
showMem
showInput
showStatus("Version 0.7")
inputKey
conio:gotoxy(0, 22)
done
