Executing TI BASIC from a String - ti-basic

Is it possible to Execute TI BASIC from a string? Such that:
execute(":Disp Str1")
would Print out Str1?

It could be done with a small Asm( program that takes a string (from Ans), turns it into a BASIC program, and executes it. For example:
.nolist
#include "ti83plus.inc"
.list
.org userMem-2
.db $BB,$6D
ld hl,SavesScreen
ld (hl),tTheta
inc hl
ld (hl),0
inc hl
push hl
bcall(_AnsName)
rst rFindSym
ex de,hl
ld c,(hl)
inc hl
ld b,(hl)
dec hl
inc bc
inc bc
pop de
ldir
ld hl,SavesScreen
ld a,6
bcall(_ExecuteNewPrgm)
; no ret because _ExecuteNewPrgm does not return
This is not ideal,
No safety in case Ans did not contain a string (could be added).
A program named θ should not exist, because it will be overwritten. If prgmθ already exists and is archived, then it does not work at all. (could be improved)
prgmθ is not deleted afterwards. (not sure how to do this)
When prgmθ is done, it quits to the Homescreen, it does not return to the calling program. (not sure how to do this)
Aside from that, it does work, for example:
:"TESTING->Str1
:":Disp Str1
:Asm(prgmRUNSTR
Looks like this afterwards:
You can create the assembly program by typing this into a normal program:
AsmPrgmBB6D21
EC86365B2336
0023E5EF524B
D7EB4E23462B
0303D1EDB021
EC863E06EF3C
4C
It can be made smaller with AsmComp(.

Unfortunately, no, not like this- expr and eval only work on expressions.

Related

Why does ghostscript hang when passing a procedure to a procedure to another procedure to invoke when using the same argument name?

I am passing a postscript procedure (c) as an argument on the stack to a procedure (a), which then passes this as an argument on the stack to another procedure (b) that invokes the procedure (c).
When I use a dictionary to localize variables and use the same name for the argument in a and b, ghostscript hangs (in the below code, these are procedures a1 and a2 and they both use proc). When I make them different names (in the below code these are procedures a and b which use Aproc and Bproc, respectively), it runs correctly.
Note that I am aware that I can just use the stack to pass down the original argument and avoid the whole exch def step, but in my real world code, I want to capture the stack argument to use locally.
Below is a minimal working example of the issue:
%!PS
/c{
(C START) =
(C output) =
(C END) =
}bind def
/b{
(B START) =
1 dict begin
/Bproc exch def
Bproc
end
(B END) =
}bind def
/a{
(A START) =
1 dict begin
/Aproc exch def
{Aproc} b
end
(A END) =
}bind def
/b2{
(B2 START) =
1 dict begin
/proc exch def
proc
end
(B2 END) =
}bind def
/a2{
(A2 START) =
1 dict begin
/proc exch def
{proc} b2
end
(A2 END) =
}bind def
{c} a
% {c} a2
Here is the output with the above code (different argument names, no issues):
$ gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=test2.pdf test2.ps
GPL Ghostscript 9.54.0 (2021-03-30)
Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
A START
B START
C START
C output
C END
B END
A END
$
Change the last 2 lines of the code to the following (comment out first, uncomment out the second):
...
% {c} a
{c} a2
Now ghostscript just hangs:
$ gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=test2.pdf test2.ps
GPL Ghostscript 9.54.0 (2021-03-30)
Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
C-c C-c: *** Interrupt
$
Environment: OpenSuse:
$ uname -a
Linux localhost.localdomain 5.18.4-1-default #1 SMP PREEMPT_DYNAMIC Wed Jun 15 06:00:33 UTC 2022 (ed6345d) x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/os-release
NAME="openSUSE Tumbleweed"
# VERSION="20220619"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20220619"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20220619"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed"
LOGO="distributor-logo-Tumbleweed"
I think you are being tripped up by early and late name binding, which is a subtle point in PostScript and often traps newcomers to the language.
Because of late name binding you can change the definition of a key/value pair during the course of execution, which can have unexpected effects. This is broadly similar to self-modifying code.
See section 3.12 Early Name Binding on page 117 of the 3rd Edition PostScript Language Reference Manual for details, but the basic point is right at the first paragraph:
"Normally, when the PostScript language scanner encounters an executable name in the program being scanned, it simply produces an executable name object; it does not look up the value of the name. It looks up the name only when the name object is executed by the interpreter. The lookup occurs in the dictionaries that are on the dictionary tack at the time of execution."
Also you aren't quite (I think) doing what you think you are with the executable array tokens '{' and '}'. That isn't putting the definition on the stack, exactly, it is defining an executable array on the stack. Not quite the same thing. In particular it means that the content of the array (between { and }) isn't being executed.
If you wanted to put the original function on the stack you would do '/proc load'. For example if you wanted to define a function to be the same as showpage you might do:
/my_showpage /showpage load def
Not:
/my_showpage {showpage} def
So looking at your failing case.
In function a2 You start by creating and opening a new dictionary. Said dictionary is only present on the stack, so if you ever pop it off it will go out of scope and be garbage collected (I'm sure you know this of course).
In that dictionary you create a key '/proc' with the associated value of an executable array. The array contains {c}.
Then you create a new executable array on the stack {proc}
You then execute function b2 with that executable array on the stack.
b2 starts another new dictionary, again on the stack, and then defines a key/value pair '/proc' with the associated value {proc}.
You then execute proc. That executes the array, the only thing in it is a reference to 'proc', so that is looked up starting in the current dictionary.
Oh look! We have a definition in the current dictionary, it's an executable array. So we push that array and execute it. The only thing in it is a reference to 'proc', so we look that up in the current dictionary, and we have one, so we execute that array. Round and round and round we go.....
You can avoid this by simply changing :
/a2{
(A2 START) =
1 dict begin
/proc exch def
{proc} b2
end
(A2 END) =
}bind def
To :
/a2{
(A2 START) =
1 dict begin
/proc exch def
/proc load b2
end
(A2 END) =
}bind def

Local Label Address in GNU GCC Assembely

I'm trying to use some PPC assembly code in C, but I'm having trouble understanding and transposing this particular piece of ASM into the GNU GCC format:
...
b 1f
1:
lis p0, HI(2f)
ori p0, p0, LO(2f)
mtsrr0 p0
rfi
2:
mtssr0 p1 /* Restore srr0 & srr1 */
mtssr1 p2
...
The lines in question are those that reference 2f. I'm aware of Local Labels and I can only assume that is what is meant by 2f in those two instructions. Looking at the more general mtspr instruction, the RS parameter should be a register.
EDIT: Peter Cordes helped me understand the intent of this code. It looks like we're using lis and ori to build the 32 bit address of the label 2: to load into ssr0. The following quote from the PowerPC Architecture Primer completely explains the intent of this assembly.
Save/restore registers (SRR0 and SRR1) — SRR0 holds the address of the instruction where an interrupted process should resume. When rfi executes, instruction execution continues at the address in SRR0. In Book E, SRR0 is used for non-critical interrupts.— SRR1 holds machine state information. When an interrupt is taken, MSR contents are placed in SRR1. When rfi executes, SRR1 contents are placed into MSR. In Book E, SRR1 is used for non-critical interrupts.
Now that I understand what the code is doing, I need to represent this code with GNU GCC in C:
__asm__ __volatile__ (
"b 1f\n\t"
"1:\n\t"
"lis %2, %hi(2f)\n\t"
"ori %2, %2, %lo(2f)\n\t"
"mtsrr0 %2\n\t"
"rfi\n\t"
"2:\n\t"
"mtsrr0 %0\n\t" /* srr0 = p1 */
"mtsrr1 %1\n\t" /* srr1 = p2 */
: "=r" (p1), "=r" (p2)
: "r" (val), "r" (p1), "r" (p2));
This yields the following error (twice, for each instance of 2f I assume:
invalid 'asm': operand number missing after %-letter
Commenting out the lines with instructions lis and ori allows the code to compile without errors.

EBNF: prefix and suffix-like operator in assembly code production

I'm trying to write down 6809 assembly in EBNF to write a tree-sitter parser.
I'm stuck on one certain production. In 6809 assembly, you can use a register as an operand and additionally de- or increment it:
LDA 0,X+ ; loads A from X then bumps X by 1
LDD ,Y++ ; loads D from Y then bumps Y by 2
LDA 0,-U ; decrements U by 1 then loads A from address in U
LDU ,--S ; decrements S by 2 then loads U from address in S
Mind the "missing" first operand in the second line of code. Here are the productions I wrote:
instruction = opcode, [operand], ["," , register_exp];
...
register_exp = [{operator}], register | register, [{operator}];
register = "X" | "Y" | "U" | etc. ;
operator = "+" | "-";
The problem is register_exp = .... I feel like there could be a more elegant way to define this production. Also, what happens if only a register is given to register_exp?
You probably need
register_exp = [{operator}], register | register, [{operator}] | register;
to allow register names without operators. Why do you find it not so elegant? Quite descriptive.

Strange behaviour of clang assembler

I tried to compile this overflow detection macro of Zend engine:
#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
long __tmpvar; \
__asm__( \
"mul %0, %2, %3\n" \
"smulh %1, %2, %3\n" \
"sub %1, %1, %0, asr #63\n" \
: "=X"(__tmpvar), "=X"(usedval) \
: "X"(a), "X"(b)); \
if (usedval) (dval) = (double) (a) * (double) (b); \
else (lval) = __tmpvar; \
} while (0)
And got this result in assembly:
; InlineAsm Start
mul x8, x8, x9
smulh x9, x8, x9
sub x9, x9, x8, asr #63
; InlineAsm End
The compiler used only 2 register for both input and output of the macro, which i think it must be at least 3, and lead to wrong result of the calculation (for example, -1 * -1). Any suggestion?
The assembly code is buggy. From GCC's documentation on extended asm:
Use the ‘&’ constraint modifier (see Modifiers) on all output operands that must not overlap an input. Otherwise, GCC may allocate the output operand in the same register as an unrelated input operand, on the assumption that the assembler code consumes its inputs before producing outputs. This assumption may be false if the assembler code actually consists of more than one instruction.
This basically says that from the moment you write to an output parameter not marked with an ampersand, you're not allowed to use the input parameters anymore because they might have been overwritten.
The syntax is designed around the concept of wrapping a single insn which reads its inputs before writing its outputs.
When you use multiple insns, you often need to use an early-clobber modifier on the constraint ("=&x") to let the compiler know you write an output or read-write register before reading all the inputs. Then it will make sure that output register isn't the same register as any of the input registers.
See also the x86 tag wiki, and my collection of inline asm docs and SO answers at the bottom of this answer.
#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
long __tmpvar; \
__asm__( \
"mul %[tmp], %[a], %[b]\n\t" \
"smulh %[uv], %[a], %[b]\n\t" \
"sub %[uv], %[uv], %[tmp], asr #63\n" \
: [tmp] "=&X"(__tmpvar), [uv] "=&X"(usedval) \
: [a] "X"(a), [b] "X"(b)); \
if (usedval) (dval) = (double) (a) * (double) (b); \
else (lval) = __tmpvar; \
} while (0)
Do you really need all those instructions to be inside the inline asm? Can't you make long tmp = a * b an input operand? Then if the compiler needs a*b elsewhere in the function, CSE can see it.
You can convince gcc to broadcast the sign bit with an arithmetic right shift using a ternary operator. So hopefully you can coax the compiler to do the sub that way. Then it could use subs to set flags from the sub instead of needing a separate test insn on usedval.
If you can't get your target compiler to make the code you want, then sure, give inline asm a shot. But beware, I've seen clang be a lot worse than gcc with inline asm. It tends to make worse code around the inline
asm on x86.

Why am I getting extra characters?

Program Description
I used .BLKW to allocate 20 locations for each character that the user inputs and for now, I just want to display the string the user typed at the first prompt. (This will be a pig latin translator, hence the second prompt; but right now I just want to see if I can print out the user input)
The Problem
The problem is that when I run it, I get extra characters at the end.
For example:
English Word: apple
Pig-Latin Word: apple
English Word: at
Pig-Latin Word: atple
English Word: set
Pig-Latin Word: setle
My Program
.ORIG x3000
START ST R1,SAVER1
ST R2,SAVER2
ST R3,SAVER3
LD R5,ENTER
REPEAT LEA R0,PROMPT ; loading the starting address of prompt
PUTS ; displays PROMPT on screen
LEA R4,ENGLWORD ; sets aside memory locations for typed characters
INPUT GETC ; now that user has typed, read char into R0
ADD R6,R5,R0 ; adds the negative value of the ASCII enter key code to the input character
BRz PIGPROMPT ; if the sum of the ASCII codes from step before is 0, that means user pressed enter so go to PIGPROMPT
OUT ; write char in R0 to console
STR R0,R4,#0 ; store typed character into memory location
ADD R4,R4,#1 ; increment memory location so you write next character to the next location
BRnzp INPUT ; break no matter what to the INPUT step to receive next typed character
PIGPROMPT LEA R0,PIG ; loads starting address of pig latin prompt
PUTS ; displays pig latin prompt on screen
LEA R0,ENGLWORD
PUTS
BRnzp REPEAT
LD R1,SAVER1 ; restore R1 to original value
LD R2,SAVER2 ; restore R2 to original value
LD R3,SAVER3 ; restore R3 to original value
HALT
SAVER1 .BLKW 1 ; allocates 1 memory location for SAVER1
SAVER2 .BLKW 1 ; allocates 1 memory location for SAVER2
SAVER3 .BLKW 1 ; allocates 1 memory location for SAVER3
ENGLWORD .BLKW #20
ENTER .FILL xFFF6 ; the negative value of the ASCII code for the enter key
NEWLINE .FILL x000A
PROMPT .STRINGZ "\nEnglish Word: " ; initializes a sequence of stringLength+1 memory locations to hold string
PIG .STRINGZ "\nPig-Latin Word: "
DSR .FILL xFE04
DDR .FILL xFE06
KBSR .FILL xFE00
KBDR .FILL xFE02
.END
Attempted Solution
I was thinking that the problem was that R4 holds the string of the first user input throughout the whole program. So for a solution, I thought about clearing R4 after it is displayed so that it's ready to take the next user input. Does anyone know how I would do that?
The key here is how PUTS works -- it prints all the characters starting at the address in R0 until it reaches a 0 ('\0' not '0').
The first time you run it, the memory will contain ['A','P','P','L','E'], followed by zeroes if you didn't randomize memory contents when you loaded the program. This means that a PUTS call will return "APPLE". When you enter the new word, it doesn't clear out that memory, so entering "at" will result in ['A','T','P','L','E'], and your print routine will print "ATPLE".
In order to properly finish the word, you need to add a '\0' (a.k.a. 0) to the element after the last character to print. In other words, if your memory contains ['A','T','\0','L','E'], your print routine will print "AT".
Aqua's right, the PUTs command is looking for a zero to stop printing characters to the screen. I've added two lines of code just after PIGPROMPT and it seems to be working as intended.
Revised:
.ORIG x3000
START ST R1,SAVER1
ST R2,SAVER2
ST R3,SAVER3
LD R5,ENTER
REPEAT LEA R0,PROMPT ; loading the starting address of prompt
PUTS ; displays PROMPT on screen
LEA R4,ENGLWORD ; sets aside memory locations for typed characters
INPUT GETC ; now that user has typed, read char into R0
ADD R6,R5,R0 ; adds the negative value of the ASCII enter keycode to the input character
BRz PIGPROMPT ; if the sum of the ASCII codes from step before is 0, that means user pressed enter so go to PIGPROMPT
OUT ; write char in R0 to console
STR R0,R4,#0 ; store typed character into memory location
ADD R4,R4,#1 ; increment memory location so you write next character to the next location
BRnzp INPUT ; break no matter what to the INPUT step to receive next typed character
PIGPROMPT AND R0, R0, #0 ; clear R0
STR R0,R4,#0 ; store typed character into memory location
LEA R0,PIG ; loads starting address of pig latin prompt
PUTS ; displays pig latin prompt on screen
LEA R0,ENGLWORD
PUTS
BRnzp REPEAT
LD R1,SAVER1 ; restore R1 to original value
LD R2,SAVER2 ; restore R2 to original value
LD R3,SAVER3 ; restore R3 to original value
HALT
SAVER1 .BLKW 1 ; allocates 1 memory location for SAVER1
SAVER2 .BLKW 1 ; allocates 1 memory location for SAVER2
SAVER3 .BLKW 1 ; allocates 1 memory location for SAVER3
ENGLWORD .BLKW #20
ENTER .FILL xFFF6 ; the negative value of the ASCII code for the enter key
NEWLINE .FILL x000A
PROMPT .STRINGZ "\nEnglish Word: " ; initializes a sequence of stringLength+1 memory locations to hold string
PIG .STRINGZ "\nPig-Latin Word: "
DSR .FILL xFE04
DDR .FILL xFE06
KBSR .FILL xFE00
KBDR .FILL xFE02
.END
All I did was store a '0' value at the end of the user's string, that way when PUTs is called it will stop at the zero value.

Resources