[BEGIN] and [UNTIL] revisited and a new look at loading multiple blocks.
The versions of [BEGIN] and [UNTIL] presented here will only work on a single screen of Forth source in a block based system. That single screen can have a LOAD or THRU between [BEGIN] and [UNTIL] ; however, that could be somewhat inconvenient.
Here are versions which can span multiple blocks.
Code:
: [BEGIN] ( -- )
BLK @ >IN @ 2>A ; IMMEDIATE
: [UNTIL] ( F -- )
IF 2A> 2DROP EXIT THEN
2A> 2DUP 2>A
>IN ! BLK ! ; IMMEDIATE
These words will not work properly with THRU .
Code:
SCR# 300
[BEGIN]
CR .( NUMBER ON THE STACK: )
DUP .
SCR# 301
1-
SCR# 302
?DUP 0=
[UNTIL]
CR .( FINISHED)
Here is the test run. By the way, these blocks are in the C64's REU so the numbers are higher than usual.
Code:
300 RAM 302 RAM THRU 16684
NUMBER ON THE STACK: 10 16685 16686
NUMBER ON THE STACK: 9 OK
THRU displays the number of each block it is about to load: 16684, 16685 and 16686. Notice that the [BEGIN] [UNTIL] structure almost worked. When block 16686 (ram block 302) was loaded, [UNTIL] worked perfectly and caused ram block 300 to load just after [BEGIN] instead of the rest of ram block 302; however, THRU exits because ram block 302 was the last one to load. Even if ram block 302 wasn't the last one to load, THRU would have proceeded to load ram block 303 instead of ram block 301 as desired. This THRU is implemented with a DO LOOP and uses the loop index to determine the number of the next block to load.
Here is a word from the uncontrolled reference words in the Forth-83 Standard which will make a multi block spanning [BEGIN] [UNTIL] structure possible.
Code:
: --> ( -- )
1 BLK +! >IN OFF ; IMMEDIATE
--> causes the next higher block to load, but it is incompatible with THRU , at least this THRU .
Modifying the source slightly by appending --> to the end of each block will allow [BEGIN] and [UNTIL] to work across multiple blocks, but the first block must be LOADed. THRU can not be used!
Code:
SCR# 200
[BEGIN]
CR .( NUMBER ON THE STACK: )
DUP .
-->
SCR# 201
1-
-->
SCR# 202
?DUP 0=
[UNTIL]
CR .( FINISHED)
Here is the session log.
Code:
200 RAM LOAD
NUMBER ON THE STACK: 10
NUMBER ON THE STACK: 9
NUMBER ON THE STACK: 8
NUMBER ON THE STACK: 7
NUMBER ON THE STACK: 6
NUMBER ON THE STACK: 5
NUMBER ON THE STACK: 4
NUMBER ON THE STACK: 3
NUMBER ON THE STACK: 2
NUMBER ON THE STACK: 1
FINISHED OK
I normally don't use --> and didn't define it until just moments ago. I'm not fond of the need to be extra cautious to not mix --> and THRU .
I can use a feature of my system to rewrite THRU .
My Forth's WORD saves the history of the text stream (the values of BLK and >IN) for use with WHERE , one of the error handling words.
Code:
2VARIABLE HISTORY
: WORD ( C -- HERE )
'STREAM
BLK 2@ HISTORY 2!
DUP >IN +!
2PICK SKIP
ROT 2PICK -ROT SCAN
1- 0 MAX NEGATE >IN +!
OVER - >HERE ;
This version of THRU does not use a DO LOOP.
Code:
: THRU ( LO HI -- )
>R 1- HISTORY !
BEGIN
5 ?CR
HISTORY @ 1+
DUP U. LOAD
HISTORY @ R@ U< 0=
DONE? OR
UNTIL
R> DROP ;
Under normal circumstances this THRU does exactly what the original does.
The last block to load is pushed to the return stack. The number of the first block is decremented and stored in the first cell of HISTORY . The line with ?CR is just for 'pretty printing' and can be ignored in this explanation. The value of HISTORY is fetched and incremented by one. This will be the first block to load the first time through the loop. Once the block loads, the first cell of HISTORY contains the number of the block which just finished loading, even if that block is the last in a chain of blocks linked by --> . This block number is compared to the number saved on the return stack. The loop keeps going as long as the block just loaded is less than the last one to be loaded.
This version of THRU can safely be used with --> . Indeed, they use a similar mechanism to advance to the next block. BLK contains the number of a block while it's loading. The first cell of HISTORY contains the number of the block which has just finished loading. More accurately, HISTORY holds the location of the last text string parsed by WORD wherever that string was.
Used in a block like this:
Code:
SCR# 157
HISTORY @ CR U. CR
0 RAM LOAD
The number of the block currently loading will be displayed.
Here are some blocks of source where --> is only used in one block and THRU is used to load the range of blocks.
Code:
SCR# 200
[BEGIN]
CR .( NUMBER ON THE STACK: )
DUP .
.( BLK# R200) CR
-->
SCR# 201
1-
CR .( BLK# R201) CR
SCR# 202
CR .( BLK# R202) CR
?DUP 0=
[UNTIL]
CR .( FINISHED)
--> chains the loading of ram blocks 200 and 201. Notice that there is no chaining of blocks 201 and 202. I will load these blocks with the new THRU .
Code:
200 RAM LIST
SCR# 16584 8 200
0:
1:
2:
3:
4:
5:
6:
7: [BEGIN]
8: CR .( NUMBER ON THE STACK: )
9: DUP .
A:
B: .( BLK# R200) CR
C:
D:
E:
F: -->
OK
0 FH 2 FH THRU 16584
NUMBER ON THE STACK: 9 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 8 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 7 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 6 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 5 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 4 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 3 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 2 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 1 BLK# R200
BLK# R201
16586
BLK# R202
FINISHED OK
And success! --> was successfully used within a range of blocks loaded by THRU . All it did was advance the loading in place of the mechanism used by this new THRU . Since this new THRU gets the number of the latest block loaded from HISTORY , --> will not cause it to unintentionally load blocks multiple times.
One last test.
Code:
SCR# 200
[BEGIN]
CR .( NUMBER ON THE STACK: )
DUP .
.( BLK# R200) CR
-->
SCR# 201
1-
CR .( BLK# R201) CR
SCR# 202
CR .( BLK# R202) CR
?DUP 0=
[UNTIL]
CR .( FINISHED)
-->
SCR# 203
CR .( LET'S PULL A FAST ONE ON THRU)
CR .( AND SEE IF IT RUNS WILD!!!)
CR .( RAM BLK # 203)
-->
SCR# 204
CR .( LET'S PULL A FAST ONE ON THRU)
CR .( AND SEE IF IT RUNS WILD!!!)
CR .( RAM BLK #204)
ram blocks 202, 203 and 204 have been chained together with --> .
Here is the session log.
Code:
200 RAM 202 RAM THRU 16584
NUMBER ON THE STACK: 10 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 9 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 8 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 7 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 6 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 5 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 4 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 3 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 2 BLK# R200
BLK# R201
16586
BLK# R202
NUMBER ON THE STACK: 1 BLK# R200
BLK# R201
16586
BLK# R202
FINISHED
LET'S PULL A FAST ONE ON THRU
AND SEE IF IT RUNS WILD!!!
RAM BLK # 203
LET'S PULL A FAST ONE ON THRU
AND SEE IF IT RUNS WILD!!!
RAM BLK #204 OK
The result is the same as if I had typed
Code:
200 RAM 204 RAM THRU
even if all instances of --> are removed from this last test typing
Code:
200 RAM 204 RAM THRU
has the same effect.
I honestly don't know if anyone has implemented THRU without a DO LOOP . This version of THRU is compatible with --> and the versions of [BEGIN] and [UNTIL] capable of spanning multiple blocks.