rem*************************************************************************
rem
rem    RPN Calculator for the Psion Series 3a
rem    Version 1.0
rem    Copyright (c) 1992  Jaime Pereira
rem
rem    This program is free software; you can redistribute it and/or modify
rem    it under the terms of the GNU General Public License as published by
rem    the Free Software Foundation; either version 1, or (at your option)
rem    any later version.
rem
rem    This program is distributed in the hope that it will be useful,
rem    but WITHOUT ANY WARRANTY; without even the implied warranty of
rem    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
rem    GNU General Public License for more details.
rem
rem    You should have received a copy of the GNU General Public License
rem    along with this program; if not, write to the Free Software
rem    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
rem
rem    Revision: $Id: rpncalc.opl 1.3 92/12/10 00:32:02 jaime Exp Locker: jaime $
rem    Ported to the Series 3A by Victor Alvarado 10/10/94
rem
rem*************************************************************************

app RPNcalc
	type $1000
	icon "\opl\rpncalc.pic"
enda

proc rpncalc:
	global stack(20), tos%, wid%
	global memory(10)
	global editstr$(30), editpos%, udpos%

	global frmtype%, frmprec&, frmtrig%

	local k%, c$(1), inedit%

	rem
	rem	Relocate the base window so we can see the
	rem	status window.
	rem
	screen 35,9
	gsetwin 416, 160
	statuswin on

	gupdate off

	rem
	rem	Create the display window
	rem
	wid% = gcreate(0, 0, 416, 160, 1)
	guse wid%
	gstyle 16

	rem
	rem	Add the adornments
	rem
	gborder $200
	k% = 0
	gat 0, 140
	while (k% < 416)
		glineby 2, 0
		gmove 2,0
		k% = k% + 4
	endwh	


	rem
	rem	Initialize asorted stuff
	rem
	tos% = 1
	editpos% = 1
	editstr$ = ""
	frmtype% = 1
	frmtrig% = 1
	frmprec& = 12
	udpos% = 0
	showstk:

	rem
	rem	The Loop!
	rem
	do
		rem
		rem	Read from the keyboard
		rem
		guse wid%

		k% = get
		c$ = upper$(chr$(k%))

		rem
		rem	Decode keystroke
		rem

		if k% = 632		rem Psion-X
			break
	
		elseif k% = 13		rem CR
			rem
			rem CR doubles as DUP if editstr is empty
			rem
			if len(editstr$) = 0 and tos% > 1
				push:(stack(tos%-1))
			else
				pushstr: 
			endif
			showstk:

					rem Drop 
		elseif k% = 8 and len(editstr$) = 0   
			pop:
			showstk:

		elseif k% = 27		rem Escape key
			inedit% = 0
			editstr$ = ""
			editpos% = 1
									
		elseif k% = 290		rem Menu
			menuop:

		elseif k% = 291		rem Help
			helpfunc:
		

		elseif loc("+-/*X", c$)	rem Math operation
				binop:(c$)
				showstk:

		elseif k% > 512		rem Special Psion operation	
			psionop:(k%)
			showstk:

					rem Change sign
		elseif c$ = "C"	and len(editstr$)
			chsop:
			showcmd:
			
		elseif loc("CSRIOP", c$) rem General manupulation
			stkop:(c$)
			showstk:

					rem Up and Down arrows
		elseif (k% = 257 or k% = 256) and tos% > 1
			if len(editstr$) and inedit%
				continue
			endif
			inedit% = 0
			if (k% = 256)
				udpos% = udpos% + 1
				if udpos% >= tos%  : udpos% = 0: endif
			else 	
				udpos% = udpos% - 1
				if udpos% < 0 : udpos% = tos% - 1: endif
			endif

			if udpos% = 0
				editstr$ = ""
				editpos% = 1
			else
				editstr$ = gen$(stack(tos% - udpos%), 25)
				editpos% = len(editstr$)+1
			endif
			showcmd:

		else			rem Otherwise to the edit box
			inedit% = 1
			myedit:(k%)
			showcmd:
		endif
		
	until 0
endp

rem*******************************************
rem
rem	My own field editor routine
rem
rem*******************************************

proc myedit:(k%)
	local hstr$(40), tstr$(40)
	local length%
	
	length% = len(editstr$)
	hstr$ = mid$(editstr$, 1, editpos%-1)
	tstr$ = mid$(editstr$, editpos%, length%)

	if k%=8
		length% = len(hstr$)
		if length% > 0
			editstr$ = left$(hstr$, length%-1) + tstr$
			editpos% = editpos% - 1
		endif

	elseif k% = 259
		editpos% = editpos% - 1
		if editpos% < 1
			editpos% = 1
		endif

	elseif k% = 258
		editpos% = editpos% + 1
		if editpos% > length%+1
			editpos% = length%+1
		endif

	elseif (length% > 22)
		return
		
	elseif (k% >= 48 and k% <= 57) or k% = 69 or k% = 101 or k% = 46
		editstr$ = hstr$ + chr$(k%) + tstr$
		editpos% = editpos% + 1
	endif
endp


rem************************************************
rem
rem	Show the stack display
rem
rem************************************************

proc showstk:
	local i%, k%
	local numstr$(25)
	local xo%, yo%

	cursor off  

	xo% = 5: yo% = 3

	gat xo%, yo%
	gfill 356, 136, 1

	if tos% = 1
		gat 220, yo% + 130
		gprint " ** Empty **"
	endif
	i% = 10
	k% = tos% - 1
	while i% > 0 and k% > 0
		gat xo%, i% * 13 + yo%
		gprint fix$(tos% - k%, 0, -2);":"
		if frmtype% = 1
			numstr$ = gen$(stack(k%), 25)
		elseif frmtype% = 2
			numstr$ = fix$(stack(k%), frmprec&, 25)
		elseif frmtype% = 3
			numstr$ = sci$(stack(k%), frmprec&, 25)
		endif
		gprintb numstr$, 320, 1
		i% = i% - 1
		k% = k% - 1
		
	endwh
	showcmd:
endp

rem*******************************************
rem
rem	Show the edit box
rem
rem*******************************************

proc showcmd:
	local x%, cwidth%
	local xo%, yo%

	cwidth% = gtwidth("A")
	
	xo% = 7: yo% = 145

	cursor off  

	gat xo%, yo% - 1
	gfill 356, 12, 1

	gtmode 3
	gat xo%, yo% + 10
	gprint "Rpn:"
	x% = gx + 3
	gat x%, gy
	gprint editstr$
	gat x% + cwidth% * (editpos%-1), gy
	cursor wid%
	gupdate
endp

rem*******************************************
rem
rem	Format window
rem
rem*******************************************

proc formatop:
	dinit "Set RPN Calculator format"
	dchoice frmtype%, "Format:", "General,Fixed,Scientific"
	dlong   frmprec&, "Decimal places:",  0, 12
	dchoice frmtrig%, "Trigonometry units:", "Degrees,Radians"
	if dialog
		showstk:
	endif
endp

rem*******************************************
rem
rem	Push the edit box string into the
rem	stack.
rem
rem*******************************************

proc pushstr:
	onerr errhand
	if len(editstr$)
		push:(eval(editstr$))
	endif
	onerr off
	
	editstr$ = ""
	editpos% = 1
	return 0
	
	errhand::
	onerr off
	problem:(err$(err))
	return 1
endp

rem*******************************************
rem
rem	Push a number in the stack
rem
rem*******************************************

proc push:(data)
	udpos% = 0
	if tos% > 19
		problem:("Stack Full")
	else
		stack(tos%) = data
		tos% = tos% + 1
	endif
endp

rem*******************************************
rem
rem	Pop a number from the stack
rem
rem*******************************************

proc pop:
	udpos% = 0

	if tos% = 1
		problem:("Stack empty")
		return 0
	endif
	tos% = tos% - 1
	return 	stack(tos%)
endp

rem*******************************************
rem
rem	Clear the stack
rem
rem*******************************************

proc clrstk:
	tos% = 1
	showstk:
endp

rem*******************************************
rem
rem	Stack operations
rem
rem*******************************************

proc stkop:(op$)
	local mem, value

	rem
	rem		Put the edit box in the stack
	rem
	if pushstr:
		return
	endif

	if op$ = "S" and tos% > 2	rem Swap
		stack(tos%) = stack(tos%-1)		
		stack(tos%-1) = stack(tos%-2)		
		stack(tos%-2) = stack(tos%)		

	elseif op$ = "R"  and tos% > 3	rem Rot
		stack(tos%) = stack(tos%-1)		
		stack(tos%-1) = stack(tos%-2)		
		stack(tos%-2) = stack(tos%-3)		
		stack(tos%-3) = stack(tos%)

	elseif op$ = "I" and tos% > 2		rem Into memory
		mem = pop:
		if (mem >= 1 and mem <= 10)
			value = stack(tos%-1)
			memory(mem) = value
		else
			problem:("Illegal memory number")
		endif

	elseif op$ = "O" and tos% > 1		rem Out from memory
		mem = pop:
		if (mem >= 1 and mem <= 10)
			push:(memory(mem))
		else
			problem:("Illegal memory number")
		endif

	elseif op$ = "C" and tos% > 1		rem Change sign
		stack(tos%-1) = -stack(tos%-1)		

	elseif op$ = "P"			rem Pi constant
		push:(pi)
	else
		problem:("Stack empty")
	endif
endp

rem*******************************************
rem
rem	Psion key operations
rem
rem*******************************************

proc psionop:(ki%)
	local result, val1, tval, k%
	local op$(1)
	local temp$(30)

	rem
	rem	Put the edit box in the stack
	rem
	temp$ = editstr$
	if pushstr:
		return
	endif

	rem	
	rem 	Get the character withoud the Psion key
	rem 
	k% = ki% and 511
	op$ = upper$(chr$(k%))
	
	rem
	rem Process the menu keys
	rem
	if op$ = "I"
		about:
		return
	elseif op$ = "F"
		formatop:
		return
	elseif op$ = "N"
		clrstk:
		return
	endif

	rem
	rem	All this operations require at least 
	rem	one element
	rem
	if tos% < 2
		problem:("Stack empty")
		return
	endif

	rem
	rem	Get the element and keep an exta copy just
	rem 	in case the operation goes sour
	rem
	val1 = pop:
	tval = val1

	onerr errhand

	rem
	rem	If this is a trig operation convert
	rem	the value to the trig format selected
	rem
	if loc("SCT", op$) and frmtrig% = 1
		val1 = rad(val1)
	endif

	rem
	rem	Decode the operation
	rem
	if op$ = "S"
		result = sin(val1)
	elseif op$ = "C"
		result = cos(val1)
	elseif op$ = "T"
		result = tan(val1)
	elseif op$ = "A"
		result = asin(val1)
	elseif op$ = "K"
		result = acos(val1)
	elseif op$ = "J"
		result = atan(val1)
	elseif op$ = "L"
		result = ln(val1)
	elseif op$ = "G"
		result = log(val1)
	elseif op$ = "E"
		result = exp(val1)
	elseif op$ = "P"
		result = val1 ** 2
	elseif op$ = "Q"
		result = sqr(val1)
	elseif op$ = "V"
		result = 1 / val1
	endif

	rem
	rem	If this is an inverse trig opeation
	rem	conver to the trig format slected
	rem
	if loc("AKJ", op$) and frmtrig% = 1
		result = deg(result)
	endif
	
	onerr off

	rem
	rem	Put the result back in the stack
	rem	and get out
	rem
	push:(result)
	return
	
	rem
	rem	Error handler. Complain and
	rem 	resotre the stack to its previous state.
	rem
	errhand::
	onerr off
	problem:(err$(err))

	if len(temp$)
		editstr$ = temp$
		editpos% = len(temp$)+1
	else
		push:(tval)
	endif
endp

rem*******************************************
rem
rem	Basic arithmetic operations
rem
rem*******************************************

proc binop:(op$)
	local result, val1, val2
	local temp$(30)
	
	rem
	rem		Put the edit box in the stack
	rem
	temp$ = editstr$
	if pushstr:
		return
	endif
	
	if tos% < 3
		problem:("Stack empty")
		return
	endif

	val2 = pop:
	val1 = pop:
	
	onerr errhand
	
	if op$ = "+"
		result = val1 + val2
	elseif op$ = "-"
		result = val1 - val2
	elseif op$ = "*"
		result = val1 * val2
	elseif op$ = "/"
		result = val1 / val2
	elseif op$ = "X"
		result = val1 ** val2
	endif

	onerr off
	
	push:(result)
	return
	
	rem
	rem	Error handler. Complain and
	rem 	resotre the stack to its previous state.
	rem
	errhand::
	onerr off
	problem:(err$(err))

	push:(val1)
	if len(temp$)
		editstr$ = temp$
		editpos% = len(temp$)+1
	else
		push:(val2)
	endif
endp

rem*******************************************
rem
rem	Change Sign operation
rem
rem*******************************************
proc chsop:
	local epos%, echar$(1)
	local base, expon, length%

	epos% = loc(editstr$, "E")
	
	length% = len(editstr$)
 
  onerr errhand

	base = eval(editstr$)

	if epos% = 0
		base  = eval(editstr$)
		expon = 0
	else
		echar$ = mid$(editstr$, epos%, 1)
		base  = eval(left$(editstr$, epos%-1))
		epos% = epos% + 1
		if mid$(editstr$, epos%, 1) = "+"
			epos% = epos% + 1
		endif
		expon = eval(right$(editstr$, length% - epos%+1))
	endif

	if epos% = 0 or editpos% < epos%
		base = -base
	else
		expon = -expon
	endif

	if epos%
		editstr$ = gen$(base, 25) + echar$ + gen$(expon, 25)
	else
		editstr$ = gen$(base,25)
	endif
	if len(editstr$) < length%
		editpos% = editpos% -1
		if editpos% < 1 
			editpos% = 1
		endif
	elseif len(editstr$) > length%
		editpos% = editpos% + 1
	endif
	
	onerr off
	return

	errhand::
	onerr off
	problem:(err$(err))
endp
	
rem*******************************************
rem
rem	Attend to the menu
rem
rem*******************************************
proc menuop:
	local k%, c$(1)

	minit
	mcard "Options", "Format", %f
	mcard "Special", "New stack", %n, "About", -%i, "Exit", %x

	k% = menu
	c$ = chr$(k%)
	
	if k% = 0	
		return
	elseif c$ = "x"
		stop
	elseif c$ = "i"
		about:
	elseif c$ = "f"
		formatop:
	elseif c$ = "n"
		clrstk:
	endif
endp

rem*******************************************
rem
rem	Format and display the about window
rem
rem*******************************************

proc about:
	dinit "RPN 3a Calculator Version 1.0"
	dtext "", "Original program:", 2
	dtext "", "RPN Calculator Ver 1.01 for the Series 3", 2
	dtext "", "Copyright (c) 1992 Jaime Pereira", 2
	dtext "", "jep@world.std.com", 2
	dtext "", "Compuserve 70441,465", 2
	dtext "", "Ported to the Series 3a by:", 2
	dtext "", "Victor Alvarado  vma@mvuts.att.com", 2
	dialog
endp

rem*******************************************
rem
rem	Format and display the help window
rem
rem*******************************************

proc helpfunc:
	global hcmd$(5,7), hkey$(5,2)

	local id%
	
	id% = gcreate(0, 10, 480, 140, 0, 1)
	guse id%
	gxborder 1,$203

	gat 120, 24 : glineby 0, 116
	gat 240, 24 : glineby 0, 116
	gat 360, 24 : glineby 0, 116

	gat 0, 24 : glineby 480, 0

	gat 180, 20: gprint "Keyboard Usage"
	
	rem
	rem	first column
	rem
	hcmd$(1) = "Swap" : hkey$(1) = "S"
	hcmd$(2) = "Rot"  : hkey$(2) = "R"
	hcmd$(3) = "Sto"  : hkey$(3) = "I"
	hcmd$(4) = "Rcl"  : hkey$(4) = "O"
	hcmd$(5) = "Pi"   : hkey$(5) = "P"

	hcolput:(12)

 	rem
	rem	Second column
	rem
	hcmd$(1) = "sin(x)"      : hkey$(1) = chr$(2) + "S"
	hcmd$(2) = "cos(x)"      : hkey$(2) = chr$(2) + "C"
	hcmd$(3) = "tan(x)"      : hkey$(3) = chr$(2) + "T"
	hcmd$(4) = "ln(x)"       : hkey$(4) = chr$(2) + "L"
	hcmd$(5) = "e"+chr$(3)   : hkey$(5) = chr$(2) + "E"

	hcolput:(132)

	rem
	rem	third column
	rem
	hcmd$(1) = "asin(x)"      : hkey$(1) = chr$(2) + "A"
	hcmd$(2) = "acos(x)"      : hkey$(2) = chr$(2) + "K"
	hcmd$(3) = "atan(x)"      : hkey$(3) = chr$(2) + "J"
	hcmd$(4) = "log(x)"       : hkey$(4) = chr$(2) + "G"
	hcmd$(5) = "y"+chr$(3)  : hkey$(5) = "X"

	hcolput:(252)

	rem
	rem	fourth column
	rem
	hcmd$(1) = "1/x"        : hkey$(1) = chr$(2) + "V"
	hcmd$(2) = "sqrt(x)"	: hkey$(2) = chr$(2) + "Q"
	hcmd$(3) = "-x"		: hkey$(3) = "C"
	hcmd$(4) = ""		: hkey$(4) = ""
	hcmd$(5) = ""		: hkey$(5) = ""

	hcolput:(372)

	gvisible on
	
	get
	gclose id%
endp

rem*******************************************
rem
rem	A helper function for the
rem	help display
rem
rem*******************************************

proc hcolput:(xpos%)
	local ypos%, cnt%
	
	ypos% = 48
	cnt% = 1

	while cnt% <= 5
		gat xpos%, ypos%
		gprint hcmd$(cnt%)
		gat xpos% + 78, gy
		gprint hkey$(cnt%)
		ypos% = ypos% + 20
		cnt% = cnt% + 1
	endwh
endp

rem*******************************************
rem
rem	My error handler
rem
rem*******************************************

proc problem:(info$)
	giprint info$
	beep 5, 300
endp

