Jump to content

Photo

Hacking your way out of the TI-Basic sandbox


32 replies to this topic

#1 retroclouds OFFLINE  

retroclouds

    Stargunner

  • 1,516 posts
  • Location:Germany

Posted Thu Oct 7, 2010 2:08 AM

Here's a quick question:

would it be possible to run an assembly language program from an unexpanded TI-99/4A without using an external device such as a cartridge (mini memory), memory expansion, etc. Suppose all you have is a bare TI-99/4A with a cassette player.
Would it be possible to break out of the TI-Basic "sandbox" and run a machine code program? I could image you can write a basic program
that reads a file from the cassette player into VDP memory. This file could contain a small machine code program.

The question is how would you copy the machine code program into scratch-pad memory and direct the program counter there so that it starts
executing the machine code program. I wonder if there are any bugs in the TI-Basic commands that could be misused for accomplishing just that.

I'm sure the details must be in TI-Intern somewhere, but it would require a profound knowledge of both the GPL and TI-Basic interpreter.
I don't see a real use, but it's a geek idea anyway :D

#2 matthew180 OFFLINE  

matthew180

    River Patroller

  • 2,208 posts
  • Location:Castaic, California

Posted Thu Oct 7, 2010 10:15 AM

The problem is, unexpanded TI-BASIC does not have any commands to allow you access to the only memory you could use to execute the machine code, i.e. scratch pad. You would have to get the code into some part of unused scratch pad, then branch to it somehow. The only way I can think of would be to find a way to overflow something. However, for all the effort I don't think it would be worth it.

Matthew

#3 Bones-69 OFFLINE  

Bones-69

    Chopper Commander

  • 164 posts
  • Location:Australia

Posted Thu Oct 7, 2010 2:47 PM

This question is right out of my league but if it was/is possible it would have rocked the world of a lot of early TI users with basic set-ups.

#4 matthew180 OFFLINE  

matthew180

    River Patroller

  • 2,208 posts
  • Location:Castaic, California

Posted Thu Oct 7, 2010 3:08 PM

Well, even if someone does not have a PEB, it is usually not too much to ask them to plug in a cartridge. The XB or EA carts are pretty much universally expected. I really don't know how much use you could get with the limited memory you could actually have in the scratch pad. You're talking less than 100 bytes probably, and not contiguous.

Matthew

#5 rocky007 OFFLINE  

rocky007

    Moonsweeper

  • 395 posts
  • Location:Belgium

Posted Thu Oct 7, 2010 3:28 PM

Ti-Basic is bugged and it's probably possible to create a exploit branching to ASM code.

#6 acadiel OFFLINE  

acadiel

    Dragonstomper

  • 848 posts
  • www.hexbus.com
  • Location:USA

Posted Thu Oct 7, 2010 6:11 PM

Ti-Basic is bugged and it's probably possible to create a exploit branching to ASM code.


That's what I was thinking... there has to be a jailbreak exploit. :-)

#7 Gary from OPA OFFLINE  

Gary from OPA

    Moonsweeper

  • 414 posts
  • Location:The World Wide Web

Posted Thu Oct 7, 2010 6:19 PM

Ti-Basic is bugged and it's probably possible to create a exploit branching to ASM code.


That's what I was thinking... there has to be a jailbreak exploit. :-)


I hope not, the code was very well debugged, and you got to fool two levels, the basic code on top of the gpl code.

There is some doors opened when you insert certain modules, as they add CALLS into the basic system.

But stock TI-Basic with no modules pulled in is very air-tight.

If I can come across a possible bug, I yet you know, I got the full original source code around here someplace still.

#8 matthew180 OFFLINE  

matthew180

    River Patroller

  • 2,208 posts
  • Location:Castaic, California

Posted Thu Oct 7, 2010 6:22 PM

You would need two kinds of exploits. One to get code into scratch pad at a certain memory address, and another to branch to that code. Also, you have to do this in such a way that you don't destroy the BASIC environment, since, after all, you have less than 256 bytes to do whatever it is you need to do, and thus I'm sure at some point you will need to return to BASIC.

While it might be possible, I can't imagine it being very practical once you found the required bugs.

Matthew

#9 matthew180 OFFLINE  

matthew180

    River Patroller

  • 2,208 posts
  • Location:Castaic, California

Posted Thu Oct 7, 2010 6:24 PM

If I can come across a possible bug, I yet you know, I got the full original source code around here someplace still.


Not *THAT* I would like to see! Full source to the BASIC Interpreter. Assuming you are talking about something other than a disassembled dump? Like source with comments, variable names, etc.?

Matthew

#10 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Fri Oct 8, 2010 1:12 AM

You would need two kinds of exploits. One to get code into scratch pad at a certain memory address, and another to branch to that code. Also, you have to do this in such a way that you don't destroy the BASIC environment, since, after all, you have less than 256 bytes to do whatever it is you need to do, and thus I'm sure at some point you will need to return to BASIC.

While it might be possible, I can't imagine it being very practical once you found the required bugs.

Matthew


I don't see a pratical use either, but the geek factor is there anyway ;)
If I would do something in that direction, I would not let it return to BASIC. That way you'd have 256 bytes of scratchpad to your disposal.
Would be more than enough for doing a sprite demo :)

There is that one TI-Basic game that loads from cassette tape and uses a sprite. That was pretty clever.
It was discussed on the Y! group a while ago.


Yeah, my next game will be on cassette tape instead of cartridge. Hhmm, no not really :roll:

#11 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Fri Oct 8, 2010 1:29 AM

Below is the TI-basic game that uses a sprite.
Check the Y! thread here .

Hello everyone, i found a mistic italian game write in tibasic that use
a sprite, his name is morphy!! I never listen firts! This game work only
ti99 console and unit cassette with never expansion, but i discovered
that work also with ti99 +32K+minimemory and use first this command
CALL LOAD(31888,63,255) that seem how a call files(0).
For use with only ti99 console first you need write this short program
IN TI-BASIC:

100 call clear
110 INPUT "registro(0-7),dato(0-255)?":R,D
120 A=18429-(256*R+D)
130 X$=CHR$(0)
140 OPEN #1:"CS1",OUTPUT,FIXED
150 PRINT #1:X$&X$&X$&X$&X$&X$&CHR$(INT(A/256))&CHR$(A)

after run it, you insert this value 5,15 and enter.
The program write on cs1 a binary program (you no can list it) that his
use is a loader for the next program.

MORPHY game program

Spoiler


When ti99 read this program on cassette from cs1 on screen show strange
carachter but seem normal, and after run you have a first game in
tibasic that use a sprite with only ti99 console and cassette recorder!!!!

For use with minimory and a 32k expansion put this instruction 15 CALL
PEEKV(-753,X) in listate game and no use first program. Remember use a
special call load write first.

I like if you test this program and write a comment. Greetings at all.

********

P.s. sorry for my english :-p



#12 matthew180 OFFLINE  

matthew180

    River Patroller

  • 2,208 posts
  • Location:Castaic, California

Posted Fri Oct 8, 2010 7:39 AM

That Morphy program looks like it might be worth digging into. If they found a way to access the VDP registers, you might be able to get access to scratch pad as well.

I was thinking yesterday, and inspired by the Minimal 8-bit CPU, that if instead of trying to write a game or useful program in the 256 bytes of scratch pad, write a byte code interpreter, like a stripped down GPL. That way you could use BASIC's DATA, CHAR, HCHAR, and VCHAR statements to get the byte code program into VDP RAM, then load the interpreter into scratch pad using the exploit. The interpreter takes over and starts executing your program from VDP RAM just like GPL "runs" from GROM.

I think this would make a good challenge. 256 bytes to make a byte code interpreter with enough power to write games. The compiler should probably be a PC based program, and maybe a port to the actual 99/4A once it is working. Probably strongly typed with 3 variable types: byte, word, base-100 floating point (to use the console's routines). Let's see, what would be the minimum useful set of instructions?

load and store memory, VRAM and conventional
compare and branch
logical
arithmetic
input and output for joystick / keyboard

But even that might be too much for 256 bytes, but you never know.

Matthew

#13 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Fri Oct 8, 2010 9:20 AM

That Morphy program looks like it might be worth digging into. If they found a way to access the VDP registers, you might be able to get access to scratch pad as well.

I was thinking yesterday, and inspired by the Minimal 8-bit CPU, that if instead of trying to write a game or useful program in the 256 bytes of scratch pad, write a byte code interpreter, like a stripped down GPL. That way you could use BASIC's DATA, CHAR, HCHAR, and VCHAR statements to get the byte code program into VDP RAM, then load the interpreter into scratch pad using the exploit. The interpreter takes over and starts executing your program from VDP RAM just like GPL "runs" from GROM.

I think this would make a good challenge. 256 bytes to make a byte code interpreter with enough power to write games. The compiler should probably be a PC based program, and maybe a port to the actual 99/4A once it is working. Probably strongly typed with 3 variable types: byte, word, base-100 floating point (to use the console's routines). Let's see, what would be the minimum useful set of instructions?

load and store memory, VRAM and conventional
compare and branch
logical
arithmetic
input and output for joystick / keyboard

But even that might be too much for 256 bytes, but you never know.

Matthew


Yes, that was what I was thinking about too with the work on spectra: a "mini" interpreter that runs your (game-)programs from VDP memory.
If the interpreter would be able to "swap" part of scratchpad memory with the VDP, I'm sure you could accomplish quite a bit.
Dunno if it would be worth to tap the console routines though. Mainly because of the specific locations it requires in scratchpad memory.

Let's dig out the cassette player ;)

No better not, there's already too much on my plate. But it is tempting :cool:

#14 rocky007 OFFLINE  

rocky007

    Moonsweeper

  • 395 posts
  • Location:Belgium

Posted Sun Oct 10, 2010 2:39 AM

i checked how this tricks worked (fake header CS1), and it's really amazing. unfortunally, it's limited to the sprite table, but it prooves that everything is not locked.

I remember when i was 10 years old, i found a special sequence ( few letters ) that crash the TI99 under TI basic. It was really amazing, bec if i remember well, in crashing, i had some sprite everywhere on the screen with strange char everywhere, it was little little special effect. This sequence was based on the the special command to desactivate something in math to accelerate the TI basic.. ( #???* or something like this) sorry, i don't remember exactly

#15 bfollett ONLINE  

bfollett

    Dragonstomper

  • 506 posts

Posted Mon Oct 25, 2010 5:17 PM

This is somewhat unrelated and perhaps it's well known now, but I remember I came across a bad TI extended basic command you could issue that would drop you out of extended basic and into regular basic with your code still intact. If you listed your code you would see garbled characters for any extended basic commands, but if the code was entirely TI basic compliant it would run. I wish I remembered what the line of code was, but I remember I would use extended basic even if I was programming a standard basic program because it was faster and then I would issue the command to switch and test it under regular basic.

Bob

#16 Opry99er OFFLINE  

Opry99er

    Quadrunner

  • 7,771 posts
  • Location:Cookeville, TN

Posted Mon Oct 25, 2010 11:06 PM

Fascinating stuff....

#17 Vorticon OFFLINE  

Vorticon

    River Patroller

  • 2,524 posts
  • Location:Eagan, MN, USA

Posted Mon Nov 1, 2010 10:26 AM

You would need two kinds of exploits. One to get code into scratch pad at a certain memory address, and another to branch to that code. Also, you have to do this in such a way that you don't destroy the BASIC environment, since, after all, you have less than 256 bytes to do whatever it is you need to do, and thus I'm sure at some point you will need to return to BASIC.

While it might be possible, I can't imagine it being very practical once you found the required bugs.

Matthew


I don't see a pratical use either, but the geek factor is there anyway ;)
If I would do something in that direction, I would not let it return to BASIC. That way you'd have 256 bytes of scratchpad to your disposal.
Would be more than enough for doing a sprite demo :)

There is that one TI-Basic game that loads from cassette tape and uses a sprite. That was pretty clever.
It was discussed on the Y! group a while ago.


Yeah, my next game will be on cassette tape instead of cartridge. Hhmm, no not really :roll:


That game is called Morphy by Ermanno Alekine and is available on the TI Gameshelf site. Very cool hack. It runs perfectly fine under Win994a using the included cassette emulator.

Edited by Vorticon, Mon Nov 1, 2010 10:30 AM.


#18 kl99 OFFLINE  

kl99

    Dragonstomper

  • 603 posts
  • Location:Vienna, Austria

Posted Tue Dec 28, 2010 4:29 AM

While it might be possible, I can't imagine it being very practical once you found the required bugs.


I thought about this and the most practical use I could think of would be the possibility of saving the cartridge content to disk or to tape.

#19 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Tue Dec 28, 2010 1:19 PM

While it might be possible, I can't imagine it being very practical once you found the required bugs.


I thought about this and the most practical use I could think of would be the possibility of saving the cartridge content to disk or to tape.


Have to admit that I never dumped any cartridge ROMS/GROMS until now.

But I think these are the steps involved for dumping a cartridge ROM:

1) Write an assembly language program to dump the memory area >6000->7FFF and load this program into the high memory expansion area (e.g. >A000).
2) Poke the address of the new register workspace at memory location >FFFC
Poke the address of the assembly language program (e.g. >A000) into memory location >FFFE (vector for load-interrupt)
3) Insert the cartridge to dump into the cartridge slot
4) Trigger the load-interrupt hardware signal (*)

(*) Check Thierry's page for details.

You need a hardware switch for (4), e.g. I have a speech synthesizer with a load-interrupt switch hacked into it.

There is a problem though: when you insert a cartridge, the TI-99/4A is reset and I think that always clears high-memory.
I suppose this could be prevented by disabling the reset signal on the cartridge pcb.

Hoping that someone with hardware knowledge can chime in and give some more details :)

#20 Tursi OFFLINE  

Tursi

    River Patroller

  • 4,495 posts
  • Location:BUR

Posted Tue Dec 28, 2010 3:26 PM

Most people disabled the reset just by taping over the reset pin on the cartridge (pin 1, was it?). That way you could write your dumper, load it in Editor/Assembler, have it wait for a key, swap carts, then dump. You don't even need a LOAD interrupt then.

#21 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Wed Dec 29, 2010 1:37 AM

Most people disabled the reset just by taping over the reset pin on the cartridge (pin 1, was it?). That way you could write your dumper, load it in Editor/Assembler, have it wait for a key, swap carts, then dump. You don't even need a LOAD interrupt then.


Now that's a nice one! Just comes to mind that if you have a GRAM Kracker, it has the software built-in for dumping the cartridge. The GK sports a cartridge slot where you plugin the cartridge to dump without issuing the reset signal. Guess that's why the GK has an own reset switch.

Check here.

#22 sometimes99er OFFLINE  

sometimes99er

    River Patroller

  • 3,747 posts
  • Location:Denmark

Posted Wed Dec 29, 2010 2:44 AM

With my setup in heyday consisting of console, cassettedeck, mini memory and 32K standalone, I was able to run in high, exchange carts, copy and examine - the console didn't reset in like 1 out of 4. Also seemed to work better if the exchange was rapid.

#23 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,516 posts
  • Location:Germany

Posted Mon Oct 28, 2013 11:52 AM

Thank you Vorticon for posting this. I thought it would be wise to make a cross-post here so that it does not got lost in history

I saw this today on the Yahoo listserv, and I thought it was fascinating. Essentially the author, James Abbatiello, is able to coerce TI BASIC to run a small assembly language program. Take a look:
 
 
10 REM Escape the BASIC sandbox
20 REM by James Abbatiello <abbeyj@...>
30 REM Displays an animated Hello, World!
40 REM message using machine language.
50 REM Works even without Extended BASIC
60 REM and without the 32K memory expansion.
100 CALL CLEAR
110 PRINT "PRESS"
120 PRINT "1 FOR TI-99/4A"
130 PRINT "2 FOR TI-99/4A (alt)"
140 PRINT "3 FOR TI-99/4A V2.2"
150 CALL KEY(5,C,S)
160 IF (C < 49) + (C > 51) THEN 150
170 PRINT "INITIALIZING ..."
180 V(0)=885
190 V(1)=882
200 V(2)=846
210 L=V(C-49)
220 A$=CHR$(INT(L/256))&CHR$(L-256*INT(L/256))
230 FOR I = 1 TO 126
240 READ C
250 A$=A$&CHR$©
260 NEXT I
270 OPEN #1:A$
800 DATA 128,168,165,172,172,175,140,128
810 DATA 183,175,178,172,164,129,0,0
820 DATA 0,0,0,0,0,0,0,0
830 DATA 0,0,0,0,0,0,0,0
840 DATA 0,0,0,0,0,0,0,74
850 DATA 0,0,0,0,0,0,0,0
860 DATA 0,0,0,0,131,130,2,0
870 DATA 2,244,2,11,64,0,6,0
880 DATA 19,250,208,111,252,0,10,17
890 DATA 23,252,215,224,131,247,2,2
900 DATA 131,76,215,203,5,139,208,114
910 DATA 19,242,219,193,255,254,16,251
920 DATA 0,0,0,0,0,0,0,0
930 DATA 0,0,0,0,0,0,0,0
940 DATA 0,0,0,0,0,0,0,0
950 DATA 0,0,0,0,0,0

The first thing we need is the address of some specific code in GROM but this unfortunately varies in the different GROM versions and I have no way to detect which value is appropriate.  I resorted to asking the user.  Armed with this, we use it as the first two bytes of a long string.  Then we take this string and try to OPEN it.

For the following explanation you might want to follow along in your own copy of TI Intern.  

When we try to open this weird filename we'll end up at G>401E (Basic OPEN).  From there we'll call G>4BA1 to build a PAB.  There's a bug here but not one that I exploit.  The code takes the length of the input string and adds >0E to it to compute the length of the entire PAB and then it tries to allocate this much space.  Only one byte is used for this length so passing in a long name can cause this calculation to overflow, leading us to allocate less space than needed.  We'll end up writing past the end of the allocated space and overwriting something (probably part of the line number table).  If we ever want to return to BASIC this would be a problem.  The string I use is not long enough to trigger this bug.

Eventually we'll try to actually open this file by calling G>03D9 (DSRLNK).  This is expecting a string like "DSK1" or "DSK1.FOO" and it wants to find the first period (if any) and then treat the part to the left as the device name.  It will copy this device name into the FAC (at >834A) but only if it fits.  First it looks for a period and stores its index (or the length of the entire string if there is no period) in >8355.  It then uses "CGE @>8355,>08" to check the length and make sure it is 7 bytes or less.  However this doesn't work properly for long strings because it is a signed comparison.  So if you have a string of length >80 or longer it will still pass this check.  Then the code uses "MOVE @>8354 TO @>834A FROM VDP*>8356" to try to copy the device name into the FAC and conveniently copies the entire string, blindly overwriting whatever was there.  This is nice for us because it copies our string unmodified right into the scratchpad.

Let's take a closer look at that string.  Here's a hex dump along with the addresses that it will end up being copied to:

Addr    Offset 
834A    0000    03 ?? 80 A8 A5 AC AC AF
8352    0008    8C 80 B7 AF B2 AC A4 81
835A    0010    00 00 00 00 00 00 00 00
8362    0018    00 00 00 00 00 00 00 00
836A    0020    00 00 00 00 00 00 00 00
8372    0028    00 4A 00 00 00 00 00 00
837A    0030    00 00 00 00 00 00 83 82 
8382    0038    02 00 02 F4 02 0B 40 00
838A    0040    06 00 13 FA D0 6F FC 00
8392    0048    0A 11 17 FC D7 E0 83 F7
839A    0050    02 02 83 4C D7 CB 05 8B
83A2    0058    D0 72 13 F2 DB C1 FF FE
83AA    0060    10 FB 00 00 00 00 00 00
83B2    0068    00 00 00 00 00 00 00 00
83BA    0070    00 00 00 00 00 00 00 00
83C2    0078    00 00 00 00 00 00 00 00


0000-0001 is the address in GROM.  It can be >0375 (Classic99 and my actual hardware), >0372 (as in TI Intern), or >034E (V2.2).

0002-000F is the string " HELLO, WORLD!" with a leading space and the >60 BASIC screen bias.

0029 is >4A which will overwrite the GPL substack pointer at >8373

0036-0037 contains the address of the entry point of our program (>8382).

0038-0061 contains the actual program:
ENTRY   LI R0,756             768 - len(" HELLO, WORLD!")
        LI R11,>4000          VDP write address
WSTR    DEC R0                at end of screen?
        JEQ ENTRY             if so, start over at top
WAITVBL MOVB @->400(R15),R1   wait for vblank
        SLA R1,1
        JNC WAITVBL
        MOVB @>83F7,*R15      low byte of VDP write address
        LI R2,>834C           address of " HELLO, WORLD!"
        MOVB R11,*R15         high byte of VDP write address
        INC R11
WCHAR   MOVB *R2+,R1          load byte of string
        JEQ WSTR              done when we hit NUL terminator
        MOVB R1,@-2(R15)      write byte to screen
        JMP WCHAR


The rest of the bytes are just filler/padding.  We end up overwriting quite a lot of stuff in the scratchpad including R0 through R4 in the interrupt workspace.

After copying the string to the scratchpad the GPL will continue running until it hits RTN or RTNC.  The interrupt handler might execute between any of these instructions and update the timer at >8379 or the VDP status byte at >837B so we have to be careful not to rely on having anything in those locations.  This bit me at first and I it took a while to figure out why things would work properly one time and not the next.

At this point the RTN will pop a GROM address off the GPL subroutine stack.  We've set that pointer to >4A so the address will be fetched from >834A.  Hopefully we've managed to arrange for that to point at these GPL instructions:
    DST @>8300,@>8380  Fetch address in >8300 (for XML)
    XML >F0            and execute
This copies our start address from >8380 to >8300 and then calls XML >F0 which jumps to an address stored in >8300.  And we're off and running!
 
I don't suppose anybody wants to try their hand at making a more interesting payload for this?

-- 
James Abbatiello



#24 Tursi OFFLINE  

Tursi

    River Patroller

  • 4,495 posts
  • Location:BUR

Posted Mon Oct 28, 2013 3:37 PM

I was thinking about this and wondering how it might be turned into something bigger...

 

On a TI BASIC system with 32k expansion available, you could use the scratchpad code as a bootstrap to load assembly into 32k and branch to it. I expect you could load a fair bit of assembly into character definitions.. strings might be possible but you'd need to be able to find them in VDP (taking into account disk systems as well, ideally, so they may move. Perhaps searching with a tag... hell. Maybe even storing it in comments.

 

But what could you do without 32k? Is there enough space in the over-writable scratchpad that if you loaded a bunch of assembly into VDP, you could maybe page "blocks" through scratchpad to execute?

 

Fun challenge, maybe. :)



#25 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 775 posts
  • Location:Lansing, NY, USA

Posted Mon Oct 28, 2013 4:29 PM

I was thinking about this and wondering how it might be turned into something bigger...

 

On a TI BASIC system with 32k expansion available, you could use the scratchpad code as a bootstrap to load assembly into 32k and branch to it. I expect you could load a fair bit of assembly into character definitions.. strings might be possible but you'd need to be able to find them in VDP (taking into account disk systems as well, ideally, so they may move. Perhaps searching with a tag... hell. Maybe even storing it in comments.

 

But what could you do without 32k? Is there enough space in the over-writable scratchpad that if you loaded a bunch of assembly into VDP, you could maybe page "blocks" through scratchpad to execute?

 

Fun challenge, maybe. :)

DATA statements would be a good place to store the code.  You can fit about 160 bytes into a DATA statement..How about loading a short program that stashes the contents of the scratchpad from >8300 to >83BF to a buffer in the VDP. Now you have 192 bytes to play with, 32 more if you use the workspace at >8C00 . Then as Tursi suggests, you could page blocks of code through that part of the scratchpad as needed.  You'd need something like VMBR fetch the pages.  When finished with assembly restore the scratchpad and return to BASIC.    This is a really interesting challenge!  I have a couple of ideas that I may try out.






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users