A long while ago, I made an interpreter for a custom Turtle language in Hybrid BASIC for the 84+CSE. I have long wanted to port this to the CE using C. In fact, that was the impetus to turtle library diving the Spirographic Generative Art program.

Well, I started working on it. As with the previous version, parameters are evaluated using the operation system's eval, so all math and variables are supported.

You can find the readme in the github repo with more info on commands:
https://github.com/merthsoft/tugace/blob/main/readme.md

Still a work in progress. No release availble yet.

Here are some screenshots to show off what it's capable of so far.

A spiral:


Code: https://github.com/merthsoft/tugace/blob/main/programs/SPIRAL.txt

A more advanced spiral, using the OS variables:

With different inputs:


Code: https://github.com/merthsoft/tugace/blob/main/programs/SPIRAL2.txt


A tree, showing off the ability to push and pop the turtle state onto a stack at will:


Code: https://github.com/merthsoft/tugace/blob/main/programs/TREE.txt

I'm current working on GOSUB and RET for TREEFREC to work:
https://github.com/merthsoft/tugace/blob/main/programs/TREEFRAC.txt
So cool! The graphical demonstrations are very nice.

I think it was a great idea to use the calculators ability to eval and such, lots of flexibility!
Yeah, makes it a lot easier for me, too, because then I don't need to worry about variables and such. And all commands can take lists, where it will use the first X elements where X is the number of params. So FORWARD L1 is the same as FORWARD L1(1 and GOTO L1 is the same as GOTO {L1(1),L1(2). Additionally, complexes are accepted, but the imaginary value is dropped. So you can do wild imaginary stuff if you want.

GOTO and LABEL actually work the same way. LABEL A doesn't create a label called A. It creates assigns the label number that A evaluates to. So if A is 13.2, LABEL A creates LABEL 13 at that spot. Labels only support real and complex values right now, not lists, so a small limitation. But I suspect I'll combine some of that code to make it easier and then that won't be the case.

I'm curious how people feel about this part of the spec:
https://github.com/merthsoft/tugace/blob/main/readme.md#additional-shorthands

Then programs could be written more tersely. E.g. SPIRAL would look like:

Code:
TURT:Return
"DRAWS A SPIRAL
R0
P1
0F
:1
?F/4=iPart(F/4
CrandInt(0,255
^F
(92
+F
.1


Which is equivalent to:

Code:
TURT:Return
"DRAWS A SPIRAL
CLEAR 0
PEN 1
ZERO F
LABEL 1
IF F/4=iPart(F/4
COLOR randInt(0,255
FORWARD F
LEFT 92
INC F
GOTO 1


No matter what I'm keeping the colon for label definitions just because it makes them stand out and therefore more readable. The programmer could of course pick which shorthands they prefer to use. It would see a small performance increase.

Some additional design thoughts I had:

- I want to support using colon as a line delimiter. This is easy enough in the code. I just need to then
choose a different shorthand for LABEL. Perhaps pi.
- Right now, the interpreter is a big switch statement on non-contigurous values which are the commands hashed. This performs ok because it can be optimized to a binary search. Shorthands are turned into their equivalent hash.
- This will be changed to be a switch on contiguous values to make use of potentially a jump table. To that end, shorthands will be converted to their opcodes, as well as hashes, and then the switch will drive that.
- Given that change, I have planned a pre-processor pass which will pre-cache labels (where static) and tokenize the commands to their opcode value, thus the translation doeasn't need to happen on the fly and performance will improve.
- Tokenized commands will include pre-computed versions where operands have already been evaluated. This is appropriate if it's a constant value rather than a variable

Stack thoughts:
- There will be a system stack for GOSUB, RET, and any other commands that need a stack
- This is STACK -1 in terms of stack commands, you can access it as a programmer and do nasty things so beware
- The idea is to have a stack per turtle. You choose which stack is active, though, so it's up to you if you follow convention.
- It's one contiguous block of memory, one stack after the other. Right now you can't bleed over to other stacks, but It hink I will change that. So it's more like stack pages than separate stacks.

Header/launcher
- Soon it will check for program name in Ans and run that program. This will allow the Tuga programs to launch TUGA itself from the header.
- If it's launched without a valid program name in Ans, it'll have a little shell to select program, and only list them if they have the header "TUGA:"
- The first comment in the program (the second line after the header) is a description of the program. Up to you what you put.
- After the standard header will be an optional second comment which serves as the flags comment. This is how you will tun on the pre-processor and possibly various debugging flags and such
I finished GOSUB and RET, and added FADEIN and FADEOUT. Also, when done, you can press enter to restart from the top.


Code:
TURT:Return
"Draws a tree fractal, by Shaun McFall
FADEOUT 2
COLOR 126
PEN 0
MOVE {160,200
STO {I,80
STO {G,5
CLEAR 0
FADEIN 10
PEN 1
COLOR randInt(135,255
FORWARD I
STACK 3
PUSHVEC
PUSH 0
PUSH 0
,0
STACK 1
PUSHVEC
STO {I,I/1.5
LEFT 60
COLOR randInt(135,255
FORWARD I
STACK 3
PUSHVEC
PUSH G
PUSH I
STACK 1
POPVEC
RIGHT 60
COLOR randInt(135,255
FORWARD I
DEC G
IF G<=0
GOSUB 1
.0
,1
STACK 3
POP
POP
POPVEC
POP
STO {I,Ans
POP
STO {G,Ans-1
POPVEC
IF G>0
RET


You can see here that I've changed the shorthand for label to comma, and goto to period. I may revert to a colon for label as I debate with myself whether to support colon-delimited lines.
I made some further progress today. I have fixed some bugs in the palette handling and fading in and out, as well as improved performance in the drawing code. No screenshots to share, but here's a screenshot of the planned remaining commands:

(POP [var] was removed, actually, because of ambiguity.)

On top of those remaining commands, the following is planned:
1) Start program from Ans when invoked
2) Small shell to browse TUGA programs
3) Shorthand commands
4) Pre-processor (Tokenize commands, pre-compute labels where able, some other things)
5) Invoking other TUGA programs from within a program
6) Tilemap support
Today I mostly did some under the hood stuff. However, I did add a new command, INIT, which returns the currently selected turtle to the initial conditions. I also implemented some OS BASIC token shorthands. Here's the familiar pretty spiral:

And this is the code for is:
SourceCoder 3 (SPIRALB) wrote:
:TUGA:"SPIRALB":prgmTUGA:Return
:"Draws a spiral
:Lbl 0
:INIT
:CLEAR 0
:PEN 1
:ZERO F
:COLOR randInt(1,255
:Lbl 1
:If F/4=iPart(F/4
:COLOR randInt(1,255
:FORWARD F
:LEFT 92
:INC F
:If F<150
:Goto 1
:Goto 0


You can see I'm using Lbl and Goto and If from the OS commands. Additionally, "LEFT" here is an OS token that's being interpreted as the "LEFT" command. Another one I added earlier is that "POPVEC" can use the single "PV" token. Good for when types on the computer.

I do still plan on supporting a more complete shorthand as well. The idea being the most used things are all on the main keyboard. So the above program could also be written like this:
SourceCoder 3 (SPIRALSH) wrote:
:TUGA:"SPIRALSH":prgmTUGA:Return
:"Draws a spiral
::0
:[i]
:R0
:P1
:0F
:CrandInt(0,255
::1
:?F/4=iPart(F/4
:CrandInt(0,255
:^F
:(92
:+F
:?F<100
:.1
:.0


I also did some work on the readme. It's got a lot of sections now:
Today I worked on letting PUSH accept a list, and POP accept a variable, as well as the RECT command. It will draw an empty rectangle if the pen is down, otherwise it will draw a filled rectangle. I did this in order to more easily implement this fractal:



Code:
TUGA:"SQUARES":prgmTUGA:Return
Draws fractal squares
FADEOUT 2
PEN 0
COLOR 0
CLEAR 0
FADEIN 10
PUSH {0,0,320,240
GOSUB 0
:1
.1
:0
}H
}W
}Y
}X
?H<=15
RET
COLOR randInt(1,255
MOVE {X,Y
RECT {W/2,H/2
PUSH {X,Y+H/2,W/2,H/2
PUSH {X+W/2,Y,W/2,H/2
PUSH {X+W/2,Y+H/2,W/2,H/2
*0
*0
*0
RET


The is recursively drawing a squares.

An animated PNG of it drawing:


As a fun milestone, I recently passed 1,000 significant lines of C code for this project, which is about the same amount of code in Block Dude CE.
I'm trying to think of how to grant access to the turtle's internal values for reading. You can right now do a PUSHVEC and then POP W\POP P\POP C\POP A\POP Y\POP X to have Width, Pen, Color, Angle, Y and X. Seven commands just to get the Turtle's X value into X seems excessive. So I'm thinking either something like:

Code:
GETPOS {X,Y
GETANGLE A
GETCOLOR C
GETPEN P
GETWIDTH W

GETVEC
GETVEC {X,Y,A,C,P,W

First five get values directly. Other two let yo get the turtle into Ans (first one) or the vars that are passed in (second one) in-order and anything left out is just ignored, so GETVEC {D,E,Z,C would have the turtle's location in D,E, angle in Z, and color in C.

Additionally, a SETVEC that takes in a list and can set the turtle vector based on that list. This gives the progammer more direct control over the values in a direct way. SETVEC {X,Y then becomes an easy way to teleport without having to deal with the pen. I will need to decide if that wraps based on the t->wrap parameter.
I started to work on key input. Here's a picture:

Here's the code to do that:
SourceCoder 3 (KEYSCAN) wrote:
:TUGA:Return
:"keyscan test
:CLEAR 0
:COLOR 128
:INIT 0
::0
:KEYSCAN
:KEYDOWN 21
:?Ans
:COLOR randInt(1,255
:KEYDOWN 24
:?Ans
:LEFT 10
:KEYDOWN 26
:?Ans
:RIGHT 10
:KEYDOWN 25
:?Ans
:FORWARD 5
:FORWARD 5
:GOTO 0


Those familiar with BASIC should recognize those keycodes. Right now, KEYDOWN returns 1 in ans if the key is down. Originally, I had planned on making it work like If, but I think having it in Ans is better. GETKEY will also put the key in ans, or in the var passed in.

Here's the same program using GETKEY:


SourceCoder 3 (GETKEY) wrote:
:TUGA:Return
:"GETKEY TEST
:CLEAR 0
:COLOR 128
:INIT 0
::0
:GETKEY K
:?K=21
:COLOR randInt(1,255
:?K=24
:LEFT 10
:?K=26
:RIGHT 10
:?K=25
:FORWARD 5
:FORWARD 5
:GOTO 0


If you only need one key detection, this is a way to get a little more performance.

I am also thinking that KEYDOWN should be able to take and return a list. So KEYDOWN {21,24,25,26,35 will return {0,1,1,0,0 if up and left are pressed.


The rectangle fractal looks great! Nice work!
Thanks tr1p! Plenty more pretty stuff coming eventually. For today, I realized I don't need to choose between KEYDOWN working on Ans or like If. I added IFKEYDOWN and IFKEYUP. So now the KEYSCAN test code looks like this:
SourceCoder 3 (KEYSCAN) wrote:
:TUGA:Return
:"KEYSCAN test
:CLEAR 0
:INIT 0
::0
:KEYSCAN
:IFKEYDOWN 21
:COLOR randInt(1,255
:IFKEYDOWN 24
:LEFT 10
:IFKEYDOWN 26
:RIGHT 10
:IFKEYDOWN 25
:FORWARD 5
:FORWARD 5
:GOTO 0
Worked on a lot of under the hood stuff today. Moved the interpreter core to its own file to make it more manageable. Also made it so the command hash gets converted to an opcode, and then I switch on that opcode. Once I pre-process, the hash will only happen once per line.
I also added the rainbow palette, and used it in this fractal:

https://github.com/merthsoft/tugace/blob/main/programs/BUSH.txt
Also, you can have multiple turtles going at once. Here's a Koch snowflake being drawn showing three at once:

https://github.com/merthsoft/tugace/blob/main/programs/SNOW.txt
I also had used the FILL command to fill the snowflake, but this eventually breaks:


I'm not sure why. Something with gfx_FloodFill I suppose.

Also, here's something I think is fun: I've been using the memory visualizers in CEmu to help me debug issues. Here's my debug view:

And then an additional level of debugging is available where it outputs a LOT of as it runs:
Finally got around to running the program that's in ANS:

The string in Ans will be restored to Ans after TUGA exits, so you can run it multiple times without resetting Ans, even though TUGA messes with Ans.

I also added support for the DCS header, so programs can include a nice icon that shows nicely in shells:

This just puts the DCS header first, so it's a four-line header:

Code:
:DCS
"ICON DATA"
TUGA:"SNOW":Asm(prgmTUGA:Return
"Draws a Koch snowflake

Maybe I can convince shell makers to be more accepting with header format, and it could be:

Code:
:DCS:TUGA:"SNOW":Asm(prgmTUGA:Return
"ICON DATA
"Description

or even:

Code:
:TUGA:"SNOW":Asm(prgmTUGA:Return
"ICON DATA
"Description


If there's a way to get consistently the name of the program who called you, I could use that as well as ans. But when I asked calc84maniac he wasn't sure there was such a thing.
Still waiting for this to run DOOM.

Joking aside I love the visual that this produces. Smile
Thanks!

Working on palette shifting and text output today. Text output is bugged. Palette shifting looks nice:

I'm not sure why some parts don't update even when the palette does. Not sure if that happens on hardware. I'd expect the text color to shift too, and all the bush bits.

It's pretty:

And works better than a bunch of flood fills:


That's very simply done:

Code:
LABEL 5
PALSHIFT
GOTO 5


Edit: Got text workin'
This is looking great!

Would you mind creating an issue on the toolchain where gfx_FloodFill fails?
Oh yeah! Absolutely! I've created this issue with a minimal reproduction of the error.

Working more on text and eval today. The EVAL command basically already worked, but I wanted to test some capabilities. It can't handle the sto arrow, but it can do quite a bit. This code:

Code:
TUGA:"EVAL":Asm(prgmTUGA:Return
"Eval text
CLEAR 0
INIT 0
TEXT "Ans=
PEN 0
ANGLE 90
FORWARD 35
EVAL inString("SHAUN","A
TEXT toString(Ans

Gives this response:

If you start your TEXT command with a double quote, it skips evaluation and just prints the string. If you don't, it evals what's passed in, so you can do complex stuff with it. That means the EVAL/TEXT line actually could've even been
TEXT toString(inString("SHAUN","A
But I wanted to text EVAL as a function. It basically does nothing, as all params are evaled anyway.

Right now, the TEXT command doesn't move the turtle with the text cursor. I'm thinking maybe it should, and you can use PUSHVEC/POPVEC to get back to where you were. The text cursor in the gfx library is really quite handy, and I think I'd like to preserve the ability to chain things.

I did some additional under the hood stuff today. If an error occurs, it pauses and lets you know. Eventually I'd like for it to tell you what the error is rather than telling you to check the console--since you can't do that on-calc Razz. I need to find a way to open the BASIC editor at a specific line so I can take the user to the error.

Anyway, unrelated, here's a cool demo. Palette shifting the square spiral form the first post is pretty:


And with a grayscale palette:
Sprite update double post:


Code:
:DCS
"5555553333555555555555333355555555555533335555553335555555FFFFFF3335555555FFFFFF3335555555FFFFFF3335555555555333333555555555533333355555555553333335555555555333555777555577755555577755557775555557775555777555333FFF3333333FFF333FFF3333333FFF333FFF3333333FFF
TUGA:"SPRITE":Asm(prgmTUGA:Return
"Sprite demo
FADEOUT 2
CLEAR 0
ONERROR 1
SIZESPRITE {0,12,12
DEFSPRITE "C5C5C5C508080808C5C5C5C5C5C5C5C508080808C5C5C5C508088484EDEDC4C48484848408088484EDEDC4C4848484840808EDEDC4C4EDEDC4C408080808EDEDC4C4EDEDC4C408080808C4C4EDEDC4C4AAAA08080808C4C4EDEDC4C4AAAA0808C5C5AAAAC4C4AAAA6363C5C5C5C5AAAAC4C4AAAA6363C5C5080884840808080808088484080884840808080808088484
INIT
PEN ⁻1
SPRITE 0
RIGHT 25
AUTODRAW 0
DRAWBUFFER
FADEIN 10
Lbl 0
CLEAR 0
DRAW
SWAPDRAW
KEYSCAN
IFKEYDOWN 24
LEFT 10
IFKEYDOWN 26
RIGHT 10
IFKEYDOWN 25
FORWARD 5
FORWARD 5
Goto 0
EDIT: If anyone's interested, here's an alpha release with some sample programs:
https://merthsoft.com/tuga/Tuga.zip
There are lots of things it doesn't check which could cause issues, and syntax errors and such are reported through the debug console, so probably run it through an emu for now. That said, I've been playing with it on-calc and haven't run into any major issues yet. Just know, it's possible unstable!

Worked on named labels today. You can now specify labels such as "LABEL SHAUN" and you can "GOTO SHAUN" and it'll work as expected. Numbered labels still work the same, and you can still do an evaluative goto with "+var", e.g. "GOTO +A".


Code:
"..."
ZERO S
Lbl 0
  CLEAR 32
  SPRITE S/5
  DRAW
  SWAPDRAW
  INC S
  ?S=20
  ZERO S
  KEYSCAN
  IFKEYDOWN 24
    GOSUB LEFT
  IFKEYDOWN 25
    GOSUB UP
  IFKEYDOWN 26
    GOSUB RIGHT
  IFKEYDOWN 34
    GOSUB DOWN
Goto 0
:LEFT
  ANGLE ⁻90
  FORWARD 8
RET
:RIGHT
  ANGLE 90
  FORWARD 8
RET
:UP
  ANGLE 0
  FORWARD 8
RET
:DOWN
  ANGLE 180
  FORWARD 8
RET


For evaluative goto, it would work like this:

Code:
"..."
ZERO S
Lbl 0
  CLEAR 32
  SPRITE S/5
  DRAW
  SWAPDRAW
  INC S
  ?S=20
  ZERO S
  GETKEY K
  Goto +K
Goto 0
:24
  ANGLE ⁻90
  FORWARD 8
  Goto 0
:26
  ANGLE 90
  FORWARD 8
  Goto 0
:25
  ANGLE 0
  FORWARD 8
  Goto 0
:34
  ANGLE 180
  FORWARD 8
  Goto 0


(I stripped out the preliminary stuff, see the repo for the full thing.)
Today I worked on getting the shell launch. This way people can browse specifically for TUGA programs. There will be icons eventually, and I've decided to abandon preserving the DCS header as it just makes things too complicated. Anyway:


Any program that starts with "TUGA" will show up. It's recommended that you at the very least include ":Return" after TUGA, so it doesn't function as a TI-BASIC program. Additionally, it still supports self-launching, so you can still do:

Code:
TUGA:"PROGNAME":Asm(prgmPROGNAME:Return


Or any amount of BASIC code on the first line that you want, really.

There are also two additional headers supported for the sake of the shell:
Iconless:

Code:
TUGA:"PROGNAME":Asm(prgmTUGA:Return
"Description


With icon:

Code:
TUGA:"PROGNAME":Asm(prgmTUGA:Return
:HEX
"Description


Icon will be a 16x16 xLIBC palette sprite.

For sub-programs, just leave out "TUGA", and make the first line just "Return" (so it doesn't work as a BASIC program).

EDIT: After discussion with roccolox, I've decided on usign the same icon sprite format as the DCS header, so it'll be BASIC palette colors.

Additionally, I am probably going to change the prefix from `TUGA` to `0TUGA`, as calc84 pointed out `TUGA` can cause overflow, depending on the values in the variables.

Further, I'm pasting this from calc84 for future reference for me:
Quote:
if you wanted to more quickly collect the next m names, you could have a small minheap that collects only entries larger than a given name, that would be O(n*log m) for speed and O(m) for space

I also just thought of an interesting approach for seeking directly to a given letter
You could have both a max and min heap for collecting up to 8 entries above and below the given name, while also counting how many total are below
then use that total mod 8 to determine the page offset, and pop from the two heaps
and that same method would be reusable for seeking to the next and previous pages, if you set the input name appropriately
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement