Revision 2017-01-25
Background
Forth users will be familiar with the Forth-94
Standard words CATCH
and THROW. These provide
a standard procedure
for handling exceptions without the need to propagate flags
through multiple levels of word nesting.
While effective and simple to use, CATCH THROW can be quite resource
hungry. In order to function over an arbitrary
number of levels, it must save and restore stack positions and
other parameters as determined by the system. In many
applications such complexity is unwarranted and/or prohibitive
and a simpler mechanism can be employed. Typical
situations are those that involve skipping one level of nesting.
UNNEST
Traditionally Forth programmers used the phrase R>
DROP EXIT to terminate the
current definition and the definition
that called it. Today R> DROP is not guaranteed to work on all Forth
systems so we define a portable word UNNEST
which has the same functionality:
In addition to portability, having a dedicated word for this
function offers improved readability - unlike the sequence
R> DROP whose purpose could only be
ascertained by examining the code in which it appeared.
Sample implementation
: UNNEST POSTPONE R> POSTPONE DROP ; IMMEDIATE
Example
\ ANSFORTH file handling for CP/M
forth definitions decimal
128 constant recsiz \ CP/M record size
10 constant #fcb \ max handles
0 value fh ( -- a ) \ current handle
6 36 + constant hsiz \ handle size
sp@ 512 - $FFF0 and constant dlimit
: hbuf ( -- a ) dlimit recsiz - ; \ r/w buffer
: /fh ( -- ) #fcb hsiz * hbuf over - swap erase ; /fh
: use ( -- a ) fh hsiz negate * hbuf + ;
: uid ( -- a ) use 1+ ; \ user number
: fcb ( -- a ) use 6 + ; \ FCB
: rwp@ ( -- ud ) use 2+ 2@ ;
: rwp! ( ud -- ) use 2+ 2! ;
\ get free handle, 0 if none
: getfh ( -- fid | 0 )
#fcb 1+ begin 1- dup while
dup to fh use c@ 0=
until then ;
\ select/check handle
\ mode mask: 1=read 2=write 3=read/write
: setfh ( fid mask -- 0 | err# )
swap dup 1- 0 #fcb within if
to fh use c@ dup if
and 0= 5 and
end
then 2drop 6 ;
\ get/set #records at 3-byte field expressed as bytes
: REC@ ( a -- +d ) dup >r @ r> 2+ c@ 7 0 do d2* loop ;
: REC! ( +d a -- ) >r 7 0 do d2/ loop r@ 2+ c! r> ! ;
: >IOR ( err# -- ior ) dup if $FE00 or then ;
: FILE-POSITION ( fid -- ud ior )
3 setfh rwp@ rot >ior ;
: REPOSITION-FILE ( ud fid -- ior )
3 setfh dup >r if 2drop else rwp! then r> >ior ;
\ set/reset partial close attribute (CPM3/MPM)
: xf5 ( mask fcb -- )
5 + tuck c@ over or swap >< and swap c! ;
: FLUSH-FILE ( fid -- ior )
3 setfh dup 0= if
uid c@ setusr fcb $FF80 over xf5
dup 16 bdos drop $7F00 over xf5
35 bdos drop rstusr
then >ior ;
: FILE-SIZE ( fid -- ud ior )
flush-file fcb 33 + rec@ rot ;
: CLOSE-FILE ( fid -- ior )
3 setfh dup 0= if
( 0 ) use c! uid c@ setusr
fcb 16 bdos rstusr 255 = 2 and
then >ior ;
aka 0 R/O
aka 1 W/O
aka 2 R/W
aka noop BIN immediate
: open1 ( a u fam -- | x ior )
getfh 0= if 2drop 4 >ior unnest end
1+ use c! >fname count fcb setfcb dup uid c!
setusr 0 0 rwp! ;
: open2 ( fn# -- fid flag )
fh fcb rot bdos 255 = rstusr ;
: open3 ( flag err -- | ior )
and >ior ?dup if 0 use c! unnest then ;
: OPEN-FILE ( a u fam -- fid ior )
open1 15 open2 2 open3
( test if file R/O and mode=write )
fcb 9 + c@ 6 rshift use c@ and 1 > 5 open3 0 ;
: CREATE-FILE ( a u fam -- fid ior )
open1 fcb 19 bdos drop 22 open2 5 open3 0 ;
: >FCB ( a u -- fcb usr )
2dup >fname 18 + -rot 2 pick setfcb ;
: DELETE-FILE ( a u -- ior )
>fcb setusr 19 bdos rstusr 255 = 2 and >ior ;
: RENAME-FILE ( a1 u1 a2 u2 -- ior )
2swap >fcb setusr >r >fcb drop
r@ c@ over c! dup 15 bdos 255 = if
r@ 16 + 16 cmove
r> 23 bdos 255 = 2 and >ior
else
16 bdos r> 2drop 5 >ior
then rstusr ;
0 value offs
: setsiz ( -- size )
rwp@ over $7F and to offs fcb 33 + rec!
recsiz offs - ;
: SETDMA ( a -- ) 26 bdos drop ;
: ranrw ( a size fn# -- a size | a u' )
fcb swap bdos if drop unnest r> then ;
: rdini ( a u fid -- u a u | u ior )
1 setfh ?dup if nip >ior unnest end tuck ;
: fread ( a u1 -- a u2 )
begin dup while
>r setsiz r@ umin ( siz)
dup recsiz = if
over setdma 33 ranrw
else
hbuf setdma 33 ranrw
2dup hbuf offs + -rot cmove
then
dup >r + rwp@ r@ m+ rwp! 2r> -
repeat ;
: wrini ( a u fid -- a u | ior )
2 setfh ?dup if nip nip >ior unnest then ;
: fwrite ( a u1 -- a u2 )
begin dup while
>r setsiz r@ umin ( siz)
dup recsiz = if
over setdma
else
hbuf recsiz $1A fill
hbuf setdma fcb 33 bdos drop ( allow errors)
2dup hbuf offs + swap cmove
then
34 ranrw
dup >r + rwp@ r@ m+ rwp! 2r> -
repeat ;
: READ-FILE ( a u1 fid -- u2 ior )
rdini uid c@ setusr fread rstusr nip - 0 ;
: WRITE-FILE ( a u fid -- ior )
wrini uid c@ setusr fwrite rstusr nip 0<> 255 and >ior ;
create (cr) 13 c, 10 c,
: WRITE-LINE ( c-addr u fileid -- ior )
dup >r write-file ?dup if r> drop end
(cr) 2 r> write-file ;
: eol? ( addr -- 2|1|0 )
c@ case
$0D of 2 endof
$0A of 1 endof
0 swap
endcase ;
: READ-LINE ( addr u1 fid -- u2 flag ior )
>r over swap r> read-file ?dup if end
( a u') 2dup over + swap ?do
i dup c@ $1A = if
rot - fh file-size drop rwp! leave
then
eol? ?dup if
i + >r over + r> swap - dup 0<> rwp@ d+ rwp!
i swap - -1 0 unloop
end
loop nip dup 0<> 0 ;
2013-07-30 First release
2014-03-18 Add note on readability
![]()
Page updated: 2017-01-25