BASIC without line numbers?

Schlowski

Member
Sep 24, 2019
45
22
8
Sorry, I didn't realize the context was addressing a claim that no structured blocks is about being interactive.
No problem at all :)

I guess the way you were intended to do "ELSE" in Dartmouth Basic was to do a second IF, "IF X>6 THEN ...", "IF X<7 THEN"
Yes, but that's pretty complicated and ugly, an ELSE would have made for a much better and more readable version.
 
  • Like
Reactions: BruceMcF
May 22, 2019
409
211
43
I know how that works, I just wanted to point out the other direction, there are some constructs which cannot be (fully) utilized in direct mode. WHILE...WEND on the other hand could be used in direct mode like a FOR...NEXT loop. So the argument of BASIC being interactive and therefore omitting some of the structured blocks don't fit.
The worst omission cause of space constraints is the lack of ELSE which leads to a lot of unnecessary complicated constructs with IF...THEN and GOTOS so simulate the ELSE case.
You're trying to imply a meaning that I did not present.

I was not referring to immediate mode. I said "due to the interactive nature", because the interpreter lives in memory alongside the BASIC program code, which is entirely different than compiled programs, which do not have the application program and the compiler in memory at the same time.

This means every byte of interpreter is a byte that can't be used for the user program, so the interpreter must not contain any unnecessary or overlapping commands, and both DO and WHILE qualify as such. A Turing-complete programming language needs some sort of branch statement, and IF/GOTO is the most flexible branch instruction. You cannot have a Turing-complete language without some sort of IF, but you can certainly have one without WHILE or DO.

If there's enough code space, there's no reason to not include WHILE loops, but they're not necessary for completeness or performance; the WHILE construct is entirely cosmetic.
 
Last edited:
  • Like
Reactions: BruceMcF
May 22, 2019
409
211
43
Yes, but that's pretty complicated and ugly, an ELSE would have made for a much better and more readable version.
ELSE is another one that's interesting to consider, especially with nested IF statements.

On the C128, the ELSE statement must be on the same line as the IF statement, so you get things like:

IF X>80 THEN Y=Y+1:X=1:ELSE X=X+1

and this is perfectly reasonable.

but look at this...

1570229958897.png

BASIC interpreters tend to be pretty stupid, and this is just one example.

I'm actually okay with the idea of having an ELSE command in line numbered BASIC, but issues like this would have to be addressed, even if the answer is "only one ELSE per line."
 
  • Like
Reactions: BruceMcF

BruceMcF

Active Member
May 19, 2019
100
30
28
ELSE is another one that's interesting to consider, especially with nested IF statements.

On the C128, the ELSE statement must be on the same line as the IF statement, so you get things like:

IF X>80 THEN Y=Y+1:X=1:ELSE X=X+1

and this is perfectly reasonable.

but look at this...

View attachment 336

BASIC interpreters tend to be pretty stupid, and this is just one example.

I'm actually okay with the idea of having an ELSE command in line numbered BASIC, but issues like this would have to be addressed, even if the answer is "only one ELSE per line."
That is a fine rule. The language is called BASIC, not ADVANCED.

If a nested IF-THEN-ELSE is desired, those are different keywords, IF ... THENDO ... DONE ELSEDO ... DONE which understands a structured nesting count. They can also include a spot for a forward branch address initialized with a dummy address, so they don't have to repeat the scanning of the structure if done inside a loop.

But a simple single-line IF...THEN...ELSE... is a higher priority.

~~~~~~~~~~~~~~~~~~~~~~~~~
If there's enough code space, there's no reason to not include WHILE loops, but they're not necessary for completeness or performance; the WHILE construct is entirely cosmetic.
I would take issue with the performance side, if we are talking about multiple-line conditional loops implemented with a "GOTO line" (for BEGIN/BEND), or "IF cond THEN line" (for REPEAT/UNTIL) that step through the program looking for the line. Just like FOR/NEXT, DO/LOOP gains a substantial performance benefit on the branch by branching back to a known target stored on the structure stack.

But the WHILE/WEND version of the construct is an incomplete version of the WHILE loop ...Test in Front and jump over the loop is unsuccessful or REPEAT/UNTIL to test at the End and repeat the loop if unsuccessful are both special cases of Start the Loop, ... Test and jump out if unsuccessful ... Repeat the Loop.

So, since any condition can be inverted with a NOT, DO ... [WHILE(cond)] ... LOOP is a perfectly general structured programming loop.

And while it can be constructed from IF...THEN and GOTO, the functional reason for implementing it in a CX16 ROM basic is to provide a three keywords that are not in V2 Basic so (1) that the equivalent of the GOTO implied by LOOP doesn't scan for a line but instead takes the backward jump target from the structure stack and (2) the WHILE can scan for it's exit in a way that respects nested DO/LOOP blocks (see the issue above with IF/THEN/ELSE).

With DO ... [WHILE(cond)] ... LOOP in the BASIC Rom, a pretty screen editor basic can craft a QBasic DO:[WHILE|UNTIL]:[LOOP|UNTIL] ... and extend it to a DO:[WHILE|UNTIL]:[LOOP|UNTIL|DONE] ... to provide a simple block extension of IF/THEN with IF ... THEN DO ... DONE ... with DONE as WHILE(FALSE) REPEAT.

Regarding the optimization of only scanning the first time and storing the result, for repeated WHILE branches, rather than doing it as a simple scanning process ... the thing is, it's only done once per loop. The LOOP backward jump is done once per iteration. And adding complexity to the LOOP back means there's a trade-off for any savings gained by optimizing the WHILE jump ... even a WHILE jump that is executed repeatedly because it's in an inner loop ... so I may repent of that and suggest just directly using the FOR/NEXT stack for DO/LOOP, LOOP and have all the complexity of scanning for the loop exit while respecting nested DO/LOOPs contained in WHILE().
 
Last edited:
  • Like
Reactions: Schlowski

millenniumtree

New Member
Oct 2, 2019
24
16
3
I didn't realize there was so much scanning being done in BASIC - no bloody wonder it's so slow.
What happens to the loop stack pointers when you GOTO out of 3 nested loops? Are there stack pointers at all? Sheesh!
Also - if there is to be a numberless editor, could it do auto-indentation?
(the spaces wouldn't be saved in the tokenized output)
Probably for a 40-column display, that would be wasted space, but we have 80 now, and with the kids these days playing with python, indentation is sort of a no-brainer.
I noticed some code also removes spaces around the TO token so you get weirdness where there's no space between numbers, variables, and tokens, like this:
FORA=XTOY
that does appear to save a few bytes, but heck. Can this editor just parse the tokens and add spaces, then strip the spaces when it renders down to tokenized code? That way, spaces or not, you get the most efficient memory usage.

I've been writing PHP and Javascript code for nearly 20 years, and rarely use the do-while construct. It's just as easy to initialize your variable first to get you into the while, so IMO, it's not critical.
The single biggest shortcoming I see in the current BASIC implementation is a lack of ELSE and multi-line IF blocks.
I'm starting to learn C again (it's been 23 years or so), because I already want to do more than BASIC has available, and it's only been 2 days.

I realize much of the above is some serious scope creep. I know the programmers can only do what they can, so take the above as just a wishlist from an old new BASIC user.
I'm not suggesting BASIC should be python, but any quality of life improvements that can easily be done, should be, if it's feasible.
 
Last edited:
  • Like
Reactions: BruceMcF

BruceMcF

Active Member
May 19, 2019
100
30
28
I didn't realize there was so much scanning being done in BASIC - no bloody wonder it's so slow.

What happens to the loop stack pointers when you GOTO out of 3 nested loops? Are there stack pointers at all? Sheesh!
With FOR/NEXT loops ...
(1) When FOR/NEXT terminates normally, the information is removed from the stack
(2) a NEXT that has other FOR references on top of IT'S FOR reference discards those abandoned inner loop
(3) any FOR-NEXT that begins inside a subroutine is removed from the stack on RETURN
(4) any current loop re-using a FOR variable name is discarded when a FOR using the same variable begins again.

But if you start and abandon too many loops, and they don't happen to get cleaned up in time, you end up with an OUT OF MEMORY error.

(2) doesn't work the same way with anonymous NEXT, since they have no variable to identify them so they are implicitly the variable of the topmost FOR entry.

With DO/LOOP, only (1) and (3) would work directly, since there is no variable to identify a DO stack entry. Though to let it work correctly with FOR/NEXT loops, the DO stack entry might have a pointer to a 0-length null variable, so if it was nested with FOR/NEXT loops, a FOR/NEXT loop that cleaned out inner loops would clean out DO/LOOPs as well.

Adding a marker to LOOP based on DO/LOOP structure depth would be possible, but I'd rather have it like a "Chinese OSHA" rule ... tell people "don't do that" about GOTO's leaving DO/LOOPs and if they ignore the warning, oh well. That avoids bogging the ROM Basic codebase with nanny guardrails.

Also - if there is to be a numberless editor, could it do auto-indentation?
(the spaces wouldn't be saved in the tokenized output)
Probably for a 40-column display, that would be wasted space, but we have 80 now, and with the kids these days playing with python, indentation is sort of a no-brainer.
I don't recall the details of the description recently on the FB group, but I think indentation of some form was mentioned as being supported. If that's part of a separate 16K system ROM segment, I gather it would be a general purpose text editor / text file viewer as well as a basic editor.

I noticed some code also removes spaces around the TO token so you get weirdness where there's no space between numbers, variables, and tokens, like this:
FORA=XTOY
that does appear to save a few bytes, but heck. Can this editor just parse the tokens and add spaces, then strip the spaces when it renders down to tokenized code? That way, spaces or not, you get the most efficient memory usage.
I'm guessing they'll do that, but as always I'm not an insider, just a noisy outsider, so it's just a guess.

I've been writing PHP and Javascript code for nearly 20 years, and rarely use the do-while construct. It's just as easy to initialize your variable first to get you into the while, so IMO, it's not critical.
In Forth it's naturally two separate words, because WHILE has to have a flag in the top of the stack to react to, so it's entirely open whether its BEGIN cond WHILE action REPEAT or BEGIN action1 cond WHILE action2 REPEAT ... but in an interpreted BASIC, where each keyword is taking one parsed keyword code until you run low and have to reserve a couple for extension code, I think it makes sense to be a bit parsimonious in the loop structure elements and use the more general form ... the screen editor can add WHILE/WEND and REPEAT/UNTIL loops as syntactic sugar.

The single biggest shortcoming I see in the current BASIC implementation is a lack of ELSE and multi-line IF blocks.
I'm starting to learn C again (it's been 23 years or so), because I already want to do more than BASIC has available, and it's only been 2 days.
This gives multi-line IF blocks in the form of DO WHILE(cond) ... DONE. If the screen editor wanted to make that available as "IF cond THENDO ... DONE" that is a simple substitution.
 
Last edited:

qbradq

New Member
Sep 14, 2019
24
13
3
I would like to state the following things I have learn after coding 6502 machines for a while:

Everyone writes their own assembler, compiler, or language at some point. They are great learning projects. And they can end up being very useful (see FORTH History).

Programming in BASIC on the computer (or emulator), with line numbers, and using the screen editor's features is quite a nice experience. Just SAVE often! Even extending BASIC programs with ML routines with MON is a pretty nice work flow once you understand what to write down :)

Finally, MS BASIC is painfully slow. It was in the 80's too. Bill's big accomplishment wasn't making an efficient language interpreter. It was cramming into the Alter 8800's 4K of RAM. I think David was pretty explicit that he didn't expect a massive increase in BASIC execution speed, but maybe enough to do basic games. Judging from the 8MHZ emulator it seems to be.

P.S. When I first started writing code for this project it was an integer BASIC interpreter to replace the painfully slow MS BASIC. That was a fun week :)
 

BruceMcF

Active Member
May 19, 2019
100
30
28
I would like to state the following things I have learn after coding 6502 machines for a while:

Everyone writes their own assembler, compiler, or language at some point. They are great learning projects. And they can end up being very useful (see FORTH History).
Not all of those efforts actually succeed, of course ... the port of Camel for the Z80 I am doing is as close as I have come to a working one, it can execute "2 4 +" and I can use the VICE Monitor to see that there is now a "6" on the stack. It still crashes somewhere, but FIND seems to work and number conversion works with single digits.

Finally, MS BASIC is painfully slow. It was in the 80's too. Bill's big accomplishment wasn't making an efficient language interpreter. It was cramming into the Alter 8800's 4K of RAM. I think David was pretty explicit that he didn't expect a massive increase in BASIC execution speed, but maybe enough to do basic games. Judging from the 8MHZ emulator it seems to be.
And the MS BASIC for the 6502 was a port of 8080 code, so it has a very inefficient main loop. Just cleaning up the main loop code to be good 6502 code would give a noticeable speed improvement in bigger programs.

But not much incentive to spend a lot of time working on that until the final thumbs up/down is given on the 65816, because it would be much easier to make a close port of what was originally 8080 code run faster using 65816 instructions, and it would result in a bigger speed-up as well.[/QUOTE]
 

BruceMcF

Active Member
May 19, 2019
100
30
28
I've changed my loops suggestion after the discussion when I posted it at FB.

DO/LEAVE/DONE is a fix for the existing problem with early exits from FOR/NEXT loops, so I build around that.

DO pushes a "nul variable" FOR/NEXT structure stack entry. DONE pops it ... with all of the FOR/NEXT rules, including inner loops left lying around the stack get popped at the same time.

LEAVE scans for DO and DONE ... starting with a blockcount of 1, each DO increments the blockcount, each DONE decrements it, when it decrements to 0, it continues execution at that DONE.

LOOP then puts a "LOOP" token FOLLOWED BY a "DONE" token. LOOP uses the highest nul variable structure stack entry to jump back to DO. So LEAVE is the only "clean" way to get to the DONE out of a DO/LOOP (using GOTO retains the out of memory problem this is helping to fix).

Finally, IF ... THEN ... ELSE is provided. On failing the IF test, THEN goes to the next line OR an ELSE on the current line, starting with the expression after ELSE. The ELSE routine itself skips to the next line.

Multi-line IF/THEN/ELSE is then done with DO/LEAVE/DONE.

While/Wend:
10 DO IF X<50 THEN 20 ELSE LEAVE
20 ...
....
90 LOOP

Repeat/Until:
10 DO ...
....
80 IF X>50 THEN LEAVE
90 LOOP

DO UNTIL ... LOOP
10 DO IF X>50 THEN LEAVE
20 ...
...
90 LOOP

Multiline nestable IF test THEN { action1 } ELSE { action2}
10 DO IF X<50 THEN 20 ELSE 50
20 ... (action1)
...
40 LEAVE
50 ... (action2)
...
90 DONE
 
Last edited:

Schlowski

Member
Sep 24, 2019
45
22
8
I see the reason for this construct, but it feels a little bit clunky and unintuitive for the multiline IF...THEN...ELSE clause.
I would vote for BEGIN...ENDIF as additional commands. (If that was discussed and discarded on FB please excuse me, I'm not on Facebook)
Single line IF as usual:

IF cond THEN <commands> ELSE <commands>

Multiline IF started by BEGIN
IF cond THEN BEGIN
<commands>
ELSE
<commands>
ENDIF
Then you can apply more or less the same scanning rules (incrementing/decrementing nesting counters) for searching for ELSE or ENDIF as done for the loops.
 

BruceMcF

Active Member
May 19, 2019
100
30
28
I see the reason for this construct, but it feels a little bit clunky and unintuitive for the multiline IF...THEN...ELSE clause.
It is indeed a clunky way to get at a multi-line IF...THEN...ELSE.

It's purpose is fix the mess of early exits from FOR/NEXT while supporting conditional loops while adding the minimum number of new tokens.{+}

I'm just pointing out that it OFFERS that clunky multi-line IF/THEN as a side-effect, without additional keyword namelist & routine codespace required. If the V2 modification Basic author is happy to add a nicer multi-line IF/THEN/ELSE, I'd take it.

Indeed, just like using DO/LEAVE/LOOP to implement "Screen Basic" While/Wend, Repeat/Until and DO [While/Until] ... LOOP [While/Until], the screen editor Basic could support a prettier block if which would be implemented in ROM basic used DO/LEAVE/DONE.

Then you can apply more or less the same scanning rules (incrementing/decrementing nesting counters) for searching for ELSE or ENDIF as done for the loops.
Yes, I adopted it from Forth scripting [IF] ... [ELSE] ... [THEN] words in the first place (they are interpreter mode words, so can't use the compiling BRANCH and ?BRANCH words and resolving their addresses that compiled IF...ELSE...THEN relies on).

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{+ Indeed, if there are two-tier tokens, reserving one or two prefix tokens to allow 256 or 512 additional tokens, THEN LEAVE and LOOP could all be second tier tokens ... the only ones that have a strong argument for being first tier tokens are DO and DONE, given the better efficiency of scanning for a single byte token.}
 

Schlowski

Member
Sep 24, 2019
45
22
8
Given that the X16 runs at 8 times the speed of an Commodore the overhead for 2 byte tokens shouldn't be too bad. We already have VPEEK, VPOKE and some other 2 byte tokens.
 

mobluse

New Member
Sep 20, 2019
21
8
3
I don't think X16 should have a BASIC that is a new BASIC type language, but rather follow existing BASICs such as Visual Basic. In VB they use ':' after labels.
https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/program-structure/how-to-label-statements
I don't like 'WEND' and other such invented words, but prefer End While.
https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/while-end-while-statement
This would not be difficult to type if you have a helpful editor with completion when you press Tab.

What would be useful is a VB to BASIC2 compiler. If one writes this in C one could compile it with cc65 and run it on X16 (or C64).

Also, I think one should follow the Python principle: "There is only one way to do it." I.e. one should only allow one way to make labels, comments, greater than or equal (>=), less than or equal (<=), and not equal (<>). OK for comments there should be two ways: ' and REM, but only REMs should make it to the BASIC2 code.
 

Schlowski

Member
Sep 24, 2019
45
22
8
I really like WEND, it's short. And I really dislike two word commands such as END WHILE, END IF etc. For me a command should be one word - and I think this makes the tokenizer and interpreter a lot easier as END in END IF should be another token than END for ending the program.
 

BruceMcF

Active Member
May 19, 2019
100
30
28
Given that the X16 runs at 8 times the speed of an Commodore the overhead for 2 byte tokens shouldn't be too bad. We already have VPEEK, VPOKE and some other 2 byte tokens.
Executing a two tier token is very little extra overhead. If it's a 65C02, I think scanning for a two byte token where the first token occurs and there are a lot of 'false positives" is going to be more overhead. OTOH if it's a 65C816, you could just scan for the dual token as a single item.

If it were the price to have them, I'd pay, but I'd rather the LEAVE is no slower than a scan-forward operation needs to be.
 

cml37

Moderator
Staff member
May 19, 2019
116
33
28
Washington, D.C. Area
Executing a two tier token is very little extra overhead. If it's a 65C02, I think scanning for a two byte token where the first token occurs and there are a lot of 'false positives" is going to be more overhead. OTOH if it's a 65C816, you could just scan for the dual token as a single item.

If it were the price to have them, I'd pay, but I'd rather the LEAVE is no slower than a scan-forward operation needs to be.
Can't both be implemented? Then add a directive to indicate whether in "two token mode" or "single token mode"
 

BruceMcF

Active Member
May 19, 2019
100
30
28
Can't both be implemented? Then add a directive to indicate whether in "two token mode" or "single token mode"
If the primary tokens can be set aside, there's no benefit in also having a two tier token, that's for if there aren't enough unassigned tokens available.

Most extension keywords along the lines of the Basic 7 keywords would be like WHILE ELSE or LOOP, with fairly negligible overhead from having a two tier token.

Though there's one reduction in primary token use which might speed up rather than slow down the WHILE process ... where a distinct primary token is allocated to DO and DONE and the secondary token is either 1 or -1 ($01 or $FF). Then WHILE only has to search for one token, and can literally just add the secondary token to the block count ... the "stop when the blockcount hits $0 or you run out of program" still works. The DO/DONE routine would have a single entry point which reads the secondary token to see which operation to perform (literally just BPL or BMI if those are the only two secondary tokens for that primary token).
 

mobluse

New Member
Sep 20, 2019
21
8
3
I really like WEND, it's short. And I really dislike two word commands such as END WHILE, END IF etc. For me a command should be one word - and I think this makes the tokenizer and interpreter a lot easier as END in END IF should be another token than END for ending the program.
I think WEND is ugly. It is the editor or Visual Basic compiler that converts End While etc. to BASIC2, so this doesn't affect tokenizing for BASIC2.
 
  • Like
Reactions: BruceMcF

BruceMcF

Active Member
May 19, 2019
100
30
28
I think WEND is ugly. It is the editor or Visual Basic compiler that converts End While etc. to BASIC2, so this doesn't affect tokenizing for BASIC2.
I don't know if it's pretty or ugly, but it is a deceptive name, since that is NEVER the end ... it loops there, and ends (hopefully) when it fails the while test up front.

An advantage of the QBasic DO/LOOP for the Screen Basic overlay over the DO/LEAVE/LOOP is that you don't have to remember different beginning and ending words based on what "type" of conditional loop it is. It's very orthogonal, which is good for beginners just graduating from spaghetti code to structured coding.

DO WHILE (cond)
...
LOOP

DO UNTIL (cond)
...
LOOP

DO
...
LOOP WHILE (cond)

DO
...
LOOP UNTIL (cond)

... and those are all fixed patterns of DO/LEAVE/LOOP and IF/THEN/ELSE when translated into extended-Basic2: WHILE is always "line# IF (cond) THEN line#+1 ELSE LEAVE" and until is always "line# IF (cond) THEN LEAVE", immediately after the DO line for DO [WHILE/UNTIL] and immediately before the LOOP line for LOOP [WHILE/UNTIL], and it's a much more efficient loop than the equivalent with IF/THEN and GOTO alone, because of the known location on the stack at the time of the loop back.