Odd nested FOR loop behaviour

Basic and Machine Language

Moderator: Moderators

Post Reply
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Odd nested FOR loop behaviour

Post by srowe »

The following program

Code: Select all

10 FORI=1TO4
20 IFI=3GOTO40
30 NEXT
40 FORU=1TO2:FORI=1TO2:PRINTI,U:NEXT:NEXT
errors out with

Code: Select all

 1                         1
 2                         1
 ?NEXT WITHOUT FOR ERROR
 IN 40 
It seems connected with the re-use of I after leaving the first loop, but taking out the FORU loop makes the error go away.

The same behaviour occurs on the C64.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Odd nested FOR loop behaviour

Post by chysn »

I think I see what's going on here. See the ROM disassembly for FOR:

Code: Select all

LAB_C742
	LDA	#$80			; set FNX
	STA	LAB_10		; set subscript/FNX flag
	JSR	LAB_C9A5		; perform LET
	JSR	LAB_C38A		; search the stack for FOR or GOSUB activity
	BNE	LAB_C753		; branch if FOR, this variable, not found

					; FOR, this variable, was found so first we dump the old one
	TXA				; copy index
	ADC	#$0F			; add FOR structure size-2
	TAX				; copy to index
	TXS				; set stack (dump FOR structure (-2 bytes))
	etc...
If you re-use a variable in FOR, it clears and replaces that FOR entry in the stack, but it also sets the stack pointer to the position after the re-defined FOR. So it's basically losing track of "FOR U" data at that point; by the time you get to the implicit NEXT U, the entry for "FOR U" is unreachable. The NEXT WITHOUT FOR error is against the second NEXT of line 40.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Re: Odd nested FOR loop behaviour

Post by srowe »

Thanks for the hypothesis, I've never looked into the BASIC stack handling code but that would certainly explain the behaviour.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Odd nested FOR loop behaviour

Post by Mike »

Especially there's nothing odd about it.
chysn wrote:[...] it's basically losing track of "FOR U" data at that point [...]
Actively purging the stack from all dangling FOR loops would be a slightly better description of what happens here.

At some other place I described under what circumstances an equal-named FOR loop would remain on stack, see here: Menger-Sponge. Although, the loop variable is re-initialised there as well, so it is necessary to use an own software stack to retain the loop value of the outer loop with same variable name.

The BASIC interpreter does a similar purging procedure if there are dangling FOR loops when a sub-routine is ended with RETURN. It is not necessary to close uncompleted FOR loops to be 'allowed' a RETURN. As an example, I use this in the 'free-direction?' scanner in my TRON game. When the enemy lightcycle is about to hit a wall, the following sub-routine in line 41 is called:

Code: Select all

17 IFPEEK(G+D(H))<>219ORRND(1)<.1THENI=C:GOSUB41
[...]
41 FORT=1TO4:I=(I+1)AND3:IFPEEK(G+D(I))=219THENH=I:RETURN
42 NEXT:RETURN
In case there's a free direction, H is assigned that value and the sub-routine returns immediately. Otherwise the loop is completed, H retains its value, and the sub-routine returns normally.
wimoos
Vic 20 Afficionado
Posts: 348
Joined: Tue Apr 14, 2009 8:15 am
Website: http://wimbasic.webs.com
Location: Netherlands
Occupation: farmer

Re: Odd nested FOR loop behaviour

Post by wimoos »

WimBasic actually has a command to resolve issues like this, i.e. DISPOSE NEXT. This drops the most recent FOR/NEXT frame, or can drop multiple frames when the accompanying variable is specified.

Code: Select all

10 FORI=1TO4
20 IFI=3THEN DISPOSE NEXT :GOTO40
30 NEXT
40 FORU=1TO2:FORI=1TO2:PRINTI,U:NEXT:NEXT
The DISPOSE command can also be used to drop GOSUB/RETURN frames (DISPOSE RETURN), or to completely purge the stack (DISPOSE CLR).
VICE; selfwritten 65asmgen; tasm; maintainer of WimBasic
Post Reply