Zikzak - crummiest 8bit console/computer ever .. but I'm making it!


skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
I do love python (I even stealthed in a buttload of jython as I'm definitely not a fan of java, at work ;) .. it really depends how configurable the build is for cpython; perl for instance is crazy flexible in the ./configure script.. I've never looked that detail into cpython's build though. Take a peek .. if it can build sans all OS functions, then maybe we'd have a chance. ie: If it has a core that is 'just python', and almost no dependancies, and then #ifdef in various other stuff, maybe. I don't actually have any useful filesystem interfaces built yet (just direct CART access for mass storage), but the zikshield does have the SD-slot on it, so it should be doable .. just need to code up some low level SD block interface and then port/code a FAT16 library or something.

Take a look at the cpython build configuration options...

FWIW, there is a unix-like OS that should be able to port to zikzak, which I've often wanted to do, but never had time slices for. Do some googling, see if theres a python build for nuttx, too. ie: Running nuttx would get us a lot of functionality (filesystems, TCP, etc). But for retro insanity I've been more leaning towards working on CP/M, useless as it is :)
[doublepost=1465778333,1465778297][/doublepost]If SkeeBASIC gets remotely useful, I'll post the code on a git so people can cry laughing themselves to sleep, and you can fiddle with it on Linux (or whatever OS; it takes very little to build.)

jeff

edit: And of course, if it gets remotely useful, I'll add in some graphic primtivies like circles, lines, loading a sprite, moving a sprite, etc. May be instead of writing the space invader demo game in card compiled, it'll be SkeeBASIC for extra wank :)
 
Last edited:

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Yeah, getting something posix on there would be cute, but I also visualised it as having a strictly microcomputer os. I guess I'm just not that big a fan of BASIC, and am wondering if we could do the 1980s again, but without making the same mistakes. Granted, Python in that framework is a bit of a cheat, given it didn't exist until around '95.

Maybe python will be too slow though. That's really why I'm for a single user os I guess, so you can run your app on bare metal if you want to, and not be interrupted by pesky preemption. I've never run Python on anything predating manufacture in about '96, although that chip was clocked at just over 100MHz (DEC strongarm FTW!) - and what's your ez80 running at, around half that?
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
I forget offhand actually .. maybe 10MHz? Some of those models go up to 30MHz but I don't think I bothered with that one.

This BASIC will be very slow I'm sure, as its not compiled per se; just compiled to IML and then simulated from there, with a lot of waste, and written reasonably 'clean' (badly, but clean :) .. ie: its not cheating to be fast, so its extra slow. I'm coding it on a real machine, of course, so performance is not a problem.

But yes, BASIC does suck really bad, but its a fun little exercise. Writing the interpreter was actually really easy (4 hours!), but the parser is a real pain in the ass :)

But yeah, I'll get a basic BASIC for lulz, but I probably wouldn't use it for mucj.. because BASIC sucks. Maybe 'my BASIC' will be a bit better (isn't that _arrogant_ :).. but GFA Basic was pretty good (as far as BASICs go.)

Myself, any real work I do would likely be straight up C or asm code, and flashed to cart... or maybe deposited executable of some sort onto SD.

But we'll see.. whatever is fun.

Retro, but yes, a good point; going 'too far' may just make it suck, like it did back then :)

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Most BASICs of the the original z80s vintage didn't compile BASIC programmes - they just tokenised it i.e. replace any commands it recognised with a short byte sequence (of non-ascii characters) but leave everything else as is, funky spacing and weird characters as written. So to actually do anything it has to re-parse the statement every damn time and convert 4*(3-1*2) into reverse polish for example, just the keyword recognition bit is done already. That's the main reason why it was so dog slow at the time.

Since you have relatively oddles of ram, maybe you could consider the parser actually writing out z80 instructions then running them. That way you could cache recent instructions, and speed up relatively tight loops no end, assuming you don't allow self-modifying code. Actually, since variables do change, you'd have to leave them expanded in the instruction cache, so it may be more effort than its worth thinking it through. You'd be most of the way to writing a bytecode intermediate code you could use to 'compile' the program to I guess.
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
Yeah, theres lots that could be done, but my amount of spare time and interest level is not on working on a tight little BASIC; it has no future after all, and there are other BASICs out there (including the original Tiny Basic, which is pretty shit hot for 70s code :) .. ie: Right now its just a simple lexer/parser that "compiles" to a shit-tonne of structs, and an interpreter that knows how ot run them. Its essentially a pile of function pointers.

Now, it wouldn't be conceptually hard to have a post-process that runs down the struct tree and spits out C code, or assembly .. ie; a BASIC -> C compiler, or BASIC -> z80 .. which is in and of itself a little interesting Not to mention the optimizations that could be done.

That's just not likely where I'm going here. This is just for lulz, spend a couple nights hacking something together, so that we could tap tap a few rudimentary programs in and have them work.. or who knows, maybe do some fun stuff with it.

It sort of provides a framework for plugging in some built in stuff; ie: the question of how booting works and how you integrate a 'monitor' app, and a flasher, etc; well, if you make the built in BASIC a 'thing', and then you just offer additional commands like 'monitor', etc; as is, I have to #if in what assumptions my firmware is that boot, or have it show a full on menu where the user hits a 1, 2, 3, etc to pick various things to go. Sort of goofy. Framing it with a BASIC is lame, but very in-style of the 70s and 80s, too, so its an option.

Hell, it should just have a lisp machine, to make it more my general interests, but that'd be evil :)
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
:)

That does sound pretty extensible already. I gather most old BASICs just did things as they parsed them, only arithmetic operations needed to be parsed first, sorted into reverse polish notation then executed. Stuff like:

LET X=2*1024

Became:

LET - expect a variable name
X - store as variable to set, expect =
= - got =, expect expression
2*1024 - got expression, calculate
\n - store result in variable.

All because they didn't have the RAM scratchspace to store partially decoded values for very long, and they could cope with surprisingly involved calculations. When you're already storing all the variables, and the trig lookups (unless you had ROM space to store those, and don't have to precalculate them), the few K you have as workspace is kind of limited.
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
Well, on the one hand you can do a serious parser like an LALR parser /etc; or you can run lex/yacc/bison (etc) to spit out the code for one based on your BNF grammar. But given this is just a few hours project, I went easy :)

The language is all '1 line constructs', and of the form KEYWORD followed by EXPRESSION.

LET expression
PRINT expression
WHILE expression
IF expression

.. if all else fails, assume its a LET.. so you can do stuff like:
X=X+1
.. without having to put LET in front.

It does sort of get weird for 'do it'; "IF expression THEN GOSUB john" but to keep parser simple, I'd think..
IF expression
GOSUB john
ELSE
somethign else
ENDIF

Try to maintain the simple style of 'verb'+'expression...'. *shrug*

The lexer just breaks on whitespace, the parser just wants to find a statement token, and then everythign after is an expression (operators + constants + strings + variables).. ie:
constant or string or variable
followed by end of line or operator
followed by another constant or string or variable

So the parser is .. find me a token, then find me mroe tokens to build an expression, finish at end of line.

The intermediate language is a pile of structs in a tree, so expressions can be arbitrarily complicated, or branches of code. ie: Each node is a Thing, but one of those Things is a Sequence (list of things), so any leaf can just be a whole subprogram, or a single thing; a thing coudl also be an Operator, or a While, or whatever, that does some neato stuff. The runtime is just a statemachine running down the nodes, and trying to 'run' each one (call a function pointer on the object type).. so running a String just returns a string, but running a While has it check the conditional and if good, run its contained node (which is likely a Sequence, with more nodes..)

Easy stuff.

Extensibility is envisioned mostly to add external functions.. so have an API call to register a function, and then the BASIC would be something like..

CALL funcname arg1 arg2 arg3

Course, could just have it imply the CALL keyword, if it sees a "(" in there, say, to make a weird syntax like: funcname(arg1,arg2).. very non-BASIC, but only used for external C callbacks, so it sort of makes sense?

Could also extend to have crazy features.. from one of my toy languages I made 10 years ago, a "WHEN" concept; "when vblank call this subroutine" concept.

ie:

# some random program
upload_sprite ( 'sprite1', variableWithGraphics )
LET X = 10
LET Y = 10
WHEN vblank
draw_sprite ( 'sprite1', x, y )
x = x + 1
ENDWHEN

ie: GOSUB for "basic subroutines" defined in the user code, but "CALL" semantics for external C routines. I dunno.

Making it up as I go, and haven't thought that far ahead yet.

Again, not sure if needed ..

May just get a basic parser working a bit better, and call it a day for now. Could add keywords like RUN FLASHTOOL and RUN MONITOR and RUN TESTTOOL, and then the SkeeBASIC becomes the user interface framework .. like a Commodore 64.

*shrug*

Of course, if anyone takes one of these off my hands, they could build their own firmware to do anything they want :)

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Yeah, in BBC BASIC GOSUB called a numbered subroutine, while CALL started the processor running on the specified address, so was usually used for machine code you'd built or loaded in. Since you're supporting labels, and your GOSUB uses those rather than line numbers, it's a little more like BBC BASICs named procedures, and CALL for compiled C code is theoretically quite comparable to machine code.

Not sure if GOSUBs returning values will complicate your parser much. I guess if they were embedded in a mathematical operation that could complicate them, but them not taking defined arguments means you can do them in any order I guess.
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
Depends on 'how old' a BASIC you look at.

Some of them only did "GOSUB <reference>" standalone, and the subroutine could do "RETURN" to jump back; the advantage was just the automatic jum pback, from wherever the jump was. IT was better than GOTO in those cases, simce GOTO didnt' knwo where to go back without a pile of IF statements and variables. A return value would be somethign tyou manage in your own variables. My intention was to have scoping, so when you did a GOSUB you'd get your own stack frame, and the globals.. so, that'd still work.

Then you'd also have FUNCTIONS which was a subroutine that returned a value, but had very limited syntax .. ie: it was only used in expressions. X = 1 + MYFUNC(y) ..
so it parsered pretty easy.

But yeah, this is why BASIC sucks; its like COBOL, where you grow by keywords and special cases..

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Yes, looks like I was mistaken; even the BBC's BASIC GOSUBs just returned none always.

Scoped GOSUBs sound quite cool, but I wonder how you're meant to get anything out of them in that case? Traditional BASIC only ever had one scope, so you could set variables inside a GOSUB and use them after being returned. I guess you could declare which variables to copy back when calling your routine, while still keeping your main namespace uncorrupted and unmodified.

So you'd have reference to expandable heap based blocks on a stack? Could be quite neat, although a little work. If you have a strict execution tree, building a new space on the current end of your existing space shouldn't be too hard. You can't delete variables in most BASIC implementations once you've declared them, so you don't have to worry about holes that need compacting, I guess.
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
yeah, in my lame implementation so far, theres just a 'scope stack'; scope [ 0 ] is globals and always exists; but if you go into a GOSUB (say), it'll increment the scope-stack counter (0->1). Whenever a variable is looked up, it first checks the highest scope stacks symbol table, and if not found, checks the 0th (global) scope symbol table, before being a missing symbol error. Theoretically, could be other stuff scoped in. Its not coded yet, but when the scope exits, it should go through and free up any symbols in that scope.

The way I had envisioned communicating from a GOSUB scope out to the main would be via a 'global' keyword (I think perl and python do this sort of trickery); so you can declare a variable that is global into the local frame, and thats really just the symbol table pointing to the global symbol tables value instead of its own.. easy to implement. The only trick is when the scope is exited, you're just being sure not to impact that global value. So maybe a boolean flag on each symbol pointing out if its a global link or not.

ie something like..

LET x = 10
do stuff
do stuff
GOSUB myroutine
do stuff
ENDPROG

myroutine:
GLOBAL x
LET y = 20
x = x + y
RETURN

--> where X is the same value in the subroutine and global, and Y is a temporary scoped value.

I've not mentioned arrays, or things like malloc()/free() or garbage collection yet, on purpose. Ignoring that for now in my head.. but adding dynamic memory or at least arrays woudl be required to be useful.

If I did the "WHEN vblank GOSUB myrender" type thing, then you need that subroutine to be able to work with the global sprite-arrays to move things around, etc, which is what drives the above sort of stuff. Not that any of that is built, but floating around in my head.

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Good plan. Most BASICs I've used had arrays, but beyond that you had to roll your own. BBC BASIC had the automatic varibles TOP and HIMEM - TOP being the end of your program code, and HIMEM being the start of screen memory, so you could use the space in between for anything you liked. Having struct types might make your BASIC much more useful than old BASICs were, but I'd still say don't waste too much time making BASIC all singing and dancing when you could get a better language running on there instead.
 

liveunderwater

熱いソース
Joined
Feb 26, 2010
Messages
193
Yeah, point X, Y, COLOR line X1, Y1, X2,Y2, COLOR maybe a circle/ellipse, floodfill ?
That would be really nice to have in BASIC for the ZikZak. I'm really liking the TI-99/4A Basic (that I'm using right now) that uses a function Call Char(y, 0x00) to change your typed and reserved characters (ascii y value) to a hex defined bitmap. So I can call hchar or vchar(row, col, char in ascii, repetitions) to make some pretty fun stuff.
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
Not much time lately, but skeebasic does support WHILE and IF and MODULO now:

let x = 0

while x < 11
print x
if x % 2
print "Odd in while"
endif
let x = x + 1
endwhile

print "done!"

When run, that spits out:
0
1
Odd in while
2
3
Odd in while
4
5
Odd in while
6
7
Odd in while
8
9
Odd in while
10
done!

(I need to add some syntax so PRINT knows where to do a newlnie or not; I may just interpret \n to mean that..

I'm currently working on PROCEDURE to define a proc, and GOSUB to call it:

let x = 0

while x < 11
if x % 1
gosub odd_ones
endif
let x = x + 1
endwhile

print "done!"

exit

procedure odd_ones
print "Odd" + x
endproc

Its still very lame, surely memory leaky, poorly written; but about 8 or 10 hours into coding, and thats pretty nifty to me :)

Its funky, but had to handle all the parse types..
like print "STRING" + " " + x + "STRING2"
.. that ends up parsing into:

X+"String2" -> a PLUS operator with a var and a string
That leaves PRINT "STRING" + " " + <operator>
So then it has to know how to parse and 'compile' String+Operator (" " + operator)
Which spits out an expression element.

So then it has to know how to handle String+Expression
and then lastly, PRINT + <expression>

So that one lie is multiple compile-down steps. PITA to code up all these little interactions .. confusing stuff to think about. But now I'm starting to have a library of parse operations, so sometimes I just add a garammar rule and poof, bunch of various things ust start working. Thats exhilerating.

Its about 2000 or so lines of code now (including headers etc), which is getting biggish, but not bad for realyl a days work or so :)

jeff
 
  • Like
Reactions: rSl

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
Nice. Are keywords defined as lower case, or is it case insensitive? Most old BASICs demand upper case keywords.

The BASIC I used to use had a REPEAT...UNTIL structure in place of a WHILE...ENDWHILE, in other words it tests at the end of the loop, not at the start. Still to this day in coding I find it more of a hassle to copy the contents of the loop if I want to always do it once regardless of conditions, and easier to just put the whole thing in an IF (or goto a label at the end if you don't support multiline IFs, I guess) if I don't want it to run based on the condition.

Minor note: Should 'GOSUB' not call a subroutine, rather than a procedure?
 

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
OKay, hacked for an hour, got GOSUB and PROCEDURE working (though scoping is a little bunged up so far, for the variables therein). So IF, WHILE, GOSUB, etc, good to go so far. Its still limited of course.. just doing language constructs is fine and dandy, but it can't actually _Accomplish_ anything in the ZZ (no graphics keywords or external functions etc), but that's all 'easy stuff' ;) The language, crappy as it is, works :)

REPEAT..UNTIL would be trivial to add; its just a variant of WHILE but with the test on the other end; same code nearly. ie: Its just a parser trick.

This is pure C, and not using tools like yacc or bison or lex .. just hand rolled since I started it for lulz and did the firt cut in about 2 hours flat. Probably the worst implementation possible, but I've not written a simple compiler/interp in 15 years I bet :)

But essentialy, the lexer returns a token which just has a type, and for some of them, a captured bit of text. ie: for a WORD type, it keeps the text, since the WORD could likely be a variable reference, so the text is relevant. But a LET is always a LET, so no need to keep it. Theres also a pseudo-type, ELEMENT which is what all the AST nodes are. ie: Given a LET token, you try to parse it, and if successful get a LET ELEMENT back. A programme is a SEQUENCE, and a SEQUENCE contains a list of ELEMENTS. Any element can of course contain other elements, which is how say an OPerator works.. a OP+ has a LEFT and RIGHT element, and when the OP is run, it in turn runs its left and right sides, and then does the operator job (adding, assigning, less-thanning, etc). Very simple stuff. All function pointers, so each element type has its own source file to contain relevant functions.

The trick then is parsing. the Lexer returns a "LET, WORD, OPERATOR, CONSTANT" for "LET X = 1".

IT starts at the right and runs down all the rules, and I have a rule for WORD/OP/CONSTANT, so it first parses that, which returns a OP element with a LEFT of VAR(X) and a RIGHT of CONSTANT(1).

Then when it restarts the parser matcher, it has a rule for LET/ELEMENT, to catch that guy.

So, I just have to add a UNTIL token, and element; when oit ees REPEAT token/element, it then recurses another parser, to suck up the sublisting and return it, and then the UNTIL just stores it all; so would end up with a REPEATUNTIL element, that has a CONDITIONAL side, and a LISTING side, and then repeats until the conditional is true. Trivial.

REPEAT...UNTIL
and REPEAT...WHILE make sense to me

GOSUB-SUBROUTINE ... you know I forget the nomenclature; I've not looked at any basic samples in years; just going by old old foggy memories :) Does "PROCEDURE" mean anything to BASIC? Or would that be what a GOTO jumps to?

So far I've not added labels; when it sees PROCEDURE WORD, it just creates a symbol table entry and stores the sublisitng in that. Then the GOSUB looks up the symbol table and if the type is a PROCEDURE, then runs it.

Since no line numbers, GOTO needs a label.. or _Something_. I've not put thought about how I'd do it yet.. maybe a list of labels in the symbol table, with a type of goto or something.

Anyway yeah, need to get bac to hardware hacking (finish fixing my 3d printer to get a case out?), and firmware coding and testing, but this has been a fun week writing a interpreter.

(only a week or less, in hours here or there :)

....

it is really tempting to make a skeebasic -> python compilter mode, though.

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
In BBC BASIC it has 'GOSUB lineno', and BASIC II added 'DEF PROC name/ENDPROC', which defines a symbol 'name', can take arguments and return a value. So based on those keywords I call the latter procedures, and the former surbroutines (or more commonly, just gosubs).

I do tend to have a hang up about synonyms of computer terms, and do tend to try to keep them all the same for the same things. You'll find bits of my code that use args, parameters and options for different things across contexts for example, but I always try to refer to the same stuff by the same term. So using GOSUB to call a PROC just sounds kooky to me, but that may just be me.

Edit: One neat powerful thing about GOSUBs was that you could gosub to anywhere, so you could gosub somewhere a few lines earlier and do some additional stuff. It was always a bit of a hack, and can easily be acheived using scoped procedures by having a wrapper procedure that calls the second one, doing some extra stuff first. I'm not sure even how your idea of GLOBAL declaring variables would work in that scope too - can you declare them anywhere, not just at the start of a subroutine, or do they effectively mean your marking BEGIN and END places, so you may as well make them more explicit?
 
Last edited:

skeezix

Internal Development
Joined
Mar 11, 2003
Messages
8,064
Website
www.codejedi.com
I suppose a question comes up..

Would anyone care?

- is this only of interest to some subset of Zikzak interested people (a niche of a niche of a niche) (Openhandhelds - zikzak - those interested in basic - those interested in alpha level script languages ... 3 people? :)
- or possibly is this of interest to Pandora people somehow?
- or possibly to greater Linux or open source people in general?

I could post it to GitHub (probably will soon enough anyway).

To be useful to _anyone_ it probably needs..
- a few more parser rules and code to handle .. ie: it only knows a dozen or so rules now, which gets it a bunch of things working, but not a tonne .. ie: it knows how to parse String/OP/String ("a"+"b"), and things like that, but a lot of things it probably blows up on. I've only coded parsers as I've needed them for my test basic programs.. which is like 6 little programs.
- an extension mechanism that's easier to follow; ie: that than have people change some of the internal logic, which locks people into the ABI etc, I should have a sb_extension.[ch] that presents a simple API, like 'sb_add_keyword ( "REPEAT" )', and so on to allow someone to call API to add parser rules, add keywords... maybe even add new types, etc. At least adding keyword and parse rules, and giving it function pointers to call when a match happens. Without adding types and such, even that would be useful.
- then add a few sample extensions .. like on Linux/win32/Pandora .. open an SDL window, and offer a half dozen SDL functions as extensions to the language, so could call LINE or CIRCLE or something.

Then, if there are anyone interested, they'd have some sample tniy programmes, some sample extensions, and could bug me in GitHub to do fixes or add stuff.

I dunno.

ITs probably a total waste, given there are well done languages out there, but still :)

So if I did a build for Pandora would you guys or anyone care? or is it all just fun pure abstract discussion here? :)

If you're devs, I can just point you to the GitHub one of these days and you could build it; its just a simple makefile, pure stdio console app.

jeff

FWIW, I use .sb for the file ectension; skeebasic -> sb :)
[doublepost=1466304471,1466304154][/doublepost]Ah I see.. so they are using GOSUB as a RETURNABLE GOTO; its fundamentally the same as a PROCEDURE invocation, but a PROC is named.

In my case so far, I've not bothered with line-no's at all, and so it only has PROCEDURES, but I've used GOSUB as the inokeer rather than just the proc name.

I probably could make it work like that, but I've cheated so far and always had KEYWORD <stuff>, so the parser is rather expecting the first thing to kick off the match rules. I need to add a fallback so that if it just sees WORD.... that it can assume "LET" in front of it, but I worry that doing that would conflict with some other things I might want to do (like adding procedures as callables without using GOSUB) ... but then, if PROCs have arguments, and you use ( as an argument list grouper, then parser could handle seeing WORD PAREN WORD... to sort of imply what to do..

Hmmf.

All depends how far we want to go with this.

Its bad code, for sure, given my rush, but maybe it has legs?

But back to question above.. if you guys want to see it I can shove it up on GitHub for all the world to see my shame, and then you too can hrow some code in there and clean it up ;)

...

Theres a fair amount of work needed to clean it up nicely.. ie: it uses printf() and string.h and stdlib.h (malloc, free) all over the place. They should all be sb_free() and sb_print_string() and junk, so that they can be further #if'd by platform; I've only lightly tested on Zikzak so far and theres lots of worts like that in the way. Easy stuff, just needs some hours of cleaning.

All in time..

jeff
 

levi

Still fresh, damnit!
Joined
Oct 6, 2008
Messages
14,057
Location
Somewhere off the coast of the EU
For me it's mostly just a fun diversionary discussion here. I couldn't grok trying to code anything serious in BASIC any more. I'd probably have a play with anything you posted to github, but I can't see myself using it for more than a few hours in reality.
 
Top