Clone of old TI Programmer's calculator. Written in XB with TiCodEd. Codex helped too.

TI Calculator

Available in the downloads section!

  ! TI-PROG - TI PROGRAMMER FOR TI-99/4A EXTENDED BASIC
  ! KEYBOARD-DRIVEN LED PROGRAMMER CALCULATOR, 2026
  ! ORIGINAL TI PROGRAMMER FUNCTIONS: DEC/OCT/HEX, MEM,
  ! CONSTANT, PARENTHESES, 1'S COMPLEMENT, SHF, OR, AND, XOR.
  ! BY BRAD SNYDER AND CODEX 8/26/26
  DIM V(15):: DIM O$(15)::VR$="V1.02"
  ON BREAK NEXT
  B=10 :: GOSUB SET_BASE_PARAMS
  ANS=0 :: MEM=0 :: MH=0 :: E$="" :: NE=0 :: ER=0 :: BAD=0
  VT=-1 :: OT=-1 :: AFTER=0 :: LAST$="" :: LVAL=0
  KRDY=0 :: KOP$="" :: KVAL=0 :: KP=0 :: MSG$="READY"
  SDIRTY=0 :: MBAD=-1 :: BFLAG=0
  KSND=1 :: DBEEP=1 :: GOSUB CONFIG_MENU
  GOSUB DRAW_CALC
  GOSUB DRAW_LED_AND_STATUS

KEY_LOOP:
  CALL KEY(3,KEY,ST)
  IF ST<>1 THEN KEY_LOOP
  GOSUB MAIN_ENTRY
  GOTO KEY_LOOP

  ! ----- KEY DISPATCH -----
  ! 0-9
KEY_DISPATCH:
  IF KEY<48 THEN DISPATCH_HEX_DIGIT
  IF KEY>57 THEN DISPATCH_HEX_DIGIT
  D$=CHR$(KEY):: GOSUB ADD_DIGIT :: RETURN 

  ! A-F, UPPER CASE (CALL KEY(3,...) RETURNS UPPER CASE)
DISPATCH_HEX_DIGIT:
  IF KEY<65 THEN DISPATCH_COMMAND
  IF KEY>70 THEN DISPATCH_COMMAND
  D$=CHR$(KEY):: GOSUB ADD_DIGIT :: RETURN 

  ! OPERATORS AND PUNCTUATION
DISPATCH_COMMAND:
  IF KEY=43 THEN P$="+" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=45 THEN P$="-" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=42 THEN P$="*" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=47 THEN P$="/" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=61 THEN GOSUB EQUALS :: RETURN 
  IF KEY=13 THEN GOSUB EQUALS :: RETURN 
  IF KEY=40 THEN GOSUB OPEN_PAREN :: RETURN 
  IF KEY=41 THEN GOSUB CLOSE_PAREN :: RETURN 
  IF KEY=46 THEN GOSUB DECIMAL_POINT :: RETURN 
  IF KEY=8 THEN GOSUB CLEAR_ENTRY :: RETURN 
  IF KEY=15 THEN CALL CLEAR ::PRINT "* TI PROGRAMMER ";VR$;" *":: STOP
  ! Z=DEC, H=HEX, O=OCT, L=CLEAR
  IF KEY=90 THEN NB=10 :: GOSUB CHANGE_BASE :: RETURN 
  IF KEY=72 THEN NB=16 :: GOSUB CHANGE_BASE :: RETURN 
  IF KEY=79 THEN NB=8 :: GOSUB CHANGE_BASE :: RETURN 
  IF KEY=76 THEN GOSUB ALL_CLEAR :: RETURN 
  ! S=STO, R=RCL, U=SUM, K=CONSTANT
  IF KEY=83 THEN GOSUB STORE_MEMORY :: RETURN 
  IF KEY=82 THEN GOSUB RECALL_MEMORY :: RETURN 
  IF KEY=85 THEN GOSUB SUM_MEMORY :: RETURN 
  IF KEY=75 THEN GOSUB SET_CONSTANT :: RETURN 
  ! Q=SHF, W=OR, I=AND, Y=XOR, V=1'S C, T=+/-
  IF KEY=81 THEN P$="S" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=87 THEN P$="O" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=73 THEN P$="A" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=89 THEN P$="X" :: GOSUB ACCEPT_OPERATOR :: RETURN 
  IF KEY=86 THEN GOSUB ONES_COMPLEMENT :: RETURN 
  IF KEY=84 THEN GOSUB TOGGLE_SIGN :: RETURN 
  BAD=1 :: MSG$="KEY?" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ENTRY POINT USED BY THE MAIN LOOP
  ! SHORT KEYPRESS CLICK
MAIN_ENTRY:
  IF KSND=0 THEN SKIP_KEY_SOUND
  CALL SOUND(40,-7,10)

SKIP_KEY_SOUND:
  BAD=0::GOTO KEY_DISPATCH

  ! ----- ADD ONE DIGIT IN D$ -----
ADD_DIGIT:

  IF ER=0 THEN CHECK_DEC_DIGIT
  GOSUB ALL_CLEAR
  ER=0

CHECK_DEC_DIGIT:
  IF B=16 THEN CHECK_OCT_DIGIT
  IF ASC(D$)<58 THEN CHECK_OCT_DIGIT
  BAD=1 :: MSG$="DEC ONLY" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

CHECK_OCT_DIGIT:
  IF B<>8 THEN PREPARE_ENTRY
  IF ASC(D$)<56 THEN PREPARE_ENTRY
  BAD=1 :: MSG$="OCT 0-7" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

PREPARE_ENTRY:
  IF NE=1 THEN CHECK_DIGIT_LENGTH
  IF AFTER=0 THEN INIT_ENTRY
  VT=-1 :: OT=-1 :: AFTER=0
  LAST$=""

INIT_ENTRY:
  E$="" :: NE=1 :: SDIRTY=1

CHECK_DIGIT_LENGTH:
  IF LEN(E$)<8 THEN APPEND_DIGIT
  BAD=1 :: MSG$="8 DIGITS" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

APPEND_DIGIT:
  E$=E$&D$ :: MSG$="ENTRY"
  IF OT<0 THEN REDRAW_DIGIT
  IF O$(OT)="S" THEN MSG$="SHF: T=RIGHT"

REDRAW_DIGIT:
  IF MBAD<>0 THEN REDRAW_DIGIT_STATUS
  IF SDIRTY=0 THEN REDRAW_DIGIT_FAST

REDRAW_DIGIT_STATUS:
  BFLAG=1 :: GOSUB DRAW_LED_AND_STATUS :: BFLAG=0 :: SDIRTY=0 :: RETURN

REDRAW_DIGIT_FAST:
  BFLAG=1 :: GOSUB DRAW_LED_VALUE :: BFLAG=0 :: RETURN

  ! ----- ACCEPT A BINARY OPERATOR IN P$ -----
ACCEPT_OPERATOR:
  IF ER=0 THEN CHECK_OPERATOR_MODE
  GOSUB ALL_CLEAR
  ER=0

CHECK_OPERATOR_MODE:
  IF B<>10 THEN PREPARE_OPERATOR
  IF P$="S" THEN BAD=1 :: MSG$="SHF OCT/HEX" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF P$="O" THEN BAD=1 :: MSG$="OR OCT/HEX" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF P$="A" THEN BAD=1 :: MSG$="AND OCT/HEX" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF P$="X" THEN BAD=1 :: MSG$="XOR OCT/HEX" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

PREPARE_OPERATOR:
  IF NE=0 THEN CHECK_OPERATOR_REPLACE

PREPARE_OPERATOR_VALUE:
  IF NE=1 THEN GOSUB PUSH_ENTRY_VALUE
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF VT>-1 THEN SET_PENDING_PRECEDENCE
  VT=0 :: V(VT)=ANS
  KP=AFTER
  AFTER=0
  GOTO SET_PENDING_PRECEDENCE

  ! REPLACE AN OPERATOR WHEN ITS RIGHT-HAND VALUE HAS NOT BEEN ENTERED
CHECK_OPERATOR_REPLACE:
  IF OT<0 THEN PREPARE_OPERATOR_VALUE
  IF O$(OT)="(" THEN PREPARE_OPERATOR_VALUE
  O$(OT)=P$ :: GOSUB SET_OPERATOR_STATUS :: AFTER=0
  GOSUB DRAW_LED_AND_STATUS :: RETURN

  ! REDUCE HIGHER-OR-EQUAL PRIORITY OPERATORS
SET_PENDING_PRECEDENCE:
  NP$=P$ :: GOSUB GET_OPERATOR_PRECEDENCE :: NPR=PR

REDUCE_PRECEDENCE:
  IF OT<0 THEN PUSH_OPERATOR
  IF O$(OT)="(" THEN PUSH_OPERATOR
  P$=O$(OT):: GOSUB GET_OPERATOR_PRECEDENCE
  IF PR<NPR THEN PUSH_OPERATOR
  GOSUB APPLY_TOP_OPERATOR
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  GOTO REDUCE_PRECEDENCE

PUSH_OPERATOR:
  OT=OT+1 :: O$(OT)=NP$ :: P$=NP$ :: GOSUB SET_OPERATOR_STATUS :: AFTER=0
  GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! COMMIT E$ AS A VALUE ONTO THE VALUE STACK
PUSH_ENTRY_VALUE:
  GOSUB PARSE_ENTRY
  IF ER=1 THEN RETURN 
  VT=VT+1 :: V(VT)=X :: ANS=X :: NE=0
RETURN

  ! APPLY THE TOP STACK OPERATOR
APPLY_TOP_OPERATOR:
  IF VT>0 THEN POP_OPERATOR_VALUES
  ER=1 :: MSG$="SYNTAX ERR" :: RETURN 

POP_OPERATOR_VALUES:
  AA=V(VT-1):: BB=V(VT):: P$=O$(OT)
  VT=VT-2 :: OT=OT-1
  GOSUB EXECUTE_OPERATOR
  IF ER=1 THEN RETURN 
  VT=VT+1 :: V(VT)=R :: ANS=R
  EXOP$=P$ :: EXVAL=BB
RETURN

  ! OPERATOR PRECEDENCE: OR, XOR, AND, ARITHMETIC, SHIFT
GET_OPERATOR_PRECEDENCE:
  PR=1
  IF P$="O" THEN RETURN 
  IF P$="X" THEN PR=2 :: RETURN 
  IF P$="A" THEN PR=3 :: RETURN 
  IF P$="+" THEN PR=4 :: RETURN 
  IF P$="-" THEN PR=4 :: RETURN 
  IF P$="*" THEN PR=5 :: RETURN 
  IF P$="/" THEN PR=5 :: RETURN 
  PR=6 :: RETURN 

  ! EXECUTE P$ WITH AA (LEFT) AND BB (RIGHT)
EXECUTE_OPERATOR:
  IF P$="+" THEN R=AA+BB :: GOTO NORMALIZE_RESULT
  IF P$="-" THEN R=AA-BB :: GOTO NORMALIZE_RESULT
  IF P$="*" THEN R=AA*BB :: GOTO NORMALIZE_RESULT
  IF P$="/" THEN DIVIDE_VALUES
  GOTO EXECUTE_LOGICAL

DIVIDE_VALUES:
  IF BB<>0 THEN R=AA/BB :: GOTO NORMALIZE_RESULT
  ER=1 :: MSG$="DIV BY 0" :: RETURN 

NORMALIZE_RESULT:
  GOSUB NORMALIZE_RANGE :: RETURN 

  ! LOGICAL AND SHIFT OPERATIONS REQUIRE OCT OR HEX
EXECUTE_LOGICAL:
  IF B<>10 THEN PREPARE_LOGICAL_VALUES
  ER=1 :: MSG$="OCT/HEX" :: RETURN 

PREPARE_LOGICAL_VALUES:
  U=AA :: IF U<0 THEN U=U+W
  Z=BB :: IF Z<0 THEN Z=Z+W
  IF P$="S" THEN EXECUTE_SHIFT
  R=0 :: PW=1
  FOR J=1 TO NBITS
  DA=U-2*INT(U/2):: DB=Z-2*INT(Z/2)
  IF P$="A" THEN DC=DA*DB
  IF P$="O" THEN DC=DA+DB-DA*DB
  IF P$="X" THEN DC=DA+DB-2*DA*DB
  R=R+DC*PW :: U=INT(U/2):: Z=INT(Z/2):: PW=PW*2
  NEXT J
  GOTO CONVERT_RESULT_SIGN

EXECUTE_SHIFT:
  IF BB=INT(BB)THEN CHECK_SHIFT_RANGE
  ER=1 :: MSG$="SHIFT COUNT" :: RETURN 

CHECK_SHIFT_RANGE:
  N=ABS(BB)
  IF N<NBITS+1 THEN EXECUTE_SHIFT_COUNT
  ER=1 :: MSG$="SHIFT RANGE" :: RETURN 

EXECUTE_SHIFT_COUNT:
  R=U :: IF BB<0 THEN SHIFT_RIGHT
  FOR J=1 TO N :: R=R*2-W*INT(R/W):: NEXT J :: GOTO CONVERT_RESULT_SIGN

SHIFT_RIGHT:
  FOR J=1 TO N :: R=INT(R/2):: NEXT J

CONVERT_RESULT_SIGN:
  IF R<W/2 THEN OPERATOR_DONE
  R=R-W

OPERATOR_DONE:
RETURN

  ! ----- EQUALS -----
EQUALS:
  IF ER=0 THEN CHECK_CONSTANT_MODE
  GOSUB DRAW_LED_AND_STATUS :: RETURN 

CHECK_CONSTANT_MODE:
  IF KRDY=0 THEN EVALUATE_EXPRESSION
  IF OT<0 THEN APPLY_CONSTANT
  GOTO EVALUATE_EXPRESSION

APPLY_CONSTANT:
  IF NE=1 THEN GOSUB PARSE_ENTRY ELSE X=ANS
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  AA=X :: BB=KVAL :: P$=KOP$ :: GOSUB EXECUTE_OPERATOR
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  ANS=R :: NE=0 :: VT=-1 :: OT=-1 :: AFTER=1
  LAST$=KOP$ :: LVAL=KVAL :: MSG$="K "&KOP$ :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

EVALUATE_EXPRESSION:
  IF NE=1 THEN GOSUB PUSH_ENTRY_VALUE
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF VT>-1 THEN REDUCE_EQUALS
  IF LAST$="" THEN FINISH_RESULT_ONLY
  AA=ANS :: BB=LVAL :: P$=LAST$ :: GOSUB EXECUTE_OPERATOR
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  ANS=R :: MSG$="REPEAT" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

REDUCE_EQUALS:
  IF OT<0 THEN FINISH_EQUALS
  IF O$(OT)<>"(" THEN APPLY_EQUALS_OPERATOR
  ER=1 :: MSG$="MISSING )" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

APPLY_EQUALS_OPERATOR:
  GOSUB APPLY_TOP_OPERATOR :: IF ER=0 THEN REDUCE_EQUALS
  GOSUB DRAW_LED_AND_STATUS :: RETURN 

FINISH_EQUALS:
  ANS=V(VT):: VT=-1 :: OT=-1 :: NE=0 :: AFTER=1
  LAST$=EXOP$ :: LVAL=EXVAL :: MSG$="RESULT" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

FINISH_RESULT_ONLY:
  AFTER=1 :: MSG$="RESULT" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- OPEN PARENTHESIS -----
OPEN_PAREN:
  IF NE=0 THEN CHECK_PAREN_SPACE
  ER=1 :: MSG$="OP BEFORE (" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

CHECK_PAREN_SPACE:
  IF OT<15 THEN PUSH_OPEN_PAREN
  ER=1 :: MSG$="PAREN LIMIT" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

PUSH_OPEN_PAREN:
  OT=OT+1 :: O$(OT)="(" :: AFTER=0 :: MSG$="OPEN PAREN" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- CLOSE PARENTHESIS -----
CLOSE_PAREN:
  IF NE=1 THEN GOSUB PUSH_ENTRY_VALUE
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 

CLOSE_PAREN_LOOP:
  IF OT>-1 THEN FIND_OPEN_PAREN
  ER=1 :: MSG$="UNMATCHED )" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

FIND_OPEN_PAREN:
  IF O$(OT)="(" THEN FINISH_CLOSE_PAREN
  GOSUB APPLY_TOP_OPERATOR :: IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  GOTO CLOSE_PAREN_LOOP

FINISH_CLOSE_PAREN:
  OT=OT-1 :: NE=0 :: AFTER=0 :: MSG$="CLOSE PAREN" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- DECIMAL POINT -----
DECIMAL_POINT:
  IF B=10 THEN START_DECIMAL_ENTRY
  BAD=1 :: MSG$="DEC ONLY" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

START_DECIMAL_ENTRY:
  IF NE=1 THEN CHECK_POINT
  IF AFTER=1 THEN VT=-1 :: OT=-1 :: AFTER=0
  E$="0" :: NE=1

CHECK_POINT:
  IF POS(E$,".",1)=0 THEN CHECK_POINT_LENGTH
  BAD=1 :: MSG$="ONE POINT" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

CHECK_POINT_LENGTH:
  IF LEN(E$)<8 THEN APPEND_POINT
  BAD=1 :: MSG$="8 DIGITS" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

APPEND_POINT:
  E$=E$&"." :: MSG$="DECIMAL" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- CHANGE BASE TO NB -----
CHANGE_BASE:
  IF NB<>B THEN CONVERT_BASE
  GOSUB SET_MODE_STATUS :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

CONVERT_BASE:
  IF NE=1 THEN GOSUB PARSE_ENTRY
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  HOLD=X :: B=NB :: GOSUB SET_BASE_PARAMS
  IF NE=0 THEN FINISH_BASE_CHANGE
  X=HOLD :: GOSUB FORMAT_NUMBER
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  E$=D$

FINISH_BASE_CHANGE:
  GOSUB SET_MODE_STATUS :: MSG$=MSG$&" CONVERTED" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- CLEAR ENTRY (BACKSPACE) -----
CLEAR_ENTRY:
  E$="" :: NE=0 :: MSG$="CE" :: GOSUB DRAW_LED_AND_STATUS :: RETURN

  ! ----- ALL CLEAR -----
ALL_CLEAR:
  ANS=0 :: E$="" :: NE=0 :: ER=0 :: VT=-1 :: OT=-1
  AFTER=0 :: LAST$="" :: KRDY=0 :: KP=0 :: MSG$="CLEAR"
  GOSUB DRAW_LED_AND_STATUS :: RETURN 

! ----- STORE MEM -----
STORE_MEMORY:
  IF NE=1 THEN GOSUB PARSE_ENTRY ELSE X=ANS
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  MEM=X :: MH=1 :: MSG$="STO" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

! ----- RECALL MEM -----
RECALL_MEMORY:
  IF MH=1 THEN LOAD_MEMORY
  BAD=1 :: MSG$="MEM EMPTY" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

LOAD_MEMORY:
  X=MEM :: GOSUB FORMAT_NUMBER
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  E$=D$ :: NE=1 :: AFTER=0 :: MSG$="RCL" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

! ----- SUM INTO MEM -----
SUM_MEMORY:
  IF NE=1 THEN GOSUB PARSE_ENTRY ELSE X=ANS
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  MEM=MEM+X :: MH=1 :: MSG$="SUM" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- STORE THE PENDING OPERATOR AND VALUE AS CONSTANT -----
SET_CONSTANT:
  IF OT<0 THEN CONSTANT_NEEDS_OPERATOR
  IF O$(OT)<>"(" THEN SELECT_CONSTANT_VALUE

CONSTANT_NEEDS_OPERATOR:
  BAD=1 :: MSG$="K NEEDS OP" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

SELECT_CONSTANT_VALUE:
  IF NE=1 THEN PARSE_CONSTANT_VALUE
  KVAL=V(VT):: IF KP=0 THEN STORE_CONSTANT
  IF LAST$<>O$(OT)THEN STORE_CONSTANT
  KVAL=LVAL :: GOTO STORE_CONSTANT

PARSE_CONSTANT_VALUE:
  GOSUB PARSE_ENTRY :: IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  KVAL=X

STORE_CONSTANT:
  KOP$=O$(OT):: KRDY=1 :: NE=0 :: VT=-1 :: OT=-1 :: AFTER=0 :: KP=0 :: MSG$="K SET" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- ONE'S COMPLEMENT -----
ONES_COMPLEMENT:
  IF B<>10 THEN APPLY_ONES_COMPLEMENT
  BAD=1 :: MSG$="1C OCT/HEX" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

APPLY_ONES_COMPLEMENT:
  IF NE=1 THEN GOSUB PARSE_ENTRY ELSE X=ANS
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  U=X :: IF U<0 THEN U=U+W
  R=W-1-U :: IF R>=W/2 THEN R=R-W
  IF NE=0 THEN  ANS=R :: IF VT>-1 THEN V(VT)=R :: GOTO FINISH_ONES_COMPLEMENT
  X=R :: GOSUB FORMAT_NUMBER :: E$=D$

FINISH_ONES_COMPLEMENT:
  MSG$="1'S C" :: GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- +/- IN DEC, TWO'S COMPLEMENT IN OCT/HEX -----
TOGGLE_SIGN:
  IF NE=1 THEN GOSUB PARSE_ENTRY ELSE X=ANS
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  R=-X :: GOSUB NORMALIZE_RANGE
  IF ER=1 THEN GOSUB DRAW_LED_AND_STATUS :: RETURN 
  IF NE=0 THEN  ANS=R :: IF VT>-1 THEN V(VT)=R :: GOTO SET_SIGN_STATUS
  X=R :: GOSUB FORMAT_NUMBER :: E$=D$

SET_SIGN_STATUS:
  IF B=10 THEN SET_NEGATIVE_STATUS
  IF OT<0 THEN SET_TWOS_STATUS
  IF O$(OT)="S" THEN MSG$="SHF RIGHT" :: GOTO REDRAW_SIGN

SET_TWOS_STATUS:
  MSG$="2'S C" :: GOTO REDRAW_SIGN

SET_NEGATIVE_STATUS:
  MSG$="NEGATIVE"

REDRAW_SIGN:
  GOSUB DRAW_LED_AND_STATUS :: RETURN 

  ! ----- CONVERT CURRENT ENTRY E$ TO X -----
PARSE_ENTRY:
  IF B<>10 THEN PARSE_NONDEC_ENTRY
  X=VAL(E$)
  R=X :: GOSUB NORMALIZE_RANGE :: RETURN 

PARSE_NONDEC_ENTRY:
  X=0
  FOR J=1 TO LEN(E$)
  C$=SEG$(E$,J,1):: D=ASC(C$)
  IF D<58 THEN D=D-48 ELSE D=D-55
  X=X*B+D
  NEXT J
  IF X<W/2 THEN NORMALIZE_PARSED_VALUE
  X=X-W

NORMALIZE_PARSED_VALUE:
  R=X :: GOSUB NORMALIZE_RANGE :: RETURN 

  ! ----- STATUS LABEL FOR THE CURRENT BASE -----
SET_MODE_STATUS:
  MSG$="MODE DEC"
  IF B=8 THEN MSG$="MODE OCT"
  IF B=16 THEN MSG$="MODE HEX"
RETURN

  ! ----- STATUS LABEL FOR A PENDING OPERATOR -----
SET_OPERATOR_STATUS:
  MSG$="OP"
  IF P$="+" THEN MSG$="ADD"
  IF P$="-" THEN MSG$="SUBTRACT"
  IF P$="*" THEN MSG$="MULTIPLY"
  IF P$="/" THEN MSG$="DIVIDE"
  IF P$="O" THEN MSG$="OR"
  IF P$="X" THEN MSG$="XOR"
  IF P$="A" THEN MSG$="AND"
  IF P$="S" THEN MSG$="SHF: T=RIGHT"
RETURN

  ! ----- PARAMETERS FOR THE SELECTED BASE -----
SET_BASE_PARAMS:
  IF B=10 THEN W=0 :: P=99999999 :: NBITS=0 :: RETURN
  IF B=8 THEN W=16777216 :: P=8388607 :: NBITS=24 :: RETURN 
  W=4294967296 :: P=2147483647 :: NBITS=32 :: RETURN 

  ! ----- FORMAT X AS D$ IN THE SELECTED BASE -----
FORMAT_NUMBER:
  IF B<>10 THEN FORMAT_NONDEC_NUMBER
  GOSUB FORMAT_DECIMAL_NUMBER
  IF LEN(D$)<9 THEN RETURN 
  ER=1 :: MSG$="OVERFLOW" :: RETURN 

  ! ROUND DECIMAL RESULTS TO THE AVAILABLE LED DISPLAY POSITIONS
FORMAT_DECIMAL_NUMBER:
  S$="" :: U=X
  IF U<0 THEN S$="-" :: U=-U
  I=INT(U) :: I$=STR$(I)
  IF SEG$(I$,1,1)=" " THEN I$=SEG$(I$,2,LEN(I$)-1)
  ND=7-LEN(I$)-LEN(S$)
  IF ND>0 THEN FORMAT_DECIMAL_FRACTION
  IF U=I THEN FORMAT_DECIMAL_INTEGER
  ER=1 :: MSG$="OVERFLOW" :: RETURN

FORMAT_DECIMAL_FRACTION:
  SC=1
  FOR J=1 TO ND :: SC=SC*10 :: NEXT J
  R=INT(U*SC+.5) :: I=INT(R/SC) :: F=R-I*SC
  I$=STR$(I)
  IF SEG$(I$,1,1)=" " THEN I$=SEG$(I$,2,LEN(I$)-1)
  IF F=0 THEN FORMAT_DECIMAL_INTEGER_TEXT
  F$=STR$(F)
  IF SEG$(F$,1,1)=" " THEN F$=SEG$(F$,2,LEN(F$)-1)
  IF LEN(F$)<ND THEN F$=RPT$("0",ND-LEN(F$))&F$

STRIP_DECIMAL_ZEROES:
  IF SEG$(F$,LEN(F$),1)<>"0" THEN BUILD_DECIMAL_TEXT
  F$=SEG$(F$,1,LEN(F$)-1):: GOTO STRIP_DECIMAL_ZEROES

BUILD_DECIMAL_TEXT:
  D$=S$&I$&"."&F$ :: RETURN

FORMAT_DECIMAL_INTEGER:
  I=INT(U)

FORMAT_DECIMAL_INTEGER_TEXT:
  IF I=0 THEN S$=""
  I$=STR$(I)
  IF SEG$(I$,1,1)=" " THEN I$=SEG$(I$,2,LEN(I$)-1)
  D$=S$&I$ :: RETURN

FORMAT_NONDEC_NUMBER:
  U=X :: IF U<0 THEN U=U+W
  D$=""
  FOR J=1 TO 8
  Q=INT(U/B):: D=U-B*Q
  IF D<10 THEN C$=CHR$(48+D)ELSE C$=CHR$(55+D)
  D$=C$&D$ :: U=Q
  NEXT J
  IF U=0 THEN STRIP_LEADING_ZERO
  ER=1 :: MSG$="OVERFLOW" :: RETURN 

STRIP_LEADING_ZERO:
  IF LEN(D$)=1 THEN RETURN 
  IF SEG$(D$,1,1)<>"0" THEN RETURN 
  D$=SEG$(D$,2,LEN(D$)-1):: GOTO STRIP_LEADING_ZERO

  ! ----- RANGE / TWO'S-COMPLEMENT WRAP -----
NORMALIZE_RANGE:
  IF B<>10 THEN WRAP_WORD
  IF ABS(R)<100000000 THEN RETURN 
  ER=1 :: MSG$="OVERFLOW" :: RETURN 

WRAP_WORD:
  R=R-W*INT((R+W/2)/W)
RETURN

  ! ----- DRAW THE CALCULATOR FACE -----
DRAW_CALC:
  CALL SCREEN(15):: CALL CLEAR
  FOR J=1 TO 14 :: CALL COLOR(J,2,15):: NEXT J
  CALL COLOR(9,10,7):: CALL COLOR(10,10,7):: CALL COLOR(11,10,7)
  CALL CHAR(96,"0000000000000000")
  CALL CHAR(97,"3C666E7666663C00")  // 0
  CALL CHAR(98,"1838181818187E00")  // 1
  CALL CHAR(99,"3C66060C30607E00")  // 2
  CALL CHAR(100,"3C66061C06663C00") // 3
  CALL CHAR(101,"0C1C3C6C7E0C0C00") // 4
  CALL CHAR(102,"7E607C0606663C00") // 5
  CALL CHAR(103,"1C30607C66663C00") // 6
  CALL CHAR(104,"7E060C1830303000") // 7
  CALL CHAR(105,"3C66663C66663C00") // 8
  CALL CHAR(106,"3C66663E060C3800") // 9
  CALL CHAR(107,"183C66667E666600") // A
  CALL CHAR(108,"7C66667C66667C00") // B
  CALL CHAR(109,"3C66606060663C00") // C
  CALL CHAR(110,"786C6666666C7800") // D
  CALL CHAR(111,"7E60607C60607E00") // E
  CALL CHAR(112,"7E60607C60606000") // F
  CALL CHAR(113,"0000007E00000000") // -
  CALL CHAR(114,"0000000000001838") // .
  CALL CHAR(136,"000000FFFF000000") // horizontal line
  CALL CHAR(137,"1818181818181818") // vertical line
  CALL CHAR(138,"000000FFFF181818") // upper corner
  CALL CHAR(139,"181818FFFF000000") // lower corner
  // Define TI Logo (Brad Snyder)
  CALL CHAR(140,"0F0F0F0F0F0FFFFF7F1F0F05000000000000208EAE26AEAFAFAF86F8F0703010")
  CALL CHAR(95,"FFFFFFFFFFFFFFFF") // status block
  H$=RPT$(CHR$(136),26):: V$=CHR$(137)
  TL$=CHR$(138):: TR$=CHR$(138):: BL$=CHR$(139):: BR$=CHR$(139)
  DISPLAY AT(1,1)SIZE(28):TL$&H$&TR$
  DISPLAY AT(2,1)SIZE(28):V$&TL$&RPT$(CHR$(136),24)&TR$&V$
  DISPLAY AT(3,1)SIZE(28):V$&V$&RPT$(CHR$(96),24)&V$&V$
  DISPLAY AT(4,1)SIZE(28):V$&BL$&RPT$(CHR$(136),24)&BR$&V$
  DISPLAY AT(5,1)SIZE(28):V$&"  TEXAS INSTRUMENTS  "&VR$&V$
  DISPLAY AT(6,1)SIZE(28):V$&"             TI PROGRAMMER"&V$
  // Print TI Logo
  CALL HCHAR(5,4,140)::CALL HCHAR(5,5,142)::CALL HCHAR(6,4,141)::CALL HCHAR(6,5,143)
  DISPLAY AT(7,1)SIZE(28):V$&H$&V$
  DISPLAY AT(8,1)SIZE(28):V$&"[DEC][HEX][OCT][OFF][C/ON]"&V$
  DISPLAY AT(9,1)SIZE(28):V$&"[STO][RCL][SUM][ ( ][ ) ] "&V$
  DISPLAY AT(10,1)SIZE(28):V$&"[SHF][ D ][ E ][ F ][ K ] "&V$
  DISPLAY AT(11,1)SIZE(28):V$&"[1SC][ A ][ B ][ C ][ / ] "&V$
  DISPLAY AT(12,1)SIZE(28):V$&"[ OR][ 7 ][ 8 ][ 9 ][ * ] "&V$
  DISPLAY AT(13,1)SIZE(28):V$&"[AND][ 4 ][ 5 ][ 6 ][ - ] "&V$
  DISPLAY AT(14,1)SIZE(28):V$&"[XOR][ 1 ][ 2 ][ 3 ][ + ] "&V$
  DISPLAY AT(15,1)SIZE(28):V$&"[CE ][ 0 ][ . ][+/-][ = ] "&V$
  DISPLAY AT(16,1)SIZE(28):V$&H$&V$
  DISPLAY AT(18,1)SIZE(28):V$&"Z-DEC H-HEX O-OCT L-CLEAR "&V$
  DISPLAY AT(19,1)SIZE(28):V$&"S-STO R-RCL U-SUM K-CONST "&V$
  DISPLAY AT(20,1)SIZE(28):V$&"Q-SHF V-1SC W-OR I-AND    "&V$
  DISPLAY AT(21,1)SIZE(28):V$&"Y-XOR T-(+/-); RIGHT SHF  "&V$
  DISPLAY AT(22,1)SIZE(28):V$&"ENTER-= BKSP-CE BACK-EXIT "&V$
  DISPLAY AT(23,1)SIZE(28):V$&"K: M OP K; THEN N =       "&V$
  DISPLAY AT(24,1)SIZE(28):BL$&H$&BR$
  // Status Line Markers
  CALL SPRITE(#1,95,5,16*8+1,8)::CALL SPRITE(#2,95,5,16*8+1,241)
RETURN

  ! ----- REDRAW LED DISPLAY AND STATUS -----
DRAW_LED_AND_STATUS:
  IF ER=1 THEN BAD=1
  IF BAD=MBAD THEN DRAW_LED_AND_STATUS_BODY
  IF BAD=0 THEN SET_MARKER_BLUE
  CALL COLOR(#1,10)::CALL COLOR(#2,10)::CALL SOUND(200,110,12)::MBAD=1::GOTO DRAW_LED_AND_STATUS_BODY

SET_MARKER_BLUE:
  CALL COLOR(#1,5)::CALL COLOR(#2,5)::MBAD=0

DRAW_LED_AND_STATUS_BODY:
  GOSUB DRAW_LED_VALUE
  GOSUB DRAW_STATUS
RETURN

DRAW_LED_VALUE:
  LED$=""
  IF ER=0 THEN GET_LED_VALUE
  D$="EEEEEEEE" :: GOTO CHECK_LED_LENGTH

GET_LED_VALUE:
  IF NE=1 THEN D$=E$ ELSE X=ANS :: GOSUB FORMAT_NUMBER
  IF ER=0 THEN CHECK_LED_LENGTH
  D$="EEEEEEEE"

CHECK_LED_LENGTH:
  IF LEN(D$)<9 THEN BUILD_LED_DIGITS
  ER=1 :: MSG$="OVERFLOW" :: D$="EEEEEEEE"

BUILD_LED_DIGITS:
  FOR J=1 TO 8
  C=96
  IF J>8-LEN(D$)THEN C$=SEG$(D$,J-8+LEN(D$),1):: GOSUB MAP_LED_GLYPH
  LED$=LED$&CHR$(C)
  NEXT J :: DISPLAY AT(3,19)SIZE(8):LED$
  IF BFLAG=0 THEN DRAW_LED_DONE
  IF DBEEP=0 THEN DRAW_LED_DONE
  CALL SOUND(40,988,10)

DRAW_LED_DONE:
RETURN

DRAW_STATUS:
  IF B=10 THEN MS$="DEC"
  IF B=8 THEN MS$="OCT"
  IF B=16 THEN MS$="HEX"
  IF MH=0 THEN MM$="---" ELSE MM$="SET"
  IF MEM=0 THEN MM$="---"
  SS$="MODE "&MS$&" MEM "&MM$&" "&MSG$
  IF LEN(SS$)<27 THEN FIT_STATUS
  SS$=MS$&" "&MM$&" "&MSG$

FIT_STATUS:
  IF LEN(SS$)<26 THEN PAD_STATUS
  IF LEN(SS$)=26 THEN DRAW_STATUS_LINE
  SS$=SEG$(SS$,1,26)

PAD_STATUS:
  SS$=SS$&RPT$(" ",26-LEN(SS$))

DRAW_STATUS_LINE:
  DISPLAY AT(17,1)SIZE(28):V$&SS$&V$
RETURN

  ! MAP ONE DISPLAY CHARACTER C$ TO A CUSTOM LED GLYPH C
MAP_LED_GLYPH:
  D=ASC(C$)
  IF D<45 THEN C=D :: RETURN 
  IF D=45 THEN C=113 :: RETURN 
  IF D=46 THEN C=114 :: RETURN 
  IF D<58 THEN C=97+D-48 :: RETURN 
  IF D<71 THEN C=107+D-65 :: RETURN 
  C=D :: RETURN 

  ! ----- STARTUP SOUND CONFIGURATION -----
CONFIG_MENU:
  CALL SCREEN(15):: CALL CLEAR
  FOR J=1 TO 14 :: CALL COLOR(J,2,15):: NEXT J
  DISPLAY AT(4,2):"TI PROGRAMMER ";VR$;" SETUP"
  DISPLAY AT(6,3):"(C) 2026 - BRAD SNYDER"
  DISPLAY AT(7,9):"AND CODEX"
  DISPLAY AT(10,4):"K   KEY CLICK:"
  DISPLAY AT(12,4):"D   DIGIT BEEP:"
  DISPLAY AT(14,4):"PRESS K OR D TO TOGGLE"
  DISPLAY AT(17,4):"ENTER OR = TO START"
  GOSUB DRAW_CONFIG_OPTIONS

CONFIG_KEY_LOOP:
  CALL KEY(3,CKEY,CST)
  IF CST<>1 THEN CONFIG_KEY_LOOP
  IF CKEY=75 THEN TOGGLE_KEY_CLICK
  IF CKEY=68 THEN TOGGLE_DIGIT_BEEP
  IF CKEY=13 THEN RETURN
  IF CKEY=61 THEN RETURN
  GOTO CONFIG_KEY_LOOP

TOGGLE_KEY_CLICK:
  KSND=1-KSND :: GOSUB DRAW_CONFIG_OPTIONS :: GOTO CONFIG_KEY_LOOP

TOGGLE_DIGIT_BEEP:
  DBEEP=1-DBEEP :: GOSUB DRAW_CONFIG_OPTIONS :: GOTO CONFIG_KEY_LOOP

DRAW_CONFIG_OPTIONS:
  IF KSND=1 THEN KCFG$="ON " ELSE KCFG$="OFF"
  IF DBEEP=1 THEN DCFG$="ON " ELSE DCFG$="OFF"
  DISPLAY AT(10,26)SIZE(3):KCFG$
  DISPLAY AT(12,26)SIZE(3):DCFG$
RETURN
 

Comments