Jump to content

Recommended Posts

10 minutes ago, OLD CS1 said:

You made the explicit comparison.

MINIMEM, EXPMEM1, EXPMEM2, for instance. Then there is RS232, ALPHON, SPEECH, CLOCK, &c., not seven letters but more than three.

And how many people use these 7 letter names vs how many use the 3 letter names with number?

 

Do you have a issue with backwards compatibility?

Share this post


Link to post
Share on other sites
2 hours ago, RXB said:

And how many people use these 7 letter names vs how many use the 3 letter names with number?

 

Do you have a issue with backwards compatibility?

Our failure to communicate should not continue to pull this thread further off-topic.

  • Like 3

Share this post


Link to post
Share on other sites
8 hours ago, OLD CS1 said:

You made the explicit comparison.

MINIMEM, EXPMEM1, EXPMEM2, for instance. Then there is RS232, ALPHON, SPEECH, CLOCK, &c., not seven letters but more than three.

Don't forget TP, as that one is only two. . .but I definitely like the thought of using STRANGE as the device name pointer, as it is just appropriate, somehow. TI device name lengths have definitely been all over the map. . .

  • Like 2
  • Thanks 2

Share this post


Link to post
Share on other sites

STRANGE is certainly more than appropriate for the device name.
DRSTRNG... because it's all magic!
ARMCPU?

  • Like 2

Share this post


Link to post
Share on other sites

I posted a quick video on Youtube on accelerating BASIC listing with the StrangeCart. This is the result of a bit of work to integrate the StrangeCart into TI BASIC.

 

To say it shortly, it use the processing power of the StrangeCart to speed up the listing of a Basic program. The listing is produced at 14 times of the speed of TI BASIC list command. According to my measurement, during the listing the ARM core is spending 99.7% of its time waiting for the TMS9900 to update the screen. This measurement is a bit inaccurate, since I used a timer interrupt ticking at 10kHz, and at that low frequency it causes aliasing of the measurement given the speed of the ARM. But the point is that it's much faster :) 

 

And with that, I now finally have the first working pipeline to go from TI BASIC to ARM C/C++ (and back) while getting the output on the TI's screen. In this instance the ARM core is not generating TMS9900 machine code on the fly, but rather the machine code is simply part of a modified version of the mini memory cartridge. I had to learn a bit of GPL to get this far.

 

And now, the next step is pretty obvious - get the ARM to run the Basic program :) 

 

I am using the Lasergate TI BASIC game by the user @tibasic as test material.

 

  • Like 4
  • Thanks 1

Share this post


Link to post
Share on other sites

I've had a bit of good fun working on the Basic interpreter for StrangeCart.

 

My workflow is like this: I am implementing my TI Basic clone as a C++ module. I am developing it on my Mac, and the same module can be compiled and linked into the StrangeCart firmware. This is how I developed the Basic list command presented in the video I linked. The benefit of developing the algorithms on the Mac is that I can use Xcode and all the debugging tools in it. This is much more productive and faster than working in the embedded software world of StrangeCart, without having to flash the MCU all the time.

 

So I am using @tibasic's Lasergate as a test game. I can load the binary into my interpreter both on the StrangeCart and on the Mac. On the StrangeCart this is done with "SAVE STRANGE.LASER" after loading the game from disk. On the Mac I have a wrapper program which loads Lasergate from disk into the interpreter module. On both cases my Basic interpreter is interpreting the binary of produced by the SAVE command, so it is unmodified Basic binary. There is some extra work here, since both the Mac and StrangeCart are little endian machines, while the TMS9900 is big endian, so there is a whole lot of byte swapping going on. Also pointers on the Mac are 64-bit, on the StrangeCart 32-bit and on TI-99/4A 16-bit, so one needs to be careful with these. But with a high level language like C++, you just need to get it right once.

 

I am working on this in an iteratetive manner, basically so that I let the interpreter start running the program. It will then decode the tokens. Since I only have the beginning of a Basic interpreter, the interpreter will almost immediately halt to an error, when it finds code it cannot interpret yet. That way I get constant instant gratification as the interpreter can run Lasergate a bit more each time I add functionality into the interpreter. 

 

So currently the interpreter halts when it gets to line 1420. This line has the statement "DIM A$(16)", and it can't handle it yet, since it does not know how allocate an array, nor how to handle strings. But in the few hours I have put into this after gaining the capability to LIST the program, I have added preliminary support for:

  • REM (this is easy :) )
  • CALL SCREEN / COLOR / CLEAR
  • FOR .. TO .. STEP and NEXT
  • GOSUB

Before getting to line 1420, the game has already for example executed a for loop, and the interpreter is doing it properly.

 

There is quite a bit of underlying code to handle quoted and unquoted string tokens, capture variable names, allocate symbols, search through symbols, store numeric values as floating point entities, find lines by number, implement 10-level stack for nested for-next loops as well as a separate stack for gosub-return (no return yet though). I would say that a portion of the code is of what I call industrial strength, while a perhaps half of it is still a bit hacky since I'm only working with Lasergate as the test program. It is pretty amazing that I have not had to write a proper expression evaluator yet, since so far (at line 1420) the program is only assigning constants or fetching variables. Once I hit the first real expression in the game's code, I will do a quick-and-dirty recursive descent parser for floating point values.

 

Even if I'm doing development on a Mac, I am only using a subset of C++ and for example not doing any dynamic memory allocation or exception handling with C++, but rather using my own small heap (currently set to 512 bytes) where memory allocation occurs. Also on the Mac I am not writing implementations for the aforementioned CALL functions, since they only really make sense when running on the StrangeCart plugged into the TI. Thus once the Mac version can interpret Lasergate, after I recompile the code for the StrangeCart I still need to write the implementations for the TI specific Basic calls. But this is going to be simple, since I am already doing the hard part of evaluating the expressions at that point.

 

Anyway I am quite enjoying this incremental approach, it is very gratifying to be able to (seemingly) correctly handle line after line. I am also discovering that there is a ton of error checking the interpreter needs to be doing all the time... Luckily a lot of that is very simple, and not a big burden for the CPU. I will be very interested in seeing what performance I can reach with the first version. I estimate that I will have around 128K of RAM as working space available for Basic programs once done with this.

 

  • Like 5

Share this post


Link to post
Share on other sites

RXB source for GPL and ROMs have source for how ARRAYS work in XB, but they are exactly the same in TI Basic.

The only real difference is TI Basic does not have enough memory for use of 7 Dimensional Arrays.

Matter of fact not even 5 Dimensional Arrays would work in TI Basic and still make a TI Basic program fit into the 12K of VDP.

RXB 202E.zip

  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, RXB said:

RXB source for GPL and ROMs have source for how ARRAYS work in XB, but they are exactly the same in TI Basic.

The only real difference is TI Basic does not have enough memory for use of 7 Dimensional Arrays.

Matter of fact not even 5 Dimensional Arrays would work in TI Basic and still make a TI Basic program fit into the 12K of VDP.

RXB 202E.zip 14.75 MB · 2 downloads

Thanks @RXB I will probably use this as reference material. I'm currently just trying to do regular TI Basic, not extended Basic, at least yet. I want to see how far I get with TI Basic first.

 

I am trying to keep StrangeCart a fun project, and building the Basic interpreter is certainly a fun part. I spent a lot of time working with the low level software of the microcontroller - that has nothing to do with the TI and it is interesting in its own way, but when something is not working it's pretty labour intensive to find out what's wrong.

 

A quick question: where does extended Basic store the line number table? With TI Basic it's "simple" (and slow), since everything is in VDP space. But extended Basic can use memory expansion, and also work without it... I assume the line number table is still in VDP memory, even if the code is in processor memory, but I might be wrong. My C++ implementation is greatly simplified in that I only have one memory address space to work with :)

 

  • Like 1

Share this post


Link to post
Share on other sites
11 hours ago, speccery said:

Thanks @RXB I will probably use this as reference material. I'm currently just trying to do regular TI Basic, not extended Basic, at least yet. I want to see how far I get with TI Basic first.

 

I am trying to keep StrangeCart a fun project, and building the Basic interpreter is certainly a fun part. I spent a lot of time working with the low level software of the microcontroller - that has nothing to do with the TI and it is interesting in its own way, but when something is not working it's pretty labour intensive to find out what's wrong.

 

A quick question: where does extended Basic store the line number table? With TI Basic it's "simple" (and slow), since everything is in VDP space. But extended Basic can use memory expansion, and also work without it... I assume the line number table is still in VDP memory, even if the code is in processor memory, but I might be wrong. My C++ implementation is greatly simplified in that I only have one memory address space to work with :)

 

XB works exactly like TI BASIC but if you have a 32K it moves the line number table from VDP to upper 24K.

Look in GPL Source SRXB4 for OLDZ5 this is where the table is moved from VDP to RAM.

Also you will see RXB unlike normal XB the location CPUBAS is changed so you can modify the location of XB programs reside using my CALL PRAM command

that lets you pick where XB programs reside in RAM. This even allows multiple XB programs in memory as to run one just pick the locations to run.

You can run 20 small XB programs all from 32K RAM as all are loaded. XB programs as small as single lines of XB Programs.

 

Anyway hopes this helps.

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, RXB said:

XB works exactly like TI BASIC but if you have a 32K it moves the line number table from VDP to upper 24K.

Look in GPL Source SRXB4 for OLDZ5 this is where the table is moved from VDP to RAM.

Also you will see RXB unlike normal XB the location CPUBAS is changed so you can modify the location of XB programs reside using my CALL PRAM command

that lets you pick where XB programs reside in RAM. This even allows multiple XB programs in memory as to run one just pick the locations to run.

You can run 20 small XB programs all from 32K RAM as all are loaded. XB programs as small as single lines of XB Programs.

 

Anyway hopes this helps.

Thank you for the quick response and the pointer - I found the code you mentioned. Interesting that XB has quite a bit flexibility - having 32K seems to enable a fair amount of alternate code paths.

I think it is helpful.

The feature of being able to select the XB program location is interesting, and gives a lot of flexibility. If you change CPUBAS, I guess the user must know what he/she is doing, as it probably would be easy to overwrite other programs unless you're careful - I assume there can't be too much protection in place.

Share this post


Link to post
Share on other sites
2 hours ago, speccery said:

Thank you for the quick response and the pointer - I found the code you mentioned. Interesting that XB has quite a bit flexibility - having 32K seems to enable a fair amount of alternate code paths.

I think it is helpful.

The feature of being able to select the XB program location is interesting, and gives a lot of flexibility. If you change CPUBAS, I guess the user must know what he/she is doing, as it probably would be easy to overwrite other programs unless you're careful - I assume there can't be too much protection in place.

Yea I have to do a few demos to show it working with multiple small XB programs:

Rich Gilbertson - YouTube

  • Like 3

Share this post


Link to post
Share on other sites

My implementation of TI BASIC clone continues. I've added some static code analysis to my interpreter, giving me this output:

Spoiler

All call destinations:
SOUND : 5
HCHAR : 7
VCHAR : 3
KEY : 2
CHAR : 4
COLOR : 7
SCREEN : 2
CLEAR : 8
Used tokens:
0x81 used   7 times ELSE            done
0x84 used  17 times IF              done
0x86 used   8 times GOTO            done
0x87 used   2 times GOSUB           done
0x88 used   2 times RETURN          done
0x8A used   2 times DIM             done
0x8B used   2 times END             not implemented
0x8C used   8 times FOR             done
0x92 used   2 times INPUT           done
0x95 used   2 times RANDOMIZE       done
0x96 used   8 times NEXT            done
0x9A used  19 times REM             done
0x9C used  29 times PRINT           done
0x9D used  39 times CALL            done
0xB0 used  17 times THEN            done
0xB1 used   8 times TO              done
0xB2 used   4 times STEP            done
0xB3 used  74 times ,               done
0xB4 used   8 times ;               done
0xB5 used  40 times :               done
0xB6 used  72 times )               done
0xB7 used  72 times (               done
0xBE used  72 times =               done
0xBF used   7 times <               done
0xC0 used   7 times >               done
0xC1 used  46 times +               done
0xC2 used  33 times -               done
0xC3 used  21 times *               done
0xC4 used   2 times /               done
0xC7 used  50 times quoted string   done
0xC8 used 199 times unq string      done
0xC9 used  30 times line num        done
0xCB used   3 times ABS             not implemented
0xCF used   7 times INT             done
0xD7 used   3 times RND             done
0xDC used   3 times ASC             not implemented

 

In the "call destinations" above I am calculating a histogram of the number of calls to different subprograms.

 

That, and the used tokens table with appearance counts, is a sort of static analysis of the @tibasic's program LASERGATE. The done / not implemented is a listing of all tokens I am already supporting vs. the ones I am not support yet. This represents the breadth of my Basic implementation, since I have implemented TI BASIC in "lightweight" c++ code as required by LASERGATE to run. The tokens I have not implemented yet are on code paths the game does not reach yet while running under my interpreter. The reason why it doesn't get there is that my implementation (under Mac OS with Xcode) implements CALL KEY(..) subprogram so that a button is never pressed, and the code thus cannot get everywhere since user input is not recognised. 

 

I'm also currently issuing a warning when a variable is used before it is initialised (this happens with the variable X2 on line 530) - the variable is created at that moment and assigned to zero.

 

I believe I have implemented now a full-blown expression evaluator for numeric expressions. It is only lacking some functions (such as ABS listed in the table above) but those should all be trivial to implement. For string expressions the expression evaluator is not there: the basic support strings and assigns them to variables (including arrays), but since LASERGATE does not do string manipulation that is not supported yet.  Of course values form string variables can also be fetched.

 

I have setup two separate memory pools (heaps): one for symbols and another one for strings. I'm currently keeping their sizes small, to see that the system functions when memory is very scarce. The symbol pool is 768 bytes and string pool 1024 bytes for LASERGATE to run. The string pool works pretty much the same was as in the original TI Basic as far as I know:all strings encountered are stored there and the string entries contain pointers to symbol table for strings associated with variables. The string pool also contains temporary strings (such as ones generated with PRINT "HELLO") which are not associated with a variable. The string pool supports compacting (garbage collection) when string space is exhausting.

 

It's been kind of fun to see how the string heap fills up while debugging the code. Since I am developing in Mac OS, the pointers to symbols are 64-bit entities and thus consume 8 bytes. In my system a 1-byte string consumes in the string pool the following memory:

- symbol pointer (8 bytes), zero for temporary strings

- string length (1 byte)

- the actual payload, for a 1-byte length string this is one byte

- a terminating zero (1 byte) to keep the strings compatible with C library functions

 

All of that is 11 bytes. Now, since on the ARM pointers are 32-bits and must be naturally aligned (i.e. 32 bit quantities need to be aligned on 32-bit boundaries in memory, similar to TMS9900 which requires 16-bit entities at 16-bit aligned addresses (even addresses)) I am aligning all entities with pointer sized alignment. This is done even on the Mac OS where alignment is not needed since the CPU can fetch unaligned entities, and thus for Mac OS the entries must be 8 byte alignment. And with that, the 11 bytes becomes 16 bytes after padding. On the ARM this would be 8 bytes, and on the TMS9900 (if this basic was ported to it) 6 bytes. Thus with my 1024 byte string pool not too many strings can be stored :) Of course I could make the string pool for example 8 gigs on the Mac, but that would not really expose bugs with garbage collection...

 

It's been a fun project so far :) and soon at a point I will port the code back to StrangeCart to get TI Basic a well deserved speed boost.

 

 

Edited by speccery
  • Like 8

Share this post


Link to post
Share on other sites

I've now gotten to a point where I've been able to run LASERGATE on the StrangeCart for the first time. I think it is supporting enough of TI Basic to run it correctly, although without sound (CALL SOUND is implemented but does not propagate yet to sound chip). I have now implemented all TI Basic CALLs so that they actually show display output and capture key presses on the real iron. Also PRINT and INPUT statements work with some limitations. CALL GCHAR is still missing but is trivial to add. I've been also testing with some other programs - such as SKI99 - but found bugs in my Basic implementation and some unimplemented features, which will be simple to add.

 

In order to be able to play LASERGATE I had to add a dummy for loop to slow down the gameplay to somewhat manageable levels. I'm pleased to say that the amount of loop iterations is currently at 50 000 to slow down the main loop to reasonable speeds. Basically at line 1170 of LASERGATE there is normally a CALL CLEAR to prepare for next game loop iterations. I did this change to add the delay loop before CALL CLEAR:

1170 FOR I=0 TO 50000
1172 NEXT I
1176 CALL CLEAR

And voila, now the game is playable and I dare say more enjoyable than with normal TI Basic, since the delay before CALL CLEAR causes the graphics to be visible for a bit before wiping the screen.

 

I also ran the the speed test which was presented in Noel's Retro Lab Youtube channel (and I don't right now remember his AA handle). On the real iron it takes 78 seconds. Well, just save it to the StrangeCart with SAVE STRANGE.S, and issue CALL RUN(0) to have it executed by the StrangeCart. And the runtime just became a bit shorter - just less than 0.25s :) Bear in mind that this is still an interpreted Basic, nothing is compiled or preprocessed. Everything is calculated in floating point. I am having a hard time from keeping myself from going into full code optimisation mode before the whole Basic is implemented... Even in the current unoptimised state the FOR..NEXT loops in interpreted Basic run faster than what the TMS9900 can do in machine code with 16-bit integers, while the StrangeCart is doing floating point loop variable, step and limit...

I need to make a video of this, it is pretty fast even if I say so myself.

Edited by speccery
  • Like 11

Share this post


Link to post
Share on other sites
25 minutes ago, dhe said:

Any chance of getting some boards out to the community soon?

Yes I think I have gained now enough confidence that the board works, at least on my specific computers. There's of course still work with the firmware, but that will remain the case for a while. I will start building them gradually and then sending the first ones out for further testing with the community, including you :)

 

  • Like 3

Share this post


Link to post
Share on other sites
On 8/31/2021 at 3:42 PM, speccery said:

I've now gotten to a point where I've been able to run LASERGATE on the StrangeCart for the first time. I think it is supporting enough of TI Basic to run it correctly, although without sound (CALL SOUND is implemented but does not propagate yet to sound chip). I have now implemented all TI Basic CALLs so that they actually show display output and capture key presses on the real iron. Also PRINT and INPUT statements work with some limitations. CALL GCHAR is still missing but is trivial to add. I've been also testing with some other programs - such as SKI99 - but found bugs in my Basic implementation and some unimplemented features, which will be simple to add.

 

In order to be able to play LASERGATE I had to add a dummy for loop to slow down the gameplay to somewhat manageable levels. I'm pleased to say that the amount of loop iterations is currently at 50 000 to slow down the main loop to reasonable speeds. Basically at line 1170 of LASERGATE there is normally a CALL CLEAR to prepare for next game loop iterations. I did this change to add the delay loop before CALL CLEAR:


1170 FOR I=0 TO 50000
1172 NEXT I
1176 CALL CLEAR

And voila, now the game is playable and I dare say more enjoyable than with normal TI Basic, since the delay before CALL CLEAR causes the graphics to be visible for a bit before wiping the screen.

 

I also ran the the speed test which was presented in Noel's Retro Lab Youtube channel (and I don't right now remember his AA handle). On the real iron it takes 78 seconds. Well, just save it to the StrangeCart with SAVE STRANGE.S, and issue CALL RUN(0) to have it executed by the StrangeCart. And the runtime just became a bit shorter - just less than 0.25s :) Bear in mind that this is still an interpreted Basic, nothing is compiled or preprocessed. Everything is calculated in floating point. I am having a hard time from keeping myself from going into full code optimisation mode before the whole Basic is implemented... Even in the current unoptimised state the FOR..NEXT loops in interpreted Basic run faster than what the TMS9900 can do in machine code with 16-bit integers, while the StrangeCart is doing floating point loop variable, step and limit...

I need to make a video of this, it is pretty fast even if I say so myself.

This is extremely cool to me.

Did you write the BASIC interpreter from scratch or did you start with something like Chipmunk BASIC for a template?

  • Like 3

Share this post


Link to post
Share on other sites
5 hours ago, TheBF said:

This is extremely cool to me.

Did you write the BASIC interpreter from scratch or did you start with something like Chipmunk BASIC for a template?

I wrote it from scratch, that's much more interesting to me :)  ... and all bugs are on me...

  • Like 8
  • Thanks 1

Share this post


Link to post
Share on other sites
9 hours ago, James Wilson said:

would it be possible to add a usb keyboard interface on to this cart?   It may make it easier to get keyboard input fast enough to play games etc.

That's an interesting idea, but I'm not planning to make major changes to the present version of the board. The LPC54114 MCU I am using does not have USB host capability (it does have USB Device capability which I'm using a VCOM serial port), so adding a USB host interface would require an additional chip. The other issue is that even if it had USB host, it would not be able to inject the keyboard data in a standard way, since that's handled by the TMS9901 chip on the motherboard. The CRU bus used for communicating with the TMS9901 is available on the cartridge slot, so forcibly overriding the data might be possible, but not good practice and might damage the TMS9901. 

 

The strangecart uses the TMS9900 code for most I/O, so the normal keyboard will work, with the usual performance :)

 

There is a USB keyboard adapter design by @Tursi , have you looked at it? It's using a USB host chip (on a breakout board) and connects to the standard keyboard connector as I recall. I actually did order that board, but haven't had time to play with it.

  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, speccery said:

The CRU bus used for communicating with the TMS9901 is available on the cartridge slot, so forcibly overriding the data might be possible, but not good practice and might damage the TMS9901. 

Also, this bus is not available to the QI's GROM port.

  • Like 2

Share this post


Link to post
Share on other sites
14 hours ago, speccery said:

There is a USB keyboard adapter design by @Tursi , have you looked at it? It's using a USB host chip (on a breakout board) and connects to the standard keyboard connector as I recall. I actually did order that board, but haven't had time to play with it.

No, my board only does PS/2, uses an AVR, and has a dedicated PCB. ;)

 

  • Like 2

Share this post


Link to post
Share on other sites
4 hours ago, Tursi said:

No, my board only does PS/2, uses an AVR, and has a dedicated PCB. ;)

 

Sorry my bad, wrong on all accounts :(  - I was thinking the @jedimatt42 board and thought it was done by you. 

Edited by speccery
  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, jedimatt42 said:

Looks like one of my hosting sites in down :( but the resources for the USB keyboard are here: 

 

https://www.jedimatt42.com/ti99usbkeys.html

 

and here: https://github.com/jedimatt42/TI-99-usb-keys

Thanks for posting the links - and sorry for misremembering who did the project... Nice project, all credits to you :)  I actually have in my drawer two of these USB host boards waiting to go into a TI. I don't have a spare Teensy lying a round, but I have a ton of other microcontroller boards which would work for this purpose. Including rewiring one for the first strangeCart prototypes for this purpose to replace the Teensy.

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...