The Display Register or X Register shows the result after the execution of a Program.
In the Python code, the X Register corresponds to the variable "x".
The Display History shows the values of the Display Register each time the Program encounters the "2nd Pause" key.
The real Calculator would pause for a second while the Emulator keeps track of the X Register.
In the Python code, the Display History is stored in the "regx" list.
The Display Register is editable. Enter a list of numbers separated by spaces to change the Data. Then click elsewhere to see the effect of the change in the Program. Or press the Enter key to run the Program with the new Data. For example, suppose the Data begins with 10 STO 1 20 STO 2 30 STO 3 and you want to replace the first two with 11 STO 1 22 STO 2. You can either edit the program directly or enter 11 22 in the Display Register. This is handy especially with games.
# | History |
---|---|
Current. | 0 |
Program Instructions/Keys
# Lunar Lander
# You are in a Lunar Lander at an altitude of 5000 m with 130 L left of gas.
# Your job is to touch the ground at less than 5 m/s by adjusting the consumption every 10 sec.
# Enter a number in the Display Register then press Return.
# The calculator will display the current speed and height in one number, ex. -96.4020.
# That means you are descending at 96 m/s at an altitude of 4020 m.
# Check how much gas you have left in the History.
# You will know you have hit the ground when you see a number ending in .0000.
# You landed safely if the speed is under 5 m/s, otherwise you crashed!
# Lastly, if you see 9999.9999, that means you are trying to consume more than you have left...
# Good luck!
# Source: "Ordinateur de poche n°8.pdf#page=52", p. 54
import re
from math import *
from goto import with_goto # pip install goto-statement
@with_goto
def main():
global ee, mem, rounding, stack, unit, x
label .label_rst
# Data Input
# Consumption (n)
x = 10 # 10
mem[4] = x # STO 4 (32 0)
# Game on?
x = mem[5] # RCL 5 (33 0)
if x != mem[7]: # INV 2nd x=t (- 66)
# Descending
goto .label_0 # GTO 0 (51 0)
# New game
# Gas (G)
x = 130 # 130
mem[0] = x # STO 0 (32 0)
# Altitude (H)
x = 5000 # 5000
mem[1] = x # STO 1 (32 0)
# Speed (V)
x = 100 # 100
x = -x # +/- (84)
mem[2] = x # STO 2 (32 0)
# Game on
x = 1 # 1
mem[5] = x # STO 5 (32 0)
rounding = 4 # 2nd Fix 4 (48)
# Data Processing (58 steps excl. Add-ons)
label .label_0 # 2nd Lbl 0 (86 0)
# Check gas left (Add-on)
sbr_2() # SBR 2 (61 0)
# n
x = mem[4] # RCL 4 (33 0)
# G -= n
mem[0] -= x # INV SUM 0 (- 34 0)
x = mem[0] # RCL 0 (33 0)
# (Add-on)
rounding = None # INV 2nd Fix (- 48)
regx.append(fix(x)) # 2nd Pause (36)
rounding = 4 # 2nd Fix 4 (48)
# 10V
x = 10 # 10
stack.append(x) # X (55)
x = mem[2] # RCL 2 (33 0)
# ΔV = 2n - 16
y = stack.pop() # + (75)
x = y * x
stack.append(x)
# ( (43)
x = 2 # 2
stack.append(x) # X (55)
x = mem[4] # RCL 4 (33 0)
y = stack.pop() # - (65)
x = y * x
stack.append(x)
x = 16 # 16
y = stack.pop() # ) (44)
x = y - x
mem[3] = x # STO 3 (32 0)
# V += Δv
mem[2] += x # SUM 2 (34 0)
# ΔH = 10V + 5ΔV
stack.append(x) # X (55)
x = 5 # 5
y = stack.pop() # = (85)
x = y * x
y = stack.pop()
x = y + x
# H += ΔH
mem[1] += x # SUM 1 (34 0)
# V.H = H/1000 + Int(|V|), ex. 96.4020
x = mem[1] # RCL 1 (33 0)
stack.append(x) # : (45)
x = 4 # 4
x = pow(10, x) # INV 2nd log (- 18)
y = stack.pop() # + (75)
x = y / x
stack.append(x)
x = mem[2] # RCL 2 (33 0)
x = abs(x) # 2nd |x| (40)
x = int(x) # 2nd Int (49)
y = stack.pop() # = (85)
x = y + x
mem[6] = x # STO 6 (32 0)
# Fix V.H sign (Add-on)
sbr_3() # SBR 3 (61 0)
# H < 0?
x = mem[1] # RCL 1 (33 0)
if x < mem[7]: # INV 2nd x>=t (- 76)
# Game over!
goto .label_4 # GTO 4 (51 0)
# H != 0?
if x != mem[7]: # INV 2nd x=t (- 66)
# Descending
goto .label_1 # GTO 1 (51 0)
label .label_4 # 2nd Lbl 4 (86 0)
x = 0 # 0
mem[5] = x # STO 5 (32 0)
# V = √(V² - H X ΔV)/5
x = mem[2] # RCL 2 (33 0)
x *= x # x^2 (23)
stack.append(x) # - (65)
x = mem[1] # RCL 1 (33 0)
stack.append(x) # X (55)
x = mem[3] # RCL 3 (33 0)
y = stack.pop() # : (45)
x = y * x
stack.append(x)
x = 5 # 5
y = stack.pop() # = (85)
x = y / x
y = stack.pop()
x = y - x
x = sqrt(x) # Vx (24)
x = int(x) # 2nd Int (49)
mem[6] = x # STO 6 (32 0)
sbr_3() # SBR 3 (61 0)
regx.append(fix(x)) # 2nd Pause (36)
raise UserWarning('R/S') # R/S (81)
label .label_1 # 2nd Lbl 1 (86 0)
x = mem[6] # RCL 6 (33 0)
regx.append(fix(x)) # 2nd Pause (36)
raise UserWarning('R/S') # R/S (81)
label .end
# Subroutine: Check gas left (Add-on)
@with_goto
def sbr_2():
global ee, mem, rounding, stack, unit, x
label .label_2 # 2nd Lbl 2 (86 0)
x = mem[0] # RCL 0 (33 0)
stack.append(x) # - (65)
x = mem[4] # RCL 4 (33 0)
y = stack.pop() # = (85)
x = y - x
# G - n >= 0?
if x >= mem[7]: # 2nd x>=t (76)
goto .end # INV SBR (- 61)
# Not enough gas
x = 9999.9999 # 9999.9999
raise UserWarning('R/S') # R/S (81)
label .end
# Subroutine: Fix V.H sign (Add-on)
@with_goto
def sbr_3():
global ee, mem, rounding, stack, unit, x
label .label_3 # 2nd Lbl 3 (86 0)
# V >= 0?
x = mem[2] # RCL 2 (33 0)
if x >= mem[7]: # 2nd x>=t (76)
# Going up!
goto .end # INV SBR (- 61)
# V.H = -V.H
x = mem[6] # RCL 6 (33 0)
x = -x # +/- (84)
mem[6] = x # STO 6 (32 0)
x = mem[6] # RCL 6 (33 0)
label .end # INV SBR (- 61)
# Note that this program would not fit into a real calculator even excluding Add-ons!
# Internal functions
def degrees2dms(degrees):
"""Convert decimal degrees to degrees, minutes, seconds."""
degrees = float(degrees)
is_positive = degrees >= 0
if not is_positive:
degrees = -degrees
minutes, seconds = divmod(degrees * 3600, 60)
degrees, minutes = divmod(minutes, 60)
# Internal mantissa has 11 digits on TI-57
seconds = f"{seconds:016.13f}".replace(".", "")
dms = f"{int(degrees)}.{int(minutes):02}{seconds}".rstrip("0")
if not is_positive:
dms = "-" + dms
return dms
def dms2degrees(dms):
"""Convert degrees, minutes, seconds to decimal degrees."""
match = re.fullmatch(
r"(?P[+-])?(?P[0-9]+)\.?(?P[0-9]{1,2})?(?P[0-9]{1,2})?(?P[0-9]*)",
str(dms),
)
if match is None:
raise Exception(f"Calculator error: invalid DMS angle {dms}")
angle = match.groupdict()
degrees = float(angle["degrees"])
if angle["minutes"]:
if len(angle["minutes"]) == 1:
angle["minutes"] += "0"
degrees += float(angle["minutes"]) / 60
if angle["seconds"]:
if len(angle["seconds"]) == 1:
angle["seconds"] += "0"
degrees += float(angle["seconds"]) / 3600
if angle["remainder"]:
degrees += float("0." + angle["remainder"]) / 3600
if angle["sign"] == "-":
degrees = -degrees
return degrees
def fix(number):
"""Round a number and/or convert it to the scientific notation."""
global ee, rounding
if ee:
# The scientific notation is on
if rounding is None:
# Default to the calculator 7 digit precision
number = f"{number:.7E}"
# Remove trailing 0s
number = re.sub("0+E", "E", number)
else:
# Round as exponent with the given precision
number = f"{number:.{rounding}E}"
elif rounding is not None:
# Rounding is on
number = f"{number:.{rounding}f}"
else:
# No rounding
number = str(number)
return number
def grd2rad(gradian):
"""Convert gradian to radian."""
return (gradian / 200) * pi
def get_calculator_state():
"""Return the calculator sate."""
global ee, error, mem, regx, rounding, stack, unit, x
return {
"ee": ee,
"error": error,
"fixed_x": fix(x),
"mem": mem,
"regx": regx,
"rounding": rounding,
"stack": stack,
"unit": unit,
"x": x,
}
def init_calculator_state(state={}):
"""Initialize the calculator state.
Must be called before running the program, see run().
ee -- Scientific notation (EE)
error -- Syntax error etc.
mem -- Memories (STO)
regx -- History of values displayed before a pause (2nd pause)
rounding -- Number of digit after the decimal point (2nd Fix)
stack -- Internal memory stack used for computing nested operations
unit -- Angle unit (2nd Deg, 2nd Rad, 2nd Grad)
x -- Display register or X register
"""
global ee, error, mem, regx, rounding, stack, unit, x
ee = state["ee"] if "ee" in state else False
error = state["error"] if "error" in state else None
mem = state["mem"] if "mem" in state else [0 for i in range(8)]
regx = state["regx"] if "regx" in state else []
rounding = state["rounding"] if "rounding" in state else None
stack = state["stack"] if "stack" in state else []
unit = state["unit"] if "unit" in state else "Deg"
x = state["x"] if "x" in state else 0
def rad2grd(radian):
"""Convert radian to gradian."""
return (radian / pi) * 200
def rad2unit(number):
"""Convert radian to degree or gradian."""
global unit
number = float(number)
if unit == "Deg":
number = degrees(number)
elif unit == "Grd":
number = rad2grd(number)
return number
def run_program():
"""Run the program (the entry point).
The calculator must be initialized beforehand, see init_calculator_state().
"""
global error, x
try:
main()
except ZeroDivisionError:
x = 9.9999999
except UserWarning: # R/S key
pass
except Exception as e:
error = str(e)
def unit2rad(number):
"""Convert degree or gradian to radian."""
global unit
number = float(number)
if unit == "Deg":
number = radians(number)
elif unit == "Grd":
number = grd2rad(number)
return number
# Program execution, uncomment to run the file on its own.
# init_calculator_state()
# run_program()
# calculator_state = get_calculator_state()
# print(calculator_state)