Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

1,146 Excellent

About speccery

  • Rank

Recent Profile Visitors

4,762 profile views
  1. I’ve created several “true” 16 bit versions of the TI99/4A in my FPGA designs. The 99105 CPU based version was my first venture into this direction - RAM, ROM, cartridge memory all are 16-bit wide in that design. It is about 7 times faster than the TMS9900 based normal computer, but as I wanted to be able run existing software the video subsystem still is 9918-like and using a 8-bit bus, as are the GROMs. Thus going 16-bit would have gained you more speed. But arguably a much bigger speed boost would have been reached by just dropping the GROMs and GPL stuff altogether and using bigger normal ROMs and machine code for the Basic etc. Dropping the GROMs would have destroyed their closed ecosystem model and increasing size of normal ROM could have increased the cost of the system (I assume the GROMs at least were cheap for TI). The plain vanilla 9900 is a pretty slow CPU even over 16 bit bus. Having said that I do wish they had provided a bit more 16-bit wide RAM. For instance using four 2114 static RAMs (also used in the VIC20 around the same time) would have provided a whopping 2 kilobytes of 16-bit wide RAM. Like @matthew180 and others said, the other decisions they made were the ones which killed the system, not crippling the system to access a lot of its memory and peripherals over an 8 bit bus, although that certainly did not help.
  2. @Madhatter1 this is a great project. Thank you for sharing your design! I see that you wrote the code in AVR assembly I played around with AVR assembly code when the 20-pin AT2313 were a thing, but that must have been more than a decade ago. I've been working on my ET-PEB project but not too much recently, one of the things making development slow is/was that in that design I needed to write the whole DSR from scratch, as it was not trying to emulate the original drive. Your approach is better in that you're using the original DSR. I'm tempted to see if I could implement your design using the ET-PEB hardware (reprogramming the CPLD and the MCU), or just make another board design altogether. Being me, if I were to do a board design, I would probably put all or most of the logic into a CPLD and add a RAM chip to also have memory expansion on the same board. That Are you planning to release the source code for the utilities? Just asking as a Mac user, could compile those for Mac or Linux.
  3. 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.
  4. Sorry my bad, wrong on all accounts - I was thinking the @jedimatt42 board and thought it was done by you.
  5. 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.
  6. I wrote it from scratch, that's much more interesting to me ... and all bugs are on me...
  7. 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
  8. 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.
  9. My implementation of TI BASIC clone continues. I've added some static code analysis to my interpreter, giving me this output: 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.
  10. Thanks @FarmerPotato I was not aware of this patent. I will take a look, should be interesting. Speaking of patents, I'm co-inventor in five patent families, but that was a while ago
  11. 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.
  12. 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
  13. 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.
  14. This is an interesting thread despite venturing off-topic quite a bit... As background, I have now started to work on a Basic interpreter for my StrangeCart project, and as part of that dived into the way basic stores the programs. I posted a video of the current result of my work - it enables now listing of Basic programs with the StrangeCart processor. The StrangeCart also makes it possible to override the console GROM content, so it would be possible to modify how the GPL code works there. But to my point: the crunched basic code uses the token 0xC9 to describe line numbers. The token C9 is followed by two bytes, containing the line number as 16-bit integer. These line numbers are then searched for in the line number table, as has been discussed in depth in this thread. An idea from ZX Spectrum Basic could be borrowed here - I believe it stores pointers to destination addresses in the basic program code. Thus TI Basic could be speeded up simply for example this way: when during program execution the token C9 and a linenumber is found, the search during line number table occurs as usual. Once the target is found in the line number table, the token C9 could be replaced with another token, let's call it FA, followed by a pointer to the line number table of the destination line. Thus there would not be a need to search for the destination line address anymore, as it can be retrieved from the line number table. This would take the same amount of space as the C9+line number, but the searches would only take place once. After that all goto/gosubs would execute with the same (maximum) speed. it would still be possible to list the destination linenumber, since the pointer to line number table enables that. Of course, before subsequent editing is done, a pass through the stored code would be needed to replace the FA tokens with C9 again, since the pointers would get broken whenever the program or line number table changes.
  15. 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.
  • Create New...